diff --git a/application.fam b/application.fam index 4c76f24..81bc63b 100644 --- a/application.fam +++ b/application.fam @@ -22,11 +22,13 @@ App( "scenes/tagtinker_scene_image_upload.c", "scenes/tagtinker_scene_main_menu.c", "scenes/tagtinker_scene_preset_list.c", + "scenes/tagtinker_scene_settings.c", "scenes/tagtinker_scene_size_picker.c", "scenes/tagtinker_scene_target_actions.c", "scenes/tagtinker_scene_target_menu.c", "scenes/tagtinker_scene_text_input.c", "scenes/tagtinker_scene_transmit.c", + "scenes/tagtinker_scene_warning.c", "views/numlock_input.c", ], ) diff --git a/scenes/tagtinker_scene.c b/scenes/tagtinker_scene.c index e92ef50..147e84d 100644 --- a/scenes/tagtinker_scene.c +++ b/scenes/tagtinker_scene.c @@ -5,7 +5,9 @@ #include "tagtinker_scene.h" void(*const tagtinker_scene_on_enter_handlers[])(void*) = { + tagtinker_scene_warning_on_enter, tagtinker_scene_main_menu_on_enter, + tagtinker_scene_settings_on_enter, tagtinker_scene_broadcast_menu_on_enter, tagtinker_scene_broadcast_on_enter, tagtinker_scene_target_menu_on_enter, @@ -20,7 +22,9 @@ void(*const tagtinker_scene_on_enter_handlers[])(void*) = { }; bool(*const tagtinker_scene_on_event_handlers[])(void*, SceneManagerEvent) = { + tagtinker_scene_warning_on_event, tagtinker_scene_main_menu_on_event, + tagtinker_scene_settings_on_event, tagtinker_scene_broadcast_menu_on_event, tagtinker_scene_broadcast_on_event, tagtinker_scene_target_menu_on_event, @@ -35,7 +39,9 @@ bool(*const tagtinker_scene_on_event_handlers[])(void*, SceneManagerEvent) = { }; void(*const tagtinker_scene_on_exit_handlers[])(void*) = { + tagtinker_scene_warning_on_exit, tagtinker_scene_main_menu_on_exit, + tagtinker_scene_settings_on_exit, tagtinker_scene_broadcast_menu_on_exit, tagtinker_scene_broadcast_on_exit, tagtinker_scene_target_menu_on_exit, diff --git a/scenes/tagtinker_scene.h b/scenes/tagtinker_scene.h index 03a988e..992ede5 100644 --- a/scenes/tagtinker_scene.h +++ b/scenes/tagtinker_scene.h @@ -7,7 +7,9 @@ #include typedef enum { + TagTinkerSceneWarning, TagTinkerSceneMainMenu, + TagTinkerSceneSettings, TagTinkerSceneBroadcastMenu, TagTinkerSceneBroadcast, TagTinkerSceneTargetMenu, @@ -23,10 +25,18 @@ typedef enum { } TagTinkerScene; /* Scene handler declarations */ +void tagtinker_scene_warning_on_enter(void* ctx); +bool tagtinker_scene_warning_on_event(void* ctx, SceneManagerEvent event); +void tagtinker_scene_warning_on_exit(void* ctx); + void tagtinker_scene_main_menu_on_enter(void* ctx); bool tagtinker_scene_main_menu_on_event(void* ctx, SceneManagerEvent event); void tagtinker_scene_main_menu_on_exit(void* ctx); +void tagtinker_scene_settings_on_enter(void* ctx); +bool tagtinker_scene_settings_on_event(void* ctx, SceneManagerEvent event); +void tagtinker_scene_settings_on_exit(void* ctx); + void tagtinker_scene_broadcast_menu_on_enter(void* ctx); bool tagtinker_scene_broadcast_menu_on_event(void* ctx, SceneManagerEvent event); void tagtinker_scene_broadcast_menu_on_exit(void* ctx); diff --git a/scenes/tagtinker_scene_main_menu.c b/scenes/tagtinker_scene_main_menu.c index a65d411..64142eb 100644 --- a/scenes/tagtinker_scene_main_menu.c +++ b/scenes/tagtinker_scene_main_menu.c @@ -7,6 +7,7 @@ enum { MainMenuBroadcast, MainMenuTargetESL, + MainMenuSettings, MainMenuAndroid, MainMenuAbout, }; @@ -23,9 +24,10 @@ void tagtinker_scene_main_menu_on_enter(void* ctx) { submenu_set_header(app->submenu, TAGTINKER_DISPLAY_NAME " v" TAGTINKER_VERSION); submenu_add_item(app->submenu, "Broadcast Tag", MainMenuBroadcast, main_menu_cb, app); - submenu_add_item(app->submenu, "Target Tag", MainMenuTargetESL, main_menu_cb, app); - submenu_add_item(app->submenu, "Android App", MainMenuAndroid, main_menu_cb, app); - submenu_add_item(app->submenu, "About", MainMenuAbout, main_menu_cb, app); + submenu_add_item(app->submenu, "Target Tag", MainMenuTargetESL, main_menu_cb, app); + submenu_add_item(app->submenu, "Settings", MainMenuSettings, main_menu_cb, app); + submenu_add_item(app->submenu, "Android App", MainMenuAndroid, main_menu_cb, app); + submenu_add_item(app->submenu, "About", MainMenuAbout, main_menu_cb, app); submenu_set_selected_item( app->submenu, @@ -47,6 +49,9 @@ bool tagtinker_scene_main_menu_on_event(void* ctx, SceneManagerEvent event) { case MainMenuTargetESL: scene_manager_next_scene(app->scene_manager, TagTinkerSceneTargetMenu); return true; + case MainMenuSettings: + scene_manager_next_scene(app->scene_manager, TagTinkerSceneSettings); + return true; case MainMenuAndroid: /* state=1 tells About scene to show Android teaser */ scene_manager_set_scene_state(app->scene_manager, TagTinkerSceneAbout, 1); diff --git a/scenes/tagtinker_scene_settings.c b/scenes/tagtinker_scene_settings.c new file mode 100644 index 0000000..7264397 --- /dev/null +++ b/scenes/tagtinker_scene_settings.c @@ -0,0 +1,50 @@ +/* + * Settings scene + */ + +#include "../tagtinker_app.h" + +enum { + SettingsItemStartupWarning, +}; + +static const char* settings_toggle_labels[] = {"Off", "On"}; + +static void startup_warning_changed(VariableItem* item) { + TagTinkerApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + app->show_startup_warning = (index == 1); + variable_item_set_current_value_text(item, settings_toggle_labels[index]); + + if(!tagtinker_settings_save(app)) { + FURI_LOG_W(TAGTINKER_TAG, "Failed to save settings"); + } +} + +void tagtinker_scene_settings_on_enter(void* ctx) { + TagTinkerApp* app = ctx; + VariableItemList* list = app->var_item_list; + + variable_item_list_reset(list); + + VariableItem* item = variable_item_list_add( + list, "Startup Warning", 2, startup_warning_changed, app); + variable_item_set_current_value_index(item, app->show_startup_warning ? 1 : 0); + variable_item_set_current_value_text( + item, settings_toggle_labels[app->show_startup_warning ? 1 : 0]); + + variable_item_list_set_selected_item(list, SettingsItemStartupWarning); + view_dispatcher_switch_to_view(app->view_dispatcher, TagTinkerViewVarItemList); +} + +bool tagtinker_scene_settings_on_event(void* ctx, SceneManagerEvent event) { + UNUSED(ctx); + UNUSED(event); + return false; +} + +void tagtinker_scene_settings_on_exit(void* ctx) { + TagTinkerApp* app = ctx; + variable_item_list_reset(app->var_item_list); +} diff --git a/scenes/tagtinker_scene_warning.c b/scenes/tagtinker_scene_warning.c new file mode 100644 index 0000000..8673e9c --- /dev/null +++ b/scenes/tagtinker_scene_warning.c @@ -0,0 +1,60 @@ +/* + * Startup warning scene + */ + +#include "../tagtinker_app.h" + +enum { + TagTinkerStartupWarningContinue, +}; + +static const char* startup_warning_text = + "\ec\e#TagTinker - Research Tool\n" + "\n" + "This is an educational research tool for infrared electronic shelf label " + "(ESL) protocols.\n" + "It implements publicly documented signaling for tags commonly used in " + "retail (often referred to as Pricer-style ESLs).\n" + "Use ONLY on tags you personally own or have explicit permission to test.\n" + "Using this tool on retail store systems without authorization may violate " + "laws on fraud or interference with business operations. You are solely " + "responsible for your actions. Misuse is not endorsed.\n" + "\n" + "\ecPress OK to continue."; + +static void startup_warning_button_cb(GuiButtonType result, InputType type, void* context) { + if(type != InputTypeShort || result != GuiButtonTypeCenter) return; + + TagTinkerApp* app = context; + view_dispatcher_send_custom_event( + app->view_dispatcher, TagTinkerStartupWarningContinue); +} + +void tagtinker_scene_warning_on_enter(void* ctx) { + TagTinkerApp* app = ctx; + + widget_reset(app->widget); + widget_add_text_scroll_element(app->widget, 0, 0, 128, 52, startup_warning_text); + widget_add_button_element( + app->widget, GuiButtonTypeCenter, "OK", startup_warning_button_cb, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, TagTinkerViewWidget); +} + +bool tagtinker_scene_warning_on_event(void* ctx, SceneManagerEvent event) { + TagTinkerApp* app = ctx; + + if(event.type == SceneManagerEventTypeCustom && + event.event == TagTinkerStartupWarningContinue) { + scene_manager_search_and_switch_to_another_scene( + app->scene_manager, TagTinkerSceneMainMenu); + return true; + } + + return false; +} + +void tagtinker_scene_warning_on_exit(void* ctx) { + TagTinkerApp* app = ctx; + widget_reset(app->widget); +} diff --git a/tagtinker_app.c b/tagtinker_app.c index 71cee6b..eac4d55 100644 --- a/tagtinker_app.c +++ b/tagtinker_app.c @@ -29,6 +29,42 @@ static bool custom_event_cb(void* ctx, uint32_t event) { extern const SceneManagerHandlers tagtinker_scene_handlers; +void tagtinker_settings_load(TagTinkerApp* app) { + app->show_startup_warning = true; + + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + if(storage_file_open(file, APP_DATA_PATH("settings.txt"), FSAM_READ, FSOM_OPEN_EXISTING)) { + char value = '1'; + if(storage_file_read(file, &value, 1) == 1) { + app->show_startup_warning = (value != '0'); + } + storage_file_close(file); + } + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +bool tagtinker_settings_save(const TagTinkerApp* app) { + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_mkdir(storage, APP_DATA_PATH("")); + + File* file = storage_file_alloc(storage); + bool ok = false; + + if(storage_file_open(file, APP_DATA_PATH("settings.txt"), FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + const char value = app->show_startup_warning ? '1' : '0'; + ok = (storage_file_write(file, &value, 1) == 1); + storage_file_close(file); + } + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return ok; +} + void tagtinker_targets_load(TagTinkerApp* app) { app->target_count = 0; @@ -127,6 +163,7 @@ static TagTinkerApp* app_alloc(void) { app->invert_text = false; strcpy(app->text_input_buf, "TagTinker"); app->selected_target = -1; + tagtinker_settings_load(app); tagtinker_targets_load(app); /* Scene manager */ @@ -236,7 +273,9 @@ static void app_free(TagTinkerApp* app) { int32_t tagtinker_app_main(void* p) { UNUSED(p); TagTinkerApp* app = app_alloc(); - scene_manager_next_scene(app->scene_manager, TagTinkerSceneMainMenu); + scene_manager_next_scene( + app->scene_manager, + app->show_startup_warning ? TagTinkerSceneWarning : TagTinkerSceneMainMenu); view_dispatcher_run(app->view_dispatcher); app_free(app); return 0; diff --git a/tagtinker_app.h b/tagtinker_app.h index 61c0580..a2583cb 100644 --- a/tagtinker_app.h +++ b/tagtinker_app.h @@ -86,6 +86,7 @@ struct TagTinkerApp { uint16_t repeats; bool forever; bool tx_spam; + bool show_startup_warning; /* Current target */ char barcode[TAGTINKER_BC_LEN + 1]; @@ -140,6 +141,8 @@ struct TagTinkerApp { typedef enum { TagTinkerMenuBroadcast, TagTinkerMenuTargetESL, + TagTinkerMenuSettings, + TagTinkerMenuAndroid, TagTinkerMenuAbout, } TagTinkerMainMenuItem; @@ -156,5 +159,7 @@ typedef enum { TagTinkerTargetPingFlash, } TagTinkerTargetActionItem; +void tagtinker_settings_load(TagTinkerApp* app); +bool tagtinker_settings_save(const TagTinkerApp* app); void tagtinker_targets_load(TagTinkerApp* app); bool tagtinker_targets_save(const TagTinkerApp* app);