Merge branch 'release-candidate' into release
This commit is contained in:
		
						commit
						d21e5bd56e
					
				
							
								
								
									
										5
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @ -76,8 +76,8 @@ jobs: | ||||
|         with: | ||||
|           run: | | ||||
|             set -e | ||||
|             make -C assets clean | ||||
|             make -C assets | ||||
|             make assets_rebuild assets_manifest | ||||
|             git diff --quiet || ( echo "Assets recompilation required."; exit 255 ) | ||||
| 
 | ||||
|       - name: 'Build the firmware in docker' | ||||
|         uses: ./.github/actions/docker | ||||
| @ -117,7 +117,6 @@ jobs: | ||||
|       - name: 'Bundle resources' | ||||
|         if: ${{ !github.event.pull_request.head.repo.fork }} | ||||
|         run: | | ||||
|           ./scripts/assets.py manifest assets/resources | ||||
|           tar czpf artifacts/flipper-z-any-resources-${{steps.names.outputs.suffix}}.tgz -C assets resources | ||||
| 
 | ||||
|       - name: 'Bundle core2 firmware' | ||||
|  | ||||
							
								
								
									
										25
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								Makefile
									
									
									
									
									
								
							| @ -51,6 +51,10 @@ flash: firmware_flash | ||||
| debug: | ||||
| 	@$(MAKE) -C firmware -j$(NPROCS) debug | ||||
| 
 | ||||
| .PHONY: debug_other | ||||
| debug_other: | ||||
| 	@$(MAKE) -C firmware -j$(NPROCS) debug_other | ||||
| 
 | ||||
| .PHONY: blackmagic | ||||
| blackmagic: | ||||
| 	@$(MAKE) -C firmware -j$(NPROCS) blackmagic | ||||
| @ -75,7 +79,6 @@ ifeq ($(FORCE), 1) | ||||
| endif | ||||
| 	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) flash | ||||
| 
 | ||||
| 
 | ||||
| .PHONY: updater | ||||
| updater: | ||||
| 	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) RAM_EXEC=1 all | ||||
| @ -88,10 +91,22 @@ updater_clean: | ||||
| updater_debug: | ||||
| 	@$(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) RAM_EXEC=1 debug | ||||
| 
 | ||||
| .PHONY: updater_package | ||||
| updater_package: firmware_all updater | ||||
| .PHONY: updater_package_bin | ||||
| updater_package_bin: firmware_all updater | ||||
| 	@$(PROJECT_ROOT)/scripts/dist.py copy -t $(TARGET) -p firmware updater -s $(DIST_SUFFIX) --bundlever "$(VERSION_STRING)" | ||||
| 
 | ||||
| .PHONY: updater_package | ||||
| updater_package: firmware_all updater assets_manifest | ||||
| 	@$(PROJECT_ROOT)/scripts/dist.py copy -t $(TARGET) -p firmware updater -s $(DIST_SUFFIX) -r $(PROJECT_ROOT)/assets/resources --bundlever "$(VERSION_STRING)" | ||||
| 
 | ||||
| .PHONY: assets_manifest | ||||
| assets_manifest: | ||||
| 	@$(MAKE) -C $(PROJECT_ROOT)/assets manifest | ||||
| 
 | ||||
| .PHONY: assets_rebuild | ||||
| assets_rebuild: | ||||
| 	@$(MAKE) -C $(PROJECT_ROOT)/assets clean all | ||||
| 
 | ||||
| .PHONY: flash_radio | ||||
| flash_radio: | ||||
| 	@$(PROJECT_ROOT)/scripts/flash.py core2radio 0x080D7000 $(COPRO_DIR)/stm32wb5x_BLE_Stack_light_fw.bin | ||||
| @ -110,8 +125,8 @@ flash_radio_fus: | ||||
| 
 | ||||
| .PHONY: flash_radio_fus_please_i_m_not_going_to_complain | ||||
| flash_radio_fus_please_i_m_not_going_to_complain: | ||||
| 	@$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw_for_fus_0_5_3.bin | ||||
| 	@$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw.bin | ||||
| 	@$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw_for_fus_0_5_3.bin | ||||
| 	@$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw.bin | ||||
| 	@$(PROJECT_ROOT)/scripts/ob.py set | ||||
| 
 | ||||
| .PHONY: lint | ||||
|  | ||||
| @ -43,6 +43,7 @@ extern int32_t usb_test_app(void* p); | ||||
| extern int32_t vibro_test_app(void* p); | ||||
| extern int32_t bt_hid_app(void* p); | ||||
| extern int32_t battery_test_app(void* p); | ||||
| extern int32_t text_box_test_app(void* p); | ||||
| 
 | ||||
| // Plugins
 | ||||
