Kaynağa Gözat

[FL-1178] Idle screen improvements (#444)

* Dolphin idle screen: Cleanup of debug views; Hold [down] to open debug view; Sequential input for unlocking;
* Lock screen popup, WIP
* Fix reverse modulus
* Lock popup added
* Dolphin: timer for handling UI animation sequences
* Allow tick events to be discarded on queue overflow and prevent timer service from crash

Co-authored-by: あく <alleteam@gmail.com>
its your bedtime 4 yıl önce
ebeveyn
işleme
920b3c3dee

+ 163 - 127
applications/dolphin/dolphin.c

@@ -2,8 +2,7 @@
 #include <stdlib.h>
 #include "applications.h"
 
-static void
-dolphin_switch_to_interactive_scene(Dolphin* dolphin, const FlipperApplication* flipper_app) {
+static void dolphin_switch_to_app(Dolphin* dolphin, const FlipperApplication* flipper_app) {
     furi_assert(dolphin);
     furi_assert(flipper_app);
     furi_assert(flipper_app->app);
@@ -42,98 +41,126 @@ bool dolphin_view_first_start_input(InputEvent* event, void* context) {
     furi_assert(context);
     Dolphin* dolphin = context;
     if(event->type == InputTypeShort) {
+        DolphinViewFirstStartModel* model = view_get_model(dolphin->idle_view_first_start);
         if(event->key == InputKeyLeft) {
-            with_view_model(
-                dolphin->idle_view_first_start, (DolphinViewFirstStartModel * model) {
-                    if(model->page > 0) model->page--;
-                    return true;
-                });
+            if(model->page > 0) model->page--;
         } else if(event->key == InputKeyRight) {
-            uint32_t page;
-            with_view_model(
-                dolphin->idle_view_first_start, (DolphinViewFirstStartModel * model) {
-                    page = ++model->page;
-                    return true;
-                });
+            uint32_t page = ++model->page;
             if(page > 8) {
                 dolphin_save(dolphin);
                 view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain);
             }
         }
+        view_commit_model(dolphin->idle_view_first_start, true);
     }
-    // All events consumed
+    // All evennts cosumed
     return true;
 }
 
-bool dolphin_view_idle_main_input(InputEvent* event, void* context) {
+void dolphin_lock_handler(InputEvent* event, Dolphin* dolphin) {
     furi_assert(event);
-    furi_assert(context);
-    Dolphin* dolphin = context;
+    furi_assert(dolphin);
+    if(event->key == InputKeyBack) {
+        uint32_t press_time = HAL_GetTick();
+
+        // check if pressed sequentially
+        if(press_time - dolphin->lock_lastpress < 200) {
+            dolphin->lock_lastpress = press_time;
+            dolphin->lock_count++;
+        } else if(press_time - dolphin->lock_lastpress > 200) {
+            dolphin->lock_lastpress = press_time;
+            dolphin->lock_count = 0;
+        }
 
-    if(event->type == InputTypeShort) {
-        if(!dolphin->locked) {
-            if(event->key == InputKeyOk) {
-                with_value_mutex(
-                    dolphin->menu_vm, (Menu * menu) { menu_ok(menu); });
-            } else if(event->key == InputKeyUp) {
-                view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewLockMenu);
-            } else if(event->key == InputKeyLeft) {
-                view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleUp);
-            } else if(event->key == InputKeyRight) {
-                dolphin_switch_to_interactive_scene(dolphin, &FLIPPER_SCENE);
-            } else if(event->key == InputKeyDown) {
-                view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleDown);
-            }
-            if(event->key == InputKeyBack) {
-                view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain);
-            }
-        } else {
-            if(event->key == InputKeyBack) {
-                dolphin->lock_count++;
-                if(dolphin->lock_count == 3) {
-                    dolphin->locked = false;
-                    dolphin->lock_count = 0;
-                    view_dispatcher_switch_to_view(
-                        dolphin->idle_view_dispatcher, DolphinViewIdleMain);
-                    view_port_enabled_set(dolphin->lock_viewport, false);
-                }
-            }
+        if(dolphin->lock_count == 3) {
+            dolphin->locked = false;
+            dolphin->lock_count = 0;
+
+            with_view_model(
+                dolphin->view_lockmenu, (DolphinViewLockMenuModel * model) {
+                    model->locked = false;
+                    return true;
+                });
+
+            with_view_model(
+                dolphin->idle_view_main, (DolphinViewMainModel * model) {
+                    model->hint_timeout = 0;
+                    return true;
+                });
+
+            view_port_enabled_set(dolphin->lock_viewport, false);
         }
-        dolphin_scene_handler_switch_scene(dolphin);
     }
-
-    // All events consumed
-    return true;
 }
 
-bool dolphin_view_idle_up_input(InputEvent* event, void* context) {
+bool dolphin_view_idle_main_input(InputEvent* event, void* context) {
     furi_assert(event);
     furi_assert(context);
     Dolphin* dolphin = context;
+    // unlocked
+    if(!dolphin->locked) {
+        if(event->key == InputKeyOk && event->type == InputTypeShort) {
+            with_value_mutex(
+                dolphin->menu_vm, (Menu * menu) { menu_ok(menu); });
+        } else if(event->key == InputKeyUp && event->type == InputTypeShort) {
+            osTimerStart(dolphin->timeout_timer, 40);
+            view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewLockMenu);
+        } else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
+#if 0
+            dolphin_switch_to_app(dolphin, &FAV_APP);
+#endif
+        } else if(event->key == InputKeyRight && event->type == InputTypeShort) {
+            dolphin_switch_to_app(dolphin, &FLIPPER_SCENE);
+        } else if(event->key == InputKeyDown && event->type == InputTypeShort) {
+#if 0
+            dolphin_switch_to_app(dolphin, &ARCHIVE_APP);
+#endif
+        } else if(event->key == InputKeyDown && event->type == InputTypeLong) {
+            view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewStats);
+        } else if(event->key == InputKeyBack && event->type == InputTypeShort) {
+            view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain);
+        }
+    } else {
+        // locked
 
-    if(event->type != InputTypeShort) return false;
+        with_view_model(
+            dolphin->idle_view_main, (DolphinViewMainModel * model) {
+                model->hint_timeout = 3;
+                return true;
+            });
 
-    if(event->key == InputKeyLeft) {
-        dolphin_deed(dolphin, DolphinDeedWrong);
-    } else if(event->key == InputKeyRight) {
-        dolphin_deed(dolphin, DolphinDeedIButtonRead);
-    } else if(event->key == InputKeyOk) {
-        dolphin_save(dolphin);
-    } else {
-        return false;
+        dolphin_lock_handler(event, dolphin);
+        dolphin_scene_handler_switch_scene(dolphin);
     }
-
+    // All events consumed
     return true;
 }
 
