[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
This commit is contained in:
		
							parent
							
								
									839e52ac32
								
							
						
					
					
						commit
						a8acfcabb4
					
				| @ -29,7 +29,6 @@ extern int32_t display_test_app(void* p); | |||||||
| extern int32_t gpio_app(void* p); | extern int32_t gpio_app(void* p); | ||||||
| extern int32_t ibutton_app(void* p); | extern int32_t ibutton_app(void* p); | ||||||
| extern int32_t infrared_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 keypad_test_app(void* p); | ||||||
| extern int32_t lfrfid_app(void* p); | extern int32_t lfrfid_app(void* p); | ||||||
| extern int32_t lfrfid_debug_app(void* p); | extern int32_t lfrfid_debug_app(void* p); | ||||||
| @ -412,14 +411,6 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = { | |||||||
|      .flags = FlipperApplicationFlagDefault}, |      .flags = FlipperApplicationFlagDefault}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_INFRARED_MONITOR |  | ||||||
|     {.app = infrared_monitor_app, |  | ||||||
|      .name = "Infrared Monitor", |  | ||||||
|      .stack_size = 1024, |  | ||||||
|      .icon = NULL, |  | ||||||
|      .flags = FlipperApplicationFlagDefault}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_LF_RFID | #ifdef APP_LF_RFID | ||||||
|     {.app = lfrfid_debug_app, |     {.app = lfrfid_debug_app, | ||||||
|      .name = "LF-RFID Debug", |      .name = "LF-RFID Debug", | ||||||
|  | |||||||
| @ -51,7 +51,6 @@ APP_SNAKE_GAME = 1 | |||||||
| # Debug
 | # Debug
 | ||||||
| APP_ACCESSOR = 1 | APP_ACCESSOR = 1 | ||||||
| APP_BLINK = 1 | APP_BLINK = 1 | ||||||
| APP_INFRARED_MONITOR = 1 |  | ||||||
| APP_KEYPAD_TEST = 1 | APP_KEYPAD_TEST = 1 | ||||||
| APP_SD_TEST	= 1 | APP_SD_TEST	= 1 | ||||||
| APP_VIBRO_TEST = 1 | APP_VIBRO_TEST = 1 | ||||||
| @ -70,14 +69,6 @@ endif | |||||||
| # that will be shown in menu
 | # that will be shown in menu
 | ||||||
| # Prefix with APP_*
 | # Prefix with APP_*
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| APP_INFRARED_MONITOR	?= 0 |  | ||||||
| ifeq ($(APP_INFRARED_MONITOR), 1) |  | ||||||
| CFLAGS		+= -DAPP_INFRARED_MONITOR |  | ||||||
| SRV_GUI		= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| APP_UNIT_TESTS ?= 0 | APP_UNIT_TESTS ?= 0 | ||||||
| ifeq ($(APP_UNIT_TESTS), 1) | ifeq ($(APP_UNIT_TESTS), 1) | ||||||
| CFLAGS		+= -DAPP_UNIT_TESTS | CFLAGS		+= -DAPP_UNIT_TESTS | ||||||
|  | |||||||
| @ -20,10 +20,10 @@ typedef struct { | |||||||
| static void loading_draw_callback(Canvas* canvas, void* _model) { | static void loading_draw_callback(Canvas* canvas, void* _model) { | ||||||
|     LoadingModel* model = (LoadingModel*)_model; |     LoadingModel* model = (LoadingModel*)_model; | ||||||
| 
 | 
 | ||||||
|     uint8_t x = 7; |  | ||||||
|     uint8_t y = 40; |  | ||||||
|     uint8_t width = 49; |     uint8_t width = 49; | ||||||
|     uint8_t height = 47; |     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); |     elements_bold_rounded_frame(canvas, x, y, width, height); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -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) { | bool scene_manager_has_previous_scene(SceneManager* scene_manager, uint32_t scene_id) { | ||||||
|     furi_assert(scene_manager); |     furi_assert(scene_manager); | ||||||
|     bool scene_found = false; |     bool scene_found = false; | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ | |||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <stddef.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| 
 | 
 | ||||||
| @ -146,6 +147,19 @@ bool scene_manager_search_and_switch_to_previous_scene( | |||||||
|     SceneManager* scene_manager, |     SceneManager* scene_manager, | ||||||
|     uint32_t scene_id); |     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
 | /** Clear Scene stack and switch to another Scene
 | ||||||
|  * |  * | ||||||
|  * @param      scene_manager  SceneManager instance |  * @param      scene_manager  SceneManager instance | ||||||
|  | |||||||
| @ -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 <flipper_format/flipper_format.h> |  | ||||||
| #include <memory> |  | ||||||
| #include <string> |  | ||||||
| #include <furi_hal_infrared.h> |  | ||||||
| 
 |  | ||||||
| #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; |  | ||||||
| } |  | ||||||
| @ -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 <flipper_format/flipper_format.h> |  | ||||||
| #include <string> |  | ||||||
| 
 |  | ||||||
| /** 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); |  | ||||||
							
								
								
									
										397
									
								
								applications/infrared/infrared.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										397
									
								
								applications/infrared/infrared.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,397 @@ | |||||||
|  | #include "infrared_i.h" | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								applications/infrared/infrared.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								applications/infrared/infrared.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | typedef struct Infrared Infrared; | ||||||
| @ -1,250 +0,0 @@ | |||||||
| #include "infrared_app.h" |  | ||||||
| #include "m-string.h" |  | ||||||
| #include <infrared_worker.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include <gui/gui.h> |  | ||||||
| #include <input/input.h> |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <callback-connector.h> |  | ||||||
| 
 |  | ||||||
| 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<NotificationApp*>(furi_record_open("notification")); |  | ||||||
|     dialogs = static_cast<DialogsApp*>(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<Scene>& 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<InfraredApp*>(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<InfraredApp*>(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<InfraredApp*>(context); |  | ||||||
|     app->notify_blink_send(); |  | ||||||
| } |  | ||||||
| @ -1,326 +0,0 @@ | |||||||
| /**
 |  | ||||||
|   * @file infrared_app.h |  | ||||||
|   * Infrared: Main infrared application class |  | ||||||
|   */ |  | ||||||
| #pragma once |  | ||||||
| #include <map> |  | ||||||
| #include <infrared.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include <forward_list> |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <notification/notification_messages.h> |  | ||||||
| #include <dialogs/dialogs.h> |  | ||||||
| #include <infrared_worker.h> |  | ||||||
| 
 |  | ||||||
| #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<Scene>& 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<Scene> previous_scenes_list; |  | ||||||
|     /** Now acting scene */ |  | ||||||
|     Scene current_scene = Scene::Start; |  | ||||||
| 
 |  | ||||||
|     /** Map of index/scene objects */ |  | ||||||
|     std::map<Scene, InfraredAppScene*> 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()}, |  | ||||||
|     }; |  | ||||||
| }; |  | ||||||
| @ -1,93 +0,0 @@ | |||||||
| 
 |  | ||||||
| #include "helpers/infrared_parser.h" |  | ||||||
| #include "infrared_app_brute_force.h" |  | ||||||
| #include "infrared_app_signal.h" |  | ||||||
| #include <memory> |  | ||||||
| #include <m-string.h> |  | ||||||
| #include <furi.h> |  | ||||||
| 
 |  | ||||||
| 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<Storage*>(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<Storage*>(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; |  | ||||||
| } |  | ||||||
| @ -1,67 +0,0 @@ | |||||||
| /**
 |  | ||||||
|   * @file infrared_app_brute_force.h |  | ||||||
|   * Infrared: Brute Force class description |  | ||||||
|   */ |  | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <unordered_map> |  | ||||||
| #include <memory> |  | ||||||
| #include <flipper_format/flipper_format.h> |  | ||||||
| 
 |  | ||||||
| /** 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<std::string, Record> 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() { |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| @ -1,48 +0,0 @@ | |||||||
| /**
 |  | ||||||
|   * @file infrared_app_event.h |  | ||||||
|   * Infrared: Scene events description |  | ||||||
|   */ |  | ||||||
| #pragma once |  | ||||||
| #include <infrared.h> |  | ||||||
| #include <gui/modules/dialog_ex.h> |  | ||||||
| 
 |  | ||||||
| /** 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; |  | ||||||
| }; |  | ||||||
| @ -1,266 +0,0 @@ | |||||||
| #include "m-string.h" |  | ||||||
| #include "storage/filesystem_api_defines.h" |  | ||||||
| #include <flipper_format/flipper_format.h> |  | ||||||
| #include "infrared_app_remote_manager.h" |  | ||||||
| #include "infrared/helpers/infrared_parser.h" |  | ||||||
| #include "infrared/infrared_app_signal.h" |  | ||||||
| 
 |  | ||||||
| #include <utility> |  | ||||||
| 
 |  | ||||||
| #include <infrared.h> |  | ||||||
| #include <cstdio> |  | ||||||
| #include <furi.h> |  | ||||||
| #include <gui/modules/button_menu.h> |  | ||||||
| #include <storage/storage.h> |  | ||||||
| #include "infrared_app.h" |  | ||||||
| #include <toolbox/path.h> |  | ||||||
| 
 |  | ||||||
| static const char* default_remote_name = "remote"; |  | ||||||
| 
 |  | ||||||
| void InfraredAppRemoteManager::find_vacant_remote_name(string_t name, string_t path) { |  | ||||||
|     Storage* storage = static_cast<Storage*>(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<InfraredAppRemote>(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<std::string> InfraredAppRemoteManager::get_button_list(void) const { |  | ||||||
|     std::vector<std::string> 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<Storage*>(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<Storage*>(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<Storage*>(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<Storage*>(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<InfraredAppRemote>(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; |  | ||||||
| } |  | ||||||
| @ -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 <infrared_worker.h> |  | ||||||
| #include <infrared.h> |  | ||||||
| 
 |  | ||||||
| #include <cstdint> |  | ||||||
| #include <string> |  | ||||||
| #include <memory> |  | ||||||
| #include <vector> |  | ||||||
| 
 |  | ||||||
| /** 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<InfraredAppRemoteButton> 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<InfraredAppRemote> 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<std::string> 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); |  | ||||||
| }; |  | ||||||
| @ -1,116 +0,0 @@ | |||||||
| #include "infrared_app_signal.h" |  | ||||||
| #include <infrared_transmit.h> |  | ||||||
| 
 |  | ||||||
| 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); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,134 +0,0 @@ | |||||||
| /**
 |  | ||||||
|   * @file infrared_app_signal.h |  | ||||||
|   * Infrared: Signal class |  | ||||||
|   */ |  | ||||||
| #pragma once |  | ||||||
| #include <infrared_worker.h> |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <string> |  | ||||||
| #include <infrared.h> |  | ||||||
| 
 |  | ||||||
| /** 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; |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| @ -1,163 +0,0 @@ | |||||||
| #include <gui/modules/button_menu.h> |  | ||||||
| #include <gui/view_stack.h> |  | ||||||
| #include <gui/modules/loading.h> |  | ||||||
| #include <gui/modules/button_panel.h> |  | ||||||
| #include <gui/modules/dialog_ex.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include <callback-connector.h> |  | ||||||
| 
 |  | ||||||
| #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<Gui*>(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<uint32_t>(InfraredAppViewManager::ViewId::UniversalRemote)); |  | ||||||
|     view_dispatcher_remove_view( |  | ||||||
|         view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::ButtonMenu)); |  | ||||||
|     view_dispatcher_remove_view( |  | ||||||
|         view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::TextInput)); |  | ||||||
|     view_dispatcher_remove_view( |  | ||||||
|         view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::DialogEx)); |  | ||||||
|     view_dispatcher_remove_view( |  | ||||||
|         view_dispatcher, static_cast<uint32_t>(InfraredAppViewManager::ViewId::Submenu)); |  | ||||||
|     view_dispatcher_remove_view( |  | ||||||
|         view_dispatcher, static_cast<uint32_t>(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<uint32_t>(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<uint32_t>(view_type), view); |  | ||||||
| } |  | ||||||
| @ -1,164 +0,0 @@ | |||||||
| /**
 |  | ||||||
|   * @file infrared_app_view_manager.h |  | ||||||
|   * Infrared: Scene events description |  | ||||||
|   */ |  | ||||||
| #pragma once |  | ||||||
| #include <gui/modules/button_menu.h> |  | ||||||
| #include <gui/modules/text_input.h> |  | ||||||
| #include <gui/view_stack.h> |  | ||||||
| #include <gui/modules/button_panel.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include <gui/view_dispatcher.h> |  | ||||||
| #include <gui/modules/dialog_ex.h> |  | ||||||
| #include <gui/modules/submenu.h> |  | ||||||
| #include <gui/modules/popup.h> |  | ||||||
| #include <gui/modules/loading.h> |  | ||||||
| 
 |  | ||||||
| #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); |  | ||||||
| }; |  | ||||||
							
								
								
									
										153
									
								
								applications/infrared/infrared_brute_force.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								applications/infrared/infrared_brute_force.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,153 @@ | |||||||
|  | #include "infrared_brute_force.h" | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <m-dict.h> | ||||||
|  | #include <m-string.h> | ||||||
|  | #include <flipper_format/flipper_format.h> | ||||||
|  | 
 | ||||||
|  | #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); | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								applications/infrared/infrared_brute_force.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								applications/infrared/infrared_brute_force.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
| @ -1,16 +1,12 @@ | |||||||
| #include <furi_hal_delay.h> |  | ||||||
| #include <infrared.h> |  | ||||||
| #include <cli/cli.h> |  | ||||||
| #include <cmsis_os2.h> |  | ||||||
| #include <infrared_worker.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include <furi_hal_infrared.h> |  | ||||||
| #include <sstream> |  | ||||||
| #include <string> |  | ||||||
| #include <m-string.h> | #include <m-string.h> | ||||||
| #include <infrared_transmit.h> | #include <cli/cli.h> | ||||||
| #include <sys/types.h> | #include <infrared.h> | ||||||
| #include "../helpers/infrared_parser.h" | #include <infrared_worker.h> | ||||||
|  | #include <furi_hal_infrared.h> | ||||||
|  | 
 | ||||||
|  | #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_rx(Cli* cli, string_t args); | ||||||
| static void infrared_cli_start_ir_tx(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); |         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]; |     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) { |     if(parsed != 3) { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     message->protocol = infrared_get_protocol_by_name(protocol_name); |     message.repeat = false; | ||||||
|     message->repeat = false; |     infrared_signal_set_message(signal, &message); | ||||||
| 
 |     return infrared_signal_is_valid(signal); | ||||||
|     return infrared_parser_is_parsed_signal_valid(message); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool parse_signal_raw( | static bool infrared_cli_parse_raw(const char* str, InfraredSignal* signal) { | ||||||
|     const char* str, |     char frequency_str[INFRARED_CLI_BUF_SIZE]; | ||||||
|     uint32_t* timings, |     char duty_cycle_str[INFRARED_CLI_BUF_SIZE]; | ||||||
|     uint32_t* timings_cnt, |  | ||||||
|     float* duty_cycle, |  | ||||||
|     uint32_t* frequency) { |  | ||||||
|     char frequency_str[10]; |  | ||||||
|     char duty_cycle_str[10]; |  | ||||||
|     int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str); |     int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str); | ||||||
|     if(parsed != 2) return false; |  | ||||||
| 
 | 
 | ||||||
|     *frequency = atoi(frequency_str); |     if(parsed != 2) { | ||||||
|     *duty_cycle = (float)atoi(duty_cycle_str) / 100; |         return false; | ||||||
|     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; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     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) { | static void infrared_cli_start_ir_tx(Cli* cli, string_t args) { | ||||||
|     UNUSED(cli); |     UNUSED(cli); | ||||||
|     InfraredMessage message; |  | ||||||
|     const char* str = string_get_cstr(args); |     const char* str = string_get_cstr(args); | ||||||
|     uint32_t frequency; |     InfraredSignal* signal = infrared_signal_alloc(); | ||||||
|     float duty_cycle; |  | ||||||
|     uint32_t timings_cnt = MAX_TIMINGS_AMOUNT; |  | ||||||
|     uint32_t* timings = (uint32_t*)malloc(sizeof(uint32_t) * timings_cnt); |  | ||||||
| 
 | 
 | ||||||
|     if(parse_message(str, &message)) { |     bool success = infrared_cli_parse_message(str, signal) || infrared_cli_parse_raw(str, signal); | ||||||
|         infrared_send(&message, 1); |     if(success) { | ||||||
|     } else if(parse_signal_raw(str, timings, &timings_cnt, &duty_cycle, &frequency)) { |         infrared_signal_transmit(signal); | ||||||
|         infrared_send_raw_ext(timings, timings_cnt, true, frequency, duty_cycle); |  | ||||||
|     } else { |     } else { | ||||||
|         printf("Wrong arguments.\r\n"); |         printf("Wrong arguments.\r\n"); | ||||||
|         infrared_cli_print_usage(); |         infrared_cli_print_usage(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     free(timings); |     infrared_signal_free(signal); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) { | static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) { | ||||||
|     UNUSED(context); |     UNUSED(context); | ||||||
|     if(furi_hal_infrared_is_busy()) { |     if(furi_hal_infrared_is_busy()) { | ||||||
|         printf("INFRARED is busy. Exit."); |         printf("INFRARED is busy. Exiting."); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -189,8 +189,7 @@ static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) { | |||||||
|         infrared_cli_print_usage(); |         infrared_cli_print_usage(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | void infrared_on_system_start() { | ||||||
| extern "C" void infrared_on_system_start() { |  | ||||||
| #ifdef SRV_CLI | #ifdef SRV_CLI | ||||||
|     Cli* cli = (Cli*)furi_record_open("cli"); |     Cli* cli = (Cli*)furi_record_open("cli"); | ||||||
|     cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL); |     cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL); | ||||||
							
								
								
									
										51
									
								
								applications/infrared/infrared_custom_event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								applications/infrared/infrared_custom_event.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stddef.h> | ||||||
|  | 
 | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										132
									
								
								applications/infrared/infrared_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								applications/infrared/infrared_i.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,132 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/gui.h> | ||||||
|  | #include <gui/view.h> | ||||||
|  | #include <gui/view_stack.h> | ||||||
|  | #include <gui/view_dispatcher.h> | ||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | 
 | ||||||
|  | #include <gui/modules/popup.h> | ||||||
|  | #include <gui/modules/loading.h> | ||||||
|  | #include <gui/modules/submenu.h> | ||||||
|  | #include <gui/modules/dialog_ex.h> | ||||||
|  | #include <gui/modules/text_input.h> | ||||||
|  | #include <gui/modules/button_menu.h> | ||||||
|  | #include <gui/modules/button_panel.h> | ||||||
|  | 
 | ||||||
|  | #include <storage/storage.h> | ||||||
|  | #include <dialogs/dialogs.h> | ||||||
|  | 
 | ||||||
|  | #include <notification/notification_messages.h> | ||||||
|  | 
 | ||||||
|  | #include <infrared_worker.h> | ||||||
|  | 
 | ||||||
|  | #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); | ||||||
							
								
								
									
										176
									
								
								applications/infrared/infrared_remote.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								applications/infrared/infrared_remote.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,176 @@ | |||||||
|  | #include "infrared_remote.h" | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <m-string.h> | ||||||
|  | #include <m-array.h> | ||||||
|  | #include <toolbox/path.h> | ||||||
|  | #include <storage/storage.h> | ||||||
|  | #include <furi/common_defines.h> | ||||||
|  | 
 | ||||||
|  | #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); | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								applications/infrared/infrared_remote.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								applications/infrared/infrared_remote.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | #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); | ||||||
							
								
								
									
										38
									
								
								applications/infrared/infrared_remote_button.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								applications/infrared/infrared_remote_button.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | #include "infrared_remote_button.h" | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <m-string.h> | ||||||
|  | 
 | ||||||
|  | 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; | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								applications/infrared/infrared_remote_button.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								applications/infrared/infrared_remote_button.h
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||||
| @ -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; |  | ||||||
| } |  | ||||||
							
								
								
									
										264
									
								
								applications/infrared/infrared_signal.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								applications/infrared/infrared_signal.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,264 @@ | |||||||
|  | #include "infrared_signal.h" | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <furi/check.h> | ||||||
|  | #include <infrared_worker.h> | ||||||
|  | #include <infrared_transmit.h> | ||||||
|  | 
 | ||||||
|  | #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); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										41
									
								
								applications/infrared/infrared_signal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								applications/infrared/infrared_signal.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | #include <infrared.h> | ||||||
|  | #include <flipper_format/flipper_format.h> | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
| @ -1,305 +0,0 @@ | |||||||
| /**
 |  | ||||||
|  * @file infrared_app_scene.h |  | ||||||
|  * Infrared: Application scenes |  | ||||||
|  */ |  | ||||||
| #pragma once |  | ||||||
| #include "../infrared_app_event.h" |  | ||||||
| #include <furi_hal_infrared.h> |  | ||||||
| #include "infrared.h" |  | ||||||
| #include <vector> |  | ||||||
| #include <string> |  | ||||||
| #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<std::string> 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<std::string> 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<std::string> 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() { |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| @ -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 <string> |  | ||||||
| 
 |  | ||||||
| static void dialog_result_callback(DialogExResult result, void* context) { |  | ||||||
|     auto app = static_cast<InfraredApp*>(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*) { |  | ||||||
| } |  | ||||||
| @ -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<InfraredApp*>(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); |  | ||||||
| } |  | ||||||
| @ -1,100 +0,0 @@ | |||||||
| #include "../infrared_app.h" |  | ||||||
| #include "infrared.h" |  | ||||||
| #include "infrared/scene/infrared_app_scene.h" |  | ||||||
| #include <string> |  | ||||||
| 
 |  | ||||||
| static void dialog_result_callback(DialogExResult result, void* context) { |  | ||||||
|     auto app = static_cast<InfraredApp*>(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*) { |  | ||||||
| } |  | ||||||
| @ -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); |  | ||||||
| } |  | ||||||
| @ -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<InfraredApp*>(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); |  | ||||||
| } |  | ||||||
| @ -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); |  | ||||||
| } |  | ||||||
| @ -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*) { |  | ||||||
| } |  | ||||||
| @ -1,76 +0,0 @@ | |||||||
| #include "../infrared_app.h" |  | ||||||
| #include "../infrared_app_event.h" |  | ||||||
| #include "infrared.h" |  | ||||||
| #include <infrared_worker.h> |  | ||||||
| 
 |  | ||||||
| static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     furi_assert(received_signal); |  | ||||||
| 
 |  | ||||||
|     InfraredApp* app = static_cast<InfraredApp*>(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); |  | ||||||
| } |  | ||||||
| @ -1,41 +0,0 @@ | |||||||
| #include "../infrared_app.h" |  | ||||||
| #include <dolphin/dolphin.h> |  | ||||||
| 
 |  | ||||||
| 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); |  | ||||||
| } |  | ||||||
| @ -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*) { |  | ||||||
| } |  | ||||||
| @ -1,142 +0,0 @@ | |||||||
| #include <gui/modules/dialog_ex.h> |  | ||||||
| #include <memory> |  | ||||||
| #include <dolphin/dolphin.h> |  | ||||||
| 
 |  | ||||||
