From a8acfcabb4104045b6eadd6c215fc3c88b88614e Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Tue, 21 Jun 2022 15:45:50 +0300 Subject: [PATCH] [FL-2568] Infrared C port (#1326) * Add skeleton for infrared C port, rename old app * Add scene stubs * Add more views * Misc changes * Add remote and signal class stubs * Complete infrared signal class * Add remote button class stub * Check if button contains a signal during destruction * Complete infrared signal class more * Implement remote storing * Implement remote loading * Fix error handling * Implement remote transmitting * Rename scene * Canonise event consumption * Implement remote learning (stub) * Implement learn success screen (stub) * Implement AskBack scene * Improve remote saving&loading * Fix remote file name * Add LearnDone scene * Switch from Remote scene correctly * Add SceneButtonSelect * Remove unneeded assert * Add new SceneManager method * Use new SceneManager method in Infrared * Implement renaming of buttons and remotes * Implement deleting of buttons and remotes * Add universal remotes list * Add brute force code * Brute force code improvements * Partially implement Universal Remote GUI * Fix wrong singnal handling * Fully implement Universal Remote * Use standard custom events everywhere * Return infrared CLI * Remove old Infrared app * Change container name * Fix scene order * Put ButtonPanel into stack only when needed * Show loading animation during slow operations * Do not hardcode Loading widget coordinates * Switch Loading widget orientation as needed * Save Start scene state * Save Remote scene state * Save Edit scene state * Save EditButtonSelect scene state * Do not use scene state * Use string_t instead of const char* for brevity * Fix memory leak * Fix saving raw remotes * Add Infrared debug menu * Add debug view * Move Infrared monitor into Infrared application * Remove old Infrared monitor app * Use common signal received callback everywhere --- applications/applications.c | 9 - applications/applications.mk | 9 - applications/gui/modules/loading.c | 4 +- applications/gui/scene_manager.c | 19 + applications/gui/scene_manager.h | 14 + .../infrared/helpers/infrared_parser.cpp | 157 ------- .../infrared/helpers/infrared_parser.h | 48 --- applications/infrared/infrared.c | 397 ++++++++++++++++++ applications/infrared/infrared.h | 3 + applications/infrared/infrared_app.cpp | 250 ----------- applications/infrared/infrared_app.h | 326 -------------- .../infrared/infrared_app_brute_force.cpp | 93 ---- .../infrared/infrared_app_brute_force.h | 67 --- applications/infrared/infrared_app_event.h | 48 --- .../infrared/infrared_app_remote_manager.cpp | 266 ------------ .../infrared/infrared_app_remote_manager.h | 189 --------- applications/infrared/infrared_app_signal.cpp | 116 ----- applications/infrared/infrared_app_signal.h | 134 ------ .../infrared/infrared_app_view_manager.cpp | 163 ------- .../infrared/infrared_app_view_manager.h | 164 -------- applications/infrared/infrared_brute_force.c | 153 +++++++ applications/infrared/infrared_brute_force.h | 22 + .../{cli/infrared_cli.cpp => infrared_cli.c} | 117 +++--- applications/infrared/infrared_custom_event.h | 51 +++ applications/infrared/infrared_i.h | 132 ++++++ applications/infrared/infrared_remote.c | 176 ++++++++ applications/infrared/infrared_remote.h | 28 ++ .../infrared/infrared_remote_button.c | 38 ++ .../infrared/infrared_remote_button.h | 14 + applications/infrared/infrared_runner.cpp | 9 - applications/infrared/infrared_signal.c | 264 ++++++++++++ applications/infrared/infrared_signal.h | 41 ++ .../infrared/scene/infrared_app_scene.h | 305 -------------- .../scene/infrared_app_scene_ask_back.cpp | 73 ---- .../scene/infrared_app_scene_edit.cpp | 79 ---- .../scene/infrared_app_scene_edit_delete.cpp | 100 ----- .../infrared_app_scene_edit_delete_done.cpp | 38 -- .../infrared_app_scene_edit_key_select.cpp | 58 --- .../scene/infrared_app_scene_edit_rename.cpp | 83 ---- .../infrared_app_scene_edit_rename_done.cpp | 30 -- .../scene/infrared_app_scene_learn.cpp | 76 ---- .../scene/infrared_app_scene_learn_done.cpp | 41 -- .../infrared_app_scene_learn_enter_name.cpp | 60 --- .../infrared_app_scene_learn_success.cpp | 142 ------- .../scene/infrared_app_scene_remote.cpp | 131 ------ .../scene/infrared_app_scene_remote_list.cpp | 45 -- .../scene/infrared_app_scene_start.cpp | 68 --- .../scene/infrared_app_scene_universal.cpp | 57 --- .../infrared_app_scene_universal_common.cpp | 107 ----- .../scene/infrared_app_scene_universal_tv.cpp | 123 ------ .../common/infrared_scene_universal_common.c | 92 ++++ .../common/infrared_scene_universal_common.h | 8 + applications/infrared/scenes/infrared_scene.c | 30 ++ applications/infrared/scenes/infrared_scene.h | 29 ++ .../infrared/scenes/infrared_scene_ask_back.c | 59 +++ .../infrared/scenes/infrared_scene_config.h | 17 + .../infrared/scenes/infrared_scene_debug.c | 68 +++ .../infrared/scenes/infrared_scene_edit.c | 101 +++++ .../infrared_scene_edit_button_select.c | 63 +++ .../scenes/infrared_scene_edit_delete.c | 112 +++++ .../scenes/infrared_scene_edit_delete_done.c | 46 ++ .../scenes/infrared_scene_edit_rename.c | 107 +++++ .../scenes/infrared_scene_edit_rename_done.c | 35 ++ .../infrared/scenes/infrared_scene_learn.c | 46 ++ .../scenes/infrared_scene_learn_done.c | 44 ++ .../scenes/infrared_scene_learn_enter_name.c | 66 +++ .../scenes/infrared_scene_learn_success.c | 131 ++++++ .../infrared/scenes/infrared_scene_remote.c | 119 ++++++ .../scenes/infrared_scene_remote_list.c | 44 ++ .../infrared/scenes/infrared_scene_start.c | 83 ++++ .../scenes/infrared_scene_universal.c | 53 +++ .../scenes/infrared_scene_universal_tv.c | 111 +++++ .../infrared/views/infrared_debug_view.c | 59 +++ .../infrared/views/infrared_debug_view.h | 11 + .../{view => views}/infrared_progress_view.c | 0 .../{view => views}/infrared_progress_view.h | 0 .../infrared_monitor/infrared_monitor.c | 140 ------ 77 files changed, 2946 insertions(+), 3865 deletions(-) delete mode 100644 applications/infrared/helpers/infrared_parser.cpp delete mode 100644 applications/infrared/helpers/infrared_parser.h create mode 100644 applications/infrared/infrared.c create mode 100644 applications/infrared/infrared.h delete mode 100644 applications/infrared/infrared_app.cpp delete mode 100644 applications/infrared/infrared_app.h delete mode 100644 applications/infrared/infrared_app_brute_force.cpp delete mode 100644 applications/infrared/infrared_app_brute_force.h delete mode 100644 applications/infrared/infrared_app_event.h delete mode 100644 applications/infrared/infrared_app_remote_manager.cpp delete mode 100644 applications/infrared/infrared_app_remote_manager.h delete mode 100644 applications/infrared/infrared_app_signal.cpp delete mode 100644 applications/infrared/infrared_app_signal.h delete mode 100644 applications/infrared/infrared_app_view_manager.cpp delete mode 100644 applications/infrared/infrared_app_view_manager.h create mode 100644 applications/infrared/infrared_brute_force.c create mode 100644 applications/infrared/infrared_brute_force.h rename applications/infrared/{cli/infrared_cli.cpp => infrared_cli.c} (72%) create mode 100644 applications/infrared/infrared_custom_event.h create mode 100644 applications/infrared/infrared_i.h create mode 100644 applications/infrared/infrared_remote.c create mode 100644 applications/infrared/infrared_remote.h create mode 100644 applications/infrared/infrared_remote_button.c create mode 100644 applications/infrared/infrared_remote_button.h delete mode 100644 applications/infrared/infrared_runner.cpp create mode 100644 applications/infrared/infrared_signal.c create mode 100644 applications/infrared/infrared_signal.h delete mode 100644 applications/infrared/scene/infrared_app_scene.h delete mode 100644 applications/infrared/scene/infrared_app_scene_ask_back.cpp delete mode 100644 applications/infrared/scene/infrared_app_scene_edit.cpp delete mode 100644 applications/infrared/scene/infrared_app_scene_edit_delete.cpp delete mode 100644 applications/infrared/scene/infrared_app_scene_edit_delete_done.cpp delete mode 100644 applications/infrared/scene/infrared_app_scene_edit_key_select.cpp delete mode 100644 applications/infrared/scene/infrared_app_scene_edit_rename.cpp delete mode 100644 applications/infrared/scene/infrared_app_scene_edit_rename_done.cpp delete mode 100644 applications/infrared/scene/infrared_app_scene_learn.cpp delete mode 100644 applications/infrared/scene/infrared_app_scene_learn_done.cpp delete mode 100644 applications/infrared/scene/infrared_app_scene_learn_enter_name.cpp delete mode 100644 applications/infrared/scene/infrared_app_scene_learn_success.cpp delete mode 100644 applications/infrared/scene/infrared_app_scene_remote.cpp delete mode 100644 applications/infrared/scene/infrared_app_scene_remote_list.cpp delete mode 100644 applications/infrared/scene/infrared_app_scene_start.cpp delete mode 100644 applications/infrared/scene/infrared_app_scene_universal.cpp delete mode 100644 applications/infrared/scene/infrared_app_scene_universal_common.cpp delete mode 100644 applications/infrared/scene/infrared_app_scene_universal_tv.cpp create mode 100644 applications/infrared/scenes/common/infrared_scene_universal_common.c create mode 100644 applications/infrared/scenes/common/infrared_scene_universal_common.h create mode 100644 applications/infrared/scenes/infrared_scene.c create mode 100644 applications/infrared/scenes/infrared_scene.h create mode 100644 applications/infrared/scenes/infrared_scene_ask_back.c create mode 100644 applications/infrared/scenes/infrared_scene_config.h create mode 100644 applications/infrared/scenes/infrared_scene_debug.c create mode 100644 applications/infrared/scenes/infrared_scene_edit.c create mode 100644 applications/infrared/scenes/infrared_scene_edit_button_select.c create mode 100644 applications/infrared/scenes/infrared_scene_edit_delete.c create mode 100644 applications/infrared/scenes/infrared_scene_edit_delete_done.c create mode 100644 applications/infrared/scenes/infrared_scene_edit_rename.c create mode 100644 applications/infrared/scenes/infrared_scene_edit_rename_done.c create mode 100644 applications/infrared/scenes/infrared_scene_learn.c create mode 100644 applications/infrared/scenes/infrared_scene_learn_done.c create mode 100644 applications/infrared/scenes/infrared_scene_learn_enter_name.c create mode 100644 applications/infrared/scenes/infrared_scene_learn_success.c create mode 100644 applications/infrared/scenes/infrared_scene_remote.c create mode 100644 applications/infrared/scenes/infrared_scene_remote_list.c create mode 100644 applications/infrared/scenes/infrared_scene_start.c create mode 100644 applications/infrared/scenes/infrared_scene_universal.c create mode 100644 applications/infrared/scenes/infrared_scene_universal_tv.c create mode 100644 applications/infrared/views/infrared_debug_view.c create mode 100644 applications/infrared/views/infrared_debug_view.h rename applications/infrared/{view => views}/infrared_progress_view.c (100%) rename applications/infrared/{view => views}/infrared_progress_view.h (100%) delete mode 100644 applications/infrared_monitor/infrared_monitor.c diff --git a/applications/applications.c b/applications/applications.c index dccab589f..22f9520ec 100644 --- a/applications/applications.c +++ b/applications/applications.c @@ -29,7 +29,6 @@ extern int32_t display_test_app(void* p); extern int32_t gpio_app(void* p); extern int32_t ibutton_app(void* p); extern int32_t infrared_app(void* p); -extern int32_t infrared_monitor_app(void* p); extern int32_t keypad_test_app(void* p); extern int32_t lfrfid_app(void* p); extern int32_t lfrfid_debug_app(void* p); @@ -412,14 +411,6 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = { .flags = FlipperApplicationFlagDefault}, #endif -#ifdef APP_INFRARED_MONITOR - {.app = infrared_monitor_app, - .name = "Infrared Monitor", - .stack_size = 1024, - .icon = NULL, - .flags = FlipperApplicationFlagDefault}, -#endif - #ifdef APP_LF_RFID {.app = lfrfid_debug_app, .name = "LF-RFID Debug", diff --git a/applications/applications.mk b/applications/applications.mk index e7a9ebfc0..d2272efb1 100644 --- a/applications/applications.mk +++ b/applications/applications.mk @@ -51,7 +51,6 @@ APP_SNAKE_GAME = 1 # Debug APP_ACCESSOR = 1 APP_BLINK = 1 -APP_INFRARED_MONITOR = 1 APP_KEYPAD_TEST = 1 APP_SD_TEST = 1 APP_VIBRO_TEST = 1 @@ -70,14 +69,6 @@ endif # that will be shown in menu # Prefix with APP_* - -APP_INFRARED_MONITOR ?= 0 -ifeq ($(APP_INFRARED_MONITOR), 1) -CFLAGS += -DAPP_INFRARED_MONITOR -SRV_GUI = 1 -endif - - APP_UNIT_TESTS ?= 0 ifeq ($(APP_UNIT_TESTS), 1) CFLAGS += -DAPP_UNIT_TESTS diff --git a/applications/gui/modules/loading.c b/applications/gui/modules/loading.c index 60dc8338d..575126972 100644 --- a/applications/gui/modules/loading.c +++ b/applications/gui/modules/loading.c @@ -20,10 +20,10 @@ typedef struct { static void loading_draw_callback(Canvas* canvas, void* _model) { LoadingModel* model = (LoadingModel*)_model; - uint8_t x = 7; - uint8_t y = 40; uint8_t width = 49; uint8_t height = 47; + uint8_t x = (canvas_width(canvas) - width) / 2; + uint8_t y = (canvas_height(canvas) - height) / 2; elements_bold_rounded_frame(canvas, x, y, width, height); diff --git a/applications/gui/scene_manager.c b/applications/gui/scene_manager.c index a8c403209..590145e1e 100755 --- a/applications/gui/scene_manager.c +++ b/applications/gui/scene_manager.c @@ -165,6 +165,25 @@ bool scene_manager_search_and_switch_to_previous_scene( } } +bool scene_manager_search_and_switch_to_previous_scene_one_of( + SceneManager* scene_manager, + const uint32_t* scene_ids, + size_t scene_ids_size) { + furi_assert(scene_manager); + furi_assert(scene_ids); + bool scene_found = false; + + for(size_t i = 0; i < scene_ids_size; ++i) { + const uint32_t scene_id = scene_ids[i]; + if(scene_manager_has_previous_scene(scene_manager, scene_id)) { + scene_manager_search_and_switch_to_previous_scene(scene_manager, scene_id); + scene_found = true; + break; + } + } + return scene_found; +} + bool scene_manager_has_previous_scene(SceneManager* scene_manager, uint32_t scene_id) { furi_assert(scene_manager); bool scene_found = false; diff --git a/applications/gui/scene_manager.h b/applications/gui/scene_manager.h index 691628530..5b833e5de 100755 --- a/applications/gui/scene_manager.h +++ b/applications/gui/scene_manager.h @@ -5,6 +5,7 @@ #pragma once +#include #include #include @@ -146,6 +147,19 @@ bool scene_manager_search_and_switch_to_previous_scene( SceneManager* scene_manager, uint32_t scene_id); +/** Search and switch to previous Scene, multiple choice + * + * @param scene_manager SceneManager instance + * @param scene_ids Array of scene IDs + * @param scene_ids_size Array of scene IDs size + * + * @return true if one of previous scenes was found, false otherwise + */ +bool scene_manager_search_and_switch_to_previous_scene_one_of( + SceneManager* scene_manager, + const uint32_t* scene_ids, + size_t scene_ids_size); + /** Clear Scene stack and switch to another Scene * * @param scene_manager SceneManager instance diff --git a/applications/infrared/helpers/infrared_parser.cpp b/applications/infrared/helpers/infrared_parser.cpp deleted file mode 100644 index d85a7269b..000000000 --- a/applications/infrared/helpers/infrared_parser.cpp +++ /dev/null @@ -1,157 +0,0 @@ - -#include "../infrared_app_signal.h" -#include "infrared.h" -#include "infrared/helpers/infrared_parser.h" -#include "infrared_worker.h" -#include "m-string.h" -#include -#include -#include -#include - -#define TAG "InfraredParser" - -bool infrared_parser_save_signal( - FlipperFormat* ff, - const InfraredAppSignal& signal, - const std::string& name) { - furi_assert(ff); - furi_assert(!name.empty()); - - bool result = false; - - do { - if(!flipper_format_write_comment_cstr(ff, "")) break; - if(!flipper_format_write_string_cstr(ff, "name", name.c_str())) break; - if(signal.is_raw()) { - furi_assert(signal.get_raw_signal().timings_cnt <= MAX_TIMINGS_AMOUNT); - auto raw_signal = signal.get_raw_signal(); - if(!flipper_format_write_string_cstr(ff, "type", "raw")) break; - if(!flipper_format_write_uint32(ff, "frequency", &raw_signal.frequency, 1)) break; - if(!flipper_format_write_float(ff, "duty_cycle", &raw_signal.duty_cycle, 1)) break; - if(!flipper_format_write_uint32(ff, "data", raw_signal.timings, raw_signal.timings_cnt)) - break; - } else { - auto parsed_signal = signal.get_message(); - const char* protocol_name = infrared_get_protocol_name(parsed_signal.protocol); - if(!flipper_format_write_string_cstr(ff, "type", "parsed")) break; - if(!flipper_format_write_string_cstr(ff, "protocol", protocol_name)) break; - if(!flipper_format_write_hex(ff, "address", (uint8_t*)&parsed_signal.address, 4)) - break; - if(!flipper_format_write_hex(ff, "command", (uint8_t*)&parsed_signal.command, 4)) - break; - } - result = true; - } while(0); - - return result; -} - -bool infrared_parser_read_signal(FlipperFormat* ff, InfraredAppSignal& signal, std::string& name) { - furi_assert(ff); - - bool result = false; - string_t read_string; - string_init(read_string); - - do { - if(!flipper_format_read_string(ff, "name", read_string)) break; - name = string_get_cstr(read_string); - if(!flipper_format_read_string(ff, "type", read_string)) break; - if(!string_cmp_str(read_string, "raw")) { - uint32_t* timings = nullptr; - uint32_t timings_cnt = 0; - uint32_t frequency = 0; - float duty_cycle = 0; - - if(!flipper_format_read_uint32(ff, "frequency", &frequency, 1)) break; - if(!flipper_format_read_float(ff, "duty_cycle", &duty_cycle, 1)) break; - if(!flipper_format_get_value_count(ff, "data", &timings_cnt)) break; - if(timings_cnt > MAX_TIMINGS_AMOUNT) break; - timings = (uint32_t*)malloc(sizeof(uint32_t) * timings_cnt); - if(flipper_format_read_uint32(ff, "data", timings, timings_cnt)) { - signal.set_raw_signal(timings, timings_cnt, frequency, duty_cycle); - result = true; - } - free(timings); - } else if(!string_cmp_str(read_string, "parsed")) { - InfraredMessage parsed_signal; - if(!flipper_format_read_string(ff, "protocol", read_string)) break; - parsed_signal.protocol = infrared_get_protocol_by_name(string_get_cstr(read_string)); - if(!flipper_format_read_hex(ff, "address", (uint8_t*)&parsed_signal.address, 4)) break; - if(!flipper_format_read_hex(ff, "command", (uint8_t*)&parsed_signal.command, 4)) break; - if(!infrared_parser_is_parsed_signal_valid(&parsed_signal)) break; - signal.set_message(&parsed_signal); - result = true; - } else { - FURI_LOG_E(TAG, "Unknown type of signal (allowed - raw/parsed) "); - } - } while(0); - - string_clear(read_string); - return result; -} - -bool infrared_parser_is_parsed_signal_valid(const InfraredMessage* signal) { - furi_assert(signal); - bool result = true; - - if(!infrared_is_protocol_valid(signal->protocol)) { - FURI_LOG_E(TAG, "Unknown protocol"); - result = false; - } - - if(result) { - uint32_t address_length = infrared_get_protocol_address_length(signal->protocol); - uint32_t address_mask = (1LU << address_length) - 1; - if(signal->address != (signal->address & address_mask)) { - FURI_LOG_E( - TAG, - "Address is out of range (mask 0x%08lX): 0x%lX\r\n", - address_mask, - signal->address); - result = false; - } - } - - if(result) { - uint32_t command_length = infrared_get_protocol_command_length(signal->protocol); - uint32_t command_mask = (1LU << command_length) - 1; - if(signal->command != (signal->command & command_mask)) { - FURI_LOG_E( - TAG, - "Command is out of range (mask 0x%08lX): 0x%lX\r\n", - command_mask, - signal->command); - result = false; - } - } - - return result; -} - -bool infrared_parser_is_raw_signal_valid( - uint32_t frequency, - float duty_cycle, - uint32_t timings_cnt) { - bool result = true; - - if((frequency > INFRARED_MAX_FREQUENCY) || (frequency < INFRARED_MIN_FREQUENCY)) { - FURI_LOG_E( - TAG, - "Frequency is out of range (%lX - %lX): %lX", - INFRARED_MIN_FREQUENCY, - INFRARED_MAX_FREQUENCY, - frequency); - result = false; - } else if((duty_cycle <= 0) || (duty_cycle > 1)) { - FURI_LOG_E(TAG, "Duty cycle is out of range (0 - 1): %f", (double)duty_cycle); - result = false; - } else if((timings_cnt <= 0) || (timings_cnt > MAX_TIMINGS_AMOUNT)) { - FURI_LOG_E( - TAG, "Timings amount is out of range (0 - %lX): %lX", MAX_TIMINGS_AMOUNT, timings_cnt); - result = false; - } - - return result; -} diff --git a/applications/infrared/helpers/infrared_parser.h b/applications/infrared/helpers/infrared_parser.h deleted file mode 100644 index 2e790c381..000000000 --- a/applications/infrared/helpers/infrared_parser.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @file infrared_parser.h - * Infrared: Helper file for conversion Flipper File Format - * to Infrared signal class, and backwards - */ -#pragma once - -#include "../infrared_app_signal.h" -#include -#include - -/** Save Infrared signal into file - * - * @param ff - Flipper File Format instance - * @param signal - Infrared signal to save - * @param name - name for saved signal. Every - * signal on disk has name. - */ -bool infrared_parser_save_signal( - FlipperFormat* ff, - const InfraredAppSignal& signal, - const std::string& name); - -/** Read Infrared signal from file - * - * @param ff - Flipper File Format instance - * @param signal - Infrared signal to read to - * @param name - name for saved signal. Every - * signal in file has name. - */ -bool infrared_parser_read_signal(FlipperFormat* ff, InfraredAppSignal& signal, std::string& name); - -/** Validate parsed signal - * - * @signal - signal to validate - * @retval true if valid, false otherwise - */ -bool infrared_parser_is_parsed_signal_valid(const InfraredMessage* signal); - -/** Validate raw signal - * - * @signal - signal to validate - * @retval true if valid, false otherwise - */ -bool infrared_parser_is_raw_signal_valid( - uint32_t frequency, - float duty_cycle, - uint32_t timings_cnt); diff --git a/applications/infrared/infrared.c b/applications/infrared/infrared.c new file mode 100644 index 000000000..ee6b931ed --- /dev/null +++ b/applications/infrared/infrared.c @@ -0,0 +1,397 @@ +#include "infrared_i.h" + +#include +#include + +static const NotificationSequence* infrared_notification_sequences[] = { + &sequence_success, + &sequence_set_only_green_255, + &sequence_reset_green, + &sequence_blink_cyan_10, + &sequence_blink_magenta_10}; + +static void infrared_make_app_folder(Infrared* infrared) { + if(!storage_simply_mkdir(infrared->storage, INFRARED_APP_FOLDER)) { + dialog_message_show_storage_error(infrared->dialogs, "Cannot create\napp folder"); + } +} + +static bool infrared_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + Infrared* infrared = context; + return scene_manager_handle_custom_event(infrared->scene_manager, event); +} + +static bool infrared_back_event_callback(void* context) { + furi_assert(context); + Infrared* infrared = context; + return scene_manager_handle_back_event(infrared->scene_manager); +} + +static void infrared_tick_event_callback(void* context) { + furi_assert(context); + Infrared* infrared = context; + scene_manager_handle_tick_event(infrared->scene_manager); +} + +static void infrared_find_vacant_remote_name(string_t name, const char* path) { + Storage* storage = furi_record_open("storage"); + + string_t base_path; + string_init_set_str(base_path, path); + + if(string_end_with_str_p(base_path, INFRARED_APP_EXTENSION)) { + size_t filename_start = string_search_rchar(base_path, '/'); + string_left(base_path, filename_start); + } + + string_printf(base_path, "%s/%s%s", path, string_get_cstr(name), INFRARED_APP_EXTENSION); + + FS_Error status = storage_common_stat(storage, string_get_cstr(base_path), NULL); + + if(status == FSE_OK) { + /* If the suggested name is occupied, try another one (name2, name3, etc) */ + size_t dot = string_search_rchar(base_path, '.'); + string_left(base_path, dot); + + string_t path_temp; + string_init(path_temp); + + uint32_t i = 1; + do { + string_printf( + path_temp, "%s%u%s", string_get_cstr(base_path), ++i, INFRARED_APP_EXTENSION); + status = storage_common_stat(storage, string_get_cstr(path_temp), NULL); + } while(status == FSE_OK); + + string_clear(path_temp); + + if(status == FSE_NOT_EXIST) { + string_cat_printf(name, "%u", i); + } + } + + string_clear(base_path); + furi_record_close("storage"); +} + +static Infrared* infrared_alloc() { + Infrared* infrared = malloc(sizeof(Infrared)); + + string_init(infrared->file_path); + + InfraredAppState* app_state = &infrared->app_state; + app_state->is_learning_new_remote = false; + app_state->is_debug_enabled = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); + app_state->edit_target = InfraredEditTargetNone; + app_state->edit_mode = InfraredEditModeNone; + app_state->current_button_index = InfraredButtonIndexNone; + + infrared->scene_manager = scene_manager_alloc(&infrared_scene_handlers, infrared); + infrared->view_dispatcher = view_dispatcher_alloc(); + + infrared->gui = furi_record_open("gui"); + + ViewDispatcher* view_dispatcher = infrared->view_dispatcher; + view_dispatcher_attach_to_gui(view_dispatcher, infrared->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_enable_queue(view_dispatcher); + view_dispatcher_set_event_callback_context(view_dispatcher, infrared); + view_dispatcher_set_custom_event_callback(view_dispatcher, infrared_custom_event_callback); + view_dispatcher_set_navigation_event_callback(view_dispatcher, infrared_back_event_callback); + view_dispatcher_set_tick_event_callback(view_dispatcher, infrared_tick_event_callback, 100); + + infrared->storage = furi_record_open("storage"); + infrared->dialogs = furi_record_open("dialogs"); + infrared->notifications = furi_record_open("notification"); + + infrared->worker = infrared_worker_alloc(); + infrared->remote = infrared_remote_alloc(); + infrared->received_signal = infrared_signal_alloc(); + infrared->brute_force = infrared_brute_force_alloc(); + + infrared->submenu = submenu_alloc(); + view_dispatcher_add_view( + view_dispatcher, InfraredViewSubmenu, submenu_get_view(infrared->submenu)); + + infrared->text_input = text_input_alloc(); + view_dispatcher_add_view( + view_dispatcher, InfraredViewTextInput, text_input_get_view(infrared->text_input)); + + infrared->dialog_ex = dialog_ex_alloc(); + view_dispatcher_add_view( + view_dispatcher, InfraredViewDialogEx, dialog_ex_get_view(infrared->dialog_ex)); + + infrared->button_menu = button_menu_alloc(); + view_dispatcher_add_view( + view_dispatcher, InfraredViewButtonMenu, button_menu_get_view(infrared->button_menu)); + + infrared->popup = popup_alloc(); + view_dispatcher_add_view(view_dispatcher, InfraredViewPopup, popup_get_view(infrared->popup)); + + infrared->view_stack = view_stack_alloc(); + view_dispatcher_add_view( + view_dispatcher, InfraredViewStack, view_stack_get_view(infrared->view_stack)); + + if(app_state->is_debug_enabled) { + infrared->debug_view = infrared_debug_view_alloc(); + view_dispatcher_add_view( + view_dispatcher, + InfraredViewDebugView, + infrared_debug_view_get_view(infrared->debug_view)); + } + + infrared->button_panel = button_panel_alloc(); + infrared->loading = loading_alloc(); + infrared->progress = infrared_progress_view_alloc(); + + return infrared; +} + +static void infrared_free(Infrared* infrared) { + furi_assert(infrared); + ViewDispatcher* view_dispatcher = infrared->view_dispatcher; + InfraredAppState* app_state = &infrared->app_state; + + view_dispatcher_remove_view(view_dispatcher, InfraredViewSubmenu); + submenu_free(infrared->submenu); + + view_dispatcher_remove_view(view_dispatcher, InfraredViewTextInput); + text_input_free(infrared->text_input); + + view_dispatcher_remove_view(view_dispatcher, InfraredViewDialogEx); + dialog_ex_free(infrared->dialog_ex); + + view_dispatcher_remove_view(view_dispatcher, InfraredViewButtonMenu); + button_menu_free(infrared->button_menu); + + view_dispatcher_remove_view(view_dispatcher, InfraredViewPopup); + popup_free(infrared->popup); + + view_dispatcher_remove_view(view_dispatcher, InfraredViewStack); + view_stack_free(infrared->view_stack); + + if(app_state->is_debug_enabled) { + view_dispatcher_remove_view(view_dispatcher, InfraredViewDebugView); + infrared_debug_view_free(infrared->debug_view); + } + + button_panel_free(infrared->button_panel); + loading_free(infrared->loading); + infrared_progress_view_free(infrared->progress); + + view_dispatcher_free(view_dispatcher); + scene_manager_free(infrared->scene_manager); + + infrared_brute_force_free(infrared->brute_force); + infrared_signal_free(infrared->received_signal); + infrared_remote_free(infrared->remote); + infrared_worker_free(infrared->worker); + + furi_record_close("gui"); + infrared->gui = NULL; + + furi_record_close("notification"); + infrared->notifications = NULL; + + furi_record_close("dialogs"); + infrared->dialogs = NULL; + + furi_record_close("gui"); + infrared->gui = NULL; + + string_clear(infrared->file_path); + + free(infrared); +} + +bool infrared_add_remote_with_button( + Infrared* infrared, + const char* button_name, + InfraredSignal* signal) { + InfraredRemote* remote = infrared->remote; + + string_t new_name, new_path; + string_init_set_str(new_name, INFRARED_DEFAULT_REMOTE_NAME); + string_init_set_str(new_path, INFRARED_APP_FOLDER); + + infrared_find_vacant_remote_name(new_name, string_get_cstr(new_path)); + string_cat_printf(new_path, "/%s%s", string_get_cstr(new_name), INFRARED_APP_EXTENSION); + + infrared_remote_reset(remote); + infrared_remote_set_name(remote, string_get_cstr(new_name)); + infrared_remote_set_path(remote, string_get_cstr(new_path)); + + string_clear(new_name); + string_clear(new_path); + return infrared_remote_add_button(remote, button_name, signal); +} + +bool infrared_rename_current_remote(Infrared* infrared, const char* name) { + InfraredRemote* remote = infrared->remote; + const char* remote_path = infrared_remote_get_path(remote); + + if(!strcmp(infrared_remote_get_name(remote), name)) { + return true; + } + + string_t new_name; + string_init_set_str(new_name, name); + + infrared_find_vacant_remote_name(new_name, remote_path); + + string_t new_path; + string_init_set(new_path, infrared_remote_get_path(remote)); + if(string_end_with_str_p(new_path, INFRARED_APP_EXTENSION)) { + size_t filename_start = string_search_rchar(new_path, '/'); + string_left(new_path, filename_start); + } + string_cat_printf(new_path, "/%s%s", string_get_cstr(new_name), INFRARED_APP_EXTENSION); + + Storage* storage = furi_record_open("storage"); + + FS_Error status = storage_common_rename( + storage, infrared_remote_get_path(remote), string_get_cstr(new_path)); + infrared_remote_set_name(remote, string_get_cstr(new_name)); + + string_clear(new_name); + string_clear(new_path); + + furi_record_close("storage"); + return (status == FSE_OK || status == FSE_EXIST); +} + +void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) { + if(infrared_signal_is_raw(signal)) { + InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); + infrared_worker_set_raw_signal(infrared->worker, raw->timings, raw->timings_size); + } else { + InfraredMessage* message = infrared_signal_get_message(signal); + infrared_worker_set_decoded_signal(infrared->worker, message); + } + + DOLPHIN_DEED(DolphinDeedIrSend); + infrared_worker_tx_start(infrared->worker); +} + +void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) { + furi_assert(button_index < infrared_remote_get_button_count(infrared->remote)); + + InfraredRemoteButton* button = infrared_remote_get_button(infrared->remote, button_index); + InfraredSignal* signal = infrared_remote_button_get_signal(button); + + infrared_tx_start_signal(infrared, signal); +} + +void infrared_tx_start_received(Infrared* infrared) { + infrared_tx_start_signal(infrared, infrared->received_signal); +} + +void infrared_tx_stop(Infrared* infrared) { + infrared_worker_tx_stop(infrared->worker); +} + +void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...) { + va_list args; + va_start(args, text); + + vsnprintf(infrared->text_store[bank], INFRARED_TEXT_STORE_SIZE, text, args); + + va_end(args); +} + +void infrared_text_store_clear(Infrared* infrared, uint32_t bank) { + memset(infrared->text_store[bank], 0, INFRARED_TEXT_STORE_SIZE); +} + +void infrared_play_notification_message(Infrared* infrared, uint32_t message) { + furi_assert(message < sizeof(infrared_notification_sequences) / sizeof(NotificationSequence*)); + notification_message(infrared->notifications, infrared_notification_sequences[message]); +} + +void infrared_show_loading_popup(Infrared* infrared, bool show) { + TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); + ViewStack* view_stack = infrared->view_stack; + Loading* loading = infrared->loading; + + if(show) { + // Raise timer priority so that animations can play + vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); + view_stack_add_view(view_stack, loading_get_view(loading)); + } else { + view_stack_remove_view(view_stack, loading_get_view(loading)); + // Restore default timer priority + vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); + } +} + +void infrared_signal_sent_callback(void* context) { + furi_assert(context); + Infrared* infrared = context; + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkSend); +} + +void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { + furi_assert(context); + Infrared* infrared = context; + + if(infrared_worker_signal_is_decoded(received_signal)) { + infrared_signal_set_message( + infrared->received_signal, infrared_worker_get_decoded_signal(received_signal)); + } else { + const uint32_t* timings; + size_t timings_size; + infrared_worker_get_raw_signal(received_signal, &timings, &timings_size); + infrared_signal_set_raw_signal( + infrared->received_signal, + timings, + timings_size, + INFRARED_COMMON_CARRIER_FREQUENCY, + INFRARED_COMMON_DUTY_CYCLE); + } + + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeSignalReceived); +} + +void infrared_text_input_callback(void* context) { + furi_assert(context); + Infrared* infrared = context; + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeTextEditDone); +} + +void infrared_popup_timeout_callback(void* context) { + furi_assert(context); + Infrared* infrared = context; + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypePopupTimeout); +} + +int32_t infrared_app(void* p) { + Infrared* infrared = infrared_alloc(); + + infrared_make_app_folder(infrared); + + bool is_remote_loaded = false; + + if(p) { + string_set_str(infrared->file_path, (const char*)p); + is_remote_loaded = infrared_remote_load(infrared->remote, infrared->file_path); + if(!is_remote_loaded) { + dialog_message_show_storage_error( + infrared->dialogs, "Failed to load\nselected remote"); + return -1; + } + } + + if(is_remote_loaded) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); + } else { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneStart); + } + + view_dispatcher_run(infrared->view_dispatcher); + + infrared_free(infrared); + return 0; +} diff --git a/applications/infrared/infrared.h b/applications/infrared/infrared.h new file mode 100644 index 000000000..e5eeb1177 --- /dev/null +++ b/applications/infrared/infrared.h @@ -0,0 +1,3 @@ +#pragma once + +typedef struct Infrared Infrared; diff --git a/applications/infrared/infrared_app.cpp b/applications/infrared/infrared_app.cpp deleted file mode 100644 index 1ac859d10..000000000 --- a/applications/infrared/infrared_app.cpp +++ /dev/null @@ -1,250 +0,0 @@ -#include "infrared_app.h" -#include "m-string.h" -#include -#include -#include -#include -#include -#include - -int32_t InfraredApp::run(void* args) { - InfraredAppEvent event; - bool consumed; - bool exit = false; - - if(args) { - string_t path; - string_init_set_str(path, (char*)args); - if(string_end_with_str_p(path, InfraredApp::infrared_extension)) { - bool result = remote_manager.load(path); - if(result) { - current_scene = InfraredApp::Scene::Remote; - } else { - printf("Failed to load remote \'%s\'\r\n", string_get_cstr(path)); - return -1; - } - } - string_clear(path); - } - - scenes[current_scene]->on_enter(this); - - while(!exit) { - view_manager.receive_event(&event); - - if(event.type == InfraredAppEvent::Type::Exit) break; - - consumed = scenes[current_scene]->on_event(this, &event); - - if(!consumed) { - if(event.type == InfraredAppEvent::Type::Back) { - exit = switch_to_previous_scene(); - } - } - }; - - scenes[current_scene]->on_exit(this); - - return 0; -}; - -InfraredApp::InfraredApp() { - furi_check(InfraredAppRemoteManager::max_button_name_length < get_text_store_size()); - string_init_set_str(file_path, InfraredApp::infrared_directory); - notification = static_cast(furi_record_open("notification")); - dialogs = static_cast(furi_record_open("dialogs")); - infrared_worker = infrared_worker_alloc(); -} - -InfraredApp::~InfraredApp() { - infrared_worker_free(infrared_worker); - furi_record_close("notification"); - furi_record_close("dialogs"); - string_clear(file_path); - for(auto& [key, scene] : scenes) delete scene; -} - -InfraredAppViewManager* InfraredApp::get_view_manager() { - return &view_manager; -} - -void InfraredApp::set_learn_new_remote(bool value) { - learn_new_remote = value; -} - -bool InfraredApp::get_learn_new_remote() { - return learn_new_remote; -} - -void InfraredApp::switch_to_next_scene(Scene next_scene) { - previous_scenes_list.push_front(current_scene); - switch_to_next_scene_without_saving(next_scene); -} - -void InfraredApp::switch_to_next_scene_without_saving(Scene next_scene) { - if(next_scene != Scene::Exit) { - scenes[current_scene]->on_exit(this); - current_scene = next_scene; - scenes[current_scene]->on_enter(this); - view_manager.clear_events(); - } -} - -void InfraredApp::search_and_switch_to_previous_scene( - const std::initializer_list& scenes_list) { - Scene previous_scene = Scene::Start; - bool scene_found = false; - - while(!scene_found) { - previous_scene = get_previous_scene(); - - if(previous_scene == Scene::Exit) break; - - for(Scene element : scenes_list) { - if(previous_scene == element) { - scene_found = true; - break; - } - } - } - - if(previous_scene == Scene::Exit) { - InfraredAppEvent event; - event.type = InfraredAppEvent::Type::Exit; - view_manager.send_event(&event); - } else { - scenes[current_scene]->on_exit(this); - current_scene = previous_scene; - scenes[current_scene]->on_enter(this); - view_manager.clear_events(); - } -} - -bool InfraredApp::switch_to_previous_scene(uint8_t count) { - Scene previous_scene = Scene::Start; - - for(uint8_t i = 0; i < count; i++) previous_scene = get_previous_scene(); - - if(previous_scene == Scene::Exit) return true; - - scenes[current_scene]->on_exit(this); - current_scene = previous_scene; - scenes[current_scene]->on_enter(this); - view_manager.clear_events(); - return false; -} - -InfraredApp::Scene InfraredApp::get_previous_scene() { - Scene scene = Scene::Exit; - - if(!previous_scenes_list.empty()) { - scene = previous_scenes_list.front(); - previous_scenes_list.pop_front(); - } - - return scene; -} - -InfraredAppRemoteManager* InfraredApp::get_remote_manager() { - return &remote_manager; -} - -void InfraredApp::set_text_store(uint8_t index, const char* text...) { - furi_check(index < text_store_max); - - va_list args; - va_start(args, text); - - vsnprintf(text_store[index], text_store_size, text, args); - - va_end(args); -} - -char* InfraredApp::get_text_store(uint8_t index) { - furi_check(index < text_store_max); - - return text_store[index]; -} - -uint8_t InfraredApp::get_text_store_size() { - return text_store_size; -} - -void InfraredApp::text_input_callback(void* context) { - InfraredApp* app = static_cast(context); - InfraredAppEvent event; - event.type = InfraredAppEvent::Type::TextEditDone; - app->get_view_manager()->send_event(&event); -} - -void InfraredApp::popup_callback(void* context) { - InfraredApp* app = static_cast(context); - InfraredAppEvent event; - event.type = InfraredAppEvent::Type::PopupTimer; - app->get_view_manager()->send_event(&event); -} - -void InfraredApp::set_edit_element(InfraredApp::EditElement value) { - element = value; -} - -InfraredApp::EditElement InfraredApp::get_edit_element(void) { - return element; -} - -void InfraredApp::set_edit_action(InfraredApp::EditAction value) { - action = value; -} - -InfraredApp::EditAction InfraredApp::get_edit_action(void) { - return action; -} - -void InfraredApp::set_current_button(int value) { - current_button = value; -} - -int InfraredApp::get_current_button() { - return current_button; -} - -void InfraredApp::notify_success() { - notification_message(notification, &sequence_success); -} - -void InfraredApp::notify_blink_read() { - notification_message(notification, &sequence_blink_cyan_10); -} - -void InfraredApp::notify_blink_send() { - notification_message(notification, &sequence_blink_magenta_10); -} - -DialogsApp* InfraredApp::get_dialogs() { - return dialogs; -} - -void InfraredApp::notify_green_on() { - notification_message(notification, &sequence_set_only_green_255); -} - -void InfraredApp::notify_green_off() { - notification_message(notification, &sequence_reset_green); -} - -InfraredWorker* InfraredApp::get_infrared_worker() { - return infrared_worker; -} - -const InfraredAppSignal& InfraredApp::get_received_signal() const { - return received_signal; -} - -void InfraredApp::set_received_signal(const InfraredAppSignal& signal) { - received_signal = signal; -} - -void InfraredApp::signal_sent_callback(void* context) { - InfraredApp* app = static_cast(context); - app->notify_blink_send(); -} diff --git a/applications/infrared/infrared_app.h b/applications/infrared/infrared_app.h deleted file mode 100644 index 1cb8b6617..000000000 --- a/applications/infrared/infrared_app.h +++ /dev/null @@ -1,326 +0,0 @@ -/** - * @file infrared_app.h - * Infrared: Main infrared application class - */ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include - -#include "scene/infrared_app_scene.h" -#include "scene/infrared_app_scene.h" -#include "infrared_app_view_manager.h" -#include "infrared_app_remote_manager.h" -#include "infrared_app_view_manager.h" - -/** Main Infrared application class */ -class InfraredApp { -public: - /** Enum to save scene state: edit element */ - enum class EditElement : uint8_t { - Button, - Remote, - }; - /** Enum to save scene state: edit action */ - enum class EditAction : uint8_t { - Rename, - Delete, - }; - /** List of scenes for Infrared application */ - enum class Scene : uint8_t { - Exit, - Start, - Universal, - UniversalTV, - UniversalAudio, - UniversalAirConditioner, - Learn, - LearnSuccess, - LearnEnterName, - LearnDone, - AskBack, - Remote, - RemoteList, - Edit, - EditKeySelect, - EditRename, - EditDelete, - EditRenameDone, - EditDeleteDone, - }; - - /** Start application - * - * @param args - application arguments. - * Allowed argument is path to remote file. - * @retval 0 on success, error code otherwise - */ - int32_t run(void* args); - - /** Switch to next scene. Put current scene number on stack. - * Doesn't save scene state. - * - * @param index - next scene index - */ - void switch_to_next_scene(Scene index); - - /** Switch to next scene, but don't put current scene on - * stack. Thus calling switch_to_previous_scene() doesn't return - * to current scene. - * - * @param index - next scene index - */ - void switch_to_next_scene_without_saving(Scene index); - - /** Switch to previous scene. Pop scenes from stack and switch to last one. - * - * @param count - how many scenes should be popped - * @retval false on failed, true on success - */ - bool switch_to_previous_scene(uint8_t count = 1); - - /** Get previous scene in scene stack - * - * @retval previous scene - */ - Scene get_previous_scene(); - - /** Get view manager instance - * - * @retval view manager instance - */ - InfraredAppViewManager* get_view_manager(); - - /** Set one of text stores - * - * @param index - index of text store - * @param text - text to set - */ - void set_text_store(uint8_t index, const char* text...); - - /** Get value in text store - * - * @param index - index of text store - * @retval value in text_store - */ - char* get_text_store(uint8_t index); - - /** Get text store size - * - * @retval size of text store - */ - uint8_t get_text_store_size(); - - /** Get remote manager instance - * - * @retval remote manager instance - */ - InfraredAppRemoteManager* get_remote_manager(); - - /** Get infrared worker instance - * - * @retval infrared worker instance - */ - InfraredWorker* get_infrared_worker(); - - /** Get signal, previously got on Learn scene - * - * @retval received signal - */ - const InfraredAppSignal& get_received_signal() const; - - /** Set received signal - * - * @param signal - signal - */ - void set_received_signal(const InfraredAppSignal& signal); - - /** Switch to previous scene in one of provided in list. - * Pop scene stack, and find first scene from list. - * - * @param scenes_list - list of scenes - */ - void search_and_switch_to_previous_scene(const std::initializer_list& scenes_list); - - /** Set edit element value. It is used on edit scene to determine - * what should be deleted - remote or button. - * - * @param value - value to set - */ - void set_edit_element(EditElement value); - - /** Get edit element - * - * @retval edit element value - */ - EditElement get_edit_element(void); - - /** Set edit action value. It is used on edit scene to determine - * what action to perform - deletion or renaming. - * - * @param value - value to set - */ - void set_edit_action(EditAction value); - - /** Get edit action - * - * @retval edit action value - */ - EditAction get_edit_action(void); - - /** Get state of learning new signal. - * Adding new remote with 1 button from start scene and - * learning 1 additional button to remote have very similar - * flow, so they are joined. Difference in flow is handled - * by this boolean flag. - * - * @retval false if flow is in learning new remote, true if - * adding signal to created remote - * - */ - bool get_learn_new_remote(); - - /** Set state of learning new signal. - * Adding new remote with 1 button from start scene and - * learning 1 additional button to remote have very similar - * flow, so they are joined. Difference in flow is handled - * by this boolean flag. - * - * @param value - false if flow is in learning new remote, true if - * adding signal to created remote - */ - void set_learn_new_remote(bool value); - - /** Button is not assigned value - */ - enum : int { - ButtonNA = -1, - }; - - /** Get current button index - * - * @retval current button index - */ - int get_current_button(); - - /** Set current button index - * - * @param current button index - */ - void set_current_button(int value); - - /** Play success notification */ - void notify_success(); - /** Play red blink notification */ - void notify_blink_read(); - /** Light green */ - void notify_green_on(); - /** Disable green light */ - void notify_green_off(); - /** Blink on send */ - void notify_blink_send(); - - /** Get Dialogs instance */ - DialogsApp* get_dialogs(); - - /** Text input callback - * - * @param context - context to pass to callback - */ - static void text_input_callback(void* context); - - /** Popup callback - * - * @param context - context to pass to callback - */ - static void popup_callback(void* context); - - /** Signal sent callback - * - * @param context - context to pass to callback - */ - static void signal_sent_callback(void* context); - - /** Main class constructor, initializes all critical objects */ - InfraredApp(); - /** Main class destructor, deinitializes all critical objects */ - ~InfraredApp(); - - string_t file_path; - - /** Path to Infrared directory */ - static constexpr const char* infrared_directory = "/any/infrared"; - /** Infrared files extension (remote files and universal databases) */ - static constexpr const char* infrared_extension = ".ir"; - /** Max Raw timings in signal */ - static constexpr const uint32_t max_raw_timings_in_signal = 512; - /** Max line length in Infrared file */ - static constexpr const uint32_t max_line_length = - (9 + 1) * InfraredApp::max_raw_timings_in_signal + 100; - -private: - /** Text store size */ - static constexpr const uint8_t text_store_size = 128; - /** Amount of text stores */ - static constexpr const uint8_t text_store_max = 2; - /** Store text here, for some views, because they doesn't - * hold ownership of text */ - char text_store[text_store_max][text_store_size + 1]; - /** - * Flag to control adding new signal flow. - * Adding new remote with 1 button from start scene and - * learning 1 additional button to remote have very similar - * flow, so they are joined. Difference in flow is handled - * by this boolean flag. - */ - bool learn_new_remote; - /** Value to control edit scene */ - EditElement element; - /** Value to control edit scene */ - EditAction action; - /** Selected button index */ - uint32_t current_button; - - /** Notification instance */ - NotificationApp* notification; - /** Dialogs instance */ - DialogsApp* dialogs; - /** View manager instance */ - InfraredAppViewManager view_manager; - /** Remote manager instance */ - InfraredAppRemoteManager remote_manager; - /** Infrared worker instance */ - InfraredWorker* infrared_worker; - /** Signal received on Learn scene */ - InfraredAppSignal received_signal; - - /** Stack of previous scenes */ - std::forward_list previous_scenes_list; - /** Now acting scene */ - Scene current_scene = Scene::Start; - - /** Map of index/scene objects */ - std::map scenes = { - {Scene::Start, new InfraredAppSceneStart()}, - {Scene::Universal, new InfraredAppSceneUniversal()}, - {Scene::UniversalTV, new InfraredAppSceneUniversalTV()}, - {Scene::Learn, new InfraredAppSceneLearn()}, - {Scene::LearnSuccess, new InfraredAppSceneLearnSuccess()}, - {Scene::LearnEnterName, new InfraredAppSceneLearnEnterName()}, - {Scene::LearnDone, new InfraredAppSceneLearnDone()}, - {Scene::AskBack, new InfraredAppSceneAskBack()}, - {Scene::Remote, new InfraredAppSceneRemote()}, - {Scene::RemoteList, new InfraredAppSceneRemoteList()}, - {Scene::Edit, new InfraredAppSceneEdit()}, - {Scene::EditKeySelect, new InfraredAppSceneEditKeySelect()}, - {Scene::EditRename, new InfraredAppSceneEditRename()}, - {Scene::EditDelete, new InfraredAppSceneEditDelete()}, - {Scene::EditRenameDone, new InfraredAppSceneEditRenameDone()}, - {Scene::EditDeleteDone, new InfraredAppSceneEditDeleteDone()}, - }; -}; diff --git a/applications/infrared/infrared_app_brute_force.cpp b/applications/infrared/infrared_app_brute_force.cpp deleted file mode 100644 index 2910a3998..000000000 --- a/applications/infrared/infrared_app_brute_force.cpp +++ /dev/null @@ -1,93 +0,0 @@ - -#include "helpers/infrared_parser.h" -#include "infrared_app_brute_force.h" -#include "infrared_app_signal.h" -#include -#include -#include - -void InfraredAppBruteForce::add_record(int index, const char* name) { - records[name].index = index; - records[name].amount = 0; -} - -bool InfraredAppBruteForce::calculate_messages() { - bool result = false; - - Storage* storage = static_cast(furi_record_open("storage")); - FlipperFormat* ff = flipper_format_file_alloc(storage); - result = flipper_format_file_open_existing(ff, universal_db_filename); - - if(result) { - InfraredAppSignal signal; - - string_t signal_name; - string_init(signal_name); - while(flipper_format_read_string(ff, "name", signal_name)) { - auto element = records.find(string_get_cstr(signal_name)); - if(element != records.cend()) { - ++element->second.amount; - } - } - string_clear(signal_name); - } - - flipper_format_free(ff); - furi_record_close("storage"); - return result; -} - -void InfraredAppBruteForce::stop_bruteforce() { - furi_assert((current_record.size())); - - if(current_record.size()) { - furi_assert(ff); - current_record.clear(); - flipper_format_free(ff); - furi_record_close("storage"); - } -} - -bool InfraredAppBruteForce::send_next_bruteforce(void) { - furi_assert(current_record.size()); - furi_assert(ff); - - InfraredAppSignal signal; - std::string signal_name; - bool result = false; - do { - result = infrared_parser_read_signal(ff, signal, signal_name); - } while(result && current_record.compare(signal_name)); - - if(result) { - signal.transmit(); - } - return result; -} - -bool InfraredAppBruteForce::start_bruteforce(int index, int& record_amount) { - bool result = false; - record_amount = 0; - - for(const auto& it : records) { - if(it.second.index == index) { - record_amount = it.second.amount; - if(record_amount) { - current_record = it.first; - } - break; - } - } - - if(record_amount) { - Storage* storage = static_cast(furi_record_open("storage")); - ff = flipper_format_file_alloc(storage); - result = flipper_format_file_open_existing(ff, universal_db_filename); - if(!result) { - flipper_format_free(ff); - furi_record_close("storage"); - } - } - - return result; -} diff --git a/applications/infrared/infrared_app_brute_force.h b/applications/infrared/infrared_app_brute_force.h deleted file mode 100644 index 2dd3ade9c..000000000 --- a/applications/infrared/infrared_app_brute_force.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * @file infrared_app_brute_force.h - * Infrared: Brute Force class description - */ -#pragma once - -#include -#include -#include - -/** Class handles brute force mechanic */ -class InfraredAppBruteForce { - /** Universal database filename */ - const char* universal_db_filename; - - /** Current record name (POWER, MUTE, VOL+, etc). - * This is the name of signal to brute force. */ - std::string current_record; - - /** Flipper File Format instance */ - FlipperFormat* ff; - - /** Data about every record - index in button panel view - * and amount of signals, which is need for correct - * progress bar displaying. */ - typedef struct { - /** Index of record in button panel view model */ - int index; - /** Amount of signals of that type (POWER, MUTE, etc) */ - int amount; - } Record; - - /** Container to hold Record info. - * 'key' is record name, because we have to search by both, index and name, - * but index search has place once per button press, and should not be - * noticed, but name search should occur during entering universal menu, - * and will go through container for every record in file, that's why - * more critical to have faster search by record name. - */ - std::unordered_map records; - -public: - /** Calculate messages. Walk through the file ('universal_db_name') - * and calculate amount of records of certain type. */ - bool calculate_messages(); - - /** Start brute force */ - bool start_bruteforce(int index, int& record_amount); - - /** Stop brute force */ - void stop_bruteforce(); - - /** Send next signal during brute force */ - bool send_next_bruteforce(); - - /** Add record to container of records */ - void add_record(int index, const char* name); - - /** Initialize class, set db file */ - InfraredAppBruteForce(const char* filename) - : universal_db_filename(filename) { - } - - /** Deinitialize class */ - ~InfraredAppBruteForce() { - } -}; diff --git a/applications/infrared/infrared_app_event.h b/applications/infrared/infrared_app_event.h deleted file mode 100644 index 168ba0b1c..000000000 --- a/applications/infrared/infrared_app_event.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @file infrared_app_event.h - * Infrared: Scene events description - */ -#pragma once -#include -#include - -/** Infrared events class */ -class InfraredAppEvent { -public: - /** Type of event enum */ - enum class Type : uint8_t { - /** Tick event come after no other events came in 100 ms */ - Tick, - /** Exit application event */ - Exit, - /** Back event */ - Back, - /** Menu selected event type. Provided with payload value. */ - MenuSelected, - /** Button press event. Need for continuous signal sending. */ - MenuSelectedPress, - /** Button release event. Need for continuous signal sending. */ - MenuSelectedRelease, - /** Events from DialogEx view module */ - DialogExSelected, - /** Infrared signal received event */ - InfraredMessageReceived, - /** Text edit done event */ - TextEditDone, - /** Popup timer finished event */ - PopupTimer, - /** Button panel pressed event */ - ButtonPanelPressed, - }; - - union { - int32_t dummy; - /** Menu selected event type payload. Selected index. */ - int32_t menu_index; - /** DialogEx view module event type payload */ - DialogExResult dialog_ex_result; - } payload; - - /** Type of event */ - Type type; -}; diff --git a/applications/infrared/infrared_app_remote_manager.cpp b/applications/infrared/infrared_app_remote_manager.cpp deleted file mode 100644 index faeccb39e..000000000 --- a/applications/infrared/infrared_app_remote_manager.cpp +++ /dev/null @@ -1,266 +0,0 @@ -#include "m-string.h" -#include "storage/filesystem_api_defines.h" -#include -#include "infrared_app_remote_manager.h" -#include "infrared/helpers/infrared_parser.h" -#include "infrared/infrared_app_signal.h" - -#include - -#include -#include -#include -#include -#include -#include "infrared_app.h" -#include - -static const char* default_remote_name = "remote"; - -void InfraredAppRemoteManager::find_vacant_remote_name(string_t name, string_t path) { - Storage* storage = static_cast(furi_record_open("storage")); - - string_t base_path; - string_init_set(base_path, path); - - if(string_end_with_str_p(base_path, InfraredApp::infrared_extension)) { - size_t filename_start = string_search_rchar(base_path, '/'); - string_left(base_path, filename_start); - } - - string_printf( - base_path, - "%s/%s%s", - string_get_cstr(path), - string_get_cstr(name), - InfraredApp::infrared_extension); - - FS_Error error = storage_common_stat(storage, string_get_cstr(base_path), NULL); - - if(error == FSE_OK) { - /* if suggested name is occupied, try another one (name2, name3, etc) */ - size_t dot = string_search_rchar(base_path, '.'); - string_left(base_path, dot); - - string_t path_temp; - string_init(path_temp); - - uint32_t i = 1; - do { - string_printf( - path_temp, - "%s%u%s", - string_get_cstr(base_path), - ++i, - InfraredApp::infrared_extension); - error = storage_common_stat(storage, string_get_cstr(path_temp), NULL); - } while(error == FSE_OK); - - string_clear(path_temp); - - if(error == FSE_NOT_EXIST) { - string_cat_printf(name, "%u", i); - } - } - - string_clear(base_path); - furi_record_close("storage"); -} - -bool InfraredAppRemoteManager::add_button(const char* button_name, const InfraredAppSignal& signal) { - remote->buttons.emplace_back(button_name, signal); - return store(); -} - -bool InfraredAppRemoteManager::add_remote_with_button( - const char* button_name, - const InfraredAppSignal& signal) { - furi_check(button_name != nullptr); - - string_t new_name; - string_init_set_str(new_name, default_remote_name); - - string_t new_path; - string_init_set_str(new_path, InfraredApp::infrared_directory); - - find_vacant_remote_name(new_name, new_path); - - string_cat_printf( - new_path, "/%s%s", string_get_cstr(new_name), InfraredApp::infrared_extension); - - remote = std::make_unique(new_path); - remote->name = std::string(string_get_cstr(new_name)); - - string_clear(new_path); - string_clear(new_name); - - return add_button(button_name, signal); -} - -std::vector InfraredAppRemoteManager::get_button_list(void) const { - std::vector name_vector; - name_vector.reserve(remote->buttons.size()); - - for(const auto& it : remote->buttons) { - name_vector.emplace_back(it.name); - } - - // copy elision - return name_vector; -} - -const InfraredAppSignal& InfraredAppRemoteManager::get_button_data(size_t index) const { - furi_check(remote.get() != nullptr); - auto& buttons = remote->buttons; - furi_check(index < buttons.size()); - - return buttons.at(index).signal; -} - -bool InfraredAppRemoteManager::delete_remote() { - Storage* storage = static_cast(furi_record_open("storage")); - - FS_Error error = storage_common_remove(storage, string_get_cstr(remote->path)); - reset_remote(); - - furi_record_close("storage"); - return (error == FSE_OK || error == FSE_NOT_EXIST); -} - -void InfraredAppRemoteManager::reset_remote() { - remote.reset(); -} - -bool InfraredAppRemoteManager::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 InfraredAppRemoteManager::get_button_name(uint32_t index) { - furi_check(remote.get() != nullptr); - auto& buttons = remote->buttons; - furi_check(index < buttons.size()); - return buttons[index].name.c_str(); -} - -std::string InfraredAppRemoteManager::get_remote_name() { - return remote.get() ? remote->name : std::string(); -} - -bool InfraredAppRemoteManager::rename_remote(const char* str) { - furi_check(str != nullptr); - furi_check(remote.get() != nullptr); - furi_check(!string_empty_p(remote->path)); - - if(!remote->name.compare(str)) { - return true; - } - - string_t new_name; - string_init_set_str(new_name, str); - find_vacant_remote_name(new_name, remote->path); - - string_t new_path; - string_init_set(new_path, remote->path); - if(string_end_with_str_p(new_path, InfraredApp::infrared_extension)) { - size_t filename_start = string_search_rchar(new_path, '/'); - string_left(new_path, filename_start); - } - string_cat_printf( - new_path, "/%s%s", string_get_cstr(new_name), InfraredApp::infrared_extension); - - Storage* storage = static_cast(furi_record_open("storage")); - - FS_Error error = - storage_common_rename(storage, string_get_cstr(remote->path), string_get_cstr(new_path)); - remote->name = std::string(string_get_cstr(new_name)); - - string_clear(new_name); - string_clear(new_path); - - furi_record_close("storage"); - return (error == FSE_OK || error == FSE_EXIST); -} - -bool InfraredAppRemoteManager::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 InfraredAppRemoteManager::get_number_of_buttons() { - furi_check(remote.get() != nullptr); - return remote->buttons.size(); -} - -bool InfraredAppRemoteManager::store(void) { - bool result = false; - Storage* storage = static_cast(furi_record_open("storage")); - - if(!storage_simply_mkdir(storage, InfraredApp::infrared_directory)) return false; - - FlipperFormat* ff = flipper_format_file_alloc(storage); - - FURI_LOG_I("RemoteManager", "store file: \'%s\'", string_get_cstr(remote->path)); - result = flipper_format_file_open_always(ff, string_get_cstr(remote->path)); - if(result) { - result = flipper_format_write_header_cstr(ff, "IR signals file", 1); - } - if(result) { - for(const auto& button : remote->buttons) { - result = infrared_parser_save_signal(ff, button.signal, button.name.c_str()); - if(!result) { - break; - } - } - } - - flipper_format_free(ff); - furi_record_close("storage"); - return result; -} - -bool InfraredAppRemoteManager::load(string_t path) { - bool result = false; - Storage* storage = static_cast(furi_record_open("storage")); - FlipperFormat* ff = flipper_format_file_alloc(storage); - - FURI_LOG_I("RemoteManager", "load file: \'%s\'", string_get_cstr(path)); - result = flipper_format_file_open_existing(ff, string_get_cstr(path)); - if(result) { - string_t header; - string_init(header); - uint32_t version; - result = flipper_format_read_header(ff, header, &version); - if(result) { - result = !string_cmp_str(header, "IR signals file") && (version == 1); - } - string_clear(header); - } - if(result) { - string_t new_name; - string_init(new_name); - - remote = std::make_unique(path); - path_extract_filename(path, new_name, true); - remote->name = std::string(string_get_cstr(new_name)); - - string_clear(new_name); - InfraredAppSignal signal; - std::string signal_name; - while(infrared_parser_read_signal(ff, signal, signal_name)) { - remote->buttons.emplace_back(signal_name.c_str(), std::move(signal)); - } - } - - flipper_format_free(ff); - furi_record_close("storage"); - return result; -} diff --git a/applications/infrared/infrared_app_remote_manager.h b/applications/infrared/infrared_app_remote_manager.h deleted file mode 100644 index b6f0b170f..000000000 --- a/applications/infrared/infrared_app_remote_manager.h +++ /dev/null @@ -1,189 +0,0 @@ -/** - * @file infrared_app_remote_manager.h - * Infrared: Remote manager class. - * It holds remote, can load/save/rename remote, - * add/remove/rename buttons. - */ -#pragma once - -#include "infrared_app_signal.h" - -#include "m-string.h" -#include -#include - -#include -#include -#include -#include - -/** Class to handle remote button */ -class InfraredAppRemoteButton { - /** Allow field access */ - friend class InfraredAppRemoteManager; - /** Name of signal */ - std::string name; - /** Signal data */ - InfraredAppSignal signal; - -public: - /** Initialize remote button - * - * @param name - button name - * @param signal - signal to copy for remote button - */ - InfraredAppRemoteButton(const char* name, const InfraredAppSignal& signal) - : name(name) - , signal(signal) { - } - - /** Initialize remote button - * - * @param name - button name - * @param signal - signal to move for remote button - */ - InfraredAppRemoteButton(const char* name, InfraredAppSignal&& signal) - : name(name) - , signal(std::move(signal)) { - } - - /** Deinitialize remote button */ - ~InfraredAppRemoteButton() { - } -}; - -/** Class to handle remote */ -class InfraredAppRemote { - /** Allow field access */ - friend class InfraredAppRemoteManager; - /** Button container */ - std::vector buttons; - /** Name of remote */ - std::string name; - /** Path to remote file */ - string_t path; - -public: - /** Initialize new remote - * - * @param path - remote file path - */ - InfraredAppRemote(string_t file_path) { - string_init_set(path, file_path); - } - - ~InfraredAppRemote() { - string_clear(path); - } -}; - -/** Class to handle remote manager */ -class InfraredAppRemoteManager { - /** Remote instance. There can be 1 remote loaded at a time. */ - std::unique_ptr remote; - -public: - /** Restriction to button name length. Buttons larger are ignored. */ - static constexpr const uint32_t max_button_name_length = 22; - - /** Restriction to remote name length. Remotes larger are ignored. */ - static constexpr const uint32_t max_remote_name_length = 22; - - /** Construct button from signal, and create remote - * - * @param button_name - name of button to create - * @param signal - signal to create button from - * @retval true for success, false otherwise - * */ - bool add_remote_with_button(const char* button_name, const InfraredAppSignal& signal); - - /** Add button to current remote - * - * @param button_name - name of button to create - * @param signal - signal to create button from - * @retval true for success, false otherwise - * */ - bool add_button(const char* button_name, const InfraredAppSignal& signal); - - /** Rename button in current remote - * - * @param index - index of button to rename - * @param str - new button name - */ - bool rename_button(uint32_t index, const char* str); - - /** Rename current remote - * - * @param str - new remote name - */ - bool rename_remote(const char* str); - - /** Find vacant remote name. If suggested name is occupied, - * incremented digit(2,3,4,etc) added to name and check repeated. - * - * @param name - suggested remote name - * @param path - remote file path - */ - void find_vacant_remote_name(string_t name, string_t path); - - /** Get button list - * - * @retval container of button names - */ - std::vector get_button_list() const; - - /** Get button name by index - * - * @param index - index of button to get name from - * @retval button name - */ - std::string get_button_name(uint32_t index); - - /** Get remote name - * - * @retval remote name - */ - std::string get_remote_name(); - - /** Get number of buttons - * - * @retval number of buttons - */ - size_t get_number_of_buttons(); - - /** Get button's signal - * - * @param index - index of interested button - * @retval signal - */ - const InfraredAppSignal& get_button_data(size_t index) const; - - /** Delete button - * - * @param index - index of interested button - * @retval true if success, false otherwise - */ - bool delete_button(uint32_t index); - - /** Delete remote - * - * @retval true if success, false otherwise - */ - bool delete_remote(); - - /** Clean all loaded info in current remote */ - void reset_remote(); - - /** Store current remote data on disk - * - * @retval true if success, false otherwise - */ - bool store(); - - /** Load data from disk into current remote - * - * @param path - path to remote file - * @retval true if success, false otherwise - */ - bool load(string_t path); -}; diff --git a/applications/infrared/infrared_app_signal.cpp b/applications/infrared/infrared_app_signal.cpp deleted file mode 100644 index 3344b3ca4..000000000 --- a/applications/infrared/infrared_app_signal.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "infrared_app_signal.h" -#include - -void InfraredAppSignal::copy_raw_signal( - const uint32_t* timings, - size_t size, - uint32_t frequency, - float duty_cycle) { - furi_assert(size); - furi_assert(timings); - - payload.raw.frequency = frequency; - payload.raw.duty_cycle = duty_cycle; - payload.raw.timings_cnt = size; - if(size) { - payload.raw.timings = new uint32_t[size]; - memcpy(payload.raw.timings, timings, size * sizeof(uint32_t)); - } -} - -void InfraredAppSignal::clear_timings() { - if(raw_signal) { - delete[] payload.raw.timings; - payload.raw.timings_cnt = 0; - payload.raw.timings = nullptr; - } -} - -InfraredAppSignal::InfraredAppSignal( - const uint32_t* timings, - size_t timings_cnt, - uint32_t frequency, - float duty_cycle) { - raw_signal = true; - copy_raw_signal(timings, timings_cnt, frequency, duty_cycle); -} - -InfraredAppSignal::InfraredAppSignal(const InfraredMessage* infrared_message) { - raw_signal = false; - payload.message = *infrared_message; -} - -InfraredAppSignal& InfraredAppSignal::operator=(const InfraredAppSignal& other) { - clear_timings(); - raw_signal = other.raw_signal; - if(!raw_signal) { - payload.message = other.payload.message; - } else { - copy_raw_signal( - other.payload.raw.timings, - other.payload.raw.timings_cnt, - other.payload.raw.frequency, - other.payload.raw.duty_cycle); - } - - return *this; -} - -InfraredAppSignal::InfraredAppSignal(const InfraredAppSignal& other) { - raw_signal = other.raw_signal; - if(!raw_signal) { - payload.message = other.payload.message; - } else { - copy_raw_signal( - other.payload.raw.timings, - other.payload.raw.timings_cnt, - other.payload.raw.frequency, - other.payload.raw.duty_cycle); - } -} - -InfraredAppSignal::InfraredAppSignal(InfraredAppSignal&& other) { - raw_signal = other.raw_signal; - if(!raw_signal) { - payload.message = other.payload.message; - } else { - furi_assert(other.payload.raw.timings_cnt > 0); - - payload.raw.timings = other.payload.raw.timings; - payload.raw.timings_cnt = other.payload.raw.timings_cnt; - payload.raw.frequency = other.payload.raw.frequency; - payload.raw.duty_cycle = other.payload.raw.duty_cycle; - other.payload.raw.timings = nullptr; - other.payload.raw.timings_cnt = 0; - other.raw_signal = false; - } -} - -void InfraredAppSignal::set_message(const InfraredMessage* infrared_message) { - clear_timings(); - raw_signal = false; - payload.message = *infrared_message; -} - -void InfraredAppSignal::set_raw_signal( - uint32_t* timings, - size_t timings_cnt, - uint32_t frequency, - float duty_cycle) { - clear_timings(); - raw_signal = true; - copy_raw_signal(timings, timings_cnt, frequency, duty_cycle); -} - -void InfraredAppSignal::transmit() const { - if(!raw_signal) { - infrared_send(&payload.message, 1); - } else { - infrared_send_raw_ext( - payload.raw.timings, - payload.raw.timings_cnt, - true, - payload.raw.frequency, - payload.raw.duty_cycle); - } -} diff --git a/applications/infrared/infrared_app_signal.h b/applications/infrared/infrared_app_signal.h deleted file mode 100644 index 7b0b491b1..000000000 --- a/applications/infrared/infrared_app_signal.h +++ /dev/null @@ -1,134 +0,0 @@ -/** - * @file infrared_app_signal.h - * Infrared: Signal class - */ -#pragma once -#include -#include -#include -#include - -/** Infrared application signal class */ -class InfraredAppSignal { -public: - /** Raw signal structure */ - typedef struct { - /** Timings amount */ - size_t timings_cnt; - /** Samples of raw signal in ms */ - uint32_t* timings; - /** PWM Frequency of raw signal */ - uint32_t frequency; - /** PWM Duty cycle of raw signal */ - float duty_cycle; - } RawSignal; - -private: - /** if true - signal is raw, if false - signal is parsed */ - bool raw_signal; - /** signal data, either raw or parsed */ - union { - /** signal data for parsed signal */ - InfraredMessage message; - /** raw signal data */ - RawSignal raw; - } payload; - - /** Copy raw signal into object - * - * @param timings - timings (samples) of raw signal - * @param size - number of timings - * @frequency - PWM frequency of raw signal - * @duty_cycle - PWM duty cycle - */ - void - copy_raw_signal(const uint32_t* timings, size_t size, uint32_t frequency, float duty_cycle); - /** Clear and free timings data */ - void clear_timings(); - -public: - /** Construct Infrared signal class */ - InfraredAppSignal() { - raw_signal = false; - payload.message.protocol = InfraredProtocolUnknown; - } - - /** Destruct signal class and free all allocated data */ - ~InfraredAppSignal() { - clear_timings(); - } - - /** Construct object with raw signal - * - * @param timings - timings (samples) of raw signal - * @param size - number of timings - * @frequency - PWM frequency of raw signal - * @duty_cycle - PWM duty cycle - */ - InfraredAppSignal( - const uint32_t* timings, - size_t timings_cnt, - uint32_t frequency, - float duty_cycle); - - /** Construct object with parsed signal - * - * @param infrared_message - parsed_signal to construct from - */ - InfraredAppSignal(const InfraredMessage* infrared_message); - - /** Copy constructor */ - InfraredAppSignal(const InfraredAppSignal& other); - /** Move constructor */ - InfraredAppSignal(InfraredAppSignal&& other); - - /** Assignment operator */ - InfraredAppSignal& operator=(const InfraredAppSignal& signal); - - /** Set object to parsed signal - * - * @param infrared_message - parsed_signal to construct from - */ - void set_message(const InfraredMessage* infrared_message); - - /** Set object to raw signal - * - * @param timings - timings (samples) of raw signal - * @param size - number of timings - * @frequency - PWM frequency of raw signal - * @duty_cycle - PWM duty cycle - */ - void - set_raw_signal(uint32_t* timings, size_t timings_cnt, uint32_t frequency, float duty_cycle); - - /** Transmit held signal (???) */ - void transmit() const; - - /** Show is held signal raw - * - * @retval true if signal is raw, false if signal is parsed - */ - bool is_raw(void) const { - return raw_signal; - } - - /** Get parsed signal. - * User must check is_raw() signal before calling this function. - * - * @retval parsed signal pointer - */ - const InfraredMessage& get_message(void) const { - furi_assert(!raw_signal); - return payload.message; - } - - /** Get raw signal. - * User must check is_raw() signal before calling this function. - * - * @retval raw signal - */ - const RawSignal& get_raw_signal(void) const { - furi_assert(raw_signal); - return payload.raw; - } -}; diff --git a/applications/infrared/infrared_app_view_manager.cpp b/applications/infrared/infrared_app_view_manager.cpp deleted file mode 100644 index f1e48ed4a..000000000 --- a/applications/infrared/infrared_app_view_manager.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "infrared/infrared_app_view_manager.h" -#include "infrared/view/infrared_progress_view.h" -#include "infrared_app.h" -#include "infrared/infrared_app_event.h" - -InfraredAppViewManager::InfraredAppViewManager() { - event_queue = osMessageQueueNew(10, sizeof(InfraredAppEvent), NULL); - - view_dispatcher = view_dispatcher_alloc(); - auto callback = cbc::obtain_connector(this, &InfraredAppViewManager::previous_view_callback); - - gui = static_cast(furi_record_open("gui")); - view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen); - - button_menu = button_menu_alloc(); - submenu = submenu_alloc(); - popup = popup_alloc(); - dialog_ex = dialog_ex_alloc(); - text_input = text_input_alloc(); - button_panel = button_panel_alloc(); - progress_view = infrared_progress_view_alloc(); - loading_view = loading_alloc(); - universal_view_stack = view_stack_alloc(); - view_stack_add_view(universal_view_stack, button_panel_get_view(button_panel)); - view_set_orientation(view_stack_get_view(universal_view_stack), ViewOrientationVertical); - - add_view(ViewId::UniversalRemote, view_stack_get_view(universal_view_stack)); - add_view(ViewId::ButtonMenu, button_menu_get_view(button_menu)); - add_view(ViewId::Submenu, submenu_get_view(submenu)); - add_view(ViewId::Popup, popup_get_view(popup)); - add_view(ViewId::DialogEx, dialog_ex_get_view(dialog_ex)); - add_view(ViewId::TextInput, text_input_get_view(text_input)); - - view_set_previous_callback(view_stack_get_view(universal_view_stack), callback); - view_set_previous_callback(button_menu_get_view(button_menu), callback); - view_set_previous_callback(submenu_get_view(submenu), callback); - view_set_previous_callback(popup_get_view(popup), callback); - view_set_previous_callback(dialog_ex_get_view(dialog_ex), callback); - view_set_previous_callback(text_input_get_view(text_input), callback); -} - -InfraredAppViewManager::~InfraredAppViewManager() { - view_dispatcher_remove_view( - view_dispatcher, static_cast(InfraredAppViewManager::ViewId::UniversalRemote)); - view_dispatcher_remove_view( - view_dispatcher, static_cast(InfraredAppViewManager::ViewId::ButtonMenu)); - view_dispatcher_remove_view( - view_dispatcher, static_cast(InfraredAppViewManager::ViewId::TextInput)); - view_dispatcher_remove_view( - view_dispatcher, static_cast(InfraredAppViewManager::ViewId::DialogEx)); - view_dispatcher_remove_view( - view_dispatcher, static_cast(InfraredAppViewManager::ViewId::Submenu)); - view_dispatcher_remove_view( - view_dispatcher, static_cast(InfraredAppViewManager::ViewId::Popup)); - - view_stack_remove_view(universal_view_stack, button_panel_get_view(button_panel)); - view_stack_free(universal_view_stack); - button_panel_free(button_panel); - submenu_free(submenu); - popup_free(popup); - button_menu_free(button_menu); - dialog_ex_free(dialog_ex); - text_input_free(text_input); - infrared_progress_view_free(progress_view); - loading_free(loading_view); - - view_dispatcher_free(view_dispatcher); - furi_record_close("gui"); - osMessageQueueDelete(event_queue); -} - -void InfraredAppViewManager::switch_to(ViewId type) { - view_dispatcher_switch_to_view(view_dispatcher, static_cast(type)); -} - -TextInput* InfraredAppViewManager::get_text_input() { - return text_input; -} - -DialogEx* InfraredAppViewManager::get_dialog_ex() { - return dialog_ex; -} - -Submenu* InfraredAppViewManager::get_submenu() { - return submenu; -} - -Popup* InfraredAppViewManager::get_popup() { - return popup; -} - -ButtonMenu* InfraredAppViewManager::get_button_menu() { - return button_menu; -} - -ButtonPanel* InfraredAppViewManager::get_button_panel() { - return button_panel; -} - -InfraredProgressView* InfraredAppViewManager::get_progress() { - return progress_view; -} - -Loading* InfraredAppViewManager::get_loading() { - return loading_view; -} - -ViewStack* InfraredAppViewManager::get_universal_view_stack() { - return universal_view_stack; -} - -osMessageQueueId_t InfraredAppViewManager::get_event_queue() { - return event_queue; -} - -void InfraredAppViewManager::clear_events() { - InfraredAppEvent event; - while(osMessageQueueGet(event_queue, &event, NULL, 0) == osOK) - ; -} - -void InfraredAppViewManager::receive_event(InfraredAppEvent* event) { - if(osMessageQueueGet(event_queue, event, NULL, 100) != osOK) { - event->type = InfraredAppEvent::Type::Tick; - } -} - -void InfraredAppViewManager::send_event(InfraredAppEvent* event) { - uint32_t timeout = 0; - /* Rapid button hammering on signal send scenes causes queue overflow - ignore it, - * but try to keep button release event - it switches off INFRARED DMA sending. */ - if(event->type == InfraredAppEvent::Type::MenuSelectedRelease) { - timeout = 200; - } - if((event->type == InfraredAppEvent::Type::DialogExSelected) && - (event->payload.dialog_ex_result == DialogExReleaseCenter)) { - timeout = 200; - } - - osMessageQueuePut(event_queue, event, 0, timeout); -} - -uint32_t InfraredAppViewManager::previous_view_callback(void*) { - if(event_queue != NULL) { - InfraredAppEvent event; - event.type = InfraredAppEvent::Type::Back; - send_event(&event); - } - - return VIEW_IGNORE; -} - -void InfraredAppViewManager::add_view(ViewId view_type, View* view) { - view_dispatcher_add_view(view_dispatcher, static_cast(view_type), view); -} diff --git a/applications/infrared/infrared_app_view_manager.h b/applications/infrared/infrared_app_view_manager.h deleted file mode 100644 index 106d2660e..000000000 --- a/applications/infrared/infrared_app_view_manager.h +++ /dev/null @@ -1,164 +0,0 @@ -/** - * @file infrared_app_view_manager.h - * Infrared: Scene events description - */ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "infrared_app_event.h" -#include "view/infrared_progress_view.h" - -/** Infrared View manager class */ -class InfraredAppViewManager { -public: - /** Infrared View Id enum, it is used - * to identify added views */ - enum class ViewId : uint8_t { - DialogEx, - TextInput, - Submenu, - ButtonMenu, - UniversalRemote, - Popup, - }; - - /** Class constructor */ - InfraredAppViewManager(); - /** Class destructor */ - ~InfraredAppViewManager(); - - /** Switch to another view - * - * @param id - view id to switch to - */ - void switch_to(ViewId id); - - /** Receive event from queue - * - * @param event - received event - */ - void receive_event(InfraredAppEvent* event); - - /** Send event to queue - * - * @param event - event to send - */ - void send_event(InfraredAppEvent* event); - - /** Clear events that already in queue - * - * @param event - event to send - */ - void clear_events(); - - /** Get dialog_ex view module - * - * @retval dialog_ex view module - */ - DialogEx* get_dialog_ex(); - - /** Get submenu view module - * - * @retval submenu view module - */ - Submenu* get_submenu(); - - /** Get popup view module - * - * @retval popup view module - */ - Popup* get_popup(); - - /** Get text_input view module - * - * @retval text_input view module - */ - TextInput* get_text_input(); - - /** Get button_menu view module - * - * @retval button_menu view module - */ - ButtonMenu* get_button_menu(); - - /** Get button_panel view module - * - * @retval button_panel view module - */ - ButtonPanel* get_button_panel(); - - /** Get view_stack view module used in universal remote - * - * @retval view_stack view module - */ - ViewStack* get_universal_view_stack(); - - /** Get progress view module - * - * @retval progress view module - */ - InfraredProgressView* get_progress(); - - /** Get loading view module - * - * @retval loading view module - */ - Loading* get_loading(); - - /** Get event queue - * - * @retval event queue - */ - osMessageQueueId_t get_event_queue(); - - /** Callback to handle back button - * - * @param context - context to pass to callback - * @retval always returns VIEW_IGNORE - */ - uint32_t previous_view_callback(void* context); - -private: - /** View Dispatcher instance. - * It handles view switching */ - ViewDispatcher* view_dispatcher; - /** Gui instance */ - Gui* gui; - /** Text input view module instance */ - TextInput* text_input; - /** DialogEx view module instance */ - DialogEx* dialog_ex; - /** Submenu view module instance */ - Submenu* submenu; - /** Popup view module instance */ - Popup* popup; - /** ButtonMenu view module instance */ - ButtonMenu* button_menu; - /** ButtonPanel view module instance */ - ButtonPanel* button_panel; - /** ViewStack view module instance */ - ViewStack* universal_view_stack; - /** ProgressView view module instance */ - InfraredProgressView* progress_view; - /** Loading view module instance */ - Loading* loading_view; - - /** Queue to handle events, which are processed in scenes */ - osMessageQueueId_t event_queue; - - /** Add View to pull of views - * - * @param view_id - id to identify view - * @param view - view to add - */ - void add_view(ViewId view_id, View* view); -}; diff --git a/applications/infrared/infrared_brute_force.c b/applications/infrared/infrared_brute_force.c new file mode 100644 index 000000000..872a76376 --- /dev/null +++ b/applications/infrared/infrared_brute_force.c @@ -0,0 +1,153 @@ +#include "infrared_brute_force.h" + +#include +#include +#include +#include + +#include "infrared_signal.h" + +typedef struct { + uint32_t index; + uint32_t count; +} InfraredBruteForceRecord; + +DICT_DEF2( + InfraredBruteForceRecordDict, + string_t, + STRING_OPLIST, + InfraredBruteForceRecord, + M_POD_OPLIST); + +struct InfraredBruteForce { + FlipperFormat* ff; + const char* db_filename; + string_t current_record_name; + InfraredBruteForceRecordDict_t records; +}; + +InfraredBruteForce* infrared_brute_force_alloc() { + InfraredBruteForce* brute_force = malloc(sizeof(InfraredBruteForce)); + brute_force->ff = NULL; + brute_force->db_filename = NULL; + string_init(brute_force->current_record_name); + InfraredBruteForceRecordDict_init(brute_force->records); + return brute_force; +} + +void infrared_brute_force_free(InfraredBruteForce* brute_force) { + furi_assert(!brute_force->ff); + InfraredBruteForceRecordDict_clear(brute_force->records); + string_clear(brute_force->current_record_name); + free(brute_force); +} + +void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename) { + brute_force->db_filename = db_filename; +} + +bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { + furi_assert(brute_force->db_filename); + bool success = false; + + Storage* storage = furi_record_open("storage"); + FlipperFormat* ff = flipper_format_file_alloc(storage); + + success = flipper_format_file_open_existing(ff, brute_force->db_filename); + if(success) { + string_t signal_name; + string_init(signal_name); + while(flipper_format_read_string(ff, "name", signal_name)) { + InfraredBruteForceRecord* record = + InfraredBruteForceRecordDict_get(brute_force->records, signal_name); + if(record) { + ++(record->count); + } + } + string_clear(signal_name); + } + + flipper_format_free(ff); + furi_record_close("storage"); + return success; +} + +bool infrared_brute_force_start( + InfraredBruteForce* brute_force, + uint32_t index, + uint32_t* record_count) { + bool success = false; + *record_count = 0; + + InfraredBruteForceRecordDict_it_t it; + for(InfraredBruteForceRecordDict_it(it, brute_force->records); + !InfraredBruteForceRecordDict_end_p(it); + InfraredBruteForceRecordDict_next(it)) { + const InfraredBruteForceRecordDict_itref_t* record = InfraredBruteForceRecordDict_cref(it); + if(record->value.index == index) { + *record_count = record->value.count; + if(*record_count) { + string_set(brute_force->current_record_name, record->key); + } + break; + } + } + + if(*record_count) { + Storage* storage = furi_record_open("storage"); + brute_force->ff = flipper_format_file_alloc(storage); + success = flipper_format_file_open_existing(brute_force->ff, brute_force->db_filename); + if(!success) { + flipper_format_free(brute_force->ff); + furi_record_close("storage"); + } + } + return success; +} + +bool infrared_brute_force_is_started(InfraredBruteForce* brute_force) { + return brute_force->ff; +} + +void infrared_brute_force_stop(InfraredBruteForce* brute_force) { + furi_assert(string_size(brute_force->current_record_name)); + furi_assert(brute_force->ff); + + string_reset(brute_force->current_record_name); + flipper_format_free(brute_force->ff); + furi_record_close("storage"); + brute_force->ff = NULL; +} + +bool infrared_brute_force_send_next(InfraredBruteForce* brute_force) { + furi_assert(string_size(brute_force->current_record_name)); + furi_assert(brute_force->ff); + bool success = false; + + string_t signal_name; + string_init(signal_name); + InfraredSignal* signal = infrared_signal_alloc(); + + do { + success = infrared_signal_read(signal, brute_force->ff, signal_name); + } while(success && !string_equal_p(brute_force->current_record_name, signal_name)); + + if(success) { + infrared_signal_transmit(signal); + } + + infrared_signal_free(signal); + string_clear(signal_name); + return success; +} + +void infrared_brute_force_add_record( + InfraredBruteForce* brute_force, + uint32_t index, + const char* name) { + InfraredBruteForceRecord value = {.index = index, .count = 0}; + string_t key; + string_init_set_str(key, name); + InfraredBruteForceRecordDict_set_at(brute_force->records, key, value); + string_clear(key); +} diff --git a/applications/infrared/infrared_brute_force.h b/applications/infrared/infrared_brute_force.h new file mode 100644 index 000000000..acf0d7b6e --- /dev/null +++ b/applications/infrared/infrared_brute_force.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +typedef struct InfraredBruteForce InfraredBruteForce; + +InfraredBruteForce* infrared_brute_force_alloc(); +void infrared_brute_force_free(InfraredBruteForce* brute_force); +void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename); +bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force); +bool infrared_brute_force_start( + InfraredBruteForce* brute_force, + uint32_t index, + uint32_t* record_count); +bool infrared_brute_force_is_started(InfraredBruteForce* brute_force); +void infrared_brute_force_stop(InfraredBruteForce* brute_force); +bool infrared_brute_force_send_next(InfraredBruteForce* brute_force); +void infrared_brute_force_add_record( + InfraredBruteForce* brute_force, + uint32_t index, + const char* name); diff --git a/applications/infrared/cli/infrared_cli.cpp b/applications/infrared/infrared_cli.c similarity index 72% rename from applications/infrared/cli/infrared_cli.cpp rename to applications/infrared/infrared_cli.c index 79ab987dd..1b3ec4b31 100644 --- a/applications/infrared/cli/infrared_cli.cpp +++ b/applications/infrared/infrared_cli.c @@ -1,16 +1,12 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include "../helpers/infrared_parser.h" +#include +#include +#include +#include + +#include "infrared_signal.h" + +#define INFRARED_CLI_BUF_SIZE 10 static void infrared_cli_start_ir_rx(Cli* cli, string_t args); static void infrared_cli_start_ir_tx(Cli* cli, string_t args); @@ -92,79 +88,83 @@ static void infrared_cli_print_usage(void) { INFRARED_MAX_FREQUENCY); } -static bool parse_message(const char* str, InfraredMessage* message) { +static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) { char protocol_name[32]; - int parsed = sscanf(str, "%31s %lX %lX", protocol_name, &message->address, &message->command); + InfraredMessage message; + int parsed = sscanf(str, "%31s %lX %lX", protocol_name, &message.address, &message.command); if(parsed != 3) { return false; } - message->protocol = infrared_get_protocol_by_name(protocol_name); - message->repeat = false; - - return infrared_parser_is_parsed_signal_valid(message); + message.repeat = false; + infrared_signal_set_message(signal, &message); + return infrared_signal_is_valid(signal); } -static bool parse_signal_raw( - const char* str, - uint32_t* timings, - uint32_t* timings_cnt, - float* duty_cycle, - uint32_t* frequency) { - char frequency_str[10]; - char duty_cycle_str[10]; +static bool infrared_cli_parse_raw(const char* str, InfraredSignal* signal) { + char frequency_str[INFRARED_CLI_BUF_SIZE]; + char duty_cycle_str[INFRARED_CLI_BUF_SIZE]; int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str); - if(parsed != 2) return false; - *frequency = atoi(frequency_str); - *duty_cycle = (float)atoi(duty_cycle_str) / 100; - str += strlen(frequency_str) + strlen(duty_cycle_str) + 10; - - uint32_t timings_cnt_max = *timings_cnt; - *timings_cnt = 0; - - while(1) { - char timing_str[10]; - for(; *str == ' '; ++str) - ; - if(1 != sscanf(str, "%9s", timing_str)) break; - str += strlen(timing_str); - uint32_t timing = atoi(timing_str); - if(timing <= 0) break; - if(*timings_cnt >= timings_cnt_max) break; - timings[*timings_cnt] = timing; - ++*timings_cnt; + if(parsed != 2) { + return false; } - return infrared_parser_is_raw_signal_valid(*frequency, *duty_cycle, *timings_cnt); + uint32_t* timings = malloc(sizeof(uint32_t) * MAX_TIMINGS_AMOUNT); + uint32_t frequency = atoi(frequency_str); + float duty_cycle = (float)atoi(duty_cycle_str) / 100; + + str += strlen(frequency_str) + strlen(duty_cycle_str) + INFRARED_CLI_BUF_SIZE; + + size_t timings_size = 0; + while(1) { + while(*str == ' ') { + ++str; + } + + char timing_str[INFRARED_CLI_BUF_SIZE]; + if(sscanf(str, "%9s", timing_str) != 1) { + break; + } + + str += strlen(timing_str); + uint32_t timing = atoi(timing_str); + + if((timing <= 0) || (timings_size >= MAX_TIMINGS_AMOUNT)) { + break; + } + + timings[timings_size] = timing; + ++timings_size; + } + + infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle); + free(timings); + + return infrared_signal_is_valid(signal); } static void infrared_cli_start_ir_tx(Cli* cli, string_t args) { UNUSED(cli); - InfraredMessage message; const char* str = string_get_cstr(args); - uint32_t frequency; - float duty_cycle; - uint32_t timings_cnt = MAX_TIMINGS_AMOUNT; - uint32_t* timings = (uint32_t*)malloc(sizeof(uint32_t) * timings_cnt); + InfraredSignal* signal = infrared_signal_alloc(); - if(parse_message(str, &message)) { - infrared_send(&message, 1); - } else if(parse_signal_raw(str, timings, &timings_cnt, &duty_cycle, &frequency)) { - infrared_send_raw_ext(timings, timings_cnt, true, frequency, duty_cycle); + bool success = infrared_cli_parse_message(str, signal) || infrared_cli_parse_raw(str, signal); + if(success) { + infrared_signal_transmit(signal); } else { printf("Wrong arguments.\r\n"); infrared_cli_print_usage(); } - free(timings); + infrared_signal_free(signal); } static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) { UNUSED(context); if(furi_hal_infrared_is_busy()) { - printf("INFRARED is busy. Exit."); + printf("INFRARED is busy. Exiting."); return; } @@ -189,8 +189,7 @@ static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) { infrared_cli_print_usage(); } } - -extern "C" void infrared_on_system_start() { +void infrared_on_system_start() { #ifdef SRV_CLI Cli* cli = (Cli*)furi_record_open("cli"); cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL); diff --git a/applications/infrared/infrared_custom_event.h b/applications/infrared/infrared_custom_event.h new file mode 100644 index 000000000..d991afb9c --- /dev/null +++ b/applications/infrared/infrared_custom_event.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +enum InfraredCustomEventType { + // Reserve first 100 events for button types and indexes, starting from 0 + InfraredCustomEventTypeReserved = 100, + InfraredCustomEventTypeMenuSelected, + InfraredCustomEventTypeTransmitStarted, + InfraredCustomEventTypeTransmitStopped, + InfraredCustomEventTypeSignalReceived, + InfraredCustomEventTypeTextEditDone, + InfraredCustomEventTypePopupTimeout, + InfraredCustomEventTypeButtonSelected, + InfraredCustomEventTypeBackPressed, +}; + +#pragma pack(push, 1) +typedef union { + uint32_t packed_value; + struct { + uint16_t type; + int16_t value; + } content; +} InfraredCustomEvent; +#pragma pack(pop) + +static inline uint32_t infrared_custom_event_pack(uint16_t type, int16_t value) { + InfraredCustomEvent event = {.content = {.type = type, .value = value}}; + return event.packed_value; +} + +static inline void + infrared_custom_event_unpack(uint32_t packed_value, uint16_t* type, int16_t* value) { + InfraredCustomEvent event = {.packed_value = packed_value}; + if(type) *type = event.content.type; + if(value) *value = event.content.value; +} + +static inline uint16_t infrared_custom_event_get_type(uint32_t packed_value) { + uint16_t type; + infrared_custom_event_unpack(packed_value, &type, NULL); + return type; +} + +static inline int16_t infrared_custom_event_get_value(uint32_t packed_value) { + int16_t value; + infrared_custom_event_unpack(packed_value, NULL, &value); + return value; +} diff --git a/applications/infrared/infrared_i.h b/applications/infrared/infrared_i.h new file mode 100644 index 000000000..06c6a5f7e --- /dev/null +++ b/applications/infrared/infrared_i.h @@ -0,0 +1,132 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include "infrared.h" +#include "infrared_remote.h" +#include "infrared_brute_force.h" +#include "infrared_custom_event.h" + +#include "scenes/infrared_scene.h" +#include "views/infrared_progress_view.h" +#include "views/infrared_debug_view.h" + +#define INFRARED_FILE_NAME_SIZE 100 +#define INFRARED_TEXT_STORE_NUM 2 +#define INFRARED_TEXT_STORE_SIZE 128 + +#define INFRARED_MAX_BUTTON_NAME_LENGTH 22 +#define INFRARED_MAX_REMOTE_NAME_LENGTH 22 + +#define INFRARED_APP_FOLDER "/any/infrared" +#define INFRARED_APP_EXTENSION ".ir" + +#define INFRARED_DEFAULT_REMOTE_NAME "Remote" + +typedef enum { + InfraredButtonIndexNone = -1, +} InfraredButtonIndex; + +typedef enum { + InfraredEditTargetNone, + InfraredEditTargetRemote, + InfraredEditTargetButton, +} InfraredEditTarget; + +typedef enum { + InfraredEditModeNone, + InfraredEditModeRename, + InfraredEditModeDelete, +} InfraredEditMode; + +typedef struct { + bool is_learning_new_remote; + bool is_debug_enabled; + InfraredEditTarget edit_target : 8; + InfraredEditMode edit_mode : 8; + int32_t current_button_index; +} InfraredAppState; + +struct Infrared { + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + + Gui* gui; + Storage* storage; + DialogsApp* dialogs; + NotificationApp* notifications; + InfraredWorker* worker; + InfraredRemote* remote; + InfraredSignal* received_signal; + InfraredBruteForce* brute_force; + + Submenu* submenu; + TextInput* text_input; + DialogEx* dialog_ex; + ButtonMenu* button_menu; + Popup* popup; + + ViewStack* view_stack; + InfraredDebugView* debug_view; + + ButtonPanel* button_panel; + Loading* loading; + InfraredProgressView* progress; + + string_t file_path; + char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1]; + InfraredAppState app_state; +}; + +typedef enum { + InfraredViewSubmenu, + InfraredViewTextInput, + InfraredViewDialogEx, + InfraredViewButtonMenu, + InfraredViewPopup, + InfraredViewStack, + InfraredViewDebugView, +} InfraredView; + +typedef enum { + InfraredNotificationMessageSuccess, + InfraredNotificationMessageGreenOn, + InfraredNotificationMessageGreenOff, + InfraredNotificationMessageBlinkRead, + InfraredNotificationMessageBlinkSend, +} InfraredNotificationMessage; + +bool infrared_add_remote_with_button(Infrared* infrared, const char* name, InfraredSignal* signal); +bool infrared_rename_current_remote(Infrared* infrared, const char* name); +void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal); +void infrared_tx_start_button_index(Infrared* infrared, size_t button_index); +void infrared_tx_start_received(Infrared* infrared); +void infrared_tx_stop(Infrared* infrared); +void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...); +void infrared_text_store_clear(Infrared* infrared, uint32_t bank); +void infrared_play_notification_message(Infrared* infrared, uint32_t message); +void infrared_show_loading_popup(Infrared* infrared, bool show); + +void infrared_signal_sent_callback(void* context); +void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal); +void infrared_text_input_callback(void* context); +void infrared_popup_timeout_callback(void* context); diff --git a/applications/infrared/infrared_remote.c b/applications/infrared/infrared_remote.c new file mode 100644 index 000000000..ad75efe7f --- /dev/null +++ b/applications/infrared/infrared_remote.c @@ -0,0 +1,176 @@ +#include "infrared_remote.h" + +#include +#include +#include +#include +#include +#include + +#define TAG "InfraredRemote" + +ARRAY_DEF(InfraredButtonArray, InfraredRemoteButton*, M_PTR_OPLIST); + +struct InfraredRemote { + InfraredButtonArray_t buttons; + string_t name; + string_t path; +}; + +static void infrared_remote_clear_buttons(InfraredRemote* remote) { + InfraredButtonArray_it_t it; + for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it); + InfraredButtonArray_next(it)) { + infrared_remote_button_free(*InfraredButtonArray_cref(it)); + } + InfraredButtonArray_reset(remote->buttons); +} + +InfraredRemote* infrared_remote_alloc() { + InfraredRemote* remote = malloc(sizeof(InfraredRemote)); + InfraredButtonArray_init(remote->buttons); + string_init(remote->name); + string_init(remote->path); + return remote; +} + +void infrared_remote_free(InfraredRemote* remote) { + infrared_remote_clear_buttons(remote); + InfraredButtonArray_clear(remote->buttons); + string_clear(remote->path); + string_clear(remote->name); + free(remote); +} + +void infrared_remote_reset(InfraredRemote* remote) { + infrared_remote_clear_buttons(remote); + string_reset(remote->name); + string_reset(remote->path); +} + +void infrared_remote_set_name(InfraredRemote* remote, const char* name) { + string_set_str(remote->name, name); +} + +const char* infrared_remote_get_name(InfraredRemote* remote) { + return string_get_cstr(remote->name); +} + +void infrared_remote_set_path(InfraredRemote* remote, const char* path) { + string_set_str(remote->path, path); +} + +const char* infrared_remote_get_path(InfraredRemote* remote) { + return string_get_cstr(remote->path); +} + +size_t infrared_remote_get_button_count(InfraredRemote* remote) { + return InfraredButtonArray_size(remote->buttons); +} + +InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index) { + furi_assert(index < InfraredButtonArray_size(remote->buttons)); + return *InfraredButtonArray_get(remote->buttons, index); +} + +bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal) { + InfraredRemoteButton* button = infrared_remote_button_alloc(); + infrared_remote_button_set_name(button, name); + infrared_remote_button_set_signal(button, signal); + InfraredButtonArray_push_back(remote->buttons, button); + return infrared_remote_store(remote); +} + +bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index) { + furi_assert(index < InfraredButtonArray_size(remote->buttons)); + InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, index); + infrared_remote_button_set_name(button, new_name); + return infrared_remote_store(remote); +} + +bool infrared_remote_delete_button(InfraredRemote* remote, size_t index) { + furi_assert(index < InfraredButtonArray_size(remote->buttons)); + InfraredRemoteButton* button; + InfraredButtonArray_pop_at(&button, remote->buttons, index); + infrared_remote_button_free(button); + return infrared_remote_store(remote); +} + +bool infrared_remote_store(InfraredRemote* remote) { + Storage* storage = furi_record_open("storage"); + FlipperFormat* ff = flipper_format_file_alloc(storage); + const char* path = string_get_cstr(remote->path); + + FURI_LOG_I(TAG, "store file: \'%s\'", path); + + bool success = flipper_format_file_open_always(ff, path) && + flipper_format_write_header_cstr(ff, "IR signals file", 1); + if(success) { + InfraredButtonArray_it_t it; + for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it); + InfraredButtonArray_next(it)) { + InfraredRemoteButton* button = *InfraredButtonArray_cref(it); + success = infrared_signal_save( + infrared_remote_button_get_signal(button), + ff, + infrared_remote_button_get_name(button)); + if(!success) { + break; + } + } + } + + flipper_format_free(ff); + furi_record_close("storage"); + return success; +} + +bool infrared_remote_load(InfraredRemote* remote, string_t path) { + Storage* storage = furi_record_open("storage"); + FlipperFormat* ff = flipper_format_file_alloc(storage); + + string_t buf; + string_init(buf); + + FURI_LOG_I(TAG, "load file: \'%s\'", string_get_cstr(path)); + bool success = flipper_format_file_open_existing(ff, string_get_cstr(path)); + + if(success) { + uint32_t version; + success = flipper_format_read_header(ff, buf, &version) && + !string_cmp_str(buf, "IR signals file") && (version == 1); + } + + if(success) { + path_extract_filename(path, buf, true); + infrared_remote_clear_buttons(remote); + infrared_remote_set_name(remote, string_get_cstr(buf)); + infrared_remote_set_path(remote, string_get_cstr(path)); + + for(bool can_read = true; can_read;) { + InfraredRemoteButton* button = infrared_remote_button_alloc(); + can_read = infrared_signal_read(infrared_remote_button_get_signal(button), ff, buf); + if(can_read) { + infrared_remote_button_set_name(button, string_get_cstr(buf)); + InfraredButtonArray_push_back(remote->buttons, button); + } else { + infrared_remote_button_free(button); + } + } + } + + string_clear(buf); + flipper_format_free(ff); + furi_record_close("storage"); + return success; +} + +bool infrared_remote_remove(InfraredRemote* remote) { + Storage* storage = furi_record_open("storage"); + + FS_Error status = storage_common_remove(storage, string_get_cstr(remote->path)); + infrared_remote_reset(remote); + + furi_record_close("storage"); + return (status == FSE_OK || status == FSE_NOT_EXIST); +} diff --git a/applications/infrared/infrared_remote.h b/applications/infrared/infrared_remote.h new file mode 100644 index 000000000..1336383f2 --- /dev/null +++ b/applications/infrared/infrared_remote.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "infrared_remote_button.h" + +typedef struct InfraredRemote InfraredRemote; + +InfraredRemote* infrared_remote_alloc(); +void infrared_remote_free(InfraredRemote* remote); +void infrared_remote_reset(InfraredRemote* remote); + +void infrared_remote_set_name(InfraredRemote* remote, const char* name); +const char* infrared_remote_get_name(InfraredRemote* remote); + +void infrared_remote_set_path(InfraredRemote* remote, const char* path); +const char* infrared_remote_get_path(InfraredRemote* remote); + +size_t infrared_remote_get_button_count(InfraredRemote* remote); +InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index); + +bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal); +bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index); +bool infrared_remote_delete_button(InfraredRemote* remote, size_t index); + +bool infrared_remote_store(InfraredRemote* remote); +bool infrared_remote_load(InfraredRemote* remote, string_t path); +bool infrared_remote_remove(InfraredRemote* remote); diff --git a/applications/infrared/infrared_remote_button.c b/applications/infrared/infrared_remote_button.c new file mode 100644 index 000000000..7525ce48f --- /dev/null +++ b/applications/infrared/infrared_remote_button.c @@ -0,0 +1,38 @@ +#include "infrared_remote_button.h" + +#include +#include + +struct InfraredRemoteButton { + string_t name; + InfraredSignal* signal; +}; + +InfraredRemoteButton* infrared_remote_button_alloc() { + InfraredRemoteButton* button = malloc(sizeof(InfraredRemoteButton)); + string_init(button->name); + button->signal = infrared_signal_alloc(); + return button; +} + +void infrared_remote_button_free(InfraredRemoteButton* button) { + string_clear(button->name); + infrared_signal_free(button->signal); + free(button); +} + +void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name) { + string_set_str(button->name, name); +} + +const char* infrared_remote_button_get_name(InfraredRemoteButton* button) { + return string_get_cstr(button->name); +} + +void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal) { + infrared_signal_set_signal(button->signal, signal); +} + +InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button) { + return button->signal; +} diff --git a/applications/infrared/infrared_remote_button.h b/applications/infrared/infrared_remote_button.h new file mode 100644 index 000000000..f25b759b5 --- /dev/null +++ b/applications/infrared/infrared_remote_button.h @@ -0,0 +1,14 @@ +#pragma once + +#include "infrared_signal.h" + +typedef struct InfraredRemoteButton InfraredRemoteButton; + +InfraredRemoteButton* infrared_remote_button_alloc(); +void infrared_remote_button_free(InfraredRemoteButton* button); + +void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name); +const char* infrared_remote_button_get_name(InfraredRemoteButton* button); + +void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal); +InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button); diff --git a/applications/infrared/infrared_runner.cpp b/applications/infrared/infrared_runner.cpp deleted file mode 100644 index 650b0fcd9..000000000 --- a/applications/infrared/infrared_runner.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "infrared_app.h" - -extern "C" int32_t infrared_app(void* p) { - InfraredApp* app = new InfraredApp(); - int32_t result = app->run(p); - delete app; - - return result; -} diff --git a/applications/infrared/infrared_signal.c b/applications/infrared/infrared_signal.c new file mode 100644 index 000000000..79eafb308 --- /dev/null +++ b/applications/infrared/infrared_signal.c @@ -0,0 +1,264 @@ +#include "infrared_signal.h" + +#include +#include +#include +#include +#include + +#define TAG "InfraredSignal" + +struct InfraredSignal { + bool is_raw; + union { + InfraredMessage message; + InfraredRawSignal raw; + } payload; +}; + +static void infrared_signal_clear_timings(InfraredSignal* signal) { + if(signal->is_raw) { + free(signal->payload.raw.timings); + signal->payload.raw.timings_size = 0; + signal->payload.raw.timings = NULL; + } +} + +static bool infrared_signal_is_message_valid(InfraredMessage* message) { + if(!infrared_is_protocol_valid(message->protocol)) { + FURI_LOG_E(TAG, "Unknown protocol"); + return false; + } + + uint32_t address_length = infrared_get_protocol_address_length(message->protocol); + uint32_t address_mask = (1UL << address_length) - 1; + + if(message->address != (message->address & address_mask)) { + FURI_LOG_E( + TAG, + "Address is out of range (mask 0x%08lX): 0x%lX\r\n", + address_mask, + message->address); + return false; + } + + uint32_t command_length = infrared_get_protocol_command_length(message->protocol); + uint32_t command_mask = (1UL << command_length) - 1; + + if(message->command != (message->command & command_mask)) { + FURI_LOG_E( + TAG, + "Command is out of range (mask 0x%08lX): 0x%lX\r\n", + command_mask, + message->command); + return false; + } + + return true; +} + +static bool infrared_signal_is_raw_valid(InfraredRawSignal* raw) { + if((raw->frequency > INFRARED_MAX_FREQUENCY) || (raw->frequency < INFRARED_MIN_FREQUENCY)) { + FURI_LOG_E( + TAG, + "Frequency is out of range (%lX - %lX): %lX", + INFRARED_MIN_FREQUENCY, + INFRARED_MAX_FREQUENCY, + raw->frequency); + return false; + + } else if((raw->duty_cycle <= 0) || (raw->duty_cycle > 1)) { + FURI_LOG_E(TAG, "Duty cycle is out of range (0 - 1): %f", (double)raw->duty_cycle); + return false; + + } else if((raw->timings_size <= 0) || (raw->timings_size > MAX_TIMINGS_AMOUNT)) { + FURI_LOG_E( + TAG, + "Timings amount is out of range (0 - %lX): %lX", + MAX_TIMINGS_AMOUNT, + raw->timings_size); + return false; + } + + return true; +} + +static inline bool infrared_signal_save_message(InfraredMessage* message, FlipperFormat* ff) { + const char* protocol_name = infrared_get_protocol_name(message->protocol); + return flipper_format_write_string_cstr(ff, "type", "parsed") && + flipper_format_write_string_cstr(ff, "protocol", protocol_name) && + flipper_format_write_hex(ff, "address", (uint8_t*)&message->address, 4) && + flipper_format_write_hex(ff, "command", (uint8_t*)&message->command, 4); +} + +static inline bool infrared_signal_save_raw(InfraredRawSignal* raw, FlipperFormat* ff) { + furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT); + return flipper_format_write_string_cstr(ff, "type", "raw") && + flipper_format_write_uint32(ff, "frequency", &raw->frequency, 1) && + flipper_format_write_float(ff, "duty_cycle", &raw->duty_cycle, 1) && + flipper_format_write_uint32(ff, "data", raw->timings, raw->timings_size); +} + +static inline bool infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) { + string_t buf; + string_init(buf); + bool success = false; + + do { + if(!flipper_format_read_string(ff, "protocol", buf)) break; + + InfraredMessage message; + message.protocol = infrared_get_protocol_by_name(string_get_cstr(buf)); + + success = flipper_format_read_hex(ff, "address", (uint8_t*)&message.address, 4) && + flipper_format_read_hex(ff, "command", (uint8_t*)&message.command, 4) && + infrared_signal_is_message_valid(&message); + + if(!success) break; + + infrared_signal_set_message(signal, &message); + } while(0); + + string_clear(buf); + return success; +} + +static inline bool infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) { + uint32_t timings_size, frequency; + float duty_cycle; + + bool success = flipper_format_read_uint32(ff, "frequency", &frequency, 1) && + flipper_format_read_float(ff, "duty_cycle", &duty_cycle, 1) && + flipper_format_get_value_count(ff, "data", &timings_size); + + if(!success || timings_size > MAX_TIMINGS_AMOUNT) { + return false; + } + + uint32_t* timings = malloc(sizeof(uint32_t) * timings_size); + success = flipper_format_read_uint32(ff, "data", timings, timings_size); + + if(success) { + infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle); + } + + free(timings); + return success; +} + +InfraredSignal* infrared_signal_alloc() { + InfraredSignal* signal = malloc(sizeof(InfraredSignal)); + + signal->is_raw = false; + signal->payload.message.protocol = InfraredProtocolUnknown; + + return signal; +} + +void infrared_signal_free(InfraredSignal* signal) { + infrared_signal_clear_timings(signal); + free(signal); +} + +bool infrared_signal_is_raw(InfraredSignal* signal) { + return signal->is_raw; +} + +bool infrared_signal_is_valid(InfraredSignal* signal) { + return signal->is_raw ? infrared_signal_is_raw_valid(&signal->payload.raw) : + infrared_signal_is_message_valid(&signal->payload.message); +} + +void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other) { + if(other->is_raw) { + const InfraredRawSignal* raw = &other->payload.raw; + infrared_signal_set_raw_signal( + signal, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle); + } else { + const InfraredMessage* message = &other->payload.message; + infrared_signal_set_message(signal, message); + } +} + +void infrared_signal_set_raw_signal( + InfraredSignal* signal, + const uint32_t* timings, + size_t timings_size, + uint32_t frequency, + float duty_cycle) { + infrared_signal_clear_timings(signal); + + signal->is_raw = true; + + signal->payload.raw.timings_size = timings_size; + signal->payload.raw.frequency = frequency; + signal->payload.raw.duty_cycle = duty_cycle; + + signal->payload.raw.timings = malloc(timings_size * sizeof(uint32_t)); + memcpy(signal->payload.raw.timings, timings, timings_size * sizeof(uint32_t)); +} + +InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal) { + furi_assert(signal->is_raw); + return &signal->payload.raw; +} + +void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message) { + infrared_signal_clear_timings(signal); + + signal->is_raw = false; + signal->payload.message = *message; +} + +InfraredMessage* infrared_signal_get_message(InfraredSignal* signal) { + furi_assert(!signal->is_raw); + return &signal->payload.message; +} + +bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name) { + if(!flipper_format_write_comment_cstr(ff, "") || + !flipper_format_write_string_cstr(ff, "name", name)) { + return false; + } else if(signal->is_raw) { + return infrared_signal_save_raw(&signal->payload.raw, ff); + } else { + return infrared_signal_save_message(&signal->payload.message, ff); + } +} + +bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, string_t name) { + string_t buf; + string_init(buf); + bool success = false; + + do { + if(!flipper_format_read_string(ff, "name", buf)) break; + string_set(name, buf); + if(!flipper_format_read_string(ff, "type", buf)) break; + if(!string_cmp_str(buf, "raw")) { + success = infrared_signal_read_raw(signal, ff); + } else if(!string_cmp_str(buf, "parsed")) { + success = infrared_signal_read_message(signal, ff); + } else { + FURI_LOG_E(TAG, "Unknown type of signal (allowed - raw/parsed) "); + } + } while(0); + + string_clear(buf); + return success; +} + +void infrared_signal_transmit(InfraredSignal* signal) { + if(signal->is_raw) { + InfraredRawSignal* raw_signal = &signal->payload.raw; + infrared_send_raw_ext( + raw_signal->timings, + raw_signal->timings_size, + true, + raw_signal->frequency, + raw_signal->duty_cycle); + } else { + InfraredMessage* message = &signal->payload.message; + infrared_send(message, 1); + } +} diff --git a/applications/infrared/infrared_signal.h b/applications/infrared/infrared_signal.h new file mode 100644 index 000000000..2dbaa75fa --- /dev/null +++ b/applications/infrared/infrared_signal.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include + +#include +#include + +typedef struct InfraredSignal InfraredSignal; + +typedef struct { + size_t timings_size; + uint32_t* timings; + uint32_t frequency; + float duty_cycle; +} InfraredRawSignal; + +InfraredSignal* infrared_signal_alloc(); +void infrared_signal_free(InfraredSignal* signal); + +bool infrared_signal_is_raw(InfraredSignal* signal); +bool infrared_signal_is_valid(InfraredSignal* signal); + +void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other); + +void infrared_signal_set_raw_signal( + InfraredSignal* signal, + const uint32_t* timings, + size_t timings_size, + uint32_t frequency, + float duty_cycle); +InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal); + +void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message); +InfraredMessage* infrared_signal_get_message(InfraredSignal* signal); + +bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name); +bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, string_t name); + +void infrared_signal_transmit(InfraredSignal* signal); diff --git a/applications/infrared/scene/infrared_app_scene.h b/applications/infrared/scene/infrared_app_scene.h deleted file mode 100644 index 9c2e20e92..000000000 --- a/applications/infrared/scene/infrared_app_scene.h +++ /dev/null @@ -1,305 +0,0 @@ -/** - * @file infrared_app_scene.h - * Infrared: Application scenes - */ -#pragma once -#include "../infrared_app_event.h" -#include -#include "infrared.h" -#include -#include -#include "../infrared_app_brute_force.h" - -/** Anonymous class */ -class InfraredApp; - -/** Base Scene class */ -class InfraredAppScene { -public: - /** Called when enter scene */ - virtual void on_enter(InfraredApp* app) = 0; - /** Events handler callback */ - virtual bool on_event(InfraredApp* app, InfraredAppEvent* event) = 0; - /** Called when exit scene */ - virtual void on_exit(InfraredApp* app) = 0; - /** Virtual destructor of base class */ - virtual ~InfraredAppScene(){}; - -private: -}; - -/** Start scene - * Main Infrared application menu - */ -class InfraredAppSceneStart : public InfraredAppScene { -public: - /** Called when enter scene */ - void on_enter(InfraredApp* app) final; - /** Events handler callback */ - bool on_event(InfraredApp* app, InfraredAppEvent* event) final; - /** Called when exit scene */ - void on_exit(InfraredApp* app) final; - -private: - /** Save previously selected submenu index - * to highlight it when get back */ - uint32_t submenu_item_selected = 0; -}; - -/** Universal menu scene - * Scene to select universal remote - */ -class InfraredAppSceneUniversal : public InfraredAppScene { -public: - /** Called when enter scene */ - void on_enter(InfraredApp* app) final; - /** Events handler callback */ - bool on_event(InfraredApp* app, InfraredAppEvent* event) final; - /** Called when exit scene */ - void on_exit(InfraredApp* app) final; - -private: - /** Save previously selected submenu index - * to highlight it when get back */ - uint32_t submenu_item_selected = 0; -}; - -/** Learn new signal scene - * On this scene catching new IR signal performed. - */ -class InfraredAppSceneLearn : public InfraredAppScene { -public: - /** Called when enter scene */ - void on_enter(InfraredApp* app) final; - /** Events handler callback */ - bool on_event(InfraredApp* app, InfraredAppEvent* event) final; - /** Called when exit scene */ - void on_exit(InfraredApp* app) final; -}; - -/** New signal learn succeeded scene - */ -class InfraredAppSceneLearnSuccess : public InfraredAppScene { -public: - /** Called when enter scene */ - void on_enter(InfraredApp* app) final; - /** Events handler callback */ - bool on_event(InfraredApp* app, InfraredAppEvent* event) final; - /** Called when exit scene */ - void on_exit(InfraredApp* app) final; - bool button_pressed = false; -}; - -/** Scene to enter name for new button in remote - */ -class InfraredAppSceneLearnEnterName : public InfraredAppScene { -public: - /** Called when enter scene */ - void on_enter(InfraredApp* app) final; - /** Events handler callback */ - bool on_event(InfraredApp* app, InfraredAppEvent* event) final; - /** Called when exit scene */ - void on_exit(InfraredApp* app) final; -}; - -/** Scene where signal is learnt - */ -class InfraredAppSceneLearnDone : public InfraredAppScene { -public: - /** Called when enter scene */ - void on_enter(InfraredApp* app) final; - /** Events handler callback */ - bool on_event(InfraredApp* app, InfraredAppEvent* event) final; - /** Called when exit scene */ - void on_exit(InfraredApp* app) final; -}; - -/** Remote interface scene - * On this scene you can send IR signals from selected remote - */ -class InfraredAppSceneRemote : public InfraredAppScene { -public: - /** Called when enter scene */ - void on_enter(InfraredApp* app) final; - /** Events handler callback */ - bool on_event(InfraredApp* app, InfraredAppEvent* event) final; - /** Called when exit scene */ - void on_exit(InfraredApp* app) final; - -private: - /** container of button names in current remote. */ - std::vector buttons_names; - /** Save previously selected index - * to highlight it when get back */ - uint32_t buttonmenu_item_selected = 0; - /** state flag to show button is pressed. - * As long as send-signal button pressed no other button - * events are handled. */ - bool button_pressed = false; -}; - -/** List of remotes scene - * Every remote is a file, located on internal/external storage. - * Every file has same format, and same extension. - * Files are parsed as you enter 'Remote scene' and showed - * as a buttons. - */ -class InfraredAppSceneRemoteList : public InfraredAppScene { -public: - /** Called when enter scene */ - void on_enter(InfraredApp* app) final; - /** Events handler callback */ - bool on_event(InfraredApp* app, InfraredAppEvent* event) final; - /** Called when exit scene */ - void on_exit(InfraredApp* app) final; - -private: - /** Save previously selected index - * to highlight it when get back */ - uint32_t submenu_item_selected = 0; - /** Remote names to show them in submenu */ - std::vector remote_names; -}; - -class InfraredAppSceneAskBack : public InfraredAppScene { -public: - /** Called when enter scene */ - void on_enter(InfraredApp* app) final; - /** Events handler callback */ - bool on_event(InfraredApp* app, InfraredAppEvent* event) final; - /** Called when exit scene */ - void on_exit(InfraredApp* app) final; -}; - -class InfraredAppSceneEdit : public InfraredAppScene { -public: - /** Called when enter scene */ - void on_enter(InfraredApp* app) final; - /** Events handler callback */ - bool on_event(InfraredApp* app, InfraredAppEvent* event) final; - /** Called when exit scene */ - void on_exit(InfraredApp* app) final; - -private: - /** Save previously selected index - * to highlight it when get back */ - uint32_t submenu_item_selected = 0; -}; - -class InfraredAppSceneEditKeySelect : public InfraredAppScene { -public: - /** Called when enter scene */ - void on_enter(InfraredApp* app) final; - /** Events handler callback */ - bool on_event(InfraredApp* app, InfraredAppEvent* event) final; - /** Called when exit scene */ - void on_exit(InfraredApp* app) final; - -private: - /** Button names to show them in submenu */ - std::vector buttons_names; -}; - -class InfraredAppSceneEditRename : public InfraredAppScene { -public: - /** Called when enter scene */ - void on_enter(InfraredApp* app) final; - /** Events handler callback */ - bool on_event(InfraredApp* app, InfraredAppEvent* event) final; - /** Called when exit scene */ - void on_exit(InfraredApp* app) final; -}; - -class InfraredAppSceneEditDelete : public InfraredAppScene { -public: - /** Called when enter scene */ - void on_enter(InfraredApp* app) final; - /** Events handler callback */ - bool on_event(InfraredApp* app, InfraredAppEvent* event) final; - /** Called when exit scene */ - void on_exit(InfraredApp* app) final; -}; - -class InfraredAppSceneEditRenameDone : public InfraredAppScene { -public: - /** Called when enter scene */ - void on_enter(InfraredApp* app) final; - /** Events handler callback */ - bool on_event(InfraredApp* app, InfraredAppEvent* event) final; - /** Called when exit scene */ - void on_exit(InfraredApp* app) final; -}; - -class InfraredAppSceneEditDeleteDone : public InfraredAppScene { -public: - /** Called when enter scene */ - void on_enter(InfraredApp* app) final; - /** Events handler callback */ - bool on_event(InfraredApp* app, InfraredAppEvent* event) final; - /** Called when exit scene */ - void on_exit(InfraredApp* app) final; -}; - -class InfraredAppSceneUniversalCommon : public InfraredAppScene { - /** Brute force started flag */ - bool brute_force_started = false; - -protected: - /** Events handler callback */ - bool on_event(InfraredApp* app, InfraredAppEvent* event) final; - /** Called when exit scene */ - void on_exit(InfraredApp* app) final; - - /** Show popup window - * - * @param app - application instance - */ - void show_popup(InfraredApp* app, int record_amount); - - /** Hide popup window - * - * @param app - application instance - */ - void hide_popup(InfraredApp* app); - - /** Propagate progress in popup window - * - * @param app - application instance - */ - bool progress_popup(InfraredApp* app); - - /** Item selected callback - * - * @param context - context - * @param index - selected item index - */ - static void infrared_app_item_callback(void* context, uint32_t index); - - /** Brute Force instance */ - InfraredAppBruteForce brute_force; - - /** Constructor */ - InfraredAppSceneUniversalCommon(const char* filename) - : brute_force(filename) { - } - - /** Destructor */ - ~InfraredAppSceneUniversalCommon() { - } -}; - -class InfraredAppSceneUniversalTV : public InfraredAppSceneUniversalCommon { -public: - /** Called when enter scene */ - void on_enter(InfraredApp* app) final; - - /** Constructor - * Specifies path to brute force db library */ - InfraredAppSceneUniversalTV() - : InfraredAppSceneUniversalCommon("/ext/infrared/assets/tv.ir") { - } - - /** Destructor */ - ~InfraredAppSceneUniversalTV() { - } -}; diff --git a/applications/infrared/scene/infrared_app_scene_ask_back.cpp b/applications/infrared/scene/infrared_app_scene_ask_back.cpp deleted file mode 100644 index 65d7ca578..000000000 --- a/applications/infrared/scene/infrared_app_scene_ask_back.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "../infrared_app.h" -#include "gui/modules/dialog_ex.h" -#include "infrared.h" -#include "infrared/scene/infrared_app_scene.h" -#include - -static void dialog_result_callback(DialogExResult result, void* context) { - auto app = static_cast(context); - InfraredAppEvent event; - - event.type = InfraredAppEvent::Type::DialogExSelected; - event.payload.dialog_ex_result = result; - - app->get_view_manager()->send_event(&event); -} - -void InfraredAppSceneAskBack::on_enter(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - DialogEx* dialog_ex = view_manager->get_dialog_ex(); - - if(app->get_learn_new_remote()) { - dialog_ex_set_header(dialog_ex, "Exit to Infrared menu?", 64, 0, AlignCenter, AlignTop); - } else { - dialog_ex_set_header(dialog_ex, "Exit to remote menu?", 64, 0, AlignCenter, AlignTop); - } - - dialog_ex_set_text( - dialog_ex, "All unsaved data\nwill be lost", 64, 31, AlignCenter, AlignCenter); - dialog_ex_set_icon(dialog_ex, 0, 0, NULL); - dialog_ex_set_left_button_text(dialog_ex, "Exit"); - dialog_ex_set_center_button_text(dialog_ex, nullptr); - dialog_ex_set_right_button_text(dialog_ex, "Stay"); - dialog_ex_set_result_callback(dialog_ex, dialog_result_callback); - dialog_ex_set_context(dialog_ex, app); - - view_manager->switch_to(InfraredAppViewManager::ViewId::DialogEx); -} - -bool InfraredAppSceneAskBack::on_event(InfraredApp* app, InfraredAppEvent* event) { - bool consumed = false; - - if(event->type == InfraredAppEvent::Type::DialogExSelected) { - switch(event->payload.dialog_ex_result) { - case DialogExResultLeft: - consumed = true; - if(app->get_learn_new_remote()) { - app->search_and_switch_to_previous_scene({InfraredApp::Scene::Start}); - } else { - app->search_and_switch_to_previous_scene( - {InfraredApp::Scene::Edit, InfraredApp::Scene::Remote}); - } - break; - case DialogExResultCenter: - furi_assert(0); - break; - case DialogExResultRight: - app->switch_to_previous_scene(); - consumed = true; - break; - default: - break; - } - } - - if(event->type == InfraredAppEvent::Type::Back) { - consumed = true; - } - - return consumed; -} - -void InfraredAppSceneAskBack::on_exit(InfraredApp*) { -} diff --git a/applications/infrared/scene/infrared_app_scene_edit.cpp b/applications/infrared/scene/infrared_app_scene_edit.cpp deleted file mode 100644 index 5a2ae72ff..000000000 --- a/applications/infrared/scene/infrared_app_scene_edit.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "../infrared_app.h" -#include "gui/modules/submenu.h" - -typedef enum { - SubmenuIndexAddKey, - SubmenuIndexRenameKey, - SubmenuIndexDeleteKey, - SubmenuIndexRenameRemote, - SubmenuIndexDeleteRemote, -} SubmenuIndex; - -static void submenu_callback(void* context, uint32_t index) { - InfraredApp* app = static_cast(context); - InfraredAppEvent event; - - event.type = InfraredAppEvent::Type::MenuSelected; - event.payload.menu_index = index; - - app->get_view_manager()->send_event(&event); -} - -void InfraredAppSceneEdit::on_enter(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - Submenu* submenu = view_manager->get_submenu(); - - submenu_add_item(submenu, "Add Button", SubmenuIndexAddKey, submenu_callback, app); - submenu_add_item(submenu, "Rename Button", SubmenuIndexRenameKey, submenu_callback, app); - submenu_add_item(submenu, "Delete Button", SubmenuIndexDeleteKey, submenu_callback, app); - submenu_add_item(submenu, "Rename Remote", SubmenuIndexRenameRemote, 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(InfraredAppViewManager::ViewId::Submenu); -} - -bool InfraredAppSceneEdit::on_event(InfraredApp* app, InfraredAppEvent* event) { - bool consumed = false; - - if(event->type == InfraredAppEvent::Type::MenuSelected) { - submenu_item_selected = event->payload.menu_index; - switch(event->payload.menu_index) { - case SubmenuIndexAddKey: - app->set_learn_new_remote(false); - app->switch_to_next_scene(InfraredApp::Scene::Learn); - break; - case SubmenuIndexRenameKey: - app->set_edit_action(InfraredApp::EditAction::Rename); - app->set_edit_element(InfraredApp::EditElement::Button); - app->switch_to_next_scene(InfraredApp::Scene::EditKeySelect); - break; - case SubmenuIndexDeleteKey: - app->set_edit_action(InfraredApp::EditAction::Delete); - app->set_edit_element(InfraredApp::EditElement::Button); - app->switch_to_next_scene(InfraredApp::Scene::EditKeySelect); - break; - case SubmenuIndexRenameRemote: - app->set_edit_action(InfraredApp::EditAction::Rename); - app->set_edit_element(InfraredApp::EditElement::Remote); - app->switch_to_next_scene(InfraredApp::Scene::EditRename); - break; - case SubmenuIndexDeleteRemote: - app->set_edit_action(InfraredApp::EditAction::Delete); - app->set_edit_element(InfraredApp::EditElement::Remote); - app->switch_to_next_scene(InfraredApp::Scene::EditDelete); - break; - } - consumed = true; - } - - return consumed; -} - -void InfraredAppSceneEdit::on_exit(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - Submenu* submenu = view_manager->get_submenu(); - - submenu_reset(submenu); -} diff --git a/applications/infrared/scene/infrared_app_scene_edit_delete.cpp b/applications/infrared/scene/infrared_app_scene_edit_delete.cpp deleted file mode 100644 index e17cfee07..000000000 --- a/applications/infrared/scene/infrared_app_scene_edit_delete.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "../infrared_app.h" -#include "infrared.h" -#include "infrared/scene/infrared_app_scene.h" -#include - -static void dialog_result_callback(DialogExResult result, void* context) { - auto app = static_cast(context); - InfraredAppEvent event; - - event.type = InfraredAppEvent::Type::DialogExSelected; - event.payload.dialog_ex_result = result; - - app->get_view_manager()->send_event(&event); -} - -void InfraredAppSceneEditDelete::on_enter(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - DialogEx* dialog_ex = view_manager->get_dialog_ex(); - - auto remote_manager = app->get_remote_manager(); - - if(app->get_edit_element() == InfraredApp::EditElement::Button) { - auto signal = remote_manager->get_button_data(app->get_current_button()); - dialog_ex_set_header(dialog_ex, "Delete button?", 64, 0, AlignCenter, AlignTop); - if(!signal.is_raw()) { - auto message = &signal.get_message(); - app->set_text_store( - 0, - "%s\n%s\nA=0x%0*lX C=0x%0*lX", - remote_manager->get_button_name(app->get_current_button()).c_str(), - infrared_get_protocol_name(message->protocol), - ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), - message->address, - ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), - message->command); - } else { - app->set_text_store( - 0, - "%s\nRAW\n%ld samples", - remote_manager->get_button_name(app->get_current_button()).c_str(), - signal.get_raw_signal().timings_cnt); - } - } else { - dialog_ex_set_header(dialog_ex, "Delete remote?", 64, 0, AlignCenter, AlignTop); - app->set_text_store( - 0, - "%s\n with %lu buttons", - remote_manager->get_remote_name().c_str(), - remote_manager->get_number_of_buttons()); - } - - dialog_ex_set_text(dialog_ex, app->get_text_store(0), 64, 31, AlignCenter, AlignCenter); - dialog_ex_set_icon(dialog_ex, 0, 0, NULL); - dialog_ex_set_left_button_text(dialog_ex, "Cancel"); - dialog_ex_set_right_button_text(dialog_ex, "Delete"); - dialog_ex_set_result_callback(dialog_ex, dialog_result_callback); - dialog_ex_set_context(dialog_ex, app); - - view_manager->switch_to(InfraredAppViewManager::ViewId::DialogEx); -} - -bool InfraredAppSceneEditDelete::on_event(InfraredApp* app, InfraredAppEvent* event) { - bool consumed = false; - - if(event->type == InfraredAppEvent::Type::DialogExSelected) { - switch(event->payload.dialog_ex_result) { - case DialogExResultLeft: - app->switch_to_previous_scene(); - break; - case DialogExResultCenter: - furi_assert(0); - break; - case DialogExResultRight: { - auto remote_manager = app->get_remote_manager(); - bool result = false; - if(app->get_edit_element() == InfraredApp::EditElement::Remote) { - result = remote_manager->delete_remote(); - } else { - result = remote_manager->delete_button(app->get_current_button()); - app->set_current_button(InfraredApp::ButtonNA); - } - - if(!result) { - app->search_and_switch_to_previous_scene( - {InfraredApp::Scene::RemoteList, InfraredApp::Scene::Start}); - } else { - app->switch_to_next_scene(InfraredApp::Scene::EditDeleteDone); - } - break; - } - default: - break; - } - } - - return consumed; -} - -void InfraredAppSceneEditDelete::on_exit(InfraredApp*) { -} diff --git a/applications/infrared/scene/infrared_app_scene_edit_delete_done.cpp b/applications/infrared/scene/infrared_app_scene_edit_delete_done.cpp deleted file mode 100644 index 3f9800e1a..000000000 --- a/applications/infrared/scene/infrared_app_scene_edit_delete_done.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "../infrared_app.h" - -void InfraredAppSceneEditDeleteDone::on_enter(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - Popup* popup = view_manager->get_popup(); - - popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); - popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); - - popup_set_callback(popup, InfraredApp::popup_callback); - popup_set_context(popup, app); - popup_set_timeout(popup, 1500); - popup_enable_timeout(popup); - - view_manager->switch_to(InfraredAppViewManager::ViewId::Popup); -} - -bool InfraredAppSceneEditDeleteDone::on_event(InfraredApp* app, InfraredAppEvent* event) { - bool consumed = false; - - if(event->type == InfraredAppEvent::Type::PopupTimer) { - if(app->get_edit_element() == InfraredApp::EditElement::Remote) { - app->search_and_switch_to_previous_scene( - {InfraredApp::Scene::Start, InfraredApp::Scene::RemoteList}); - } else { - app->search_and_switch_to_previous_scene({InfraredApp::Scene::Remote}); - } - consumed = true; - } - - return consumed; -} - -void InfraredAppSceneEditDeleteDone::on_exit(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - Popup* popup = view_manager->get_popup(); - popup_set_header(popup, nullptr, 0, 0, AlignLeft, AlignTop); -} diff --git a/applications/infrared/scene/infrared_app_scene_edit_key_select.cpp b/applications/infrared/scene/infrared_app_scene_edit_key_select.cpp deleted file mode 100644 index 49142c212..000000000 --- a/applications/infrared/scene/infrared_app_scene_edit_key_select.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "../infrared_app.h" -#include "gui/modules/submenu.h" - -static void submenu_callback(void* context, uint32_t index) { - InfraredApp* app = static_cast(context); - InfraredAppEvent event; - - event.type = InfraredAppEvent::Type::MenuSelected; - event.payload.menu_index = index; - - app->get_view_manager()->send_event(&event); -} - -void InfraredAppSceneEditKeySelect::on_enter(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - Submenu* submenu = view_manager->get_submenu(); - int item_number = 0; - - const char* header = app->get_edit_action() == InfraredApp::EditAction::Rename ? - "Rename Button:" : - "Delete Button:"; - submenu_set_header(submenu, header); - - auto remote_manager = app->get_remote_manager(); - buttons_names = remote_manager->get_button_list(); - for(const auto& it : buttons_names) { - submenu_add_item(submenu, it.c_str(), item_number++, submenu_callback, app); - } - if((item_number > 0) && (app->get_current_button() != InfraredApp::ButtonNA)) { - submenu_set_selected_item(submenu, app->get_current_button()); - app->set_current_button(InfraredApp::ButtonNA); - } - - view_manager->switch_to(InfraredAppViewManager::ViewId::Submenu); -} - -bool InfraredAppSceneEditKeySelect::on_event(InfraredApp* app, InfraredAppEvent* event) { - bool consumed = false; - - if(event->type == InfraredAppEvent::Type::MenuSelected) { - app->set_current_button(event->payload.menu_index); - consumed = true; - if(app->get_edit_action() == InfraredApp::EditAction::Rename) { - app->switch_to_next_scene(InfraredApp::Scene::EditRename); - } else { - app->switch_to_next_scene(InfraredApp::Scene::EditDelete); - } - } - - return consumed; -} - -void InfraredAppSceneEditKeySelect::on_exit(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - Submenu* submenu = view_manager->get_submenu(); - - submenu_reset(submenu); -} diff --git a/applications/infrared/scene/infrared_app_scene_edit_rename.cpp b/applications/infrared/scene/infrared_app_scene_edit_rename.cpp deleted file mode 100644 index 761da49f3..000000000 --- a/applications/infrared/scene/infrared_app_scene_edit_rename.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "../infrared_app.h" -#include "m-string.h" -#include "toolbox/path.h" - -void InfraredAppSceneEditRename::on_enter(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - TextInput* text_input = view_manager->get_text_input(); - size_t enter_name_length = 0; - - auto remote_manager = app->get_remote_manager(); - if(app->get_edit_element() == InfraredApp::EditElement::Button) { - furi_assert(app->get_current_button() != InfraredApp::ButtonNA); - auto button_name = remote_manager->get_button_name(app->get_current_button()); - char* buffer_str = app->get_text_store(0); - size_t max_len = InfraredAppRemoteManager::max_button_name_length; - strncpy(buffer_str, button_name.c_str(), max_len); - buffer_str[max_len + 1] = 0; - enter_name_length = max_len; - text_input_set_header_text(text_input, "Name the button"); - } else { - auto remote_name = remote_manager->get_remote_name(); - strncpy(app->get_text_store(0), remote_name.c_str(), app->get_text_store_size()); - enter_name_length = InfraredAppRemoteManager::max_remote_name_length; - text_input_set_header_text(text_input, "Name the remote"); - - string_t folder_path; - string_init(folder_path); - - if(string_end_with_str_p(app->file_path, InfraredApp::infrared_extension)) { - path_extract_dirname(string_get_cstr(app->file_path), folder_path); - } - - ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - string_get_cstr(folder_path), app->infrared_extension, remote_name.c_str()); - text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); - - string_clear(folder_path); - } - - text_input_set_result_callback( - text_input, - InfraredApp::text_input_callback, - app, - app->get_text_store(0), - enter_name_length, - false); - - view_manager->switch_to(InfraredAppViewManager::ViewId::TextInput); -} - -bool InfraredAppSceneEditRename::on_event(InfraredApp* app, InfraredAppEvent* event) { - bool consumed = false; - - if(event->type == InfraredAppEvent::Type::TextEditDone) { - auto remote_manager = app->get_remote_manager(); - bool result = false; - if(app->get_edit_element() == InfraredApp::EditElement::Button) { - result = - remote_manager->rename_button(app->get_current_button(), app->get_text_store(0)); - app->set_current_button(InfraredApp::ButtonNA); - } else { - result = remote_manager->rename_remote(app->get_text_store(0)); - } - if(!result) { - app->search_and_switch_to_previous_scene( - {InfraredApp::Scene::Start, InfraredApp::Scene::RemoteList}); - } else { - app->switch_to_next_scene_without_saving(InfraredApp::Scene::EditRenameDone); - } - consumed = true; - } - - return consumed; -} - -void InfraredAppSceneEditRename::on_exit(InfraredApp* app) { - TextInput* text_input = app->get_view_manager()->get_text_input(); - - void* validator_context = text_input_get_validator_callback_context(text_input); - text_input_set_validator(text_input, NULL, NULL); - - if(validator_context != NULL) validator_is_file_free((ValidatorIsFile*)validator_context); -} diff --git a/applications/infrared/scene/infrared_app_scene_edit_rename_done.cpp b/applications/infrared/scene/infrared_app_scene_edit_rename_done.cpp deleted file mode 100644 index 803481f6b..000000000 --- a/applications/infrared/scene/infrared_app_scene_edit_rename_done.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "../infrared_app.h" - -void InfraredAppSceneEditRenameDone::on_enter(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - Popup* popup = view_manager->get_popup(); - - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); - - popup_set_callback(popup, InfraredApp::popup_callback); - popup_set_context(popup, app); - popup_set_timeout(popup, 1500); - popup_enable_timeout(popup); - - view_manager->switch_to(InfraredAppViewManager::ViewId::Popup); -} - -bool InfraredAppSceneEditRenameDone::on_event(InfraredApp* app, InfraredAppEvent* event) { - bool consumed = false; - - if(event->type == InfraredAppEvent::Type::PopupTimer) { - app->switch_to_next_scene(InfraredApp::Scene::Remote); - consumed = true; - } - - return consumed; -} - -void InfraredAppSceneEditRenameDone::on_exit(InfraredApp*) { -} diff --git a/applications/infrared/scene/infrared_app_scene_learn.cpp b/applications/infrared/scene/infrared_app_scene_learn.cpp deleted file mode 100644 index 3ad152cff..000000000 --- a/applications/infrared/scene/infrared_app_scene_learn.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "../infrared_app.h" -#include "../infrared_app_event.h" -#include "infrared.h" -#include - -static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { - furi_assert(context); - furi_assert(received_signal); - - InfraredApp* app = static_cast(context); - - if(infrared_worker_signal_is_decoded(received_signal)) { - InfraredAppSignal signal(infrared_worker_get_decoded_signal(received_signal)); - app->set_received_signal(signal); - } else { - const uint32_t* timings; - size_t timings_cnt; - infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt); - InfraredAppSignal signal( - timings, timings_cnt, INFRARED_COMMON_CARRIER_FREQUENCY, INFRARED_COMMON_DUTY_CYCLE); - app->set_received_signal(signal); - } - - infrared_worker_rx_set_received_signal_callback(app->get_infrared_worker(), NULL, NULL); - InfraredAppEvent event; - event.type = InfraredAppEvent::Type::InfraredMessageReceived; - auto view_manager = app->get_view_manager(); - view_manager->send_event(&event); -} - -void InfraredAppSceneLearn::on_enter(InfraredApp* app) { - auto view_manager = app->get_view_manager(); - auto popup = view_manager->get_popup(); - - auto worker = app->get_infrared_worker(); - infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, app); - infrared_worker_rx_start(worker); - - popup_set_icon(popup, 0, 32, &I_InfraredLearnShort_128x31); - popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignCenter); - popup_set_text( - popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter); - popup_set_callback(popup, NULL); - - view_manager->switch_to(InfraredAppViewManager::ViewId::Popup); -} - -bool InfraredAppSceneLearn::on_event(InfraredApp* app, InfraredAppEvent* event) { - bool consumed = false; - - switch(event->type) { - case InfraredAppEvent::Type::Tick: - consumed = true; - app->notify_blink_read(); - break; - case InfraredAppEvent::Type::InfraredMessageReceived: - app->notify_success(); - app->switch_to_next_scene_without_saving(InfraredApp::Scene::LearnSuccess); - break; - case InfraredAppEvent::Type::Back: - consumed = true; - app->switch_to_previous_scene(); - break; - default: - furi_assert(0); - } - - return consumed; -} - -void InfraredAppSceneLearn::on_exit(InfraredApp* app) { - infrared_worker_rx_stop(app->get_infrared_worker()); - auto view_manager = app->get_view_manager(); - auto popup = view_manager->get_popup(); - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignCenter); -} diff --git a/applications/infrared/scene/infrared_app_scene_learn_done.cpp b/applications/infrared/scene/infrared_app_scene_learn_done.cpp deleted file mode 100644 index 6ee13b432..000000000 --- a/applications/infrared/scene/infrared_app_scene_learn_done.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "../infrared_app.h" -#include - -void InfraredAppSceneLearnDone::on_enter(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - Popup* popup = view_manager->get_popup(); - - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - DOLPHIN_DEED(DolphinDeedIrSave); - - if(app->get_learn_new_remote()) { - popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop); - } else { - popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); - } - - popup_set_callback(popup, InfraredApp::popup_callback); - popup_set_context(popup, app); - popup_set_timeout(popup, 1500); - popup_enable_timeout(popup); - - view_manager->switch_to(InfraredAppViewManager::ViewId::Popup); -} - -bool InfraredAppSceneLearnDone::on_event(InfraredApp* app, InfraredAppEvent* event) { - bool consumed = false; - - if(event->type == InfraredAppEvent::Type::PopupTimer) { - app->switch_to_next_scene(InfraredApp::Scene::Remote); - consumed = true; - } - - return consumed; -} - -void InfraredAppSceneLearnDone::on_exit(InfraredApp* app) { - app->set_learn_new_remote(false); - InfraredAppViewManager* view_manager = app->get_view_manager(); - Popup* popup = view_manager->get_popup(); - popup_set_header(popup, nullptr, 0, 0, AlignLeft, AlignTop); -} diff --git a/applications/infrared/scene/infrared_app_scene_learn_enter_name.cpp b/applications/infrared/scene/infrared_app_scene_learn_enter_name.cpp deleted file mode 100644 index 7d15a7c56..000000000 --- a/applications/infrared/scene/infrared_app_scene_learn_enter_name.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "../infrared_app.h" -#include "gui/modules/text_input.h" - -void InfraredAppSceneLearnEnterName::on_enter(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - TextInput* text_input = view_manager->get_text_input(); - - auto signal = app->get_received_signal(); - - if(!signal.is_raw()) { - auto message = &signal.get_message(); - app->set_text_store( - 0, - "%.4s_%0*lX", - infrared_get_protocol_name(message->protocol), - ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), - message->command); - } else { - auto raw_signal = signal.get_raw_signal(); - app->set_text_store(0, "RAW_%d", raw_signal.timings_cnt); - } - - text_input_set_header_text(text_input, "Name the button"); - text_input_set_result_callback( - text_input, - InfraredApp::text_input_callback, - app, - app->get_text_store(0), - InfraredAppRemoteManager::max_button_name_length, - true); - - view_manager->switch_to(InfraredAppViewManager::ViewId::TextInput); -} - -bool InfraredAppSceneLearnEnterName::on_event(InfraredApp* app, InfraredAppEvent* event) { - bool consumed = false; - - if(event->type == InfraredAppEvent::Type::TextEditDone) { - auto remote_manager = app->get_remote_manager(); - bool result = false; - if(app->get_learn_new_remote()) { - result = remote_manager->add_remote_with_button( - app->get_text_store(0), app->get_received_signal()); - } else { - result = - remote_manager->add_button(app->get_text_store(0), app->get_received_signal()); - } - - if(!result) { - app->search_and_switch_to_previous_scene( - {InfraredApp::Scene::Start, InfraredApp::Scene::RemoteList}); - } else { - app->switch_to_next_scene_without_saving(InfraredApp::Scene::LearnDone); - } - } - return consumed; -} - -void InfraredAppSceneLearnEnterName::on_exit(InfraredApp*) { -} diff --git a/applications/infrared/scene/infrared_app_scene_learn_success.cpp b/applications/infrared/scene/infrared_app_scene_learn_success.cpp deleted file mode 100644 index a7f7c4cd7..000000000 --- a/applications/infrared/scene/infrared_app_scene_learn_success.cpp +++ /dev/null @@ -1,142 +0,0 @@ -#include -#include -#include - -#include "../infrared_app.h" -#include "infrared.h" - -static void dialog_result_callback(DialogExResult result, void* context) { - auto app = static_cast(context); - InfraredAppEvent event; - - event.type = InfraredAppEvent::Type::DialogExSelected; - event.payload.dialog_ex_result = result; - - app->get_view_manager()->send_event(&event); -} - -void InfraredAppSceneLearnSuccess::on_enter(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - DialogEx* dialog_ex = view_manager->get_dialog_ex(); - - DOLPHIN_DEED(DolphinDeedIrLearnSuccess); - app->notify_green_on(); - - infrared_worker_tx_set_get_signal_callback( - app->get_infrared_worker(), infrared_worker_tx_get_signal_steady_callback, app); - infrared_worker_tx_set_signal_sent_callback( - app->get_infrared_worker(), InfraredApp::signal_sent_callback, app); - - auto signal = app->get_received_signal(); - - if(!signal.is_raw()) { - auto message = &signal.get_message(); - uint8_t adr_digits = - ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4); - uint8_t cmd_digits = - ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4); - uint8_t max_digits = MAX(adr_digits, cmd_digits); - max_digits = MIN(max_digits, 7); - size_t label_x_offset = 63 + (7 - max_digits) * 3; - - app->set_text_store(0, "%s", infrared_get_protocol_name(message->protocol)); - app->set_text_store( - 1, - "A: 0x%0*lX\nC: 0x%0*lX\n", - adr_digits, - message->address, - cmd_digits, - message->command); - - dialog_ex_set_header(dialog_ex, app->get_text_store(0), 95, 7, AlignCenter, AlignCenter); - dialog_ex_set_text( - dialog_ex, app->get_text_store(1), label_x_offset, 34, AlignLeft, AlignCenter); - } else { - dialog_ex_set_header(dialog_ex, "Unknown", 95, 10, AlignCenter, AlignCenter); - app->set_text_store(0, "%d samples", signal.get_raw_signal().timings_cnt); - dialog_ex_set_text(dialog_ex, app->get_text_store(0), 75, 23, AlignLeft, AlignTop); - } - - dialog_ex_set_left_button_text(dialog_ex, "Retry"); - 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_DolphinReadingSuccess_59x63); - dialog_ex_set_result_callback(dialog_ex, dialog_result_callback); - dialog_ex_set_context(dialog_ex, app); - dialog_ex_enable_extended_events(dialog_ex); - - view_manager->switch_to(InfraredAppViewManager::ViewId::DialogEx); -} - -bool InfraredAppSceneLearnSuccess::on_event(InfraredApp* app, InfraredAppEvent* event) { - bool consumed = false; - if(event->type == InfraredAppEvent::Type::Tick) { - /* Send event every tick to suppress any switching off green light */ - if(!button_pressed) { - app->notify_green_on(); - } - } - - if(event->type == InfraredAppEvent::Type::DialogExSelected) { - switch(event->payload.dialog_ex_result) { - case DialogExResultLeft: - consumed = true; - if(!button_pressed) { - app->switch_to_next_scene_without_saving(InfraredApp::Scene::Learn); - } - break; - case DialogExResultRight: { - consumed = true; - if(!button_pressed) { - app->switch_to_next_scene(InfraredApp::Scene::LearnEnterName); - } - break; - } - case DialogExPressCenter: - if(!button_pressed) { - button_pressed = true; - - auto signal = app->get_received_signal(); - if(signal.is_raw()) { - infrared_worker_set_raw_signal( - app->get_infrared_worker(), - signal.get_raw_signal().timings, - signal.get_raw_signal().timings_cnt); - } else { - infrared_worker_set_decoded_signal( - app->get_infrared_worker(), &signal.get_message()); - } - - infrared_worker_tx_start(app->get_infrared_worker()); - } - break; - case DialogExReleaseCenter: - if(button_pressed) { - button_pressed = false; - infrared_worker_tx_stop(app->get_infrared_worker()); - app->notify_green_off(); - } - break; - default: - break; - } - } - - if(event->type == InfraredAppEvent::Type::Back) { - if(!button_pressed) { - app->switch_to_next_scene(InfraredApp::Scene::AskBack); - } - consumed = true; - } - - return consumed; -} - -void InfraredAppSceneLearnSuccess::on_exit(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - DialogEx* dialog_ex = view_manager->get_dialog_ex(); - dialog_ex_reset(dialog_ex); - app->notify_green_off(); - infrared_worker_tx_set_get_signal_callback(app->get_infrared_worker(), nullptr, nullptr); - infrared_worker_tx_set_signal_sent_callback(app->get_infrared_worker(), nullptr, nullptr); -} diff --git a/applications/infrared/scene/infrared_app_scene_remote.cpp b/applications/infrared/scene/infrared_app_scene_remote.cpp deleted file mode 100644 index ac87b5814..000000000 --- a/applications/infrared/scene/infrared_app_scene_remote.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include -#include -#include -#include -#include "../infrared_app.h" -#include "../infrared_app_view_manager.h" - -typedef enum { - ButtonIndexPlus = -2, - ButtonIndexEdit = -1, - ButtonIndexNA = 0, -} ButtonIndex; - -static void button_menu_callback(void* context, int32_t index, InputType type) { - InfraredApp* app = static_cast(context); - InfraredAppEvent event; - - if(type == InputTypePress) { - event.type = InfraredAppEvent::Type::MenuSelectedPress; - } else if(type == InputTypeRelease) { - event.type = InfraredAppEvent::Type::MenuSelectedRelease; - } else if(type == InputTypeShort) { - event.type = InfraredAppEvent::Type::MenuSelected; - } else { - furi_assert(0); - } - - event.payload.menu_index = index; - - app->get_view_manager()->send_event(&event); -} - -void InfraredAppSceneRemote::on_enter(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - ButtonMenu* button_menu = view_manager->get_button_menu(); - auto remote_manager = app->get_remote_manager(); - int i = 0; - button_pressed = false; - - infrared_worker_tx_set_get_signal_callback( - app->get_infrared_worker(), infrared_worker_tx_get_signal_steady_callback, app); - infrared_worker_tx_set_signal_sent_callback( - app->get_infrared_worker(), InfraredApp::signal_sent_callback, app); - buttons_names = remote_manager->get_button_list(); - - i = 0; - for(auto& name : buttons_names) { - button_menu_add_item( - button_menu, name.c_str(), i++, button_menu_callback, ButtonMenuItemTypeCommon, app); - } - - button_menu_add_item( - button_menu, "+", ButtonIndexPlus, button_menu_callback, ButtonMenuItemTypeControl, app); - button_menu_add_item( - button_menu, "Edit", ButtonIndexEdit, button_menu_callback, ButtonMenuItemTypeControl, app); - - app->set_text_store(0, "%s", remote_manager->get_remote_name().c_str()); - 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(InfraredAppViewManager::ViewId::ButtonMenu); -} - -bool InfraredAppSceneRemote::on_event(InfraredApp* app, InfraredAppEvent* event) { - bool consumed = true; - - if((event->type == InfraredAppEvent::Type::MenuSelected) || - (event->type == InfraredAppEvent::Type::MenuSelectedPress) || - (event->type == InfraredAppEvent::Type::MenuSelectedRelease)) { - switch(event->payload.menu_index) { - case ButtonIndexPlus: - furi_assert(event->type == InfraredAppEvent::Type::MenuSelected); - buttonmenu_item_selected = event->payload.menu_index; - app->set_learn_new_remote(false); - app->switch_to_next_scene(InfraredApp::Scene::Learn); - break; - case ButtonIndexEdit: - furi_assert(event->type == InfraredAppEvent::Type::MenuSelected); - buttonmenu_item_selected = event->payload.menu_index; - app->switch_to_next_scene(InfraredApp::Scene::Edit); - break; - default: - furi_assert(event->type != InfraredAppEvent::Type::MenuSelected); - bool pressed = (event->type == InfraredAppEvent::Type::MenuSelectedPress); - - if(pressed && !button_pressed) { - button_pressed = true; - - auto button_signal = - app->get_remote_manager()->get_button_data(event->payload.menu_index); - if(button_signal.is_raw()) { - infrared_worker_set_raw_signal( - app->get_infrared_worker(), - button_signal.get_raw_signal().timings, - button_signal.get_raw_signal().timings_cnt); - } else { - infrared_worker_set_decoded_signal( - app->get_infrared_worker(), &button_signal.get_message()); - } - - DOLPHIN_DEED(DolphinDeedIrSend); - infrared_worker_tx_start(app->get_infrared_worker()); - } else if(!pressed && button_pressed) { - button_pressed = false; - infrared_worker_tx_stop(app->get_infrared_worker()); - app->notify_green_off(); - } - break; - } - } else if(event->type == InfraredAppEvent::Type::Back) { - if(!button_pressed) { - app->search_and_switch_to_previous_scene( - {InfraredApp::Scene::Start, InfraredApp::Scene::RemoteList}); - } - } else { - consumed = false; - } - - return consumed; -} - -void InfraredAppSceneRemote::on_exit(InfraredApp* app) { - infrared_worker_tx_set_get_signal_callback(app->get_infrared_worker(), nullptr, nullptr); - infrared_worker_tx_set_signal_sent_callback(app->get_infrared_worker(), nullptr, nullptr); - InfraredAppViewManager* view_manager = app->get_view_manager(); - ButtonMenu* button_menu = view_manager->get_button_menu(); - - button_menu_reset(button_menu); -} diff --git a/applications/infrared/scene/infrared_app_scene_remote_list.cpp b/applications/infrared/scene/infrared_app_scene_remote_list.cpp deleted file mode 100644 index c72acb6e8..000000000 --- a/applications/infrared/scene/infrared_app_scene_remote_list.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "../infrared_app.h" -#include "assets_icons.h" -#include "infrared/infrared_app_event.h" -#include - -void InfraredAppSceneRemoteList::on_enter(InfraredApp* app) { - furi_assert(app); - - bool result = false; - bool file_select_result; - auto remote_manager = app->get_remote_manager(); - DialogsApp* dialogs = app->get_dialogs(); - - InfraredAppViewManager* view_manager = app->get_view_manager(); - ButtonMenu* button_menu = view_manager->get_button_menu(); - button_menu_reset(button_menu); - view_manager->switch_to(InfraredAppViewManager::ViewId::ButtonMenu); - - file_select_result = dialog_file_browser_show( - dialogs, - app->file_path, - app->file_path, - InfraredApp::infrared_extension, - true, - &I_ir_10px, - true); - - if(file_select_result) { - if(remote_manager->load(app->file_path)) { - app->switch_to_next_scene(InfraredApp::Scene::Remote); - result = true; - } - } - - if(!result) { - app->switch_to_previous_scene(); - } -} - -bool InfraredAppSceneRemoteList::on_event(InfraredApp*, InfraredAppEvent*) { - return false; -} - -void InfraredAppSceneRemoteList::on_exit(InfraredApp*) { -} diff --git a/applications/infrared/scene/infrared_app_scene_start.cpp b/applications/infrared/scene/infrared_app_scene_start.cpp deleted file mode 100644 index 5efdce7a0..000000000 --- a/applications/infrared/scene/infrared_app_scene_start.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "../infrared_app.h" - -typedef enum { - SubmenuIndexUniversalLibrary, - SubmenuIndexLearnNewRemote, - SubmenuIndexSavedRemotes, -} SubmenuIndex; - -static void submenu_callback(void* context, uint32_t index) { - InfraredApp* app = static_cast(context); - InfraredAppEvent event; - - event.type = InfraredAppEvent::Type::MenuSelected; - event.payload.menu_index = index; - - app->get_view_manager()->send_event(&event); -} - -void InfraredAppSceneStart::on_enter(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - Submenu* submenu = view_manager->get_submenu(); - - submenu_add_item( - submenu, "Universal Library", SubmenuIndexUniversalLibrary, submenu_callback, app); - submenu_add_item( - submenu, "Learn New Remote", SubmenuIndexLearnNewRemote, submenu_callback, app); - submenu_add_item(submenu, "Saved Remotes", SubmenuIndexSavedRemotes, submenu_callback, app); - submenu_set_selected_item(submenu, submenu_item_selected); - - string_set_str(app->file_path, InfraredApp::infrared_directory); - submenu_item_selected = 0; - - view_manager->switch_to(InfraredAppViewManager::ViewId::Submenu); -} - -bool InfraredAppSceneStart::on_event(InfraredApp* app, InfraredAppEvent* event) { - bool consumed = false; - - if(event->type == InfraredAppEvent::Type::MenuSelected) { - submenu_item_selected = event->payload.menu_index; - switch(event->payload.menu_index) { - case SubmenuIndexUniversalLibrary: - app->switch_to_next_scene(InfraredApp::Scene::Universal); - break; - case SubmenuIndexLearnNewRemote: - app->set_learn_new_remote(true); - app->switch_to_next_scene(InfraredApp::Scene::Learn); - break; - case SubmenuIndexSavedRemotes: - app->switch_to_next_scene(InfraredApp::Scene::RemoteList); - break; - default: - furi_assert(0); - break; - } - consumed = true; - } - - return consumed; -} - -void InfraredAppSceneStart::on_exit(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - Submenu* submenu = view_manager->get_submenu(); - - app->get_remote_manager()->reset_remote(); - submenu_reset(submenu); -} diff --git a/applications/infrared/scene/infrared_app_scene_universal.cpp b/applications/infrared/scene/infrared_app_scene_universal.cpp deleted file mode 100644 index 57b5653a0..000000000 --- a/applications/infrared/scene/infrared_app_scene_universal.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "../infrared_app.h" - -typedef enum { - SubmenuIndexUniversalTV, - SubmenuIndexUniversalAudio, - SubmenuIndexUniversalAirConditioner, -} SubmenuIndex; - -static void submenu_callback(void* context, uint32_t index) { - InfraredApp* app = static_cast(context); - InfraredAppEvent event; - - event.type = InfraredAppEvent::Type::MenuSelected; - event.payload.menu_index = index; - - app->get_view_manager()->send_event(&event); -} - -void InfraredAppSceneUniversal::on_enter(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - Submenu* submenu = view_manager->get_submenu(); - - submenu_add_item(submenu, "TVs", SubmenuIndexUniversalTV, submenu_callback, app); - submenu_set_selected_item(submenu, submenu_item_selected); - submenu_item_selected = 0; - - view_manager->switch_to(InfraredAppViewManager::ViewId::Submenu); -} - -bool InfraredAppSceneUniversal::on_event(InfraredApp* app, InfraredAppEvent* event) { - bool consumed = false; - - if(event->type == InfraredAppEvent::Type::MenuSelected) { - submenu_item_selected = event->payload.menu_index; - switch(event->payload.menu_index) { - case SubmenuIndexUniversalTV: - app->switch_to_next_scene(InfraredApp::Scene::UniversalTV); - break; - case SubmenuIndexUniversalAudio: - // app->switch_to_next_scene(InfraredApp::Scene::UniversalAudio); - break; - case SubmenuIndexUniversalAirConditioner: - // app->switch_to_next_scene(InfraredApp::Scene::UniversalAirConditioner); - break; - } - consumed = true; - } - - return consumed; -} - -void InfraredAppSceneUniversal::on_exit(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - Submenu* submenu = view_manager->get_submenu(); - - submenu_reset(submenu); -} diff --git a/applications/infrared/scene/infrared_app_scene_universal_common.cpp b/applications/infrared/scene/infrared_app_scene_universal_common.cpp deleted file mode 100644 index 9e7a18f40..000000000 --- a/applications/infrared/scene/infrared_app_scene_universal_common.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include -#include -#include -#include -#include - -#include "../infrared_app.h" -#include "infrared/infrared_app_event.h" -#include "infrared/infrared_app_view_manager.h" -#include "infrared/scene/infrared_app_scene.h" -#include "../view/infrared_progress_view.h" - -void InfraredAppSceneUniversalCommon::infrared_app_item_callback(void* context, uint32_t index) { - InfraredApp* app = static_cast(context); - InfraredAppEvent event; - - event.type = InfraredAppEvent::Type::ButtonPanelPressed; - event.payload.menu_index = index; - - app->get_view_manager()->send_event(&event); -} - -static void infrared_progress_back_callback(void* context) { - furi_assert(context); - auto app = static_cast(context); - - InfraredAppEvent infrared_event = { - .payload = {.dummy = 0}, - .type = InfraredAppEvent::Type::Back, - }; - app->get_view_manager()->clear_events(); - app->get_view_manager()->send_event(&infrared_event); -} - -void InfraredAppSceneUniversalCommon::hide_popup(InfraredApp* app) { - auto stack_view = app->get_view_manager()->get_universal_view_stack(); - auto progress_view = app->get_view_manager()->get_progress(); - view_stack_remove_view(stack_view, infrared_progress_view_get_view(progress_view)); -} - -void InfraredAppSceneUniversalCommon::show_popup(InfraredApp* app, int record_amount) { - auto stack_view = app->get_view_manager()->get_universal_view_stack(); - auto progress_view = app->get_view_manager()->get_progress(); - infrared_progress_view_set_progress_total(progress_view, record_amount); - infrared_progress_view_set_back_callback(progress_view, infrared_progress_back_callback, app); - view_stack_add_view(stack_view, infrared_progress_view_get_view(progress_view)); -} - -bool InfraredAppSceneUniversalCommon::progress_popup(InfraredApp* app) { - auto progress_view = app->get_view_manager()->get_progress(); - return infrared_progress_view_increase_progress(progress_view); -} - -bool InfraredAppSceneUniversalCommon::on_event(InfraredApp* app, InfraredAppEvent* event) { - bool consumed = false; - - if(brute_force_started) { - if(event->type == InfraredAppEvent::Type::Tick) { - auto view_manager = app->get_view_manager(); - app->notify_blink_send(); - InfraredAppEvent tick_event = { - .payload = {.dummy = 0}, - .type = InfraredAppEvent::Type::Tick, - }; - view_manager->send_event(&tick_event); - bool result = brute_force.send_next_bruteforce(); - if(result) { - result = progress_popup(app); - } - if(!result) { - brute_force.stop_bruteforce(); - brute_force_started = false; - hide_popup(app); - } - consumed = true; - } else if(event->type == InfraredAppEvent::Type::Back) { - brute_force_started = false; - brute_force.stop_bruteforce(); - hide_popup(app); - consumed = true; - } - } else { - if(event->type == InfraredAppEvent::Type::ButtonPanelPressed) { - int record_amount = 0; - if(brute_force.start_bruteforce(event->payload.menu_index, record_amount)) { - DOLPHIN_DEED(DolphinDeedIrBruteForce); - brute_force_started = true; - show_popup(app, record_amount); - app->notify_blink_send(); - } else { - app->switch_to_previous_scene(); - } - consumed = true; - } else if(event->type == InfraredAppEvent::Type::Back) { - app->switch_to_previous_scene(); - consumed = true; - } - } - - return consumed; -} - -void InfraredAppSceneUniversalCommon::on_exit(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - ButtonPanel* button_panel = view_manager->get_button_panel(); - button_panel_reset(button_panel); -} diff --git a/applications/infrared/scene/infrared_app_scene_universal_tv.cpp b/applications/infrared/scene/infrared_app_scene_universal_tv.cpp deleted file mode 100644 index a12481ec9..000000000 --- a/applications/infrared/scene/infrared_app_scene_universal_tv.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include -#include -#include "infrared/scene/infrared_app_scene.h" -#include "infrared/infrared_app.h" - -void InfraredAppSceneUniversalTV::on_enter(InfraredApp* app) { - InfraredAppViewManager* view_manager = app->get_view_manager(); - ButtonPanel* button_panel = view_manager->get_button_panel(); - button_panel_reserve(button_panel, 2, 3); - - int i = 0; - button_panel_add_item( - button_panel, - i, - 0, - 0, - 3, - 19, - &I_Power_25x27, - &I_Power_hvr_25x27, - infrared_app_item_callback, - app); - brute_force.add_record(i, "POWER"); - ++i; - button_panel_add_item( - button_panel, - i, - 1, - 0, - 36, - 19, - &I_Mute_25x27, - &I_Mute_hvr_25x27, - infrared_app_item_callback, - app); - brute_force.add_record(i, "MUTE"); - ++i; - button_panel_add_item( - button_panel, - i, - 0, - 1, - 3, - 66, - &I_Vol_up_25x27, - &I_Vol_up_hvr_25x27, - infrared_app_item_callback, - app); - brute_force.add_record(i, "VOL+"); - ++i; - button_panel_add_item( - button_panel, - i, - 1, - 1, - 36, - 66, - &I_Up_25x27, - &I_Up_hvr_25x27, - infrared_app_item_callback, - app); - brute_force.add_record(i, "CH+"); - ++i; - button_panel_add_item( - button_panel, - i, - 0, - 2, - 3, - 98, - &I_Vol_down_25x27, - &I_Vol_down_hvr_25x27, - infrared_app_item_callback, - app); - brute_force.add_record(i, "VOL-"); - ++i; - button_panel_add_item( - button_panel, - i, - 1, - 2, - 36, - 98, - &I_Down_25x27, - &I_Down_hvr_25x27, - infrared_app_item_callback, - app); - brute_force.add_record(i, "CH-"); - - button_panel_add_label(button_panel, 6, 11, FontPrimary, "TV remote"); - button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol"); - button_panel_add_label(button_panel, 43, 64, FontSecondary, "Ch"); - - view_manager->switch_to(InfraredAppViewManager::ViewId::UniversalRemote); - - auto stack_view = app->get_view_manager()->get_universal_view_stack(); - auto loading_view = app->get_view_manager()->get_loading(); - view_stack_add_view(stack_view, loading_get_view(loading_view)); - - /** - * Problem: Update events are not handled in Loading View, because: - * 1) Timer task has least prio - * 2) Storage service uses drivers that capture whole CPU time - * to handle SD communication - * - * Ugly workaround, but it works for current situation: - * raise timer task prio for DB scanning period. - */ - TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); - TaskHandle_t storage_task = xTaskGetHandle("StorageSrv"); - uint32_t timer_prio = uxTaskPriorityGet(timer_task); - uint32_t storage_prio = uxTaskPriorityGet(storage_task); - vTaskPrioritySet(timer_task, storage_prio + 1); - bool result = brute_force.calculate_messages(); - vTaskPrioritySet(timer_task, timer_prio); - - view_stack_remove_view(stack_view, loading_get_view(loading_view)); - - if(!result) { - app->switch_to_previous_scene(); - } -} diff --git a/applications/infrared/scenes/common/infrared_scene_universal_common.c b/applications/infrared/scenes/common/infrared_scene_universal_common.c new file mode 100644 index 000000000..f0e690308 --- /dev/null +++ b/applications/infrared/scenes/common/infrared_scene_universal_common.c @@ -0,0 +1,92 @@ +#include "../../infrared_i.h" + +#include + +void infrared_scene_universal_common_item_callback(void* context, uint32_t index) { + Infrared* infrared = context; + uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeButtonSelected, index); + view_dispatcher_send_custom_event(infrared->view_dispatcher, event); +} + +static void infrared_scene_universal_common_progress_back_callback(void* context) { + Infrared* infrared = context; + uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeBackPressed, -1); + view_dispatcher_send_custom_event(infrared->view_dispatcher, event); +} + +static void infrared_scene_universal_common_show_popup(Infrared* infrared, uint32_t record_count) { + ViewStack* view_stack = infrared->view_stack; + InfraredProgressView* progress = infrared->progress; + infrared_progress_view_set_progress_total(progress, record_count); + infrared_progress_view_set_back_callback( + progress, infrared_scene_universal_common_progress_back_callback, infrared); + view_stack_add_view(view_stack, infrared_progress_view_get_view(progress)); +} + +static void infrared_scene_universal_common_hide_popup(Infrared* infrared) { + ViewStack* view_stack = infrared->view_stack; + InfraredProgressView* progress = infrared->progress; + view_stack_remove_view(view_stack, infrared_progress_view_get_view(progress)); +} + +void infrared_scene_universal_common_on_enter(void* context) { + Infrared* infrared = context; + view_stack_add_view(infrared->view_stack, button_panel_get_view(infrared->button_panel)); +} + +bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + SceneManager* scene_manager = infrared->scene_manager; + InfraredBruteForce* brute_force = infrared->brute_force; + bool consumed = false; + + if(infrared_brute_force_is_started(brute_force)) { + if(event.type == SceneManagerEventTypeTick) { + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkSend); + bool success = infrared_brute_force_send_next(brute_force); + if(success) { + success = infrared_progress_view_increase_progress(infrared->progress); + } + if(!success) { + infrared_brute_force_stop(brute_force); + infrared_scene_universal_common_hide_popup(infrared); + } + consumed = true; + } else if(event.type == SceneManagerEventTypeCustom) { + if(infrared_custom_event_get_type(event.event) == InfraredCustomEventTypeBackPressed) { + infrared_brute_force_stop(brute_force); + infrared_scene_universal_common_hide_popup(infrared); + consumed = true; + } + } + } else { + if(event.type == SceneManagerEventTypeBack) { + scene_manager_previous_scene(scene_manager); + consumed = true; + } else if(event.type == SceneManagerEventTypeCustom) { + if(infrared_custom_event_get_type(event.event) == + InfraredCustomEventTypeButtonSelected) { + uint32_t record_count; + if(infrared_brute_force_start( + brute_force, infrared_custom_event_get_value(event.event), &record_count)) { + DOLPHIN_DEED(DolphinDeedIrBruteForce); + infrared_scene_universal_common_show_popup(infrared, record_count); + infrared_play_notification_message( + infrared, InfraredNotificationMessageBlinkSend); + } else { + scene_manager_previous_scene(scene_manager); + } + consumed = true; + } + } + } + + return consumed; +} + +void infrared_scene_universal_common_on_exit(void* context) { + Infrared* infrared = context; + ButtonPanel* button_panel = infrared->button_panel; + view_stack_remove_view(infrared->view_stack, button_panel_get_view(button_panel)); + button_panel_reset(button_panel); +} diff --git a/applications/infrared/scenes/common/infrared_scene_universal_common.h b/applications/infrared/scenes/common/infrared_scene_universal_common.h new file mode 100644 index 000000000..a6c697d77 --- /dev/null +++ b/applications/infrared/scenes/common/infrared_scene_universal_common.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +void infrared_scene_universal_common_on_enter(void* context); +bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event); +void infrared_scene_universal_common_on_exit(void* context); +void infrared_scene_universal_common_item_callback(void* context, uint32_t index); diff --git a/applications/infrared/scenes/infrared_scene.c b/applications/infrared/scenes/infrared_scene.c new file mode 100644 index 000000000..b0f298e69 --- /dev/null +++ b/applications/infrared/scenes/infrared_scene.c @@ -0,0 +1,30 @@ +#include "infrared_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const infrared_on_enter_handlers[])(void*) = { +#include "infrared_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const infrared_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "infrared_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const infrared_on_exit_handlers[])(void* context) = { +#include "infrared_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers infrared_scene_handlers = { + .on_enter_handlers = infrared_on_enter_handlers, + .on_event_handlers = infrared_on_event_handlers, + .on_exit_handlers = infrared_on_exit_handlers, + .scene_num = InfraredSceneNum, +}; diff --git a/applications/infrared/scenes/infrared_scene.h b/applications/infrared/scenes/infrared_scene.h new file mode 100644 index 000000000..e9d499a7a --- /dev/null +++ b/applications/infrared/scenes/infrared_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) InfraredScene##id, +typedef enum { +#include "infrared_scene_config.h" + InfraredSceneNum, +} InfraredScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers infrared_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "infrared_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "infrared_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "infrared_scene_config.h" +#undef ADD_SCENE diff --git a/applications/infrared/scenes/infrared_scene_ask_back.c b/applications/infrared/scenes/infrared_scene_ask_back.c new file mode 100644 index 000000000..11cfcb04d --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_ask_back.c @@ -0,0 +1,59 @@ +#include "../infrared_i.h" + +static void infrared_scene_dialog_result_callback(DialogExResult result, void* context) { + Infrared* infrared = context; + view_dispatcher_send_custom_event(infrared->view_dispatcher, result); +} + +void infrared_scene_ask_back_on_enter(void* context) { + Infrared* infrared = context; + DialogEx* dialog_ex = infrared->dialog_ex; + + if(infrared->app_state.is_learning_new_remote) { + dialog_ex_set_header(dialog_ex, "Exit to Infrared menu?", 64, 0, AlignCenter, AlignTop); + } else { + dialog_ex_set_header(dialog_ex, "Exit to remote menu?", 64, 0, AlignCenter, AlignTop); + } + + dialog_ex_set_text( + dialog_ex, "All unsaved data\nwill be lost", 64, 31, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 0, 0, NULL); + dialog_ex_set_left_button_text(dialog_ex, "Exit"); + dialog_ex_set_center_button_text(dialog_ex, NULL); + dialog_ex_set_right_button_text(dialog_ex, "Stay"); + dialog_ex_set_result_callback(dialog_ex, infrared_scene_dialog_result_callback); + dialog_ex_set_context(dialog_ex, context); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx); +} + +bool infrared_scene_ask_back_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + SceneManager* scene_manager = infrared->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } else if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultLeft) { + if(infrared->app_state.is_learning_new_remote) { + scene_manager_search_and_switch_to_previous_scene( + scene_manager, InfraredSceneStart); + } else { + scene_manager_search_and_switch_to_previous_scene( + scene_manager, InfraredSceneRemote); + } + consumed = true; + } else if(event.event == DialogExResultRight) { + scene_manager_previous_scene(scene_manager); + consumed = true; + } + } + + return consumed; +} + +void infrared_scene_ask_back_on_exit(void* context) { + Infrared* infrared = context; + dialog_ex_reset(infrared->dialog_ex); +} diff --git a/applications/infrared/scenes/infrared_scene_config.h b/applications/infrared/scenes/infrared_scene_config.h new file mode 100644 index 000000000..3677ab3c9 --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_config.h @@ -0,0 +1,17 @@ +ADD_SCENE(infrared, start, Start) +ADD_SCENE(infrared, ask_back, AskBack) +ADD_SCENE(infrared, edit, Edit) +ADD_SCENE(infrared, edit_delete, EditDelete) +ADD_SCENE(infrared, edit_delete_done, EditDeleteDone) +ADD_SCENE(infrared, edit_button_select, EditButtonSelect) +ADD_SCENE(infrared, edit_rename, EditRename) +ADD_SCENE(infrared, edit_rename_done, EditRenameDone) +ADD_SCENE(infrared, learn, Learn) +ADD_SCENE(infrared, learn_done, LearnDone) +ADD_SCENE(infrared, learn_enter_name, LearnEnterName) +ADD_SCENE(infrared, learn_success, LearnSuccess) +ADD_SCENE(infrared, remote, Remote) +ADD_SCENE(infrared, remote_list, RemoteList) +ADD_SCENE(infrared, universal, Universal) +ADD_SCENE(infrared, universal_tv, UniversalTV) +ADD_SCENE(infrared, debug, Debug) diff --git a/applications/infrared/scenes/infrared_scene_debug.c b/applications/infrared/scenes/infrared_scene_debug.c new file mode 100644 index 000000000..ddb85644b --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_debug.c @@ -0,0 +1,68 @@ +#include "../infrared_i.h" + +void infrared_scene_debug_on_enter(void* context) { + Infrared* infrared = context; + InfraredWorker* worker = infrared->worker; + + infrared_worker_rx_set_received_signal_callback( + worker, infrared_signal_received_callback, context); + infrared_worker_rx_enable_blink_on_receiving(worker, true); + infrared_worker_rx_start(worker); + + infrared_debug_view_set_text(infrared->debug_view, "Received signals\nwill appear here"); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDebugView); +} + +bool infrared_scene_debug_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == InfraredCustomEventTypeSignalReceived) { + InfraredDebugView* debug_view = infrared->debug_view; + InfraredSignal* signal = infrared->received_signal; + + if(infrared_signal_is_raw(signal)) { + InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); + infrared_debug_view_set_text(debug_view, "RAW\n%d samples\n", raw->timings_size); + + printf("RAW, %d samples:\r\n", raw->timings_size); + for(size_t i = 0; i < raw->timings_size; ++i) { + printf("%lu ", raw->timings[i]); + } + printf("\r\n"); + + } else { + InfraredMessage* message = infrared_signal_get_message(signal); + infrared_debug_view_set_text( + debug_view, + "%s\nA:0x%0*lX\nC:0x%0*lX\n%s\n", + infrared_get_protocol_name(message->protocol), + ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), + message->address, + ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), + message->command, + message->repeat ? " R" : ""); + + printf( + "== %s, A:0x%0*lX, C:0x%0*lX%s ==\r\n", + infrared_get_protocol_name(message->protocol), + ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), + message->address, + ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), + message->command, + message->repeat ? " R" : ""); + } + consumed = true; + } + } + + return consumed; +} + +void infrared_scene_debug_on_exit(void* context) { + Infrared* infrared = context; + InfraredWorker* worker = infrared->worker; + infrared_worker_rx_stop(worker); + infrared_worker_rx_enable_blink_on_receiving(worker, false); +} diff --git a/applications/infrared/scenes/infrared_scene_edit.c b/applications/infrared/scenes/infrared_scene_edit.c new file mode 100644 index 000000000..360ed49b3 --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_edit.c @@ -0,0 +1,101 @@ +#include "../infrared_i.h" + +typedef enum { + SubmenuIndexAddButton, + SubmenuIndexRenameButton, + SubmenuIndexDeleteButton, + SubmenuIndexRenameRemote, + SubmenuIndexDeleteRemote, +} SubmenuIndex; + +static void infrared_scene_edit_submenu_callback(void* context, uint32_t index) { + Infrared* infrared = context; + view_dispatcher_send_custom_event(infrared->view_dispatcher, index); +} + +void infrared_scene_edit_on_enter(void* context) { + Infrared* infrared = context; + Submenu* submenu = infrared->submenu; + SceneManager* scene_manager = infrared->scene_manager; + + submenu_add_item( + submenu, + "Add Button", + SubmenuIndexAddButton, + infrared_scene_edit_submenu_callback, + context); + submenu_add_item( + submenu, + "Rename Button", + SubmenuIndexRenameButton, + infrared_scene_edit_submenu_callback, + context); + submenu_add_item( + submenu, + "Delete Button", + SubmenuIndexDeleteButton, + infrared_scene_edit_submenu_callback, + context); + submenu_add_item( + submenu, + "Rename Remote", + SubmenuIndexRenameRemote, + infrared_scene_edit_submenu_callback, + context); + submenu_add_item( + submenu, + "Delete Remote", + SubmenuIndexDeleteRemote, + infrared_scene_edit_submenu_callback, + context); + + const uint32_t submenu_index = scene_manager_get_scene_state(scene_manager, InfraredSceneEdit); + submenu_set_selected_item(submenu, submenu_index); + scene_manager_set_scene_state(scene_manager, InfraredSceneEdit, SubmenuIndexAddButton); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu); +} + +bool infrared_scene_edit_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + SceneManager* scene_manager = infrared->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + const uint32_t submenu_index = event.event; + scene_manager_set_scene_state(scene_manager, InfraredSceneEdit, submenu_index); + + if(submenu_index == SubmenuIndexAddButton) { + infrared->app_state.is_learning_new_remote = false; + scene_manager_next_scene(scene_manager, InfraredSceneLearn); + consumed = true; + } else if(submenu_index == SubmenuIndexRenameButton) { + infrared->app_state.edit_target = InfraredEditTargetButton; + infrared->app_state.edit_mode = InfraredEditModeRename; + scene_manager_next_scene(scene_manager, InfraredSceneEditButtonSelect); + consumed = true; + } else if(submenu_index == SubmenuIndexDeleteButton) { + infrared->app_state.edit_target = InfraredEditTargetButton; + infrared->app_state.edit_mode = InfraredEditModeDelete; + scene_manager_next_scene(scene_manager, InfraredSceneEditButtonSelect); + consumed = true; + } else if(submenu_index == SubmenuIndexRenameRemote) { + infrared->app_state.edit_target = InfraredEditTargetRemote; + infrared->app_state.edit_mode = InfraredEditModeRename; + scene_manager_next_scene(scene_manager, InfraredSceneEditRename); + consumed = true; + } else if(submenu_index == SubmenuIndexDeleteRemote) { + infrared->app_state.edit_target = InfraredEditTargetRemote; + infrared->app_state.edit_mode = InfraredEditModeDelete; + scene_manager_next_scene(scene_manager, InfraredSceneEditDelete); + consumed = true; + } + } + + return consumed; +} + +void infrared_scene_edit_on_exit(void* context) { + Infrared* infrared = context; + submenu_reset(infrared->submenu); +} diff --git a/applications/infrared/scenes/infrared_scene_edit_button_select.c b/applications/infrared/scenes/infrared_scene_edit_button_select.c new file mode 100644 index 000000000..a7f8a2bf7 --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_edit_button_select.c @@ -0,0 +1,63 @@ +#include "../infrared_i.h" + +static void infrared_scene_edit_button_select_submenu_callback(void* context, uint32_t index) { + Infrared* infrared = context; + view_dispatcher_send_custom_event(infrared->view_dispatcher, index); +} + +void infrared_scene_edit_button_select_on_enter(void* context) { + Infrared* infrared = context; + Submenu* submenu = infrared->submenu; + InfraredRemote* remote = infrared->remote; + InfraredAppState* app_state = &infrared->app_state; + + const char* header = infrared->app_state.edit_mode == InfraredEditModeRename ? + "Rename Button:" : + "Delete Button:"; + submenu_set_header(submenu, header); + + const size_t button_count = infrared_remote_get_button_count(remote); + for(size_t i = 0; i < button_count; ++i) { + InfraredRemoteButton* button = infrared_remote_get_button(remote, i); + submenu_add_item( + submenu, + infrared_remote_button_get_name(button), + i, + infrared_scene_edit_button_select_submenu_callback, + context); + } + + if(button_count && app_state->current_button_index != InfraredButtonIndexNone) { + submenu_set_selected_item(submenu, app_state->current_button_index); + app_state->current_button_index = InfraredButtonIndexNone; + } + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu); +} + +bool infrared_scene_edit_button_select_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + InfraredAppState* app_state = &infrared->app_state; + SceneManager* scene_manager = infrared->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + app_state->current_button_index = event.event; + const InfraredEditMode edit_mode = app_state->edit_mode; + if(edit_mode == InfraredEditModeRename) { + scene_manager_next_scene(scene_manager, InfraredSceneEditRename); + } else if(edit_mode == InfraredEditModeDelete) { + scene_manager_next_scene(scene_manager, InfraredSceneEditDelete); + } else { + furi_assert(0); + } + consumed = true; + } + + return consumed; +} + +void infrared_scene_edit_button_select_on_exit(void* context) { + Infrared* infrared = context; + submenu_reset(infrared->submenu); +} diff --git a/applications/infrared/scenes/infrared_scene_edit_delete.c b/applications/infrared/scenes/infrared_scene_edit_delete.c new file mode 100644 index 000000000..0842cd613 --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_edit_delete.c @@ -0,0 +1,112 @@ +#include "../infrared_i.h" + +static void + infrared_scene_edit_delete_dialog_result_callback(DialogExResult result, void* context) { + Infrared* infrared = context; + view_dispatcher_send_custom_event(infrared->view_dispatcher, result); +} + +void infrared_scene_edit_delete_on_enter(void* context) { + Infrared* infrared = context; + DialogEx* dialog_ex = infrared->dialog_ex; + InfraredRemote* remote = infrared->remote; + + const InfraredEditTarget edit_target = infrared->app_state.edit_target; + if(edit_target == InfraredEditTargetButton) { + int32_t current_button_index = infrared->app_state.current_button_index; + furi_assert(current_button_index != InfraredButtonIndexNone); + + dialog_ex_set_header(dialog_ex, "Delete button?", 64, 0, AlignCenter, AlignTop); + InfraredRemoteButton* current_button = + infrared_remote_get_button(remote, current_button_index); + InfraredSignal* signal = infrared_remote_button_get_signal(current_button); + + if(infrared_signal_is_raw(signal)) { + const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); + infrared_text_store_set( + infrared, + 0, + "%s\nRAW\n%ld samples", + infrared_remote_button_get_name(current_button), + raw->timings_size); + + } else { + const InfraredMessage* message = infrared_signal_get_message(signal); + infrared_text_store_set( + infrared, + 0, + "%s\n%s\nA=0x%0*lX C=0x%0*lX", + infrared_remote_button_get_name(current_button), + infrared_get_protocol_name(message->protocol), + ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), + message->address, + ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), + message->command); + } + + } else if(edit_target == InfraredEditTargetRemote) { + dialog_ex_set_header(dialog_ex, "Delete remote?", 64, 0, AlignCenter, AlignTop); + infrared_text_store_set( + infrared, + 0, + "%s\n with %lu buttons", + infrared_remote_get_name(remote), + infrared_remote_get_button_count(remote)); + } else { + furi_assert(0); + } + + dialog_ex_set_text(dialog_ex, infrared->text_store[0], 64, 31, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 0, 0, NULL); + dialog_ex_set_left_button_text(dialog_ex, "Cancel"); + dialog_ex_set_right_button_text(dialog_ex, "Delete"); + dialog_ex_set_result_callback(dialog_ex, infrared_scene_edit_delete_dialog_result_callback); + dialog_ex_set_context(dialog_ex, context); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx); +} + +bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + SceneManager* scene_manager = infrared->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultLeft) { + scene_manager_previous_scene(scene_manager); + consumed = true; + } else if(event.event == DialogExResultRight) { + bool success = false; + InfraredRemote* remote = infrared->remote; + InfraredAppState* app_state = &infrared->app_state; + const InfraredEditTarget edit_target = app_state->edit_target; + + if(edit_target == InfraredEditTargetButton) { + furi_assert(app_state->current_button_index != InfraredButtonIndexNone); + success = infrared_remote_delete_button(remote, app_state->current_button_index); + app_state->current_button_index = InfraredButtonIndexNone; + } else if(edit_target == InfraredEditTargetRemote) { + success = infrared_remote_remove(remote); + app_state->current_button_index = InfraredButtonIndexNone; + } else { + furi_assert(0); + } + + if(success) { + scene_manager_next_scene(scene_manager, InfraredSceneEditDeleteDone); + } else { + const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; + scene_manager_search_and_switch_to_previous_scene_one_of( + scene_manager, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t)); + } + consumed = true; + } + } + + return consumed; +} + +void infrared_scene_edit_delete_on_exit(void* context) { + Infrared* infrared = context; + UNUSED(infrared); +} diff --git a/applications/infrared/scenes/infrared_scene_edit_delete_done.c b/applications/infrared/scenes/infrared_scene_edit_delete_done.c new file mode 100644 index 000000000..688c3a457 --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_edit_delete_done.c @@ -0,0 +1,46 @@ +#include "../infrared_i.h" + +void infrared_scene_edit_delete_done_on_enter(void* context) { + Infrared* infrared = context; + Popup* popup = infrared->popup; + + popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); + popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + + popup_set_callback(popup, infrared_popup_timeout_callback); + popup_set_context(popup, context); + popup_set_timeout(popup, 1500); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup); +} + +bool infrared_scene_edit_delete_done_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + SceneManager* scene_manager = infrared->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == InfraredCustomEventTypePopupTimeout) { + const InfraredEditTarget edit_target = infrared->app_state.edit_target; + if(edit_target == InfraredEditTargetButton) { + scene_manager_search_and_switch_to_previous_scene( + scene_manager, InfraredSceneRemote); + } else if(edit_target == InfraredEditTargetRemote) { + const uint32_t possible_scenes[] = {InfraredSceneStart, InfraredSceneRemoteList}; + scene_manager_search_and_switch_to_previous_scene_one_of( + scene_manager, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t)); + } else { + furi_assert(0); + } + consumed = true; + } + } + + return consumed; +} + +void infrared_scene_edit_delete_done_on_exit(void* context) { + Infrared* infrared = context; + UNUSED(infrared); +} diff --git a/applications/infrared/scenes/infrared_scene_edit_rename.c b/applications/infrared/scenes/infrared_scene_edit_rename.c new file mode 100644 index 000000000..ebe7c2a96 --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_edit_rename.c @@ -0,0 +1,107 @@ +#include "../infrared_i.h" + +#include +#include + +void infrared_scene_edit_rename_on_enter(void* context) { + Infrared* infrared = context; + InfraredRemote* remote = infrared->remote; + TextInput* text_input = infrared->text_input; + size_t enter_name_length = 0; + + const InfraredEditTarget edit_target = infrared->app_state.edit_target; + if(edit_target == InfraredEditTargetButton) { + text_input_set_header_text(text_input, "Name the button"); + + const int32_t current_button_index = infrared->app_state.current_button_index; + furi_assert(current_button_index != InfraredButtonIndexNone); + + InfraredRemoteButton* current_button = + infrared_remote_get_button(remote, current_button_index); + enter_name_length = INFRARED_MAX_BUTTON_NAME_LENGTH; + strncpy( + infrared->text_store[0], + infrared_remote_button_get_name(current_button), + enter_name_length); + + } else if(edit_target == InfraredEditTargetRemote) { + text_input_set_header_text(text_input, "Name the remote"); + enter_name_length = INFRARED_MAX_REMOTE_NAME_LENGTH; + strncpy(infrared->text_store[0], infrared_remote_get_name(remote), enter_name_length); + + string_t folder_path; + string_init(folder_path); + + if(string_end_with_str_p(infrared->file_path, INFRARED_APP_EXTENSION)) { + path_extract_dirname(string_get_cstr(infrared->file_path), folder_path); + } + + ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( + string_get_cstr(folder_path), + INFRARED_APP_EXTENSION, + infrared_remote_get_name(remote)); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + string_clear(folder_path); + } else { + furi_assert(0); + } + + text_input_set_result_callback( + text_input, + infrared_text_input_callback, + context, + infrared->text_store[0], + enter_name_length, + false); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput); +} + +bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + InfraredRemote* remote = infrared->remote; + SceneManager* scene_manager = infrared->scene_manager; + InfraredAppState* app_state = &infrared->app_state; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == InfraredCustomEventTypeTextEditDone) { + bool success = false; + const InfraredEditTarget edit_target = app_state->edit_target; + if(edit_target == InfraredEditTargetButton) { + const int32_t current_button_index = app_state->current_button_index; + furi_assert(current_button_index != InfraredButtonIndexNone); + success = infrared_remote_rename_button( + remote, infrared->text_store[0], current_button_index); + app_state->current_button_index = InfraredButtonIndexNone; + } else if(edit_target == InfraredEditTargetRemote) { + success = infrared_rename_current_remote(infrared, infrared->text_store[0]); + } else { + furi_assert(0); + } + + if(success) { + scene_manager_next_scene(scene_manager, InfraredSceneEditRenameDone); + } else { + scene_manager_search_and_switch_to_previous_scene( + scene_manager, InfraredSceneRemoteList); + } + consumed = true; + } + } + + return consumed; +} + +void infrared_scene_edit_rename_on_exit(void* context) { + Infrared* infrared = context; + TextInput* text_input = infrared->text_input; + + void* validator_context = text_input_get_validator_callback_context(text_input); + text_input_set_validator(text_input, NULL, NULL); + + if(validator_context) { + validator_is_file_free((ValidatorIsFile*)validator_context); + } +} diff --git a/applications/infrared/scenes/infrared_scene_edit_rename_done.c b/applications/infrared/scenes/infrared_scene_edit_rename_done.c new file mode 100644 index 000000000..60a26954d --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_edit_rename_done.c @@ -0,0 +1,35 @@ +#include "../infrared_i.h" + +void infrared_scene_edit_rename_done_on_enter(void* context) { + Infrared* infrared = context; + Popup* popup = infrared->popup; + + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); + + popup_set_callback(popup, infrared_popup_timeout_callback); + popup_set_context(popup, context); + popup_set_timeout(popup, 1500); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup); +} + +bool infrared_scene_edit_rename_done_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == InfraredCustomEventTypePopupTimeout) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); + consumed = true; + } + } + + return consumed; +} + +void infrared_scene_edit_rename_done_on_exit(void* context) { + Infrared* infrared = context; + UNUSED(infrared); +} diff --git a/applications/infrared/scenes/infrared_scene_learn.c b/applications/infrared/scenes/infrared_scene_learn.c new file mode 100644 index 000000000..d91b8676a --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_learn.c @@ -0,0 +1,46 @@ +#include "../infrared_i.h" + +void infrared_scene_learn_on_enter(void* context) { + Infrared* infrared = context; + Popup* popup = infrared->popup; + InfraredWorker* worker = infrared->worker; + + infrared_worker_rx_set_received_signal_callback( + worker, infrared_signal_received_callback, context); + infrared_worker_rx_start(worker); + + popup_set_icon(popup, 0, 32, &I_InfraredLearnShort_128x31); + popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignCenter); + popup_set_text( + popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter); + popup_set_callback(popup, NULL); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup); +} + +bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeTick) { + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkRead); + consumed = true; + } else if(event.type == SceneManagerEventTypeCustom) { + if(event.event == InfraredCustomEventTypeSignalReceived) { + infrared_worker_rx_set_received_signal_callback(infrared->worker, NULL, NULL); + infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess); + scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess); + consumed = true; + } + } + + return consumed; +} + +void infrared_scene_learn_on_exit(void* context) { + Infrared* infrared = context; + Popup* popup = infrared->popup; + infrared_worker_rx_stop(infrared->worker); + popup_set_icon(popup, 0, 0, NULL); + popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignCenter); +} diff --git a/applications/infrared/scenes/infrared_scene_learn_done.c b/applications/infrared/scenes/infrared_scene_learn_done.c new file mode 100644 index 000000000..a24bb9184 --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_learn_done.c @@ -0,0 +1,44 @@ +#include "../infrared_i.h" + +#include + +void infrared_scene_learn_done_on_enter(void* context) { + Infrared* infrared = context; + Popup* popup = infrared->popup; + + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + DOLPHIN_DEED(DolphinDeedIrSave); + + if(infrared->app_state.is_learning_new_remote) { + popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop); + } else { + popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); + } + + popup_set_callback(popup, infrared_popup_timeout_callback); + popup_set_context(popup, context); + popup_set_timeout(popup, 1500); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup); +} + +bool infrared_scene_learn_done_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == InfraredCustomEventTypePopupTimeout) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); + consumed = true; + } + } + + return consumed; +} + +void infrared_scene_learn_done_on_exit(void* context) { + Infrared* infrared = context; + infrared->app_state.is_learning_new_remote = false; + popup_set_header(infrared->popup, NULL, 0, 0, AlignLeft, AlignTop); +} diff --git a/applications/infrared/scenes/infrared_scene_learn_enter_name.c b/applications/infrared/scenes/infrared_scene_learn_enter_name.c new file mode 100644 index 000000000..b7f4179ea --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_learn_enter_name.c @@ -0,0 +1,66 @@ +#include "../infrared_i.h" + +void infrared_scene_learn_enter_name_on_enter(void* context) { + Infrared* infrared = context; + TextInput* text_input = infrared->text_input; + InfraredSignal* signal = infrared->received_signal; + + if(infrared_signal_is_raw(signal)) { + InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); + infrared_text_store_set(infrared, 0, "RAW_%d", raw->timings_size); + } else { + InfraredMessage* message = infrared_signal_get_message(signal); + infrared_text_store_set( + infrared, + 0, + "%.4s_%0*lX", + infrared_get_protocol_name(message->protocol), + ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), + message->command); + } + + text_input_set_header_text(text_input, "Name the button"); + text_input_set_result_callback( + text_input, + infrared_text_input_callback, + context, + infrared->text_store[0], + INFRARED_MAX_BUTTON_NAME_LENGTH, + true); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput); +} + +bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + InfraredSignal* signal = infrared->received_signal; + SceneManager* scene_manager = infrared->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == InfraredCustomEventTypeTextEditDone) { + bool success = false; + if(infrared->app_state.is_learning_new_remote) { + success = + infrared_add_remote_with_button(infrared, infrared->text_store[0], signal); + } else { + success = + infrared_remote_add_button(infrared->remote, infrared->text_store[0], signal); + } + + if(success) { + scene_manager_next_scene(scene_manager, InfraredSceneLearnDone); + } else { + scene_manager_search_and_switch_to_previous_scene( + scene_manager, InfraredSceneRemoteList); + } + consumed = true; + } + } + + return consumed; +} + +void infrared_scene_learn_enter_name_on_exit(void* context) { + Infrared* infrared = context; + UNUSED(infrared); +} diff --git a/applications/infrared/scenes/infrared_scene_learn_success.c b/applications/infrared/scenes/infrared_scene_learn_success.c new file mode 100644 index 000000000..ec950ca71 --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_learn_success.c @@ -0,0 +1,131 @@ +#include "../infrared_i.h" + +#include + +typedef enum { + InfraredSceneLearnSuccessStateIdle = 0, + InfraredSceneLearnSuccessStateSending = 1, +} InfraredSceneLearnSuccessState; + +static void + infrared_scene_learn_success_dialog_result_callback(DialogExResult result, void* context) { + Infrared* infrared = context; + view_dispatcher_send_custom_event(infrared->view_dispatcher, result); +} + +void infrared_scene_learn_success_on_enter(void* context) { + Infrared* infrared = context; + DialogEx* dialog_ex = infrared->dialog_ex; + InfraredSignal* signal = infrared->received_signal; + + DOLPHIN_DEED(DolphinDeedIrLearnSuccess); + infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn); + + infrared_worker_tx_set_get_signal_callback( + infrared->worker, infrared_worker_tx_get_signal_steady_callback, context); + infrared_worker_tx_set_signal_sent_callback( + infrared->worker, infrared_signal_sent_callback, context); + + if(infrared_signal_is_raw(signal)) { + InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); + dialog_ex_set_header(dialog_ex, "Unknown", 95, 10, AlignCenter, AlignCenter); + infrared_text_store_set(infrared, 0, "%d samples", raw->timings_size); + dialog_ex_set_text(dialog_ex, infrared->text_store[0], 75, 23, AlignLeft, AlignTop); + + } else { + InfraredMessage* message = infrared_signal_get_message(signal); + uint8_t addr_digits = + ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4); + uint8_t cmd_digits = + ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4); + uint8_t max_digits = MAX(addr_digits, cmd_digits); + max_digits = MIN(max_digits, 7); + size_t label_x_offset = 63 + (7 - max_digits) * 3; + + infrared_text_store_set(infrared, 0, "%s", infrared_get_protocol_name(message->protocol)); + infrared_text_store_set( + infrared, + 1, + "A: 0x%0*lX\nC: 0x%0*lX\n", + addr_digits, + message->address, + cmd_digits, + message->command); + + dialog_ex_set_header(dialog_ex, infrared->text_store[0], 95, 7, AlignCenter, AlignCenter); + dialog_ex_set_text( + dialog_ex, infrared->text_store[1], label_x_offset, 34, AlignLeft, AlignCenter); + } + + dialog_ex_set_left_button_text(dialog_ex, "Retry"); + 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_DolphinReadingSuccess_59x63); + dialog_ex_set_result_callback(dialog_ex, infrared_scene_learn_success_dialog_result_callback); + dialog_ex_set_context(dialog_ex, context); + dialog_ex_enable_extended_events(dialog_ex); + + scene_manager_set_scene_state( + infrared->scene_manager, InfraredSceneLearnSuccess, InfraredSceneLearnSuccessStateIdle); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx); +} + +bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + SceneManager* scene_manager = infrared->scene_manager; + uint32_t scene_state = scene_manager_get_scene_state(scene_manager, InfraredSceneLearnSuccess); + bool consumed = false; + + if(event.type == SceneManagerEventTypeTick) { + if(scene_state == InfraredSceneLearnSuccessStateIdle) { + infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn); + } + consumed = true; + } else if(event.type == SceneManagerEventTypeBack) { + if(scene_state == InfraredSceneLearnSuccessStateIdle) { + scene_manager_next_scene(scene_manager, InfraredSceneAskBack); + } + consumed = true; + } else if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultLeft) { + if(scene_state == InfraredSceneLearnSuccessStateIdle) { + scene_manager_search_and_switch_to_previous_scene( + scene_manager, InfraredSceneLearn); + } + consumed = true; + } else if(event.event == DialogExResultRight) { + if(scene_state == InfraredSceneLearnSuccessStateIdle) { + scene_manager_next_scene(scene_manager, InfraredSceneLearnEnterName); + } + consumed = true; + } else if(event.event == DialogExPressCenter) { + if(scene_state == InfraredSceneLearnSuccessStateIdle) { + scene_manager_set_scene_state( + scene_manager, + InfraredSceneLearnSuccess, + InfraredSceneLearnSuccessStateSending); + infrared_tx_start_received(infrared); + } + consumed = true; + } else if(event.event == DialogExReleaseCenter) { + if(scene_state == InfraredSceneLearnSuccessStateSending) { + scene_manager_set_scene_state( + scene_manager, InfraredSceneLearnSuccess, InfraredSceneLearnSuccessStateIdle); + infrared_tx_stop(infrared); + infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff); + } + consumed = true; + } + } + + return consumed; +} + +void infrared_scene_learn_success_on_exit(void* context) { + Infrared* infrared = context; + InfraredWorker* worker = infrared->worker; + dialog_ex_reset(infrared->dialog_ex); + infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff); + infrared_worker_tx_set_get_signal_callback(worker, NULL, NULL); + infrared_worker_tx_set_signal_sent_callback(worker, NULL, NULL); +} diff --git a/applications/infrared/scenes/infrared_scene_remote.c b/applications/infrared/scenes/infrared_scene_remote.c new file mode 100644 index 000000000..0b7a1ad93 --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_remote.c @@ -0,0 +1,119 @@ +#include "../infrared_i.h" + +typedef enum { + ButtonIndexPlus = -2, + ButtonIndexEdit = -1, + ButtonIndexNA = 0, +} ButtonIndex; + +static void + infrared_scene_remote_button_menu_callback(void* context, int32_t index, InputType type) { + Infrared* infrared = context; + + uint16_t custom_type; + if(type == InputTypePress) { + custom_type = InfraredCustomEventTypeTransmitStarted; + } else if(type == InputTypeRelease) { + custom_type = InfraredCustomEventTypeTransmitStopped; + } else if(type == InputTypeShort) { + custom_type = InfraredCustomEventTypeMenuSelected; + } else { + furi_crash("Unexpected input type"); + } + + view_dispatcher_send_custom_event( + infrared->view_dispatcher, infrared_custom_event_pack(custom_type, index)); +} + +void infrared_scene_remote_on_enter(void* context) { + Infrared* infrared = context; + InfraredRemote* remote = infrared->remote; + ButtonMenu* button_menu = infrared->button_menu; + SceneManager* scene_manager = infrared->scene_manager; + + infrared_worker_tx_set_get_signal_callback( + infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared); + infrared_worker_tx_set_signal_sent_callback( + infrared->worker, infrared_signal_sent_callback, infrared); + + size_t button_count = infrared_remote_get_button_count(remote); + for(size_t i = 0; i < button_count; ++i) { + InfraredRemoteButton* button = infrared_remote_get_button(remote, i); + button_menu_add_item( + button_menu, + infrared_remote_button_get_name(button), + i, + infrared_scene_remote_button_menu_callback, + ButtonMenuItemTypeCommon, + context); + } + + button_menu_add_item( + button_menu, + "+", + ButtonIndexPlus, + infrared_scene_remote_button_menu_callback, + ButtonMenuItemTypeControl, + context); + button_menu_add_item( + button_menu, + "Edit", + ButtonIndexEdit, + infrared_scene_remote_button_menu_callback, + ButtonMenuItemTypeControl, + context); + + button_menu_set_header(button_menu, infrared_remote_get_name(remote)); + const int16_t button_index = + (signed)scene_manager_get_scene_state(scene_manager, InfraredSceneRemote); + button_menu_set_selected_item(button_menu, button_index); + scene_manager_set_scene_state(scene_manager, InfraredSceneRemote, ButtonIndexNA); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewButtonMenu); +} + +bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + SceneManager* scene_manager = infrared->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; + scene_manager_search_and_switch_to_previous_scene_one_of( + scene_manager, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t)); + consumed = true; + } else if(event.type == SceneManagerEventTypeCustom) { + const uint16_t custom_type = infrared_custom_event_get_type(event.event); + const int16_t button_index = infrared_custom_event_get_value(event.event); + + if(custom_type == InfraredCustomEventTypeTransmitStarted) { + furi_assert(button_index >= 0); + infrared_tx_start_button_index(infrared, button_index); + consumed = true; + } else if(custom_type == InfraredCustomEventTypeTransmitStopped) { + infrared_tx_stop(infrared); + consumed = true; + } else if(custom_type == InfraredCustomEventTypeMenuSelected) { + furi_assert(button_index < 0); + scene_manager_set_scene_state( + scene_manager, InfraredSceneRemote, (unsigned)button_index); + if(button_index == ButtonIndexPlus) { + infrared->app_state.is_learning_new_remote = false; + scene_manager_next_scene(scene_manager, InfraredSceneLearn); + consumed = true; + } else if(button_index == ButtonIndexEdit) { + scene_manager_next_scene(scene_manager, InfraredSceneEdit); + consumed = true; + } + } + } + + return consumed; +} + +void infrared_scene_remote_on_exit(void* context) { + Infrared* infrared = context; + infrared_worker_tx_set_get_signal_callback(infrared->worker, NULL, NULL); + infrared_worker_tx_set_signal_sent_callback(infrared->worker, NULL, NULL); + button_menu_reset(infrared->button_menu); +} diff --git a/applications/infrared/scenes/infrared_scene_remote_list.c b/applications/infrared/scenes/infrared_scene_remote_list.c new file mode 100644 index 000000000..25b37759c --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_remote_list.c @@ -0,0 +1,44 @@ +#include "../infrared_i.h" + +void infrared_scene_remote_list_on_enter(void* context) { + Infrared* infrared = context; + SceneManager* scene_manager = infrared->scene_manager; + ViewDispatcher* view_dispatcher = infrared->view_dispatcher; + + string_set_str(infrared->file_path, INFRARED_APP_FOLDER); + bool success = dialog_file_browser_show( + infrared->dialogs, + infrared->file_path, + infrared->file_path, + INFRARED_APP_EXTENSION, + true, + &I_ir_10px, + true); + + if(success) { + view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationHorizontal); + view_dispatcher_switch_to_view(view_dispatcher, InfraredViewStack); + + infrared_show_loading_popup(infrared, true); + success = infrared_remote_load(infrared->remote, infrared->file_path); + infrared_show_loading_popup(infrared, false); + } + + if(success) { + scene_manager_next_scene(scene_manager, InfraredSceneRemote); + } else { + scene_manager_previous_scene(scene_manager); + } +} + +bool infrared_scene_remote_list_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + bool consumed = false; + + return consumed; +} + +void infrared_scene_remote_list_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/infrared/scenes/infrared_scene_start.c b/applications/infrared/scenes/infrared_scene_start.c new file mode 100644 index 000000000..6b874a3ad --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_start.c @@ -0,0 +1,83 @@ +#include "../infrared_i.h" + +enum SubmenuIndex { + SubmenuIndexUniversalRemotes, + SubmenuIndexLearnNewRemote, + SubmenuIndexSavedRemotes, + SubmenuIndexDebug +}; + +static void infrared_scene_start_submenu_callback(void* context, uint32_t index) { + Infrared* infrared = context; + view_dispatcher_send_custom_event(infrared->view_dispatcher, index); +} + +void infrared_scene_start_on_enter(void* context) { + Infrared* infrared = context; + Submenu* submenu = infrared->submenu; + SceneManager* scene_manager = infrared->scene_manager; + + submenu_add_item( + submenu, + "Universal Remotes", + SubmenuIndexUniversalRemotes, + infrared_scene_start_submenu_callback, + infrared); + submenu_add_item( + submenu, + "Learn New Remote", + SubmenuIndexLearnNewRemote, + infrared_scene_start_submenu_callback, + infrared); + submenu_add_item( + submenu, + "Saved Remotes", + SubmenuIndexSavedRemotes, + infrared_scene_start_submenu_callback, + infrared); + + if(infrared->app_state.is_debug_enabled) { + submenu_add_item( + submenu, "Debug", SubmenuIndexDebug, infrared_scene_start_submenu_callback, infrared); + } + + const uint32_t submenu_index = + scene_manager_get_scene_state(scene_manager, InfraredSceneStart); + submenu_set_selected_item(submenu, submenu_index); + scene_manager_set_scene_state(scene_manager, InfraredSceneStart, SubmenuIndexUniversalRemotes); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu); +} + +bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + SceneManager* scene_manager = infrared->scene_manager; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + const uint32_t submenu_index = event.event; + scene_manager_set_scene_state(scene_manager, InfraredSceneStart, submenu_index); + if(submenu_index == SubmenuIndexUniversalRemotes) { + scene_manager_next_scene(scene_manager, InfraredSceneUniversal); + consumed = true; + } else if(submenu_index == SubmenuIndexLearnNewRemote) { + infrared->app_state.is_learning_new_remote = true; + scene_manager_next_scene(scene_manager, InfraredSceneLearn); + consumed = true; + } else if(submenu_index == SubmenuIndexSavedRemotes) { + scene_manager_next_scene(scene_manager, InfraredSceneRemoteList); + consumed = true; + } else if(submenu_index == SubmenuIndexDebug) { + scene_manager_next_scene(scene_manager, InfraredSceneDebug); + consumed = true; + } + } + + return consumed; +} + +void infrared_scene_start_on_exit(void* context) { + Infrared* infrared = context; + submenu_reset(infrared->submenu); +} diff --git a/applications/infrared/scenes/infrared_scene_universal.c b/applications/infrared/scenes/infrared_scene_universal.c new file mode 100644 index 000000000..cc6568834 --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_universal.c @@ -0,0 +1,53 @@ +#include "../infrared_i.h" + +typedef enum { + SubmenuIndexUniversalTV, + SubmenuIndexUniversalAudio, + SubmenuIndexUniversalAirConditioner, +} SubmenuIndex; + +static void infrared_scene_universal_submenu_callback(void* context, uint32_t index) { + Infrared* infrared = context; + view_dispatcher_send_custom_event(infrared->view_dispatcher, index); +} + +void infrared_scene_universal_on_enter(void* context) { + Infrared* infrared = context; + Submenu* submenu = infrared->submenu; + + submenu_add_item( + submenu, + "TVs", + SubmenuIndexUniversalTV, + infrared_scene_universal_submenu_callback, + context); + submenu_set_selected_item(submenu, 0); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu); +} + +bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + SceneManager* scene_manager = infrared->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexUniversalTV) { + scene_manager_next_scene(scene_manager, InfraredSceneUniversalTV); + consumed = true; + } else if(event.event == SubmenuIndexUniversalAudio) { + //TODO Implement Audio universal remote + consumed = true; + } else if(event.event == SubmenuIndexUniversalAirConditioner) { + //TODO Implement A/C universal remote + consumed = true; + } + } + + return consumed; +} + +void infrared_scene_universal_on_exit(void* context) { + Infrared* infrared = context; + submenu_reset(infrared->submenu); +} diff --git a/applications/infrared/scenes/infrared_scene_universal_tv.c b/applications/infrared/scenes/infrared_scene_universal_tv.c new file mode 100644 index 000000000..f3a9a06bc --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_universal_tv.c @@ -0,0 +1,111 @@ +#include "../infrared_i.h" + +#include "common/infrared_scene_universal_common.h" + +void infrared_scene_universal_tv_on_enter(void* context) { + infrared_scene_universal_common_on_enter(context); + + Infrared* infrared = context; + ButtonPanel* button_panel = infrared->button_panel; + InfraredBruteForce* brute_force = infrared->brute_force; + + infrared_brute_force_set_db_filename(brute_force, "/ext/infrared/assets/tv.ir"); + + button_panel_reserve(button_panel, 2, 3); + uint32_t i = 0; + button_panel_add_item( + button_panel, + i, + 0, + 0, + 3, + 19, + &I_Power_25x27, + &I_Power_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "POWER"); + button_panel_add_item( + button_panel, + i, + 1, + 0, + 36, + 19, + &I_Mute_25x27, + &I_Mute_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "MUTE"); + button_panel_add_item( + button_panel, + i, + 0, + 1, + 3, + 66, + &I_Vol_up_25x27, + &I_Vol_up_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "VOL+"); + button_panel_add_item( + button_panel, + i, + 1, + 1, + 36, + 66, + &I_Up_25x27, + &I_Up_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "CH+"); + button_panel_add_item( + button_panel, + i, + 0, + 2, + 3, + 98, + &I_Vol_down_25x27, + &I_Vol_down_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "VOL-"); + button_panel_add_item( + button_panel, + i, + 1, + 2, + 36, + 98, + &I_Down_25x27, + &I_Down_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "CH-"); + + button_panel_add_label(button_panel, 6, 11, FontPrimary, "TV remote"); + button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol"); + button_panel_add_label(button_panel, 43, 64, FontSecondary, "Ch"); + + view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); + + infrared_show_loading_popup(infrared, true); + bool success = infrared_brute_force_calculate_messages(brute_force); + infrared_show_loading_popup(infrared, false); + + if(!success) { + scene_manager_previous_scene(infrared->scene_manager); + } +} + +bool infrared_scene_universal_tv_on_event(void* context, SceneManagerEvent event) { + return infrared_scene_universal_common_on_event(context, event); +} + +void infrared_scene_universal_tv_on_exit(void* context) { + infrared_scene_universal_common_on_exit(context); +} diff --git a/applications/infrared/views/infrared_debug_view.c b/applications/infrared/views/infrared_debug_view.c new file mode 100644 index 000000000..ab2c679c4 --- /dev/null +++ b/applications/infrared/views/infrared_debug_view.c @@ -0,0 +1,59 @@ +#include "infrared_debug_view.h" + +#include +#include + +#include +#include + +#define INFRARED_DEBUG_TEXT_LENGTH 64 + +struct InfraredDebugView { + View* view; +}; + +typedef struct { + char text[INFRARED_DEBUG_TEXT_LENGTH]; +} InfraredDebugViewModel; + +static void infrared_debug_view_draw_callback(Canvas* canvas, void* model) { + InfraredDebugViewModel* debug_view_model = model; + + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 64, 0, AlignCenter, AlignTop, "INFRARED monitor\n"); + canvas_set_font(canvas, FontKeyboard); + + if(strlen(debug_view_model->text)) { + elements_multiline_text_aligned( + canvas, 64, 43, AlignCenter, AlignCenter, debug_view_model->text); + } +} + +InfraredDebugView* infrared_debug_view_alloc() { + InfraredDebugView* debug_view = malloc(sizeof(InfraredDebugView)); + debug_view->view = view_alloc(); + view_allocate_model(debug_view->view, ViewModelTypeLocking, sizeof(InfraredDebugViewModel)); + view_set_draw_callback(debug_view->view, infrared_debug_view_draw_callback); + view_set_context(debug_view->view, debug_view); + return debug_view; +} +void infrared_debug_view_free(InfraredDebugView* debug_view) { + view_free(debug_view->view); + free(debug_view); +} + +View* infrared_debug_view_get_view(InfraredDebugView* debug_view) { + return debug_view->view; +} + +void infrared_debug_view_set_text(InfraredDebugView* debug_view, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + InfraredDebugViewModel* model = view_get_model(debug_view->view); + vsnprintf(model->text, INFRARED_DEBUG_TEXT_LENGTH, fmt, args); + view_commit_model(debug_view->view, true); + + va_end(args); +} diff --git a/applications/infrared/views/infrared_debug_view.h b/applications/infrared/views/infrared_debug_view.h new file mode 100644 index 000000000..722850f8d --- /dev/null +++ b/applications/infrared/views/infrared_debug_view.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +typedef struct InfraredDebugView InfraredDebugView; + +InfraredDebugView* infrared_debug_view_alloc(); +void infrared_debug_view_free(InfraredDebugView* debug_view); + +View* infrared_debug_view_get_view(InfraredDebugView* debug_view); +void infrared_debug_view_set_text(InfraredDebugView* debug_view, const char* fmt, ...); diff --git a/applications/infrared/view/infrared_progress_view.c b/applications/infrared/views/infrared_progress_view.c similarity index 100% rename from applications/infrared/view/infrared_progress_view.c rename to applications/infrared/views/infrared_progress_view.c diff --git a/applications/infrared/view/infrared_progress_view.h b/applications/infrared/views/infrared_progress_view.h similarity index 100% rename from applications/infrared/view/infrared_progress_view.h rename to applications/infrared/views/infrared_progress_view.h diff --git a/applications/infrared_monitor/infrared_monitor.c b/applications/infrared_monitor/infrared_monitor.c deleted file mode 100644 index 08691dfa2..000000000 --- a/applications/infrared_monitor/infrared_monitor.c +++ /dev/null @@ -1,140 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define INFRARED_TIMINGS_SIZE 700 - -typedef struct { - uint32_t timing_cnt; - struct { - uint8_t level; - uint32_t duration; - } timing[INFRARED_TIMINGS_SIZE]; -} InfraredDelaysArray; - -typedef struct { - char display_text[64]; - osMessageQueueId_t event_queue; - InfraredDelaysArray delays; - InfraredWorker* worker; - ViewPort* view_port; -} InfraredMonitor; - -void infrared_monitor_input_callback(InputEvent* input_event, void* ctx) { - furi_assert(ctx); - InfraredMonitor* infrared_monitor = (InfraredMonitor*)ctx; - - if((input_event->type == InputTypeShort) && (input_event->key == InputKeyBack)) { - osMessageQueuePut(infrared_monitor->event_queue, input_event, 0, 0); - } -} - -static void infrared_monitor_draw_callback(Canvas* canvas, void* ctx) { - furi_assert(canvas); - furi_assert(ctx); - InfraredMonitor* infrared_monitor = (InfraredMonitor*)ctx; - - canvas_clear(canvas); - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 64, 0, AlignCenter, AlignTop, "INFRARED monitor\n"); - canvas_set_font(canvas, FontKeyboard); - if(strlen(infrared_monitor->display_text)) { - elements_multiline_text_aligned( - canvas, 64, 43, AlignCenter, AlignCenter, infrared_monitor->display_text); - } -} - -static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { - furi_assert(context); - furi_assert(received_signal); - InfraredMonitor* infrared_monitor = context; - - if(infrared_worker_signal_is_decoded(received_signal)) { - const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal); - snprintf( - infrared_monitor->display_text, - sizeof(infrared_monitor->display_text), - "%s\nA:0x%0*lX\nC:0x%0*lX\n%s\n", - infrared_get_protocol_name(message->protocol), - ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), - message->address, - ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), - message->command, - message->repeat ? " R" : ""); - view_port_update(infrared_monitor->view_port); - printf( - "== %s, A:0x%0*lX, C:0x%0*lX%s ==\r\n", - infrared_get_protocol_name(message->protocol), - ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), - message->address, - ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), - message->command, - message->repeat ? " R" : ""); - } else { - const uint32_t* timings; - size_t timings_cnt; - infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt); - snprintf( - infrared_monitor->display_text, - sizeof(infrared_monitor->display_text), - "RAW\n%d samples\n", - timings_cnt); - view_port_update(infrared_monitor->view_port); - printf("RAW, %d samples:\r\n", timings_cnt); - for(size_t i = 0; i < timings_cnt; ++i) { - printf("%lu ", timings[i]); - } - printf("\r\n"); - } -} - -int32_t infrared_monitor_app(void* p) { - (void)p; - - InfraredMonitor* infrared_monitor = malloc(sizeof(InfraredMonitor)); - infrared_monitor->display_text[0] = 0; - infrared_monitor->event_queue = osMessageQueueNew(1, sizeof(InputEvent), NULL); - infrared_monitor->view_port = view_port_alloc(); - Gui* gui = furi_record_open("gui"); - - view_port_draw_callback_set( - infrared_monitor->view_port, infrared_monitor_draw_callback, infrared_monitor); - view_port_input_callback_set( - infrared_monitor->view_port, infrared_monitor_input_callback, infrared_monitor); - - gui_add_view_port(gui, infrared_monitor->view_port, GuiLayerFullscreen); - - infrared_monitor->worker = infrared_worker_alloc(); - infrared_worker_rx_start(infrared_monitor->worker); - infrared_worker_rx_set_received_signal_callback( - infrared_monitor->worker, signal_received_callback, infrared_monitor); - infrared_worker_rx_enable_blink_on_receiving(infrared_monitor->worker, true); - - while(1) { - InputEvent event; - if(osOK == osMessageQueueGet(infrared_monitor->event_queue, &event, NULL, 50)) { - if((event.type == InputTypeShort) && (event.key == InputKeyBack)) { - break; - } - } - } - - infrared_worker_rx_stop(infrared_monitor->worker); - infrared_worker_free(infrared_monitor->worker); - osMessageQueueDelete(infrared_monitor->event_queue); - view_port_enabled_set(infrared_monitor->view_port, false); - gui_remove_view_port(gui, infrared_monitor->view_port); - view_port_free(infrared_monitor->view_port); - furi_record_close("gui"); - free(infrared_monitor); - - return 0; -}