[FL-1369, FL-1397, FL-1420] IRDA + SDcard (#513)
* Add saving to SD-Card (not ready yet) * Add saving to SD-card (done) * Select previous menu item * Fix central button * Fix current_button * Refactoring * Add notifications * [FL-1417] Add IRDA CLI CLI commands: 1) ir_rx Receives all IR-trafic, decodes and prints result to stdout 2) ir_tx <protocol> <address> <command> Transmits IR-signal. Address and command are hex-formatted * Fix BUG with random memory corruption at random time in random place in random universe in random unknown space and time forever amen * Fix submenu set_selected_item * Bring protocol order back * Add TODO sdcard check
This commit is contained in:
		
							parent
							
								
									498ffe8d2c
								
							
						
					
					
						commit
						6c74ea65c2
					
				| @ -41,6 +41,7 @@ int32_t app_archive(void* p); | |||||||
| int32_t notification_app(void* p); | int32_t notification_app(void* p); | ||||||
| 
 | 
 | ||||||
| // On system start hooks declaration
 | // On system start hooks declaration
 | ||||||
|  | void irda_cli_init(); | ||||||
| void nfc_cli_init(); | void nfc_cli_init(); | ||||||
| void subghz_cli_init(); | void subghz_cli_init(); | ||||||
| void bt_cli_init(); | void bt_cli_init(); | ||||||
| @ -104,7 +105,7 @@ const FlipperApplication FLIPPER_SERVICES[] = { | |||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_IRDA | #ifdef SRV_IRDA | ||||||
|     {.app = irda, .name = "irda", .stack_size = 1024, .icon = A_Plugins_14}, |     {.app = irda, .name = "irda", .stack_size = 1024 * 3, .icon = A_Plugins_14}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_EXAMPLE_QRCODE | #ifdef SRV_EXAMPLE_QRCODE | ||||||
| @ -186,7 +187,7 @@ const FlipperApplication FLIPPER_APPS[] = { | |||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_IRDA | #ifdef APP_IRDA | ||||||
|     {.app = irda, .name = "Infrared", .stack_size = 1024, .icon = A_Infrared_14}, |     {.app = irda, .name = "Infrared", .stack_size = 1024 * 3, .icon = A_Infrared_14}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_GPIO_DEMO | #ifdef APP_GPIO_DEMO | ||||||
| @ -203,6 +204,7 @@ const size_t FLIPPER_APPS_COUNT = sizeof(FLIPPER_APPS) / sizeof(FlipperApplicati | |||||||
| 
 | 
 | ||||||
| // On system start hooks
 | // On system start hooks
 | ||||||
| const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = { | const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = { | ||||||
|  |     irda_cli_init, | ||||||
| #ifdef APP_NFC | #ifdef APP_NFC | ||||||
|     nfc_cli_init, |     nfc_cli_init, | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -71,7 +71,7 @@ void elements_button_left(Canvas* canvas, const char* str) { | |||||||
|     const uint8_t horizontal_offset = 3; |     const uint8_t horizontal_offset = 3; | ||||||
|     const uint8_t string_width = canvas_string_width(canvas, str); |     const uint8_t string_width = canvas_string_width(canvas, str); | ||||||
|     const IconData* icon = assets_icons_get_data(I_ButtonLeft_4x7); |     const IconData* icon = assets_icons_get_data(I_ButtonLeft_4x7); | ||||||
|     const uint8_t icon_offset = 6; |     const uint8_t icon_offset = 3; | ||||||
|     const uint8_t icon_width_with_offset = icon->width + icon_offset; |     const uint8_t icon_width_with_offset = icon->width + icon_offset; | ||||||
|     const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; |     const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; | ||||||
| 
 | 
 | ||||||
| @ -97,7 +97,7 @@ void elements_button_right(Canvas* canvas, const char* str) { | |||||||
|     const uint8_t horizontal_offset = 3; |     const uint8_t horizontal_offset = 3; | ||||||
|     const uint8_t string_width = canvas_string_width(canvas, str); |     const uint8_t string_width = canvas_string_width(canvas, str); | ||||||
|     const IconData* icon = assets_icons_get_data(I_ButtonRight_4x7); |     const IconData* icon = assets_icons_get_data(I_ButtonRight_4x7); | ||||||
|     const uint8_t icon_offset = 6; |     const uint8_t icon_offset = 3; | ||||||
|     const uint8_t icon_width_with_offset = icon->width + icon_offset; |     const uint8_t icon_width_with_offset = icon->width + icon_offset; | ||||||
|     const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; |     const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; | ||||||
| 
 | 
 | ||||||
| @ -122,10 +122,10 @@ void elements_button_right(Canvas* canvas, const char* str) { | |||||||
| void elements_button_center(Canvas* canvas, const char* str) { | void elements_button_center(Canvas* canvas, const char* str) { | ||||||
|     const uint8_t button_height = 13; |     const uint8_t button_height = 13; | ||||||
|     const uint8_t vertical_offset = 3; |     const uint8_t vertical_offset = 3; | ||||||
|     const uint8_t horizontal_offset = 3; |     const uint8_t horizontal_offset = 1; | ||||||
|     const uint8_t string_width = canvas_string_width(canvas, str); |     const uint8_t string_width = canvas_string_width(canvas, str); | ||||||
|     const IconData* icon = assets_icons_get_data(I_ButtonCenter_7x7); |     const IconData* icon = assets_icons_get_data(I_ButtonCenter_7x7); | ||||||
|     const uint8_t icon_offset = 6; |     const uint8_t icon_offset = 3; | ||||||
|     const uint8_t icon_width_with_offset = icon->width + icon_offset; |     const uint8_t icon_width_with_offset = icon->width + icon_offset; | ||||||
|     const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; |     const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -286,3 +286,21 @@ void button_menu_free(ButtonMenu* button_menu) { | |||||||
|     view_free(button_menu->view); |     view_free(button_menu->view); | ||||||
|     free(button_menu); |     free(button_menu); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void button_menu_set_selected_item(ButtonMenu* button_menu, uint32_t index) { | ||||||
|  |     furi_assert(button_menu); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         button_menu->view, (ButtonMenuModel * model) { | ||||||
|  |             uint8_t item_position = 0; | ||||||
|  |             ButtonMenuItemArray_it_t it; | ||||||
|  |             for(ButtonMenuItemArray_it(it, model->items); !ButtonMenuItemArray_end_p(it); | ||||||
|  |                 ButtonMenuItemArray_next(it), ++item_position) { | ||||||
|  |                 if(ButtonMenuItemArray_cref(it)->index == index) { | ||||||
|  |                     model->position = item_position; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | |||||||
| @ -68,6 +68,13 @@ void button_menu_free(ButtonMenu* button_menu); | |||||||
|  */ |  */ | ||||||
| void button_menu_set_header(ButtonMenu* button_menu, const char* header); | void button_menu_set_header(ButtonMenu* button_menu, const char* header); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Set selected item | ||||||
|  |  * @param button_menu   - ButtonMenu instance | ||||||
|  |  * @param index         - index of ButtonMenu to be selected | ||||||
|  |  */ | ||||||
|  | void button_menu_set_selected_item(ButtonMenu* button_menu, uint32_t index); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -36,12 +36,12 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { | |||||||
|     const uint8_t item_width = 123; |     const uint8_t item_width = 123; | ||||||
| 
 | 
 | ||||||
|     canvas_clear(canvas); |     canvas_clear(canvas); | ||||||
|     canvas_set_font(canvas, FontPrimary); |  | ||||||
| 
 | 
 | ||||||
|     uint8_t position = 0; |     uint8_t position = 0; | ||||||
|     SubmenuItemArray_it_t it; |     SubmenuItemArray_it_t it; | ||||||
| 
 | 
 | ||||||
|     if(model->header) { |     if(model->header) { | ||||||
|  |         canvas_set_font(canvas, FontPrimary); | ||||||
|         canvas_draw_str(canvas, 4, 11, model->header); |         canvas_draw_str(canvas, 4, 11, model->header); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -49,10 +49,10 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { | |||||||
|     for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); |     for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); | ||||||
|         SubmenuItemArray_next(it)) { |         SubmenuItemArray_next(it)) { | ||||||
|         uint8_t item_position = position - model->window_position; |         uint8_t item_position = position - model->window_position; | ||||||
|         uint8_t elements_on_screen = model->header ? 3 : 4; |         uint8_t items_on_screen = model->header ? 3 : 4; | ||||||
|         uint8_t y_offset = model->header ? 16 : 0; |         uint8_t y_offset = model->header ? 16 : 0; | ||||||
| 
 | 
 | ||||||
|         if(item_position < elements_on_screen) { |         if(item_position < items_on_screen) { | ||||||
|             if(position == model->position) { |             if(position == model->position) { | ||||||
|                 canvas_set_color(canvas, ColorBlack); |                 canvas_set_color(canvas, ColorBlack); | ||||||
|                 elements_slightly_rounded_box( |                 elements_slightly_rounded_box( | ||||||
| @ -202,11 +202,15 @@ void submenu_set_selected_item(Submenu* submenu, uint32_t index) { | |||||||
|                 model->window_position -= 1; |                 model->window_position -= 1; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if(SubmenuItemArray_size(model->items) <= 4) { |             uint8_t items_on_screen = model->header ? 3 : 4; | ||||||
|  | 
 | ||||||
|  |             if(SubmenuItemArray_size(model->items) <= items_on_screen) { | ||||||
|                 model->window_position = 0; |                 model->window_position = 0; | ||||||
|             } else { |             } else { | ||||||
|                 if(model->window_position >= (SubmenuItemArray_size(model->items) - 4)) { |                 if(model->window_position >= | ||||||
|                     model->window_position = (SubmenuItemArray_size(model->items) - 4); |                    (SubmenuItemArray_size(model->items) - items_on_screen)) { | ||||||
|  |                     model->window_position = | ||||||
|  |                         (SubmenuItemArray_size(model->items) - items_on_screen); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
| @ -217,7 +221,7 @@ void submenu_set_selected_item(Submenu* submenu, uint32_t index) { | |||||||
| void submenu_process_up(Submenu* submenu) { | void submenu_process_up(Submenu* submenu) { | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         submenu->view, (SubmenuModel * model) { |         submenu->view, (SubmenuModel * model) { | ||||||
|             uint8_t elements_on_screen = model->header ? 3 : 4; |             uint8_t items_on_screen = model->header ? 3 : 4; | ||||||
|             if(model->position > 0) { |             if(model->position > 0) { | ||||||
|                 model->position--; |                 model->position--; | ||||||
|                 if(((model->position - model->window_position) < 1) && |                 if(((model->position - model->window_position) < 1) && | ||||||
| @ -226,8 +230,8 @@ void submenu_process_up(Submenu* submenu) { | |||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 model->position = SubmenuItemArray_size(model->items) - 1; |                 model->position = SubmenuItemArray_size(model->items) - 1; | ||||||
|                 if(model->position > (elements_on_screen - 1)) { |                 if(model->position > (items_on_screen - 1)) { | ||||||
|                     model->window_position = model->position - (elements_on_screen - 1); |                     model->window_position = model->position - (items_on_screen - 1); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             return true; |             return true; | ||||||
| @ -237,12 +241,12 @@ void submenu_process_up(Submenu* submenu) { | |||||||
| void submenu_process_down(Submenu* submenu) { | void submenu_process_down(Submenu* submenu) { | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         submenu->view, (SubmenuModel * model) { |         submenu->view, (SubmenuModel * model) { | ||||||
|             uint8_t elements_on_screen = model->header ? 3 : 4; |             uint8_t items_on_screen = model->header ? 3 : 4; | ||||||
|             if(model->position < (SubmenuItemArray_size(model->items) - 1)) { |             if(model->position < (SubmenuItemArray_size(model->items) - 1)) { | ||||||
|                 model->position++; |                 model->position++; | ||||||
|                 if((model->position - model->window_position) > (elements_on_screen - 2) && |                 if((model->position - model->window_position) > (items_on_screen - 2) && | ||||||
|                    model->window_position < |                    model->window_position < | ||||||
|                        (SubmenuItemArray_size(model->items) - elements_on_screen)) { |                        (SubmenuItemArray_size(model->items) - items_on_screen)) { | ||||||
|                     model->window_position++; |                     model->window_position++; | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|  | |||||||
							
								
								
									
										104
									
								
								applications/irda/cli/irda-cli.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								applications/irda/cli/irda-cli.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,104 @@ | |||||||
|  | #include "app-template.h" | ||||||
|  | #include "cli/cli.h" | ||||||
|  | #include "cmsis_os2.h" | ||||||
|  | #include <furi.h> | ||||||
|  | #include <api-hal-irda.h> | ||||||
|  | #include "irda.h" | ||||||
|  | #include <sstream> | ||||||
|  | #include <string> | ||||||
|  | #include <m-string.h> | ||||||
|  | 
 | ||||||
|  | typedef struct IrdaCli { | ||||||
|  |     IrdaHandler* handler; | ||||||
|  |     osMessageQueueId_t message_queue; | ||||||
|  | } IrdaCli; | ||||||
|  | 
 | ||||||
|  | static void irda_rx_callback(void* ctx, bool level, uint32_t duration) { | ||||||
|  |     IrdaCli* irda_cli = (IrdaCli*)ctx; | ||||||
|  |     const IrdaMessage* message; | ||||||
|  |     message = irda_decode(irda_cli->handler, level, duration); | ||||||
|  |     if(message) { | ||||||
|  |         osMessageQueuePut(irda_cli->message_queue, message, 0, 0); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void irda_cli_start_ir_rx(Cli* cli, string_t args, void* context) { | ||||||
|  |     if(api_hal_irda_rx_irq_is_busy()) { | ||||||
|  |         printf("IRDA is busy. Exit."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     IrdaCli irda_cli; | ||||||
|  |     irda_cli.handler = irda_alloc_decoder(); | ||||||
|  |     irda_cli.message_queue = osMessageQueueNew(2, sizeof(IrdaMessage), NULL); | ||||||
|  |     api_hal_irda_rx_irq_init(); | ||||||
|  |     api_hal_irda_rx_irq_set_callback(irda_rx_callback, &irda_cli); | ||||||
|  | 
 | ||||||
|  |     printf("Receiving IRDA...\r\nPress Ctrl+C to abort\r\n"); | ||||||
|  |     while(!cli_cmd_interrupt_received(cli)) { | ||||||
|  |         IrdaMessage message; | ||||||
|  |         if(osOK == osMessageQueueGet(irda_cli.message_queue, &message, NULL, 50)) { | ||||||
|  |             printf( | ||||||
|  |                 "%s, A:0x%0*lX, C:0x%0*lX%s\r\n", | ||||||
|  |                 irda_get_protocol_name(message.protocol), | ||||||
|  |                 irda_get_protocol_address_length(message.protocol), | ||||||
|  |                 message.address, | ||||||
|  |                 irda_get_protocol_command_length(message.protocol), | ||||||
|  |                 message.command, | ||||||
|  |                 message.repeat ? " R" : ""); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     api_hal_irda_rx_irq_deinit(); | ||||||
|  |     irda_free_decoder(irda_cli.handler); | ||||||
|  |     osMessageQueueDelete(irda_cli.message_queue); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void irda_cli_print_usage(void) { | ||||||
|  |     printf("Usage:\r\n\tir_tx <protocol> <command> <address>\r\n"); | ||||||
|  |     printf("\t<command> and <address> are hex-formatted\r\n"); | ||||||
|  |     printf("\tAvailable protocols:"); | ||||||
|  |     for(int i = 0; irda_is_protocol_valid((IrdaProtocol)i); ++i) { | ||||||
|  |         printf(" %s", irda_get_protocol_name((IrdaProtocol)i)); | ||||||
|  |     } | ||||||
|  |     printf("\r\n"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) { | ||||||
|  |     if(api_hal_irda_rx_irq_is_busy()) { | ||||||
|  |         printf("IRDA is busy. Exit."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     auto ss = std::istringstream(string_get_cstr(args)); | ||||||
|  |     uint32_t command = 0; | ||||||
|  |     uint32_t address = 0; | ||||||
|  |     std::string protocol_name; | ||||||
|  | 
 | ||||||
|  |     if(!(ss >> protocol_name) || !(ss >> std::hex >> address) || !(ss >> std::hex >> command)) { | ||||||
|  |         printf("Wrong arguments.\r\n"); | ||||||
|  |         irda_cli_print_usage(); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     IrdaProtocol protocol = irda_get_protocol_by_name(protocol_name.c_str()); | ||||||
|  | 
 | ||||||
|  |     if(!irda_is_protocol_valid(protocol)) { | ||||||
|  |         printf("Unknown protocol.\r\n"); | ||||||
|  |         irda_cli_print_usage(); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     IrdaMessage message = { | ||||||
|  |         .protocol = protocol, | ||||||
|  |         .address = address, | ||||||
|  |         .command = command, | ||||||
|  |         .repeat = false, | ||||||
|  |     }; | ||||||
|  |     irda_send(&message, 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | extern "C" void irda_cli_init() { | ||||||
|  |     Cli* cli = (Cli*)furi_record_open("cli"); | ||||||
|  |     cli_add_command(cli, "ir_rx", irda_cli_start_ir_rx, NULL); | ||||||
|  |     cli_add_command(cli, "ir_tx", irda_cli_start_ir_tx, NULL); | ||||||
|  |     furi_record_close("cli"); | ||||||
|  | } | ||||||
| @ -1,51 +1,78 @@ | |||||||
| #include "irda-app-remote-manager.hpp" | #include "irda-app-remote-manager.hpp" | ||||||
|  | #include "filesystem-api.h" | ||||||
| #include "furi.h" | #include "furi.h" | ||||||
|  | #include "furi/check.h" | ||||||
|  | #include "gui/modules/button_menu.h" | ||||||
|  | #include "irda.h" | ||||||
|  | #include "sys/_stdint.h" | ||||||
|  | #include <cstdio> | ||||||
| #include <string> | #include <string> | ||||||
| #include <utility> | #include <utility> | ||||||
| 
 | 
 | ||||||
| IrdaAppRemoteManager::IrdaAppRemoteManager() { | const char* IrdaAppRemoteManager::irda_directory = "irda"; | ||||||
|     // Read from api-hal-storage, and fill remotes
 | const char* IrdaAppRemoteManager::irda_extension = ".ir"; | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static const std::string default_remote_name = "remote"; | static const std::string default_remote_name = "remote"; | ||||||
| 
 | 
 | ||||||
| void IrdaAppRemoteManager::add_button(const char* button_name, const IrdaMessage* message) { | static bool find_string(const std::vector<std::string>& strings, const std::string& match_string) { | ||||||
|     remotes[current_remote_index].buttons.emplace_back(button_name, message); |     for(const auto& string : strings) { | ||||||
|  |         if(!string.compare(match_string)) return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void IrdaAppRemoteManager::add_remote_with_button( | static std::string | ||||||
|  | find_vacant_name(const std::vector<std::string>& strings, const std::string& name) { | ||||||
|  |     // if suggested name is occupied, try another one (name2, name3, etc)
 | ||||||
|  |     if(find_string(strings, name)) { | ||||||
|  |         int i = 1; | ||||||
|  |         while(find_string(strings, name + std::to_string(++i))) | ||||||
|  |             ; | ||||||
|  |         return name + std::to_string(i); | ||||||
|  |     } else { | ||||||
|  |         return name; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | IrdaAppRemoteManager::IrdaAppRemoteManager() { | ||||||
|  |     sd_ex_api = static_cast<SdCard_Api*>(furi_record_open("sdcard-ex")); | ||||||
|  |     fs_api = static_cast<FS_Api*>(furi_record_open("sdcard")); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | IrdaAppRemoteManager::~IrdaAppRemoteManager() { | ||||||
|  |     furi_record_close("sdcard"); | ||||||
|  |     furi_record_close("sdcard-ex"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool IrdaAppRemoteManager::add_button(const char* button_name, const IrdaMessage* message) { | ||||||
|  |     remote->buttons.emplace_back(button_name, message); | ||||||
|  |     return store(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool IrdaAppRemoteManager::add_remote_with_button( | ||||||
|     const char* button_name, |     const char* button_name, | ||||||
|     const IrdaMessage* message) { |     const IrdaMessage* message) { | ||||||
|     bool found = true; |     furi_check(button_name != nullptr); | ||||||
|     int i = 0; |     furi_check(message != nullptr); | ||||||
| 
 | 
 | ||||||
|     // find first free common name for remote
 |     std::vector<std::string> remote_list; | ||||||
|     do { |     bool result = get_remote_list(remote_list); | ||||||
|         found = false; |     if(!result) return false; | ||||||
|         ++i; |  | ||||||
|         for(const auto& it : remotes) { |  | ||||||
|             if(it.name == (default_remote_name + std::to_string(i))) { |  | ||||||
|                 found = true; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } while(found); |  | ||||||
| 
 | 
 | ||||||
|     remotes.emplace_back(default_remote_name + std::to_string(i)); |     auto new_name = find_vacant_name(remote_list, default_remote_name); | ||||||
|     current_remote_index = remotes.size() - 1; | 
 | ||||||
|     add_button(button_name, message); |     remote = std::make_unique<IrdaAppRemote>(new_name); | ||||||
|  |     return add_button(button_name, message); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| IrdaAppRemote::IrdaAppRemote(std::string name) | IrdaAppRemote::IrdaAppRemote(const std::string& name) | ||||||
|     : name(name) { |     : name(name) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::vector<std::string> IrdaAppRemoteManager::get_button_list(void) const { | std::vector<std::string> IrdaAppRemoteManager::get_button_list(void) const { | ||||||
|     std::vector<std::string> name_vector; |     std::vector<std::string> name_vector; | ||||||
|     auto remote = remotes[current_remote_index]; |     name_vector.reserve(remote->buttons.size()); | ||||||
|     name_vector.reserve(remote.buttons.size()); |  | ||||||
| 
 | 
 | ||||||
|     for(const auto& it : remote.buttons) { |     for(const auto& it : remote->buttons) { | ||||||
|         name_vector.emplace_back(it.name); |         name_vector.emplace_back(it.name); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -53,78 +80,289 @@ std::vector<std::string> IrdaAppRemoteManager::get_button_list(void) const { | |||||||
|     return name_vector; |     return name_vector; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::vector<std::string> IrdaAppRemoteManager::get_remote_list() const { | const IrdaMessage* IrdaAppRemoteManager::get_button_data(size_t index) const { | ||||||
|     std::vector<std::string> name_vector; |     furi_check(remote.get() != nullptr); | ||||||
|     name_vector.reserve(remotes.size()); |     auto& buttons = remote->buttons; | ||||||
|  |     furi_check(index < buttons.size()); | ||||||
| 
 | 
 | ||||||
|     for(const auto& it : remotes) { |     return &buttons.at(index).message; | ||||||
|         name_vector.push_back(it.name); | } | ||||||
|  | 
 | ||||||
|  | std::string IrdaAppRemoteManager::make_filename(const std::string& name) const { | ||||||
|  |     return std::string("/") + irda_directory + "/" + name + irda_extension; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool IrdaAppRemoteManager::delete_remote() { | ||||||
|  |     FS_Error fs_res; | ||||||
|  | 
 | ||||||
|  |     fs_res = fs_api->common.remove(make_filename(remote->name).c_str()); | ||||||
|  |     if(fs_res != FSE_OK) { | ||||||
|  |         show_file_error_message("Error deleting file"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     remote.reset(); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool IrdaAppRemoteManager::delete_button(uint32_t index) { | ||||||
|  |     furi_check(remote.get() != nullptr); | ||||||
|  |     auto& buttons = remote->buttons; | ||||||
|  |     furi_check(index < buttons.size()); | ||||||
|  | 
 | ||||||
|  |     buttons.erase(buttons.begin() + index); | ||||||
|  |     return store(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::string IrdaAppRemoteManager::get_button_name(uint32_t index) { | ||||||
|  |     furi_check(remote.get() != nullptr); | ||||||
|  |     auto& buttons = remote->buttons; | ||||||
|  |     furi_check(index < buttons.size()); | ||||||
|  |     return buttons[index].name; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::string IrdaAppRemoteManager::get_remote_name() { | ||||||
|  |     furi_check(remote.get() != nullptr); | ||||||
|  |     return remote->name; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int IrdaAppRemoteManager::find_remote_name(const std::vector<std::string>& strings) { | ||||||
|  |     int i = 0; | ||||||
|  |     for(const auto& str : strings) { | ||||||
|  |         if(!str.compare(remote->name)) { | ||||||
|  |             return i; | ||||||
|  |         } | ||||||
|  |         ++i; | ||||||
|  |     } | ||||||
|  |     return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool IrdaAppRemoteManager::rename_remote(const char* str) { | ||||||
|  |     furi_check(str != nullptr); | ||||||
|  |     furi_check(remote.get() != nullptr); | ||||||
|  | 
 | ||||||
|  |     if(!remote->name.compare(str)) return true; | ||||||
|  | 
 | ||||||
|  |     std::vector<std::string> remote_list; | ||||||
|  |     bool result = get_remote_list(remote_list); | ||||||
|  |     if(!result) return false; | ||||||
|  | 
 | ||||||
|  |     auto new_name = find_vacant_name(remote_list, str); | ||||||
|  |     FS_Error fs_err = fs_api->common.rename( | ||||||
|  |         make_filename(remote->name).c_str(), make_filename(new_name).c_str()); | ||||||
|  |     remote->name = new_name; | ||||||
|  |     if(fs_err != FSE_OK) { | ||||||
|  |         show_file_error_message("Error renaming\nremote file"); | ||||||
|  |     } | ||||||
|  |     return fs_err == FSE_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool IrdaAppRemoteManager::rename_button(uint32_t index, const char* str) { | ||||||
|  |     furi_check(remote.get() != nullptr); | ||||||
|  |     auto& buttons = remote->buttons; | ||||||
|  |     furi_check(index < buttons.size()); | ||||||
|  | 
 | ||||||
|  |     buttons[index].name = str; | ||||||
|  |     return store(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t IrdaAppRemoteManager::get_number_of_buttons() { | ||||||
|  |     furi_check(remote.get() != nullptr); | ||||||
|  |     return remote->buttons.size(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void IrdaAppRemoteManager::show_file_error_message(const char* error_text) const { | ||||||
|  |     sd_ex_api->show_error(sd_ex_api->context, error_text); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool IrdaAppRemoteManager::store(void) { | ||||||
|  |     File file; | ||||||
|  |     uint16_t write_count; | ||||||
|  |     std::string dirname(std::string("/") + irda_directory); | ||||||
|  | 
 | ||||||
|  |     FS_Error fs_err = fs_api->common.mkdir(dirname.c_str()); | ||||||
|  |     if((fs_err != FSE_OK) && (fs_err != FSE_EXIST)) { | ||||||
|  |         show_file_error_message("Can't create directory"); | ||||||
|  |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // copy elision
 |     std::string filename = dirname + "/" + remote->name + irda_extension; | ||||||
|     return name_vector; |     bool res = fs_api->file.open(&file, filename.c_str(), FSAM_WRITE, FSOM_CREATE_ALWAYS); | ||||||
|  | 
 | ||||||
|  |     if(!res) { | ||||||
|  |         show_file_error_message("Cannot create\nnew remote file"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     char content[128]; | ||||||
|  | 
 | ||||||
|  |     for(const auto& button : remote->buttons) { | ||||||
|  |         auto protocol = button.message.protocol; | ||||||
|  | 
 | ||||||
|  |         sniprintf( | ||||||
|  |             content, | ||||||
|  |             sizeof(content), | ||||||
|  |             "%.31s %.31s A:%0*lX C:%0*lX\n", | ||||||
|  |             button.name.c_str(), | ||||||
|  |             irda_get_protocol_name(protocol), | ||||||
|  |             irda_get_protocol_address_length(protocol), | ||||||
|  |             button.message.address, | ||||||
|  |             irda_get_protocol_command_length(protocol), | ||||||
|  |             button.message.command); | ||||||
|  | 
 | ||||||
|  |         auto content_len = strlen(content); | ||||||
|  |         write_count = fs_api->file.write(&file, content, content_len); | ||||||
|  |         if(file.error_id != FSE_OK || write_count != content_len) { | ||||||
|  |             show_file_error_message("Cannot write\nto key file"); | ||||||
|  |             fs_api->file.close(&file); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fs_api->file.close(&file); | ||||||
|  |     sd_ex_api->check_error(sd_ex_api->context); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| size_t IrdaAppRemoteManager::get_current_remote(void) const { | bool IrdaAppRemoteManager::parse_button(std::string& str) { | ||||||
|     return current_remote_index; |     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; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| size_t IrdaAppRemoteManager::get_current_button(void) const { | std::string getline( | ||||||
|     return current_button_index; |     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; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void IrdaAppRemote::add_button( | bool IrdaAppRemoteManager::get_remote_list(std::vector<std::string>& remote_names) const { | ||||||
|     size_t remote_index, |     bool fs_res = false; | ||||||
|     const char* button_name, |     char name[128]; | ||||||
|     const IrdaMessage* message) { |     File dir; | ||||||
|     buttons.emplace_back(button_name, message); |     std::string dirname(std::string("/") + irda_directory); | ||||||
|  |     remote_names.clear(); | ||||||
|  | 
 | ||||||
|  |     fs_res = fs_api->dir.open(&dir, dirname.c_str()); | ||||||
|  |     if(!fs_res) { | ||||||
|  |         if(!check_fs()) { | ||||||
|  |             show_file_error_message("Cannot open\napplication directory"); | ||||||
|  |             return false; | ||||||
|  |         } else { | ||||||
|  |             return true; // SD ok, but no files written yet
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     while(fs_api->dir.read(&dir, nullptr, name, sizeof(name)) && strlen(name)) { | ||||||
|  |         std::string filename(name); | ||||||
|  |         auto extension_index = filename.rfind(irda_extension); | ||||||
|  |         if((extension_index == std::string::npos) || | ||||||
|  |            (extension_index + strlen(irda_extension) != filename.size())) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         remote_names.push_back(filename.erase(extension_index)); | ||||||
|  |     } | ||||||
|  |     fs_api->dir.close(&dir); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const IrdaMessage* IrdaAppRemoteManager::get_button_data(size_t button_index) const { | bool IrdaAppRemoteManager::load(const std::string& name) { | ||||||
|     furi_check(remotes[current_remote_index].buttons.size() > button_index); |     bool fs_res = false; | ||||||
|     auto& b = remotes[current_remote_index].buttons.at(button_index); |     File file; | ||||||
|     return &b.message; | 
 | ||||||
|  |     fs_res = fs_api->file.open(&file, make_filename(name).c_str(), FSAM_READ, FSOM_OPEN_EXISTING); | ||||||
|  |     if(!fs_res) { | ||||||
|  |         show_file_error_message("Error opening file"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     remote = std::make_unique<IrdaAppRemote>(name); | ||||||
|  | 
 | ||||||
|  |     while(1) { | ||||||
|  |         auto str = getline(fs_api, file, file_buf, sizeof(file_buf), file_buf_cnt); | ||||||
|  |         if(str.empty()) break; | ||||||
|  |         parse_button(str); | ||||||
|  |     } | ||||||
|  |     fs_api->file.close(&file); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void IrdaAppRemoteManager::set_current_remote(size_t index) { | bool IrdaAppRemoteManager::check_fs() const { | ||||||
|     furi_check(index < remotes.size()); |     // TODO: [FL-1431] Add return value to sd_ex_api->check_error() and replace get_fs_info().
 | ||||||
|     current_remote_index = index; |     auto fs_err = fs_api->common.get_fs_info(nullptr, nullptr); | ||||||
| } |     if(fs_err != FSE_OK) show_file_error_message("SD card not found"); | ||||||
| 
 |     return fs_err == FSE_OK; | ||||||
| void IrdaAppRemoteManager::set_current_button(size_t index) { |  | ||||||
|     furi_check(current_remote_index < remotes.size()); |  | ||||||
|     furi_check(index < remotes[current_remote_index].buttons.size()); |  | ||||||
|     current_button_index = index; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void IrdaAppRemoteManager::delete_current_remote() { |  | ||||||
|     remotes.erase(remotes.begin() + current_remote_index); |  | ||||||
|     current_remote_index = 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void IrdaAppRemoteManager::delete_current_button() { |  | ||||||
|     auto& buttons = remotes[current_remote_index].buttons; |  | ||||||
|     buttons.erase(buttons.begin() + current_button_index); |  | ||||||
|     current_button_index = 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| std::string IrdaAppRemoteManager::get_current_button_name() { |  | ||||||
|     auto buttons = remotes[current_remote_index].buttons; |  | ||||||
|     return buttons[current_button_index].name; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| std::string IrdaAppRemoteManager::get_current_remote_name() { |  | ||||||
|     return remotes[current_remote_index].name; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void IrdaAppRemoteManager::rename_remote(const char* str) { |  | ||||||
|     remotes[current_remote_index].name = str; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void IrdaAppRemoteManager::rename_button(const char* str) { |  | ||||||
|     remotes[current_remote_index].buttons[current_button_index].name = str; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| size_t IrdaAppRemoteManager::get_current_remote_buttons_number() { |  | ||||||
|     return remotes[current_remote_index].buttons.size(); |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,9 +1,14 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  | #include "sys/_stdint.h" | ||||||
|  | #include <algorithm> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <string> | #include <string> | ||||||
| #include <list> | #include <list> | ||||||
| #include <vector> | #include <vector> | ||||||
|  | #include <memory> | ||||||
| #include <irda.h> | #include <irda.h> | ||||||
|  | #include <sd-card-api.h> | ||||||
|  | #include <filesystem-api.h> | ||||||
| 
 | 
 | ||||||
| class IrdaAppRemoteButton { | class IrdaAppRemoteButton { | ||||||
|     friend class IrdaAppRemoteManager; |     friend class IrdaAppRemoteManager; | ||||||
| @ -19,35 +24,50 @@ class IrdaAppRemote { | |||||||
|     friend class IrdaAppRemoteManager; |     friend class IrdaAppRemoteManager; | ||||||
|     std::vector<IrdaAppRemoteButton> buttons; |     std::vector<IrdaAppRemoteButton> buttons; | ||||||
|     std::string name; |     std::string name; | ||||||
|     bool add(const IrdaMessage*); |  | ||||||
|     void add_button(size_t remote_index, const char* button_name, const IrdaMessage* message); |  | ||||||
| public: | public: | ||||||
|     IrdaAppRemote(std::string name); |     IrdaAppRemote(const std::string& name); | ||||||
|  |     IrdaAppRemote& operator=(std::string& new_name) noexcept | ||||||
|  |     { | ||||||
|  |         name = new_name; | ||||||
|  |         buttons.clear(); | ||||||
|  |         return *this; | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class IrdaAppRemoteManager { | class IrdaAppRemoteManager { | ||||||
|     size_t current_remote_index; |     static const char* irda_directory; | ||||||
|     size_t current_button_index; |     static const char* irda_extension; | ||||||
|     std::vector<IrdaAppRemote> remotes; |     std::unique_ptr<IrdaAppRemote> remote; | ||||||
| public: |     // TODO: make FS_Api and SdCard_Api unique_ptr
 | ||||||
|     std::vector<std::string> get_remote_list() const; |     SdCard_Api* sd_ex_api; | ||||||
|     std::vector<std::string> get_button_list() const; |     FS_Api* fs_api; | ||||||
|     void add_remote_with_button(const char* button_name, const IrdaMessage* message); |     void show_file_error_message(const char* error_text) const; | ||||||
|     void add_button(const char* button_name, const IrdaMessage* message); |     bool parse_button(std::string& str); | ||||||
|  |     std::string make_filename(const std::string& name) const; | ||||||
|  |     char file_buf[48]; | ||||||
|  |     size_t file_buf_cnt = 0; | ||||||
| 
 | 
 | ||||||
|     size_t get_current_remote(void) const; | public: | ||||||
|     size_t get_current_button(void) const; |     bool add_remote_with_button(const char* button_name, const IrdaMessage* message); | ||||||
|  |     bool add_button(const char* button_name, const IrdaMessage* message); | ||||||
|  | 
 | ||||||
|  |     int find_remote_name(const std::vector<std::string>& strings); | ||||||
|  |     bool rename_button(uint32_t index, const char* str); | ||||||
|  |     bool rename_remote(const char* str); | ||||||
|  | 
 | ||||||
|  |     bool get_remote_list(std::vector<std::string>& remote_names) const; | ||||||
|  |     std::vector<std::string> get_button_list() const; | ||||||
|  |     std::string get_button_name(uint32_t index); | ||||||
|  |     std::string get_remote_name(); | ||||||
|  |     size_t get_number_of_buttons(); | ||||||
|     const IrdaMessage* get_button_data(size_t button_index) const; |     const IrdaMessage* get_button_data(size_t button_index) const; | ||||||
|     void set_current_remote(size_t index); |     bool delete_button(uint32_t index); | ||||||
|     void set_current_button(size_t index); |     bool delete_remote(); | ||||||
|     void rename_button(const char* str); |  | ||||||
|     void rename_remote(const char* str); |  | ||||||
|     std::string get_current_button_name(); |  | ||||||
|     std::string get_current_remote_name(); |  | ||||||
|     size_t get_current_remote_buttons_number(); |  | ||||||
|     void delete_current_button(); |  | ||||||
|     void delete_current_remote(); |  | ||||||
|     IrdaAppRemoteManager(); |     IrdaAppRemoteManager(); | ||||||
|     ~IrdaAppRemoteManager() {}; |     ~IrdaAppRemoteManager(); | ||||||
|  | 
 | ||||||
|  |     bool store(); | ||||||
|  |     bool load(const std::string& name); | ||||||
|  |     bool check_fs() const; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| #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> | ||||||
| @ -154,3 +155,70 @@ void IrdaApp::set_edit_action(IrdaApp::EditAction value) { | |||||||
| IrdaApp::EditAction IrdaApp::get_edit_action(void) { | IrdaApp::EditAction IrdaApp::get_edit_action(void) { | ||||||
|     return action; |     return action; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void IrdaApp::set_current_button(int value) { | ||||||
|  |     current_button = value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int IrdaApp::get_current_button() { | ||||||
|  |     return current_button; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void IrdaApp::notify_success() { | ||||||
|  |     notification_message(notification, &sequence_success); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void IrdaApp::notify_red_blink() { | ||||||
|  |     notification_message(notification, &sequence_blink_red_10); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void IrdaApp::notify_space_blink() { | ||||||
|  |     static const NotificationSequence sequence = { | ||||||
|  |         &message_green_0, | ||||||
|  |         &message_delay_50, | ||||||
|  |         &message_green_255, | ||||||
|  |         &message_do_not_reset, | ||||||
|  |         NULL, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     notification_message_block(notification, &sequence); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void IrdaApp::notify_click() { | ||||||
|  |     static const NotificationSequence sequence = { | ||||||
|  |         &message_click, | ||||||
|  |         &message_delay_1, | ||||||
|  |         &message_sound_off, | ||||||
|  |         NULL, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     notification_message_block(notification, &sequence); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void IrdaApp::notify_click_and_blink() { | ||||||
|  |     static const NotificationSequence sequence = { | ||||||
|  |         &message_click, | ||||||
|  |         &message_delay_1, | ||||||
|  |         &message_sound_off, | ||||||
|  |         &message_red_0, | ||||||
|  |         &message_green_255, | ||||||
|  |         &message_blue_0, | ||||||
|  |         &message_delay_10, | ||||||
|  |         &message_green_0, | ||||||
|  |         NULL, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     notification_message_block(notification, &sequence); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void IrdaApp::notify_double_vibro() { | ||||||
|  |     notification_message(notification, &sequence_double_vibro); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void IrdaApp::notify_green_on() { | ||||||
|  |     notification_message(notification, &sequence_set_only_green_255); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void IrdaApp::notify_green_off() { | ||||||
|  |     notification_message(notification, &sequence_reset_green); | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  | #include "sys/_stdint.h" | ||||||
| #include <map> | #include <map> | ||||||
| #include <irda.h> | #include <irda.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| @ -9,6 +10,7 @@ | |||||||
| #include "irda-app-receiver.hpp" | #include "irda-app-receiver.hpp" | ||||||
| #include <forward_list> | #include <forward_list> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
|  | #include <notification/notification-messages.h> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class IrdaApp { | class IrdaApp { | ||||||
| @ -65,11 +67,29 @@ public: | |||||||
|     bool get_learn_new_remote(); |     bool get_learn_new_remote(); | ||||||
|     void set_learn_new_remote(bool value); |     void set_learn_new_remote(bool value); | ||||||
| 
 | 
 | ||||||
|  |     enum : int { | ||||||
|  |            ButtonNA = -1, | ||||||
|  |     }; | ||||||
|  |     int get_current_button(); | ||||||
|  |     void set_current_button(int value); | ||||||
|  | 
 | ||||||
|  |     void notify_success(); | ||||||
|  |     void notify_red_blink(); | ||||||
|  |     void notify_space_blink(); | ||||||
|  |     void notify_double_vibro(); | ||||||
|  |     void notify_green_on(); | ||||||
|  |     void notify_green_off(); | ||||||
|  |     void notify_click(); | ||||||
|  |     void notify_click_and_blink(); | ||||||
|  | 
 | ||||||
|     static void text_input_callback(void* context, char* text); |     static void text_input_callback(void* context, char* text); | ||||||
|     static void popup_callback(void* context); |     static void popup_callback(void* context); | ||||||
| 
 | 
 | ||||||
|     IrdaApp() {} |     IrdaApp() { | ||||||
|  |         notification = static_cast<NotificationApp*>(furi_record_open("notification")); | ||||||
|  |     } | ||||||
|     ~IrdaApp() { |     ~IrdaApp() { | ||||||
|  |         furi_record_close("notification"); | ||||||
|         for (auto &it : scenes) |         for (auto &it : scenes) | ||||||
|             delete it.second; |             delete it.second; | ||||||
|     } |     } | ||||||
| @ -80,7 +100,9 @@ private: | |||||||
|     bool learn_new_remote; |     bool learn_new_remote; | ||||||
|     EditElement element; |     EditElement element; | ||||||
|     EditAction action; |     EditAction action; | ||||||
|  |     uint32_t current_button; | ||||||
| 
 | 
 | ||||||
|  |     NotificationApp* notification; | ||||||
|     IrdaAppSignalReceiver receiver; |     IrdaAppSignalReceiver receiver; | ||||||
|     IrdaAppViewManager view_manager; |     IrdaAppViewManager view_manager; | ||||||
|     IrdaAppRemoteManager remote_manager; |     IrdaAppRemoteManager remote_manager; | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| #include "../irda-app.hpp" | #include "../irda-app.hpp" | ||||||
| #include "irda.h" | #include "irda.h" | ||||||
|  | #include "irda/scene/irda-app-scene.hpp" | ||||||
| #include <string> | #include <string> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| 
 | 
 | ||||||
| @ -20,12 +21,12 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) { | |||||||
|     auto remote_manager = app->get_remote_manager(); |     auto remote_manager = app->get_remote_manager(); | ||||||
| 
 | 
 | ||||||
|     if(app->get_edit_element() == IrdaApp::EditElement::Button) { |     if(app->get_edit_element() == IrdaApp::EditElement::Button) { | ||||||
|         auto message = remote_manager->get_button_data(remote_manager->get_current_button()); |         auto message = remote_manager->get_button_data(app->get_current_button()); | ||||||
|         dialog_ex_set_header(dialog_ex, "Delete button?", 64, 6, AlignCenter, AlignCenter); |         dialog_ex_set_header(dialog_ex, "Delete button?", 64, 6, AlignCenter, AlignCenter); | ||||||
|         app->set_text_store( |         app->set_text_store( | ||||||
|             0, |             0, | ||||||
|             "%s\n%s\nA=0x%0*lX C=0x%0*lX", |             "%s\n%s\nA=0x%0*lX C=0x%0*lX", | ||||||
|             remote_manager->get_current_button_name().c_str(), |             remote_manager->get_button_name(app->get_current_button()).c_str(), | ||||||
|             irda_get_protocol_name(message->protocol), |             irda_get_protocol_name(message->protocol), | ||||||
|             irda_get_protocol_address_length(message->protocol), |             irda_get_protocol_address_length(message->protocol), | ||||||
|             message->address, |             message->address, | ||||||
| @ -36,8 +37,8 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) { | |||||||
|         app->set_text_store( |         app->set_text_store( | ||||||
|             0, |             0, | ||||||
|             "%s\n with %lu buttons", |             "%s\n with %lu buttons", | ||||||
|             remote_manager->get_current_remote_name().c_str(), |             remote_manager->get_remote_name().c_str(), | ||||||
|             remote_manager->get_current_remote_buttons_number()); |             remote_manager->get_number_of_buttons()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     dialog_ex_set_text(dialog_ex, app->get_text_store(0), 64, 32, AlignCenter, AlignCenter); |     dialog_ex_set_text(dialog_ex, app->get_text_store(0), 64, 32, AlignCenter, AlignCenter); | ||||||
| @ -63,13 +64,20 @@ bool IrdaAppSceneEditDelete::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
|             break; |             break; | ||||||
|         case DialogExResultRight: |         case DialogExResultRight: | ||||||
|             auto remote_manager = app->get_remote_manager(); |             auto remote_manager = app->get_remote_manager(); | ||||||
|  |             bool result = false; | ||||||
|             if(app->get_edit_element() == IrdaApp::EditElement::Remote) { |             if(app->get_edit_element() == IrdaApp::EditElement::Remote) { | ||||||
|                 remote_manager->delete_current_remote(); |                 result = remote_manager->delete_remote(); | ||||||
|             } else { |             } else { | ||||||
|                 remote_manager->delete_current_button(); |                 result = remote_manager->delete_button(app->get_current_button()); | ||||||
|  |                 app->set_current_button(IrdaApp::ButtonNA); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             app->switch_to_next_scene(IrdaApp::Scene::EditDeleteDone); |             if(!result) { | ||||||
|  |                 app->search_and_switch_to_previous_scene( | ||||||
|  |                     {IrdaApp::Scene::RemoteList, IrdaApp::Scene::Start}); | ||||||
|  |             } else { | ||||||
|  |                 app->switch_to_next_scene(IrdaApp::Scene::EditDeleteDone); | ||||||
|  |             } | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ static void submenu_callback(void* context, uint32_t index) { | |||||||
| void IrdaAppSceneEditKeySelect::on_enter(IrdaApp* app) { | void IrdaAppSceneEditKeySelect::on_enter(IrdaApp* app) { | ||||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); |     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     Submenu* submenu = view_manager->get_submenu(); |     Submenu* submenu = view_manager->get_submenu(); | ||||||
|     int i = 0; |     int item_number = 0; | ||||||
| 
 | 
 | ||||||
|     const char* header = app->get_edit_action() == IrdaApp::EditAction::Rename ? "Rename key:" : |     const char* header = app->get_edit_action() == IrdaApp::EditAction::Rename ? "Rename key:" : | ||||||
|                                                                                  "Delete key:"; |                                                                                  "Delete key:"; | ||||||
| @ -23,7 +23,11 @@ void IrdaAppSceneEditKeySelect::on_enter(IrdaApp* app) { | |||||||
|     auto remote_manager = app->get_remote_manager(); |     auto remote_manager = app->get_remote_manager(); | ||||||
|     buttons_names = remote_manager->get_button_list(); |     buttons_names = remote_manager->get_button_list(); | ||||||
|     for(const auto& it : buttons_names) { |     for(const auto& it : buttons_names) { | ||||||
|         submenu_add_item(submenu, it.c_str(), i++, submenu_callback, app); |         submenu_add_item(submenu, it.c_str(), item_number++, submenu_callback, app); | ||||||
|  |     } | ||||||
|  |     if((item_number > 0) && (app->get_current_button() != IrdaApp::ButtonNA)) { | ||||||
|  |         submenu_set_selected_item(submenu, app->get_current_button()); | ||||||
|  |         app->set_current_button(IrdaApp::ButtonNA); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); |     view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); | ||||||
| @ -33,8 +37,7 @@ bool IrdaAppSceneEditKeySelect::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event->type == IrdaAppEvent::Type::MenuSelected) { |     if(event->type == IrdaAppEvent::Type::MenuSelected) { | ||||||
|         auto remote_manager = app->get_remote_manager(); |         app->set_current_button(event->payload.menu_index); | ||||||
|         remote_manager->set_current_button(event->payload.menu_index); |  | ||||||
|         consumed = true; |         consumed = true; | ||||||
|         if(app->get_edit_action() == IrdaApp::EditAction::Rename) { |         if(app->get_edit_action() == IrdaApp::EditAction::Rename) { | ||||||
|             app->switch_to_next_scene(IrdaApp::Scene::EditRename); |             app->switch_to_next_scene(IrdaApp::Scene::EditRename); | ||||||
|  | |||||||
| @ -7,10 +7,11 @@ void IrdaAppSceneEditRename::on_enter(IrdaApp* app) { | |||||||
| 
 | 
 | ||||||
|     auto remote_manager = app->get_remote_manager(); |     auto remote_manager = app->get_remote_manager(); | ||||||
|     if(app->get_edit_element() == IrdaApp::EditElement::Button) { |     if(app->get_edit_element() == IrdaApp::EditElement::Button) { | ||||||
|         auto button_name = remote_manager->get_current_button_name(); |         furi_assert(app->get_current_button() != IrdaApp::ButtonNA); | ||||||
|  |         auto button_name = remote_manager->get_button_name(app->get_current_button()); | ||||||
|         strncpy(app->get_text_store(0), button_name.c_str(), app->get_text_store_size()); |         strncpy(app->get_text_store(0), button_name.c_str(), app->get_text_store_size()); | ||||||
|     } else { |     } else { | ||||||
|         auto remote_name = remote_manager->get_current_remote_name(); |         auto remote_name = remote_manager->get_remote_name(); | ||||||
|         strncpy(app->get_text_store(0), remote_name.c_str(), app->get_text_store_size()); |         strncpy(app->get_text_store(0), remote_name.c_str(), app->get_text_store_size()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -30,12 +31,20 @@ bool IrdaAppSceneEditRename::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(); | ||||||
|  |         bool result = false; | ||||||
|         if(app->get_edit_element() == IrdaApp::EditElement::Button) { |         if(app->get_edit_element() == IrdaApp::EditElement::Button) { | ||||||
|             remote_manager->rename_button(app->get_text_store(0)); |             result = | ||||||
|  |                 remote_manager->rename_button(app->get_current_button(), app->get_text_store(0)); | ||||||
|  |             app->set_current_button(IrdaApp::ButtonNA); | ||||||
|         } else { |         } else { | ||||||
|             remote_manager->rename_remote(app->get_text_store(0)); |             result = remote_manager->rename_remote(app->get_text_store(0)); | ||||||
|  |         } | ||||||
|  |         if(!result) { | ||||||
|  |             app->search_and_switch_to_previous_scene( | ||||||
|  |                 {IrdaApp::Scene::Start, IrdaApp::Scene::RemoteList}); | ||||||
|  |         } else { | ||||||
|  |             app->switch_to_next_scene_without_saving(IrdaApp::Scene::EditRenameDone); | ||||||
|         } |         } | ||||||
|         app->switch_to_next_scene_without_saving(IrdaApp::Scene::EditRenameDone); |  | ||||||
|         consumed = true; |         consumed = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| #include "../irda-app.hpp" | #include "../irda-app.hpp" | ||||||
|  | #include "gui/modules/submenu.h" | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     SubmenuIndexAddKey, |     SubmenuIndexAddKey, | ||||||
| @ -27,6 +28,8 @@ void IrdaAppSceneEdit::on_enter(IrdaApp* app) { | |||||||
|     submenu_add_item(submenu, "Delete key", SubmenuIndexDeleteKey, submenu_callback, app); |     submenu_add_item(submenu, "Delete key", SubmenuIndexDeleteKey, submenu_callback, app); | ||||||
|     submenu_add_item(submenu, "Rename remote", SubmenuIndexRenameRemote, submenu_callback, app); |     submenu_add_item(submenu, "Rename remote", SubmenuIndexRenameRemote, submenu_callback, app); | ||||||
|     submenu_add_item(submenu, "Delete remote", SubmenuIndexDeleteRemote, submenu_callback, app); |     submenu_add_item(submenu, "Delete remote", SubmenuIndexDeleteRemote, submenu_callback, app); | ||||||
|  |     submenu_set_selected_item(submenu, submenu_item_selected); | ||||||
|  |     submenu_item_selected = 0; | ||||||
| 
 | 
 | ||||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); |     view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); | ||||||
| } | } | ||||||
| @ -35,8 +38,10 @@ bool IrdaAppSceneEdit::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event->type == IrdaAppEvent::Type::MenuSelected) { |     if(event->type == IrdaAppEvent::Type::MenuSelected) { | ||||||
|  |         submenu_item_selected = event->payload.menu_index; | ||||||
|         switch(event->payload.menu_index) { |         switch(event->payload.menu_index) { | ||||||
|         case SubmenuIndexAddKey: |         case SubmenuIndexAddKey: | ||||||
|  |             app->set_learn_new_remote(false); | ||||||
|             app->switch_to_next_scene(IrdaApp::Scene::Learn); |             app->switch_to_next_scene(IrdaApp::Scene::Learn); | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexRenameKey: |         case SubmenuIndexRenameKey: | ||||||
|  | |||||||
| @ -35,14 +35,21 @@ 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 receiver = app->get_receiver(); | ||||||
|  |         bool result = false; | ||||||
|         if(app->get_learn_new_remote()) { |         if(app->get_learn_new_remote()) { | ||||||
|             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), receiver->get_last_message()); | ||||||
|         } else { |         } else { | ||||||
|             remote_manager->add_button(app->get_text_store(0), receiver->get_last_message()); |             result = | ||||||
|  |                 remote_manager->add_button(app->get_text_store(0), receiver->get_last_message()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnDone); |         if(!result) { | ||||||
|  |             app->search_and_switch_to_previous_scene( | ||||||
|  |                 {IrdaApp::Scene::Start, IrdaApp::Scene::RemoteList}); | ||||||
|  |         } else { | ||||||
|  |             app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnDone); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
|  | |||||||
| @ -17,6 +17,8 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) { | |||||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); |     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     DialogEx* dialog_ex = view_manager->get_dialog_ex(); |     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||||
| 
 | 
 | ||||||
|  |     app->notify_green_on(); | ||||||
|  | 
 | ||||||
|     auto receiver = app->get_receiver(); |     auto receiver = app->get_receiver(); | ||||||
|     auto message = receiver->get_last_message(); |     auto message = receiver->get_last_message(); | ||||||
| 
 | 
 | ||||||
| @ -32,6 +34,7 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) { | |||||||
|     dialog_ex_set_text(dialog_ex, app->get_text_store(1), 75, 23, AlignLeft, AlignTop); |     dialog_ex_set_text(dialog_ex, app->get_text_store(1), 75, 23, AlignLeft, AlignTop); | ||||||
|     dialog_ex_set_left_button_text(dialog_ex, "Retry"); |     dialog_ex_set_left_button_text(dialog_ex, "Retry"); | ||||||
|     dialog_ex_set_right_button_text(dialog_ex, "Save"); |     dialog_ex_set_right_button_text(dialog_ex, "Save"); | ||||||
|  |     dialog_ex_set_center_button_text(dialog_ex, "Send"); | ||||||
|     dialog_ex_set_icon(dialog_ex, 0, 1, I_DolphinExcited_64x63); |     dialog_ex_set_icon(dialog_ex, 0, 1, I_DolphinExcited_64x63); | ||||||
|     dialog_ex_set_result_callback(dialog_ex, dialog_result_callback); |     dialog_ex_set_result_callback(dialog_ex, dialog_result_callback); | ||||||
|     dialog_ex_set_context(dialog_ex, app); |     dialog_ex_set_context(dialog_ex, app); | ||||||
| @ -47,11 +50,20 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
|         case DialogExResultLeft: |         case DialogExResultLeft: | ||||||
|             app->switch_to_next_scene_without_saving(IrdaApp::Scene::Learn); |             app->switch_to_next_scene_without_saving(IrdaApp::Scene::Learn); | ||||||
|             break; |             break; | ||||||
|         case DialogExResultCenter: |         case DialogExResultCenter: { | ||||||
|             furi_assert(0); |             app->notify_space_blink(); | ||||||
|  |             auto receiver = app->get_receiver(); | ||||||
|  |             auto message = receiver->get_last_message(); | ||||||
|  |             irda_send(message, 1); | ||||||
|             break; |             break; | ||||||
|  |         } | ||||||
|         case DialogExResultRight: |         case DialogExResultRight: | ||||||
|             app->switch_to_next_scene(IrdaApp::Scene::LearnEnterName); |             auto remote_manager = app->get_remote_manager(); | ||||||
|  |             if(remote_manager->check_fs()) { | ||||||
|  |                 app->switch_to_next_scene(IrdaApp::Scene::LearnEnterName); | ||||||
|  |             } else { | ||||||
|  |                 app->switch_to_previous_scene(); | ||||||
|  |             } | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -60,4 +72,8 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void IrdaAppSceneLearnSuccess::on_exit(IrdaApp* app) { | void IrdaAppSceneLearnSuccess::on_exit(IrdaApp* app) { | ||||||
|  |     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||||
|  |     DialogEx* dialog_ex = view_manager->get_dialog_ex(); | ||||||
|  |     dialog_ex_set_center_button_text(dialog_ex, nullptr); | ||||||
|  |     app->notify_green_off(); | ||||||
| } | } | ||||||
|  | |||||||
| @ -14,13 +14,22 @@ void IrdaAppSceneLearn::on_enter(IrdaApp* app) { | |||||||
|         popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter); |         popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter); | ||||||
|     popup_set_callback(popup, NULL); |     popup_set_callback(popup, NULL); | ||||||
| 
 | 
 | ||||||
|  |     if(app->get_learn_new_remote()) { | ||||||
|  |         app->notify_double_vibro(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Popup); |     view_manager->switch_to(IrdaAppViewManager::ViewType::Popup); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool IrdaAppSceneLearn::on_event(IrdaApp* app, IrdaAppEvent* event) { | bool IrdaAppSceneLearn::on_event(IrdaApp* app, IrdaAppEvent* event) { | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|  |     if(event->type == IrdaAppEvent::Type::Tick) { | ||||||
|  |         consumed = true; | ||||||
|  |         app->notify_red_blink(); | ||||||
|  |     } | ||||||
|     if(event->type == IrdaAppEvent::Type::IrdaMessageReceived) { |     if(event->type == IrdaAppEvent::Type::IrdaMessageReceived) { | ||||||
|  |         app->notify_success(); | ||||||
|         app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnSuccess); |         app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnSuccess); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -20,13 +20,26 @@ void IrdaAppSceneRemoteList::on_enter(IrdaApp* app) { | |||||||
|     auto remote_manager = app->get_remote_manager(); |     auto remote_manager = app->get_remote_manager(); | ||||||
|     int i = 0; |     int i = 0; | ||||||
| 
 | 
 | ||||||
|     remote_names = remote_manager->get_remote_list(); |     bool result = remote_manager->get_remote_list(remote_names); | ||||||
|     for(auto& a : remote_names) { |     if(!result) { | ||||||
|         submenu_add_item(submenu, a.c_str(), i++, submenu_callback, app); |         app->switch_to_previous_scene(); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for(auto& name : remote_names) { | ||||||
|  |         submenu_add_item(submenu, name.c_str(), i++, submenu_callback, app); | ||||||
|     } |     } | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         submenu, "                           +", SubmenuIndexPlus, submenu_callback, app); |         submenu, "                           +", SubmenuIndexPlus, submenu_callback, app); | ||||||
| 
 | 
 | ||||||
|  |     if((SubmenuIndex)submenu_item_selected == SubmenuIndexPlus) { | ||||||
|  |         submenu_set_selected_item(submenu, submenu_item_selected); | ||||||
|  |     } else { | ||||||
|  |         int remote_index = remote_manager->find_remote_name(remote_names); | ||||||
|  |         submenu_set_selected_item(submenu, (remote_index >= 0) ? remote_index : 0); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     submenu_item_selected = 0; | ||||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); |     view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -38,11 +51,14 @@ bool IrdaAppSceneRemoteList::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
|         case SubmenuIndexPlus: |         case SubmenuIndexPlus: | ||||||
|             app->set_learn_new_remote(true); |             app->set_learn_new_remote(true); | ||||||
|             app->switch_to_next_scene(IrdaApp::Scene::Learn); |             app->switch_to_next_scene(IrdaApp::Scene::Learn); | ||||||
|  |             submenu_item_selected = event->payload.menu_index; | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|             auto remote_manager = app->get_remote_manager(); |             auto remote_manager = app->get_remote_manager(); | ||||||
|             remote_manager->set_current_remote(event->payload.menu_index); |             bool result = remote_manager->load(remote_names.at(event->payload.menu_index)); | ||||||
|             app->switch_to_next_scene(IrdaApp::Scene::Remote); |             if(result) { | ||||||
|  |                 app->switch_to_next_scene(IrdaApp::Scene::Remote); | ||||||
|  |             } | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ | |||||||
| typedef enum { | typedef enum { | ||||||
|     ButtonIndexPlus = -2, |     ButtonIndexPlus = -2, | ||||||
|     ButtonIndexEdit = -1, |     ButtonIndexEdit = -1, | ||||||
|  |     ButtonIndexNA = 0, | ||||||
| } ButtonIndex; | } ButtonIndex; | ||||||
| 
 | 
 | ||||||
| static void button_menu_callback(void* context, int32_t index) { | static void button_menu_callback(void* context, int32_t index) { | ||||||
| @ -35,8 +36,12 @@ void IrdaAppSceneRemote::on_enter(IrdaApp* app) { | |||||||
|     button_menu_add_item( |     button_menu_add_item( | ||||||
|         button_menu, "Edit", ButtonIndexEdit, button_menu_callback, ButtonMenuItemTypeControl, app); |         button_menu, "Edit", ButtonIndexEdit, button_menu_callback, ButtonMenuItemTypeControl, app); | ||||||
| 
 | 
 | ||||||
|     app->set_text_store(0, "%s", remote_manager->get_current_remote_name().c_str()); |     app->set_text_store(0, "%s", remote_manager->get_remote_name().c_str()); | ||||||
|     button_menu_set_header(button_menu, app->get_text_store(0)); |     button_menu_set_header(button_menu, app->get_text_store(0)); | ||||||
|  |     if(buttonmenu_item_selected != ButtonIndexNA) { | ||||||
|  |         button_menu_set_selected_item(button_menu, buttonmenu_item_selected); | ||||||
|  |         buttonmenu_item_selected = ButtonIndexNA; | ||||||
|  |     } | ||||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::ButtonMenu); |     view_manager->switch_to(IrdaAppViewManager::ViewType::ButtonMenu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -46,12 +51,18 @@ bool IrdaAppSceneRemote::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
|     if(event->type == IrdaAppEvent::Type::MenuSelected) { |     if(event->type == IrdaAppEvent::Type::MenuSelected) { | ||||||
|         switch(event->payload.menu_index) { |         switch(event->payload.menu_index) { | ||||||
|         case ButtonIndexPlus: |         case ButtonIndexPlus: | ||||||
|  |             app->notify_click(); | ||||||
|  |             buttonmenu_item_selected = event->payload.menu_index; | ||||||
|  |             app->set_learn_new_remote(false); | ||||||
|             app->switch_to_next_scene(IrdaApp::Scene::Learn); |             app->switch_to_next_scene(IrdaApp::Scene::Learn); | ||||||
|             break; |             break; | ||||||
|         case ButtonIndexEdit: |         case ButtonIndexEdit: | ||||||
|  |             app->notify_click(); | ||||||
|  |             buttonmenu_item_selected = event->payload.menu_index; | ||||||
|             app->switch_to_next_scene(IrdaApp::Scene::Edit); |             app->switch_to_next_scene(IrdaApp::Scene::Edit); | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|  |             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_receiver()->send_message(message); | ||||||
|  | |||||||
| @ -25,6 +25,8 @@ void IrdaAppSceneStart::on_enter(IrdaApp* app) { | |||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         submenu, "Learn new remote", SubmenuIndexLearnNewRemote, submenu_callback, app); |         submenu, "Learn new remote", SubmenuIndexLearnNewRemote, submenu_callback, app); | ||||||
|     submenu_add_item(submenu, "Saved remotes", SubmenuIndexSavedRemotes, submenu_callback, app); |     submenu_add_item(submenu, "Saved remotes", SubmenuIndexSavedRemotes, submenu_callback, app); | ||||||
|  |     submenu_set_selected_item(submenu, submenu_item_selected); | ||||||
|  |     submenu_item_selected = 0; | ||||||
| 
 | 
 | ||||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); |     view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); | ||||||
| } | } | ||||||
| @ -33,6 +35,7 @@ bool IrdaAppSceneStart::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event->type == IrdaAppEvent::Type::MenuSelected) { |     if(event->type == IrdaAppEvent::Type::MenuSelected) { | ||||||
|  |         submenu_item_selected = event->payload.menu_index; | ||||||
|         switch(event->payload.menu_index) { |         switch(event->payload.menu_index) { | ||||||
|         case SubmenuIndexUniversalLibrary: |         case SubmenuIndexUniversalLibrary: | ||||||
|             app->switch_to_next_scene(IrdaApp::Scene::Universal); |             app->switch_to_next_scene(IrdaApp::Scene::Universal); | ||||||
| @ -44,6 +47,9 @@ bool IrdaAppSceneStart::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
|         case SubmenuIndexSavedRemotes: |         case SubmenuIndexSavedRemotes: | ||||||
|             app->switch_to_next_scene(IrdaApp::Scene::RemoteList); |             app->switch_to_next_scene(IrdaApp::Scene::RemoteList); | ||||||
|             break; |             break; | ||||||
|  |         default: | ||||||
|  |             furi_assert(0); | ||||||
|  |             break; | ||||||
|         } |         } | ||||||
|         consumed = true; |         consumed = true; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -24,6 +24,8 @@ void IrdaAppSceneUniversal::on_enter(IrdaApp* app) { | |||||||
|     submenu_add_item(submenu, "Audio Players", SubmenuIndexUniversalAudio, submenu_callback, app); |     submenu_add_item(submenu, "Audio Players", SubmenuIndexUniversalAudio, submenu_callback, app); | ||||||
|     submenu_add_item( |     submenu_add_item( | ||||||
|         submenu, "Air Conditioners", SubmenuIndexUniversalAirConditioner, submenu_callback, app); |         submenu, "Air Conditioners", SubmenuIndexUniversalAirConditioner, submenu_callback, app); | ||||||
|  |     submenu_set_selected_item(submenu, submenu_item_selected); | ||||||
|  |     submenu_item_selected = 0; | ||||||
| 
 | 
 | ||||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); |     view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); | ||||||
| } | } | ||||||
| @ -32,6 +34,7 @@ bool IrdaAppSceneUniversal::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
| 
 | 
 | ||||||
|     if(event->type == IrdaAppEvent::Type::MenuSelected) { |     if(event->type == IrdaAppEvent::Type::MenuSelected) { | ||||||
|  |         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);
 | ||||||
|  | |||||||
| @ -23,6 +23,8 @@ public: | |||||||
|     void on_enter(IrdaApp* app) final; |     void on_enter(IrdaApp* app) final; | ||||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; |     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||||
|     void on_exit(IrdaApp* app) final; |     void on_exit(IrdaApp* app) final; | ||||||
|  | private: | ||||||
|  |     uint32_t submenu_item_selected = 0; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class IrdaAppSceneUniversal : public IrdaAppScene { | class IrdaAppSceneUniversal : public IrdaAppScene { | ||||||
| @ -30,6 +32,8 @@ public: | |||||||
|     void on_enter(IrdaApp* app) final; |     void on_enter(IrdaApp* app) final; | ||||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; |     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||||
|     void on_exit(IrdaApp* app) final; |     void on_exit(IrdaApp* app) final; | ||||||
|  | private: | ||||||
|  |     uint32_t submenu_item_selected = 0; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class IrdaAppSceneLearn : public IrdaAppScene { | class IrdaAppSceneLearn : public IrdaAppScene { | ||||||
| @ -74,6 +78,7 @@ public: | |||||||
|     void on_exit(IrdaApp* app) final; |     void on_exit(IrdaApp* app) final; | ||||||
| private: | private: | ||||||
|     std::vector<std::string> buttons_names; |     std::vector<std::string> buttons_names; | ||||||
|  |     uint32_t buttonmenu_item_selected = 0; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class IrdaAppSceneRemoteList : public IrdaAppScene { | class IrdaAppSceneRemoteList : public IrdaAppScene { | ||||||
| @ -81,6 +86,8 @@ public: | |||||||
|     void on_enter(IrdaApp* app) final; |     void on_enter(IrdaApp* app) final; | ||||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; |     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||||
|     void on_exit(IrdaApp* app) final; |     void on_exit(IrdaApp* app) final; | ||||||
|  | private: | ||||||
|  |     uint32_t submenu_item_selected = 0; | ||||||
|     std::vector<std::string> remote_names; |     std::vector<std::string> remote_names; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -89,6 +96,8 @@ public: | |||||||
|     void on_enter(IrdaApp* app) final; |     void on_enter(IrdaApp* app) final; | ||||||
|     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; |     bool on_event(IrdaApp* app, IrdaAppEvent* event) final; | ||||||
|     void on_exit(IrdaApp* app) final; |     void on_exit(IrdaApp* app) final; | ||||||
|  | private: | ||||||
|  |     uint32_t submenu_item_selected = 0; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class IrdaAppSceneEditKeySelect : public IrdaAppScene { | class IrdaAppSceneEditKeySelect : public IrdaAppScene { | ||||||
|  | |||||||
| @ -26,6 +26,11 @@ for octave in range(9): | |||||||
|         print(f"extern const NotificationMessage message_note_{name}{octave};") |         print(f"extern const NotificationMessage message_note_{name}{octave};") | ||||||
| */ | */ | ||||||
| 
 | 
 | ||||||
|  | const NotificationMessage message_click = { | ||||||
|  |     .type = NotificationMessageTypeSoundOn, | ||||||
|  |     .data.sound.frequency = 1.0f, | ||||||
|  |     .data.sound.pwm = 0.5f, | ||||||
|  | }; | ||||||
| const NotificationMessage message_note_c0 = { | const NotificationMessage message_note_c0 = { | ||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 16.35f, |     .data.sound.frequency = 16.35f, | ||||||
| @ -565,4 +570,4 @@ const NotificationMessage message_note_b8 = { | |||||||
|     .type = NotificationMessageTypeSoundOn, |     .type = NotificationMessageTypeSoundOn, | ||||||
|     .data.sound.frequency = 7902.13f, |     .data.sound.frequency = 7902.13f, | ||||||
|     .data.sound.pwm = 0.5f, |     .data.sound.pwm = 0.5f, | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | extern const NotificationMessage message_click; | ||||||
| extern const NotificationMessage message_note_c0; | extern const NotificationMessage message_note_c0; | ||||||
| extern const NotificationMessage message_note_cs0; | extern const NotificationMessage message_note_cs0; | ||||||
| extern const NotificationMessage message_note_d0; | extern const NotificationMessage message_note_d0; | ||||||
|  | |||||||
| @ -48,6 +48,11 @@ const NotificationMessage message_blue_0 = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // Delay
 | // Delay
 | ||||||
|  | const NotificationMessage message_delay_1 = { | ||||||
|  |     .type = NotificationMessageTypeDelay, | ||||||
|  |     .data.delay.length = 1, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| const NotificationMessage message_delay_10 = { | const NotificationMessage message_delay_10 = { | ||||||
|     .type = NotificationMessageTypeDelay, |     .type = NotificationMessageTypeDelay, | ||||||
|     .data.delay.length = 10, |     .data.delay.length = 10, | ||||||
| @ -113,12 +118,12 @@ const NotificationSequence sequence_reset_red = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const NotificationSequence sequence_reset_green = { | const NotificationSequence sequence_reset_green = { | ||||||
|     &message_blue_0, |     &message_green_0, | ||||||
|     NULL, |     NULL, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const NotificationSequence sequence_reset_blue = { | const NotificationSequence sequence_reset_blue = { | ||||||
|     &message_green_0, |     &message_blue_0, | ||||||
|     NULL, |     NULL, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -298,6 +303,17 @@ const NotificationSequence sequence_blink_white_100 = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // General
 | // General
 | ||||||
|  | const NotificationSequence sequence_double_vibro = { | ||||||
|  |     &message_vibro_on, | ||||||
|  |     &message_delay_100, | ||||||
|  |     &message_vibro_off, | ||||||
|  |     &message_delay_100, | ||||||
|  |     &message_vibro_on, | ||||||
|  |     &message_delay_100, | ||||||
|  |     &message_vibro_off, | ||||||
|  |     NULL, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| const NotificationSequence sequence_success = { | const NotificationSequence sequence_success = { | ||||||
|     &message_display_on, |     &message_display_on, | ||||||
|     &message_green_255, |     &message_green_255, | ||||||
|  | |||||||
| @ -23,6 +23,7 @@ extern const NotificationMessage message_green_0; | |||||||
| extern const NotificationMessage message_blue_0; | extern const NotificationMessage message_blue_0; | ||||||
| 
 | 
 | ||||||
| // Delay
 | // Delay
 | ||||||
|  | extern const NotificationMessage message_delay_1; | ||||||
| extern const NotificationMessage message_delay_10; | extern const NotificationMessage message_delay_10; | ||||||
| extern const NotificationMessage message_delay_25; | extern const NotificationMessage message_delay_25; | ||||||
| extern const NotificationMessage message_delay_50; | extern const NotificationMessage message_delay_50; | ||||||
| @ -87,6 +88,7 @@ extern const NotificationSequence sequence_blink_magenta_100; | |||||||
| extern const NotificationSequence sequence_blink_white_100; | extern const NotificationSequence sequence_blink_white_100; | ||||||
| 
 | 
 | ||||||
| // General
 | // General
 | ||||||
|  | extern const NotificationSequence sequence_double_vibro; | ||||||
| extern const NotificationSequence sequence_success; | extern const NotificationSequence sequence_success; | ||||||
| extern const NotificationSequence sequence_error; | extern const NotificationSequence sequence_error; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -49,6 +49,12 @@ void api_hal_irda_pwm_set(float duty_cycle, float freq); | |||||||
|  */ |  */ | ||||||
| void api_hal_irda_pwm_stop(); | void api_hal_irda_pwm_stop(); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Check if IRDA is in use now. | ||||||
|  |  * @return  false - IRDA is busy, true otherwise. | ||||||
|  |  */ | ||||||
|  | bool api_hal_irda_rx_irq_is_busy(void); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -97,6 +97,10 @@ void api_hal_irda_rx_irq_deinit(void) { | |||||||
|     LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2); |     LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool api_hal_irda_rx_irq_is_busy(void) { | ||||||
|  |     return (LL_TIM_IsEnabledIT_CC1(TIM2) || LL_TIM_IsEnabledIT_CC2(TIM2)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void api_hal_irda_rx_irq_set_callback(TimerISRCallback callback, void *ctx) { | void api_hal_irda_rx_irq_set_callback(TimerISRCallback callback, void *ctx) { | ||||||
|     furi_check(callback); |     furi_check(callback); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -97,6 +97,10 @@ void api_hal_irda_rx_irq_deinit(void) { | |||||||
|     LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2); |     LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool api_hal_irda_rx_irq_is_busy(void) { | ||||||
|  |     return (LL_TIM_IsEnabledIT_CC1(TIM2) || LL_TIM_IsEnabledIT_CC2(TIM2)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void api_hal_irda_rx_irq_set_callback(TimerISRCallback callback, void *ctx) { | void api_hal_irda_rx_irq_set_callback(TimerISRCallback callback, void *ctx) { | ||||||
|     furi_check(callback); |     furi_check(callback); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,3 +1,4 @@ | |||||||
|  | #include "irda.h" | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| @ -58,7 +59,7 @@ static const IrdaProtocolImplementation irda_protocols[] = { | |||||||
|       .address_length = 2, |       .address_length = 2, | ||||||
|       .command_length = 2, |       .command_length = 2, | ||||||
|     }, |     }, | ||||||
|     // #2
 |     // #2 - have to be after NEC
 | ||||||
|     { .protocol = IrdaProtocolNECext, |     { .protocol = IrdaProtocolNECext, | ||||||
|       .name = "NECext", |       .name = "NECext", | ||||||
|       .decoder = { |       .decoder = { | ||||||
| @ -81,10 +82,12 @@ const IrdaMessage* irda_decode(IrdaHandler* handler, bool level, uint32_t durati | |||||||
|     IrdaMessage* result = NULL; |     IrdaMessage* result = NULL; | ||||||
| 
 | 
 | ||||||
|     for (int i = 0; i < COUNT_OF(irda_protocols); ++i) { |     for (int i = 0; i < COUNT_OF(irda_protocols); ++i) { | ||||||
|         message = irda_protocols[i].decoder.decode(handler->ctx[i], level, duration); |         if (irda_protocols[i].decoder.decode) { | ||||||
|         if (!result && message) { |             message = irda_protocols[i].decoder.decode(handler->ctx[i], level, duration); | ||||||
|             message->protocol = irda_protocols[i].protocol; |             if (!result && message) { | ||||||
|             result = message; |                 message->protocol = irda_protocols[i].protocol; | ||||||
|  |                 result = message; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -96,8 +99,9 @@ IrdaHandler* irda_alloc_decoder(void) { | |||||||
|     handler->ctx = furi_alloc(sizeof(void*) * COUNT_OF(irda_protocols)); |     handler->ctx = furi_alloc(sizeof(void*) * COUNT_OF(irda_protocols)); | ||||||
| 
 | 
 | ||||||
|     for (int i = 0; i < COUNT_OF(irda_protocols); ++i) { |     for (int i = 0; i < COUNT_OF(irda_protocols); ++i) { | ||||||
|         handler->ctx[i] = irda_protocols[i].decoder.alloc(); |         handler->ctx[i] = 0; | ||||||
|         furi_check(handler->ctx[i]); |         if (irda_protocols[i].decoder.alloc) | ||||||
|  |             handler->ctx[i] = irda_protocols[i].decoder.alloc(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return handler; |     return handler; | ||||||
| @ -108,7 +112,8 @@ void irda_free_decoder(IrdaHandler* handler) { | |||||||
|     furi_assert(handler->ctx); |     furi_assert(handler->ctx); | ||||||
| 
 | 
 | ||||||
|     for (int i = 0; i < COUNT_OF(irda_protocols); ++i) { |     for (int i = 0; i < COUNT_OF(irda_protocols); ++i) { | ||||||
|         irda_protocols[i].decoder.free(handler->ctx[i]); |         if (irda_protocols[i].decoder.free) | ||||||
|  |             irda_protocols[i].decoder.free(handler->ctx[i]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     free(handler->ctx); |     free(handler->ctx); | ||||||
| @ -117,31 +122,54 @@ void irda_free_decoder(IrdaHandler* handler) { | |||||||
| 
 | 
 | ||||||
| void irda_reset_decoder(IrdaHandler* handler) { | void irda_reset_decoder(IrdaHandler* handler) { | ||||||
|     for (int i = 0; i < COUNT_OF(irda_protocols); ++i) { |     for (int i = 0; i < COUNT_OF(irda_protocols); ++i) { | ||||||
|         irda_protocols[i].decoder.reset(handler->ctx[i]); |         if (irda_protocols[i].decoder.reset) | ||||||
|  |             irda_protocols[i].decoder.reset(handler->ctx[i]); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void irda_send(const IrdaMessage* message, int times) { | void irda_send(const IrdaMessage* message, int times) { | ||||||
|     furi_assert(message); |     furi_assert(message); | ||||||
|  |     furi_assert(irda_is_protocol_valid(message->protocol)); | ||||||
| 
 | 
 | ||||||
|     for (int i = 0; i < times; ++i) { |     for (int i = 0; i < times; ++i) { | ||||||
|         osKernelLock(); |         if(irda_protocols[message->protocol].encoder.encode) { | ||||||
|         __disable_irq(); |             __disable_irq(); | ||||||
|         irda_protocols[message->protocol].encoder.encode(message->address, message->command, !!i); |             irda_protocols[message->protocol].encoder.encode(message->address, message->command, !!i); | ||||||
|         __enable_irq(); |             __enable_irq(); | ||||||
|         osKernelUnlock(); |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool irda_is_protocol_valid(IrdaProtocol protocol) { | ||||||
|  |     return (protocol >= 0) && (protocol < COUNT_OF(irda_protocols)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | IrdaProtocol irda_get_protocol_by_name(const char* protocol_name) { | ||||||
|  |     for (int i = 0; i < COUNT_OF(irda_protocols); ++i) { | ||||||
|  |         if (!strcmp(irda_protocols[i].name, protocol_name)) | ||||||
|  |             return i; | ||||||
|  |     } | ||||||
|  |     return IrdaProtocolUnknown; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const char* irda_get_protocol_name(IrdaProtocol protocol) { | const char* irda_get_protocol_name(IrdaProtocol protocol) { | ||||||
|     return irda_protocols[protocol].name; |     if (irda_is_protocol_valid(protocol)) | ||||||
|  |         return irda_protocols[protocol].name; | ||||||
|  |     else | ||||||
|  |         return "Invalid"; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint8_t irda_get_protocol_address_length(IrdaProtocol protocol) { | uint8_t irda_get_protocol_address_length(IrdaProtocol protocol) { | ||||||
|     return irda_protocols[protocol].address_length; |     if (irda_is_protocol_valid(protocol)) | ||||||
|  |         return irda_protocols[protocol].address_length; | ||||||
|  |     else | ||||||
|  |         return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint8_t irda_get_protocol_command_length(IrdaProtocol protocol) { | uint8_t irda_get_protocol_command_length(IrdaProtocol protocol) { | ||||||
|     return irda_protocols[protocol].command_length; |     if (irda_is_protocol_valid(protocol)) | ||||||
|  |         return irda_protocols[protocol].command_length; | ||||||
|  |     else | ||||||
|  |         return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ typedef struct IrdaHandler IrdaHandler; | |||||||
| 
 | 
 | ||||||
| // Do not change protocol order, as it can be saved into memory and fw update can be performed!
 | // Do not change protocol order, as it can be saved into memory and fw update can be performed!
 | ||||||
| typedef enum { | typedef enum { | ||||||
|  |     IrdaProtocolUnknown = -1, | ||||||
|     IrdaProtocolSamsung32 = 0, |     IrdaProtocolSamsung32 = 0, | ||||||
|     IrdaProtocolNEC = 1, |     IrdaProtocolNEC = 1, | ||||||
|     IrdaProtocolNECext = 2, |     IrdaProtocolNECext = 2, | ||||||
| @ -74,6 +75,14 @@ void irda_send(const IrdaMessage* message, int times); | |||||||
|  */ |  */ | ||||||
| const char* irda_get_protocol_name(IrdaProtocol protocol); | const char* irda_get_protocol_name(IrdaProtocol protocol); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get protocol enum by protocol name. | ||||||
|  |  * | ||||||
|  |  * \param[in]   protocol_name   - string to protocol name. | ||||||
|  |  * \return      protocol identifier. | ||||||
|  |  */ | ||||||
|  | IrdaProtocol irda_get_protocol_by_name(const char* protocol_name); | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Get address length by protocol enum. |  * Get address length by protocol enum. | ||||||
|  * |  * | ||||||
| @ -90,6 +99,14 @@ uint8_t irda_get_protocol_address_length(IrdaProtocol protocol); | |||||||
|  */ |  */ | ||||||
| uint8_t irda_get_protocol_command_length(IrdaProtocol protocol); | uint8_t irda_get_protocol_command_length(IrdaProtocol protocol); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Checks whether protocol valid. | ||||||
|  |  * | ||||||
|  |  * \param[in]   protocol    - protocol identifier. | ||||||
|  |  * \return      true if protocol is valid, false otherwise. | ||||||
|  |  */ | ||||||
|  | bool irda_is_protocol_valid(IrdaProtocol protocol); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Albert Kharisov
						Albert Kharisov