+void lock_menu_refresh_handler(void* p) {
+    osMessageQueueId_t event_queue = p;
+    DolphinEvent event;
+    event.type = DolphinEventTypeTick;
+    // Some tick events may lost and we don't care.
+    osMessageQueuePut(event_queue, &event, 0, 0);
+}
+
 static void lock_menu_callback(void* context, uint8_t index) {
+    furi_assert(context);
     Dolphin* dolphin = context;
     switch(index) {
+    // lock
     case 0:
         dolphin->locked = true;
-        view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain);
-        view_port_enabled_set(dolphin->lock_viewport, true);
+        DolphinViewLockMenuModel* model = view_get_model(dolphin->view_lockmenu);
+
+        model->locked = true;
+        model->exit_timeout = 20;
+
+        view_port_enabled_set(dolphin->lock_viewport, dolphin->locked);
+        view_commit_model(dolphin->view_lockmenu, true);
+
         break;
+
     default:
         break;
     }
@@ -152,42 +179,24 @@ bool dolphin_view_lockmenu_input(InputEvent* event, void* context) {
 
     if(event->type != InputTypeShort) return false;
 
+    DolphinViewLockMenuModel* model = view_get_model(dolphin->view_lockmenu);
+
     if(event->key == InputKeyUp) {
-        with_view_model(
-            dolphin->view_lockmenu, (DolphinViewMenuModel * model) {
-                if(model->idx <= 0)
-                    model->idx = 0;
-                else
-                    --model->idx;
-                return true;
-            });
+        model->idx = CLAMP(model->idx - 1, 2, 0);
     } else if(event->key == InputKeyDown) {
-        with_view_model(
-            dolphin->view_lockmenu, (DolphinViewMenuModel * model) {
-                if(model->idx >= 2)
-                    model->idx = 2;
-                else
-                    ++model->idx;
-                return true;
-            });
+        model->idx = CLAMP(model->idx + 1, 2, 0);
     } else if(event->key == InputKeyOk) {
-        with_view_model(
-            dolphin->view_lockmenu, (DolphinViewMenuModel * model) {
-                lock_menu_callback(context, model->idx);
-                return true;
-            });
+        lock_menu_callback(context, model->idx);
     } else if(event->key == InputKeyBack) {
-        with_view_model(
-            dolphin->view_lockmenu, (DolphinViewMenuModel * model) {
-                model->idx = 0;
-                return true;
-            });
+        model->idx = 0;
         view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain);
 
         if(random() % 100 > 50)
             dolphin_scene_handler_set_scene(dolphin, idle_scenes[random() % sizeof(idle_scenes)]);
     }
 