| #include "../infrared_app.h" |  | ||||||
| #include "infrared.h" |  | ||||||
| 
 |  | ||||||
| static void dialog_result_callback(DialogExResult result, void* context) { |  | ||||||
|     auto app = static_cast<InfraredApp*>(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); |  | ||||||
| } |  | ||||||
| @ -1,131 +0,0 @@ | |||||||
| #include <gui/modules/button_menu.h> |  | ||||||
| #include <input/input.h> |  | ||||||
| #include <infrared_worker.h> |  | ||||||
| #include <dolphin/dolphin.h> |  | ||||||
| #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<InfraredApp*>(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); |  | ||||||
| } |  | ||||||
| @ -1,45 +0,0 @@ | |||||||
| #include "../infrared_app.h" |  | ||||||
| #include "assets_icons.h" |  | ||||||
| #include "infrared/infrared_app_event.h" |  | ||||||
| #include <text_store.h> |  | ||||||
| 
 |  | ||||||
| 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*) { |  | ||||||
| } |  | ||||||
| @ -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<InfraredApp*>(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); |  | ||||||
| } |  | ||||||
| @ -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<InfraredApp*>(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); |  | ||||||
| } |  | ||||||
| @ -1,107 +0,0 @@ | |||||||
| #include <dolphin/dolphin.h> |  | ||||||
| #include <gui/modules/button_menu.h> |  | ||||||
| #include <gui/modules/button_panel.h> |  | ||||||
| #include <gui/view.h> |  | ||||||
| #include <gui/view_stack.h> |  | ||||||
| 
 |  | ||||||