| extern int32_t music_player_app(void* p); | ||||
| @ -304,6 +305,10 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = { | ||||
| #ifdef APP_BATTERY_TEST | ||||
|     {.app = battery_test_app, .name = "Battery Test", .stack_size = 1024, .icon = NULL}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_TEXT_BOX_TEST | ||||
|     {.app = text_box_test_app, .name = "Text Box Test", .stack_size = 1024, .icon = NULL}, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| const size_t FLIPPER_DEBUG_APPS_COUNT = COUNT_OF(FLIPPER_DEBUG_APPS); | ||||
|  | ||||
| @ -165,6 +165,12 @@ CFLAGS		+= -DAPP_DISPLAY_TEST | ||||
| SRV_GUI = 1 | ||||
| endif | ||||
| 
 | ||||
| APP_TEXT_BOX_TEST ?= 0 | ||||
| ifeq ($(APP_TEXT_BOX_TEST), 1) | ||||
| CFLAGS		+= -DAPP_TEXT_BOX_TEST | ||||
| SRV_GUI = 1 | ||||
| endif | ||||
| 
 | ||||
| APP_BATTERY_TEST ?= 0 | ||||
| ifeq ($(APP_BATTERY_TEST), 1) | ||||
| CFLAGS		+= -DAPP_BATTERY_TEST | ||||
|  | ||||
| @ -391,14 +391,7 @@ void archive_enter_dir(ArchiveBrowserView* browser, string_t name) { | ||||
|     furi_assert(browser); | ||||
|     furi_assert(name); | ||||
| 
 | ||||
|     uint8_t browser_depth = 0; | ||||
|     with_view_model( | ||||
|         browser->view, (ArchiveBrowserViewModel * model) { | ||||
|             browser_depth = idx_last_array_size(model->idx_last); | ||||
|             return false; | ||||
|         }); | ||||
| 
 | ||||
|     if(browser_depth > BROWSER_DEPTH_MAX) { | ||||
|     if(string_size(name) >= (MAX_NAME_LEN - 1)) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -4,7 +4,6 @@ | ||||
| 
 | ||||
| #define TAB_RIGHT InputKeyRight //default tab swith direction
 | ||||
| #define FILE_LIST_BUF_LEN 100 | ||||
| #define BROWSER_DEPTH_MAX 8 | ||||
| 
 | ||||
| static const char* tab_default_paths[] = { | ||||
|     [ArchiveTabFavorites] = "/any/favorites", | ||||
|  | ||||
| @ -20,7 +20,7 @@ void archive_scene_delete_on_enter(void* context) { | ||||
|     ArchiveApp* app = (ArchiveApp*)context; | ||||
| 
 | ||||
|     widget_add_button_element( | ||||
|         app->widget, GuiButtonTypeLeft, "Back", archive_scene_delete_widget_callback, app); | ||||
|         app->widget, GuiButtonTypeLeft, "Cancel", archive_scene_delete_widget_callback, app); | ||||
|     widget_add_button_element( | ||||
|         app->widget, GuiButtonTypeRight, "Delete", archive_scene_delete_widget_callback, app); | ||||
| 
 | ||||
| @ -33,7 +33,8 @@ void archive_scene_delete_on_enter(void* context) { | ||||
| 
 | ||||
|     char delete_str[64]; | ||||
|     snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", name); | ||||
|     widget_add_text_box_element(app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str); | ||||
|     widget_add_text_box_element( | ||||
|         app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str, false); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, ArchiveViewWidget); | ||||
| } | ||||
|  | ||||
| @ -189,6 +189,8 @@ static void bt_cli_print_usage() { | ||||
| } | ||||
| 
 | ||||
| static void bt_cli(Cli* cli, string_t args, void* context) { | ||||
|     furi_record_open("bt"); | ||||
| 
 | ||||
|     string_t cmd; | ||||
|     string_init(cmd); | ||||
|     BtSettings bt_settings; | ||||
| @ -235,14 +237,13 @@ static void bt_cli(Cli* cli, string_t args, void* context) { | ||||
|     } | ||||
| 
 | ||||
|     string_clear(cmd); | ||||
|     furi_record_close("bt"); | ||||
| } | ||||
| 
 | ||||
| void bt_on_system_start() { | ||||
| #ifdef SRV_CLI | ||||
|     Cli* cli = furi_record_open("cli"); | ||||
|     furi_record_open("bt"); | ||||
|     cli_add_command(cli, "bt", CliCommandFlagDefault, bt_cli, NULL); | ||||
|     furi_record_close("bt"); | ||||
|     furi_record_close("cli"); | ||||
| #else | ||||
|     UNUSED(bt_cli); | ||||
|  | ||||
| @ -320,7 +320,7 @@ int32_t bt_srv() { | ||||
|     Bt* bt = bt_alloc(); | ||||
| 
 | ||||
|     // Read keys
 | ||||
|     if(!bt_load_key_storage(bt)) { | ||||
|     if(!bt_keys_storage_load(bt)) { | ||||
|         FURI_LOG_W(TAG, "Failed to load bonding keys"); | ||||
|     } | ||||
| 
 | ||||
| @ -365,11 +365,11 @@ int32_t bt_srv() { | ||||
|             // Display PIN code
 | ||||
|             bt_pin_code_show(bt, message.data.pin_code); | ||||
|         } else if(message.type == BtMessageTypeKeysStorageUpdated) { | ||||
|             bt_save_key_storage(bt); | ||||
|             bt_keys_storage_save(bt); | ||||
|         } else if(message.type == BtMessageTypeSetProfile) { | ||||
|             bt_change_profile(bt, &message); | ||||
|         } else if(message.type == BtMessageTypeForgetBondedDevices) { | ||||
|             bt_delete_key_storage(bt); | ||||
|             bt_keys_storage_delete(bt); | ||||
|         } | ||||
|     } | ||||
|     return 0; | ||||
|  | ||||
| @ -1,46 +1,47 @@ | ||||
| #include "bt_keys_storage.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <file_worker.h> | ||||
| #include <lib/toolbox/saved_struct.h> | ||||
| 
 | ||||
| #define BT_KEYS_STORAGE_TAG "bt keys storage" | ||||
| #define BT_KEYS_STORAGE_PATH "/int/bt.keys" | ||||
| #define BT_KEYS_STORAGE_VERSION (0) | ||||
| #define BT_KEYS_STORAGE_MAGIC (0x18) | ||||
| 
 | ||||
| bool bt_load_key_storage(Bt* bt) { | ||||
| bool bt_keys_storage_load(Bt* bt) { | ||||
|     furi_assert(bt); | ||||
| 
 | ||||
|     bool file_loaded = false; | ||||
|     furi_hal_bt_get_key_storage_buff(&bt->bt_keys_addr_start, &bt->bt_keys_size); | ||||
| 
 | ||||
|     FileWorker* file_worker = file_worker_alloc(true); | ||||
|     if(file_worker_open(file_worker, BT_KEYS_STORAGE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||
|         furi_hal_bt_nvm_sram_sem_acquire(); | ||||
|         if(file_worker_read(file_worker, bt->bt_keys_addr_start, bt->bt_keys_size)) { | ||||
|             file_loaded = true; | ||||
|         } | ||||
|         furi_hal_bt_nvm_sram_sem_release(); | ||||
|     } | ||||
|     file_worker_free(file_worker); | ||||
|     furi_hal_bt_get_key_storage_buff(&bt->bt_keys_addr_start, &bt->bt_keys_size); | ||||
|     furi_hal_bt_nvm_sram_sem_acquire(); | ||||
|     file_loaded = saved_struct_load( | ||||
|         BT_KEYS_STORAGE_PATH, | ||||
|         bt->bt_keys_addr_start, | ||||
|         bt->bt_keys_size, | ||||
|         BT_KEYS_STORAGE_MAGIC, | ||||
|         BT_KEYS_STORAGE_VERSION); | ||||
|     furi_hal_bt_nvm_sram_sem_release(); | ||||
| 
 | ||||
|     return file_loaded; | ||||
| } | ||||
| 
 | ||||
| bool bt_save_key_storage(Bt* bt) { | ||||
| bool bt_keys_storage_save(Bt* bt) { | ||||
|     furi_assert(bt); | ||||
|     furi_assert(bt->bt_keys_addr_start); | ||||
| 
 | ||||
|     bool file_saved = false; | ||||
|     FileWorker* file_worker = file_worker_alloc(true); | ||||
|     if(file_worker_open(file_worker, BT_KEYS_STORAGE_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) { | ||||
|         furi_hal_bt_nvm_sram_sem_acquire(); | ||||
|         if(file_worker_write(file_worker, bt->bt_keys_addr_start, bt->bt_keys_size)) { | ||||
|             file_saved = true; | ||||
|         } | ||||
|         furi_hal_bt_nvm_sram_sem_release(); | ||||
|     } | ||||
|     file_worker_free(file_worker); | ||||
| 
 | ||||
|     furi_hal_bt_nvm_sram_sem_acquire(); | ||||
|     file_saved = saved_struct_save( | ||||
|         BT_KEYS_STORAGE_PATH, | ||||
|         bt->bt_keys_addr_start, | ||||
|         bt->bt_keys_size, | ||||
|         BT_KEYS_STORAGE_MAGIC, | ||||
|         BT_KEYS_STORAGE_VERSION); | ||||
|     furi_hal_bt_nvm_sram_sem_release(); | ||||
| 
 | ||||
|     return file_saved; | ||||
| } | ||||
| 
 | ||||
| bool bt_delete_key_storage(Bt* bt) { | ||||
| bool bt_keys_storage_delete(Bt* bt) { | ||||
|     furi_assert(bt); | ||||
|     bool delete_succeed = false; | ||||
|     bool bt_is_active = furi_hal_bt_is_active(); | ||||
|  | ||||
| @ -2,8 +2,8 @@ | ||||
| 
 | ||||
| #include "bt_i.h" | ||||
| 
 | ||||
| bool bt_load_key_storage(Bt* bt); | ||||
| bool bt_keys_storage_load(Bt* bt); | ||||
| 
 | ||||
| bool bt_save_key_storage(Bt* bt); | ||||
| bool bt_keys_storage_save(Bt* bt); | ||||
| 
 | ||||
| bool bt_delete_key_storage(Bt* bt); | ||||
| bool bt_keys_storage_delete(Bt* bt); | ||||
|  | ||||
| @ -1,50 +1,22 @@ | ||||
| #include "bt_settings.h" | ||||
| #include <furi.h> | ||||
| #include <file_worker.h> | ||||
| 
 | ||||
| #define TAG "BtSettings" | ||||
| #include <furi.h> | ||||
| #include <lib/toolbox/saved_struct.h> | ||||
| 
 | ||||
| #define BT_SETTINGS_PATH "/int/bt.settings" | ||||
| #define BT_SETTINGS_VERSION (0) | ||||
| #define BT_SETTINGS_MAGIC (0x19) | ||||
| 
 | ||||
| bool bt_settings_load(BtSettings* bt_settings) { | ||||
|     furi_assert(bt_settings); | ||||
|     bool file_loaded = false; | ||||
|     BtSettings settings = {}; | ||||
| 
 | ||||
|     FURI_LOG_I(TAG, "Loading settings from \"%s\"", BT_SETTINGS_PATH); | ||||
|     FileWorker* file_worker = file_worker_alloc(true); | ||||
|     if(file_worker_open(file_worker, BT_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||
|         if(file_worker_read(file_worker, &settings, sizeof(settings))) { | ||||
|             file_loaded = true; | ||||
|         } | ||||
|     } | ||||
|     file_worker_free(file_worker); | ||||
| 
 | ||||
|     if(file_loaded) { | ||||
|         FURI_LOG_I(TAG, "Settings load success"); | ||||
|         if(settings.version != BT_SETTINGS_VERSION) { | ||||
|             FURI_LOG_E(TAG, "Settings version mismatch"); | ||||
|         } else { | ||||
|             osKernelLock(); | ||||
|             *bt_settings = settings; | ||||
|             osKernelUnlock(); | ||||
|         } | ||||
|     } else { | ||||
|         FURI_LOG_E(TAG, "Settings load failed"); | ||||
|     } | ||||
|     return file_loaded; | ||||
|     return saved_struct_load( | ||||
|         BT_SETTINGS_PATH, bt_settings, sizeof(BtSettings), BT_SETTINGS_MAGIC, BT_SETTINGS_VERSION); | ||||
| } | ||||
| 
 | ||||
| bool bt_settings_save(BtSettings* bt_settings) { | ||||
|     furi_assert(bt_settings); | ||||
|     bool result = false; | ||||
| 
 | ||||
|     FileWorker* file_worker = file_worker_alloc(true); | ||||
|     if(file_worker_open(file_worker, BT_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) { | ||||
|         if(file_worker_write(file_worker, bt_settings, sizeof(BtSettings))) { | ||||
|             FURI_LOG_I(TAG, "Settings saved to \"%s\"", BT_SETTINGS_PATH); | ||||
|             result = true; | ||||
|         } | ||||
|     } | ||||
|     file_worker_free(file_worker); | ||||
|     return result; | ||||
|     return saved_struct_save( | ||||
|         BT_SETTINGS_PATH, bt_settings, sizeof(BtSettings), BT_SETTINGS_MAGIC, BT_SETTINGS_VERSION); | ||||
| } | ||||
|  | ||||
| @ -3,10 +3,7 @@ | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| #define BT_SETTINGS_VERSION (0) | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint8_t version; | ||||
|     bool enabled; | ||||
| } BtSettings; | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										126
									
								
								applications/debug_tools/text_box_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								applications/debug_tools/text_box_test.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,126 @@ | ||||
| #include <furi.h> | ||||
| #include <gui/gui.h> | ||||
| #include <input/input.h> | ||||
| #include <gui/elements.h> | ||||
| 
 | ||||
| #define TAG "TextBoxTest" | ||||
| 
 | ||||
| static void text_box_center_top_secondary_128x22(Canvas* canvas) { | ||||
|     canvas_draw_frame(canvas, 0, 0, 128, 22); | ||||
|     elements_text_box(canvas, 0, 0, 128, 22, AlignCenter, AlignTop, "secondary font test", false); | ||||
| } | ||||
| 
 | ||||
| static void text_box_right_bottom_bold_128x22(Canvas* canvas) { | ||||
|     canvas_draw_frame(canvas, 0, 0, 128, 22); | ||||
|     elements_text_box( | ||||
|         canvas, 0, 0, 128, 22, AlignRight, AlignBottom, "\e#Bold font test\e#", false); | ||||
| } | ||||
| 
 | ||||
| static void text_box_left_center_mixed_80x50(Canvas* canvas) { | ||||
|     canvas_draw_frame(canvas, 0, 0, 80, 50); | ||||
|     elements_text_box( | ||||
|         canvas, | ||||
|         0, | ||||
|         0, | ||||
|         80, | ||||
|         50, | ||||
|         AlignLeft, | ||||
|         AlignCenter, | ||||
|         "\e#Never\e# gonna give you up\n\e!Never\e! gonna let you down", | ||||
|         false); | ||||
| } | ||||
| 
 | ||||
| static void text_box_center_center_secondary_110x44(Canvas* canvas) { | ||||
|     canvas_draw_frame(canvas, 4, 20, 110, 30); | ||||
|     elements_text_box( | ||||
|         canvas, | ||||
|         4, | ||||
|         20, | ||||
|         110, | ||||
|         30, | ||||
|         AlignCenter, | ||||
|         AlignCenter, | ||||
|         "Loooooooooooooo0000000ooong file name from happy 100500 Flipper 0wners", | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| static void (*text_box_test_render[])(Canvas* canvas) = { | ||||
|     text_box_center_top_secondary_128x22, | ||||
|     text_box_right_bottom_bold_128x22, | ||||
|     text_box_left_center_mixed_80x50, | ||||
|     text_box_center_center_secondary_110x44, | ||||
| }; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint32_t idx; | ||||
| } TextBoxTestState; | ||||
| 
 | ||||
| static void text_box_test_render_callback(Canvas* canvas, void* ctx) { | ||||
|     TextBoxTestState* state = acquire_mutex((ValueMutex*)ctx, 25); | ||||
|     canvas_clear(canvas); | ||||
| 
 | ||||
|     text_box_test_render[state->idx](canvas); | ||||
| 
 | ||||
|     release_mutex((ValueMutex*)ctx, state); | ||||
| } | ||||
| 
 | ||||
| static void text_box_test_input_callback(InputEvent* input_event, void* ctx) { | ||||
|     osMessageQueueId_t event_queue = ctx; | ||||
|     osMessageQueuePut(event_queue, input_event, 0, osWaitForever); | ||||
| } | ||||
| 
 | ||||
| int32_t text_box_test_app(void* p) { | ||||
|     osMessageQueueId_t event_queue = osMessageQueueNew(32, sizeof(InputEvent), NULL); | ||||
|     furi_check(event_queue); | ||||
| 
 | ||||
|     TextBoxTestState _state = {.idx = 0}; | ||||
| 
 | ||||
|     ValueMutex state_mutex; | ||||
|     if(!init_mutex(&state_mutex, &_state, sizeof(TextBoxTestState))) { | ||||
|         FURI_LOG_E(TAG, "Cannot create mutex"); | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     ViewPort* view_port = view_port_alloc(); | ||||
| 
 | ||||
|     view_port_draw_callback_set(view_port, text_box_test_render_callback, &state_mutex); | ||||
|     view_port_input_callback_set(view_port, text_box_test_input_callback, event_queue); | ||||
| 
 | ||||
|     // Open GUI and register view_port
 | ||||
|     Gui* gui = furi_record_open("gui"); | ||||
|     gui_add_view_port(gui, view_port, GuiLayerFullscreen); | ||||
| 
 | ||||
|     uint32_t test_renders_num = SIZEOF_ARRAY(text_box_test_render); | ||||
|     InputEvent event; | ||||
|     while(osMessageQueueGet(event_queue, &event, NULL, osWaitForever) == osOK) { | ||||
|         TextBoxTestState* state = acquire_mutex_block(&state_mutex); | ||||
| 
 | ||||
|         if(event.type == InputTypeShort) { | ||||
|             if(event.key == InputKeyRight) { | ||||
|                 if(state->idx < test_renders_num - 1) { | ||||
|                     state->idx++; | ||||
|                 } | ||||
|             } else if(event.key == InputKeyLeft) { | ||||
|                 if(state->idx > 0) { | ||||
|                     state->idx--; | ||||
|                 } | ||||
|             } else if(event.key == InputKeyBack) { | ||||
|                 release_mutex(&state_mutex, state); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         release_mutex(&state_mutex, state); | ||||
|         view_port_update(view_port); | ||||
|     } | ||||
| 
 | ||||
|     // remove & free all stuff created by app
 | ||||
|     gui_remove_view_port(gui, view_port); | ||||
|     view_port_free(view_port); | ||||
|     osMessageQueueDelete(event_queue); | ||||
|     delete_mutex(&state_mutex); | ||||
| 
 | ||||
|     furi_record_close("gui"); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| @ -161,8 +161,9 @@ void animation_manager_new_idle_process(AnimationManager* animation_manager) { | ||||
| } | ||||
| 
 | ||||
| /* reaction to animation_manager->interact_callback() */ | ||||
| void animation_manager_interact_process(AnimationManager* animation_manager) { | ||||
| bool animation_manager_interact_process(AnimationManager* animation_manager) { | ||||
|     furi_assert(animation_manager); | ||||
|     bool consumed = true; | ||||
| 
 | ||||
|     if(animation_manager->levelup_pending) { | ||||
|         animation_manager->levelup_pending = false; | ||||
| @ -181,7 +182,11 @@ void animation_manager_interact_process(AnimationManager* animation_manager) { | ||||
|         if(!blocked) { | ||||
|             animation_manager_start_new_idle(animation_manager); | ||||
|         } | ||||
|     } else { | ||||
|         consumed = false; | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| static void animation_manager_start_new_idle(AnimationManager* animation_manager) { | ||||
|  | ||||
| @ -130,8 +130,9 @@ void animation_manager_set_interact_callback( | ||||
|  * set_new_idle_callback's call. | ||||
|  * | ||||
|  * @animation_manager   instance | ||||
|  * @return              true if event was consumed | ||||
|  */ | ||||
| void animation_manager_interact_process(AnimationManager* animation_manager); | ||||
| bool animation_manager_interact_process(AnimationManager* animation_manager); | ||||
| 
 | ||||
| /** Check if animation loaded
 | ||||
|  * | ||||
|  | ||||
| @ -117,6 +117,7 @@ static void desktop_auto_lock_inhibit(Desktop* desktop) { | ||||
| } | ||||
| 
 | ||||
| void desktop_lock(Desktop* desktop) { | ||||
|     furi_hal_rtc_set_pin_fails(0); | ||||
|     desktop_auto_lock_inhibit(desktop); | ||||
|     scene_manager_set_scene_state( | ||||
|         desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); | ||||
|  | ||||
| @ -116,7 +116,6 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { | ||||
|             } | ||||
|             consumed = true; | ||||
|             break; | ||||
| 
 | ||||
|         case DesktopAnimationEventCheckAnimation: | ||||
|             animation_manager_check_blocking_process(desktop->animation_manager); | ||||
|             consumed = true; | ||||
| @ -126,7 +125,12 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case DesktopAnimationEventInteractAnimation: | ||||
|             animation_manager_interact_process(desktop->animation_manager); | ||||
|             if(!animation_manager_interact_process(desktop->animation_manager)) { | ||||
|                 LoaderStatus status = loader_start(desktop->loader, "Passport", NULL); | ||||
|                 if(status != LoaderStatusOk) { | ||||
|                     FURI_LOG_E(TAG, "loader_start failed: %d", status); | ||||
|                 } | ||||
|             } | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case DesktopLockedEventUpdate: | ||||
|  | ||||
| @ -6,7 +6,7 @@ typedef enum { | ||||
|     DesktopMainEventOpenFavorite, | ||||
|     DesktopMainEventOpenMenu, | ||||
|     DesktopMainEventOpenDebug, | ||||
|     DesktopMainEventRightShort, | ||||
|     DesktopMainEventOpenPassport, /**< Broken, don't use it */ | ||||
| 
 | ||||
|     DesktopLockedEventUnlocked, | ||||
|     DesktopLockedEventUpdate, | ||||
|  | ||||
| @ -46,7 +46,7 @@ bool desktop_main_input(InputEvent* event, void* context) { | ||||
|         } else if(event->key == InputKeyLeft) { | ||||
|             main_view->callback(DesktopMainEventOpenFavorite, main_view->context); | ||||
|         } else if(event->key == InputKeyRight) { | ||||
|             main_view->callback(DesktopMainEventRightShort, main_view->context); | ||||
|             main_view->callback(DesktopMainEventOpenPassport, main_view->context); | ||||
|         } | ||||
|     } else if(event->type == InputTypeLong) { | ||||
|         if(event->key == InputKeyDown) { | ||||
|  | ||||
| @ -197,7 +197,11 @@ static size_t | ||||
|     uint16_t len_px = canvas_string_width(canvas, string_get_cstr(str)); | ||||
|     uint8_t px_left = 0; | ||||
|     if(horizontal == AlignCenter) { | ||||
|         px_left = canvas_width(canvas) - (x - len_px / 2); | ||||
|         if(x > (canvas_width(canvas) / 2)) { | ||||
|             px_left = (canvas_width(canvas) - x) * 2; | ||||
|         } else { | ||||
|             px_left = x * 2; | ||||
|         } | ||||
|     } else if(horizontal == AlignLeft) { | ||||
|         px_left = canvas_width(canvas) - x; | ||||
|     } else if(horizontal == AlignRight) { | ||||
| @ -208,13 +212,13 @@ static size_t | ||||
| 
 | ||||
|     if(len_px > px_left) { | ||||
|         uint8_t excess_symbols_approximately = | ||||
|             ((float)len_px - px_left) / ((float)len_px / text_size); | ||||
|             roundf((float)(len_px - px_left) / ((float)len_px / (float)text_size)); | ||||
|         // reduce to 5 to be sure dash fit, and next line will be at least 5 symbols long
 | ||||
|         excess_symbols_approximately = MAX(excess_symbols_approximately, 5); | ||||
|         if(text_size > (excess_symbols_approximately + 5)) { | ||||
|             result = text_size - excess_symbols_approximately - 5; | ||||
|         if(excess_symbols_approximately > 0) { | ||||
|             excess_symbols_approximately = MAX(excess_symbols_approximately, 5); | ||||
|             result = text_size - excess_symbols_approximately - 1; | ||||
|         } else { | ||||
|             result = text_size - 1; | ||||
|             result = text_size; | ||||
|         } | ||||
|     } else { | ||||
|         result = text_size; | ||||
| @ -258,12 +262,17 @@ void elements_multiline_text_aligned( | ||||
| 
 | ||||
|         if((start[chars_fit] == '\n') || (start[chars_fit] == 0)) { | ||||
|             string_init_printf(line, "%.*s", chars_fit, start); | ||||
|         } else if((y + font_height) > canvas_height(canvas)) { | ||||
|             string_init_printf(line, "%.*s...\n", chars_fit, start); | ||||
|         } else { | ||||
|             string_init_printf(line, "%.*s-\n", chars_fit, start); | ||||
|         } | ||||
|         canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, string_get_cstr(line)); | ||||
|         string_clear(line); | ||||
|         y += font_height; | ||||
|         if(y > canvas_height(canvas)) { | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         start += chars_fit; | ||||
|         start += start[0] == '\n' ? 1 : 0; | ||||
| @ -547,7 +556,8 @@ void elements_text_box( | ||||
|     uint8_t height, | ||||
|     Align horizontal, | ||||
|     Align vertical, | ||||
|     const char* text) { | ||||
|     const char* text, | ||||
|     bool strip_to_dots) { | ||||
|     furi_assert(canvas); | ||||
| 
 | ||||
|     ElementTextBoxLine line[ELEMENTS_MAX_LINES_NUM]; | ||||
| @ -571,6 +581,7 @@ void elements_text_box( | ||||
|     uint8_t total_height_default = 0; | ||||
|     uint16_t i = 0; | ||||
|     bool full_text_processed = false; | ||||
|     uint16_t dots_width = canvas_string_width(canvas, "..."); | ||||
| 
 | ||||
|     canvas_set_font(canvas, FontSecondary); | ||||
| 
 | ||||
| @ -663,31 +674,29 @@ void elements_text_box( | ||||
|     } | ||||
| 
 | ||||
|     // Set vertical alignment for all lines
 | ||||
|     if(full_text_processed) { | ||||
|         if(total_height_default < height) { | ||||
|             if(vertical == AlignTop) { | ||||
|                 line[0].y = y + line[0].height; | ||||
|             } else if(vertical == AlignCenter) { | ||||
|                 line[0].y = y + line[0].height + (height - total_height_default) / 2; | ||||
|             } else if(vertical == AlignBottom) { | ||||
|                 line[0].y = y + line[0].height + (height - total_height_default); | ||||
|             } | ||||
|             if(line_num > 1) { | ||||
|                 for(uint8_t i = 1; i < line_num; i++) { | ||||
|                     line[i].y = line[i - 1].y + line[i - 1].leading_default; | ||||
|                 } | ||||
|             } | ||||
|         } else if(line_num > 1) { | ||||
|             uint8_t free_pixel_num = height - total_height_min; | ||||
|             uint8_t fill_pixel = 0; | ||||
|             uint8_t j = 1; | ||||
|     if(total_height_default < height) { | ||||
|         if(vertical == AlignTop) { | ||||
|             line[0].y = y + line[0].height; | ||||
|             while(fill_pixel < free_pixel_num) { | ||||
|                 line[j].y = line[j - 1].y + line[j - 1].leading_min + 1; | ||||
|                 fill_pixel++; | ||||
|                 j = j % (line_num - 1) + 1; | ||||
|         } else if(vertical == AlignCenter) { | ||||
|             line[0].y = y + line[0].height + (height - total_height_default) / 2; | ||||
|         } else if(vertical == AlignBottom) { | ||||
|             line[0].y = y + line[0].height + (height - total_height_default); | ||||
|         } | ||||
|         if(line_num > 1) { | ||||
|             for(uint8_t i = 1; i < line_num; i++) { | ||||
|                 line[i].y = line[i - 1].y + line[i - 1].leading_default; | ||||
|             } | ||||
|         } | ||||
|     } else if(line_num > 1) { | ||||
|         uint8_t free_pixel_num = height - total_height_min; | ||||
|         uint8_t fill_pixel = 0; | ||||
|         uint8_t j = 1; | ||||
|         line[0].y = y + line[0].height; | ||||
|         while(fill_pixel < free_pixel_num) { | ||||
|             line[j].y = line[j - 1].y + line[j - 1].leading_min + 1; | ||||
|             fill_pixel++; | ||||
|             j = j % (line_num - 1) + 1; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Draw line by line
 | ||||
| @ -733,6 +742,13 @@ void elements_text_box( | ||||
|                 canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]); | ||||
|                 canvas_invert_color(canvas); | ||||
|             } else { | ||||
|                 if((i == line_num - 1) && strip_to_dots) { | ||||
|                     uint8_t next_symbol_width = canvas_glyph_width(canvas, line[i].text[j]); | ||||
|                     if(line[i].x + next_symbol_width + dots_width > x + width) { | ||||
|                         canvas_draw_str(canvas, line[i].x, line[i].y, "..."); | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 canvas_draw_glyph(canvas, line[i].x, line[i].y, line[i].text[j]); | ||||
|             } | ||||
|             line[i].x += canvas_glyph_width(canvas, line[i].text[j]); | ||||
|  | ||||
| @ -194,17 +194,18 @@ void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width); | ||||
| 
 | ||||
| /** Draw text box element
 | ||||
|  * | ||||
|  * @param       canvas      Canvas instance | ||||
|  * @param       x           x coordinate | ||||
|  * @param       y           y coordinate | ||||
|  * @param       width       width to fit text | ||||
|  * @param       height      height to fit text | ||||
|  * @param       horizontal  Align instance | ||||
|  * @param       vertical    Align instance | ||||
|  * @param[in]   text        Formatted text. The following formats are available: | ||||
|  *                          "\e#Bold text\e#" - bold font is used | ||||
|  *                          "\e*Monospaced text\e*" - monospaced font is used | ||||
|  *                          "\e#Inversed text\e#" - white text on black background | ||||
|  * @param       canvas          Canvas instance | ||||
|  * @param       x               x coordinate | ||||
|  * @param       y               y coordinate | ||||
|  * @param       width           width to fit text | ||||
|  * @param       height          height to fit text | ||||
|  * @param       horizontal      Align instance | ||||
|  * @param       vertical        Align instance | ||||
|  * @param[in]   text            Formatted text. The following formats are available: | ||||
|  *                              "\e#Bold text\e#" - bold font is used | ||||
|  *                              "\e*Monospaced text\e*" - monospaced font is used | ||||
|  *                              "\e#Inversed text\e#" - white text on black background | ||||
|  * @param      strip_to_dots    Strip text to ... if does not fit to width | ||||
|  */ | ||||
| void elements_text_box( | ||||
|     Canvas* canvas, | ||||
| @ -214,7 +215,8 @@ void elements_text_box( | ||||
|     uint8_t height, | ||||
|     Align horizontal, | ||||
|     Align vertical, | ||||
|     const char* text); | ||||
|     const char* text, | ||||
|     bool strip_to_dots); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
|  | ||||
| @ -314,6 +314,7 @@ void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer) { | ||||
|     view_port_gui_set(view_port, gui); | ||||
|     gui_unlock(gui); | ||||
| 
 | ||||
|     // Request redraw
 | ||||
|     gui_update(gui); | ||||
| } | ||||
| 
 | ||||
| @ -322,7 +323,6 @@ void gui_remove_view_port(Gui* gui, ViewPort* view_port) { | ||||
|     furi_assert(view_port); | ||||
| 
 | ||||
|     gui_lock(gui); | ||||
| 
 | ||||
|     view_port_gui_set(view_port, NULL); | ||||
|     ViewPortArray_it_t it; | ||||
|     for(size_t i = 0; i < GuiLayerMAX; i++) { | ||||
| @ -335,12 +335,13 @@ void gui_remove_view_port(Gui* gui, ViewPort* view_port) { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(gui->ongoing_input_view_port == view_port) { | ||||
|         gui->ongoing_input_view_port = NULL; | ||||
|     } | ||||
| 
 | ||||
|     gui_unlock(gui); | ||||
| 
 | ||||
|     // Request redraw
 | ||||
|     gui_update(gui); | ||||
| } | ||||
| 
 | ||||
| void gui_view_port_send_to_front(Gui* gui, ViewPort* view_port) { | ||||
| @ -367,6 +368,9 @@ void gui_view_port_send_to_front(Gui* gui, ViewPort* view_port) { | ||||
|     // Return to the top
 | ||||
|     ViewPortArray_push_back(gui->layers[layer], view_port); | ||||
|     gui_unlock(gui); | ||||
| 
 | ||||
|     // Request redraw
 | ||||
|     gui_update(gui); | ||||
| } | ||||
| 
 | ||||
| void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port) { | ||||
| @ -393,6 +397,9 @@ void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port) { | ||||
|     // Return to the top
 | ||||
|     ViewPortArray_push_at(gui->layers[layer], 0, view_port); | ||||
|     gui_unlock(gui); | ||||
| 
 | ||||
|     // Request redraw
 | ||||
|     gui_update(gui); | ||||
| } | ||||
| 
 | ||||
| void gui_add_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context) { | ||||
| @ -401,11 +408,11 @@ void gui_add_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, vo | ||||
|     const CanvasCallbackPair p = {callback, context}; | ||||
| 
 | ||||
|     gui_lock(gui); | ||||
| 
 | ||||
|     furi_assert(CanvasCallbackPairArray_count(gui->canvas_callback_pair, p) == 0); | ||||
|     CanvasCallbackPairArray_push_back(gui->canvas_callback_pair, p); | ||||
| 
 | ||||
|     gui_unlock(gui); | ||||
| 
 | ||||
|     // Request redraw
 | ||||
|     gui_update(gui); | ||||
| } | ||||
| 
 | ||||
| @ -415,10 +422,8 @@ void gui_remove_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, | ||||
|     const CanvasCallbackPair p = {callback, context}; | ||||
| 
 | ||||
|     gui_lock(gui); | ||||
| 
 | ||||
|     furi_assert(CanvasCallbackPairArray_count(gui->canvas_callback_pair, p) == 1); | ||||
|     CanvasCallbackPairArray_remove_val(gui->canvas_callback_pair, p); | ||||
| 
 | ||||
|     gui_unlock(gui); | ||||
| } | ||||
| 
 | ||||
| @ -429,9 +434,12 @@ size_t gui_get_framebuffer_size(Gui* gui) { | ||||
| 
 | ||||
| void gui_set_lockdown(Gui* gui, bool lockdown) { | ||||
|     furi_assert(gui); | ||||
| 
 | ||||
|     gui_lock(gui); | ||||
|     gui->lockdown = lockdown; | ||||
|     gui_unlock(gui); | ||||
| 
 | ||||
|     // Request redraw
 | ||||
|     gui_update(gui); | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										7
									
								
								applications/gui/modules/widget.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										7
									
								
								applications/gui/modules/widget.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -154,10 +154,11 @@ void widget_add_text_box_element( | ||||
|     uint8_t height, | ||||
|     Align horizontal, | ||||
|     Align vertical, | ||||
|     const char* text) { | ||||
|     const char* text, | ||||
|     bool strip_to_dots) { | ||||
|     furi_assert(widget); | ||||
|     WidgetElement* text_box_element = | ||||
|         widget_element_text_box_create(x, y, width, height, horizontal, vertical, text); | ||||
|     WidgetElement* text_box_element = widget_element_text_box_create( | ||||
|         x, y, width, height, horizontal, vertical, text, strip_to_dots); | ||||
|     widget_add_element(widget, text_box_element); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -81,17 +81,18 @@ void widget_add_string_element( | ||||
| 
 | ||||
| /** Add Text Box Element
 | ||||
|  * | ||||
|  * @param      widget      Widget instance | ||||
|  * @param      x           x coordinate | ||||
|  * @param      y           y coordinate | ||||
|  * @param      width       width to fit text | ||||
|  * @param      height      height to fit text | ||||
|  * @param      horizontal  Align instance | ||||
|  * @param      vertical    Align instance | ||||
|  * @param[in]  text        Formatted text. The following formats are available: | ||||
|  *                          "\e#Bold text\e#" - bold font is used | ||||
|  *                          "\e*Monospaced text\e*" - monospaced font is used | ||||
|  *                          "\e#Inversed text\e#" - white text on black background | ||||
|  * @param      widget           Widget instance | ||||
|  * @param      x                x coordinate | ||||
|  * @param      y                y coordinate | ||||
|  * @param      width            width to fit text | ||||
|  * @param      height           height to fit text | ||||
|  * @param      horizontal       Align instance | ||||
|  * @param      vertical         Align instance | ||||
|  * @param[in]  text             Formatted text. The following formats are available: | ||||
|  *                               "\e#Bold text\e#" - bold font is used | ||||
|  *                               "\e*Monospaced text\e*" - monospaced font is used | ||||
|  *                               "\e#Inversed text\e#" - white text on black background | ||||
|  * @param      strip_to_dots    Strip text to ... if does not fit to width | ||||
|  */ | ||||
| void widget_add_text_box_element( | ||||
|     Widget* widget, | ||||
| @ -101,7 +102,8 @@ void widget_add_text_box_element( | ||||
|     uint8_t height, | ||||
|     Align horizontal, | ||||
|     Align vertical, | ||||
|     const char* text); | ||||
|     const char* text, | ||||
|     bool strip_to_dots); | ||||
| 
 | ||||
| /** Add Button Element
 | ||||
|  * | ||||
|  | ||||
| @ -60,7 +60,8 @@ WidgetElement* widget_element_text_box_create( | ||||
|     uint8_t height, | ||||
|     Align horizontal, | ||||
|     Align vertical, | ||||
|     const char* text); | ||||
|     const char* text, | ||||
|     bool strip_to_dots); | ||||
| 
 | ||||
| /** Create button element */ | ||||
| WidgetElement* widget_element_button_create( | ||||
|  | ||||
| @ -10,6 +10,7 @@ typedef struct { | ||||
|     Align horizontal; | ||||
|     Align vertical; | ||||
|     string_t text; | ||||
|     bool strip_to_dots; | ||||
| } GuiTextBoxModel; | ||||
| 
 | ||||
| static void gui_text_box_draw(Canvas* canvas, WidgetElement* element) { | ||||
| @ -26,7 +27,8 @@ static void gui_text_box_draw(Canvas* canvas, WidgetElement* element) { | ||||
|             model->height, | ||||
|             model->horizontal, | ||||
|             model->vertical, | ||||
|             string_get_cstr(model->text)); | ||||
|             string_get_cstr(model->text), | ||||
|             model->strip_to_dots); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -46,7 +48,8 @@ WidgetElement* widget_element_text_box_create( | ||||
|     uint8_t height, | ||||
|     Align horizontal, | ||||
|     Align vertical, | ||||
|     const char* text) { | ||||
|     const char* text, | ||||
|     bool strip_to_dots) { | ||||
|     furi_assert(text); | ||||
| 
 | ||||
|     // Allocate and init model
 | ||||
| @ -58,6 +61,7 @@ WidgetElement* widget_element_text_box_create( | ||||
|     model->horizontal = horizontal; | ||||
|     model->vertical = vertical; | ||||
|     string_init_set_str(model->text, text); | ||||
|     model->strip_to_dots = strip_to_dots; | ||||
| 
 | ||||
|     // Allocate and init Element
 | ||||
|     WidgetElement* gui_string = malloc(sizeof(WidgetElement)); | ||||
|  | ||||
| @ -21,7 +21,7 @@ void iButtonSceneDeleteConfirm::on_enter(iButtonApp* app) { | ||||
| 
 | ||||
|     app->set_text_store("\e#Delete %s?\e#", ibutton_key_get_name_p(key)); | ||||
|     widget_add_text_box_element( | ||||
|         widget, 0, 0, 128, 27, AlignCenter, AlignCenter, app->get_text_store()); | ||||
|         widget, 0, 0, 128, 27, AlignCenter, AlignCenter, app->get_text_store(), false); | ||||
|     widget_add_button_element(widget, GuiButtonTypeLeft, "Cancel", widget_callback, app); | ||||
|     widget_add_button_element(widget, GuiButtonTypeRight, "Delete", widget_callback, app); | ||||
| 
 | ||||
|  | ||||
| @ -9,7 +9,7 @@ void iButtonSceneInfo::on_enter(iButtonApp* app) { | ||||
| 
 | ||||
|     app->set_text_store("%s", ibutton_key_get_name_p(key)); | ||||
|     widget_add_text_box_element( | ||||
|         widget, 0, 0, 128, 28, AlignCenter, AlignCenter, app->get_text_store()); | ||||
|         widget, 0, 0, 128, 28, AlignCenter, AlignCenter, app->get_text_store(), false); | ||||
| 
 | ||||
|     switch(ibutton_key_get_type(key)) { | ||||
|     case iButtonKeyDS1990: | ||||
|  | ||||
| @ -2,9 +2,9 @@ | ||||
| #include "../ibutton_app.h" | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubmenuIndexWrite, | ||||
|     SubmenuIndexEmulate, | ||||
|     SubmenuIndexSave, | ||||
|     SubmenuIndexEmulate, | ||||
|     SubmenuIndexWrite, | ||||
| } SubmenuIndex; | ||||
| 
 | ||||
| static void submenu_callback(void* context, uint32_t index) { | ||||
| @ -22,11 +22,11 @@ void iButtonSceneReadKeyMenu::on_enter(iButtonApp* app) { | ||||
|     iButtonAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Submenu* submenu = view_manager->get_submenu(); | ||||
| 
 | ||||
|     submenu_add_item(submenu, "Save", SubmenuIndexSave, submenu_callback, app); | ||||
|     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, submenu_callback, app); | ||||
|     if(ibutton_key_get_type(app->get_key()) == iButtonKeyDS1990) { | ||||
|         submenu_add_item(submenu, "Write", SubmenuIndexWrite, submenu_callback, app); | ||||
|     } | ||||
|     submenu_add_item(submenu, "Save", SubmenuIndexSave, submenu_callback, app); | ||||
|     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, submenu_callback, app); | ||||
|     submenu_set_selected_item(submenu, submenu_item_selected); | ||||
| 
 | ||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); | ||||
|  | ||||
| @ -14,14 +14,17 @@ int32_t InfraredApp::run(void* args) { | ||||
|     if(args) { | ||||
|         std::string path = static_cast<const char*>(args); | ||||
|         std::string remote_name(path, path.find_last_of('/') + 1, path.size()); | ||||
|         remote_name.erase(remote_name.find_last_of('.')); | ||||
|         path.erase(path.find_last_of('/')); | ||||
|         bool result = remote_manager.load(path, remote_name); | ||||
|         if(result) { | ||||
|             current_scene = InfraredApp::Scene::Remote; | ||||
|         } else { | ||||
|             printf("Failed to load remote \'%s\'\r\n", remote_name.c_str()); | ||||
|             return -1; | ||||
|         auto last_dot = remote_name.find_last_of('.'); | ||||
|         if(last_dot != std::string::npos) { | ||||
|             remote_name.erase(last_dot); | ||||
|             path.erase(path.find_last_of('/')); | ||||
|             bool result = remote_manager.load(path, remote_name); | ||||
|             if(result) { | ||||
|                 current_scene = InfraredApp::Scene::Remote; | ||||
|             } else { | ||||
|                 printf("Failed to load remote \'%s\'\r\n", remote_name.c_str()); | ||||
|                 return -1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -49,12 +52,14 @@ int32_t InfraredApp::run(void* args) { | ||||
| InfraredApp::InfraredApp() { | ||||
|     furi_check(InfraredAppRemoteManager::max_button_name_length < get_text_store_size()); | ||||
|     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"); | ||||
|     for(auto& [key, scene] : scenes) delete scene; | ||||
| } | ||||
| 
 | ||||
| @ -248,6 +253,10 @@ void InfraredApp::notify_blink_green() { | ||||
|     notification_message(notification, &sequence); | ||||
| } | ||||
| 
 | ||||
| DialogsApp* InfraredApp::get_dialogs() { | ||||
|     return dialogs; | ||||
| } | ||||
| 
 | ||||
| void InfraredApp::notify_green_on() { | ||||
|     notification_message(notification, &sequence_set_only_green_255); | ||||
| } | ||||
|  | ||||
| @ -9,6 +9,7 @@ | ||||
| #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" | ||||
| @ -228,6 +229,9 @@ public: | ||||
|     /** Blink green light */ | ||||
|     void notify_blink_green(); | ||||
| 
 | ||||
|     /** Get Dialogs instance */ | ||||
|     DialogsApp* get_dialogs(); | ||||
| 
 | ||||
|     /** Text input callback
 | ||||
|  * | ||||
|  * @param context - context to pass to callback | ||||
| @ -286,6 +290,8 @@ private: | ||||
| 
 | ||||
|     /** Notification instance */ | ||||
|     NotificationApp* notification; | ||||
|     /** Dialogs instance */ | ||||
|     DialogsApp* dialogs; | ||||
|     /** View manager instance */ | ||||
|     InfraredAppViewManager view_manager; | ||||
|     /** Remote manager instance */ | ||||
|  | ||||
| @ -5,7 +5,6 @@ | ||||
| #include <memory> | ||||
| #include <m-string.h> | ||||
| #include <furi.h> | ||||
| #include <file_worker_cpp.h> | ||||
| 
 | ||||
| void InfraredAppBruteForce::add_record(int index, const char* name) { | ||||
|     records[name].index = index; | ||||
|  | ||||
| @ -1,4 +1,3 @@ | ||||
| #include <file_worker_cpp.h> | ||||
| #include <flipper_format/flipper_format.h> | ||||
| #include "infrared_app_remote_manager.h" | ||||
| #include "infrared/helpers/infrared_parser.h" | ||||
| @ -22,26 +21,34 @@ std::string InfraredAppRemoteManager::make_full_name( | ||||
| } | ||||
| 
 | ||||
| std::string InfraredAppRemoteManager::find_vacant_remote_name(const std::string& name) { | ||||
|     bool exist = true; | ||||
|     FileWorkerCpp file_worker; | ||||
|     std::string result_name; | ||||
|     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); | ||||
| 
 | ||||
|     if(!file_worker.is_file_exist( | ||||
|            make_full_name(InfraredApp::infrared_directory, name).c_str(), &exist)) { | ||||
|         return std::string(); | ||||
|     } else if(!exist) { | ||||
|         return name; | ||||
|     FS_Error error = storage_common_stat( | ||||
|         storage, make_full_name(InfraredApp::infrared_directory, name).c_str(), NULL); | ||||
| 
 | ||||
|     if(error == FSE_NOT_EXIST) { | ||||
|         result_name = name; | ||||
|     } else if(error != FSE_OK) { | ||||
|         result_name = std::string(); | ||||
|     } else { | ||||
|         /* if suggested name is occupied, try another one (name2, name3, etc) */ | ||||
|         uint32_t i = 1; | ||||
|         std::string new_name; | ||||
|         do { | ||||
|             new_name = make_full_name(InfraredApp::infrared_directory, name + std::to_string(++i)); | ||||
|             error = storage_common_stat(storage, new_name.c_str(), NULL); | ||||
|         } while(error == FSE_OK); | ||||
| 
 | ||||
|         if(error == FSE_NOT_EXIST) { | ||||
|             result_name = name + std::to_string(i); | ||||
|         } else { | ||||
|             result_name = std::string(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /* if suggested name is occupied, try another one (name2, name3, etc) */ | ||||
|     uint32_t i = 1; | ||||
|     bool file_worker_result = false; | ||||
|     std::string new_name; | ||||
|     do { | ||||
|         new_name = make_full_name(InfraredApp::infrared_directory, name + std::to_string(++i)); | ||||
|         file_worker_result = file_worker.is_file_exist(new_name.c_str(), &exist); | ||||
|     } while(file_worker_result && exist); | ||||
| 
 | ||||
|     return !exist ? name + std::to_string(i) : std::string(); | ||||
|     furi_record_close("storage"); | ||||
|     return result_name; | ||||
| } | ||||
| 
 | ||||
| bool InfraredAppRemoteManager::add_button(const char* button_name, const InfraredAppSignal& signal) { | ||||
| @ -84,12 +91,14 @@ const InfraredAppSignal& InfraredAppRemoteManager::get_button_data(size_t index) | ||||
| } | ||||
| 
 | ||||
| bool InfraredAppRemoteManager::delete_remote() { | ||||
|     bool result; | ||||
|     FileWorkerCpp file_worker; | ||||
|     result = file_worker.remove(make_full_name(remote->path, remote->name).c_str()); | ||||
|     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); | ||||
| 
 | ||||
|     FS_Error error = | ||||
|         storage_common_remove(storage, make_full_name(remote->path, remote->name).c_str()); | ||||
|     reset_remote(); | ||||
|     return result; | ||||
| 
 | ||||
|     furi_record_close("storage"); | ||||
|     return (error == FSE_OK || error == FSE_NOT_EXIST); | ||||
| } | ||||
| 
 | ||||
| void InfraredAppRemoteManager::reset_remote() { | ||||
| @ -129,14 +138,15 @@ bool InfraredAppRemoteManager::rename_remote(const char* str) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     FileWorkerCpp file_worker; | ||||
|     Storage* storage = static_cast<Storage*>(furi_record_open("storage")); | ||||
| 
 | ||||
|     std::string old_filename = make_full_name(remote->path, remote->name); | ||||
|     std::string new_filename = make_full_name(remote->path, new_name); | ||||
|     bool result = file_worker.rename(old_filename.c_str(), new_filename.c_str()); | ||||
| 
 | ||||
|     FS_Error error = storage_common_rename(storage, old_filename.c_str(), new_filename.c_str()); | ||||
|     remote->name = new_name; | ||||
| 
 | ||||
|     return result; | ||||
|     furi_record_close("storage"); | ||||
|     return (error == FSE_OK || error == FSE_EXIST); | ||||
| } | ||||
| 
 | ||||
| bool InfraredAppRemoteManager::rename_button(uint32_t index, const char* str) { | ||||
| @ -155,11 +165,10 @@ size_t InfraredAppRemoteManager::get_number_of_buttons() { | ||||
| 
 | ||||
| bool InfraredAppRemoteManager::store(void) { | ||||
|     bool result = false; | ||||
|     FileWorkerCpp file_worker; | ||||
| 
 | ||||
|     if(!file_worker.mkdir(InfraredApp::infrared_directory)) return 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( | ||||
|  | ||||
| @ -23,9 +23,9 @@ void InfraredAppSceneEdit::on_enter(InfraredApp* app) { | ||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||
|     Submenu* submenu = view_manager->get_submenu(); | ||||
| 
 | ||||
|     submenu_add_item(submenu, "Add Key", SubmenuIndexAddKey, submenu_callback, app); | ||||
|     submenu_add_item(submenu, "Rename Key", SubmenuIndexRenameKey, submenu_callback, app); | ||||
|     submenu_add_item(submenu, "Delete Key", SubmenuIndexDeleteKey, submenu_callback, app); | ||||
|     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); | ||||
|  | ||||
| @ -51,7 +51,7 @@ void InfraredAppSceneEditDelete::on_enter(InfraredApp* app) { | ||||
| 
 | ||||
|     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, "Back"); | ||||
|     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); | ||||
|  | ||||
| @ -16,8 +16,9 @@ void InfraredAppSceneEditKeySelect::on_enter(InfraredApp* app) { | ||||
|     Submenu* submenu = view_manager->get_submenu(); | ||||
|     int item_number = 0; | ||||
| 
 | ||||
|     const char* header = | ||||
|         app->get_edit_action() == InfraredApp::EditAction::Rename ? "Rename key:" : "Delete key:"; | ||||
|     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(); | ||||
|  | ||||
| @ -14,7 +14,7 @@ void InfraredAppSceneEditRename::on_enter(InfraredApp* app) { | ||||
|         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 key"); | ||||
|         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()); | ||||
|  | ||||
| @ -20,7 +20,7 @@ void InfraredAppSceneLearnEnterName::on_enter(InfraredApp* app) { | ||||
|         app->set_text_store(0, "RAW_%d", raw_signal.timings_cnt); | ||||
|     } | ||||
| 
 | ||||
|     text_input_set_header_text(text_input, "Name the key"); | ||||
|     text_input_set_header_text(text_input, "Name the button"); | ||||
|     text_input_set_result_callback( | ||||
|         text_input, | ||||
|         InfraredApp::text_input_callback, | ||||
|  | ||||
| @ -1,5 +1,4 @@ | ||||
| #include <gui/modules/dialog_ex.h> | ||||
| #include <file_worker_cpp.h> | ||||
| #include <memory> | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| @ -88,13 +87,8 @@ bool InfraredAppSceneLearnSuccess::on_event(InfraredApp* app, InfraredAppEvent* | ||||
|             break; | ||||
|         case DialogExResultRight: { | ||||
|             consumed = true; | ||||
|             FileWorkerCpp file_worker; | ||||
|             if(!button_pressed) { | ||||
|                 if(file_worker.check_errors()) { | ||||
|                     app->switch_to_next_scene(InfraredApp::Scene::LearnEnterName); | ||||
|                 } else { | ||||
|                     app->switch_to_previous_scene(); | ||||
|                 } | ||||
|                 app->switch_to_next_scene(InfraredApp::Scene::LearnEnterName); | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|  | ||||
| @ -1,12 +1,10 @@ | ||||
| #include "../infrared_app.h" | ||||
| #include "infrared/infrared_app_event.h" | ||||
| #include <text_store.h> | ||||
| #include <file_worker_cpp.h> | ||||
| 
 | ||||
| void InfraredAppSceneRemoteList::on_enter(InfraredApp* app) { | ||||
|     furi_assert(app); | ||||
| 
 | ||||
|     FileWorkerCpp file_worker; | ||||
|     bool result = false; | ||||
|     bool file_select_result; | ||||
|     auto remote_manager = app->get_remote_manager(); | ||||
| @ -15,13 +13,15 @@ void InfraredAppSceneRemoteList::on_enter(InfraredApp* app) { | ||||
|         last_selected_remote.size() ? last_selected_remote.c_str() : nullptr; | ||||
|     auto filename_ts = | ||||
|         std::make_unique<TextStore>(InfraredAppRemoteManager::max_remote_name_length); | ||||
|     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 = file_worker.file_select( | ||||
|     file_select_result = dialog_file_select_show( | ||||
|         dialogs, | ||||
|         InfraredApp::infrared_directory, | ||||
|         InfraredApp::infrared_extension, | ||||
|         filename_ts->text, | ||||
|  | ||||
| @ -24,19 +24,15 @@ static void input_cli_dump(Cli* cli, string_t args, Input* input) { | ||||
|     FuriPubSubSubscription* input_subscription = | ||||
|         furi_pubsub_subscribe(input->event_pubsub, input_cli_dump_events_callback, input_queue); | ||||
| 
 | ||||
|     bool stop = false; | ||||
|     InputEvent input_event; | ||||
|     while(!stop) { | ||||
|     printf("Press CTRL+C to stop\r\n"); | ||||
|     while(!cli_cmd_interrupt_received(cli)) { | ||||
|         if(osMessageQueueGet(input_queue, &input_event, NULL, 100) == osOK) { | ||||
|             printf( | ||||
|                 "key: %s type: %s\r\n", | ||||
|                 input_get_key_name(input_event.key), | ||||
|                 input_get_type_name(input_event.type)); | ||||
|         } | ||||
| 
 | ||||
|         if(cli_cmd_interrupt_received(cli)) { | ||||
|             stop = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     furi_pubsub_unsubscribe(input->event_pubsub, input_subscription); | ||||
|  | ||||
| @ -1,17 +1,17 @@ | ||||
| #include "lfrfid_app_scene_read_menu.h" | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubmenuWrite, | ||||
|     SubmenuSave, | ||||
|     SubmenuEmulate, | ||||
|     SubmenuWrite, | ||||
| } SubmenuIndex; | ||||
| 
 | ||||
| void LfRfidAppSceneReadKeyMenu::on_enter(LfRfidApp* app, bool need_restore) { | ||||
|     auto submenu = app->view_controller.get<SubmenuVM>(); | ||||
| 
 | ||||
|     submenu->add_item("Write", SubmenuWrite, submenu_callback, app); | ||||
|     submenu->add_item("Save", SubmenuSave, submenu_callback, app); | ||||
|     submenu->add_item("Emulate", SubmenuEmulate, submenu_callback, app); | ||||
|     submenu->add_item("Write", SubmenuWrite, submenu_callback, app); | ||||
| 
 | ||||
|     if(need_restore) { | ||||
|         submenu->set_selected_item(submenu_item_selected); | ||||
|  | ||||
| @ -92,7 +92,7 @@ const FlipperApplication* loader_find_application_by_name(const char* name) { | ||||
|         application = loader_find_application_by_name_in_list( | ||||
|             name, FLIPPER_SYSTEM_APPS, FLIPPER_SYSTEM_APPS_COUNT); | ||||
|     } | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && !application) { | ||||
|     if(!application) { | ||||
|         application = loader_find_application_by_name_in_list( | ||||
|             name, FLIPPER_DEBUG_APPS, FLIPPER_DEBUG_APPS_COUNT); | ||||
|     } | ||||
| @ -239,26 +239,11 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con | ||||
|         event.type = LoaderEventTypeApplicationStarted; | ||||
|         furi_pubsub_publish(loader_instance->pubsub, &event); | ||||
|         furi_hal_power_insomnia_enter(); | ||||
| 
 | ||||
|         // Snapshot current memory usage
 | ||||
|         instance->free_heap_size = memmgr_get_free_heap(); | ||||
|     } else if(thread_state == FuriThreadStateStopped) { | ||||
|         /*
 | ||||
|          * Current Leak Sanitizer assumes that memory is allocated and freed | ||||
|          * inside one thread. Timers are allocated in one task, but freed in | ||||
|          * Timer-Task thread, and xTimerDelete() just put command to queue. | ||||
|          * To avoid some bad cases there are few fixes: | ||||
|          * 1) delay for Timer to process commands | ||||
|          * 2) there are 'heap diff' which shows difference in heap before task | ||||
|          * started and after task completed. In process of leakage monitoring | ||||
|          * both values should be taken into account. | ||||
|          */ | ||||
|         furi_hal_delay_ms(20); | ||||
|         int heap_diff = instance->free_heap_size - memmgr_get_free_heap(); | ||||
|         FURI_LOG_I( | ||||
|             TAG, | ||||
|             "Application thread stopped. Heap allocation balance: %d. Thread allocation balance: %d.", | ||||
|             heap_diff, | ||||
|             "Application thread stopped. Free heap: %d. Thread allocation balance: %d.", | ||||
|             memmgr_get_free_heap(), | ||||
|             furi_thread_get_heap_size(instance->application_thread)); | ||||
| 
 | ||||
|         if(loader_instance->application_arguments) { | ||||
| @ -455,18 +440,17 @@ void loader_update_menu() { | ||||
| } | ||||
| 
 | ||||
| int32_t loader_srv(void* p) { | ||||
|     FURI_LOG_I(TAG, "Starting"); | ||||
|     FURI_LOG_I(TAG, "Executing system start hooks"); | ||||
|     for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) { | ||||
|         FLIPPER_ON_SYSTEM_START[i](); | ||||
|     } | ||||
| 
 | ||||
|     FURI_LOG_I(TAG, "Starting"); | ||||
|     loader_instance = loader_alloc(); | ||||
| 
 | ||||
|     loader_build_menu(); | ||||
|     loader_build_submenu(); | ||||
| 
 | ||||
|     // Call on start hooks
 | ||||
|     for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) { | ||||
|         FLIPPER_ON_SYSTEM_START[i](); | ||||
|     } | ||||
| 
 | ||||
|     FURI_LOG_I(TAG, "Started"); | ||||
| 
 | ||||
|     furi_record_create("loader", loader_instance); | ||||
|  | ||||
| @ -30,7 +30,6 @@ struct Loader { | ||||
|     Submenu* debug_menu; | ||||
|     Submenu* settings_menu; | ||||
| 
 | ||||
|     size_t free_heap_size; | ||||
|     volatile uint8_t lock_count; | ||||
| 
 | ||||
|     FuriPubSub* pubsub; | ||||
|  | ||||
| @ -3,19 +3,19 @@ | ||||
| 
 | ||||
| bool nfc_custom_event_callback(void* context, uint32_t event) { | ||||
|     furi_assert(context); | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     return scene_manager_handle_custom_event(nfc->scene_manager, event); | ||||
| } | ||||
| 
 | ||||
| bool nfc_back_event_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     return scene_manager_handle_back_event(nfc->scene_manager); | ||||
| } | ||||
| 
 | ||||
| void nfc_tick_event_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     scene_manager_handle_tick_event(nfc->scene_manager); | ||||
| } | ||||
| 
 | ||||
| @ -169,11 +169,16 @@ int32_t nfc_app(void* p) { | ||||
|     char* args = p; | ||||
| 
 | ||||
|     // Check argument and run corresponding scene
 | ||||
|     if((*args != '\0') && nfc_device_load(nfc->dev, p)) { | ||||
|         if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl); | ||||
|     if((*args != '\0')) { | ||||
|         if(nfc_device_load(nfc->dev, p)) { | ||||
|             if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl); | ||||
|             } else { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); | ||||
|             } | ||||
|         } else { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); | ||||
|             // Exit app
 | ||||
|             view_dispatcher_stop(nfc->view_dispatcher); | ||||
|         } | ||||
|     } else { | ||||
|         scene_manager_next_scene(nfc->scene_manager, NfcSceneStart); | ||||
|  | ||||
| @ -16,40 +16,35 @@ static void nfc_cli_print_usage() { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_cli_detect(Cli* cli, string_t args) { | ||||
| static void nfc_cli_detect(Cli* cli, string_t args) { | ||||
|     // Check if nfc worker is not busy
 | ||||
|     if(furi_hal_nfc_is_busy()) { | ||||
|         printf("Nfc is busy\r\n"); | ||||
|         return; | ||||
|     } | ||||
|     rfalNfcDevice* dev_list; | ||||
|     uint8_t dev_cnt = 0; | ||||
| 
 | ||||
|     FuriHalNfcDevData dev_data = {}; | ||||
|     bool cmd_exit = false; | ||||
|     furi_hal_nfc_exit_sleep(); | ||||
|     printf("Detecting nfc...\r\nPress Ctrl+C to abort\r\n"); | ||||
|     while(!cmd_exit) { | ||||
|         cmd_exit |= cli_cmd_interrupt_received(cli); | ||||
|         cmd_exit |= furi_hal_nfc_detect(&dev_list, &dev_cnt, 400, true); | ||||
|         if(dev_cnt > 0) { | ||||
|             printf("Found %d devices\r\n", dev_cnt); | ||||
|             for(uint8_t i = 0; i < dev_cnt; i++) { | ||||
|                 printf("%d found: %s ", i + 1, nfc_get_rfal_type(dev_list[i].type)); | ||||
|                 if(dev_list[i].type == RFAL_NFC_LISTEN_TYPE_NFCA) { | ||||
|                     printf("type: %s, ", nfc_get_nfca_type(dev_list[i].dev.nfca.type)); | ||||
|                 } | ||||
|                 printf("UID length: %d, UID:", dev_list[i].nfcidLen); | ||||
|                 for(uint8_t j = 0; j < dev_list[i].nfcidLen; j++) { | ||||
|                     printf("%02X", dev_list[i].nfcid[j]); | ||||
|                 } | ||||
|                 printf("\r\n"); | ||||
|         if(furi_hal_nfc_detect(&dev_data, 400)) { | ||||
|             printf("found: %s ", nfc_get_dev_type(dev_data.type)); | ||||
|             printf("UID length: %d, UID:", dev_data.uid_len); | ||||
|             for(size_t i = 0; i < dev_data.uid_len; i++) { | ||||
|                 printf("%02X", dev_data.uid[i]); | ||||
|             } | ||||
|             printf("\r\n"); | ||||
|             break; | ||||
|         } | ||||
|         furi_hal_nfc_sleep(); | ||||
|         osDelay(50); | ||||
|     } | ||||
|     furi_hal_nfc_deactivate(); | ||||
|     furi_hal_nfc_sleep(); | ||||
| } | ||||
| 
 | ||||
| void nfc_cli_emulate(Cli* cli, string_t args) { | ||||
| static void nfc_cli_emulate(Cli* cli, string_t args) { | ||||
|     // Check if nfc worker is not busy
 | ||||
|     if(furi_hal_nfc_is_busy()) { | ||||
|         printf("Nfc is busy\r\n"); | ||||
| @ -60,26 +55,25 @@ void nfc_cli_emulate(Cli* cli, string_t args) { | ||||
|     printf("Emulating NFC-A Type: T2T UID: 36 9C E7 B1 0A C1 34 SAK: 00 ATQA: 00/44\r\n"); | ||||
|     printf("Press Ctrl+C to abort\r\n"); | ||||
| 
 | ||||
|     NfcDeviceCommonData params = { | ||||
|     FuriHalNfcDevData params = { | ||||
|         .uid = {0x36, 0x9C, 0xe7, 0xb1, 0x0A, 0xC1, 0x34}, | ||||
|         .uid_len = 7, | ||||
|         .atqa = {0x44, 0x00}, | ||||
|         .sak = 0x00, | ||||
|         .device = NfcDeviceNfca, | ||||
|         .protocol = NfcDeviceProtocolMifareUl, | ||||
|         .type = FuriHalNfcTypeA, | ||||
|     }; | ||||
| 
 | ||||
|     while(!cli_cmd_interrupt_received(cli)) { | ||||
|         if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 100)) { | ||||
|             printf("Reader detected\r\n"); | ||||
|             furi_hal_nfc_deactivate(); | ||||
|             furi_hal_nfc_sleep(); | ||||
|         } | ||||
|         osDelay(50); | ||||
|     } | ||||
|     furi_hal_nfc_deactivate(); | ||||
|     furi_hal_nfc_sleep(); | ||||
| } | ||||
| 
 | ||||
| void nfc_cli_field(Cli* cli, string_t args) { | ||||
| static void nfc_cli_field(Cli* cli, string_t args) { | ||||
|     // Check if nfc worker is not busy
 | ||||
|     if(furi_hal_nfc_is_busy()) { | ||||
|         printf("Nfc is busy\r\n"); | ||||
| @ -97,7 +91,7 @@ void nfc_cli_field(Cli* cli, string_t args) { | ||||
|     } | ||||
| 
 | ||||
|     furi_hal_nfc_field_off(); | ||||
|     furi_hal_nfc_deactivate(); | ||||
|     furi_hal_nfc_sleep(); | ||||
| } | ||||
| 
 | ||||
| static void nfc_cli(Cli* cli, string_t args, void* context) { | ||||
|  | ||||
| @ -41,31 +41,31 @@ static void nfc_device_prepare_format_string(NfcDevice* dev, string_t format_str | ||||
| static bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string) { | ||||
|     if(string_start_with_str_p(format_string, "UID")) { | ||||
|         dev->format = NfcDeviceSaveFormatUid; | ||||
|         dev->dev_data.nfc_data.protocol = NfcDeviceProtocolUnknown; | ||||
|         dev->dev_data.protocol = NfcDeviceProtocolUnknown; | ||||
|         return true; | ||||
|     } | ||||
|     if(string_start_with_str_p(format_string, "Bank card")) { | ||||
|         dev->format = NfcDeviceSaveFormatBankCard; | ||||
|         dev->dev_data.nfc_data.protocol = NfcDeviceProtocolEMV; | ||||
|         dev->dev_data.protocol = NfcDeviceProtocolEMV; | ||||
|         return true; | ||||
|     } | ||||
|     // Check Mifare Ultralight types
 | ||||
|     for(MfUltralightType type = MfUltralightTypeUnknown; type < MfUltralightTypeNum; type++) { | ||||
|         if(string_start_with_str_p(format_string, nfc_mf_ul_type(type, true))) { | ||||
|         if(string_equal_str_p(format_string, nfc_mf_ul_type(type, true))) { | ||||
|             dev->format = NfcDeviceSaveFormatMifareUl; | ||||
|             dev->dev_data.nfc_data.protocol = NfcDeviceProtocolMifareUl; | ||||
|             dev->dev_data.protocol = NfcDeviceProtocolMifareUl; | ||||
|             dev->dev_data.mf_ul_data.type = type; | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     if(string_start_with_str_p(format_string, "Mifare Classic")) { | ||||
|         dev->format = NfcDeviceSaveFormatMifareClassic; | ||||
|         dev->dev_data.nfc_data.protocol = NfcDeviceProtocolMifareClassic; | ||||
|         dev->dev_data.protocol = NfcDeviceProtocolMifareClassic; | ||||
|         return true; | ||||
|     } | ||||
|     if(string_start_with_str_p(format_string, "Mifare DESFire")) { | ||||
|         dev->format = NfcDeviceSaveFormatMifareDesfire; | ||||
|         dev->dev_data.nfc_data.protocol = NfcDeviceProtocolMifareDesfire; | ||||
|         dev->dev_data.protocol = NfcDeviceProtocolMifareDesfire; | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| @ -73,7 +73,7 @@ static bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_strin | ||||
| 
 | ||||
| static bool nfc_device_save_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { | ||||
|     bool saved = false; | ||||
|     MifareUlData* data = &dev->dev_data.mf_ul_data; | ||||
|     MfUltralightData* data = &dev->dev_data.mf_ul_data; | ||||
|     string_t temp_str; | ||||
|     string_init(temp_str); | ||||
| 
 | ||||
| @ -122,7 +122,7 @@ static bool nfc_device_save_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) | ||||
| 
 | ||||
| bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { | ||||
|     bool parsed = false; | ||||
|     MifareUlData* data = &dev->dev_data.mf_ul_data; | ||||
|     MfUltralightData* data = &dev->dev_data.mf_ul_data; | ||||
|     string_t temp_str; | ||||
|     string_init(temp_str); | ||||
| 
 | ||||
| @ -548,7 +548,7 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { | ||||
| 
 | ||||
| static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) { | ||||
|     bool saved = false; | ||||
|     NfcEmvData* data = &dev->dev_data.emv_data; | ||||
|     EmvData* data = &dev->dev_data.emv_data; | ||||
|     uint32_t data_temp = 0; | ||||
| 
 | ||||
|     do { | ||||
| @ -577,8 +577,8 @@ static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) | ||||
| 
 | ||||
| bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) { | ||||
|     bool parsed = false; | ||||
|     NfcEmvData* data = &dev->dev_data.emv_data; | ||||
|     memset(data, 0, sizeof(NfcEmvData)); | ||||
|     EmvData* data = &dev->dev_data.emv_data; | ||||
|     memset(data, 0, sizeof(EmvData)); | ||||
|     uint32_t data_cnt = 0; | ||||
|     string_t temp_str; | ||||
|     string_init(temp_str); | ||||
| @ -700,7 +700,7 @@ static bool nfc_device_save_file( | ||||
| 
 | ||||
|     bool saved = false; | ||||
|     FlipperFormat* file = flipper_format_file_alloc(dev->storage); | ||||
|     NfcDeviceCommonData* data = &dev->dev_data.nfc_data; | ||||
|     FuriHalNfcDevData* data = &dev->dev_data.nfc_data; | ||||
|     string_t temp_str; | ||||
|     string_init(temp_str); | ||||
| 
 | ||||
| @ -758,7 +758,7 @@ bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name) { | ||||
| static bool nfc_device_load_data(NfcDevice* dev, string_t path) { | ||||
|     bool parsed = false; | ||||
|     FlipperFormat* file = flipper_format_file_alloc(dev->storage); | ||||
|     NfcDeviceCommonData* data = &dev->dev_data.nfc_data; | ||||
|     FuriHalNfcDevData* data = &dev->dev_data.nfc_data; | ||||
|     uint32_t data_cnt = 0; | ||||
|     string_t temp_str; | ||||
|     string_init(temp_str); | ||||
| @ -789,6 +789,7 @@ static bool nfc_device_load_data(NfcDevice* dev, string_t path) { | ||||
|         if(!nfc_device_parse_format_string(dev, temp_str)) break; | ||||
|         // Read and parse UID, ATQA and SAK
 | ||||
|         if(!flipper_format_get_value_count(file, "UID", &data_cnt)) break; | ||||
|         if(!(data_cnt == 4 || data_cnt == 7)) break; | ||||
|         data->uid_len = data_cnt; | ||||
|         if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; | ||||
|         if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; | ||||
| @ -863,7 +864,7 @@ bool nfc_file_select(NfcDevice* dev) { | ||||
| } | ||||
| 
 | ||||
| void nfc_device_data_clear(NfcDeviceData* dev_data) { | ||||
|     if(dev_data->nfc_data.protocol == NfcDeviceProtocolMifareDesfire) { | ||||
|     if(dev_data->protocol == NfcDeviceProtocolMifareDesfire) { | ||||
|         mf_df_clear(&dev_data->mf_df_data); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -5,6 +5,8 @@ | ||||
| #include <storage/storage.h> | ||||
| #include <dialogs/dialogs.h> | ||||
| 
 | ||||
| #include <furi_hal_nfc.h> | ||||
| #include <lib/nfc_protocols/emv.h> | ||||
| #include <lib/nfc_protocols/mifare_ultralight.h> | ||||
| #include <lib/nfc_protocols/mifare_classic.h> | ||||
| #include <lib/nfc_protocols/mifare_desfire.h> | ||||
| @ -17,13 +19,6 @@ | ||||
| #define NFC_APP_EXTENSION ".nfc" | ||||
| #define NFC_APP_SHADOW_EXTENSION ".shd" | ||||
| 
 | ||||
| typedef enum { | ||||
|     NfcDeviceNfca, | ||||
|     NfcDeviceNfcb, | ||||
|     NfcDeviceNfcf, | ||||
|     NfcDeviceNfcv, | ||||
| } NfcDeviceType; | ||||
| 
 | ||||
| typedef enum { | ||||
|     NfcDeviceProtocolUnknown, | ||||
|     NfcDeviceProtocolEMV, | ||||
| @ -40,38 +35,18 @@ typedef enum { | ||||
|     NfcDeviceSaveFormatMifareDesfire, | ||||
| } NfcDeviceSaveFormat; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint8_t uid_len; | ||||
|     uint8_t uid[10]; | ||||
|     uint8_t atqa[2]; | ||||
|     uint8_t sak; | ||||
|     NfcDeviceType device; | ||||
|     NfcProtocol protocol; | ||||
| } NfcDeviceCommonData; | ||||
| 
 | ||||
| typedef struct { | ||||
|     char name[32]; | ||||
|     uint8_t aid[16]; | ||||
|     uint16_t aid_len; | ||||
|     uint8_t number[10]; | ||||
|     uint8_t number_len; | ||||
|     uint8_t exp_mon; | ||||
|     uint8_t exp_year; | ||||
|     uint16_t country_code; | ||||
|     uint16_t currency_code; | ||||
| } NfcEmvData; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint8_t data[NFC_READER_DATA_MAX_SIZE]; | ||||
|     uint16_t size; | ||||
| } NfcReaderRequestData; | ||||
| 
 | ||||
| typedef struct { | ||||
|     NfcDeviceCommonData nfc_data; | ||||
|     FuriHalNfcDevData nfc_data; | ||||
|     NfcProtocol protocol; | ||||
|     NfcReaderRequestData reader_data; | ||||
|     union { | ||||
|         NfcEmvData emv_data; | ||||
|         MifareUlData mf_ul_data; | ||||
|         EmvData emv_data; | ||||
|         MfUltralightData mf_ul_data; | ||||
|         MfClassicData mf_classic_data; | ||||
|         MifareDesfireData mf_df_data; | ||||
|     }; | ||||
|  | ||||
| @ -40,7 +40,7 @@ struct Nfc { | ||||
|     NotificationApp* notifications; | ||||
|     SceneManager* scene_manager; | ||||
|     NfcDevice* dev; | ||||
|     NfcDeviceCommonData dev_edit_data; | ||||
|     FuriHalNfcDevData dev_edit_data; | ||||
| 
 | ||||
|     char text_store[NFC_TEXT_STORE_SIZE + 1]; | ||||
|     string_t text_box_store; | ||||
|  | ||||
| @ -1,48 +1,14 @@ | ||||
| #include "nfc_types.h" | ||||
| 
 | ||||
| const char* nfc_get_rfal_type(rfalNfcDevType type) { | ||||
|     if(type == RFAL_NFC_LISTEN_TYPE_NFCA) { | ||||
| const char* nfc_get_dev_type(FuriHalNfcType type) { | ||||
|     if(type == FuriHalNfcTypeA) { | ||||
|         return "NFC-A"; | ||||
|     } else if(type == RFAL_NFC_LISTEN_TYPE_NFCB) { | ||||
|     } else if(type == FuriHalNfcTypeB) { | ||||
|         return "NFC-B"; | ||||
|     } else if(type == RFAL_NFC_LISTEN_TYPE_NFCF) { | ||||
|     } else if(type == FuriHalNfcTypeF) { | ||||
|         return "NFC-F"; | ||||
|     } else if(type == RFAL_NFC_LISTEN_TYPE_NFCV) { | ||||
|     } else if(type == FuriHalNfcTypeV) { | ||||
|         return "NFC-V"; | ||||
|     } else if(type == RFAL_NFC_LISTEN_TYPE_ST25TB) { | ||||
|         return "NFC-ST25TB"; | ||||
|     } else if(type == RFAL_NFC_LISTEN_TYPE_AP2P) { | ||||
|         return "NFC-AP2P"; | ||||
|     } else { | ||||
|         return "Unknown"; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| const char* nfc_get_dev_type(NfcDeviceType type) { | ||||
|     if(type == NfcDeviceNfca) { | ||||
|         return "NFC-A"; | ||||
|     } else if(type == NfcDeviceNfcb) { | ||||
|         return "NFC-B"; | ||||
|     } else if(type == NfcDeviceNfcf) { | ||||
|         return "NFC-F"; | ||||
|     } else if(type == NfcDeviceNfcv) { | ||||
|         return "NFC-V"; | ||||
|     } else { | ||||
|         return "Unknown"; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| const char* nfc_get_nfca_type(rfalNfcaListenDeviceType type) { | ||||
|     if(type == RFAL_NFCA_T1T) { | ||||
|         return "T1T"; | ||||
|     } else if(type == RFAL_NFCA_T2T) { | ||||
|         return "T2T"; | ||||
|     } else if(type == RFAL_NFCA_T4T) { | ||||
|         return "T4T"; | ||||
|     } else if(type == RFAL_NFCA_NFCDEP) { | ||||
|         return "NFCDEP"; | ||||
|     } else if(type == RFAL_NFCA_T4T_NFCDEP) { | ||||
|         return "T4T_NFCDEP"; | ||||
|     } else { | ||||
|         return "Unknown"; | ||||
|     } | ||||
|  | ||||
| @ -1,16 +1,8 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "st_errno.h" | ||||
| #include "rfal_nfc.h" | ||||
| #include "nfc_device.h" | ||||
| 
 | ||||
| #include <gui/view_dispatcher.h> | ||||
| #include "nfc_worker.h" | ||||
| 
 | ||||
| const char* nfc_get_rfal_type(rfalNfcDevType type); | ||||
| 
 | ||||
| const char* nfc_get_dev_type(NfcDeviceType type); | ||||
| 
 | ||||
| const char* nfc_get_nfca_type(rfalNfcaListenDeviceType type); | ||||
| const char* nfc_get_dev_type(FuriHalNfcType type); | ||||
| 
 | ||||
| const char* nfc_guess_protocol(NfcProtocol protocol); | ||||
| 
 | ||||
|  | ||||
| @ -2,7 +2,8 @@ | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| #include <lib/nfc_protocols/nfc_util.h> | ||||
| #include <lib/nfc_protocols/emv_decoder.h> | ||||
| #include <lib/nfc_protocols/emv.h> | ||||
| #include <lib/nfc_protocols/mifare_common.h> | ||||
| #include <lib/nfc_protocols/mifare_ultralight.h> | ||||
| #include <lib/nfc_protocols/mifare_classic.h> | ||||
| #include <lib/nfc_protocols/mifare_desfire.h> | ||||
| @ -94,22 +95,20 @@ int32_t nfc_worker_task(void* context) { | ||||
|         nfc_worker_emulate(nfc_worker); | ||||
|     } else if(nfc_worker->state == NfcWorkerStateReadEMVApp) { | ||||
|         nfc_worker_read_emv_app(nfc_worker); | ||||
|     } else if(nfc_worker->state == NfcWorkerStateReadEMV) { | ||||
|     } else if(nfc_worker->state == NfcWorkerStateReadEMVData) { | ||||
|         nfc_worker_read_emv(nfc_worker); | ||||
|     } else if(nfc_worker->state == NfcWorkerStateEmulateApdu) { | ||||
|         nfc_worker_emulate_apdu(nfc_worker); | ||||
|     } else if(nfc_worker->state == NfcWorkerStateReadMifareUl) { | ||||
|         nfc_worker_read_mifare_ul(nfc_worker); | ||||
|     } else if(nfc_worker->state == NfcWorkerStateEmulateMifareUl) { | ||||
|     } else if(nfc_worker->state == NfcWorkerStateReadMifareUltralight) { | ||||
|         nfc_worker_read_mifare_ultralight(nfc_worker); | ||||
|     } else if(nfc_worker->state == NfcWorkerStateEmulateMifareUltralight) { | ||||
|         nfc_worker_emulate_mifare_ul(nfc_worker); | ||||
|     } else if(nfc_worker->state == NfcWorkerStateReadMifareClassic) { | ||||
|         nfc_worker_mifare_classic_dict_attack(nfc_worker); | ||||
|     } else if(nfc_worker->state == NfcWorkerStateReadMifareDesfire) { | ||||
|         nfc_worker_read_mifare_desfire(nfc_worker); | ||||
|     } else if(nfc_worker->state == NfcWorkerStateField) { | ||||
|         nfc_worker_field(nfc_worker); | ||||
|     } | ||||
|     furi_hal_nfc_deactivate(); | ||||
|     furi_hal_nfc_sleep(); | ||||
|     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); | ||||
|     furi_hal_power_insomnia_exit(); | ||||
| 
 | ||||
| @ -117,579 +116,225 @@ int32_t nfc_worker_task(void* context) { | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_detect(NfcWorker* nfc_worker) { | ||||
|     rfalNfcDevice* dev_list; | ||||
|     rfalNfcDevice* dev; | ||||
|     uint8_t dev_cnt; | ||||
|     nfc_device_data_clear(nfc_worker->dev_data); | ||||
|     NfcDeviceCommonData* result = &nfc_worker->dev_data->nfc_data; | ||||
|     NfcDeviceData* dev_data = nfc_worker->dev_data; | ||||
|     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||
| 
 | ||||
|     while(nfc_worker->state == NfcWorkerStateDetect) { | ||||
|         if(furi_hal_nfc_detect(&dev_list, &dev_cnt, 1000, true)) { | ||||
|         if(furi_hal_nfc_detect(nfc_data, 1000)) { | ||||
|             // Process first found device
 | ||||
|             dev = &dev_list[0]; | ||||
|             result->uid_len = dev->nfcidLen; | ||||
|             memcpy(result->uid, dev->nfcid, dev->nfcidLen); | ||||
|             if(dev->type == RFAL_NFC_LISTEN_TYPE_NFCA) { | ||||
|                 result->device = NfcDeviceNfca; | ||||
|                 result->atqa[0] = dev->dev.nfca.sensRes.anticollisionInfo; | ||||
|                 result->atqa[1] = dev->dev.nfca.sensRes.platformInfo; | ||||
|                 result->sak = dev->dev.nfca.selRes.sak; | ||||
|                 if(mf_ul_check_card_type( | ||||
|                        dev->dev.nfca.sensRes.anticollisionInfo, | ||||
|                        dev->dev.nfca.sensRes.platformInfo, | ||||
|                        dev->dev.nfca.selRes.sak)) { | ||||
|                     result->protocol = NfcDeviceProtocolMifareUl; | ||||
|             if(nfc_data->type == FuriHalNfcTypeA) { | ||||
|                 if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { | ||||
|                     dev_data->protocol = NfcDeviceProtocolMifareUl; | ||||
|                 } else if(mf_classic_check_card_type( | ||||
|                               dev->dev.nfca.sensRes.anticollisionInfo, | ||||
|                               dev->dev.nfca.sensRes.platformInfo, | ||||
|                               dev->dev.nfca.selRes.sak)) { | ||||
|                     result->protocol = NfcDeviceProtocolMifareClassic; | ||||
|                               nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { | ||||
|                     dev_data->protocol = NfcDeviceProtocolMifareClassic; | ||||
|                 } else if(mf_df_check_card_type( | ||||
|                               dev->dev.nfca.sensRes.anticollisionInfo, | ||||
|                               dev->dev.nfca.sensRes.platformInfo, | ||||
|                               dev->dev.nfca.selRes.sak)) { | ||||
|                     result->protocol = NfcDeviceProtocolMifareDesfire; | ||||
|                 } else if(dev->rfInterface == RFAL_NFC_INTERFACE_ISODEP) { | ||||
|                     result->protocol = NfcDeviceProtocolEMV; | ||||
|                               nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { | ||||
|                     dev_data->protocol = NfcDeviceProtocolMifareDesfire; | ||||
|                 } else if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) { | ||||
|                     dev_data->protocol = NfcDeviceProtocolEMV; | ||||
|                 } else { | ||||
|                     result->protocol = NfcDeviceProtocolUnknown; | ||||
|                     dev_data->protocol = NfcDeviceProtocolUnknown; | ||||
|                 } | ||||
|             } else if(dev->type == RFAL_NFC_LISTEN_TYPE_NFCB) { | ||||
|                 result->device = NfcDeviceNfcb; | ||||
|             } else if(dev->type == RFAL_NFC_LISTEN_TYPE_NFCF) { | ||||
|                 result->device = NfcDeviceNfcf; | ||||
|             } else if(dev->type == RFAL_NFC_LISTEN_TYPE_NFCV) { | ||||
|                 result->device = NfcDeviceNfcv; | ||||
|             } | ||||
| 
 | ||||
|             // Notify caller and exit
 | ||||
|             if(nfc_worker->callback) { | ||||
|                 nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|         furi_hal_nfc_sleep(); | ||||
|         osDelay(100); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool nfc_worker_emulate_uid_callback( | ||||
|     uint8_t* buff_rx, | ||||
|     uint16_t buff_rx_len, | ||||
|     uint8_t* buff_tx, | ||||
|     uint16_t* buff_tx_len, | ||||
|     uint32_t* data_type, | ||||
|     void* context) { | ||||
|     furi_assert(context); | ||||
|     NfcWorker* nfc_worker = context; | ||||
|     NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data; | ||||
|     reader_data->size = buff_rx_len / 8; | ||||
|     if(reader_data->size > 0) { | ||||
|         memcpy(reader_data->data, buff_rx, reader_data->size); | ||||
|         if(nfc_worker->callback) { | ||||
|             nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_emulate(NfcWorker* nfc_worker) { | ||||
|     NfcDeviceCommonData* data = &nfc_worker->dev_data->nfc_data; | ||||
|     FuriHalNfcTxRxContext tx_rx = {}; | ||||
|     FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data; | ||||
|     NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data; | ||||
| 
 | ||||
|     while(nfc_worker->state == NfcWorkerStateEmulate) { | ||||
|         furi_hal_nfc_emulate_nfca( | ||||
|             data->uid, | ||||
|             data->uid_len, | ||||
|             data->atqa, | ||||
|             data->sak, | ||||
|             nfc_worker_emulate_uid_callback, | ||||
|             nfc_worker, | ||||
|             1000); | ||||
|         if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, true, 100)) { | ||||
|             if(furi_hal_nfc_tx_rx(&tx_rx, 100)) { | ||||
|                 reader_data->size = tx_rx.rx_bits / 8; | ||||
|                 if(reader_data->size > 0) { | ||||
|                     memcpy(reader_data->data, tx_rx.rx_data, reader_data->size); | ||||
|                     if(nfc_worker->callback) { | ||||
|                         nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 FURI_LOG_E(TAG, "Failed to get reader commands"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_read_emv_app(NfcWorker* nfc_worker) { | ||||
|     ReturnCode err; | ||||
|     rfalNfcDevice* dev_list; | ||||
|     FuriHalNfcTxRxContext tx_rx = {}; | ||||
|     EmvApplication emv_app = {}; | ||||
|     uint8_t dev_cnt = 0; | ||||
|     uint8_t tx_buff[255] = {}; | ||||
|     uint16_t tx_len = 0; | ||||
|     uint8_t* rx_buff; | ||||
|     uint16_t* rx_len; | ||||
|     NfcDeviceData* result = nfc_worker->dev_data; | ||||
|     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||
|     nfc_device_data_clear(result); | ||||
| 
 | ||||
|     while(nfc_worker->state == NfcWorkerStateReadEMVApp) { | ||||
|         memset(&emv_app, 0, sizeof(emv_app)); | ||||
|         if(furi_hal_nfc_detect(&dev_list, &dev_cnt, 1000, false)) { | ||||
|         if(furi_hal_nfc_detect(nfc_data, 1000)) { | ||||
|             // Card was found. Check that it supports EMV
 | ||||
|             if(dev_list[0].rfInterface == RFAL_NFC_INTERFACE_ISODEP) { | ||||
|                 result->nfc_data.uid_len = dev_list[0].dev.nfca.nfcId1Len; | ||||
|                 result->nfc_data.atqa[0] = dev_list[0].dev.nfca.sensRes.anticollisionInfo; | ||||
|                 result->nfc_data.atqa[1] = dev_list[0].dev.nfca.sensRes.platformInfo; | ||||
|                 result->nfc_data.sak = dev_list[0].dev.nfca.selRes.sak; | ||||
|                 memcpy( | ||||
|                     result->nfc_data.uid, dev_list[0].dev.nfca.nfcId1, result->nfc_data.uid_len); | ||||
|                 result->nfc_data.protocol = NfcDeviceProtocolEMV; | ||||
| 
 | ||||
|                 FURI_LOG_D(TAG, "Send select PPSE command"); | ||||
|                 tx_len = emv_prepare_select_ppse(tx_buff); | ||||
|                 err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false); | ||||
|                 if(err != ERR_NONE) { | ||||
|                     FURI_LOG_D(TAG, "Error during selection PPSE request: %d", err); | ||||
|                     furi_hal_nfc_deactivate(); | ||||
|                     continue; | ||||
|                 } | ||||
|                 FURI_LOG_D(TAG, "Select PPSE response received. Start parsing response"); | ||||
|                 if(emv_decode_ppse_response(rx_buff, *rx_len, &emv_app)) { | ||||
|                     FURI_LOG_D(TAG, "Select PPSE responce parced"); | ||||
|             if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) { | ||||
|                 result->protocol = NfcDeviceProtocolEMV; | ||||
|                 if(emv_search_application(&tx_rx, &emv_app)) { | ||||
|                     // Notify caller and exit
 | ||||
|                     result->emv_data.aid_len = emv_app.aid_len; | ||||
|                     memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len); | ||||
|                     if(nfc_worker->callback) { | ||||
|                         nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||
|                     } | ||||
|                     break; | ||||
|                 } else { | ||||
|                     FURI_LOG_D(TAG, "Can't find pay application"); | ||||
|                     furi_hal_nfc_deactivate(); | ||||
|                     continue; | ||||
|                 } | ||||
|             } else { | ||||
|                 // Can't find EMV card
 | ||||
|                 FURI_LOG_W(TAG, "Card doesn't support EMV"); | ||||
|                 furi_hal_nfc_deactivate(); | ||||
|             } | ||||
|         } else { | ||||
|             // Can't find EMV card
 | ||||
|             FURI_LOG_D(TAG, "Can't find any cards"); | ||||
|             furi_hal_nfc_deactivate(); | ||||
|         } | ||||
|         furi_hal_nfc_sleep(); | ||||
|         osDelay(20); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_read_emv(NfcWorker* nfc_worker) { | ||||
|     ReturnCode err; | ||||
|     rfalNfcDevice* dev_list; | ||||
|     FuriHalNfcTxRxContext tx_rx = {}; | ||||
|     EmvApplication emv_app = {}; | ||||
|     uint8_t dev_cnt = 0; | ||||
|     uint8_t tx_buff[255] = {}; | ||||
|     uint16_t tx_len = 0; | ||||
|     uint8_t* rx_buff; | ||||
|     uint16_t* rx_len; | ||||
|     NfcDeviceData* result = nfc_worker->dev_data; | ||||
|     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||
|     nfc_device_data_clear(result); | ||||
| 
 | ||||
|     while(nfc_worker->state == NfcWorkerStateReadEMV) { | ||||
|         memset(&emv_app, 0, sizeof(emv_app)); | ||||
|         if(furi_hal_nfc_detect(&dev_list, &dev_cnt, 1000, false)) { | ||||
|     while(nfc_worker->state == NfcWorkerStateReadEMVData) { | ||||
|         if(furi_hal_nfc_detect(nfc_data, 1000)) { | ||||
|             // Card was found. Check that it supports EMV
 | ||||
|             if(dev_list[0].rfInterface == RFAL_NFC_INTERFACE_ISODEP) { | ||||
|                 result->nfc_data.uid_len = dev_list[0].dev.nfca.nfcId1Len; | ||||
|                 result->nfc_data.atqa[0] = dev_list[0].dev.nfca.sensRes.anticollisionInfo; | ||||
|                 result->nfc_data.atqa[1] = dev_list[0].dev.nfca.sensRes.platformInfo; | ||||
|                 result->nfc_data.sak = dev_list[0].dev.nfca.selRes.sak; | ||||
|                 memcpy( | ||||
|                     result->nfc_data.uid, dev_list[0].dev.nfca.nfcId1, result->nfc_data.uid_len); | ||||
|                 result->nfc_data.protocol = NfcDeviceProtocolEMV; | ||||
| 
 | ||||
|                 FURI_LOG_D(TAG, "Send select PPSE command"); | ||||
|                 tx_len = emv_prepare_select_ppse(tx_buff); | ||||
|                 err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false); | ||||
|                 if(err != ERR_NONE) { | ||||
|                     FURI_LOG_D(TAG, "Error during selection PPSE request: %d", err); | ||||
|                     furi_hal_nfc_deactivate(); | ||||
|                     continue; | ||||
|                 } | ||||
|                 FURI_LOG_D(TAG, "Select PPSE response received. Start parsing response"); | ||||
|                 if(emv_decode_ppse_response(rx_buff, *rx_len, &emv_app)) { | ||||
|                     FURI_LOG_D(TAG, "Select PPSE responce parced"); | ||||
|             if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) { | ||||
|                 result->protocol = NfcDeviceProtocolEMV; | ||||
|                 if(emv_read_bank_card(&tx_rx, &emv_app)) { | ||||
|                     result->emv_data.number_len = emv_app.card_number_len; | ||||
|                     memcpy( | ||||
|                         result->emv_data.number, emv_app.card_number, result->emv_data.number_len); | ||||
|                     result->emv_data.aid_len = emv_app.aid_len; | ||||
|                     memcpy(result->emv_data.aid, emv_app.aid, emv_app.aid_len); | ||||
|                 } else { | ||||
|                     FURI_LOG_D(TAG, "Can't find pay application"); | ||||
|                     furi_hal_nfc_deactivate(); | ||||
|                     continue; | ||||
|                     if(emv_app.name_found) { | ||||
|                         memcpy(result->emv_data.name, emv_app.name, sizeof(emv_app.name)); | ||||
|                     } | ||||
|                     if(emv_app.exp_month) { | ||||
|                         result->emv_data.exp_mon = emv_app.exp_month; | ||||
|                         result->emv_data.exp_year = emv_app.exp_year; | ||||
|                     } | ||||
|                     if(emv_app.country_code) { | ||||
|                         result->emv_data.country_code = emv_app.country_code; | ||||
|                     } | ||||
|                     if(emv_app.currency_code) { | ||||
|                         result->emv_data.currency_code = emv_app.currency_code; | ||||
|                     } | ||||
|                     // Notify caller and exit
 | ||||
|                     if(nfc_worker->callback) { | ||||
|                         nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||
|                     } | ||||
|                     break; | ||||
|                 } | ||||
|                 FURI_LOG_D(TAG, "Starting application ..."); | ||||
|                 tx_len = emv_prepare_select_app(tx_buff, &emv_app); | ||||
|                 err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false); | ||||
|                 if(err != ERR_NONE) { | ||||
|                     FURI_LOG_D(TAG, "Error during application selection request: %d", err); | ||||
|                     furi_hal_nfc_deactivate(); | ||||
|                     continue; | ||||
|                 } | ||||
|                 FURI_LOG_D(TAG, "Select application response received. Start parsing response"); | ||||
|                 if(emv_decode_select_app_response(rx_buff, *rx_len, &emv_app)) { | ||||
|                     FURI_LOG_D(TAG, "Card name: %s", emv_app.name); | ||||
|                     memcpy(result->emv_data.name, emv_app.name, sizeof(emv_app.name)); | ||||
|                 } else if(emv_app.pdol.size > 0) { | ||||
|                     FURI_LOG_D(TAG, "Can't find card name, but PDOL is present."); | ||||
|                 } else { | ||||
|                     FURI_LOG_D(TAG, "Can't find card name or PDOL"); | ||||
|                     furi_hal_nfc_deactivate(); | ||||
|                     continue; | ||||
|                 } | ||||
|                 FURI_LOG_D(TAG, "Starting Get Processing Options command ..."); | ||||
|                 tx_len = emv_prepare_get_proc_opt(tx_buff, &emv_app); | ||||
|                 err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false); | ||||
|                 if(err != ERR_NONE) { | ||||
|                     FURI_LOG_D(TAG, "Error during Get Processing Options command: %d", err); | ||||
|                     furi_hal_nfc_deactivate(); | ||||
|                     continue; | ||||
|                 } | ||||
|                 if(emv_decode_get_proc_opt(rx_buff, *rx_len, &emv_app)) { | ||||
|                     FURI_LOG_D(TAG, "Card number parsed"); | ||||
|                     result->emv_data.number_len = emv_app.card_number_len; | ||||
|                     memcpy(result->emv_data.number, emv_app.card_number, emv_app.card_number_len); | ||||
|             } else { | ||||
|                 FURI_LOG_W(TAG, "Card doesn't support EMV"); | ||||
|             } | ||||
|         } else { | ||||
|             FURI_LOG_D(TAG, "Can't find any cards"); | ||||
|         } | ||||
|         furi_hal_nfc_sleep(); | ||||
|         osDelay(20); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { | ||||
|     FuriHalNfcTxRxContext tx_rx = {}; | ||||
|     FuriHalNfcDevData params = { | ||||
|         .uid = {0xCF, 0x72, 0xd4, 0x40}, | ||||
|         .uid_len = 4, | ||||
|         .atqa = {0x00, 0x04}, | ||||
|         .sak = 0x20, | ||||
|         .type = FuriHalNfcTypeA, | ||||
|     }; | ||||
| 
 | ||||
|     while(nfc_worker->state == NfcWorkerStateEmulateApdu) { | ||||
|         if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) { | ||||
|             FURI_LOG_D(TAG, "POS terminal detected"); | ||||
|             if(emv_card_emulation(&tx_rx)) { | ||||
|                 FURI_LOG_D(TAG, "EMV card emulated"); | ||||
|             } | ||||
|         } else { | ||||
|             FURI_LOG_D(TAG, "Can't find reader"); | ||||
|         } | ||||
|         furi_hal_nfc_sleep(); | ||||
|         osDelay(20); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_read_mifare_ultralight(NfcWorker* nfc_worker) { | ||||
|     FuriHalNfcTxRxContext tx_rx = {}; | ||||
|     MfUltralightReader reader = {}; | ||||
|     MfUltralightData data = {}; | ||||
|     NfcDeviceData* result = nfc_worker->dev_data; | ||||
|     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||
| 
 | ||||
|     while(nfc_worker->state == NfcWorkerStateReadMifareUltralight) { | ||||
|         if(furi_hal_nfc_detect(nfc_data, 300)) { | ||||
|             if(nfc_data->type == FuriHalNfcTypeA && | ||||
|                mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { | ||||
|                 FURI_LOG_D(TAG, "Found Mifare Ultralight tag. Start reading"); | ||||
|                 if(mf_ul_read_card(&tx_rx, &reader, &data)) { | ||||
|                     result->protocol = NfcDeviceProtocolMifareUl; | ||||
|                     result->mf_ul_data = data; | ||||
|                     // Notify caller and exit
 | ||||
|                     if(nfc_worker->callback) { | ||||
|                         nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||
|                     } | ||||
|                     break; | ||||
|                 } else { | ||||
|                     // Mastercard doesn't give PAN / card number as GPO response
 | ||||
|                     // Iterate over all files found in application
 | ||||
|                     bool pan_found = false; | ||||
|                     for(uint8_t i = 0; (i < emv_app.afl.size) && !pan_found; i += 4) { | ||||
|                         uint8_t sfi = emv_app.afl.data[i] >> 3; | ||||
|                         uint8_t record_start = emv_app.afl.data[i + 1]; | ||||
|                         uint8_t record_end = emv_app.afl.data[i + 2]; | ||||
| 
 | ||||
|                         // Iterate over all records in file
 | ||||
|                         for(uint8_t record = record_start; record <= record_end; ++record) { | ||||
|                             tx_len = emv_prepare_read_sfi_record(tx_buff, sfi, record); | ||||
|                             err = furi_hal_nfc_data_exchange( | ||||
|                                 tx_buff, tx_len, &rx_buff, &rx_len, false); | ||||
|                             if(err != ERR_NONE) { | ||||
|                                 FURI_LOG_D( | ||||
|                                     TAG, | ||||
|                                     "Error reading application sfi %d, record %d", | ||||
|                                     sfi, | ||||
|                                     record); | ||||
|                             } | ||||
|                             if(emv_decode_read_sfi_record(rx_buff, *rx_len, &emv_app)) { | ||||
|                                 pan_found = true; | ||||
|                                 break; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     if(pan_found) { | ||||
|                         FURI_LOG_D(TAG, "Card PAN found"); | ||||
|                         result->emv_data.number_len = emv_app.card_number_len; | ||||
|                         memcpy( | ||||
|                             result->emv_data.number, | ||||
|                             emv_app.card_number, | ||||
|                             result->emv_data.number_len); | ||||
|                         if(emv_app.exp_month) { | ||||
|                             result->emv_data.exp_mon = emv_app.exp_month; | ||||
|                             result->emv_data.exp_year = emv_app.exp_year; | ||||
|                         } | ||||
|                         if(emv_app.country_code) { | ||||
|                             result->emv_data.country_code = emv_app.country_code; | ||||
|                         } | ||||
|                         if(emv_app.currency_code) { | ||||
|                             result->emv_data.currency_code = emv_app.currency_code; | ||||
|                         } | ||||
|                         // Notify caller and exit
 | ||||
|                         if(nfc_worker->callback) { | ||||
|                             nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||
|                         } | ||||
|                         break; | ||||
|                     } else { | ||||
|                         FURI_LOG_D(TAG, "Can't read card number"); | ||||
|                     } | ||||
|                     furi_hal_nfc_deactivate(); | ||||
|                     FURI_LOG_D(TAG, "Failed reading Mifare Ultralight"); | ||||
|                 } | ||||
|             } else { | ||||
|                 // Can't find EMV card
 | ||||
|                 FURI_LOG_W(TAG, "Card doesn't support EMV"); | ||||
|                 furi_hal_nfc_deactivate(); | ||||
|             } | ||||
|         } else { | ||||
|             // Can't find EMV card
 | ||||
|             FURI_LOG_D(TAG, "Can't find any cards"); | ||||
|             furi_hal_nfc_deactivate(); | ||||
|         } | ||||
|         osDelay(20); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { | ||||
|     ReturnCode err; | ||||
|     uint8_t tx_buff[255] = {}; | ||||
|     uint16_t tx_len = 0; | ||||
|     uint8_t* rx_buff; | ||||
|     uint16_t* rx_len; | ||||
|     NfcDeviceCommonData params = { | ||||
|         .uid = {0xCF, 0x72, 0xd4, 0x40}, | ||||
|         .uid_len = 4, | ||||
|         .atqa = {0x00, 0x04}, | ||||
|         .sak = 0x20, | ||||
|         .device = NfcDeviceNfca, | ||||
|         .protocol = NfcDeviceProtocolEMV, | ||||
|     }; | ||||
|     // Test RX data
 | ||||
|     const uint8_t debug_rx[] = { | ||||
|         0xba, 0x0b, 0xba, 0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, | ||||
|         0xca, 0xfe, 0xfa, 0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, | ||||
|         0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, | ||||
|         0x0b, 0xba, 0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, | ||||
|         0xfe, 0xfa, 0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, | ||||
|         0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b, | ||||
|         0xba, 0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe, | ||||
|         0xfa, 0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, | ||||
|         0xbb, 0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b, 0xba, | ||||
|         0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe, 0xfa, | ||||
|         0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, | ||||
|         0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b, 0xba, 0xba, | ||||
|         0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe, 0xfa, 0xce, | ||||
|         0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, | ||||
|         0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b, 0xba, 0xba, 0x20, | ||||
|         0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe, 0xfa, 0xce, 0x14, | ||||
|         0x88, 0x00}; | ||||
|     // Test TX data
 | ||||
|     const uint8_t debug_tx[] = { | ||||
|         0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, | ||||
|         0x10, 0x14, 0x88, 0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, | ||||
|         0xbe, 0xef, 0xce, 0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, | ||||
|         0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, | ||||
|         0x14, 0x88, 0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe, | ||||
|         0xef, 0xce, 0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34, | ||||
|         0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14, | ||||
|         0x88, 0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe, 0xef, | ||||
|         0xce, 0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34, 0x56, | ||||
|         0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14, 0x88, | ||||
|         0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe, 0xef, 0xce, | ||||
|         0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34, 0x56, 0x78, | ||||
|         0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14, 0x88, 0x02, | ||||
|         0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe, 0xef, 0xce, 0xee, | ||||
|         0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34, 0x56, 0x78, 0x9a, | ||||
|         0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14, 0x88, 0x02, 0x28, | ||||
|         0x00, 0x00}; | ||||
| 
 | ||||
|     while(nfc_worker->state == NfcWorkerStateEmulateApdu) { | ||||
|         if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) { | ||||
|             FURI_LOG_D(TAG, "POS terminal detected"); | ||||
|             // Read data from POS terminal
 | ||||
|             err = furi_hal_nfc_data_exchange(NULL, 0, &rx_buff, &rx_len, false); | ||||
|             if(err == ERR_NONE) { | ||||
|                 FURI_LOG_D(TAG, "Received Select PPSE"); | ||||
|             } else { | ||||
|                 FURI_LOG_D(TAG, "Error in 1st data exchange: select PPSE"); | ||||
|                 furi_hal_nfc_deactivate(); | ||||
|                 continue; | ||||
|             } | ||||
|             FURI_LOG_D(TAG, "Transive SELECT PPSE ANS"); | ||||
|             tx_len = emv_select_ppse_ans(tx_buff); | ||||
|             err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false); | ||||
|             if(err == ERR_NONE) { | ||||
|                 FURI_LOG_D(TAG, "Received Select APP"); | ||||
|             } else { | ||||
|                 FURI_LOG_D(TAG, "Error in 2nd data exchange: select APP"); | ||||
|                 furi_hal_nfc_deactivate(); | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             FURI_LOG_D(TAG, "Transive SELECT APP ANS"); | ||||
|             tx_len = emv_select_app_ans(tx_buff); | ||||
|             err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false); | ||||
|             if(err == ERR_NONE) { | ||||
|                 FURI_LOG_D(TAG, "Received PDOL"); | ||||
|             } else { | ||||
|                 FURI_LOG_D(TAG, "Error in 3rd data exchange: receive PDOL"); | ||||
|                 furi_hal_nfc_deactivate(); | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             FURI_LOG_D(TAG, "Transive PDOL ANS"); | ||||
|             tx_len = emv_get_proc_opt_ans(tx_buff); | ||||
|             err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false); | ||||
|             if(err == ERR_NONE) { | ||||
|                 FURI_LOG_D(TAG, "Transive PDOL ANS"); | ||||
|             } else { | ||||
|                 FURI_LOG_D(TAG, "Error in 4rd data exchange: Transive PDOL ANS"); | ||||
|                 furi_hal_nfc_deactivate(); | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             if(*rx_len != sizeof(debug_rx) || memcmp(rx_buff, debug_rx, sizeof(debug_rx))) { | ||||
|                 FURI_LOG_D(TAG, "Failed long message test"); | ||||
|             } else { | ||||
|                 FURI_LOG_D(TAG, "Correct debug message received"); | ||||
|                 tx_len = sizeof(debug_tx); | ||||
|                 err = furi_hal_nfc_data_exchange( | ||||
|                     (uint8_t*)debug_tx, tx_len, &rx_buff, &rx_len, false); | ||||
|                 if(err == ERR_NONE) { | ||||
|                     FURI_LOG_D(TAG, "Transive Debug message"); | ||||
|                 } | ||||
|             } | ||||
|             furi_hal_nfc_deactivate(); | ||||
|         } else { | ||||
|             FURI_LOG_D(TAG, "Can't find reader"); | ||||
|         } | ||||
|         osDelay(20); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker) { | ||||
|     ReturnCode err; | ||||
|     rfalNfcDevice* dev_list; | ||||
|     uint8_t dev_cnt = 0; | ||||
|     uint8_t tx_buff[255] = {}; | ||||
|     uint16_t tx_len = 0; | ||||
|     uint8_t* rx_buff; | ||||
|     uint16_t* rx_len; | ||||
|     MifareUlDevice mf_ul_read; | ||||
|     NfcDeviceData* result = nfc_worker->dev_data; | ||||
|     nfc_device_data_clear(result); | ||||
| 
 | ||||
|     while(nfc_worker->state == NfcWorkerStateReadMifareUl) { | ||||
|         furi_hal_nfc_deactivate(); | ||||
|         memset(&mf_ul_read, 0, sizeof(mf_ul_read)); | ||||
|         if(furi_hal_nfc_detect(&dev_list, &dev_cnt, 300, false)) { | ||||
|             if(dev_list[0].type == RFAL_NFC_LISTEN_TYPE_NFCA && | ||||
|                mf_ul_check_card_type( | ||||
|                    dev_list[0].dev.nfca.sensRes.anticollisionInfo, | ||||
|                    dev_list[0].dev.nfca.sensRes.platformInfo, | ||||
|                    dev_list[0].dev.nfca.selRes.sak)) { | ||||
|                 // Get Mifare Ultralight version
 | ||||
|                 FURI_LOG_D(TAG, "Found Mifare Ultralight tag. Reading tag version"); | ||||
|                 tx_len = mf_ul_prepare_get_version(tx_buff); | ||||
|                 err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false); | ||||
|                 if(err == ERR_NONE) { | ||||
|                     mf_ul_parse_get_version_response(rx_buff, &mf_ul_read); | ||||
|                     FURI_LOG_D( | ||||
|                         TAG, | ||||
|                         "Mifare Ultralight Type: %d, Pages: %d", | ||||
|                         mf_ul_read.data.type, | ||||
|                         mf_ul_read.pages_to_read); | ||||
|                     FURI_LOG_D(TAG, "Reading signature ..."); | ||||
|                     tx_len = mf_ul_prepare_read_signature(tx_buff); | ||||
|                     if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) { | ||||
|                         FURI_LOG_D(TAG, "Failed reading signature"); | ||||
|                         memset(mf_ul_read.data.signature, 0, sizeof(mf_ul_read.data.signature)); | ||||
|                     } else { | ||||
|                         mf_ul_parse_read_signature_response(rx_buff, &mf_ul_read); | ||||
|                     } | ||||
|                 } else if(err == ERR_TIMEOUT) { | ||||
|                     FURI_LOG_D( | ||||
|                         TAG, | ||||
|                         "Card doesn't respond to GET VERSION command. Setting default read parameters"); | ||||
|                     err = ERR_NONE; | ||||
|                     mf_ul_set_default_version(&mf_ul_read); | ||||
|                     // Reinit device
 | ||||
|                     furi_hal_nfc_deactivate(); | ||||
|                     if(!furi_hal_nfc_detect(&dev_list, &dev_cnt, 300, false)) { | ||||
|                         FURI_LOG_D(TAG, "Lost connection. Restarting search"); | ||||
|                         continue; | ||||
|                     } | ||||
|                 } else { | ||||
|                     FURI_LOG_D( | ||||
|                         TAG, "Error getting Mifare Ultralight version. Error code: %d", err); | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 if(mf_ul_read.support_fast_read) { | ||||
|                     FURI_LOG_D(TAG, "Reading pages ..."); | ||||
|                     tx_len = mf_ul_prepare_fast_read(tx_buff, 0x00, mf_ul_read.pages_to_read - 1); | ||||
|                     if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) { | ||||
|                         FURI_LOG_D(TAG, "Failed reading pages"); | ||||
|                         continue; | ||||
|                     } else { | ||||
|                         mf_ul_parse_fast_read_response( | ||||
|                             rx_buff, 0x00, mf_ul_read.pages_to_read - 1, &mf_ul_read); | ||||
|                     } | ||||
| 
 | ||||
|                     FURI_LOG_D(TAG, "Reading 3 counters ..."); | ||||
|                     for(uint8_t i = 0; i < 3; i++) { | ||||
|                         tx_len = mf_ul_prepare_read_cnt(tx_buff, i); | ||||
|                         if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) { | ||||
|                             FURI_LOG_W(TAG, "Failed reading Counter %d", i); | ||||
|                             mf_ul_read.data.counter[i] = 0; | ||||
|                         } else { | ||||
|                             mf_ul_parse_read_cnt_response(rx_buff, i, &mf_ul_read); | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     FURI_LOG_D(TAG, "Checking tearing flags ..."); | ||||
|                     for(uint8_t i = 0; i < 3; i++) { | ||||
|                         tx_len = mf_ul_prepare_check_tearing(tx_buff, i); | ||||
|                         if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) { | ||||
|                             FURI_LOG_D(TAG, "Error checking tearing flag %d", i); | ||||
|                             mf_ul_read.data.tearing[i] = MF_UL_TEARING_FLAG_DEFAULT; | ||||
|                         } else { | ||||
|                             mf_ul_parse_check_tearing_response(rx_buff, i, &mf_ul_read); | ||||
|                         } | ||||
|                     } | ||||
|                 } else { | ||||
|                     // READ card with READ command (4 pages at a time)
 | ||||
|                     for(uint8_t page = 0; page < mf_ul_read.pages_to_read; page += 4) { | ||||
|                         FURI_LOG_D(TAG, "Reading pages %d - %d ...", page, page + 3); | ||||
|                         tx_len = mf_ul_prepare_read(tx_buff, page); | ||||
|                         if(furi_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false)) { | ||||
|                             FURI_LOG_D(TAG, "Read pages %d - %d failed", page, page + 3); | ||||
|                             continue; | ||||
|                         } else { | ||||
|                             mf_ul_parse_read_response(rx_buff, page, &mf_ul_read); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 // Fill result data
 | ||||
|                 result->nfc_data.uid_len = dev_list[0].dev.nfca.nfcId1Len; | ||||
|                 result->nfc_data.atqa[0] = dev_list[0].dev.nfca.sensRes.anticollisionInfo; | ||||
|                 result->nfc_data.atqa[1] = dev_list[0].dev.nfca.sensRes.platformInfo; | ||||
|                 result->nfc_data.sak = dev_list[0].dev.nfca.selRes.sak; | ||||
|                 result->nfc_data.protocol = NfcDeviceProtocolMifareUl; | ||||
|                 memcpy( | ||||
|                     result->nfc_data.uid, dev_list[0].dev.nfca.nfcId1, result->nfc_data.uid_len); | ||||
|                 result->mf_ul_data = mf_ul_read.data; | ||||
| 
 | ||||
|                 // Notify caller and exit
 | ||||
|                 if(nfc_worker->callback) { | ||||
|                     nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||
|                 } | ||||
|                 break; | ||||
|             } else { | ||||
|                 FURI_LOG_W(TAG, "Tag does not support Mifare Ultralight"); | ||||
|                 FURI_LOG_W(TAG, "Tag is not Mifare Ultralight"); | ||||
|             } | ||||
|         } else { | ||||
|             FURI_LOG_D(TAG, "Can't find any tags"); | ||||
|         } | ||||
|         furi_hal_nfc_sleep(); | ||||
|         osDelay(100); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_emulate_mifare_ul(NfcWorker* nfc_worker) { | ||||
|     NfcDeviceCommonData* nfc_common = &nfc_worker->dev_data->nfc_data; | ||||
|     MifareUlDevice mf_ul_emulate; | ||||
|     mf_ul_prepare_emulation(&mf_ul_emulate, &nfc_worker->dev_data->mf_ul_data); | ||||
|     while(nfc_worker->state == NfcWorkerStateEmulateMifareUl) { | ||||
|     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||
|     MfUltralightEmulator emulator = {}; | ||||
|     mf_ul_prepare_emulation(&emulator, &nfc_worker->dev_data->mf_ul_data); | ||||
|     while(nfc_worker->state == NfcWorkerStateEmulateMifareUltralight) { | ||||
|         furi_hal_nfc_emulate_nfca( | ||||
|             nfc_common->uid, | ||||
|             nfc_common->uid_len, | ||||
|             nfc_common->atqa, | ||||
|             nfc_common->sak, | ||||
|             nfc_data->uid, | ||||
|             nfc_data->uid_len, | ||||
|             nfc_data->atqa, | ||||
|             nfc_data->sak, | ||||
|             mf_ul_prepare_emulation_response, | ||||
|             &mf_ul_emulate, | ||||
|             &emulator, | ||||
|             5000); | ||||
|         // Check if data was modified
 | ||||
|         if(mf_ul_emulate.data_changed) { | ||||
|             nfc_worker->dev_data->mf_ul_data = mf_ul_emulate.data; | ||||
|         if(emulator.data_changed) { | ||||
|             nfc_worker->dev_data->mf_ul_data = emulator.data; | ||||
|             if(nfc_worker->callback) { | ||||
|                 nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); | ||||
|             } | ||||
|             mf_ul_emulate.data_changed = false; | ||||
|             emulator.data_changed = false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) { | ||||
|     furi_assert(nfc_worker->callback); | ||||
|     rfalNfcDevice* dev_list; | ||||
|     rfalNfcDevice* dev; | ||||
|     NfcDeviceCommonData* nfc_common; | ||||
|     uint8_t dev_cnt = 0; | ||||
|     FuriHalNfcTxRxContext tx_rx_ctx = {}; | ||||
|     MfClassicAuthContext auth_ctx = {}; | ||||
|     MfClassicReader reader = {}; | ||||
| @ -697,6 +342,7 @@ void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) { | ||||
|     uint16_t curr_sector = 0; | ||||
|     uint8_t total_sectors = 0; | ||||
|     NfcWorkerEvent event; | ||||
|     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||
| 
 | ||||
|     // Open dictionary
 | ||||
|     nfc_worker->dict_stream = file_stream_alloc(nfc_worker->storage); | ||||
| @ -710,14 +356,13 @@ void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) { | ||||
| 
 | ||||
|     // Detect Mifare Classic card
 | ||||
|     while(nfc_worker->state == NfcWorkerStateReadMifareClassic) { | ||||
|         if(furi_hal_nfc_detect(&dev_list, &dev_cnt, 300, false)) { | ||||
|             dev = &dev_list[0]; | ||||
|         if(furi_hal_nfc_detect(nfc_data, 300)) { | ||||
|             if(mf_classic_get_type( | ||||
|                    dev->nfcid, | ||||
|                    dev->nfcidLen, | ||||
|                    dev->dev.nfca.sensRes.anticollisionInfo, | ||||
|                    dev->dev.nfca.sensRes.platformInfo, | ||||
|                    dev->dev.nfca.selRes.sak, | ||||
|                    nfc_data->uid, | ||||
|                    nfc_data->uid_len, | ||||
|                    nfc_data->atqa[0], | ||||
|                    nfc_data->atqa[1], | ||||
|                    nfc_data->sak, | ||||
|                    &reader)) { | ||||
|                 total_sectors = mf_classic_get_total_sectors_num(&reader); | ||||
|                 if(reader.type == MfClassicType1k) { | ||||
| @ -745,7 +390,7 @@ void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) { | ||||
|             mf_classic_auth_init_context(&auth_ctx, reader.cuid, curr_sector); | ||||
|             bool sector_key_found = false; | ||||
|             while(nfc_mf_classic_dict_get_next_key(nfc_worker->dict_stream, &curr_key)) { | ||||
|                 furi_hal_nfc_deactivate(); | ||||
|                 furi_hal_nfc_sleep(); | ||||
|                 if(furi_hal_nfc_activate_nfca(300, &reader.cuid)) { | ||||
|                     if(!card_found_notified) { | ||||
|                         if(reader.type == MfClassicType1k) { | ||||
| @ -817,15 +462,8 @@ void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) { | ||||
|         uint8_t sectors_read = | ||||
|             mf_classic_read_card(&tx_rx_ctx, &reader, &nfc_worker->dev_data->mf_classic_data); | ||||
|         if(sectors_read) { | ||||
|             dev = &dev_list[0]; | ||||
|             nfc_common = &nfc_worker->dev_data->nfc_data; | ||||
|             nfc_common->uid_len = dev->dev.nfca.nfcId1Len; | ||||
|             nfc_common->atqa[0] = dev->dev.nfca.sensRes.anticollisionInfo; | ||||
|             nfc_common->atqa[1] = dev->dev.nfca.sensRes.platformInfo; | ||||
|             nfc_common->sak = dev->dev.nfca.selRes.sak; | ||||
|             nfc_common->protocol = NfcDeviceProtocolMifareClassic; | ||||
|             memcpy(nfc_common->uid, dev->dev.nfca.nfcId1, nfc_common->uid_len); | ||||
|             event = NfcWorkerEventSuccess; | ||||
|             nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareClassic; | ||||
|             FURI_LOG_I(TAG, "Successfully read %d sectors", sectors_read); | ||||
|         } else { | ||||
|             event = NfcWorkerEventFail; | ||||
| @ -838,42 +476,8 @@ void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker) { | ||||
|     stream_free(nfc_worker->dict_stream); | ||||
| } | ||||
| 
 | ||||
| ReturnCode nfc_exchange_full( | ||||
|     uint8_t* tx_buff, | ||||
|     uint16_t tx_len, | ||||
|     uint8_t* rx_buff, | ||||
|     uint16_t rx_cap, | ||||
|     uint16_t* rx_len) { | ||||
|     ReturnCode err; | ||||
|     uint8_t* part_buff; | ||||
|     uint16_t* part_len; | ||||
| 
 | ||||
|     err = furi_hal_nfc_data_exchange(tx_buff, tx_len, &part_buff, &part_len, false); | ||||
|     if(*part_len > rx_cap) { | ||||
|         return ERR_OVERRUN; | ||||
|     } | ||||
|     memcpy(rx_buff, part_buff, *part_len); | ||||
|     *rx_len = *part_len; | ||||
|     while(err == ERR_NONE && rx_buff[0] == 0xAF) { | ||||
|         err = furi_hal_nfc_data_exchange(rx_buff, 1, &part_buff, &part_len, false); | ||||
|         if(*part_len > rx_cap - *rx_len) { | ||||
|             return ERR_OVERRUN; | ||||
|         } | ||||
|         if(*part_len == 0) { | ||||
|             return ERR_PROTO; | ||||
|         } | ||||
|         memcpy(rx_buff + *rx_len, part_buff + 1, *part_len - 1); | ||||
|         *rx_buff = *part_buff; | ||||
|         *rx_len += *part_len - 1; | ||||
|     } | ||||
| 
 | ||||
|     return err; | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { | ||||
|     ReturnCode err; | ||||
|     rfalNfcDevice* dev_list; | ||||
|     uint8_t dev_cnt = 0; | ||||
|     uint8_t tx_buff[64] = {}; | ||||
|     uint16_t tx_len = 0; | ||||
|     uint8_t rx_buff[512] = {}; | ||||
| @ -881,19 +485,17 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { | ||||
|     NfcDeviceData* result = nfc_worker->dev_data; | ||||
|     nfc_device_data_clear(result); | ||||
|     MifareDesfireData* data = &result->mf_df_data; | ||||
|     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||
| 
 | ||||
|     while(nfc_worker->state == NfcWorkerStateReadMifareDesfire) { | ||||
|         furi_hal_nfc_deactivate(); | ||||
|         if(!furi_hal_nfc_detect(&dev_list, &dev_cnt, 300, false)) { | ||||
|         furi_hal_nfc_sleep(); | ||||
|         if(!furi_hal_nfc_detect(nfc_data, 300)) { | ||||
|             osDelay(100); | ||||
|             continue; | ||||
|         } | ||||
|         memset(data, 0, sizeof(MifareDesfireData)); | ||||
|         if(dev_list[0].type != RFAL_NFC_LISTEN_TYPE_NFCA || | ||||
|            !mf_df_check_card_type( | ||||
|                dev_list[0].dev.nfca.sensRes.anticollisionInfo, | ||||
|                dev_list[0].dev.nfca.sensRes.platformInfo, | ||||
|                dev_list[0].dev.nfca.selRes.sak)) { | ||||
|         if(nfc_data->type != FuriHalNfcTypeA || | ||||
|            !mf_df_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { | ||||
|             FURI_LOG_D(TAG, "Tag is not DESFire"); | ||||
|             osDelay(100); | ||||
|             continue; | ||||
| @ -901,18 +503,11 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { | ||||
| 
 | ||||
|         FURI_LOG_D(TAG, "Found DESFire tag"); | ||||
| 
 | ||||
|         // Fill non-DESFire result data
 | ||||
|         result->nfc_data.uid_len = dev_list[0].dev.nfca.nfcId1Len; | ||||
|         result->nfc_data.atqa[0] = dev_list[0].dev.nfca.sensRes.anticollisionInfo; | ||||
|         result->nfc_data.atqa[1] = dev_list[0].dev.nfca.sensRes.platformInfo; | ||||
|         result->nfc_data.sak = dev_list[0].dev.nfca.selRes.sak; | ||||
|         result->nfc_data.device = NfcDeviceNfca; | ||||
|         result->nfc_data.protocol = NfcDeviceProtocolMifareDesfire; | ||||
|         memcpy(result->nfc_data.uid, dev_list[0].dev.nfca.nfcId1, result->nfc_data.uid_len); | ||||
|         result->protocol = NfcDeviceProtocolMifareDesfire; | ||||
| 
 | ||||
|         // Get DESFire version
 | ||||
|         tx_len = mf_df_prepare_get_version(tx_buff); | ||||
|         err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|         err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|         if(err != ERR_NONE) { | ||||
|             FURI_LOG_W(TAG, "Bad exchange getting version, err: %d", err); | ||||
|             continue; | ||||
| @ -923,7 +518,7 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { | ||||
|         } | ||||
| 
 | ||||
|         tx_len = mf_df_prepare_get_free_memory(tx_buff); | ||||
|         err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|         err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|         if(err == ERR_NONE) { | ||||
|             data->free_memory = malloc(sizeof(MifareDesfireFreeMemory)); | ||||
|             memset(data->free_memory, 0, sizeof(MifareDesfireFreeMemory)); | ||||
| @ -935,7 +530,7 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { | ||||
|         } | ||||
| 
 | ||||
|         tx_len = mf_df_prepare_get_key_settings(tx_buff); | ||||
|         err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|         err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|         if(err != ERR_NONE) { | ||||
|             FURI_LOG_D(TAG, "Bad exchange getting key settings, err: %d", err); | ||||
|         } else { | ||||
| @ -951,7 +546,8 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { | ||||
|                 &data->master_key_settings->key_version_head; | ||||
|             for(uint8_t key_id = 0; key_id < data->master_key_settings->max_keys; key_id++) { | ||||
|                 tx_len = mf_df_prepare_get_key_version(tx_buff, key_id); | ||||
|                 err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|                 err = | ||||
|                     furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|                 if(err != ERR_NONE) { | ||||
|                     FURI_LOG_W(TAG, "Bad exchange getting key version, err: %d", err); | ||||
|                     continue; | ||||
| @ -970,7 +566,7 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { | ||||
|         } | ||||
| 
 | ||||
|         tx_len = mf_df_prepare_get_application_ids(tx_buff); | ||||
|         err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|         err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|         if(err != ERR_NONE) { | ||||
|             FURI_LOG_W(TAG, "Bad exchange getting application IDs, err: %d", err); | ||||
|         } else { | ||||
| @ -981,13 +577,13 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { | ||||
| 
 | ||||
|         for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { | ||||
|             tx_len = mf_df_prepare_select_application(tx_buff, app->id); | ||||
|             err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|             err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|             if(!mf_df_parse_select_application_response(rx_buff, rx_len)) { | ||||
|                 FURI_LOG_W(TAG, "Bad exchange selecting application, err: %d", err); | ||||
|                 continue; | ||||
|             } | ||||
|             tx_len = mf_df_prepare_get_key_settings(tx_buff); | ||||
|             err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|             err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|             if(err != ERR_NONE) { | ||||
|                 FURI_LOG_W(TAG, "Bad exchange getting key settings, err: %d", err); | ||||
|             } else { | ||||
| @ -1002,7 +598,8 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { | ||||
|                 MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head; | ||||
|                 for(uint8_t key_id = 0; key_id < app->key_settings->max_keys; key_id++) { | ||||
|                     tx_len = mf_df_prepare_get_key_version(tx_buff, key_id); | ||||
|                     err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|                     err = furi_hal_nfc_exchange_full( | ||||
|                         tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|                     if(err != ERR_NONE) { | ||||
|                         FURI_LOG_W(TAG, "Bad exchange getting key version, err: %d", err); | ||||
|                         continue; | ||||
| @ -1021,7 +618,7 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { | ||||
|             } | ||||
| 
 | ||||
|             tx_len = mf_df_prepare_get_file_ids(tx_buff); | ||||
|             err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|             err = furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|             if(err != ERR_NONE) { | ||||
|                 FURI_LOG_W(TAG, "Bad exchange getting file IDs, err: %d", err); | ||||
|             } else { | ||||
| @ -1032,7 +629,8 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { | ||||
| 
 | ||||
|             for(MifareDesfireFile* file = app->file_head; file; file = file->next) { | ||||
|                 tx_len = mf_df_prepare_get_file_settings(tx_buff, file->id); | ||||
|                 err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|                 err = | ||||
|                     furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|                 if(err != ERR_NONE) { | ||||
|                     FURI_LOG_W(TAG, "Bad exchange getting file settings, err: %d", err); | ||||
|                     continue; | ||||
| @ -1054,7 +652,8 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { | ||||
|                     tx_len = mf_df_prepare_read_records(tx_buff, file->id, 0, 0); | ||||
|                     break; | ||||
|                 } | ||||
|                 err = nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|                 err = | ||||
|                     furi_hal_nfc_exchange_full(tx_buff, tx_len, rx_buff, sizeof(rx_buff), &rx_len); | ||||
|                 if(err != ERR_NONE) { | ||||
|                     FURI_LOG_W(TAG, "Bad exchange reading file %d, err: %d", file->id, err); | ||||
|                     continue; | ||||
| @ -1073,11 +672,3 @@ void nfc_worker_read_mifare_desfire(NfcWorker* nfc_worker) { | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_worker_field(NfcWorker* nfc_worker) { | ||||
|     furi_hal_nfc_field_on(); | ||||
|     while(nfc_worker->state == NfcWorkerStateField) { | ||||
|         osDelay(50); | ||||
|     } | ||||
|     furi_hal_nfc_field_off(); | ||||
| } | ||||
|  | ||||
| @ -13,11 +13,11 @@ typedef enum { | ||||
|     NfcWorkerStateDetect, | ||||
|     NfcWorkerStateEmulate, | ||||
|     NfcWorkerStateReadEMVApp, | ||||
|     NfcWorkerStateReadEMV, | ||||
|     NfcWorkerStateReadEMVData, | ||||
|     NfcWorkerStateEmulateApdu, | ||||
|     NfcWorkerStateField, | ||||
|     NfcWorkerStateReadMifareUl, | ||||
|     NfcWorkerStateEmulateMifareUl, | ||||
|     NfcWorkerStateReadMifareUltralight, | ||||
|     NfcWorkerStateEmulateMifareUltralight, | ||||
|     NfcWorkerStateReadMifareClassic, | ||||
|     NfcWorkerStateReadMifareDesfire, | ||||
|     // Transition
 | ||||
|  | ||||
| @ -4,19 +4,8 @@ | ||||
| #include "nfc_worker.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <stdbool.h> | ||||
| #include <lib/toolbox/stream/file_stream.h> | ||||
| 
 | ||||
| #include <rfal_analogConfig.h> | ||||
| #include <rfal_rf.h> | ||||
| #include <rfal_nfc.h> | ||||
| #include <rfal_nfca.h> | ||||
| #include <rfal_nfcb.h> | ||||
| #include <rfal_nfcf.h> | ||||
| #include <rfal_nfcv.h> | ||||
| #include <st25r3916.h> | ||||
| #include <st25r3916_irq.h> | ||||
| 
 | ||||
| struct NfcWorker { | ||||
|     FuriThread* thread; | ||||
|     Storage* storage; | ||||
| @ -44,9 +33,7 @@ void nfc_worker_detect(NfcWorker* nfc_worker); | ||||
| 
 | ||||
| void nfc_worker_emulate(NfcWorker* nfc_worker); | ||||
| 
 | ||||
| void nfc_worker_field(NfcWorker* nfc_worker); | ||||
| 
 | ||||
| void nfc_worker_read_mifare_ul(NfcWorker* nfc_worker); | ||||
| void nfc_worker_read_mifare_ultralight(NfcWorker* nfc_worker); | ||||
| 
 | ||||
| void nfc_worker_mifare_classic_dict_attack(NfcWorker* nfc_worker); | ||||
| 
 | ||||
|  | ||||
| @ -17,7 +17,7 @@ void nfc_scene_card_menu_on_enter(void* context) { | ||||
|     Nfc* nfc = context; | ||||
|     Submenu* submenu = nfc->submenu; | ||||
| 
 | ||||
|     if(nfc->dev->dev_data.nfc_data.protocol > NfcDeviceProtocolUnknown) { | ||||
|     if(nfc->dev->dev_data.protocol > NfcDeviceProtocolUnknown) { | ||||
|         submenu_add_item( | ||||
|             submenu, | ||||
|             "Run Compatible App", | ||||
| @ -49,13 +49,13 @@ bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) { | ||||
|         if(event.event == SubmenuIndexRunApp) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneCardMenu, SubmenuIndexRunApp); | ||||
|             if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolMifareUl) { | ||||
|             if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareUl) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareUl); | ||||
|             } else if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolMifareDesfire) { | ||||
|             } else if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareDesfire) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfire); | ||||
|             } else if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolEMV) { | ||||
|             } else if(nfc->dev->dev_data.protocol == NfcDeviceProtocolEMV) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvApp); | ||||
|             } else if(nfc->dev->dev_data.nfc_data.protocol == NfcDeviceProtocolMifareClassic) { | ||||
|             } else if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareClassic) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareClassic); | ||||
|             } | ||||
|             consumed = true; | ||||
|  | ||||
| @ -2,7 +2,6 @@ ADD_SCENE(nfc, start, Start) | ||||
| ADD_SCENE(nfc, read_card, ReadCard) | ||||
| ADD_SCENE(nfc, read_card_success, ReadCardSuccess) | ||||
| ADD_SCENE(nfc, card_menu, CardMenu) | ||||
| ADD_SCENE(nfc, not_implemented, NotImplemented) | ||||
| ADD_SCENE(nfc, emulate_uid, EmulateUid) | ||||
| ADD_SCENE(nfc, save_name, SaveName) | ||||
| ADD_SCENE(nfc, save_success, SaveSuccess) | ||||
|  | ||||
| @ -6,13 +6,13 @@ enum SubmenuDebugIndex { | ||||
| }; | ||||
| 
 | ||||
| void nfc_scene_debug_submenu_callback(void* context, uint32_t index) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_debug_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     Submenu* submenu = nfc->submenu; | ||||
| 
 | ||||
|     submenu_add_item( | ||||
| @ -28,7 +28,7 @@ void nfc_scene_debug_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_debug_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
| @ -48,7 +48,7 @@ bool nfc_scene_debug_on_event(void* context, SceneManagerEvent event) { | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_debug_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     submenu_reset(nfc->submenu); | ||||
| } | ||||
|  | ||||
| @ -1,29 +1,29 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| void nfc_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     if(type == InputTypeShort) { | ||||
|         view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_delete_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Setup Custom Widget view
 | ||||
|     char delete_str[64]; | ||||
|     snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", nfc->dev->dev_name); | ||||
|     widget_add_text_box_element(nfc->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str); | ||||
|     char temp_str[64]; | ||||
|     snprintf(temp_str, sizeof(temp_str), "\e#Delete %s?\e#", nfc->dev->dev_name); | ||||
|     widget_add_text_box_element( | ||||
|         nfc->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, temp_str, false); | ||||
|     widget_add_button_element( | ||||
|         nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_delete_widget_callback, nfc); | ||||
|     widget_add_button_element( | ||||
|         nfc->widget, GuiButtonTypeRight, "Delete", nfc_scene_delete_widget_callback, nfc); | ||||
|     char uid_str[32]; | ||||
|     NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data; | ||||
|     FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; | ||||
|     if(data->uid_len == 4) { | ||||
|         snprintf( | ||||
|             uid_str, | ||||
|             sizeof(uid_str), | ||||
|             temp_str, | ||||
|             sizeof(temp_str), | ||||
|             "UID: %02X %02X %02X %02X", | ||||
|             data->uid[0], | ||||
|             data->uid[1], | ||||
| @ -31,8 +31,8 @@ void nfc_scene_delete_on_enter(void* context) { | ||||
|             data->uid[3]); | ||||
|     } else if(data->uid_len == 7) { | ||||
|         snprintf( | ||||
|             uid_str, | ||||
|             sizeof(uid_str), | ||||
|             temp_str, | ||||
|             sizeof(temp_str), | ||||
|             "UID: %02X %02X %02X %02X %02X %02X %02X", | ||||
|             data->uid[0], | ||||
|             data->uid[1], | ||||
| @ -42,12 +42,13 @@ void nfc_scene_delete_on_enter(void* context) { | ||||
|             data->uid[5], | ||||
|             data->uid[6]); | ||||
|     } | ||||
|     widget_add_string_element(nfc->widget, 64, 23, AlignCenter, AlignTop, FontSecondary, uid_str); | ||||
|     widget_add_string_element(nfc->widget, 64, 23, AlignCenter, AlignTop, FontSecondary, temp_str); | ||||
| 
 | ||||
|     const char* protocol_name = NULL; | ||||
|     if(data->protocol == NfcDeviceProtocolEMV) { | ||||
|         protocol_name = nfc_guess_protocol(data->protocol); | ||||
|     } else if(data->protocol == NfcDeviceProtocolMifareUl) { | ||||
|     NfcProtocol protocol = nfc->dev->dev_data.protocol; | ||||
|     if(protocol == NfcDeviceProtocolEMV) { | ||||
|         protocol_name = nfc_guess_protocol(protocol); | ||||
|     } else if(protocol == NfcDeviceProtocolMifareUl) { | ||||
|         protocol_name = nfc_mf_ul_type(nfc->dev->dev_data.mf_ul_data.type, false); | ||||
|     } | ||||
|     if(protocol_name) { | ||||
| @ -56,18 +57,17 @@ void nfc_scene_delete_on_enter(void* context) { | ||||
|     } | ||||
|     // TODO change dinamically
 | ||||
|     widget_add_string_element(nfc->widget, 118, 33, AlignRight, AlignTop, FontSecondary, "NFC-A"); | ||||
|     char sak_str[16]; | ||||
|     snprintf(sak_str, sizeof(sak_str), "SAK: %02X", data->sak); | ||||
|     widget_add_string_element(nfc->widget, 10, 43, AlignLeft, AlignTop, FontSecondary, sak_str); | ||||
|     char atqa_str[16]; | ||||
|     snprintf(atqa_str, sizeof(atqa_str), "ATQA: %02X%02X", data->atqa[0], data->atqa[1]); | ||||
|     widget_add_string_element(nfc->widget, 118, 43, AlignRight, AlignTop, FontSecondary, atqa_str); | ||||
|     snprintf(temp_str, sizeof(temp_str), "SAK: %02X", data->sak); | ||||
|     widget_add_string_element(nfc->widget, 10, 43, AlignLeft, AlignTop, FontSecondary, temp_str); | ||||
|     snprintf(temp_str, sizeof(temp_str), "ATQA: %02X%02X", data->atqa[0], data->atqa[1]); | ||||
|     widget_add_string_element(nfc->widget, 118, 43, AlignRight, AlignTop, FontSecondary, temp_str); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == GuiButtonTypeLeft) { | ||||
| @ -79,14 +79,14 @@ bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) { | ||||
|                 scene_manager_search_and_switch_to_previous_scene( | ||||
|                     nfc->scene_manager, NfcSceneStart); | ||||
|             } | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_delete_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     widget_reset(nfc->widget); | ||||
| } | ||||
|  | ||||
| @ -1,12 +1,12 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| void nfc_scene_delete_success_popup_callback(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_delete_success_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     Popup* popup = nfc->popup; | ||||
| @ -20,27 +20,21 @@ void nfc_scene_delete_success_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcCustomEventViewExit) { | ||||
|             return scene_manager_search_and_switch_to_previous_scene( | ||||
|             consumed = scene_manager_search_and_switch_to_previous_scene( | ||||
|                 nfc->scene_manager, NfcSceneStart); | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_delete_success_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
|     popup_set_callback(popup, NULL); | ||||
|     popup_set_context(popup, NULL); | ||||
|     popup_set_timeout(popup, 0); | ||||
|     popup_disable_timeout(popup); | ||||
|     popup_reset(nfc->popup); | ||||
| } | ||||
| @ -36,19 +36,19 @@ void nfc_scene_device_info_on_enter(void* context) { | ||||
|                                   (nfc->dev->format == NfcDeviceSaveFormatBankCard); | ||||
|     // Setup Custom Widget view
 | ||||
|     widget_add_text_box_element( | ||||
|         nfc->widget, 0, 0, 128, 22, AlignCenter, AlignTop, nfc->dev->dev_name); | ||||
|         nfc->widget, 0, 0, 128, 22, AlignCenter, AlignTop, nfc->dev->dev_name, false); | ||||
|     widget_add_button_element( | ||||
|         nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc); | ||||
|     if(data_display_supported) { | ||||
|         widget_add_button_element( | ||||
|             nfc->widget, GuiButtonTypeRight, "Data", nfc_scene_device_info_widget_callback, nfc); | ||||
|     } | ||||
|     char uid_str[32]; | ||||
|     NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data; | ||||
|     char temp_str[32]; | ||||
|     FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; | ||||
|     if(data->uid_len == 4) { | ||||
|         snprintf( | ||||
|             uid_str, | ||||
|             sizeof(uid_str), | ||||
|             temp_str, | ||||
|             sizeof(temp_str), | ||||
|             "UID: %02X %02X %02X %02X", | ||||
|             data->uid[0], | ||||
|             data->uid[1], | ||||
| @ -56,8 +56,8 @@ void nfc_scene_device_info_on_enter(void* context) { | ||||
|             data->uid[3]); | ||||
|     } else if(data->uid_len == 7) { | ||||
|         snprintf( | ||||
|             uid_str, | ||||
|             sizeof(uid_str), | ||||
|             temp_str, | ||||
|             sizeof(temp_str), | ||||
|             "UID: %02X %02X %02X %02X %02X %02X %02X", | ||||
|             data->uid[0], | ||||
|             data->uid[1], | ||||
| @ -67,15 +67,15 @@ void nfc_scene_device_info_on_enter(void* context) { | ||||
|             data->uid[5], | ||||
|             data->uid[6]); | ||||
|     } | ||||
|     widget_add_string_element(nfc->widget, 64, 21, AlignCenter, AlignTop, FontSecondary, uid_str); | ||||
|     widget_add_string_element(nfc->widget, 64, 21, AlignCenter, AlignTop, FontSecondary, temp_str); | ||||
| 
 | ||||
|     const char* protocol_name = NULL; | ||||
|     if(data->protocol == NfcDeviceProtocolEMV || | ||||
|        data->protocol == NfcDeviceProtocolMifareDesfire) { | ||||
|         protocol_name = nfc_guess_protocol(data->protocol); | ||||
|     } else if(data->protocol == NfcDeviceProtocolMifareUl) { | ||||
|     NfcProtocol protocol = nfc->dev->dev_data.protocol; | ||||
|     if(protocol == NfcDeviceProtocolEMV || protocol == NfcDeviceProtocolMifareDesfire) { | ||||
|         protocol_name = nfc_guess_protocol(protocol); | ||||
|     } else if(protocol == NfcDeviceProtocolMifareUl) { | ||||
|         protocol_name = nfc_mf_ul_type(nfc->dev->dev_data.mf_ul_data.type, false); | ||||
|     } else if(data->protocol == NfcDeviceProtocolMifareClassic) { | ||||
|     } else if(protocol == NfcDeviceProtocolMifareClassic) { | ||||
|         protocol_name = nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type); | ||||
|     } | ||||
|     if(protocol_name) { | ||||
| @ -84,12 +84,10 @@ void nfc_scene_device_info_on_enter(void* context) { | ||||
|     } | ||||
|     // TODO change dinamically
 | ||||
|     widget_add_string_element(nfc->widget, 118, 32, AlignRight, AlignTop, FontSecondary, "NFC-A"); | ||||
|     char sak_str[16]; | ||||
|     snprintf(sak_str, sizeof(sak_str), "SAK: %02X", data->sak); | ||||
|     widget_add_string_element(nfc->widget, 10, 42, AlignLeft, AlignTop, FontSecondary, sak_str); | ||||
|     char atqa_str[16]; | ||||
|     snprintf(atqa_str, sizeof(atqa_str), "ATQA: %02X%02X", data->atqa[0], data->atqa[1]); | ||||
|     widget_add_string_element(nfc->widget, 118, 42, AlignRight, AlignTop, FontSecondary, atqa_str); | ||||
|     snprintf(temp_str, sizeof(temp_str), "SAK: %02X", data->sak); | ||||
|     widget_add_string_element(nfc->widget, 10, 42, AlignLeft, AlignTop, FontSecondary, temp_str); | ||||
|     snprintf(temp_str, sizeof(temp_str), "ATQA: %02X%02X", data->atqa[0], data->atqa[1]); | ||||
|     widget_add_string_element(nfc->widget, 118, 42, AlignRight, AlignTop, FontSecondary, temp_str); | ||||
| 
 | ||||
|     // Setup Data View
 | ||||
|     if(nfc->dev->format == NfcDeviceSaveFormatUid) { | ||||
| @ -99,7 +97,7 @@ void nfc_scene_device_info_on_enter(void* context) { | ||||
|         dialog_ex_set_context(dialog_ex, nfc); | ||||
|         dialog_ex_set_result_callback(dialog_ex, nfc_scene_device_info_dialog_callback); | ||||
|     } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { | ||||
|         MifareUlData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; | ||||
|         MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; | ||||
|         TextBox* text_box = nfc->text_box; | ||||
|         text_box_set_font(text_box, TextBoxFontHex); | ||||
|         for(uint16_t i = 0; i < mf_ul_data->data_size; i += 2) { | ||||
| @ -130,7 +128,7 @@ void nfc_scene_device_info_on_enter(void* context) { | ||||
|         widget_add_string_element( | ||||
|             nfc->widget, 64, 17, AlignCenter, AlignBottom, FontSecondary, nfc->text_store); | ||||
|     } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { | ||||
|         NfcEmvData* emv_data = &nfc->dev->dev_data.emv_data; | ||||
|         EmvData* emv_data = &nfc->dev->dev_data.emv_data; | ||||
|         BankCard* bank_card = nfc->bank_card; | ||||
|         bank_card_set_name(bank_card, emv_data->name); | ||||
|         bank_card_set_number(bank_card, emv_data->number, emv_data->number_len); | ||||
| @ -212,21 +210,16 @@ bool nfc_scene_device_info_on_event(void* context, SceneManagerEvent event) { | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_device_info_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clear Custom Widget
 | ||||
|     // Clear views
 | ||||
|     widget_reset(nfc->widget); | ||||
| 
 | ||||
|     if(nfc->dev->format == NfcDeviceSaveFormatUid) { | ||||
|         // Clear Dialog
 | ||||
|         DialogEx* dialog_ex = nfc->dialog_ex; | ||||
|         dialog_ex_reset(dialog_ex); | ||||
|         dialog_ex_reset(nfc->dialog_ex); | ||||
|     } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { | ||||
|         // Clear TextBox
 | ||||
|         text_box_reset(nfc->text_box); | ||||
|         string_reset(nfc->text_box_store); | ||||
|     } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { | ||||
|         // Clear Bank Card
 | ||||
|         bank_card_clear(nfc->bank_card); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,36 +1,34 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| void nfc_scene_emulate_apdu_sequence_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     Popup* popup = nfc->popup; | ||||
| 
 | ||||
|     popup_set_header(popup, "Run APDU reader", 64, 31, AlignCenter, AlignTop); | ||||
| 
 | ||||
|     // Setup and start worker
 | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); | ||||
|     nfc_worker_start(nfc->worker, NfcWorkerStateEmulateApdu, &nfc->dev->dev_data, NULL, nfc); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_emulate_apdu_sequence_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeTick) { | ||||
|         notification_message(nfc->notifications, &sequence_blink_blue_10); | ||||
|         return true; | ||||
|         consumed = true; | ||||
|     } | ||||
|     return false; | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_emulate_apdu_sequence_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Stop worker
 | ||||
|     nfc_worker_stop(nfc->worker); | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     popup_reset(popup); | ||||
|     popup_reset(nfc->popup); | ||||
| } | ||||
|  | ||||
| @ -5,13 +5,14 @@ | ||||
| #define NFC_MF_UL_DATA_CHANGED (1UL) | ||||
| 
 | ||||
| void nfc_emulate_mifare_ul_worker_callback(NfcWorkerEvent event, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     scene_manager_set_scene_state( | ||||
|         nfc->scene_manager, NfcSceneEmulateMifareUl, NFC_MF_UL_DATA_CHANGED); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_emulate_mifare_ul_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcEmulate); | ||||
| 
 | ||||
|     // Setup view
 | ||||
| @ -26,14 +27,14 @@ void nfc_scene_emulate_mifare_ul_on_enter(void* context) { | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); | ||||
|     nfc_worker_start( | ||||
|         nfc->worker, | ||||
|         NfcWorkerStateEmulateMifareUl, | ||||
|         NfcWorkerStateEmulateMifareUltralight, | ||||
|         &nfc->dev->dev_data, | ||||
|         nfc_emulate_mifare_ul_worker_callback, | ||||
|         nfc); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeTick) { | ||||
| @ -55,11 +56,8 @@ bool nfc_scene_emulate_mifare_ul_on_event(void* context, SceneManagerEvent event | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_emulate_mifare_ul_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
|     popup_reset(nfc->popup); | ||||
| } | ||||
|  | ||||
| @ -1,6 +1,8 @@ | ||||
| #include "../nfc_i.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| #define NFC_SCENE_EMULATE_UID_LOG_SIZE_MAX (200) | ||||
| 
 | ||||
| enum { | ||||
|     NfcSceneEmulateUidStateWidget, | ||||
|     NfcSceneEmulateUidStateTextBox, | ||||
| @ -28,14 +30,14 @@ void nfc_emulate_uid_textbox_callback(void* context) { | ||||
| 
 | ||||
| // Add widget with device name or inform that data received
 | ||||
| static void nfc_scene_emulate_uid_widget_config(Nfc* nfc, bool data_received) { | ||||
|     NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data; | ||||
|     FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; | ||||
|     Widget* widget = nfc->widget; | ||||
|     widget_reset(widget); | ||||
|     string_t info_str; | ||||
|     string_init(info_str); | ||||
| 
 | ||||
|     widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61); | ||||
|     widget_add_string_element(widget, 56, 32, AlignLeft, AlignTop, FontPrimary, "Emulating UID"); | ||||
|     widget_add_string_element(widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Emulating UID"); | ||||
|     if(strcmp(nfc->dev->dev_name, "")) { | ||||
|         string_printf(info_str, "%s", nfc->dev->dev_name); | ||||
|     } else { | ||||
| @ -45,7 +47,7 @@ static void nfc_scene_emulate_uid_widget_config(Nfc* nfc, bool data_received) { | ||||
|     } | ||||
|     string_strim(info_str); | ||||
|     widget_add_text_box_element( | ||||
|         widget, 56, 43, 70, 21, AlignLeft, AlignTop, string_get_cstr(info_str)); | ||||
|         widget, 56, 43, 70, 21, AlignCenter, AlignTop, string_get_cstr(info_str), true); | ||||
|     string_clear(info_str); | ||||
|     if(data_received) { | ||||
|         widget_add_button_element( | ||||
| @ -95,13 +97,15 @@ bool nfc_scene_emulate_uid_on_event(void* context, SceneManagerEvent event) { | ||||
|                 nfc_scene_emulate_uid_widget_config(nfc, true); | ||||
|             } | ||||
|             // Update TextBox data
 | ||||
|             string_cat_printf(nfc->text_box_store, "R:"); | ||||
|             for(uint16_t i = 0; i < reader_data->size; i++) { | ||||
|                 string_cat_printf(nfc->text_box_store, " %02X", reader_data->data[i]); | ||||
|             if(string_size(nfc->text_box_store) < NFC_SCENE_EMULATE_UID_LOG_SIZE_MAX) { | ||||
|                 string_cat_printf(nfc->text_box_store, "R:"); | ||||
|                 for(uint16_t i = 0; i < reader_data->size; i++) { | ||||
|                     string_cat_printf(nfc->text_box_store, " %02X", reader_data->data[i]); | ||||
|                 } | ||||
|                 string_push_back(nfc->text_box_store, '\n'); | ||||
|                 text_box_set_text(nfc->text_box, string_get_cstr(nfc->text_box_store)); | ||||
|             } | ||||
|             string_push_back(nfc->text_box_store, '\n'); | ||||
|             memset(reader_data, 0, sizeof(NfcReaderRequestData)); | ||||
|             text_box_set_text(nfc->text_box, string_get_cstr(nfc->text_box_store)); | ||||
|             consumed = true; | ||||
|         } else if(event.event == GuiButtonTypeCenter && state == NfcSceneEmulateUidStateWidget) { | ||||
|             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| void nfc_scene_field_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     furi_hal_nfc_field_on(); | ||||
| 
 | ||||
| @ -23,12 +23,9 @@ bool nfc_scene_field_on_event(void* context, SceneManagerEvent event) { | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_field_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     notification_internal_message(nfc->notifications, &sequence_reset_blue); | ||||
| 
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     popup_reset(popup); | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     furi_hal_nfc_field_off(); | ||||
|     notification_internal_message(nfc->notifications, &sequence_reset_blue); | ||||
|     popup_reset(nfc->popup); | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| void nfc_scene_file_select_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     // Process file_select return
 | ||||
|     if(nfc_file_select(nfc->dev)) { | ||||
|         scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu); | ||||
|  | ||||
| @ -18,13 +18,13 @@ MifareDesfireApplication* nfc_scene_mifare_desfire_app_get_app(Nfc* nfc) { | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mifare_desfire_app_submenu_callback(void* context, uint32_t index) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mifare_desfire_app_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     Submenu* submenu = nfc->submenu; | ||||
|     MifareDesfireApplication* app = nfc_scene_mifare_desfire_app_get_app(nfc); | ||||
|     if(!app) { | ||||
| @ -73,7 +73,8 @@ void nfc_scene_mifare_desfire_app_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_mifare_desfire_app_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
|     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp); | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
| @ -96,24 +97,24 @@ bool nfc_scene_mifare_desfire_app_on_event(void* context, SceneManagerEvent even | ||||
|         text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); | ||||
|         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMifareDesfireApp, state | 1); | ||||
|         view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); | ||||
|         return true; | ||||
|         consumed = true; | ||||
|     } else if(event.type == SceneManagerEventTypeBack) { | ||||
|         if(state & 1) { | ||||
|             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneMifareDesfireApp, state & ~1); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mifare_desfire_app_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clear views
 | ||||
|     text_box_reset(nfc->text_box); | ||||
|     string_reset(nfc->text_box_store); | ||||
| 
 | ||||
|     submenu_reset(nfc->submenu); | ||||
| } | ||||
|  | ||||
| @ -19,7 +19,7 @@ void nfc_scene_mifare_desfire_data_submenu_callback(void* context, uint32_t inde | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mifare_desfire_data_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     Submenu* submenu = nfc->submenu; | ||||
|     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireData); | ||||
|     MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; | ||||
| @ -61,7 +61,8 @@ void nfc_scene_mifare_desfire_data_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_mifare_desfire_data_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
|     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMifareDesfireData); | ||||
|     MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; | ||||
| 
 | ||||
| @ -76,7 +77,7 @@ bool nfc_scene_mifare_desfire_data_on_event(void* context, SceneManagerEvent eve | ||||
|                 nfc->scene_manager, | ||||
|                 NfcSceneMifareDesfireData, | ||||
|                 MifareDesfireDataStateItem + SubmenuIndexCardInfo); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } else { | ||||
|             uint16_t index = event.event - SubmenuIndexDynamic; | ||||
|             scene_manager_set_scene_state( | ||||
| @ -84,25 +85,25 @@ bool nfc_scene_mifare_desfire_data_on_event(void* context, SceneManagerEvent eve | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneMifareDesfireApp, index << 1); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneMifareDesfireApp); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeBack) { | ||||
|         if(state >= MifareDesfireDataStateItem) { | ||||
|             view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneMifareDesfireData, MifareDesfireDataStateMenu); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mifare_desfire_data_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clear views
 | ||||
|     text_box_reset(nfc->text_box); | ||||
|     string_reset(nfc->text_box_store); | ||||
| 
 | ||||
|     submenu_reset(nfc->submenu); | ||||
| } | ||||
|  | ||||
| @ -5,13 +5,13 @@ enum SubmenuIndex { | ||||
| }; | ||||
| 
 | ||||
| void nfc_scene_mifare_desfire_menu_submenu_callback(void* context, uint32_t index) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mifare_desfire_menu_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     Submenu* submenu = nfc->submenu; | ||||
| 
 | ||||
|     submenu_add_item( | ||||
| @ -24,7 +24,8 @@ void nfc_scene_mifare_desfire_menu_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_mifare_desfire_menu_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubmenuIndexSave) { | ||||
| @ -34,15 +35,16 @@ bool nfc_scene_mifare_desfire_menu_on_event(void* context, SceneManagerEvent eve | ||||
|             // Clear device name
 | ||||
|             nfc_device_set_name(nfc->dev, ""); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mifare_desfire_menu_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     submenu_reset(nfc->submenu); | ||||
| } | ||||
|  | ||||
| @ -6,13 +6,13 @@ enum SubmenuIndex { | ||||
| }; | ||||
| 
 | ||||
| void nfc_scene_mifare_ul_menu_submenu_callback(void* context, uint32_t index) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mifare_ul_menu_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     Submenu* submenu = nfc->submenu; | ||||
| 
 | ||||
|     submenu_add_item( | ||||
| @ -26,7 +26,8 @@ void nfc_scene_mifare_ul_menu_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubmenuIndexSave) { | ||||
| @ -36,23 +37,24 @@ bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent event) { | ||||
|             // Clear device name
 | ||||
|             nfc_device_set_name(nfc->dev, ""); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexEmulate) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneMifareUlMenu, SubmenuIndexEmulate); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeBack) { | ||||
|         return scene_manager_search_and_switch_to_previous_scene( | ||||
|             nfc->scene_manager, NfcSceneStart); | ||||
|         consumed = | ||||
|             scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart); | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_mifare_ul_menu_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     submenu_reset(nfc->submenu); | ||||
| } | ||||
|  | ||||
| @ -1,42 +0,0 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| void nfc_scene_not_implemented_dialog_callback(DialogExResult result, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_not_implemented_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     // TODO Set data from worker
 | ||||
|     DialogEx* dialog_ex = nfc->dialog_ex; | ||||
|     dialog_ex_set_left_button_text(dialog_ex, "Back"); | ||||
|     dialog_ex_set_header(dialog_ex, "Not implemented", 60, 24, AlignCenter, AlignCenter); | ||||
|     dialog_ex_set_context(dialog_ex, nfc); | ||||
|     dialog_ex_set_result_callback(dialog_ex, nfc_scene_not_implemented_dialog_callback); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_not_implemented_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == DialogExResultLeft) { | ||||
|             return scene_manager_previous_scene(nfc->scene_manager); | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_not_implemented_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     DialogEx* dialog_ex = nfc->dialog_ex; | ||||
|     dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter); | ||||
|     dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     dialog_ex_set_left_button_text(dialog_ex, NULL); | ||||
|     dialog_ex_set_result_callback(dialog_ex, NULL); | ||||
|     dialog_ex_set_context(dialog_ex, NULL); | ||||
| } | ||||
| @ -2,12 +2,12 @@ | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| void nfc_read_card_worker_callback(NfcWorkerEvent event, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_card_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcRead); | ||||
| 
 | ||||
|     // Setup view
 | ||||
| @ -22,29 +22,26 @@ void nfc_scene_read_card_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_read_card_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcCustomEventWorkerExit) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|         notification_message(nfc->notifications, &sequence_blink_blue_10); | ||||
|         return true; | ||||
|         consumed = true; | ||||
|     } | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_card_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Stop worker
 | ||||
|     nfc_worker_stop(nfc->worker); | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
|     popup_reset(nfc->popup); | ||||
| } | ||||
|  | ||||
| @ -7,13 +7,15 @@ void nfc_scene_read_card_success_widget_callback( | ||||
|     void* context) { | ||||
|     furi_assert(context); | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     if(type == InputTypeShort) { | ||||
|         view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_card_success_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     string_t data_str; | ||||
|     string_t uid_str; | ||||
|     string_init(data_str); | ||||
| @ -24,9 +26,9 @@ void nfc_scene_read_card_success_on_enter(void* context) { | ||||
|     notification_message(nfc->notifications, &sequence_success); | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data; | ||||
|     FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; | ||||
|     Widget* widget = nfc->widget; | ||||
|     string_set_str(data_str, nfc_get_dev_type(data->device)); | ||||
|     string_set_str(data_str, nfc_get_dev_type(data->type)); | ||||
|     string_set_str(uid_str, "UID:"); | ||||
|     for(uint8_t i = 0; i < data->uid_len; i++) { | ||||
|         string_cat_printf(uid_str, " %02X", data->uid[i]); | ||||
| @ -34,7 +36,7 @@ void nfc_scene_read_card_success_on_enter(void* context) { | ||||
| 
 | ||||
|     widget_add_button_element( | ||||
|         widget, GuiButtonTypeLeft, "Retry", nfc_scene_read_card_success_widget_callback, nfc); | ||||
|     if(data->device == NfcDeviceNfca) { | ||||
|     if(data->type == FuriHalNfcTypeA) { | ||||
|         widget_add_button_element( | ||||
|             widget, GuiButtonTypeRight, "More", nfc_scene_read_card_success_widget_callback, nfc); | ||||
|         widget_add_icon_element(widget, 8, 13, &I_Medium_chip_22x21); | ||||
| @ -44,7 +46,7 @@ void nfc_scene_read_card_success_on_enter(void* context) { | ||||
|         string_printf( | ||||
|             data_str, | ||||
|             "%s\nATQA: %02X%02X SAK: %02X", | ||||
|             nfc_guess_protocol(data->protocol), | ||||
|             nfc_guess_protocol(nfc->dev->dev_data.protocol), | ||||
|             data->atqa[0], | ||||
|             data->atqa[1], | ||||
|             data->sak); | ||||
| @ -66,14 +68,14 @@ void nfc_scene_read_card_success_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data; | ||||
|     Nfc* nfc = context; | ||||
|     FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == GuiButtonTypeLeft) { | ||||
|             consumed = scene_manager_previous_scene(nfc->scene_manager); | ||||
|         } else if(data->device == NfcDeviceNfca && event.event == GuiButtonTypeRight) { | ||||
|         } else if(data->type == FuriHalNfcTypeA && event.event == GuiButtonTypeRight) { | ||||
|             // Clear device name
 | ||||
|             nfc_device_set_name(nfc->dev, ""); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneCardMenu); | ||||
| @ -84,6 +86,8 @@ bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_card_success_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     widget_reset(nfc->widget); | ||||
| } | ||||
|  | ||||
| @ -2,12 +2,12 @@ | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| void nfc_read_emv_app_worker_callback(NfcWorkerEvent event, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_emv_app_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcRead); | ||||
| 
 | ||||
|     // Setup view
 | ||||
| @ -26,31 +26,30 @@ void nfc_scene_read_emv_app_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_read_emv_app_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcCustomEventWorkerExit) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneReadEmvAppSuccess, NFC_SEND_NOTIFICATION_TRUE); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvAppSuccess); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|         notification_message(nfc->notifications, &sequence_blink_blue_10); | ||||
|         return true; | ||||
|         consumed = true; | ||||
|     } | ||||
|     return false; | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_emv_app_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Stop worker
 | ||||
|     nfc_worker_stop(nfc->worker); | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
|     popup_reset(nfc->popup); | ||||
| } | ||||
|  | ||||
| @ -2,27 +2,38 @@ | ||||
| #include "../helpers/nfc_emv_parser.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| #define NFC_SCENE_READ_SUCCESS_SHIFT "              " | ||||
| 
 | ||||
| void nfc_scene_read_emv_app_success_dialog_callback(DialogExResult result, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||
| void nfc_scene_read_emv_app_widget_callback(GuiButtonType result, InputType type, void* context) { | ||||
|     Nfc* nfc = context; | ||||
|     if(type == InputTypeShort) { | ||||
|         view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_emv_app_success_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     NfcDeviceCommonData* nfc_data = &nfc->dev->dev_data.nfc_data; | ||||
|     NfcEmvData* emv_data = &nfc->dev->dev_data.emv_data; | ||||
|     DialogEx* dialog_ex = nfc->dialog_ex; | ||||
|     dialog_ex_set_left_button_text(dialog_ex, "Retry"); | ||||
|     dialog_ex_set_right_button_text(dialog_ex, "Run app"); | ||||
|     dialog_ex_set_header(dialog_ex, "Found EMV App", 36, 8, AlignLeft, AlignCenter); | ||||
|     dialog_ex_set_icon(dialog_ex, 8, 13, &I_Medium_chip_22x21); | ||||
|     // Display UID and AID
 | ||||
|     FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; | ||||
|     EmvData* emv_data = &nfc->dev->dev_data.emv_data; | ||||
|     Widget* widget = nfc->widget; | ||||
|     widget_add_button_element( | ||||
|         widget, GuiButtonTypeLeft, "Retry", nfc_scene_read_emv_app_widget_callback, nfc); | ||||
|     widget_add_button_element( | ||||
|         widget, GuiButtonTypeRight, "Run app", nfc_scene_read_emv_app_widget_callback, nfc); | ||||
|     widget_add_string_element(widget, 36, 5, AlignLeft, AlignTop, FontPrimary, "Found EMV App"); | ||||
|     widget_add_icon_element(widget, 8, 5, &I_Medium_chip_22x21); | ||||
|     // Display UID
 | ||||
|     string_t temp_str; | ||||
|     string_init_printf(temp_str, "UID:"); | ||||
|     for(size_t i = 0; i < nfc_data->uid_len; i++) { | ||||
|         string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); | ||||
|     } | ||||
|     widget_add_string_element( | ||||
|         widget, 36, 18, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); | ||||
|     string_reset(temp_str); | ||||
|     // Display application
 | ||||
|     string_printf(temp_str, "App: "); | ||||
|     string_t aid; | ||||
|     string_init(aid); | ||||
|     bool aid_found = | ||||
| @ -32,19 +43,11 @@ void nfc_scene_read_emv_app_success_on_enter(void* context) { | ||||
|             string_cat_printf(aid, "%02X", emv_data->aid[i]); | ||||
|         } | ||||
|     } | ||||
|     nfc_text_store_set( | ||||
|         nfc, | ||||
|         NFC_SCENE_READ_SUCCESS_SHIFT "UID: %02X %02X %02X %02X \n" NFC_SCENE_READ_SUCCESS_SHIFT | ||||
|                                      "Application:\n%s", | ||||
|         nfc_data->uid[0], | ||||
|         nfc_data->uid[1], | ||||
|         nfc_data->uid[2], | ||||
|         nfc_data->uid[3], | ||||
|         string_get_cstr(aid)); | ||||
|     string_cat(temp_str, aid); | ||||
|     widget_add_string_element( | ||||
|         widget, 7, 29, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); | ||||
|     string_clear(temp_str); | ||||
|     string_clear(aid); | ||||
|     dialog_ex_set_text(dialog_ex, nfc->text_store, 8, 16, AlignLeft, AlignTop); | ||||
|     dialog_ex_set_context(dialog_ex, nfc); | ||||
|     dialog_ex_set_result_callback(dialog_ex, nfc_scene_read_emv_app_success_dialog_callback); | ||||
| 
 | ||||
|     // Send notification
 | ||||
|     if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadEmvAppSuccess) == | ||||
| @ -54,32 +57,27 @@ void nfc_scene_read_emv_app_success_on_enter(void* context) { | ||||
|             nfc->scene_manager, NfcSceneReadEmvAppSuccess, NFC_SEND_NOTIFICATION_FALSE); | ||||
|     } | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_read_emv_app_success_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == DialogExResultLeft) { | ||||
|             return scene_manager_previous_scene(nfc->scene_manager); | ||||
|         } else if(event.event == DialogExResultRight) { | ||||
|         if(event.event == GuiButtonTypeLeft) { | ||||
|             consumed = scene_manager_previous_scene(nfc->scene_manager); | ||||
|         } else if(event.event == GuiButtonTypeRight) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneRunEmvAppConfirm); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_emv_app_success_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     DialogEx* dialog_ex = nfc->dialog_ex; | ||||
|     dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter); | ||||
|     dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     dialog_ex_set_icon(dialog_ex, 0, 0, NULL); | ||||
|     dialog_ex_set_left_button_text(dialog_ex, NULL); | ||||
|     dialog_ex_set_right_button_text(dialog_ex, NULL); | ||||
|     dialog_ex_set_result_callback(dialog_ex, NULL); | ||||
|     dialog_ex_set_context(dialog_ex, NULL); | ||||
|     // Clear views
 | ||||
|     widget_reset(nfc->widget); | ||||
| } | ||||
|  | ||||
| @ -2,12 +2,12 @@ | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| void nfc_read_emv_data_worker_callback(NfcWorkerEvent event, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_emv_data_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcRead); | ||||
| 
 | ||||
|     // Setup view
 | ||||
| @ -21,38 +21,35 @@ void nfc_scene_read_emv_data_on_enter(void* context) { | ||||
|     // Start worker
 | ||||
|     nfc_worker_start( | ||||
|         nfc->worker, | ||||
|         NfcWorkerStateReadEMV, | ||||
|         NfcWorkerStateReadEMVData, | ||||
|         &nfc->dev->dev_data, | ||||
|         nfc_read_emv_data_worker_callback, | ||||
|         nfc); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_read_emv_data_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcCustomEventWorkerExit) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneReadEmvDataSuccess, NFC_SEND_NOTIFICATION_TRUE); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvDataSuccess); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|         notification_message(nfc->notifications, &sequence_blink_blue_10); | ||||
|         return true; | ||||
|         consumed = true; | ||||
|     } | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_emv_data_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Stop worker
 | ||||
|     nfc_worker_stop(nfc->worker); | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
|     popup_reset(nfc->popup); | ||||
| } | ||||
|  | ||||
| @ -6,16 +6,16 @@ void nfc_scene_read_emv_data_success_widget_callback( | ||||
|     GuiButtonType result, | ||||
|     InputType type, | ||||
|     void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     if(type == InputTypeShort) { | ||||
|         view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_emv_data_success_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     NfcEmvData* emv_data = &nfc->dev->dev_data.emv_data; | ||||
|     NfcDeviceCommonData* nfc_data = &nfc->dev->dev_data.nfc_data; | ||||
|     Nfc* nfc = context; | ||||
|     EmvData* emv_data = &nfc->dev->dev_data.emv_data; | ||||
|     FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||
| 
 | ||||
|     // Setup Custom Widget view
 | ||||
| @ -78,25 +78,23 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) { | ||||
|         string_clear(disp_currency); | ||||
|     } | ||||
|     string_clear(currency_name); | ||||
|     char temp_str[32]; | ||||
|     // Add ATQA
 | ||||
|     char atqa_str[16]; | ||||
|     snprintf(atqa_str, sizeof(atqa_str), "ATQA: %02X%02X", nfc_data->atqa[0], nfc_data->atqa[1]); | ||||
|     widget_add_string_element(nfc->widget, 121, 32, AlignRight, AlignTop, FontSecondary, atqa_str); | ||||
|     snprintf(temp_str, sizeof(temp_str), "ATQA: %02X%02X", nfc_data->atqa[0], nfc_data->atqa[1]); | ||||
|     widget_add_string_element(nfc->widget, 121, 32, AlignRight, AlignTop, FontSecondary, temp_str); | ||||
|     // Add UID
 | ||||
|     char uid_str[32]; | ||||
|     snprintf( | ||||
|         uid_str, | ||||
|         sizeof(uid_str), | ||||
|         temp_str, | ||||
|         sizeof(temp_str), | ||||
|         "UID: %02X %02X %02X %02X", | ||||
|         nfc_data->uid[0], | ||||
|         nfc_data->uid[1], | ||||
|         nfc_data->uid[2], | ||||
|         nfc_data->uid[3]); | ||||
|     widget_add_string_element(nfc->widget, 7, 42, AlignLeft, AlignTop, FontSecondary, uid_str); | ||||
|     widget_add_string_element(nfc->widget, 7, 42, AlignLeft, AlignTop, FontSecondary, temp_str); | ||||
|     // Add SAK
 | ||||
|     char sak_str[16]; | ||||
|     snprintf(sak_str, sizeof(sak_str), "SAK: %02X", nfc_data->sak); | ||||
|     widget_add_string_element(nfc->widget, 121, 42, AlignRight, AlignTop, FontSecondary, sak_str); | ||||
|     snprintf(temp_str, sizeof(temp_str), "SAK: %02X", nfc_data->sak); | ||||
|     widget_add_string_element(nfc->widget, 121, 42, AlignRight, AlignTop, FontSecondary, temp_str); | ||||
|     // Add expiration date
 | ||||
|     if(emv_data->exp_mon) { | ||||
|         char exp_str[16]; | ||||
| @ -117,28 +115,30 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_read_emv_data_success_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == GuiButtonTypeLeft) { | ||||
|             return scene_manager_search_and_switch_to_previous_scene( | ||||
|             consumed = scene_manager_search_and_switch_to_previous_scene( | ||||
|                 nfc->scene_manager, NfcSceneReadEmvAppSuccess); | ||||
|         } else if(event.event == GuiButtonTypeRight) { | ||||
|             // Clear device name
 | ||||
|             nfc_device_set_name(nfc->dev, ""); | ||||
|             nfc->dev->format = NfcDeviceSaveFormatBankCard; | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeBack) { | ||||
|         return scene_manager_search_and_switch_to_previous_scene( | ||||
|         consumed = scene_manager_search_and_switch_to_previous_scene( | ||||
|             nfc->scene_manager, NfcSceneReadEmvAppSuccess); | ||||
|     } | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_emv_data_success_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     widget_reset(nfc->widget); | ||||
| } | ||||
|  | ||||
| @ -2,12 +2,12 @@ | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| void nfc_read_mifare_desfire_worker_callback(NfcWorkerEvent event, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_mifare_desfire_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcRead); | ||||
| 
 | ||||
|     // Setup view
 | ||||
| @ -26,31 +26,28 @@ void nfc_scene_read_mifare_desfire_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_read_mifare_desfire_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcCustomEventWorkerExit) { | ||||
|             notification_message(nfc->notifications, &sequence_success); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadMifareDesfireSuccess); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|         notification_message(nfc->notifications, &sequence_blink_blue_10); | ||||
|         DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||
|         return true; | ||||
|         consumed = true; | ||||
|     } | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_mifare_desfire_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Stop worker
 | ||||
|     nfc_worker_stop(nfc->worker); | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
|     popup_reset(nfc->popup); | ||||
| } | ||||
|  | ||||
| @ -9,13 +9,13 @@ enum { | ||||
| }; | ||||
| 
 | ||||
| void nfc_scene_read_mifare_desfire_success_dialog_callback(DialogExResult result, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_mifare_desfire_success_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; | ||||
|     DialogEx* dialog_ex = nfc->dialog_ex; | ||||
| @ -67,9 +67,9 @@ void nfc_scene_read_mifare_desfire_success_on_enter(void* context) { | ||||
| 
 | ||||
| bool nfc_scene_read_mifare_desfire_success_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
|     uint32_t state = | ||||
|         scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareDesfireSuccess); | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(state == ReadMifareDesfireSuccessStateShowUID && event.event == DialogExResultLeft) { | ||||
| @ -98,9 +98,8 @@ bool nfc_scene_read_mifare_desfire_success_on_event(void* context, SceneManagerE | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_mifare_desfire_success_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clean dialog
 | ||||
|     DialogEx* dialog_ex = nfc->dialog_ex; | ||||
|     dialog_ex_reset(dialog_ex); | ||||
|     dialog_ex_reset(nfc->dialog_ex); | ||||
| } | ||||
|  | ||||
| @ -19,7 +19,7 @@ void nfc_scene_read_mifare_ul_on_enter(void* context) { | ||||
|     // Start worker
 | ||||
|     nfc_worker_start( | ||||
|         nfc->worker, | ||||
|         NfcWorkerStateReadMifareUl, | ||||
|         NfcWorkerStateReadMifareUltralight, | ||||
|         &nfc->dev->dev_data, | ||||
|         nfc_read_mifare_ul_worker_callback, | ||||
|         nfc); | ||||
| @ -43,6 +43,7 @@ bool nfc_scene_read_mifare_ul_on_event(void* context, SceneManagerEvent event) { | ||||
| 
 | ||||
| void nfc_scene_read_mifare_ul_on_exit(void* context) { | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Stop worker
 | ||||
|     nfc_worker_stop(nfc->worker); | ||||
|     // Clear view
 | ||||
|  | ||||
| @ -9,21 +9,21 @@ enum { | ||||
| }; | ||||
| 
 | ||||
| void nfc_scene_read_mifare_ul_success_dialog_callback(DialogExResult result, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_mifare_ul_success_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||
| 
 | ||||
|     // Send notification
 | ||||
|     notification_message(nfc->notifications, &sequence_success); | ||||
| 
 | ||||
|     // Setup dialog view
 | ||||
|     NfcDeviceCommonData* data = &nfc->dev->dev_data.nfc_data; | ||||
|     MifareUlData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; | ||||
|     FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; | ||||
|     MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; | ||||
|     DialogEx* dialog_ex = nfc->dialog_ex; | ||||
|     dialog_ex_set_left_button_text(dialog_ex, "Retry"); | ||||
|     dialog_ex_set_right_button_text(dialog_ex, "More"); | ||||
| @ -69,9 +69,9 @@ void nfc_scene_read_mifare_ul_success_on_enter(void* context) { | ||||
| 
 | ||||
| bool nfc_scene_read_mifare_ul_success_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
|     uint32_t state = | ||||
|         scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadMifareUlSuccess); | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(state == ReadMifareUlStateShowUID && event.event == DialogExResultLeft) { | ||||
| @ -99,14 +99,10 @@ bool nfc_scene_read_mifare_ul_success_on_event(void* context, SceneManagerEvent | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_mifare_ul_success_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clean dialog
 | ||||
|     DialogEx* dialog_ex = nfc->dialog_ex; | ||||
|     dialog_ex_reset(dialog_ex); | ||||
| 
 | ||||
|     // Clean TextBox
 | ||||
|     TextBox* text_box = nfc->text_box; | ||||
|     text_box_reset(text_box); | ||||
|     // Clean views
 | ||||
|     dialog_ex_reset(nfc->dialog_ex); | ||||
|     text_box_reset(nfc->text_box); | ||||
|     string_reset(nfc->text_box_store); | ||||
| } | ||||
|  | ||||
| @ -1,12 +1,12 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| void nfc_scene_restore_original_popup_callback(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_restore_original_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     Popup* popup = nfc->popup; | ||||
| @ -20,7 +20,7 @@ void nfc_scene_restore_original_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_restore_original_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
| @ -32,15 +32,8 @@ bool nfc_scene_restore_original_on_event(void* context, SceneManagerEvent event) | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_restore_original_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
|     popup_set_callback(popup, NULL); | ||||
|     popup_set_context(popup, NULL); | ||||
|     popup_set_timeout(popup, 0); | ||||
|     popup_disable_timeout(popup); | ||||
|     popup_reset(nfc->popup); | ||||
| } | ||||
|  | ||||
| @ -1,15 +1,13 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| #define NFC_SCENE_READ_SUCCESS_SHIFT "              " | ||||
| 
 | ||||
| void nfc_scene_run_emv_app_confirm_dialog_callback(DialogExResult result, void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, result); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_run_emv_app_confirm_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     DialogEx* dialog_ex = nfc->dialog_ex; | ||||
|     dialog_ex_set_left_button_text(dialog_ex, "Back"); | ||||
| @ -29,28 +27,23 @@ void nfc_scene_run_emv_app_confirm_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_run_emv_app_confirm_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == DialogExResultLeft) { | ||||
|             return scene_manager_previous_scene(nfc->scene_manager); | ||||
|             consumed = scene_manager_previous_scene(nfc->scene_manager); | ||||
|         } else if(event.event == DialogExResultRight) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadEmvData); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_run_emv_app_confirm_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     DialogEx* dialog_ex = nfc->dialog_ex; | ||||
|     dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter); | ||||
|     dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     dialog_ex_set_icon(dialog_ex, 0, 0, NULL); | ||||
|     dialog_ex_set_left_button_text(dialog_ex, NULL); | ||||
|     dialog_ex_set_right_button_text(dialog_ex, NULL); | ||||
|     dialog_ex_set_result_callback(dialog_ex, NULL); | ||||
|     dialog_ex_set_context(dialog_ex, NULL); | ||||
|     // Clean view
 | ||||
|     dialog_ex_reset(nfc->dialog_ex); | ||||
| } | ||||
|  | ||||
| @ -3,13 +3,13 @@ | ||||
| #include <gui/modules/validators.h> | ||||
| 
 | ||||
| void nfc_scene_save_name_text_input_callback(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_save_name_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     TextInput* text_input = nfc->text_input; | ||||
| @ -37,7 +37,8 @@ void nfc_scene_save_name_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcCustomEventTextInputDone) { | ||||
| @ -50,18 +51,18 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { | ||||
|             strlcpy(nfc->dev->dev_name, nfc->text_store, strlen(nfc->text_store) + 1); | ||||
|             if(nfc_device_save(nfc->dev, nfc->text_store)) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); | ||||
|                 return true; | ||||
|                 consumed = true; | ||||
|             } else { | ||||
|                 return scene_manager_search_and_switch_to_previous_scene( | ||||
|                 consumed = scene_manager_search_and_switch_to_previous_scene( | ||||
|                     nfc->scene_manager, NfcSceneStart); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_save_name_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     void* validator_context = text_input_get_validator_callback_context(nfc->text_input); | ||||
|  | ||||
| @ -2,12 +2,12 @@ | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| void nfc_scene_save_success_popup_callback(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_save_success_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     DOLPHIN_DEED(DolphinDeedNfcSave); | ||||
| 
 | ||||
|     // Setup view
 | ||||
| @ -22,7 +22,7 @@ void nfc_scene_save_success_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
| @ -47,15 +47,8 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_save_success_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     Popup* popup = nfc->popup; | ||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
|     popup_set_callback(popup, NULL); | ||||
|     popup_set_context(popup, NULL); | ||||
|     popup_set_timeout(popup, 0); | ||||
|     popup_disable_timeout(popup); | ||||
|     popup_reset(nfc->popup); | ||||
| } | ||||
|  | ||||
| @ -9,13 +9,13 @@ enum SubmenuIndex { | ||||
| }; | ||||
| 
 | ||||
| void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_saved_menu_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     Submenu* submenu = nfc->submenu; | ||||
| 
 | ||||
|     if(nfc->dev->format == NfcDeviceSaveFormatUid || | ||||
| @ -56,7 +56,7 @@ void nfc_scene_saved_menu_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
| @ -92,7 +92,7 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_saved_menu_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     submenu_reset(nfc->submenu); | ||||
| } | ||||
|  | ||||
| @ -1,13 +1,13 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| void nfc_scene_set_atqa_byte_input_callback(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_set_atqa_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     ByteInput* byte_input = nfc->byte_input; | ||||
| @ -23,19 +23,20 @@ void nfc_scene_set_atqa_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_set_atqa_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcCustomEventByteInputDone) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_set_atqa_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); | ||||
|  | ||||
| @ -1,13 +1,13 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| void nfc_scene_set_sak_byte_input_callback(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_set_sak_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     ByteInput* byte_input = nfc->byte_input; | ||||
| @ -23,19 +23,20 @@ void nfc_scene_set_sak_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_set_sak_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcCustomEventByteInputDone) { | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSetAtqua); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_set_sak_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); | ||||
|  | ||||
| @ -6,13 +6,13 @@ enum SubmenuIndex { | ||||
| }; | ||||
| 
 | ||||
| void nfc_scene_set_type_submenu_callback(void* context, uint32_t index) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_set_type_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     Submenu* submenu = nfc->submenu; | ||||
|     // Clear device name
 | ||||
|     nfc_device_set_name(nfc->dev, ""); | ||||
| @ -24,26 +24,27 @@ void nfc_scene_set_type_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubmenuIndexNFCA7) { | ||||
|             nfc->dev->dev_data.nfc_data.uid_len = 7; | ||||
|             nfc->dev->format = NfcDeviceSaveFormatUid; | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexNFCA4) { | ||||
|             nfc->dev->dev_data.nfc_data.uid_len = 4; | ||||
|             nfc->dev->format = NfcDeviceSaveFormatUid; | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_set_type_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     submenu_reset(nfc->submenu); | ||||
| } | ||||
|  | ||||
| @ -2,13 +2,13 @@ | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| void nfc_scene_set_uid_byte_input_callback(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_set_uid_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Setup view
 | ||||
|     ByteInput* byte_input = nfc->byte_input; | ||||
| @ -26,19 +26,20 @@ void nfc_scene_set_uid_on_enter(void* context) { | ||||
| 
 | ||||
| bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcCustomEventByteInputDone) { | ||||
|             DOLPHIN_DEED(DolphinDeedNfcAdd); | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); | ||||
|             return true; | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_set_uid_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); | ||||
|  | ||||
| @ -9,13 +9,13 @@ enum SubmenuIndex { | ||||
| }; | ||||
| 
 | ||||
| void nfc_scene_start_submenu_callback(void* context, uint32_t index) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_start_on_enter(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     Submenu* submenu = nfc->submenu; | ||||
| 
 | ||||
|     submenu_add_item( | ||||
| @ -43,7 +43,7 @@ void nfc_scene_start_on_enter(void* context) { | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
| @ -70,7 +70,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_start_on_exit(void* context) { | ||||
|     Nfc* nfc = (Nfc*)context; | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     submenu_reset(nfc->submenu); | ||||
| } | ||||
|  | ||||
| @ -59,6 +59,8 @@ typedef enum { | ||||
|     NotificationMessageTypeForceSpeakerVolumeSetting, | ||||
|     NotificationMessageTypeForceVibroSetting, | ||||
|     NotificationMessageTypeForceDisplayBrightnessSetting, | ||||
| 
 | ||||
|     NotificationMessageTypeLedBrightnessSettingApply, | ||||
| } NotificationMessageType; | ||||
| 
 | ||||
| typedef struct { | ||||
|  | ||||
| @ -212,18 +212,21 @@ void notification_process_notification_message( | ||||
|             // store and send on delay or after seq
 | ||||
|             led_active = true; | ||||
|             led_values[0] = notification_message->data.led.value; | ||||
|             app->led[0].value_last[LayerNotification] = led_values[0]; | ||||
|             reset_mask |= reset_red_mask; | ||||
|             break; | ||||
|         case NotificationMessageTypeLedGreen: | ||||
|             // store and send on delay or after seq
 | ||||
|             led_active = true; | ||||
|             led_values[1] = notification_message->data.led.value; | ||||
|             app->led[1].value_last[LayerNotification] = led_values[1]; | ||||
|             reset_mask |= reset_green_mask; | ||||
|             break; | ||||
|         case NotificationMessageTypeLedBlue: | ||||
|             // store and send on delay or after seq
 | ||||
|             led_active = true; | ||||
|             led_values[2] = notification_message->data.led.value; | ||||
|             app->led[2].value_last[LayerNotification] = led_values[2]; | ||||
|             reset_mask |= reset_blue_mask; | ||||
|             break; | ||||
|         case NotificationMessageTypeVibro: | ||||
| @ -273,6 +276,16 @@ void notification_process_notification_message( | ||||
|         case NotificationMessageTypeForceDisplayBrightnessSetting: | ||||
|             display_brightness_setting = | ||||
|                 notification_message->data.forced_settings.display_brightness; | ||||
|             break; | ||||
|         case NotificationMessageTypeLedBrightnessSettingApply: | ||||
|             led_active = true; | ||||
|             for(uint8_t i = 0; i < NOTIFICATION_LED_COUNT; i++) { | ||||
|                 led_values[i] = app->led[i].value_last[LayerNotification]; | ||||
|             } | ||||
|             reset_mask |= reset_red_mask; | ||||
|             reset_mask |= reset_green_mask; | ||||
|             reset_mask |= reset_blue_mask; | ||||
|             break; | ||||
|         } | ||||
|         notification_message_index++; | ||||
|         notification_message = (*message->sequence)[notification_message_index]; | ||||
| @ -316,23 +329,33 @@ void notification_process_internal_message(NotificationApp* app, NotificationApp | ||||
|                     app, notification_message->data.led.value)); | ||||
|             break; | ||||
|         case NotificationMessageTypeLedRed: | ||||
|             app->led[0].value_last[LayerInternal] = notification_message->data.led.value; | ||||
|             notification_apply_internal_led_layer( | ||||
|                 &app->led[0], | ||||
|                 notification_settings_get_rgb_led_brightness( | ||||
|                     app, notification_message->data.led.value)); | ||||
|             break; | ||||
|         case NotificationMessageTypeLedGreen: | ||||
|             app->led[1].value_last[LayerInternal] = notification_message->data.led.value; | ||||
|             notification_apply_internal_led_layer( | ||||
|                 &app->led[1], | ||||
|                 notification_settings_get_rgb_led_brightness( | ||||
|                     app, notification_message->data.led.value)); | ||||
|             break; | ||||
|         case NotificationMessageTypeLedBlue: | ||||
|             app->led[2].value_last[LayerInternal] = notification_message->data.led.value; | ||||
|             notification_apply_internal_led_layer( | ||||
|                 &app->led[2], | ||||
|                 notification_settings_get_rgb_led_brightness( | ||||
|                     app, notification_message->data.led.value)); | ||||
|             break; | ||||
|         case NotificationMessageTypeLedBrightnessSettingApply: | ||||
|             for(uint8_t i = 0; i < NOTIFICATION_LED_COUNT; i++) { | ||||
|                 uint8_t new_val = notification_settings_get_rgb_led_brightness( | ||||
|                     app, app->led[i].value_last[LayerInternal]); | ||||
|                 notification_apply_internal_led_layer(&app->led[i], new_val); | ||||
|             } | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|  | ||||
| @ -25,6 +25,7 @@ typedef enum { | ||||
| } NotificationLedLayerIndex; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint8_t value_last[LayerMAX]; | ||||
|     uint8_t value[LayerMAX]; | ||||
|     NotificationLedLayerIndex index; | ||||
|     Light light; | ||||
|  | ||||
| @ -82,12 +82,22 @@ static void screen_changed(VariableItem* item) { | ||||
|     notification_message(app->notification, &sequence_display_on); | ||||
| } | ||||
| 
 | ||||
| const NotificationMessage apply_message = { | ||||
|     .type = NotificationMessageTypeLedBrightnessSettingApply, | ||||
| }; | ||||
| const NotificationSequence apply_sequence = { | ||||
|     &apply_message, | ||||
|     NULL, | ||||
| }; | ||||
| 
 | ||||
| static void led_changed(VariableItem* item) { | ||||
|     NotificationAppSettings* app = variable_item_get_context(item); | ||||
|     uint8_t index = variable_item_get_current_value_index(item); | ||||
| 
 | ||||
|     variable_item_set_current_value_text(item, backlight_text[index]); | ||||
|     app->notification->settings.led_brightness = backlight_value[index]; | ||||
|     notification_message(app->notification, &apply_sequence); | ||||
|     notification_internal_message(app->notification, &apply_sequence); | ||||
|     notification_message(app->notification, &sequence_blink_white_100); | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										26
									
								
								applications/power/power_service/power.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										26
									
								
								applications/power/power_service/power.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -12,14 +12,19 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { | ||||
|     furi_assert(context); | ||||
|     Power* power = context; | ||||
|     canvas_draw_icon(canvas, 0, 1, &I_Battery_26x8); | ||||
|     canvas_draw_box(canvas, 2, 3, (power->info.charge + 4) / 5, 4); | ||||
|     if(power->state == PowerStateCharging) { | ||||
|         canvas_set_bitmap_mode(canvas, 1); | ||||
|         canvas_set_color(canvas, ColorWhite); | ||||
|         canvas_draw_icon(canvas, 8, 0, &I_Charging_lightning_mask_9x10); | ||||
|         canvas_set_color(canvas, ColorBlack); | ||||
|         canvas_draw_icon(canvas, 8, 0, &I_Charging_lightning_9x10); | ||||
|         canvas_set_bitmap_mode(canvas, 0); | ||||
| 
 | ||||
|     if(power->info.gauge_is_ok) { | ||||
|         canvas_draw_box(canvas, 2, 3, (power->info.charge + 4) / 5, 4); | ||||
|         if(power->state == PowerStateCharging) { | ||||
|             canvas_set_bitmap_mode(canvas, 1); | ||||
|             canvas_set_color(canvas, ColorWhite); | ||||
|             canvas_draw_icon(canvas, 8, 0, &I_Charging_lightning_mask_9x10); | ||||
|             canvas_set_color(canvas, ColorBlack); | ||||
|             canvas_draw_icon(canvas, 8, 0, &I_Charging_lightning_9x10); | ||||
|             canvas_set_bitmap_mode(canvas, 0); | ||||
|         } | ||||
|     } else { | ||||
|         canvas_draw_box(canvas, 8, 4, 8, 2); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -119,6 +124,7 @@ static void power_check_charging_state(Power* power) { | ||||
| static bool power_update_info(Power* power) { | ||||
|     PowerInfo info; | ||||
| 
 | ||||
|     info.gauge_is_ok = furi_hal_power_gauge_is_ok(); | ||||
|     info.charge = furi_hal_power_get_pct(); | ||||
|     info.health = furi_hal_power_get_bat_health_pct(); | ||||
|     info.capacity_remaining = furi_hal_power_get_battery_remaining_capacity(); | ||||
| @ -140,6 +146,10 @@ static bool power_update_info(Power* power) { | ||||
| } | ||||
| 
 | ||||
| static void power_check_low_battery(Power* power) { | ||||
|     if(!power->info.gauge_is_ok) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // Check battery charge and vbus voltage
 | ||||
|     if((power->info.charge == 0) && (power->info.voltage_vbus < 4.0f) && | ||||
|        power->show_low_bat_level_message) { | ||||
|  | ||||
| @ -29,6 +29,8 @@ typedef struct { | ||||
| } PowerEvent; | ||||
| 
 | ||||
| typedef struct { | ||||
|     bool gauge_is_ok; | ||||
| 
 | ||||
|     float current_charger; | ||||
|     float current_gauge; | ||||
| 
 | ||||
|  | ||||
| @ -49,7 +49,7 @@ bool subghz_scene_delete_on_event(void* context, SceneManagerEvent event) { | ||||
|     SubGhz* subghz = context; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubGhzCustomEventSceneDelete) { | ||||
|             strncpy(subghz->file_name_tmp, subghz->file_name, SUBGHZ_MAX_LEN_NAME); | ||||
|             strncpy(subghz->file_path_tmp, subghz->file_path, SUBGHZ_MAX_LEN_NAME); | ||||
|             if(subghz_delete_file(subghz)) { | ||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); | ||||
|             } else { | ||||
|  | ||||
| @ -21,10 +21,15 @@ void subghz_scene_delete_raw_on_enter(void* context) { | ||||
|     string_init(frequency_str); | ||||
|     string_init(modulation_str); | ||||
| 
 | ||||
|     char delete_str[64]; | ||||
|     snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", subghz->file_name); | ||||
|     char delete_str[SUBGHZ_MAX_LEN_NAME + 16]; | ||||
|     string_t file_name; | ||||
|     string_init(file_name); | ||||
|     path_extract_filename_no_ext(subghz->file_path, file_name); | ||||
|     snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", string_get_cstr(file_name)); | ||||
|     string_clear(file_name); | ||||
| 
 | ||||
|     widget_add_text_box_element( | ||||
|         subghz->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str); | ||||
|         subghz->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str, false); | ||||
| 
 | ||||
|     widget_add_string_element( | ||||
|         subghz->widget, 38, 25, AlignLeft, AlignTop, FontSecondary, "RAW signal"); | ||||
| @ -56,7 +61,7 @@ bool subghz_scene_delete_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|     SubGhz* subghz = context; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubGhzCustomEventSceneDeleteRAW) { | ||||
|             strncpy(subghz->file_name_tmp, subghz->file_name, SUBGHZ_MAX_LEN_NAME); | ||||
|             strncpy(subghz->file_path_tmp, subghz->file_path, SUBGHZ_MAX_LEN_NAME); | ||||
|             if(subghz_delete_file(subghz)) { | ||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); | ||||
|             } else { | ||||
|  | ||||
| @ -45,7 +45,7 @@ bool subghz_scene_more_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteRAW); | ||||
|             return true; | ||||
|         } else if(event.event == SubmenuIndexEdit) { | ||||
|             memset(subghz->file_name_tmp, 0, sizeof(subghz->file_name_tmp)); | ||||
|             memset(subghz->file_path_tmp, 0, sizeof(subghz->file_path_tmp)); | ||||
|             scene_manager_set_scene_state( | ||||
|                 subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexEdit); | ||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Aleksandr Kutuzov
						Aleksandr Kutuzov