+    view_commit_model(dolphin->view_lockmenu, true);
+
     return true;
 }
 
@@ -195,27 +204,40 @@ bool dolphin_view_idle_down_input(InputEvent* event, void* context) {
     furi_assert(event);
     furi_assert(context);
     Dolphin* dolphin = context;
+    DolphinViewStatsScreens current;
 
     if(event->type != InputTypeShort) return false;
 
-    if((event->key == InputKeyLeft) || (event->key == InputKeyRight)) {
-        with_view_model(
-            dolphin->idle_view_down, (DolphinViewIdleDownModel * model) {
-                model->show_fw_or_boot = !model->show_fw_or_boot;
-                return true;
-            });
+    DolphinViewStatsModel* model = view_get_model(dolphin->idle_view_dolphin_stats);
+
+    current = model->screen;
+
+    if(event->key == InputKeyDown) {
+        model->screen = (model->screen + 1) % DolphinViewStatsTotalCount;
+    } else if(event->key == InputKeyUp) {
+        model->screen =
+            ((model->screen - 1) + DolphinViewStatsTotalCount) % DolphinViewStatsTotalCount;
+    }
+
+    view_commit_model(dolphin->idle_view_dolphin_stats, true);
+
+    if(current == DolphinViewStatsMeta) {
+        if(event->key == InputKeyLeft) {
+            dolphin_deed(dolphin, DolphinDeedWrong);
+        } else if(event->key == InputKeyRight) {
+            dolphin_deed(dolphin, DolphinDeedIButtonRead);
+        } else if(event->key == InputKeyOk) {
+            dolphin_save(dolphin);
+        } else {
+            return false;
+        }
     }
 
     if(event->key == InputKeyBack) {
-        with_view_model(
-            dolphin->idle_view_down, (DolphinViewIdleDownModel * model) {
-                model->show_fw_or_boot = 0;
-                return true;
-            });
         view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain);
     }
 
-    return false;
+    return true;
 }
 
 Dolphin* dolphin_alloc() {
@@ -229,10 +251,8 @@ Dolphin* dolphin_alloc() {
     dolphin->menu_vm = furi_record_open("menu");
     // Scene thread
     dolphin->scene_thread = furi_thread_alloc();
-
     // GUI
     dolphin->gui = furi_record_open("gui");
-
     // Dispatcher
     dolphin->idle_view_dispatcher = view_dispatcher_alloc();
 
@@ -250,46 +270,45 @@ Dolphin* dolphin_alloc() {
     dolphin->idle_view_main = view_alloc();
     view_set_context(dolphin->idle_view_main, dolphin);
     view_allocate_model(
-        dolphin->idle_view_main, ViewModelTypeLockFree, sizeof(DolphinViewIdleUpModel));
+        dolphin->idle_view_main, ViewModelTypeLockFree, sizeof(DolphinViewMainModel));
 
     view_set_draw_callback(dolphin->idle_view_main, dolphin_view_idle_main_draw);
     view_set_input_callback(dolphin->idle_view_main, dolphin_view_idle_main_input);
     view_dispatcher_add_view(
         dolphin->idle_view_dispatcher, DolphinViewIdleMain, dolphin->idle_view_main);
 
-    // Stats Idle View
-    dolphin->idle_view_up = view_alloc();
-    view_set_context(dolphin->idle_view_up, dolphin);
-
-    view_allocate_model(
-        dolphin->idle_view_up, ViewModelTypeLockFree, sizeof(DolphinViewMainModel));
-    view_set_draw_callback(dolphin->idle_view_up, dolphin_view_idle_up_draw);
-    view_set_input_callback(dolphin->idle_view_up, dolphin_view_idle_up_input);
-    view_set_previous_callback(dolphin->idle_view_up, dolphin_view_idle_back);
-    view_dispatcher_add_view(
-        dolphin->idle_view_dispatcher, DolphinViewIdleUp, dolphin->idle_view_up);
-
     // Lock Menu View
     dolphin->view_lockmenu = view_alloc();
     view_set_context(dolphin->view_lockmenu, dolphin);
     view_allocate_model(
-        dolphin->view_lockmenu, ViewModelTypeLockFree, sizeof(DolphinViewMenuModel));
+        dolphin->view_lockmenu, ViewModelTypeLockFree, sizeof(DolphinViewLockMenuModel));
     view_set_draw_callback(dolphin->view_lockmenu, dolphin_view_lockmenu_draw);
     view_set_input_callback(dolphin->view_lockmenu, dolphin_view_lockmenu_input);
     view_set_previous_callback(dolphin->view_lockmenu, dolphin_view_idle_back);
     view_dispatcher_add_view(
         dolphin->idle_view_dispatcher, DolphinViewLockMenu, dolphin->view_lockmenu);
 
-    // Down Idle View
-    dolphin->idle_view_down = view_alloc();
-    view_set_context(dolphin->idle_view_down, dolphin);
+    // default doors xpos
+    with_view_model(
+        dolphin->view_lockmenu, (DolphinViewLockMenuModel * model) {
+            model->door_left_x = -57; // defaults
+            model->door_right_x = 115; // defaults
+            return true;
+        });
+
+    dolphin->timeout_timer =
+        osTimerNew(lock_menu_refresh_handler, osTimerPeriodic, dolphin->event_queue, NULL);
+
+    // Stats Idle View
+    dolphin->idle_view_dolphin_stats = view_alloc();
+    view_set_context(dolphin->idle_view_dolphin_stats, dolphin);
     view_allocate_model(
-        dolphin->idle_view_down, ViewModelTypeLockFree, sizeof(DolphinViewIdleDownModel));
-    view_set_draw_callback(dolphin->idle_view_down, dolphin_view_idle_down_draw);
-    view_set_input_callback(dolphin->idle_view_down, dolphin_view_idle_down_input);
-    view_set_previous_callback(dolphin->idle_view_down, dolphin_view_idle_back);
+        dolphin->idle_view_dolphin_stats, ViewModelTypeLockFree, sizeof(DolphinViewStatsModel));
+    view_set_draw_callback(dolphin->idle_view_dolphin_stats, dolphin_view_idle_down_draw);
+    view_set_input_callback(dolphin->idle_view_dolphin_stats, dolphin_view_idle_down_input);
+    view_set_previous_callback(dolphin->idle_view_dolphin_stats, dolphin_view_idle_back);
     view_dispatcher_add_view(
-        dolphin->idle_view_dispatcher, DolphinViewIdleDown, dolphin->idle_view_down);
+        dolphin->idle_view_dispatcher, DolphinViewStats, dolphin->idle_view_dolphin_stats);
     // HW Mismatch
     dolphin->view_hw_mismatch = view_alloc();
     view_set_draw_callback(dolphin->view_hw_mismatch, dolphin_view_hw_mismatch_draw);