| #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<InfraredApp*>(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<InfraredApp*>(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); |  | ||||||
| } |  | ||||||
| @ -1,123 +0,0 @@ | |||||||
| #include <stdint.h> |  | ||||||
| #include <gui/modules/loading.h> |  | ||||||
| #include <gui/view_stack.h> |  | ||||||
| #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(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -0,0 +1,92 @@ | |||||||
|  | #include "../../infrared_i.h" | ||||||
|  | 
 | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
|  | } | ||||||
| @ -0,0 +1,8 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
							
								
								
									
										30
									
								
								applications/infrared/scenes/infrared_scene.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								applications/infrared/scenes/infrared_scene.c
									
									
									
									
									
										Normal file
									
								
							| @ -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, | ||||||
|  | }; | ||||||
							
								
								
									
										29
									
								
								applications/infrared/scenes/infrared_scene.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								applications/infrared/scenes/infrared_scene.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | 
 | ||||||
|  | // 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 | ||||||
							
								
								
									
										59
									
								
								applications/infrared/scenes/infrared_scene_ask_back.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								applications/infrared/scenes/infrared_scene_ask_back.c
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||||
|  | } | ||||||
							
								
								
									
										17
									
								
								applications/infrared/scenes/infrared_scene_config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								applications/infrared/scenes/infrared_scene_config.h
									
									
									
									
									
										Normal file
									
								
							| @ -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) | ||||||
							
								
								
									
										68
									
								
								applications/infrared/scenes/infrared_scene_debug.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								applications/infrared/scenes/infrared_scene_debug.c
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||||
|  | } | ||||||
							
								
								
									
										101
									
								
								applications/infrared/scenes/infrared_scene_edit.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								applications/infrared/scenes/infrared_scene_edit.c
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
							
								
								
									
										112
									
								
								applications/infrared/scenes/infrared_scene_edit_delete.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								applications/infrared/scenes/infrared_scene_edit_delete.c
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
							
								
								
									
										107
									
								
								applications/infrared/scenes/infrared_scene_edit_rename.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								applications/infrared/scenes/infrared_scene_edit_rename.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,107 @@ | |||||||
