[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 <string.h> | ||||||
| #include <stdint.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( | void elements_scrollbar_pos( | ||||||
|     Canvas* canvas, |     Canvas* canvas, | ||||||
|     uint8_t x, |     uint8_t x, | ||||||
|  | |||||||
| @ -8,6 +8,22 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #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. |  * Draw scrollbar on canvas at specific position. | ||||||
|  * @param x - scrollbar position on X axis |  * @param x - scrollbar position on X axis | ||||||
| @ -16,7 +32,6 @@ extern "C" { | |||||||
|  * @param pos - current element  |  * @param pos - current element  | ||||||
|  * @param total - total elements |  * @param total - total elements | ||||||
|  */ |  */ | ||||||
| 
 |  | ||||||
| void elements_scrollbar_pos( | void elements_scrollbar_pos( | ||||||
|     Canvas* canvas, |     Canvas* canvas, | ||||||
|     uint8_t x, |     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 | #pragma once | ||||||
| #include <stdint.h> |  | ||||||
| #include <irda.h> | #include <irda.h> | ||||||
| #include <gui/modules/dialog_ex.h> | #include <gui/modules/dialog_ex.h> | ||||||
| 
 | 
 | ||||||
| @ -14,6 +13,8 @@ public: | |||||||
|         IrdaMessageReceived, |         IrdaMessageReceived, | ||||||
|         TextEditDone, |         TextEditDone, | ||||||
|         PopupTimer, |         PopupTimer, | ||||||
|  |         ButtonPanelPressed, | ||||||
|  |         ButtonPanelPopupBackPressed, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     union { |     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 "furi/check.h" | ||||||
| #include "gui/modules/button_menu.h" | #include "gui/modules/button_menu.h" | ||||||
| #include "irda.h" | #include "irda.h" | ||||||
| #include "sys/_stdint.h" |  | ||||||
| #include <cstdio> | #include <cstdio> | ||||||
| #include <string> | #include <string> | ||||||
| #include <utility> | #include <utility> | ||||||
|  | #include "irda-app-file-parser.hpp" | ||||||
| 
 | 
 | ||||||
| const char* IrdaAppRemoteManager::irda_directory = "irda"; | const char* IrdaAppRemoteManager::irda_directory = "irda"; | ||||||
| const char* IrdaAppRemoteManager::irda_extension = ".ir"; | 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) { | bool IrdaAppRemoteManager::add_button(const char* button_name, const IrdaMessage* message) { | ||||||
|     remote->buttons.emplace_back(button_name, message); |     remote->buttons.emplace_back(button_name, message); | ||||||
|     return store(); |     return store(); | ||||||
| @ -94,10 +84,12 @@ std::string IrdaAppRemoteManager::make_filename(const std::string& name) const { | |||||||
| 
 | 
 | ||||||
| bool IrdaAppRemoteManager::delete_remote() { | bool IrdaAppRemoteManager::delete_remote() { | ||||||
|     FS_Error fs_res; |     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) { |     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; |         return false; | ||||||
|     } |     } | ||||||
|     remote.reset(); |     remote.reset(); | ||||||
| @ -147,11 +139,13 @@ bool IrdaAppRemoteManager::rename_remote(const char* str) { | |||||||
|     if(!result) return false; |     if(!result) return false; | ||||||
| 
 | 
 | ||||||
|     auto new_name = find_vacant_name(remote_list, str); |     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()); |         make_filename(remote->name).c_str(), make_filename(new_name).c_str()); | ||||||
|     remote->name = new_name; |     remote->name = new_name; | ||||||
|     if(fs_err != FSE_OK) { |     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; |     return fs_err == FSE_OK; | ||||||
| } | } | ||||||
| @ -170,26 +164,25 @@ size_t IrdaAppRemoteManager::get_number_of_buttons() { | |||||||
|     return remote->buttons.size(); |     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) { | bool IrdaAppRemoteManager::store(void) { | ||||||
|     File file; |     File file; | ||||||
|     uint16_t write_count; |     uint16_t write_count; | ||||||
|     std::string dirname(std::string("/") + irda_directory); |     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)) { |     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; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::string filename = dirname + "/" + remote->name + irda_extension; |     bool res = file_parser.get_fs_api().file.open( | ||||||
|     bool res = fs_api->file.open(&file, filename.c_str(), FSAM_WRITE, FSOM_CREATE_ALWAYS); |         &file, make_filename(remote->name).c_str(), FSAM_WRITE, FSOM_CREATE_ALWAYS); | ||||||
| 
 | 
 | ||||||
|     if(!res) { |     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; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -210,103 +203,21 @@ bool IrdaAppRemoteManager::store(void) { | |||||||
|             button.message.command); |             button.message.command); | ||||||
| 
 | 
 | ||||||
|         auto content_len = strlen(content); |         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) { |         if(file.error_id != FSE_OK || write_count != content_len) { | ||||||
|             show_file_error_message("Cannot write\nto key file"); |             file_parser.get_sd_api().show_error( | ||||||
|             fs_api->file.close(&file); |                 file_parser.get_sd_api().context, "Cannot write\nto key file"); | ||||||
|  |             file_parser.get_fs_api().file.close(&file); | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fs_api->file.close(&file); |     file_parser.get_fs_api().file.close(&file); | ||||||
|     sd_ex_api->check_error(sd_ex_api->context); |     file_parser.get_sd_api().check_error(file_parser.get_sd_api().context); | ||||||
| 
 | 
 | ||||||
|     return true; |     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 IrdaAppRemoteManager::get_remote_list(std::vector<std::string>& remote_names) const { | ||||||
|     bool fs_res = false; |     bool fs_res = false; | ||||||
|     char name[128]; |     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); |     std::string dirname(std::string("/") + irda_directory); | ||||||
|     remote_names.clear(); |     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(!fs_res) { | ||||||
|         if(!check_fs()) { |         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; |             return false; | ||||||
|         } else { |         } else { | ||||||
|             return true; // SD ok, but no files written yet
 |             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); |         std::string filename(name); | ||||||
|         auto extension_index = filename.rfind(irda_extension); |         auto extension_index = filename.rfind(irda_extension); | ||||||
|         if((extension_index == std::string::npos) || |         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)); |         remote_names.push_back(filename.erase(extension_index)); | ||||||
|     } |     } | ||||||
|     fs_api->dir.close(&dir); |     file_parser.get_fs_api().dir.close(&dir); | ||||||
| 
 | 
 | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool IrdaAppRemoteManager::load(const std::string& name) { | bool IrdaAppRemoteManager::load(const std::string& name) { | ||||||
|     bool fs_res = false; |     bool fs_res = false; | ||||||
|  |     IrdaAppFileParser file_parser; | ||||||
|     File file; |     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) { |     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; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     remote = std::make_unique<IrdaAppRemote>(name); |     remote = std::make_unique<IrdaAppRemote>(name); | ||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|         auto str = getline(fs_api, file, file_buf, sizeof(file_buf), file_buf_cnt); |         auto message = file_parser.read_message(&file); | ||||||
|         if(str.empty()) break; |         if(!message) break; | ||||||
|         parse_button(str); |         remote->buttons.emplace_back(message->name, &message->message); | ||||||
|     } |     } | ||||||
|     fs_api->file.close(&file); |     file_parser.get_fs_api().file.close(&file); | ||||||
| 
 | 
 | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool IrdaAppRemoteManager::check_fs() const { | bool IrdaAppRemoteManager::check_fs() const { | ||||||
|     // TODO: [FL-1431] Add return value to sd_ex_api->check_error() and replace get_fs_info().
 |     // TODO: [FL-1431] Add return value to file_parser.get_sd_api().check_error() and replace get_fs_info().
 | ||||||
|     auto fs_err = fs_api->common.get_fs_info(nullptr, nullptr); |     IrdaAppFileParser file_parser; | ||||||
|     if(fs_err != FSE_OK) show_file_error_message("SD card not found"); |     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; |     return fs_err == FSE_OK; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,9 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "sys/_stdint.h" |  | ||||||
| #include <algorithm> |  | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <string> | #include <string> | ||||||
| #include <list> |  | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <irda.h> | #include <irda.h> | ||||||
| @ -38,14 +35,7 @@ class IrdaAppRemoteManager { | |||||||
|     static const char* irda_directory; |     static const char* irda_directory; | ||||||
|     static const char* irda_extension; |     static const char* irda_extension; | ||||||
|     std::unique_ptr<IrdaAppRemote> remote; |     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; |     std::string make_filename(const std::string& name) const; | ||||||
|     char file_buf[48]; |  | ||||||
|     size_t file_buf_cnt = 0; |  | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     bool add_remote_with_button(const char* button_name, const IrdaMessage* message); |     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; |     const IrdaMessage* get_button_data(size_t button_index) const; | ||||||
|     bool delete_button(uint32_t index); |     bool delete_button(uint32_t index); | ||||||
|     bool delete_remote(); |     bool delete_remote(); | ||||||
|     IrdaAppRemoteManager(); |  | ||||||
|     ~IrdaAppRemoteManager(); |  | ||||||
| 
 | 
 | ||||||
|     bool store(); |     bool store(); | ||||||
|     bool load(const std::string& name); |     bool load(const std::string& name); | ||||||
|  | |||||||
| @ -2,10 +2,10 @@ | |||||||
| #include "irda.h" | #include "irda.h" | ||||||
| #include <api-hal-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; |     IrdaAppEvent event; | ||||||
|     const IrdaMessage* irda_message; |     const IrdaMessage* irda_message; | ||||||
|     IrdaAppSignalReceiver* this_ = static_cast<IrdaAppSignalReceiver*>(ctx); |     IrdaAppSignalTransceiver* this_ = static_cast<IrdaAppSignalTransceiver*>(ctx); | ||||||
| 
 | 
 | ||||||
|     irda_message = irda_decode(this_->decoder, level, duration); |     irda_message = irda_decode(this_->decoder, level, duration); | ||||||
|     if(irda_message) { |     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()) { |     : decoder(irda_alloc_decoder()) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| IrdaAppSignalReceiver::~IrdaAppSignalReceiver() { | IrdaAppSignalTransceiver::~IrdaAppSignalTransceiver() { | ||||||
|     api_hal_irda_rx_irq_deinit(); |     api_hal_irda_rx_irq_deinit(); | ||||||
|     irda_free_decoder(decoder); |     irda_free_decoder(decoder); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void IrdaAppSignalReceiver::capture_once_start(osMessageQueueId_t queue) { | void IrdaAppSignalTransceiver::capture_once_start(osMessageQueueId_t queue) { | ||||||
|     event_queue = queue; |     event_queue = queue; | ||||||
|     irda_reset_decoder(decoder); |     irda_reset_decoder(decoder); | ||||||
|     api_hal_irda_rx_irq_init(); |     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(); |     api_hal_irda_rx_irq_deinit(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| IrdaMessage* IrdaAppSignalReceiver::get_last_message(void) { | IrdaMessage* IrdaAppSignalTransceiver::get_last_message(void) { | ||||||
|     return &message; |     return &message; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void IrdaAppSignalReceiver::send_message(const IrdaMessage* message) { | void IrdaAppSignalTransceiver::send_message(const IrdaMessage* message) const { | ||||||
|     irda_send(message, 1); |     irda_send(message, 1); | ||||||
| } | } | ||||||
| @ -1,14 +1,15 @@ | |||||||
|  | #pragma once | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <irda.h> | #include <irda.h> | ||||||
| 
 | 
 | ||||||
| class IrdaAppSignalReceiver { | class IrdaAppSignalTransceiver { | ||||||
| public: | public: | ||||||
|     IrdaAppSignalReceiver(void); |     IrdaAppSignalTransceiver(void); | ||||||
|     ~IrdaAppSignalReceiver(void); |     ~IrdaAppSignalTransceiver(void); | ||||||
|     void capture_once_start(osMessageQueueId_t event_queue); |     void capture_once_start(osMessageQueueId_t event_queue); | ||||||
|     void capture_stop(void); |     void capture_stop(void); | ||||||
|     IrdaMessage* get_last_message(void); |     IrdaMessage* get_last_message(void); | ||||||
|     void send_message(const IrdaMessage* message); |     void send_message(const IrdaMessage* message) const; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     osMessageQueueId_t event_queue; |     osMessageQueueId_t event_queue; | ||||||
| @ -1,7 +1,5 @@ | |||||||
| #include "furi.h" | #include "furi.h" | ||||||
| #include "gui/modules/button_menu.h" | #include "gui/modules/button_panel.h" | ||||||
| #include "gui/modules/dialog_ex.h" |  | ||||||
| #include "gui/modules/text_input.h" |  | ||||||
| #include "irda-app.hpp" | #include "irda-app.hpp" | ||||||
| #include <callback-connector.h> | #include <callback-connector.h> | ||||||
| 
 | 
 | ||||||
| @ -19,13 +17,17 @@ IrdaAppViewManager::IrdaAppViewManager() { | |||||||
|     popup = popup_alloc(); |     popup = popup_alloc(); | ||||||
|     dialog_ex = dialog_ex_alloc(); |     dialog_ex = dialog_ex_alloc(); | ||||||
|     text_input = text_input_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::ButtonMenu, button_menu_get_view(button_menu)); | ||||||
|     add_view(ViewType::Submenu, submenu_get_view(submenu)); |     add_view(ViewType::Submenu, submenu_get_view(submenu)); | ||||||
|     add_view(ViewType::Popup, popup_get_view(popup)); |     add_view(ViewType::Popup, popup_get_view(popup)); | ||||||
|     add_view(ViewType::DialogEx, dialog_ex_get_view(dialog_ex)); |     add_view(ViewType::DialogEx, dialog_ex_get_view(dialog_ex)); | ||||||
|     add_view(ViewType::TextInput, text_input_get_view(text_input)); |     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(button_menu_get_view(button_menu), callback); | ||||||
|     view_set_previous_callback(submenu_get_view(submenu), callback); |     view_set_previous_callback(submenu_get_view(submenu), callback); | ||||||
|     view_set_previous_callback(popup_get_view(popup), callback); |     view_set_previous_callback(popup_get_view(popup), callback); | ||||||
| @ -34,6 +36,8 @@ IrdaAppViewManager::IrdaAppViewManager() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| IrdaAppViewManager::~IrdaAppViewManager() { | IrdaAppViewManager::~IrdaAppViewManager() { | ||||||
|  |     view_dispatcher_remove_view( | ||||||
|  |         view_dispatcher, static_cast<uint32_t>(IrdaAppViewManager::ViewType::ButtonPanel)); | ||||||
|     view_dispatcher_remove_view( |     view_dispatcher_remove_view( | ||||||
|         view_dispatcher, static_cast<uint32_t>(IrdaAppViewManager::ViewType::ButtonMenu)); |         view_dispatcher, static_cast<uint32_t>(IrdaAppViewManager::ViewType::ButtonMenu)); | ||||||
|     view_dispatcher_remove_view( |     view_dispatcher_remove_view( | ||||||
| @ -47,9 +51,11 @@ IrdaAppViewManager::~IrdaAppViewManager() { | |||||||
| 
 | 
 | ||||||
|     submenu_free(submenu); |     submenu_free(submenu); | ||||||
|     popup_free(popup); |     popup_free(popup); | ||||||
|  |     button_panel_free(button_panel); | ||||||
|     button_menu_free(button_menu); |     button_menu_free(button_menu); | ||||||
|     dialog_ex_free(dialog_ex); |     dialog_ex_free(dialog_ex); | ||||||
|     text_input_free(text_input); |     text_input_free(text_input); | ||||||
|  |     popup_brut_free(popup_brut); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_free(view_dispatcher); |     view_dispatcher_free(view_dispatcher); | ||||||
|     furi_record_close("gui"); |     furi_record_close("gui"); | ||||||
| @ -80,6 +86,14 @@ ButtonMenu* IrdaAppViewManager::get_button_menu() { | |||||||
|     return 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() { | osMessageQueueId_t IrdaAppViewManager::get_event_queue() { | ||||||
|     return event_queue; |     return event_queue; | ||||||
| } | } | ||||||
|  | |||||||
| @ -7,6 +7,8 @@ | |||||||
| #include <gui/modules/submenu.h> | #include <gui/modules/submenu.h> | ||||||
| #include <gui/modules/popup.h> | #include <gui/modules/popup.h> | ||||||
| #include "irda-app.hpp" | #include "irda-app.hpp" | ||||||
|  | #include "view/irda-app-brut-view.h" | ||||||
|  | #include "gui/modules/button_panel.h" | ||||||
| 
 | 
 | ||||||
| class IrdaAppViewManager { | class IrdaAppViewManager { | ||||||
| public: | public: | ||||||
| @ -15,6 +17,7 @@ public: | |||||||
|         TextInput, |         TextInput, | ||||||
|         Submenu, |         Submenu, | ||||||
|         ButtonMenu, |         ButtonMenu, | ||||||
|  |         ButtonPanel, | ||||||
|         Popup, |         Popup, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| @ -31,6 +34,8 @@ public: | |||||||
|     Popup* get_popup(); |     Popup* get_popup(); | ||||||
|     TextInput* get_text_input(); |     TextInput* get_text_input(); | ||||||
|     ButtonMenu* get_button_menu(); |     ButtonMenu* get_button_menu(); | ||||||
|  |     ButtonPanel* get_button_panel(); | ||||||
|  |     IrdaAppPopupBrut* get_popup_brut(); | ||||||
| 
 | 
 | ||||||
|     osMessageQueueId_t get_event_queue(); |     osMessageQueueId_t get_event_queue(); | ||||||
| 
 | 
 | ||||||
| @ -44,6 +49,8 @@ private: | |||||||
|     Submenu* submenu; |     Submenu* submenu; | ||||||
|     Popup* popup; |     Popup* popup; | ||||||
|     ButtonMenu* button_menu; |     ButtonMenu* button_menu; | ||||||
|  |     ButtonPanel* button_panel; | ||||||
|  |     IrdaAppPopupBrut* popup_brut; | ||||||
| 
 | 
 | ||||||
|     osMessageQueueId_t event_queue; |     osMessageQueueId_t event_queue; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,9 +1,7 @@ | |||||||
| #include "irda-app.hpp" | #include "irda-app.hpp" | ||||||
| #include "sys/_stdint.h" |  | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <gui/gui.h> | #include <gui/gui.h> | ||||||
| #include <input/input.h> | #include <input/input.h> | ||||||
| #include <stdarg.h> |  | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <callback-connector.h> | #include <callback-connector.h> | ||||||
| 
 | 
 | ||||||
| @ -101,8 +99,8 @@ IrdaAppRemoteManager* IrdaApp::get_remote_manager() { | |||||||
|     return &remote_manager; |     return &remote_manager; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| IrdaAppSignalReceiver* IrdaApp::get_receiver() { | IrdaAppSignalTransceiver* IrdaApp::get_transceiver() { | ||||||
|     return &receiver; |     return &transceiver; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void IrdaApp::set_text_store(uint8_t index, const char* text...) { | void IrdaApp::set_text_store(uint8_t index, const char* text...) { | ||||||
|  | |||||||
| @ -1,13 +1,13 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "sys/_stdint.h" |  | ||||||
| #include <map> | #include <map> | ||||||
| #include <irda.h> | #include <irda.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
|  | #include "irda/scene/irda-app-scene.hpp" | ||||||
| #include "irda-app-event.hpp" | #include "irda-app-event.hpp" | ||||||
| #include "scene/irda-app-scene.hpp" | #include "scene/irda-app-scene.hpp" | ||||||
| #include "irda-app-view-manager.hpp" | #include "irda-app-view-manager.hpp" | ||||||
| #include "irda-app-remote-manager.hpp" | #include "irda-app-remote-manager.hpp" | ||||||
| #include "irda-app-receiver.hpp" | #include "irda-app-transceiver.hpp" | ||||||
| #include <forward_list> | #include <forward_list> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <notification/notification-messages.h> | #include <notification/notification-messages.h> | ||||||
| @ -51,7 +51,7 @@ public: | |||||||
|     bool switch_to_previous_scene(uint8_t count = 1); |     bool switch_to_previous_scene(uint8_t count = 1); | ||||||
|     Scene get_previous_scene(); |     Scene get_previous_scene(); | ||||||
|     IrdaAppViewManager* get_view_manager(); |     IrdaAppViewManager* get_view_manager(); | ||||||
|     IrdaAppSignalReceiver* get_receiver(); |     IrdaAppSignalTransceiver* get_transceiver(); | ||||||
|     void set_text_store(uint8_t index, const char* text...); |     void set_text_store(uint8_t index, const char* text...); | ||||||
|     char* get_text_store(uint8_t index); |     char* get_text_store(uint8_t index); | ||||||
|     uint8_t get_text_store_size(); |     uint8_t get_text_store_size(); | ||||||
| @ -103,7 +103,7 @@ private: | |||||||
|     uint32_t current_button; |     uint32_t current_button; | ||||||
| 
 | 
 | ||||||
|     NotificationApp* notification; |     NotificationApp* notification; | ||||||
|     IrdaAppSignalReceiver receiver; |     IrdaAppSignalTransceiver transceiver; | ||||||
|     IrdaAppViewManager view_manager; |     IrdaAppViewManager view_manager; | ||||||
|     IrdaAppRemoteManager remote_manager; |     IrdaAppRemoteManager remote_manager; | ||||||
| 
 | 
 | ||||||
| @ -113,6 +113,8 @@ private: | |||||||
|     std::map<Scene, IrdaAppScene*> scenes = { |     std::map<Scene, IrdaAppScene*> scenes = { | ||||||
|         {Scene::Start, new IrdaAppSceneStart()}, |         {Scene::Start, new IrdaAppSceneStart()}, | ||||||
|         {Scene::Universal, new IrdaAppSceneUniversal()}, |         {Scene::Universal, new IrdaAppSceneUniversal()}, | ||||||
|  |         {Scene::UniversalTV, new IrdaAppSceneUniversalTV()}, | ||||||
|  | //        {Scene::UniversalAudio, new IrdaAppSceneUniversalAudio()},
 | ||||||
|         {Scene::Learn, new IrdaAppSceneLearn()}, |         {Scene::Learn, new IrdaAppSceneLearn()}, | ||||||
|         {Scene::LearnSuccess, new IrdaAppSceneLearnSuccess()}, |         {Scene::LearnSuccess, new IrdaAppSceneLearnSuccess()}, | ||||||
|         {Scene::LearnEnterName, new IrdaAppSceneLearnEnterName()}, |         {Scene::LearnEnterName, new IrdaAppSceneLearnEnterName()}, | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ | |||||||
| #include "irda.h" | #include "irda.h" | ||||||
| #include "irda/scene/irda-app-scene.hpp" | #include "irda/scene/irda-app-scene.hpp" | ||||||
| #include <string> | #include <string> | ||||||
| #include <stdio.h> |  | ||||||
| 
 | 
 | ||||||
| static void dialog_result_callback(DialogExResult result, void* context) { | static void dialog_result_callback(DialogExResult result, void* context) { | ||||||
|     auto app = static_cast<IrdaApp*>(context); |     auto app = static_cast<IrdaApp*>(context); | ||||||
|  | |||||||
| @ -1,5 +1,4 @@ | |||||||
| #include "../irda-app.hpp" | #include "../irda-app.hpp" | ||||||
| #include <cstdio> |  | ||||||
| 
 | 
 | ||||||
| void IrdaAppSceneEditRename::on_enter(IrdaApp* app) { | void IrdaAppSceneEditRename::on_enter(IrdaApp* app) { | ||||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); |     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  | |||||||
| @ -1,6 +1,4 @@ | |||||||
| #include "../irda-app.hpp" | #include "../irda-app.hpp" | ||||||
| #include <string> |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <gui/modules/popup.h> | #include <gui/modules/popup.h> | ||||||
| 
 | 
 | ||||||
| void IrdaAppSceneLearnDoneAfter::on_enter(IrdaApp* app) { | void IrdaAppSceneLearnDoneAfter::on_enter(IrdaApp* app) { | ||||||
|  | |||||||
| @ -1,6 +1,4 @@ | |||||||
| #include "../irda-app.hpp" | #include "../irda-app.hpp" | ||||||
| #include <string> |  | ||||||
| #include <stdio.h> |  | ||||||
| 
 | 
 | ||||||
| void IrdaAppSceneLearnDone::on_enter(IrdaApp* app) { | void IrdaAppSceneLearnDone::on_enter(IrdaApp* app) { | ||||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); |     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  | |||||||
| @ -1,15 +1,12 @@ | |||||||
| #include "../irda-app.hpp" | #include "../irda-app.hpp" | ||||||
| #include "gui/modules/text_input.h" | #include "gui/modules/text_input.h" | ||||||
| #include <callback-connector.h> |  | ||||||
| #include <string> |  | ||||||
| #include <stdio.h> |  | ||||||
| 
 | 
 | ||||||
| void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) { | void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) { | ||||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); |     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     TextInput* text_input = view_manager->get_text_input(); |     TextInput* text_input = view_manager->get_text_input(); | ||||||
| 
 | 
 | ||||||
|     auto receiver = app->get_receiver(); |     auto transceiver = app->get_transceiver(); | ||||||
|     auto message = receiver->get_last_message(); |     auto message = transceiver->get_last_message(); | ||||||
| 
 | 
 | ||||||
|     app->set_text_store( |     app->set_text_store( | ||||||
|         0, |         0, | ||||||
| @ -34,14 +31,14 @@ bool IrdaAppSceneLearnEnterName::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
| 
 | 
 | ||||||
|     if(event->type == IrdaAppEvent::Type::TextEditDone) { |     if(event->type == IrdaAppEvent::Type::TextEditDone) { | ||||||
|         auto remote_manager = app->get_remote_manager(); |         auto remote_manager = app->get_remote_manager(); | ||||||
|         auto receiver = app->get_receiver(); |         auto transceiver = app->get_transceiver(); | ||||||
|         bool result = false; |         bool result = false; | ||||||
|         if(app->get_learn_new_remote()) { |         if(app->get_learn_new_remote()) { | ||||||
|             result = remote_manager->add_remote_with_button( |             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 { |         } else { | ||||||
|             result = |             result = remote_manager->add_button( | ||||||
|                 remote_manager->add_button(app->get_text_store(0), receiver->get_last_message()); |                 app->get_text_store(0), transceiver->get_last_message()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(!result) { |         if(!result) { | ||||||
|  | |||||||
| @ -1,7 +1,5 @@ | |||||||
| #include "../irda-app.hpp" | #include "../irda-app.hpp" | ||||||
| #include "irda.h" | #include "irda.h" | ||||||
| #include <string> |  | ||||||
| #include <stdio.h> |  | ||||||
| 
 | 
 | ||||||
| static void dialog_result_callback(DialogExResult result, void* context) { | static void dialog_result_callback(DialogExResult result, void* context) { | ||||||
|     auto app = static_cast<IrdaApp*>(context); |     auto app = static_cast<IrdaApp*>(context); | ||||||
| @ -19,8 +17,8 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) { | |||||||
| 
 | 
 | ||||||
|     app->notify_green_on(); |     app->notify_green_on(); | ||||||
| 
 | 
 | ||||||
|     auto receiver = app->get_receiver(); |     auto transceiver = app->get_transceiver(); | ||||||
|     auto message = receiver->get_last_message(); |     auto message = transceiver->get_last_message(); | ||||||
| 
 | 
 | ||||||
|     app->set_text_store(0, "%s", irda_get_protocol_name(message->protocol)); |     app->set_text_store(0, "%s", irda_get_protocol_name(message->protocol)); | ||||||
|     app->set_text_store( |     app->set_text_store( | ||||||
| @ -52,8 +50,8 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
|             break; |             break; | ||||||
|         case DialogExResultCenter: { |         case DialogExResultCenter: { | ||||||
|             app->notify_space_blink(); |             app->notify_space_blink(); | ||||||
|             auto receiver = app->get_receiver(); |             auto transceiver = app->get_transceiver(); | ||||||
|             auto message = receiver->get_last_message(); |             auto message = transceiver->get_last_message(); | ||||||
|             irda_send(message, 1); |             irda_send(message, 1); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -2,10 +2,10 @@ | |||||||
| 
 | 
 | ||||||
| void IrdaAppSceneLearn::on_enter(IrdaApp* app) { | void IrdaAppSceneLearn::on_enter(IrdaApp* app) { | ||||||
|     auto view_manager = app->get_view_manager(); |     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(); |     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(); |     auto popup = view_manager->get_popup(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -65,7 +65,7 @@ bool IrdaAppSceneRemote::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
|             app->notify_click_and_blink(); |             app->notify_click_and_blink(); | ||||||
|             auto remote_manager = app->get_remote_manager(); |             auto remote_manager = app->get_remote_manager(); | ||||||
|             auto message = remote_manager->get_button_data(event->payload.menu_index); |             auto message = remote_manager->get_button_data(event->payload.menu_index); | ||||||
|             app->get_receiver()->send_message(message); |             app->get_transceiver()->send_message(message); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|     } else if(event->type == IrdaAppEvent::Type::Back) { |     } 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; |         submenu_item_selected = event->payload.menu_index; | ||||||
|         switch(event->payload.menu_index) { |         switch(event->payload.menu_index) { | ||||||
|         case SubmenuIndexUniversalTV: |         case SubmenuIndexUniversalTV: | ||||||
|             //            app->switch_to_next_scene(IrdaApp::Scene::UniversalTV);
 |             app->switch_to_next_scene(IrdaApp::Scene::UniversalTV); | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexUniversalAudio: |         case SubmenuIndexUniversalAudio: | ||||||
|             //            app->switch_to_next_scene(IrdaApp::Scene::UniversalAudio);
 |             //            app->switch_to_next_scene(IrdaApp::Scene::UniversalAudio);
 | ||||||
|  | |||||||
| @ -1,10 +1,11 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "../irda-app.hpp" | #include "../irda-app-event.hpp" | ||||||
| #include <api-hal-irda.h> | #include <api-hal-irda.h> | ||||||
| #include "irda.h" | #include "irda.h" | ||||||
| #include <gui/elements.h> |  | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <string> | #include <string> | ||||||
|  | #include "../irda-app-brute-force.hpp" | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class IrdaApp; | class IrdaApp; | ||||||
| 
 | 
 | ||||||
| @ -137,3 +138,31 @@ public: | |||||||
|     void on_exit(IrdaApp* app) final; |     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> | #include <gui/icon.h> | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
| 	I_SDQuestion_35x43, | 	I_Down_hvr_25x27, | ||||||
| 	I_SDError_43x35, | 	I_Vol_down_hvr_25x27, | ||||||
| 	I_Health_16x16, | 	I_Down_25x27, | ||||||
| 	I_FaceCharging_29x14, | 	I_Fill_marker_7x7, | ||||||
| 	I_BatteryBody_52x28, | 	I_Vol_down_25x27, | ||||||
| 	I_Voltage_16x16, | 	I_Vol_up_25x27, | ||||||
| 	I_Temperature_16x16, | 	I_Up_hvr_25x27, | ||||||
| 	I_FaceNopower_29x14, | 	I_Vol_up_hvr_25x27, | ||||||
| 	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_IrdaLearnShort_128x31, | 	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_IrdaArrowDown_4x8, | ||||||
| 	I_IrdaLearn_128x64, | 	I_IrdaLearn_128x64, | ||||||
| 	I_IrdaSend_128x64, | 	I_Power_hvr_25x27, | ||||||
| 	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, |  | ||||||
| 	A_Games_14, | 	A_Games_14, | ||||||
| 	A_Plugins_14, | 	A_Power_14, | ||||||
|  | 	A_GPIO_14, | ||||||
|  | 	A_Bluetooth_14, | ||||||
| 	A_Passport_14, | 	A_Passport_14, | ||||||
| 	A_Sub1ghz_14, | 	A_Sub1ghz_14, | ||||||
| 	A_NFC_14, |  | ||||||
| 	A_Tamagotchi_14, |  | ||||||
| 	A_FileManager_14, |  | ||||||
| 	A_125khz_14, |  | ||||||
| 	A_U2F_14, | 	A_U2F_14, | ||||||
| 	A_Infrared_14, | 	A_Infrared_14, | ||||||
| 	A_Power_14, |  | ||||||
| 	A_Settings_14, | 	A_Settings_14, | ||||||
|  | 	A_125khz_14, | ||||||
| 	A_iButton_14, | 	A_iButton_14, | ||||||
| 	A_Bluetooth_14, | 	A_FileManager_14, | ||||||
| 	A_GPIO_14, | 	A_Tamagotchi_14, | ||||||
| 	I_DolphinMafia_115x62, | 	A_NFC_14, | ||||||
| 	I_DolphinExcited_64x63, | 	A_Plugins_14, | ||||||
| 	I_iButtonDolphinSuccess_109x60, | 	I_SDQuestion_35x43, | ||||||
| 	I_iButtonDolphinVerySuccess_108x52, | 	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_iButtonKey_49x44, | ||||||
| 	I_DolphinNice_96x59, | 	I_DolphinExcited_64x63, | ||||||
| 	I_DolphinWait_61x59, | 	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_Wink_128x64, | ||||||
| 	A_MDWL_32x32, |  | ||||||
| 	A_MDWR_32x32, |  | ||||||
| 	A_WatchingTV_128x64, |  | ||||||
| 	A_MDI_32x32, | 	A_MDI_32x32, | ||||||
| 	A_MDWRB_32x32, |  | ||||||
| 	A_MDIB_32x32, |  | ||||||
| 	A_FX_Sitting_40x27, | 	A_FX_Sitting_40x27, | ||||||
|  | 	A_MDWR_32x32, | ||||||
|  | 	A_MDWL_32x32, | ||||||
|  | 	A_MDWRB_32x32, | ||||||
| 	A_MDWLB_32x32, | 	A_MDWLB_32x32, | ||||||
| 	I_KeySave_24x11, | 	A_MDIB_32x32, | ||||||
| 	I_KeyBackspaceSelected_16x9, | 	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_KeySaveSelected_24x11, | ||||||
| 	I_KeyBackspace_16x9, | 	I_KeyBackspace_16x9, | ||||||
|  | 	I_KeyBackspaceSelected_16x9, | ||||||
|  | 	I_KeySave_24x11, | ||||||
| } IconName; | } IconName; | ||||||
| 
 | 
 | ||||||
| Icon * assets_icons_get(IconName name); | 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 = cmd & 0xFF; | ||||||
|     uint8_t command_inverse = (uint8_t) ~command; |     uint8_t command_inverse = (uint8_t) ~command; | ||||||
| 
 | 
 | ||||||
|  |     irda_encode_space(&encoder_timings, 100); | ||||||
|     if (!repeat) { |     if (!repeat) { | ||||||
|         irda_encode_samsung32_preamble(); |         irda_encode_samsung32_preamble(); | ||||||
|         irda_encode_byte(&encoder_timings, address); |         irda_encode_byte(&encoder_timings, address); | ||||||
|  | |||||||
| @ -87,6 +87,10 @@ C_SOURCES		+= $(wildcard $(LIB_DIR)/drivers/*.c) | |||||||
| CFLAGS			+= -I$(LIB_DIR)/version | CFLAGS			+= -I$(LIB_DIR)/version | ||||||
| C_SOURCES		+= $(LIB_DIR)/version/version.c | 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
 | #irda lib
 | ||||||
| CFLAGS			+= -I$(LIB_DIR)/irda | CFLAGS			+= -I$(LIB_DIR)/irda | ||||||
| C_SOURCES		+= $(wildcard $(LIB_DIR)/irda/*.c) | C_SOURCES		+= $(wildcard $(LIB_DIR)/irda/*.c) | ||||||
|  | |||||||
 Albert Kharisov
						Albert Kharisov