@@ -321,6 +340,8 @@ void dolphin_free(Dolphin* dolphin) {
     view_port_free(dolphin->lock_viewport);
     icon_free(dolphin->lock_icon);
 
+    osTimerDelete(dolphin->timeout_timer);
+
     view_dispatcher_free(dolphin->idle_view_dispatcher);
 
     furi_record_close("gui");
@@ -361,8 +382,9 @@ int32_t dolphin_task() {
     } else {
         view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewFirstStart);
     }
+
     with_view_model(
-        dolphin->idle_view_up, (DolphinViewIdleUpModel * model) {
+        dolphin->idle_view_dolphin_stats, (DolphinViewStatsModel * model) {
             model->icounter = dolphin_state_get_icounter(dolphin->state);
             model->butthurt = dolphin_state_get_butthurt(dolphin->state);
             return true;
@@ -373,13 +395,27 @@ int32_t dolphin_task() {
     if(!api_hal_version_do_i_belong_here()) {
         view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewHwMismatch);
     }
+
     DolphinEvent event;
     while(1) {
         furi_check(osMessageQueueGet(dolphin->event_queue, &event, NULL, osWaitForever) == osOK);
-        if(event.type == DolphinEventTypeDeed) {
+
+        DolphinViewLockMenuModel* lock_model = view_get_model(dolphin->view_lockmenu);
+
+        if(lock_model->locked && lock_model->exit_timeout == 0 &&
+           osTimerIsRunning(dolphin->timeout_timer)) {
+            osTimerStop(dolphin->timeout_timer);
+            osDelay(1); // smol enterprise delay
+            view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain);
+        }
+
+        if(event.type == DolphinEventTypeTick) {
+            view_commit_model(dolphin->view_lockmenu, true);
+
+        } else if(event.type == DolphinEventTypeDeed) {
             dolphin_state_on_deed(dolphin->state, event.deed);
             with_view_model(
-                dolphin->idle_view_up, (DolphinViewIdleUpModel * model) {
+                dolphin->idle_view_dolphin_stats, (DolphinViewStatsModel * model) {
                     model->icounter = dolphin_state_get_icounter(dolphin->state);
                     model->butthurt = dolphin_state_get_butthurt(dolphin->state);
                     return true;

+ 4 - 1
applications/dolphin/dolphin_i.h

@@ -17,6 +17,7 @@
 typedef enum {
     DolphinEventTypeDeed,
     DolphinEventTypeSave,
+    DolphinEventTypeTick,
 } DolphinEventType;
 
 typedef struct {
@@ -40,7 +41,7 @@ struct Dolphin {
     ViewDispatcher* idle_view_dispatcher;
     View* idle_view_first_start;
     View* idle_view_main;
-    View* idle_view_up;
+    View* idle_view_dolphin_stats;
     View* idle_view_down;
     View* idle_view_meta;
     View* view_hw_mismatch;
@@ -50,6 +51,8 @@ struct Dolphin {
 
     bool locked;
     uint8_t lock_count;
+    uint32_t lock_lastpress;
+    osTimerId_t timeout_timer;
 };
 
 // Temporary

+ 85 - 54
applications/dolphin/dolphin_views.c

@@ -59,81 +59,112 @@ void dolphin_view_first_start_draw(Canvas* canvas, void* model) {
 void dolphin_view_idle_main_draw(Canvas* canvas, void* model) {
     canvas_clear(canvas);
     DolphinViewMainModel* m = model;
-    if(m->animation) canvas_draw_icon(canvas, 0, -3, m->animation);
-}
-
-void dolphin_view_idle_up_draw(Canvas* canvas, void* model) {
-    DolphinViewIdleUpModel* m = model;
-    canvas_clear(canvas);
-
-    canvas_set_color(canvas, ColorBlack);
-    canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str(canvas, 2, 15, "Dolphin stats:");
+    if(m->animation) {
+        canvas_draw_icon(canvas, 0, -3, m->animation);
+    }
 
-    char buffer[64];
-    canvas_set_font(canvas, FontSecondary);
-    snprintf(buffer, 64, "Icounter: %ld", m->icounter);
-    canvas_draw_str(canvas, 5, 30, buffer);
-    snprintf(buffer, 64, "Butthurt: %ld", m->butthurt);
-    canvas_draw_str(canvas, 5, 40, buffer);
-    canvas_draw_str(canvas, 0, 53, "[< >] icounter value   [ok] save");
+    if(m->hint_timeout > 0) {
+        m->hint_timeout--;
+        canvas_draw_icon_name(canvas, 13, 5, I_LockPopup_100x49);
+        elements_multiline_text(canvas, 65, 20, "To unlock\npress:");
+    }
 }
 
 void dolphin_view_lockmenu_draw(Canvas* canvas, void* model) {
-    DolphinViewMenuModel* m = model;
+    DolphinViewLockMenuModel* m = model;
     canvas_clear(canvas);
     canvas_set_color(canvas, ColorBlack);
-    canvas_draw_icon_name(canvas, 5, 0, I_DoorLeft_8x56);
-    canvas_draw_icon_name(canvas, 115, 0, I_DoorRight_8x56);
+    canvas_draw_icon_name(canvas, m->door_left_x, 0, I_DoorLeft_70x55);
+    canvas_draw_icon_name(canvas, m->door_right_x, 0, I_DoorRight_70x55);
     canvas_set_font(canvas, FontSecondary);
-    for(uint8_t i = 0; i < 3; ++i) {
-        canvas_draw_str_aligned(
-            canvas, 64, 13 + (i * 17), AlignCenter, AlignCenter, Lockmenu_Items[i]);
-        if(m->idx == i) elements_frame(canvas, 15, 5 + (i * 17), 98, 15);
+
+    if(m->locked) {
+        m->exit_timeout--;
+
+        m->door_left_x = CLAMP(m->door_left_x + 10, 0, -57);
+        m->door_right_x = CLAMP(m->door_right_x - 10, 115, 60);
+
+        if(m->door_left_x > -10) {
+            canvas_set_font(canvas, FontPrimary);
+            elements_multiline_text_framed(canvas, 42, 30, "Locked");
+        }
+
+    } else {
+        m->door_left_x = CLAMP(m->door_left_x - 10, 0, -57);
+        m->door_right_x = CLAMP(m->door_right_x + 10, 115, 60);
+
+        if(m->door_left_x == -57) {
+            for(uint8_t i = 0; i < 3; ++i) {
+                canvas_draw_str_aligned(
+                    canvas, 64, 13 + (i * 17), AlignCenter, AlignCenter, Lockmenu_Items[i]);
+                if(m->idx == i) elements_frame(canvas, 15, 5 + (i * 17), 98, 15);
+            }
+        }
     }
 }
 
 void dolphin_view_idle_down_draw(Canvas* canvas, void* model) {
-    DolphinViewIdleDownModel* m = model;
+    DolphinViewStatsModel* m = model;
     const Version* ver;
     char buffer[64];
 
+    static const char* headers[] = {"Boot Version info:", "FW Version info:", "Dolphin info:"};
+
     canvas_set_color(canvas, ColorBlack);
     canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str(canvas, 2, 13, m->show_fw_or_boot ? "Boot Version info:" : "FW Version info:");
+    canvas_draw_str(canvas, 2, 13, headers[m->screen]);
     canvas_set_font(canvas, FontSecondary);
 
-    // Hardware version
-    snprintf(
-        buffer,
-        sizeof(buffer),
-        "HW: %d.F%dB%dC%d %s",
-        api_hal_version_get_hw_version(),
-        api_hal_version_get_hw_target(),
-        api_hal_version_get_hw_body(),
-        api_hal_version_get_hw_connect(),
-        api_hal_version_get_name_ptr());
-    canvas_draw_str(canvas, 5, 23, buffer);
-
-    ver = m->show_fw_or_boot ? api_hal_version_get_boot_version() :
-                               api_hal_version_get_fw_version();
-
-    if(!ver) {
-        canvas_draw_str(canvas, 5, 33, "No info");
-        return;
-    }
+    if(m->screen != DolphinViewStatsMeta) {
+        // Hardware version
+        snprintf(
+            buffer,
+            sizeof(buffer),
+            "HW: %d.F%dB%dC%d %s",
+            api_hal_version_get_hw_version(),
+            api_hal_version_get_hw_target(),
+            api_hal_version_get_hw_body(),
+            api_hal_version_get_hw_connect(),
+            api_hal_version_get_name_ptr());
+        canvas_draw_str(canvas, 5, 23, buffer);
+
+        ver = m->screen == DolphinViewStatsBoot ? api_hal_version_get_boot_version() :
+                                                  api_hal_version_get_fw_version();
+
+        if(!ver) {
+            canvas_draw_str(canvas, 5, 33, "No info");
+            return;
+        }
 
-    snprintf(
-        buffer, sizeof(buffer), "%s [%s]", version_get_version(ver), version_get_builddate(ver));
-    canvas_draw_str(canvas, 5, 33, buffer);
+        snprintf(
+            buffer,
+            sizeof(buffer),
+            "%s [%s]",
+            version_get_version(ver),
+            version_get_builddate(ver));
+        canvas_draw_str(canvas, 5, 33, buffer);
 
-    snprintf(
-        buffer, sizeof(buffer), "%s [%s]", version_get_githash(ver), version_get_gitbranchnum(ver));
-    canvas_draw_str(canvas, 5, 43, buffer);
+        snprintf(
+            buffer,
+            sizeof(buffer),
+            "%s [%s]",
+            version_get_githash(ver),
+            version_get_gitbranchnum(ver));
+        canvas_draw_str(canvas, 5, 43, buffer);
 
-    snprintf(
-        buffer, sizeof(buffer), "[%s] %s", version_get_target(ver), version_get_gitbranch(ver));
-    canvas_draw_str(canvas, 5, 53, buffer);
+        snprintf(
+            buffer, sizeof(buffer), "[%s] %s", version_get_target(ver), version_get_gitbranch(ver));
+        canvas_draw_str(canvas, 5, 53, buffer);
+
+    } else {
+        char buffer[64];
+        canvas_set_font(canvas, FontSecondary);
+        snprintf(buffer, 64, "Icounter: %ld", m->icounter);
+        canvas_draw_str(canvas, 5, 30, buffer);
+        snprintf(buffer, 64, "Butthurt: %ld", m->butthurt);
+        canvas_draw_str(canvas, 5, 40, buffer);
+        canvas_draw_str(canvas, 0, 53, "[< >] icounter value   [ok] save");
+    }
 }
 
 void dolphin_view_hw_mismatch_draw(Canvas* canvas, void* model) {

+ 30 - 10
applications/dolphin/dolphin_views.h

@@ -6,16 +6,35 @@
 #include <input/input.h>
 #include <furi.h>
 
-// Idle scree
+#ifndef MAX
+#define MAX(x, y) (((x) > (y)) ? (x) : (y))
+#endif
+
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+#ifndef CLAMP
+#define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower)))
+#endif
+
+// Idle screen
 typedef enum {
     DolphinViewIdleMain,
     DolphinViewFirstStart,
-    DolphinViewIdleUp,
-    DolphinViewIdleDown,
+    DolphinViewStats,
     DolphinViewHwMismatch,
     DolphinViewLockMenu,
 } DolphinViewIdle;
 
+// Debug info
+typedef enum {
+    DolphinViewStatsFw,
+    DolphinViewStatsBoot,
+    DolphinViewStatsMeta,
+    DolphinViewStatsTotalCount,
+} DolphinViewStatsScreens;
+
 typedef struct {
     uint32_t page;
 } DolphinViewFirstStartModel;
@@ -26,20 +45,21 @@ bool dolphin_view_first_start_input(InputEvent* event, void* context);
 typedef struct {
     uint32_t icounter;
     uint32_t butthurt;
-} DolphinViewIdleUpModel;
-
-typedef struct {
-    bool show_fw_or_boot;
-} DolphinViewIdleDownModel;
+    DolphinViewStatsScreens screen;
+} DolphinViewStatsModel;
 
 typedef struct {
     uint8_t idx;
-} DolphinViewMenuModel;
+    int8_t door_left_x;
+    int8_t door_right_x;
+    uint8_t exit_timeout;
+    bool locked;
+} DolphinViewLockMenuModel;
 
 typedef struct {
     Icon* animation;
     uint8_t scene_num;
-
+    uint8_t hint_timeout;
 } DolphinViewMainModel;
 
 void dolphin_view_idle_main_draw(Canvas* canvas, void* model);

BIN
assets/icons/Interface/DoorLeft_70x55.png


BIN
assets/icons/Interface/DoorRight_70x55.png


BIN
assets/icons/Interface/LockPopup_100x49.png