|  | #include "../infrared_i.h" | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | #include <toolbox/path.h> | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								applications/infrared/scenes/infrared_scene_learn.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								applications/infrared/scenes/infrared_scene_learn.c
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||||
|  | } | ||||||
							
								
								
									
										44
									
								
								applications/infrared/scenes/infrared_scene_learn_done.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								applications/infrared/scenes/infrared_scene_learn_done.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | |||||||
|  | #include "../infrared_i.h" | ||||||
|  | 
 | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
							
								
								
									
										131
									
								
								applications/infrared/scenes/infrared_scene_learn_success.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								applications/infrared/scenes/infrared_scene_learn_success.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,131 @@ | |||||||
|  | #include "../infrared_i.h" | ||||||
|  | 
 | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
|  | } | ||||||
							
								
								
									
										119
									
								
								applications/infrared/scenes/infrared_scene_remote.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								applications/infrared/scenes/infrared_scene_remote.c
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||||
|  | } | ||||||
							
								
								
									
										44
									
								
								applications/infrared/scenes/infrared_scene_remote_list.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								applications/infrared/scenes/infrared_scene_remote_list.c
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||||
|  | } | ||||||
							
								
								
									
										83
									
								
								applications/infrared/scenes/infrared_scene_start.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								applications/infrared/scenes/infrared_scene_start.c
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								applications/infrared/scenes/infrared_scene_universal.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								applications/infrared/scenes/infrared_scene_universal.c
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||||
|  | } | ||||||
							
								
								
									
										111
									
								
								applications/infrared/scenes/infrared_scene_universal_tv.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								applications/infrared/scenes/infrared_scene_universal_tv.c
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||||
|  | } | ||||||
							
								
								
									
										59
									
								
								applications/infrared/views/infrared_debug_view.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								applications/infrared/views/infrared_debug_view.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | |||||||
|  | #include "infrared_debug_view.h" | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #include <gui/canvas.h> | ||||||
|  | #include <gui/elements.h> | ||||||
|  | 
 | ||||||
|  | #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); | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								applications/infrared/views/infrared_debug_view.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								applications/infrared/views/infrared_debug_view.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | 
 | ||||||
|  | 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, ...); | ||||||
| @ -1,140 +0,0 @@ | |||||||
| #include <gui/canvas.h> |  | ||||||
| #include <input/input.h> |  | ||||||
| #include <infrared.h> |  | ||||||
| #include <infrared_worker.h> |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <furi.h> |  | ||||||
| #include <furi_hal_infrared.h> |  | ||||||
| #include <furi_hal.h> |  | ||||||
| #include <gui/view_port.h> |  | ||||||
| #include <gui/gui.h> |  | ||||||
| #include <gui/elements.h> |  | ||||||
| 
 |  | ||||||
| #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; |  | ||||||
| } |  | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Georgii Surkov
						Georgii Surkov