From d2eb38d59001cfd06d46b51690c9775d73419adb Mon Sep 17 00:00:00 2001 From: i12bp8 Date: Mon, 6 Apr 2026 21:20:14 +0200 Subject: [PATCH] Refine startup warning UI --- scenes/tagtinker_scene_about.c | 2 - scenes/tagtinker_scene_warning.c | 152 ++++++++++++++++++++++++++----- tagtinker_app.c | 6 ++ tagtinker_app.h | 3 + 4 files changed, 137 insertions(+), 26 deletions(-) diff --git a/scenes/tagtinker_scene_about.c b/scenes/tagtinker_scene_about.c index b5588d4..fb2f3f8 100644 --- a/scenes/tagtinker_scene_about.c +++ b/scenes/tagtinker_scene_about.c @@ -23,8 +23,6 @@ static void about_draw_cb(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignTop, "Ported by I12BP8"); canvas_draw_str_aligned(canvas, 64, 34, AlignCenter, AlignTop, "Research by furrtek"); - canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignTop, "Owned ESLs only"); - canvas_draw_str_aligned(canvas, 64, 54, AlignCenter, AlignTop, "No store use"); } } diff --git a/scenes/tagtinker_scene_warning.c b/scenes/tagtinker_scene_warning.c index 8673e9c..bd51b27 100644 --- a/scenes/tagtinker_scene_warning.c +++ b/scenes/tagtinker_scene_warning.c @@ -3,42 +3,147 @@ */ #include "../tagtinker_app.h" +#include 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."; +typedef struct { + uint8_t page; +} WarningViewModel; -static void startup_warning_button_cb(GuiButtonType result, InputType type, void* context) { - if(type != InputTypeShort || result != GuiButtonTypeCenter) return; +typedef struct { + const char* title; + const char* lines[2]; +} WarningPage; +static const WarningPage startup_warning_pages[] = { + { + .title = "RESEARCH TOOL:", + .lines = + { + "Educational tool for", + "infrared ESL study.", + }, + }, + { + .title = "PERMISSION:", + .lines = + { + "Use only on tags", + "you own or may test.", + }, + }, + { + .title = "CAUTION:", + .lines = + { + "Unauthorized use", + "may be illegal.", + }, + }, + { + .title = "RESPONSIBILITY:", + .lines = + { + "You are responsible", + "for your actions.", + }, + }, +}; + +static const uint8_t startup_warning_page_count = + sizeof(startup_warning_pages) / sizeof(startup_warning_pages[0]); + +static bool warning_is_unlocked(const WarningViewModel* model) { + return model->page >= (startup_warning_page_count - 1); +} + +static void warning_draw_cb(Canvas* canvas, void* _model) { + WarningViewModel* model = _model; + bool unlocked = warning_is_unlocked(model); + const WarningPage* page = &startup_warning_pages[model->page]; + + canvas_clear(canvas); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 13, AlignCenter, AlignCenter, page->title); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 29, AlignCenter, AlignCenter, page->lines[0]); + canvas_draw_str_aligned(canvas, 64, 39, AlignCenter, AlignCenter, page->lines[1]); + + if(model->page > 0) { + elements_button_left(canvas, "Prev"); + } + + elements_button_right(canvas, unlocked ? "OK" : "Next"); +} + +static bool warning_input_cb(InputEvent* event, void* context) { TagTinkerApp* app = context; - view_dispatcher_send_custom_event( - app->view_dispatcher, TagTinkerStartupWarningContinue); + bool handled = false; + + if(event->type != InputTypeShort && event->type != InputTypeRepeat) { + return false; + } + + WarningViewModel* model = view_get_model(app->warning_view); + + switch(event->key) { + case InputKeyDown: + case InputKeyRight: + if(model->page + 1 < startup_warning_page_count) { + model->page++; + handled = true; + } + break; + case InputKeyUp: + case InputKeyLeft: + if(model->page > 0) { + model->page--; + handled = true; + } + break; + case InputKeyOk: + if(event->type == InputTypeShort) { + if(warning_is_unlocked(model)) { + handled = true; + view_commit_model(app->warning_view, false); + view_dispatcher_send_custom_event( + app->view_dispatcher, TagTinkerStartupWarningContinue); + return true; + } else if(model->page + 1 < startup_warning_page_count) { + model->page++; + handled = true; + } + } + break; + default: + break; + } + + view_commit_model(app->warning_view, handled); + return handled; } 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); + if(!app->warning_view_allocated) { + view_allocate_model(app->warning_view, ViewModelTypeLockFree, sizeof(WarningViewModel)); + view_set_context(app->warning_view, app); + view_set_draw_callback(app->warning_view, warning_draw_cb); + view_set_input_callback(app->warning_view, warning_input_cb); + app->warning_view_allocated = true; + } - view_dispatcher_switch_to_view(app->view_dispatcher, TagTinkerViewWidget); + WarningViewModel* model = view_get_model(app->warning_view); + model->page = 0; + view_commit_model(app->warning_view, true); + + view_dispatcher_switch_to_view(app->view_dispatcher, TagTinkerViewWarning); } bool tagtinker_scene_warning_on_event(void* ctx, SceneManagerEvent event) { @@ -55,6 +160,5 @@ bool tagtinker_scene_warning_on_event(void* ctx, SceneManagerEvent event) { } void tagtinker_scene_warning_on_exit(void* ctx) { - TagTinkerApp* app = ctx; - widget_reset(app->widget); + UNUSED(ctx); } diff --git a/tagtinker_app.c b/tagtinker_app.c index eac4d55..2c0019a 100644 --- a/tagtinker_app.c +++ b/tagtinker_app.c @@ -209,6 +209,10 @@ static TagTinkerApp* app_alloc(void) { view_dispatcher_add_view( app->view_dispatcher, TagTinkerViewNumlock, numlock_input_get_view(app->numlock)); + app->warning_view = view_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, TagTinkerViewWarning, app->warning_view); + app->transmit_view = view_alloc(); view_dispatcher_add_view( app->view_dispatcher, TagTinkerViewTransmit, app->transmit_view); @@ -246,6 +250,7 @@ static void app_free(TagTinkerApp* app) { view_dispatcher_remove_view(app->view_dispatcher, TagTinkerViewPopup); view_dispatcher_remove_view(app->view_dispatcher, TagTinkerViewWidget); view_dispatcher_remove_view(app->view_dispatcher, TagTinkerViewNumlock); + view_dispatcher_remove_view(app->view_dispatcher, TagTinkerViewWarning); view_dispatcher_remove_view(app->view_dispatcher, TagTinkerViewTransmit); view_dispatcher_remove_view(app->view_dispatcher, TagTinkerViewAbout); @@ -263,6 +268,7 @@ static void app_free(TagTinkerApp* app) { furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_DIALOGS); + view_free(app->warning_view); view_free(app->transmit_view); view_free(app->about_view); furi_thread_free(app->tx_thread); diff --git a/tagtinker_app.h b/tagtinker_app.h index a2583cb..c1edb42 100644 --- a/tagtinker_app.h +++ b/tagtinker_app.h @@ -36,6 +36,7 @@ /* Views */ typedef enum { + TagTinkerViewWarning, TagTinkerViewSubmenu, TagTinkerViewVarItemList, TagTinkerViewTextInput, @@ -69,9 +70,11 @@ struct TagTinkerApp { Popup* popup; Widget* widget; NumlockInput* numlock; + View* warning_view; View* target_actions_view; View* transmit_view; View* about_view; + bool warning_view_allocated; bool transmit_view_allocated; bool about_view_allocated;