[FL-1401] Add Universal TV remote (#539)
* Remove excess headers * Add ButtonPanel * Add Popup * Move FileReader to standalone object * Universal remote (part 1) * Universal remote (part 2) * Global rename tranciever/file_parser * Compile assets * syntax fix * English: rename tranceiver to transceiver. Co-authored-by: あく <alleteam@gmail.com>
@ -8,6 +8,42 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void elements_progress_bar(
|
||||
Canvas* canvas,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
uint8_t width,
|
||||
uint8_t progress,
|
||||
uint8_t total) {
|
||||
furi_assert(canvas);
|
||||
furi_assert(total > 0);
|
||||
uint8_t height = 9;
|
||||
uint8_t marker_width = 7;
|
||||
furi_assert(width > marker_width);
|
||||
|
||||
uint8_t progress_length = ((float)progress / total) * (width - marker_width - 2);
|
||||
|
||||
// rframe doesnt work if (radius * 2) > any rect side, so write manually
|
||||
uint8_t x_max = x + width - 1;
|
||||
uint8_t y_max = y + height - 1;
|
||||
canvas_draw_line(canvas, x + 3, y, x_max - 3, y);
|
||||
canvas_draw_line(canvas, x_max - 3, y, x_max, y + 3);
|
||||
canvas_draw_line(canvas, x_max, y + 3, x_max, y_max - 3);
|
||||
canvas_draw_line(canvas, x_max, y_max - 3, x_max - 3, y_max);
|
||||
canvas_draw_line(canvas, x_max - 3, y_max, x + 3, y_max);
|
||||
canvas_draw_line(canvas, x + 3, y_max, x, y_max - 3);
|
||||
canvas_draw_line(canvas, x, y_max - 3, x, y + 3);
|
||||
canvas_draw_line(canvas, x, y + 3, x + 3, y);
|
||||
|
||||
canvas_draw_rbox(canvas, x + 1, y + 1, marker_width + progress_length, height - 2, 3);
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_dot(canvas, x + progress_length + 3, y + 2);
|
||||
canvas_draw_dot(canvas, x + progress_length + 4, y + 2);
|
||||
canvas_draw_dot(canvas, x + progress_length + 5, y + 3);
|
||||
canvas_draw_dot(canvas, x + progress_length + 6, y + 4);
|
||||
canvas_invert_color(canvas);
|
||||
}
|
||||
|
||||
void elements_scrollbar_pos(
|
||||
Canvas* canvas,
|
||||
uint8_t x,
|
||||
|
@ -8,6 +8,22 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Draw progress bar.
|
||||
* @param x - progress bar position on X axis
|
||||
* @param y - progress bar position on Y axis
|
||||
* @param width - progress bar width
|
||||
* @param progress - progress in unnamed metric
|
||||
* @param total - total amount in unnamed metric
|
||||
*/
|
||||
void elements_progress_bar(
|
||||
Canvas* canvas,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
uint8_t width,
|
||||
uint8_t progress,
|
||||
uint8_t total);
|
||||
|
||||
/*
|
||||
* Draw scrollbar on canvas at specific position.
|
||||
* @param x - scrollbar position on X axis
|
||||
@ -16,7 +32,6 @@ extern "C" {
|
||||
* @param pos - current element
|
||||
* @param total - total elements
|
||||
*/
|
||||
|
||||
void elements_scrollbar_pos(
|
||||
Canvas* canvas,
|
||||
uint8_t x,
|
||||
|
409
applications/gui/modules/button_panel.c
Normal file
@ -0,0 +1,409 @@
|
||||
#include "button_panel.h"
|
||||
#include "api-hal-resources.h"
|
||||
#include "gui/canvas.h"
|
||||
#include <m-array.h>
|
||||
#include <m-i-list.h>
|
||||
#include <m-list.h>
|
||||
#include <furi.h>
|
||||
#include <gui/elements.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
// uint16_t to support multi-screen, wide button panel
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
Font font;
|
||||
const char* str;
|
||||
} LabelElement;
|
||||
|
||||
LIST_DEF(LabelList, LabelElement, M_POD_OPLIST)
|
||||
#define M_OPL_LabelList_t() LIST_OPLIST(LabelList)
|
||||
|
||||
typedef struct {
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
IconName name;
|
||||
IconName name_selected;
|
||||
} IconElement;
|
||||
|
||||
typedef struct ButtonItem {
|
||||
uint32_t index;
|
||||
ButtonItemCallback callback;
|
||||
IconElement icon;
|
||||
void* callback_context;
|
||||
} ButtonItem;
|
||||
|
||||
ARRAY_DEF(ButtonArray, ButtonItem*, M_PTR_OPLIST);
|
||||
#define M_OPL_ButtonArray_t() ARRAY_OPLIST(ButtonArray, M_PTR_OPLIST)
|
||||
ARRAY_DEF(ButtonMatrix, ButtonArray_t);
|
||||
#define M_OPL_ButtonMatrix_t() ARRAY_OPLIST(ButtonMatrix, M_OPL_ButtonArray_t())
|
||||
|
||||
struct ButtonPanel {
|
||||
View* view;
|
||||
ButtonPanelInputCallback input_callback;
|
||||
void* input_context;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
ButtonMatrix_t button_matrix;
|
||||
LabelList_t labels;
|
||||
uint16_t reserve_x;
|
||||
uint16_t reserve_y;
|
||||
uint16_t selected_item_x;
|
||||
uint16_t selected_item_y;
|
||||
ButtonPanelDrawCallback draw_callback;
|
||||
void* draw_context;
|
||||
} ButtonPanelModel;
|
||||
|
||||
static ButtonItem** button_panel_get_item(ButtonPanelModel* model, size_t x, size_t y);
|
||||
static void button_panel_process_up(ButtonPanel* button_panel);
|
||||
static void button_panel_process_down(ButtonPanel* button_panel);
|
||||
static void button_panel_process_left(ButtonPanel* button_panel);
|
||||
static void button_panel_process_right(ButtonPanel* button_panel);
|
||||
static void button_panel_process_ok(ButtonPanel* button_panel);
|
||||
static void button_panel_view_draw_callback(Canvas* canvas, void* _model);
|
||||
static bool button_panel_view_input_callback(InputEvent* event, void* context);
|
||||
|
||||
ButtonPanel* button_panel_alloc() {
|
||||
ButtonPanel* button_panel = furi_alloc(sizeof(ButtonPanel));
|
||||
button_panel->view = view_alloc();
|
||||
view_set_orientation(button_panel->view, ViewOrientationVertical);
|
||||
view_set_context(button_panel->view, button_panel);
|
||||
view_allocate_model(button_panel->view, ViewModelTypeLocking, sizeof(ButtonPanelModel));
|
||||
view_set_draw_callback(button_panel->view, button_panel_view_draw_callback);
|
||||
view_set_input_callback(button_panel->view, button_panel_view_input_callback);
|
||||
button_panel->input_callback = NULL;
|
||||
|
||||
with_view_model(
|
||||
button_panel->view, (ButtonPanelModel * model) {
|
||||
model->reserve_x = 0;
|
||||
model->reserve_y = 0;
|
||||
model->selected_item_x = 0;
|
||||
model->selected_item_y = 0;
|
||||
model->draw_callback = NULL;
|
||||
ButtonMatrix_init(model->button_matrix);
|
||||
LabelList_init(model->labels);
|
||||
return true;
|
||||
});
|
||||
|
||||
return button_panel;
|
||||
}
|
||||
|
||||
void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t reserve_y) {
|
||||
furi_check(reserve_x > 0);
|
||||
furi_check(reserve_y > 0);
|
||||
|
||||
with_view_model(
|
||||
button_panel->view, (ButtonPanelModel * model) {
|
||||
model->reserve_x = reserve_x;
|
||||
model->reserve_y = reserve_y;
|
||||
ButtonMatrix_reserve(model->button_matrix, model->reserve_y);
|
||||
for(size_t i = 0; i > model->reserve_y; ++i) {
|
||||
ButtonArray_t* array = ButtonMatrix_get(model->button_matrix, i);
|
||||
ButtonArray_init(*array);
|
||||
ButtonArray_reserve(*array, reserve_x);
|
||||
// TODO: do we need to clear allocated memory of ptr-s to ButtonItem ??
|
||||
}
|
||||
LabelList_init(model->labels);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void button_panel_free(ButtonPanel* button_panel) {
|
||||
furi_assert(button_panel);
|
||||
|
||||
button_panel_clean(button_panel);
|
||||
|
||||
with_view_model(
|
||||
button_panel->view, (ButtonPanelModel * model) {
|
||||
LabelList_clear(model->labels);
|
||||
ButtonMatrix_clear(model->button_matrix);
|
||||
return true;
|
||||
});
|
||||
|
||||
view_free(button_panel->view);
|
||||
free(button_panel);
|
||||
}
|
||||
|
||||
void button_panel_clean(ButtonPanel* button_panel) {
|
||||
furi_assert(button_panel);
|
||||
|
||||
with_view_model(
|
||||
button_panel->view, (ButtonPanelModel * model) {
|
||||
for(size_t x = 0; x < model->reserve_x; ++x) {
|
||||
for(size_t y = 0; y < model->reserve_y; ++y) {
|
||||
ButtonItem** button_item = button_panel_get_item(model, x, y);
|
||||
free(*button_item);
|
||||
*button_item = NULL;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static ButtonItem** button_panel_get_item(ButtonPanelModel* model, size_t x, size_t y) {
|
||||
furi_assert(model);
|
||||
|
||||
furi_check(x < model->reserve_x);
|
||||
furi_check(y < model->reserve_y);
|
||||
ButtonArray_t* button_array = ButtonMatrix_get_at(model->button_matrix, x);
|
||||
ButtonItem** button_item = ButtonArray_get_at(*button_array, y);
|
||||
return button_item;
|
||||
}
|
||||
|
||||
void button_panel_add_item(
|
||||
ButtonPanel* button_panel,
|
||||
uint32_t index,
|
||||
uint16_t matrix_place_x,
|
||||
uint16_t matrix_place_y,
|
||||
uint16_t x,
|
||||
uint16_t y,
|
||||
IconName icon_name,
|
||||
IconName icon_name_selected,
|
||||
ButtonItemCallback callback,
|
||||
void* callback_context) {
|
||||
furi_assert(button_panel);
|
||||
|
||||
with_view_model(
|
||||
button_panel->view, (ButtonPanelModel * model) {
|
||||
ButtonItem** button_item_ptr =
|
||||
button_panel_get_item(model, matrix_place_x, matrix_place_y);
|
||||
furi_check(*button_item_ptr == NULL);
|
||||
*button_item_ptr = furi_alloc(sizeof(ButtonItem));
|
||||
ButtonItem* button_item = *button_item_ptr;
|
||||
button_item->callback = callback;
|
||||
button_item->callback_context = callback_context;
|
||||
button_item->icon.x = x;
|
||||
button_item->icon.y = y;
|
||||
button_item->icon.name = icon_name;
|
||||
button_item->icon.name_selected = icon_name_selected;
|
||||
button_item->index = index;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
View* button_panel_get_view(ButtonPanel* button_panel) {
|
||||
furi_assert(button_panel);
|
||||
return button_panel->view;
|
||||
}
|
||||
|
||||
static void button_panel_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
furi_assert(canvas);
|
||||
furi_assert(_model);
|
||||
|
||||
ButtonPanelModel* model = _model;
|
||||
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
for(size_t x = 0; x < model->reserve_x; ++x) {
|
||||
for(size_t y = 0; y < model->reserve_y; ++y) {
|
||||
ButtonItem* button_item = *button_panel_get_item(model, x, y);
|
||||
IconName icon_name = button_item->icon.name;
|
||||
if((model->selected_item_x == x) && (model->selected_item_y == y)) {
|
||||
icon_name = button_item->icon.name_selected;
|
||||
}
|
||||
canvas_draw_icon_name(canvas, button_item->icon.x, button_item->icon.y, icon_name);
|
||||
}
|
||||
}
|
||||
|
||||
for
|
||||
M_EACH(label, model->labels, LabelList_t) {
|
||||
canvas_set_font(canvas, label->font);
|
||||
canvas_draw_str(canvas, label->x, label->y, label->str);
|
||||
}
|
||||
|
||||
if(model->draw_callback) model->draw_callback(canvas, model->draw_context);
|
||||
}
|
||||
|
||||
static void button_panel_process_down(ButtonPanel* button_panel) {
|
||||
with_view_model(
|
||||
button_panel->view, (ButtonPanelModel * model) {
|
||||
size_t new_selected_item_x = model->selected_item_x;
|
||||
size_t new_selected_item_y = model->selected_item_y;
|
||||
size_t i;
|
||||
|
||||
if(new_selected_item_y >= (model->reserve_y - 1)) return false;
|
||||
|
||||
++new_selected_item_y;
|
||||
|
||||
for(i = 0; i < model->reserve_x; ++i) {
|
||||
new_selected_item_x = (model->selected_item_x + i) % model->reserve_x;
|
||||
if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i == model->reserve_x) return false;
|
||||
|
||||
model->selected_item_x = new_selected_item_x;
|
||||
model->selected_item_y = new_selected_item_y;
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void button_panel_process_up(ButtonPanel* button_panel) {
|
||||
with_view_model(
|
||||
button_panel->view, (ButtonPanelModel * model) {
|
||||
size_t new_selected_item_x = model->selected_item_x;
|
||||
size_t new_selected_item_y = model->selected_item_y;
|
||||
size_t i;
|
||||
|
||||
if(new_selected_item_y <= 0) return false;
|
||||
|
||||
--new_selected_item_y;
|
||||
|
||||
for(i = 0; i < model->reserve_x; ++i) {
|
||||
new_selected_item_x = (model->selected_item_x + i) % model->reserve_x;
|
||||
if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i == model->reserve_x) return false;
|
||||
|
||||
model->selected_item_x = new_selected_item_x;
|
||||
model->selected_item_y = new_selected_item_y;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void button_panel_process_left(ButtonPanel* button_panel) {
|
||||
with_view_model(
|
||||
button_panel->view, (ButtonPanelModel * model) {
|
||||
size_t new_selected_item_x = model->selected_item_x;
|
||||
size_t new_selected_item_y = model->selected_item_y;
|
||||
size_t i;
|
||||
|
||||
if(new_selected_item_x <= 0) return false;
|
||||
|
||||
--new_selected_item_x;
|
||||
|
||||
for(i = 0; i < model->reserve_y; ++i) {
|
||||
new_selected_item_y = (model->selected_item_y + i) % model->reserve_y;
|
||||
if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i == model->reserve_y) return false;
|
||||
|
||||
model->selected_item_x = new_selected_item_x;
|
||||
model->selected_item_y = new_selected_item_y;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void button_panel_process_right(ButtonPanel* button_panel) {
|
||||
with_view_model(
|
||||
button_panel->view, (ButtonPanelModel * model) {
|
||||
size_t new_selected_item_x = model->selected_item_x;
|
||||
size_t new_selected_item_y = model->selected_item_y;
|
||||
size_t i;
|
||||
|
||||
if(new_selected_item_x >= (model->reserve_x - 1)) return false;
|
||||
|
||||
++new_selected_item_x;
|
||||
|
||||
for(i = 0; i < model->reserve_y; ++i) {
|
||||
new_selected_item_y = (model->selected_item_y + i) % model->reserve_y;
|
||||
if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i == model->reserve_y) return false;
|
||||
|
||||
model->selected_item_x = new_selected_item_x;
|
||||
model->selected_item_y = new_selected_item_y;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void button_panel_process_ok(ButtonPanel* button_panel) {
|
||||
ButtonItem* button_item = NULL;
|
||||
|
||||
with_view_model(
|
||||
button_panel->view, (ButtonPanelModel * model) {
|
||||
button_item =
|
||||
*button_panel_get_item(model, model->selected_item_x, model->selected_item_y);
|
||||
return true;
|
||||
});
|
||||
|
||||
if(button_item && button_item->callback) {
|
||||
button_item->callback(button_item->callback_context, button_item->index);
|
||||
}
|
||||
}
|
||||
|
||||
static bool button_panel_view_input_callback(InputEvent* event, void* context) {
|
||||
ButtonPanel* button_panel = context;
|
||||
furi_assert(button_panel);
|
||||
bool consumed = false;
|
||||
|
||||
if(button_panel->input_callback) {
|
||||
consumed = button_panel->input_callback(event, button_panel->input_context);
|
||||
} else if(event->type == InputTypeShort) {
|
||||
switch(event->key) {
|
||||
case InputKeyUp:
|
||||
consumed = true;
|
||||
button_panel_process_up(button_panel);
|
||||
break;
|
||||
case InputKeyDown:
|
||||
consumed = true;
|
||||
button_panel_process_down(button_panel);
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
consumed = true;
|
||||
button_panel_process_left(button_panel);
|
||||
break;
|
||||
case InputKeyRight:
|
||||
consumed = true;
|
||||
button_panel_process_right(button_panel);
|
||||
break;
|
||||
case InputKeyOk:
|
||||
consumed = true;
|
||||
button_panel_process_ok(button_panel);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void button_panel_add_label(
|
||||
ButtonPanel* button_panel,
|
||||
uint16_t x,
|
||||
uint16_t y,
|
||||
Font font,
|
||||
const char* label_str) {
|
||||
furi_assert(button_panel);
|
||||
|
||||
with_view_model(
|
||||
button_panel->view, (ButtonPanelModel * model) {
|
||||
LabelElement* label = LabelList_push_raw(model->labels);
|
||||
label->x = x;
|
||||
label->y = y;
|
||||
label->font = font;
|
||||
label->str = label_str;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void button_panel_set_popup_draw_callback(
|
||||
ButtonPanel* button_panel,
|
||||
ButtonPanelDrawCallback callback,
|
||||
void* context) {
|
||||
with_view_model(
|
||||
button_panel->view, (ButtonPanelModel * model) {
|
||||
model->draw_callback = callback;
|
||||
model->draw_context = context;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void button_panel_set_popup_input_callback(
|
||||
ButtonPanel* button_panel,
|
||||
ButtonPanelInputCallback callback,
|
||||
void* context) {
|
||||
button_panel->input_context = context;
|
||||
button_panel->input_callback = callback;
|
||||
}
|
129
applications/gui/modules/button_panel.h
Normal file
@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
#include <gui/view.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Button panel module descriptor */
|
||||
typedef struct ButtonPanel ButtonPanel;
|
||||
|
||||
/** Callback type to call for handling selecting button_panel items */
|
||||
typedef void (*ButtonItemCallback)(void* context, uint32_t index);
|
||||
/** Callback type for additional drawings above main button_panel screen */
|
||||
typedef void (*ButtonPanelDrawCallback)(Canvas* canvas, void* _model);
|
||||
/** Callback type to intercept input events of button_panel */
|
||||
typedef bool (*ButtonPanelInputCallback)(InputEvent* event, void* context);
|
||||
|
||||
/** Allocate new button_panel module.
|
||||
*
|
||||
* @return just-created module
|
||||
*/
|
||||
ButtonPanel* button_panel_alloc(void);
|
||||
|
||||
/** Free button_panel module.
|
||||
*
|
||||
* @param button_panel - module to free
|
||||
*/
|
||||
void button_panel_free(ButtonPanel* button_panel);
|
||||
|
||||
/** Free items from button_panel module. Preallocated matrix stays unchanged.
|
||||
*
|
||||
* @param button_panel - module to clean
|
||||
*/
|
||||
void button_panel_clean(ButtonPanel* button_panel);
|
||||
|
||||
/** Reserve space for adding items.
|
||||
*
|
||||
* One does not simply use button_panel_add_item() without this function.
|
||||
* It should be allocated space for it first.
|
||||
*
|
||||
* @param button_panel - module to modify
|
||||
* @param reserve_x - number of columns in button_panel
|
||||
* @param reserve_y - number of rows in button_panel
|
||||
*/
|
||||
void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t reserve_y);
|
||||
|
||||
/** Add item to button_panel module.
|
||||
*
|
||||
* Have to set element in bounds of allocated size by X and by Y.
|
||||
*
|
||||
* @param button_panel - module
|
||||
* @param index - value to pass to callback
|
||||
* @param matrix_place_x - coordinates by x-axis on virtual grid, it
|
||||
* is only used for naviagation
|
||||
* @param matrix_place_y - coordinates by y-axis on virtual grid, it
|
||||
* is only used for naviagation
|
||||
* @param x - x-coordinate to draw icon on
|
||||
* @param y - y-coordinate to draw icon on
|
||||
* @param icon_name - name of the icon to draw
|
||||
* @param icon_name_selected - name of the icon to draw when current
|
||||
* element is selected
|
||||
* @param callback - function to call when specific element is selected
|
||||
* (pressed Ok on selected item)
|
||||
* @param callback_context - context to pass to callback
|
||||
*/
|
||||
void button_panel_add_item(
|
||||
ButtonPanel* button_panel,
|
||||
uint32_t index,
|
||||
uint16_t matrix_place_x,
|
||||
uint16_t matrix_place_y,
|
||||
uint16_t x,
|
||||
uint16_t y,
|
||||
IconName icon_name,
|
||||
IconName icon_name_selected,
|
||||
ButtonItemCallback callback,
|
||||
void* callback_context);
|
||||
|
||||
/** Get button_panel view.
|
||||
*
|
||||
* @param button_panel - module to get view from
|
||||
* @return acquired view
|
||||
*/
|
||||
View* button_panel_get_view(ButtonPanel* button_panel);
|
||||
|
||||
/** Add label to button_panel module.
|
||||
*
|
||||
* @param x - x-coordinate to place label
|
||||
* @param y - y-coordinate to place label
|
||||
* @param font - font to write label with
|
||||
* @param label_str - string label to write
|
||||
*/
|
||||
void button_panel_add_label(
|
||||
ButtonPanel* button_panel,
|
||||
uint16_t x,
|
||||
uint16_t y,
|
||||
Font font,
|
||||
const char* label_str);
|
||||
|
||||
// TODO: [FL-1445] Have to replace callbacks above with additional popup-layer
|
||||
/** Set popup draw callback for button_panel module.
|
||||
*
|
||||
* Used to add popup drawings after main draw callback is done.
|
||||
*
|
||||
* @param button_panel - module to modify
|
||||
* @param callback - callback function to set for draw event
|
||||
* @param context - context to pass to callback
|
||||
*/
|
||||
void button_panel_set_popup_draw_callback(
|
||||
ButtonPanel* button_panel,
|
||||
ButtonPanelDrawCallback callback,
|
||||
void* context);
|
||||
|
||||
/** Set popup input callback for button_panel module.
|
||||
*
|
||||
* Used to add popup input callback. It will intercept all input
|
||||
* events for current view.
|
||||
*
|
||||
* @param button_panel - module to modify
|
||||
* @param callback - function to overwrite main input callbacks
|
||||
* @param context - context to pass to callback
|
||||
*/
|
||||
void button_panel_set_popup_input_callback(
|
||||
ButtonPanel* button_panel,
|
||||
ButtonPanelInputCallback callback,
|
||||
void* context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
77
applications/irda/irda-app-brute-force.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
#include "irda-app-brute-force.hpp"
|
||||
|
||||
void IrdaAppBruteForce::add_record(int index, const char* name) {
|
||||
records[name].index = index;
|
||||
records[name].amount = 0;
|
||||
}
|
||||
|
||||
bool IrdaAppBruteForce::calculate_messages() {
|
||||
bool fs_res = false;
|
||||
fs_res = file_parser.get_fs_api().file.open(
|
||||
&file, universal_db_filename, FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
if(!fs_res) {
|
||||
file_parser.get_sd_api().show_error(file_parser.get_sd_api().context, "Can't open file");
|
||||
return false;
|
||||
}
|
||||
|
||||
file_parser.reset();
|
||||
while(1) {
|
||||
auto message = file_parser.read_message(&file);
|
||||
if(!message) break;
|
||||
auto element = records.find(message->name);
|
||||
if(element != records.cend()) {
|
||||
++element->second.amount;
|
||||
}
|
||||
}
|
||||
|
||||
file_parser.get_fs_api().file.close(&file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void IrdaAppBruteForce::stop_bruteforce() {
|
||||
if(current_record.size()) {
|
||||
file_parser.get_fs_api().file.close(&file);
|
||||
current_record.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: [FL-1418] replace with timer-chained consequence of messages.
|
||||
bool IrdaAppBruteForce::send_next_bruteforce(const IrdaAppSignalTransceiver& transceiver) {
|
||||
furi_assert(current_record.size());
|
||||
|
||||
std::unique_ptr<IrdaAppFileParser::IrdaFileMessage> message;
|
||||
|
||||
do {
|
||||
message = file_parser.read_message(&file);
|
||||
} while(message && current_record.compare(message->name));
|
||||
|
||||
if(message) {
|
||||
transceiver.send_message(&message->message);
|
||||
}
|
||||
return !!message;
|
||||
}
|
||||
|
||||
bool IrdaAppBruteForce::start_bruteforce(int index, int& record_amount) {
|
||||
file_parser.reset();
|
||||
for(const auto& it : records) {
|
||||
if(it.second.index == index) {
|
||||
record_amount = it.second.amount;
|
||||
current_record = it.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(record_amount) {
|
||||
bool fs_res = file_parser.get_fs_api().file.open(
|
||||
&file, universal_db_filename, FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
if(fs_res) {
|
||||
return true;
|
||||
} else {
|
||||
file_parser.get_sd_api().show_error(
|
||||
file_parser.get_sd_api().context, "Can't open file");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
38
applications/irda/irda-app-brute-force.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "furi/check.h"
|
||||
#include <unordered_map>
|
||||
#include "irda-app-file-parser.hpp"
|
||||
#include "irda-app-transceiver.hpp"
|
||||
|
||||
|
||||
class IrdaAppBruteForce {
|
||||
const char* universal_db_filename;
|
||||
IrdaAppFileParser file_parser;
|
||||
File file;
|
||||
std::string current_record;
|
||||
|
||||
typedef struct {
|
||||
int index;
|
||||
int amount;
|
||||
} Record;
|
||||
|
||||
// 'key' is record name, because we have to search by both, index and name,
|
||||
// but index search has place once per button press, and should not be
|
||||
// noticed, but name search should occur during entering universal menu,
|
||||
// and will go through container for every record in file, that's why
|
||||
// more critical to have faster search by record name.
|
||||
std::unordered_map<std::string, Record> records;
|
||||
|
||||
public:
|
||||
bool calculate_messages();
|
||||
void stop_bruteforce();
|
||||
bool send_next_bruteforce(const IrdaAppSignalTransceiver& receiver);
|
||||
bool start_bruteforce(int index, int& record_amount);
|
||||
void add_record(int index, const char* name);
|
||||
|
||||
IrdaAppBruteForce(const char* filename) : universal_db_filename (filename) {}
|
||||
~IrdaAppBruteForce() {
|
||||
stop_bruteforce();
|
||||
}
|
||||
};
|
||||
|
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <irda.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
|
||||
@ -14,6 +13,8 @@ public:
|
||||
IrdaMessageReceived,
|
||||
TextEditDone,
|
||||
PopupTimer,
|
||||
ButtonPanelPressed,
|
||||
ButtonPanelPopupBackPressed,
|
||||
};
|
||||
|
||||
union {
|
||||
|
58
applications/irda/irda-app-file-parser.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
#include "irda-app-file-parser.hpp"
|
||||
|
||||
std::unique_ptr<IrdaAppFileParser::IrdaFileMessage> IrdaAppFileParser::read_message(File* file) {
|
||||
while(1) {
|
||||
auto str = getline(file);
|
||||
if(str.empty()) return nullptr;
|
||||
|
||||
auto message = parse_message(str);
|
||||
if(message) return message;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<IrdaAppFileParser::IrdaFileMessage>
|
||||
IrdaAppFileParser::parse_message(const std::string& str) const {
|
||||
char protocol_name[32];
|
||||
uint32_t address;
|
||||
uint32_t command;
|
||||
auto irda_file_message = std::make_unique<IrdaFileMessage>();
|
||||
|
||||
int parsed = std::sscanf(
|
||||
str.c_str(),
|
||||
"%31s %31s A:%lX C:%lX",
|
||||
irda_file_message->name,
|
||||
protocol_name,
|
||||
&address,
|
||||
&command);
|
||||
|
||||
if(parsed != 4) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IrdaProtocol protocol = irda_get_protocol_by_name(protocol_name);
|
||||
|
||||
if(!irda_is_protocol_valid((IrdaProtocol)protocol)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int address_length = irda_get_protocol_address_length(protocol);
|
||||
uint32_t address_mask = (1LU << (4 * address_length)) - 1;
|
||||
if(address != (address & address_mask)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int command_length = irda_get_protocol_command_length(protocol);
|
||||
uint32_t command_mask = (1LU << (4 * command_length)) - 1;
|
||||
if(command != (command & command_mask)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
irda_file_message->message = {
|
||||
.protocol = protocol,
|
||||
.address = address,
|
||||
.command = command,
|
||||
.repeat = false,
|
||||
};
|
||||
|
||||
return irda_file_message;
|
||||
}
|
17
applications/irda/irda-app-file-parser.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "file_reader/file_reader.hpp"
|
||||
#include "irda.h"
|
||||
|
||||
class IrdaAppFileParser : public FileReader {
|
||||
public:
|
||||
typedef struct {
|
||||
char name[32];
|
||||
IrdaMessage message;
|
||||
} IrdaFileMessage;
|
||||
|
||||
std::unique_ptr<IrdaAppFileParser::IrdaFileMessage> read_message(File* file);
|
||||
|
||||
private:
|
||||
std::unique_ptr<IrdaFileMessage> parse_message(const std::string& str) const;
|
||||
};
|
||||
|
@ -4,10 +4,10 @@
|
||||
#include "furi/check.h"
|
||||
#include "gui/modules/button_menu.h"
|
||||
#include "irda.h"
|
||||
#include "sys/_stdint.h"
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "irda-app-file-parser.hpp"
|
||||
|
||||
const char* IrdaAppRemoteManager::irda_directory = "irda";
|
||||
const char* IrdaAppRemoteManager::irda_extension = ".ir";
|
||||
@ -33,16 +33,6 @@ find_vacant_name(const std::vector<std::string>& strings, const std::string& nam
|
||||
}
|
||||
}
|
||||
|
||||
IrdaAppRemoteManager::IrdaAppRemoteManager() {
|
||||
sd_ex_api = static_cast<SdCard_Api*>(furi_record_open("sdcard-ex"));
|
||||
fs_api = static_cast<FS_Api*>(furi_record_open("sdcard"));
|
||||
}
|
||||
|
||||
IrdaAppRemoteManager::~IrdaAppRemoteManager() {
|
||||
furi_record_close("sdcard");
|
||||
furi_record_close("sdcard-ex");
|
||||
}
|
||||
|
||||
bool IrdaAppRemoteManager::add_button(const char* button_name, const IrdaMessage* message) {
|
||||
remote->buttons.emplace_back(button_name, message);
|
||||
return store();
|
||||
@ -94,10 +84,12 @@ std::string IrdaAppRemoteManager::make_filename(const std::string& name) const {
|
||||
|
||||
bool IrdaAppRemoteManager::delete_remote() {
|
||||
FS_Error fs_res;
|
||||
IrdaAppFileParser file_parser;
|
||||
|
||||
fs_res = fs_api->common.remove(make_filename(remote->name).c_str());
|
||||
fs_res = file_parser.get_fs_api().common.remove(make_filename(remote->name).c_str());
|
||||
if(fs_res != FSE_OK) {
|
||||
show_file_error_message("Error deleting file");
|
||||
file_parser.get_sd_api().show_error(
|
||||
file_parser.get_sd_api().context, "Error deleting file");
|
||||
return false;
|
||||
}
|
||||
remote.reset();
|
||||
@ -147,11 +139,13 @@ bool IrdaAppRemoteManager::rename_remote(const char* str) {
|
||||
if(!result) return false;
|
||||
|
||||
auto new_name = find_vacant_name(remote_list, str);
|
||||
FS_Error fs_err = fs_api->common.rename(
|
||||
IrdaAppFileParser file_parser;
|
||||
FS_Error fs_err = file_parser.get_fs_api().common.rename(
|
||||
make_filename(remote->name).c_str(), make_filename(new_name).c_str());
|
||||
remote->name = new_name;
|
||||
if(fs_err != FSE_OK) {
|
||||
show_file_error_message("Error renaming\nremote file");
|
||||
file_parser.get_sd_api().show_error(
|
||||
file_parser.get_sd_api().context, "Error renaming\nremote file");
|
||||
}
|
||||
return fs_err == FSE_OK;
|
||||
}
|
||||
@ -170,26 +164,25 @@ size_t IrdaAppRemoteManager::get_number_of_buttons() {
|
||||
return remote->buttons.size();
|
||||
}
|
||||
|
||||
void IrdaAppRemoteManager::show_file_error_message(const char* error_text) const {
|
||||
sd_ex_api->show_error(sd_ex_api->context, error_text);
|
||||
}
|
||||
|
||||
bool IrdaAppRemoteManager::store(void) {
|
||||
File file;
|
||||
uint16_t write_count;
|
||||
std::string dirname(std::string("/") + irda_directory);
|
||||
|
||||
FS_Error fs_err = fs_api->common.mkdir(dirname.c_str());
|
||||
IrdaAppFileParser file_parser;
|
||||
FS_Error fs_err = file_parser.get_fs_api().common.mkdir(dirname.c_str());
|
||||
if((fs_err != FSE_OK) && (fs_err != FSE_EXIST)) {
|
||||
show_file_error_message("Can't create directory");
|
||||
file_parser.get_sd_api().show_error(
|
||||
file_parser.get_sd_api().context, "Can't create directory");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string filename = dirname + "/" + remote->name + irda_extension;
|
||||
bool res = fs_api->file.open(&file, filename.c_str(), FSAM_WRITE, FSOM_CREATE_ALWAYS);
|
||||
bool res = file_parser.get_fs_api().file.open(
|
||||
&file, make_filename(remote->name).c_str(), FSAM_WRITE, FSOM_CREATE_ALWAYS);
|
||||
|
||||
if(!res) {
|
||||
show_file_error_message("Cannot create\nnew remote file");
|
||||
file_parser.get_sd_api().show_error(
|
||||
file_parser.get_sd_api().context, "Cannot create\nnew remote file");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -210,103 +203,21 @@ bool IrdaAppRemoteManager::store(void) {
|
||||
button.message.command);
|
||||
|
||||
auto content_len = strlen(content);
|
||||
write_count = fs_api->file.write(&file, content, content_len);
|
||||
write_count = file_parser.get_fs_api().file.write(&file, content, content_len);
|
||||
if(file.error_id != FSE_OK || write_count != content_len) {
|
||||
show_file_error_message("Cannot write\nto key file");
|
||||
fs_api->file.close(&file);
|
||||
file_parser.get_sd_api().show_error(
|
||||
file_parser.get_sd_api().context, "Cannot write\nto key file");
|
||||
file_parser.get_fs_api().file.close(&file);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fs_api->file.close(&file);
|
||||
sd_ex_api->check_error(sd_ex_api->context);
|
||||
file_parser.get_fs_api().file.close(&file);
|
||||
file_parser.get_sd_api().check_error(file_parser.get_sd_api().context);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IrdaAppRemoteManager::parse_button(std::string& str) {
|
||||
char button_name[32];
|
||||
char protocol_name[32];
|
||||
uint32_t address;
|
||||
uint32_t command;
|
||||
|
||||
int parsed = std::sscanf(
|
||||
str.c_str(), "%31s %31s A:%lX C:%lX", button_name, protocol_name, &address, &command);
|
||||
|
||||
if(parsed != 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IrdaProtocol protocol = irda_get_protocol_by_name(protocol_name);
|
||||
|
||||
if(!irda_is_protocol_valid((IrdaProtocol)protocol)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int address_length = irda_get_protocol_address_length(protocol);
|
||||
uint32_t address_mask = (1LU << (4 * address_length)) - 1;
|
||||
if(address != (address & address_mask)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int command_length = irda_get_protocol_command_length(protocol);
|
||||
uint32_t command_mask = (1LU << (4 * command_length)) - 1;
|
||||
if(command != (command & command_mask)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IrdaMessage irda_message = {
|
||||
.protocol = protocol,
|
||||
.address = address,
|
||||
.command = command,
|
||||
.repeat = false,
|
||||
};
|
||||
remote->buttons.emplace_back(button_name, &irda_message);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string getline(
|
||||
const FS_Api* fs_api,
|
||||
File& file,
|
||||
char file_buf[],
|
||||
size_t file_buf_size,
|
||||
size_t& file_buf_cnt) {
|
||||
std::string str;
|
||||
size_t newline_index = 0;
|
||||
bool found_eol = false;
|
||||
|
||||
while(1) {
|
||||
if(file_buf_cnt > 0) {
|
||||
size_t end_index = 0;
|
||||
char* endline_ptr = (char*)memchr(file_buf, '\n', file_buf_cnt);
|
||||
newline_index = endline_ptr - file_buf;
|
||||
|
||||
if(endline_ptr == 0) {
|
||||
end_index = file_buf_cnt;
|
||||
} else if(newline_index < file_buf_cnt) {
|
||||
end_index = newline_index + 1;
|
||||
found_eol = true;
|
||||
} else {
|
||||
furi_assert(0);
|
||||
}
|
||||
|
||||
str.append(file_buf, end_index);
|
||||
memmove(file_buf, &file_buf[end_index], file_buf_cnt - end_index);
|
||||
file_buf_cnt = file_buf_cnt - end_index;
|
||||
if(found_eol) break;
|
||||
}
|
||||
|
||||
file_buf_cnt +=
|
||||
fs_api->file.read(&file, &file_buf[file_buf_cnt], file_buf_size - file_buf_cnt);
|
||||
if(file_buf_cnt == 0) {
|
||||
break; // end of reading
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
bool IrdaAppRemoteManager::get_remote_list(std::vector<std::string>& remote_names) const {
|
||||
bool fs_res = false;
|
||||
char name[128];
|
||||
@ -314,17 +225,19 @@ bool IrdaAppRemoteManager::get_remote_list(std::vector<std::string>& remote_name
|
||||
std::string dirname(std::string("/") + irda_directory);
|
||||
remote_names.clear();
|
||||
|
||||
fs_res = fs_api->dir.open(&dir, dirname.c_str());
|
||||
IrdaAppFileParser file_parser;
|
||||
fs_res = file_parser.get_fs_api().dir.open(&dir, dirname.c_str());
|
||||
if(!fs_res) {
|
||||
if(!check_fs()) {
|
||||
show_file_error_message("Cannot open\napplication directory");
|
||||
file_parser.get_sd_api().show_error(
|
||||
file_parser.get_sd_api().context, "Cannot open\napplication directory");
|
||||
return false;
|
||||
} else {
|
||||
return true; // SD ok, but no files written yet
|
||||
}
|
||||
}
|
||||
|
||||
while(fs_api->dir.read(&dir, nullptr, name, sizeof(name)) && strlen(name)) {
|
||||
while(file_parser.get_fs_api().dir.read(&dir, nullptr, name, sizeof(name)) && strlen(name)) {
|
||||
std::string filename(name);
|
||||
auto extension_index = filename.rfind(irda_extension);
|
||||
if((extension_index == std::string::npos) ||
|
||||
@ -333,36 +246,41 @@ bool IrdaAppRemoteManager::get_remote_list(std::vector<std::string>& remote_name
|
||||
}
|
||||
remote_names.push_back(filename.erase(extension_index));
|
||||
}
|
||||
fs_api->dir.close(&dir);
|
||||
file_parser.get_fs_api().dir.close(&dir);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IrdaAppRemoteManager::load(const std::string& name) {
|
||||
bool fs_res = false;
|
||||
IrdaAppFileParser file_parser;
|
||||
File file;
|
||||
|
||||
fs_res = fs_api->file.open(&file, make_filename(name).c_str(), FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
fs_res = file_parser.get_fs_api().file.open(
|
||||
&file, make_filename(name).c_str(), FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
if(!fs_res) {
|
||||
show_file_error_message("Error opening file");
|
||||
file_parser.get_sd_api().show_error(
|
||||
file_parser.get_sd_api().context, "Error opening file");
|
||||
return false;
|
||||
}
|
||||
|
||||
remote = std::make_unique<IrdaAppRemote>(name);
|
||||
|
||||
while(1) {
|
||||
auto str = getline(fs_api, file, file_buf, sizeof(file_buf), file_buf_cnt);
|
||||
if(str.empty()) break;
|
||||
parse_button(str);
|
||||
auto message = file_parser.read_message(&file);
|
||||
if(!message) break;
|
||||
remote->buttons.emplace_back(message->name, &message->message);
|
||||
}
|
||||
fs_api->file.close(&file);
|
||||
file_parser.get_fs_api().file.close(&file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IrdaAppRemoteManager::check_fs() const {
|
||||
// TODO: [FL-1431] Add return value to sd_ex_api->check_error() and replace get_fs_info().
|
||||
auto fs_err = fs_api->common.get_fs_info(nullptr, nullptr);
|
||||
if(fs_err != FSE_OK) show_file_error_message("SD card not found");
|
||||
// TODO: [FL-1431] Add return value to file_parser.get_sd_api().check_error() and replace get_fs_info().
|
||||
IrdaAppFileParser file_parser;
|
||||
auto fs_err = file_parser.get_fs_api().common.get_fs_info(nullptr, nullptr);
|
||||
if(fs_err != FSE_OK)
|
||||
file_parser.get_sd_api().show_error(file_parser.get_sd_api().context, "SD card not found");
|
||||
return fs_err == FSE_OK;
|
||||
}
|
||||
|
@ -1,9 +1,6 @@
|
||||
#pragma once
|
||||
#include "sys/_stdint.h"
|
||||
#include <algorithm>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <irda.h>
|
||||
@ -38,14 +35,7 @@ class IrdaAppRemoteManager {
|
||||
static const char* irda_directory;
|
||||
static const char* irda_extension;
|
||||
std::unique_ptr<IrdaAppRemote> remote;
|
||||
// TODO: make FS_Api and SdCard_Api unique_ptr
|
||||
SdCard_Api* sd_ex_api;
|
||||
FS_Api* fs_api;
|
||||
void show_file_error_message(const char* error_text) const;
|
||||
bool parse_button(std::string& str);
|
||||
std::string make_filename(const std::string& name) const;
|
||||
char file_buf[48];
|
||||
size_t file_buf_cnt = 0;
|
||||
|
||||
public:
|
||||
bool add_remote_with_button(const char* button_name, const IrdaMessage* message);
|
||||
@ -63,8 +53,6 @@ public:
|
||||
const IrdaMessage* get_button_data(size_t button_index) const;
|
||||
bool delete_button(uint32_t index);
|
||||
bool delete_remote();
|
||||
IrdaAppRemoteManager();
|
||||
~IrdaAppRemoteManager();
|
||||
|
||||
bool store();
|
||||
bool load(const std::string& name);
|
||||
|
@ -2,10 +2,10 @@
|
||||
#include "irda.h"
|
||||
#include <api-hal-irda.h>
|
||||
|
||||
void IrdaAppSignalReceiver::irda_rx_callback(void* ctx, bool level, uint32_t duration) {
|
||||
void IrdaAppSignalTransceiver::irda_rx_callback(void* ctx, bool level, uint32_t duration) {
|
||||
IrdaAppEvent event;
|
||||
const IrdaMessage* irda_message;
|
||||
IrdaAppSignalReceiver* this_ = static_cast<IrdaAppSignalReceiver*>(ctx);
|
||||
IrdaAppSignalTransceiver* this_ = static_cast<IrdaAppSignalTransceiver*>(ctx);
|
||||
|
||||
irda_message = irda_decode(this_->decoder, level, duration);
|
||||
if(irda_message) {
|
||||
@ -17,30 +17,30 @@ void IrdaAppSignalReceiver::irda_rx_callback(void* ctx, bool level, uint32_t dur
|
||||
}
|
||||
}
|
||||
|
||||
IrdaAppSignalReceiver::IrdaAppSignalReceiver(void)
|
||||
IrdaAppSignalTransceiver::IrdaAppSignalTransceiver(void)
|
||||
: decoder(irda_alloc_decoder()) {
|
||||
}
|
||||
|
||||
IrdaAppSignalReceiver::~IrdaAppSignalReceiver() {
|
||||
IrdaAppSignalTransceiver::~IrdaAppSignalTransceiver() {
|
||||
api_hal_irda_rx_irq_deinit();
|
||||
irda_free_decoder(decoder);
|
||||
}
|
||||
|
||||
void IrdaAppSignalReceiver::capture_once_start(osMessageQueueId_t queue) {
|
||||
void IrdaAppSignalTransceiver::capture_once_start(osMessageQueueId_t queue) {
|
||||
event_queue = queue;
|
||||
irda_reset_decoder(decoder);
|
||||
api_hal_irda_rx_irq_init();
|
||||
api_hal_irda_rx_irq_set_callback(IrdaAppSignalReceiver::irda_rx_callback, this);
|
||||
api_hal_irda_rx_irq_set_callback(IrdaAppSignalTransceiver::irda_rx_callback, this);
|
||||
}
|
||||
|
||||
void IrdaAppSignalReceiver::capture_stop(void) {
|
||||
void IrdaAppSignalTransceiver::capture_stop(void) {
|
||||
api_hal_irda_rx_irq_deinit();
|
||||
}
|
||||
|
||||
IrdaMessage* IrdaAppSignalReceiver::get_last_message(void) {
|
||||
IrdaMessage* IrdaAppSignalTransceiver::get_last_message(void) {
|
||||
return &message;
|
||||
}
|
||||
|
||||
void IrdaAppSignalReceiver::send_message(const IrdaMessage* message) {
|
||||
void IrdaAppSignalTransceiver::send_message(const IrdaMessage* message) const {
|
||||
irda_send(message, 1);
|
||||
}
|
@ -1,14 +1,15 @@
|
||||
#pragma once
|
||||
#include <furi.h>
|
||||
#include <irda.h>
|
||||
|
||||
class IrdaAppSignalReceiver {
|
||||
class IrdaAppSignalTransceiver {
|
||||
public:
|
||||
IrdaAppSignalReceiver(void);
|
||||
~IrdaAppSignalReceiver(void);
|
||||
IrdaAppSignalTransceiver(void);
|
||||
~IrdaAppSignalTransceiver(void);
|
||||
void capture_once_start(osMessageQueueId_t event_queue);
|
||||
void capture_stop(void);
|
||||
IrdaMessage* get_last_message(void);
|
||||
void send_message(const IrdaMessage* message);
|
||||
void send_message(const IrdaMessage* message) const;
|
||||
|
||||
private:
|
||||
osMessageQueueId_t event_queue;
|
@ -1,7 +1,5 @@
|
||||
#include "furi.h"
|
||||
#include "gui/modules/button_menu.h"
|
||||
#include "gui/modules/dialog_ex.h"
|
||||
#include "gui/modules/text_input.h"
|
||||
#include "gui/modules/button_panel.h"
|
||||
#include "irda-app.hpp"
|
||||
#include <callback-connector.h>
|
||||
|
||||
@ -19,13 +17,17 @@ IrdaAppViewManager::IrdaAppViewManager() {
|
||||
popup = popup_alloc();
|
||||
dialog_ex = dialog_ex_alloc();
|
||||
text_input = text_input_alloc();
|
||||
button_panel = button_panel_alloc();
|
||||
popup_brut = popup_brut_alloc();
|
||||
|
||||
add_view(ViewType::ButtonPanel, button_panel_get_view(button_panel));
|
||||
add_view(ViewType::ButtonMenu, button_menu_get_view(button_menu));
|
||||
add_view(ViewType::Submenu, submenu_get_view(submenu));
|
||||
add_view(ViewType::Popup, popup_get_view(popup));
|
||||
add_view(ViewType::DialogEx, dialog_ex_get_view(dialog_ex));
|
||||
add_view(ViewType::TextInput, text_input_get_view(text_input));
|
||||
|
||||
view_set_previous_callback(button_panel_get_view(button_panel), callback);
|
||||
view_set_previous_callback(button_menu_get_view(button_menu), callback);
|
||||
view_set_previous_callback(submenu_get_view(submenu), callback);
|
||||
view_set_previous_callback(popup_get_view(popup), callback);
|
||||
@ -34,6 +36,8 @@ IrdaAppViewManager::IrdaAppViewManager() {
|
||||
}
|
||||
|
||||
IrdaAppViewManager::~IrdaAppViewManager() {
|
||||
view_dispatcher_remove_view(
|
||||
view_dispatcher, static_cast<uint32_t>(IrdaAppViewManager::ViewType::ButtonPanel));
|
||||
view_dispatcher_remove_view(
|
||||
view_dispatcher, static_cast<uint32_t>(IrdaAppViewManager::ViewType::ButtonMenu));
|
||||
view_dispatcher_remove_view(
|
||||
@ -47,9 +51,11 @@ IrdaAppViewManager::~IrdaAppViewManager() {
|
||||
|
||||
submenu_free(submenu);
|
||||
popup_free(popup);
|
||||
button_panel_free(button_panel);
|
||||
button_menu_free(button_menu);
|
||||
dialog_ex_free(dialog_ex);
|
||||
text_input_free(text_input);
|
||||
popup_brut_free(popup_brut);
|
||||
|
||||
view_dispatcher_free(view_dispatcher);
|
||||
furi_record_close("gui");
|
||||
@ -80,6 +86,14 @@ ButtonMenu* IrdaAppViewManager::get_button_menu() {
|
||||
return button_menu;
|
||||
}
|
||||
|
||||
ButtonPanel* IrdaAppViewManager::get_button_panel() {
|
||||
return button_panel;
|
||||
}
|
||||
|
||||
IrdaAppPopupBrut* IrdaAppViewManager::get_popup_brut() {
|
||||
return popup_brut;
|
||||
}
|
||||
|
||||
osMessageQueueId_t IrdaAppViewManager::get_event_queue() {
|
||||
return event_queue;
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/popup.h>
|
||||
#include "irda-app.hpp"
|
||||
#include "view/irda-app-brut-view.h"
|
||||
#include "gui/modules/button_panel.h"
|
||||
|
||||
class IrdaAppViewManager {
|
||||
public:
|
||||
@ -15,6 +17,7 @@ public:
|
||||
TextInput,
|
||||
Submenu,
|
||||
ButtonMenu,
|
||||
ButtonPanel,
|
||||
Popup,
|
||||
};
|
||||
|
||||
@ -31,6 +34,8 @@ public:
|
||||
Popup* get_popup();
|
||||
TextInput* get_text_input();
|
||||
ButtonMenu* get_button_menu();
|
||||
ButtonPanel* get_button_panel();
|
||||
IrdaAppPopupBrut* get_popup_brut();
|
||||
|
||||
osMessageQueueId_t get_event_queue();
|
||||
|
||||
@ -44,6 +49,8 @@ private:
|
||||
Submenu* submenu;
|
||||
Popup* popup;
|
||||
ButtonMenu* button_menu;
|
||||
ButtonPanel* button_panel;
|
||||
IrdaAppPopupBrut* popup_brut;
|
||||
|
||||
osMessageQueueId_t event_queue;
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
#include "irda-app.hpp"
|
||||
#include "sys/_stdint.h"
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <callback-connector.h>
|
||||
|
||||
@ -101,8 +99,8 @@ IrdaAppRemoteManager* IrdaApp::get_remote_manager() {
|
||||
return &remote_manager;
|
||||
}
|
||||
|
||||
IrdaAppSignalReceiver* IrdaApp::get_receiver() {
|
||||
return &receiver;
|
||||
IrdaAppSignalTransceiver* IrdaApp::get_transceiver() {
|
||||
return &transceiver;
|
||||
}
|
||||
|
||||
void IrdaApp::set_text_store(uint8_t index, const char* text...) {
|
||||
|
@ -1,13 +1,13 @@
|
||||
#pragma once
|
||||
#include "sys/_stdint.h"
|
||||
#include <map>
|
||||
#include <irda.h>
|
||||
#include <furi.h>
|
||||
#include "irda/scene/irda-app-scene.hpp"
|
||||
#include "irda-app-event.hpp"
|
||||
#include "scene/irda-app-scene.hpp"
|
||||
#include "irda-app-view-manager.hpp"
|
||||
#include "irda-app-remote-manager.hpp"
|
||||
#include "irda-app-receiver.hpp"
|
||||
#include "irda-app-transceiver.hpp"
|
||||
#include <forward_list>
|
||||
#include <stdint.h>
|
||||
#include <notification/notification-messages.h>
|
||||
@ -51,7 +51,7 @@ public:
|
||||
bool switch_to_previous_scene(uint8_t count = 1);
|
||||
Scene get_previous_scene();
|
||||
IrdaAppViewManager* get_view_manager();
|
||||
IrdaAppSignalReceiver* get_receiver();
|
||||
IrdaAppSignalTransceiver* get_transceiver();
|
||||
void set_text_store(uint8_t index, const char* text...);
|
||||
char* get_text_store(uint8_t index);
|
||||
uint8_t get_text_store_size();
|
||||
@ -103,7 +103,7 @@ private:
|
||||
uint32_t current_button;
|
||||
|
||||
NotificationApp* notification;
|
||||
IrdaAppSignalReceiver receiver;
|
||||
IrdaAppSignalTransceiver transceiver;
|
||||
IrdaAppViewManager view_manager;
|
||||
IrdaAppRemoteManager remote_manager;
|
||||
|
||||
@ -113,6 +113,8 @@ private:
|
||||
std::map<Scene, IrdaAppScene*> scenes = {
|
||||
{Scene::Start, new IrdaAppSceneStart()},
|
||||
{Scene::Universal, new IrdaAppSceneUniversal()},
|
||||
{Scene::UniversalTV, new IrdaAppSceneUniversalTV()},
|
||||
// {Scene::UniversalAudio, new IrdaAppSceneUniversalAudio()},
|
||||
{Scene::Learn, new IrdaAppSceneLearn()},
|
||||
{Scene::LearnSuccess, new IrdaAppSceneLearnSuccess()},
|
||||
{Scene::LearnEnterName, new IrdaAppSceneLearnEnterName()},
|
||||
|
@ -2,7 +2,6 @@
|
||||
#include "irda.h"
|
||||
#include "irda/scene/irda-app-scene.hpp"
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
|
||||
static void dialog_result_callback(DialogExResult result, void* context) {
|
||||
auto app = static_cast<IrdaApp*>(context);
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include "../irda-app.hpp"
|
||||
#include <cstdio>
|
||||
|
||||
void IrdaAppSceneEditRename::on_enter(IrdaApp* app) {
|
||||
IrdaAppViewManager* view_manager = app->get_view_manager();
|
||||
|
@ -1,6 +1,4 @@
|
||||
#include "../irda-app.hpp"
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <gui/modules/popup.h>
|
||||
|
||||
void IrdaAppSceneLearnDoneAfter::on_enter(IrdaApp* app) {
|
||||
|
@ -1,6 +1,4 @@
|
||||
#include "../irda-app.hpp"
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
|
||||
void IrdaAppSceneLearnDone::on_enter(IrdaApp* app) {
|
||||
IrdaAppViewManager* view_manager = app->get_view_manager();
|
||||
|
@ -1,15 +1,12 @@
|
||||
#include "../irda-app.hpp"
|
||||
#include "gui/modules/text_input.h"
|
||||
#include <callback-connector.h>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
|
||||
void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) {
|
||||
IrdaAppViewManager* view_manager = app->get_view_manager();
|
||||
TextInput* text_input = view_manager->get_text_input();
|
||||
|
||||
auto receiver = app->get_receiver();
|
||||
auto message = receiver->get_last_message();
|
||||
auto transceiver = app->get_transceiver();
|
||||
auto message = transceiver->get_last_message();
|
||||
|
||||
app->set_text_store(
|
||||
0,
|
||||
@ -34,14 +31,14 @@ bool IrdaAppSceneLearnEnterName::on_event(IrdaApp* app, IrdaAppEvent* event) {
|
||||
|
||||
if(event->type == IrdaAppEvent::Type::TextEditDone) {
|
||||
auto remote_manager = app->get_remote_manager();
|
||||
auto receiver = app->get_receiver();
|
||||
auto transceiver = app->get_transceiver();
|
||||
bool result = false;
|
||||
if(app->get_learn_new_remote()) {
|
||||
result = remote_manager->add_remote_with_button(
|
||||
app->get_text_store(0), receiver->get_last_message());
|
||||
app->get_text_store(0), transceiver->get_last_message());
|
||||
} else {
|
||||
result =
|
||||
remote_manager->add_button(app->get_text_store(0), receiver->get_last_message());
|
||||
result = remote_manager->add_button(
|
||||
app->get_text_store(0), transceiver->get_last_message());
|
||||
}
|
||||
|
||||
if(!result) {
|
||||
|
@ -1,7 +1,5 @@
|
||||
#include "../irda-app.hpp"
|
||||
#include "irda.h"
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
|
||||
static void dialog_result_callback(DialogExResult result, void* context) {
|
||||
auto app = static_cast<IrdaApp*>(context);
|
||||
@ -19,8 +17,8 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) {
|
||||
|
||||
app->notify_green_on();
|
||||
|
||||
auto receiver = app->get_receiver();
|
||||
auto message = receiver->get_last_message();
|
||||
auto transceiver = app->get_transceiver();
|
||||
auto message = transceiver->get_last_message();
|
||||
|
||||
app->set_text_store(0, "%s", irda_get_protocol_name(message->protocol));
|
||||
app->set_text_store(
|
||||
@ -52,8 +50,8 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) {
|
||||
break;
|
||||
case DialogExResultCenter: {
|
||||
app->notify_space_blink();
|
||||
auto receiver = app->get_receiver();
|
||||
auto message = receiver->get_last_message();
|
||||
auto transceiver = app->get_transceiver();
|
||||
auto message = transceiver->get_last_message();
|
||||
irda_send(message, 1);
|
||||
break;
|
||||
}
|
||||
|
@ -2,10 +2,10 @@
|
||||
|
||||
void IrdaAppSceneLearn::on_enter(IrdaApp* app) {
|
||||
auto view_manager = app->get_view_manager();
|
||||
auto receiver = app->get_receiver();
|
||||
auto transceiver = app->get_transceiver();
|
||||
auto event_queue = view_manager->get_event_queue();
|
||||
|
||||
receiver->capture_once_start(event_queue);
|
||||
transceiver->capture_once_start(event_queue);
|
||||
|
||||
auto popup = view_manager->get_popup();
|
||||
|
||||
|
@ -65,7 +65,7 @@ bool IrdaAppSceneRemote::on_event(IrdaApp* app, IrdaAppEvent* event) {
|
||||
app->notify_click_and_blink();
|
||||
auto remote_manager = app->get_remote_manager();
|
||||
auto message = remote_manager->get_button_data(event->payload.menu_index);
|
||||
app->get_receiver()->send_message(message);
|
||||
app->get_transceiver()->send_message(message);
|
||||
break;
|
||||
}
|
||||
} else if(event->type == IrdaAppEvent::Type::Back) {
|
||||
|
98
applications/irda/scene/irda-app-scene-universal-common.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
#include "../irda-app.hpp"
|
||||
#include "assets_icons.h"
|
||||
#include "gui/modules/button_menu.h"
|
||||
#include "gui/modules/button_panel.h"
|
||||
#include "../view/irda-app-brut-view.h"
|
||||
#include "gui/view.h"
|
||||
#include "irda/irda-app-view-manager.hpp"
|
||||
#include "irda/scene/irda-app-scene.hpp"
|
||||
|
||||
void IrdaAppSceneUniversalCommon::irda_app_item_callback(void* context, uint32_t index) {
|
||||
IrdaApp* app = static_cast<IrdaApp*>(context);
|
||||
IrdaAppEvent event;
|
||||
|
||||
event.type = IrdaAppEvent::Type::ButtonPanelPressed;
|
||||
event.payload.menu_index = index;
|
||||
|
||||
app->get_view_manager()->send_event(&event);
|
||||
}
|
||||
|
||||
static bool irda_popup_brut_input_callback(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
furi_assert(event);
|
||||
auto app = static_cast<IrdaApp*>(context);
|
||||
bool consumed = false;
|
||||
|
||||
if((event->type == InputTypeShort) && (event->key == InputKeyBack)) {
|
||||
consumed = true;
|
||||
IrdaAppEvent irda_event;
|
||||
|
||||
irda_event.type = IrdaAppEvent::Type::ButtonPanelPopupBackPressed;
|
||||
app->get_view_manager()->send_event(&irda_event);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void IrdaAppSceneUniversalCommon::remove_popup(IrdaApp* app) {
|
||||
auto button_panel = app->get_view_manager()->get_button_panel();
|
||||
button_panel_set_popup_draw_callback(button_panel, NULL, NULL);
|
||||
button_panel_set_popup_input_callback(button_panel, NULL, NULL);
|
||||
}
|
||||
|
||||
void IrdaAppSceneUniversalCommon::show_popup(IrdaApp* app, int record_amount) {
|
||||
auto button_panel = app->get_view_manager()->get_button_panel();
|
||||
auto popup_brut = app->get_view_manager()->get_popup_brut();
|
||||
popup_brut_set_progress_max(popup_brut, record_amount);
|
||||
button_panel_set_popup_draw_callback(button_panel, popup_brut_draw_callback, popup_brut);
|
||||
button_panel_set_popup_input_callback(button_panel, irda_popup_brut_input_callback, app);
|
||||
}
|
||||
|
||||
void IrdaAppSceneUniversalCommon::progress_popup(IrdaApp* app) {
|
||||
popup_brut_increase_progress(app->get_view_manager()->get_popup_brut());
|
||||
auto button_panel = app->get_view_manager()->get_button_panel();
|
||||
with_view_model_cpp(button_panel_get_view(button_panel), void*, model, { return true; });
|
||||
}
|
||||
|
||||
bool IrdaAppSceneUniversalCommon::on_event(IrdaApp* app, IrdaAppEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == IrdaAppEvent::Type::Tick) {
|
||||
if(brute_force_started) {
|
||||
if(brute_force.send_next_bruteforce(*app->get_transceiver())) {
|
||||
progress_popup(app);
|
||||
} else {
|
||||
brute_force.stop_bruteforce();
|
||||
brute_force_started = false;
|
||||
remove_popup(app);
|
||||
}
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
if(event->type == IrdaAppEvent::Type::ButtonPanelPopupBackPressed) {
|
||||
consumed = true;
|
||||
brute_force_started = false;
|
||||
brute_force.stop_bruteforce();
|
||||
remove_popup(app);
|
||||
} else if(event->type == IrdaAppEvent::Type::ButtonPanelPressed) {
|
||||
int record_amount = 0;
|
||||
if(brute_force.start_bruteforce(event->payload.menu_index, record_amount)) {
|
||||
if(record_amount > 0) {
|
||||
brute_force_started = true;
|
||||
show_popup(app, record_amount);
|
||||
}
|
||||
} else {
|
||||
app->switch_to_previous_scene();
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void IrdaAppSceneUniversalCommon::on_exit(IrdaApp* app) {
|
||||
IrdaAppViewManager* view_manager = app->get_view_manager();
|
||||
ButtonPanel* button_panel = view_manager->get_button_panel();
|
||||
button_panel_clean(button_panel);
|
||||
}
|
61
applications/irda/scene/irda-app-scene-universal-tv.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
#include "irda/scene/irda-app-scene.hpp"
|
||||
#include "irda/irda-app.hpp"
|
||||
|
||||
void IrdaAppSceneUniversalTV::on_enter(IrdaApp* app) {
|
||||
IrdaAppViewManager* view_manager = app->get_view_manager();
|
||||
ButtonPanel* button_panel = view_manager->get_button_panel();
|
||||
button_panel_reserve(button_panel, 2, 3);
|
||||
|
||||
int i = 0;
|
||||
button_panel_add_item(
|
||||
button_panel, i, 0, 0, 3, 19, I_Power_25x27, I_Power_hvr_25x27, irda_app_item_callback, app);
|
||||
brute_force.add_record(i, "POWER");
|
||||
++i;
|
||||
button_panel_add_item(
|
||||
button_panel, i, 1, 0, 36, 19, I_Mute_25x27, I_Mute_hvr_25x27, irda_app_item_callback, app);
|
||||
brute_force.add_record(i, "MUTE");
|
||||
++i;
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
66,
|
||||
I_Vol_up_25x27,
|
||||
I_Vol_up_hvr_25x27,
|
||||
irda_app_item_callback,
|
||||
app);
|
||||
brute_force.add_record(i, "VOL+");
|
||||
++i;
|
||||
button_panel_add_item(
|
||||
button_panel, i, 1, 1, 36, 66, I_Up_25x27, I_Up_hvr_25x27, irda_app_item_callback, app);
|
||||
brute_force.add_record(i, "CH+");
|
||||
++i;
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
98,
|
||||
I_Vol_down_25x27,
|
||||
I_Vol_down_hvr_25x27,
|
||||
irda_app_item_callback,
|
||||
app);
|
||||
brute_force.add_record(i, "VOL-");
|
||||
++i;
|
||||
button_panel_add_item(
|
||||
button_panel, i, 1, 2, 36, 98, I_Down_25x27, I_Down_hvr_25x27, irda_app_item_callback, app);
|
||||
brute_force.add_record(i, "CH-");
|
||||
|
||||
button_panel_add_label(button_panel, 6, 11, FontPrimary, "TV remote");
|
||||
button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol");
|
||||
button_panel_add_label(button_panel, 43, 64, FontSecondary, "Ch");
|
||||
|
||||
view_manager->switch_to(IrdaAppViewManager::ViewType::ButtonPanel);
|
||||
|
||||
if(!brute_force.calculate_messages()) {
|
||||
app->switch_to_previous_scene();
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ bool IrdaAppSceneUniversal::on_event(IrdaApp* app, IrdaAppEvent* event) {
|
||||
submenu_item_selected = event->payload.menu_index;
|
||||
switch(event->payload.menu_index) {
|
||||
case SubmenuIndexUniversalTV:
|
||||
// app->switch_to_next_scene(IrdaApp::Scene::UniversalTV);
|
||||
app->switch_to_next_scene(IrdaApp::Scene::UniversalTV);
|
||||
break;
|
||||
case SubmenuIndexUniversalAudio:
|
||||
// app->switch_to_next_scene(IrdaApp::Scene::UniversalAudio);
|
||||
|
@ -1,10 +1,11 @@
|
||||
#pragma once
|
||||
#include "../irda-app.hpp"
|
||||
#include "../irda-app-event.hpp"
|
||||
#include <api-hal-irda.h>
|
||||
#include "irda.h"
|
||||
#include <gui/elements.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "../irda-app-brute-force.hpp"
|
||||
|
||||
|
||||
class IrdaApp;
|
||||
|
||||
@ -137,3 +138,31 @@ public:
|
||||
void on_exit(IrdaApp* app) final;
|
||||
};
|
||||
|
||||
class IrdaAppSceneUniversalCommon : public IrdaAppScene {
|
||||
bool brute_force_started = false;
|
||||
protected:
|
||||
bool on_event(IrdaApp* app, IrdaAppEvent* event) final;
|
||||
void on_exit(IrdaApp* app) final;
|
||||
IrdaAppBruteForce brute_force;
|
||||
void remove_popup(IrdaApp* app);
|
||||
void show_popup(IrdaApp* app, int record_amount);
|
||||
void progress_popup(IrdaApp* app);
|
||||
static void irda_app_item_callback(void* context, uint32_t index);
|
||||
IrdaAppSceneUniversalCommon(const char* filename) : brute_force(filename) {}
|
||||
~IrdaAppSceneUniversalCommon() {}
|
||||
};
|
||||
|
||||
class IrdaAppSceneUniversalTV : public IrdaAppSceneUniversalCommon {
|
||||
public:
|
||||
void on_enter(IrdaApp* app) final;
|
||||
IrdaAppSceneUniversalTV() : IrdaAppSceneUniversalCommon("/irda/universal/tv.ir") {}
|
||||
~IrdaAppSceneUniversalTV() {}
|
||||
};
|
||||
|
||||
class IrdaAppSceneUniversalAudio : public IrdaAppSceneUniversalCommon {
|
||||
public:
|
||||
void on_enter(IrdaApp* app) final;
|
||||
IrdaAppSceneUniversalAudio() : IrdaAppSceneUniversalCommon("/irda/universal/audio.ir") {}
|
||||
~IrdaAppSceneUniversalAudio() {}
|
||||
};
|
||||
|
||||
|
82
applications/irda/view/irda-app-brut-view.c
Normal file
@ -0,0 +1,82 @@
|
||||
#include "api-hal-resources.h"
|
||||
#include "assets_icons.h"
|
||||
#include "gui/canvas.h"
|
||||
#include "gui/view.h"
|
||||
#include "input/input.h"
|
||||
#include <gui/elements.h>
|
||||
#include <furi.h>
|
||||
#include "irda-app-brut-view.h"
|
||||
#include "gui/modules/button_panel.h"
|
||||
#include <stdint.h>
|
||||
|
||||
struct IrdaAppPopupBrut {
|
||||
uint16_t progress;
|
||||
uint16_t progress_max;
|
||||
char percents_string_storage[8];
|
||||
};
|
||||
|
||||
void popup_brut_increase_progress(IrdaAppPopupBrut* popup_brut) {
|
||||
furi_assert(popup_brut);
|
||||
|
||||
if(popup_brut->progress < popup_brut->progress_max)
|
||||
++popup_brut->progress;
|
||||
else
|
||||
furi_assert(0);
|
||||
}
|
||||
|
||||
void popup_brut_draw_callback(Canvas* canvas, void* context) {
|
||||
furi_assert(canvas);
|
||||
furi_assert(context);
|
||||
IrdaAppPopupBrut* popup_brut = (IrdaAppPopupBrut*)context;
|
||||
uint8_t x = 0;
|
||||
uint8_t width = 64;
|
||||
uint8_t x_max = x + width - 1;
|
||||
uint8_t y = 36;
|
||||
uint8_t height = 59;
|
||||
uint8_t y_max = y + height - 1;
|
||||
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_rbox(canvas, x + 1, y + 1, width - 2, height - 2, 3);
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_rframe(canvas, x, y, width, height, 3);
|
||||
canvas_draw_rframe(canvas, x + 1, y + 1, width - 2, height - 2, 3);
|
||||
canvas_draw_line(canvas, x + 2, y + 1, x + 2, y + 3);
|
||||
canvas_draw_line(canvas, x + 1, y + 2, x + 3, y + 2);
|
||||
canvas_draw_line(canvas, x_max - 2, y + 1, x_max - 2, y + 3);
|
||||
canvas_draw_line(canvas, x_max - 1, y + 2, x_max - 3, y + 2);
|
||||
canvas_draw_line(canvas, x + 2, y_max - 1, x + 2, y_max - 3);
|
||||
canvas_draw_line(canvas, x + 1, y_max - 2, x + 3, y_max - 2);
|
||||
canvas_draw_line(canvas, x_max - 2, y_max - 1, x_max - 2, y_max - 3);
|
||||
canvas_draw_line(canvas, x_max - 1, y_max - 2, x_max - 3, y_max - 2);
|
||||
|
||||
elements_progress_bar(
|
||||
canvas, x + 4, y + 19, x_max - 8, popup_brut->progress, popup_brut->progress_max);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, x + 15, y + 12, "Sending ...");
|
||||
canvas_draw_icon_name(canvas, x + 11, y_max - 14, I_Back_15x10);
|
||||
|
||||
uint8_t percent_value = 100 * popup_brut->progress / popup_brut->progress_max;
|
||||
snprintf(
|
||||
popup_brut->percents_string_storage,
|
||||
sizeof(popup_brut->percents_string_storage),
|
||||
"%d%%",
|
||||
percent_value);
|
||||
elements_multiline_text_aligned(
|
||||
canvas, x + 32, y + 40, AlignCenter, AlignBottom, popup_brut->percents_string_storage);
|
||||
canvas_draw_str(canvas, x + 30, y_max - 5, "= stop");
|
||||
}
|
||||
|
||||
void popup_brut_set_progress_max(IrdaAppPopupBrut* popup_brut, uint16_t progress_max) {
|
||||
furi_assert(popup_brut);
|
||||
popup_brut->progress = 0;
|
||||
popup_brut->progress_max = progress_max;
|
||||
}
|
||||
|
||||
IrdaAppPopupBrut* popup_brut_alloc(void) {
|
||||
return (IrdaAppPopupBrut*)furi_alloc(sizeof(IrdaAppPopupBrut));
|
||||
}
|
||||
|
||||
void popup_brut_free(IrdaAppPopupBrut* popup_brut) {
|
||||
free(popup_brut);
|
||||
}
|
18
applications/irda/view/irda-app-brut-view.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include <gui/view.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct IrdaAppPopupBrut IrdaAppPopupBrut;
|
||||
|
||||
void popup_brut_increase_progress(IrdaAppPopupBrut* popup_brut);
|
||||
IrdaAppPopupBrut* popup_brut_alloc();
|
||||
void popup_brut_free(IrdaAppPopupBrut* popup_brut);
|
||||
void popup_brut_draw_callback(Canvas* canvas, void* model);
|
||||
void popup_brut_set_progress_max(IrdaAppPopupBrut* popup_brut, uint16_t progress_max);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -3,129 +3,143 @@
|
||||
#include <gui/icon.h>
|
||||
|
||||
typedef enum {
|
||||
I_SDQuestion_35x43,
|
||||
I_SDError_43x35,
|
||||
I_Health_16x16,
|
||||
I_FaceCharging_29x14,
|
||||
I_BatteryBody_52x28,
|
||||
I_Voltage_16x16,
|
||||
I_Temperature_16x16,
|
||||
I_FaceNopower_29x14,
|
||||
I_FaceNormal_29x14,
|
||||
I_Battery_16x16,
|
||||
I_FaceConfused_29x14,
|
||||
I_PassportBottom_128x17,
|
||||
I_DoorLeft_8x56,
|
||||
I_DoorLocked_10x56,
|
||||
I_DoorRight_8x56,
|
||||
I_DoorLeft_70x55,
|
||||
I_PassportLeft_6x47,
|
||||
I_DoorRight_70x55,
|
||||
I_LockPopup_100x49,
|
||||
I_WalkR2_32x32,
|
||||
I_WalkL2_32x32,
|
||||
I_WalkRB1_32x32,
|
||||
I_Home_painting_17x20,
|
||||
I_WalkLB2_32x32,
|
||||
I_Sofa_40x13,
|
||||
I_WalkLB1_32x32,
|
||||
I_PC_22x29,
|
||||
I_WalkL1_32x32,
|
||||
I_TV_20x20,
|
||||
I_WalkR1_32x32,
|
||||
I_WalkRB2_32x32,
|
||||
I_TV_20x24,
|
||||
I_dir_10px,
|
||||
I_Nfc_10px,
|
||||
I_sub1_10px,
|
||||
I_ir_10px,
|
||||
I_ibutt_10px,
|
||||
I_unknown_10px,
|
||||
I_ble_10px,
|
||||
I_125_10px,
|
||||
I_FX_SittingB_40x27,
|
||||
I_BigGames_24x24,
|
||||
I_BigProfile_24x24,
|
||||
I_DolphinOkay_41x43,
|
||||
I_DolphinFirstStart5_45x53,
|
||||
I_DolphinFirstStart4_67x53,
|
||||
I_DolphinFirstStart2_59x51,
|
||||
I_DolphinFirstStart0_70x53,
|
||||
I_DolphinFirstStart6_58x54,
|
||||
I_DolphinFirstStart1_59x53,
|
||||
I_DolphinFirstStart8_56x51,
|
||||
I_DolphinFirstStart7_61x51,
|
||||
I_Flipper_young_80x60,
|
||||
I_BigBurger_24x24,
|
||||
I_FX_Bang_32x6,
|
||||
I_DolphinFirstStart3_57x48,
|
||||
I_BadUsb_9x8,
|
||||
I_PlaceholderR_30x13,
|
||||
I_Background_128x8,
|
||||
I_Lock_8x8,
|
||||
I_Battery_26x8,
|
||||
I_PlaceholderL_11x13,
|
||||
I_Battery_19x8,
|
||||
I_SDcardMounted_11x8,
|
||||
I_SDcardFail_11x8,
|
||||
I_USBConnected_15x8,
|
||||
I_Bluetooth_5x8,
|
||||
I_Background_128x11,
|
||||
I_IrdaArrowUp_4x8,
|
||||
I_Down_hvr_25x27,
|
||||
I_Vol_down_hvr_25x27,
|
||||
I_Down_25x27,
|
||||
I_Fill_marker_7x7,
|
||||
I_Vol_down_25x27,
|
||||
I_Vol_up_25x27,
|
||||
I_Up_hvr_25x27,
|
||||
I_Vol_up_hvr_25x27,
|
||||
I_IrdaLearnShort_128x31,
|
||||
I_IrdaSend_128x64,
|
||||
I_Mute_hvr_25x27,
|
||||
I_Back_15x10,
|
||||
I_Up_25x27,
|
||||
I_IrdaArrowUp_4x8,
|
||||
I_Mute_25x27,
|
||||
I_Power_25x27,
|
||||
I_IrdaSendShort_128x34,
|
||||
I_IrdaArrowDown_4x8,
|
||||
I_IrdaLearn_128x64,
|
||||
I_IrdaSend_128x64,
|
||||
I_IrdaSendShort_128x34,
|
||||
I_passport_happy1_43x45,
|
||||
I_passport_bad3_43x45,
|
||||
I_passport_okay2_43x45,
|
||||
I_passport_bad2_43x45,
|
||||
I_passport_okay3_43x45,
|
||||
I_passport_bad1_43x45,
|
||||
I_passport_happy3_43x45,
|
||||
I_passport_happy2_43x45,
|
||||
I_passport_okay1_43x45,
|
||||
I_ButtonRightSmall_3x5,
|
||||
I_ButtonLeft_4x7,
|
||||
I_ButtonLeftSmall_3x5,
|
||||
I_ButtonRight_4x7,
|
||||
I_ButtonCenter_7x7,
|
||||
I_Power_hvr_25x27,
|
||||
A_Games_14,
|
||||
A_Plugins_14,
|
||||
A_Power_14,
|
||||
A_GPIO_14,
|
||||
A_Bluetooth_14,
|
||||
A_Passport_14,
|
||||
A_Sub1ghz_14,
|
||||
A_NFC_14,
|
||||
A_Tamagotchi_14,
|
||||
A_FileManager_14,
|
||||
A_125khz_14,
|
||||
A_U2F_14,
|
||||
A_Infrared_14,
|
||||
A_Power_14,
|
||||
A_Settings_14,
|
||||
A_125khz_14,
|
||||
A_iButton_14,
|
||||
A_Bluetooth_14,
|
||||
A_GPIO_14,
|
||||
I_DolphinMafia_115x62,
|
||||
I_DolphinExcited_64x63,
|
||||
I_iButtonDolphinSuccess_109x60,
|
||||
I_iButtonDolphinVerySuccess_108x52,
|
||||
A_FileManager_14,
|
||||
A_Tamagotchi_14,
|
||||
A_NFC_14,
|
||||
A_Plugins_14,
|
||||
I_SDQuestion_35x43,
|
||||
I_SDError_43x35,
|
||||
I_BatteryBody_52x28,
|
||||
I_FaceCharging_29x14,
|
||||
I_Health_16x16,
|
||||
I_Temperature_16x16,
|
||||
I_Battery_16x16,
|
||||
I_FaceConfused_29x14,
|
||||
I_FaceNormal_29x14,
|
||||
I_Voltage_16x16,
|
||||
I_FaceNopower_29x14,
|
||||
I_iButtonKey_49x44,
|
||||
I_DolphinNice_96x59,
|
||||
I_DolphinExcited_64x63,
|
||||
I_DolphinWait_61x59,
|
||||
I_iButtonDolphinVerySuccess_108x52,
|
||||
I_DolphinMafia_115x62,
|
||||
I_DolphinNice_96x59,
|
||||
I_iButtonDolphinSuccess_109x60,
|
||||
I_Background_128x11,
|
||||
I_Lock_8x8,
|
||||
I_Battery_26x8,
|
||||
I_Battery_19x8,
|
||||
I_USBConnected_15x8,
|
||||
I_Background_128x8,
|
||||
I_BadUsb_9x8,
|
||||
I_PlaceholderL_11x13,
|
||||
I_SDcardFail_11x8,
|
||||
I_Bluetooth_5x8,
|
||||
I_PlaceholderR_30x13,
|
||||
I_SDcardMounted_11x8,
|
||||
I_WalkR2_32x32,
|
||||
I_WalkRB2_32x32,
|
||||
I_WalkR1_32x32,
|
||||
I_PC_22x29,
|
||||
I_WalkRB1_32x32,
|
||||
I_WalkL2_32x32,
|
||||
I_WalkLB1_32x32,
|
||||
I_WalkLB2_32x32,
|
||||
I_TV_20x20,
|
||||
I_TV_20x24,
|
||||
I_Home_painting_17x20,
|
||||
I_Sofa_40x13,
|
||||
I_WalkL1_32x32,
|
||||
I_passport_bad1_43x45,
|
||||
I_passport_bad3_43x45,
|
||||
I_passport_happy3_43x45,
|
||||
I_passport_happy2_43x45,
|
||||
I_passport_okay3_43x45,
|
||||
I_passport_okay2_43x45,
|
||||
I_passport_happy1_43x45,
|
||||
I_passport_bad2_43x45,
|
||||
I_passport_okay1_43x45,
|
||||
A_Wink_128x64,
|
||||
A_MDWL_32x32,
|
||||
A_MDWR_32x32,
|
||||
A_WatchingTV_128x64,
|
||||
A_MDI_32x32,
|
||||
A_MDWRB_32x32,
|
||||
A_MDIB_32x32,
|
||||
A_FX_Sitting_40x27,
|
||||
A_MDWR_32x32,
|
||||
A_MDWL_32x32,
|
||||
A_MDWRB_32x32,
|
||||
A_MDWLB_32x32,
|
||||
I_KeySave_24x11,
|
||||
I_KeyBackspaceSelected_16x9,
|
||||
A_MDIB_32x32,
|
||||
A_WatchingTV_128x64,
|
||||
I_PassportBottom_128x17,
|
||||
I_DoorLeft_70x55,
|
||||
I_DoorLeft_8x56,
|
||||
I_DoorRight_70x55,
|
||||
I_DoorRight_8x56,
|
||||
I_DoorLocked_10x56,
|
||||
I_PassportLeft_6x47,
|
||||
I_LockPopup_100x49,
|
||||
I_sub1_10px,
|
||||
I_ir_10px,
|
||||
I_unknown_10px,
|
||||
I_ibutt_10px,
|
||||
I_Nfc_10px,
|
||||
I_ble_10px,
|
||||
I_125_10px,
|
||||
I_dir_10px,
|
||||
I_ButtonCenter_7x7,
|
||||
I_ButtonLeft_4x7,
|
||||
I_ButtonLeftSmall_3x5,
|
||||
I_ButtonRightSmall_3x5,
|
||||
I_ButtonRight_4x7,
|
||||
I_DolphinFirstStart2_59x51,
|
||||
I_BigBurger_24x24,
|
||||
I_DolphinFirstStart6_58x54,
|
||||
I_Flipper_young_80x60,
|
||||
I_FX_Bang_32x6,
|
||||
I_DolphinFirstStart8_56x51,
|
||||
I_DolphinFirstStart1_59x53,
|
||||
I_DolphinOkay_41x43,
|
||||
I_DolphinFirstStart3_57x48,
|
||||
I_DolphinFirstStart5_45x53,
|
||||
I_DolphinFirstStart7_61x51,
|
||||
I_FX_SittingB_40x27,
|
||||
I_BigProfile_24x24,
|
||||
I_DolphinFirstStart0_70x53,
|
||||
I_BigGames_24x24,
|
||||
I_DolphinFirstStart4_67x53,
|
||||
I_KeySaveSelected_24x11,
|
||||
I_KeyBackspace_16x9,
|
||||
I_KeyBackspaceSelected_16x9,
|
||||
I_KeySave_24x11,
|
||||
} IconName;
|
||||
|
||||
Icon * assets_icons_get(IconName name);
|
||||
|
BIN
assets/icons/Irda/Back_15x10.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
assets/icons/Irda/Down_25x27.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
assets/icons/Irda/Down_hvr_25x27.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
assets/icons/Irda/Fill-marker_7x7.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
assets/icons/Irda/Mute_25x27.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
assets/icons/Irda/Mute_hvr_25x27.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
assets/icons/Irda/Power_25x27.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
assets/icons/Irda/Power_hvr_25x27.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
assets/icons/Irda/Up_25x27.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
assets/icons/Irda/Up_hvr_25x27.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
assets/icons/Irda/Vol_down_25x27.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
assets/icons/Irda/Vol_down_hvr_25x27.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
assets/icons/Irda/Vol_up_25x27.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
assets/icons/Irda/Vol_up_hvr_25x27.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
39
lib/file_reader/file_reader.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
#include "file_reader/file_reader.hpp"
|
||||
|
||||
std::string FileReader::getline(File* file) {
|
||||
std::string str;
|
||||
size_t newline_index = 0;
|
||||
bool found_eol = false;
|
||||
|
||||
while(1) {
|
||||
if(file_buf_cnt > 0) {
|
||||
size_t end_index = 0;
|
||||
char* endline_ptr = (char*)memchr(file_buf, '\n', file_buf_cnt);
|
||||
newline_index = endline_ptr - file_buf;
|
||||
|
||||
if(endline_ptr == 0) {
|
||||
end_index = file_buf_cnt;
|
||||
} else if(newline_index < file_buf_cnt) {
|
||||
end_index = newline_index + 1;
|
||||
found_eol = true;
|
||||
} else {
|
||||
furi_assert(0);
|
||||
}
|
||||
|
||||
str.append(file_buf, end_index);
|
||||
memmove(file_buf, &file_buf[end_index], file_buf_cnt - end_index);
|
||||
file_buf_cnt = file_buf_cnt - end_index;
|
||||
if(found_eol) break;
|
||||
}
|
||||
|
||||
file_buf_cnt +=
|
||||
fs_api->file.read(file, &file_buf[file_buf_cnt], sizeof(file_buf) - file_buf_cnt);
|
||||
if(file_buf_cnt == 0) {
|
||||
break; // end of reading
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
39
lib/file_reader/file_reader.hpp
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "sd-card-api.h"
|
||||
#include "filesystem-api.h"
|
||||
|
||||
class FileReader {
|
||||
private:
|
||||
char file_buf[48];
|
||||
size_t file_buf_cnt = 0;
|
||||
SdCard_Api* sd_ex_api;
|
||||
FS_Api* fs_api;
|
||||
|
||||
public:
|
||||
FileReader() {
|
||||
sd_ex_api = static_cast<SdCard_Api*>(furi_record_open("sdcard-ex"));
|
||||
fs_api = static_cast<FS_Api*>(furi_record_open("sdcard"));
|
||||
reset();
|
||||
}
|
||||
~FileReader() {
|
||||
furi_record_close("sdcard");
|
||||
furi_record_close("sdcard-ex");
|
||||
}
|
||||
|
||||
std::string getline(File* file);
|
||||
|
||||
void reset(void) {
|
||||
file_buf_cnt = 0;
|
||||
}
|
||||
|
||||
SdCard_Api& get_sd_api() {
|
||||
return *sd_ex_api;
|
||||
}
|
||||
|
||||
FS_Api& get_fs_api() {
|
||||
return *fs_api;
|
||||
}
|
||||
};
|
||||
|
@ -30,6 +30,7 @@ void irda_encoder_samsung32_encode(uint32_t addr, uint32_t cmd, bool repeat) {
|
||||
uint8_t command = cmd & 0xFF;
|
||||
uint8_t command_inverse = (uint8_t) ~command;
|
||||
|
||||
irda_encode_space(&encoder_timings, 100);
|
||||
if (!repeat) {
|
||||
irda_encode_samsung32_preamble();
|
||||
irda_encode_byte(&encoder_timings, address);
|
||||
|
@ -87,6 +87,10 @@ C_SOURCES += $(wildcard $(LIB_DIR)/drivers/*.c)
|
||||
CFLAGS += -I$(LIB_DIR)/version
|
||||
C_SOURCES += $(LIB_DIR)/version/version.c
|
||||
|
||||
#file reader
|
||||
CFLAGS += -I$(LIB_DIR)/file_reader
|
||||
CPP_SOURCES += $(wildcard $(LIB_DIR)/file_reader/*.cpp)
|
||||
|
||||
#irda lib
|
||||
CFLAGS += -I$(LIB_DIR)/irda
|
||||
C_SOURCES += $(wildcard $(LIB_DIR)/irda/*.c)
|
||||
|