Просмотр исходного кода

[FL-1019] New main screen and graphics (#389)

* new status bar, lock menu and dolphin activities screen

* lock icon indication 

* main screen animation, basic scene switching

* level progression calculations based on icounter value 

Co-authored-by: rusdacent <rusdacentx0x08@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
its your bedtime 4 лет назад
Родитель
Сommit
372710c31a
42 измененных файлов с 454 добавлено и 34 удалено
  1. 5 1
      .github/workflows/ci.yml
  2. 275 10
      applications/dolphin/dolphin.c
  3. 11 0
      applications/dolphin/dolphin_i.h
  4. 14 0
      applications/dolphin/dolphin_state.c
  5. 4 0
      applications/dolphin/dolphin_state.h
  6. 52 17
      applications/dolphin/dolphin_views.c
  7. 17 0
      applications/dolphin/dolphin_views.h
  8. 51 2
      applications/gui/gui.c
  9. 10 0
      applications/gui/icon.c
  10. 10 0
      applications/gui/icon.h
  11. 3 4
      applications/power/power.c
  12. BIN
      assets/icons/Animations/WatchingTV_128x64/frame_01.png
  13. BIN
      assets/icons/Animations/WatchingTV_128x64/frame_02.png
  14. BIN
      assets/icons/Animations/WatchingTV_128x64/frame_03.png
  15. BIN
      assets/icons/Animations/WatchingTV_128x64/frame_04.png
  16. BIN
      assets/icons/Animations/WatchingTV_128x64/frame_05.png
  17. 1 0
      assets/icons/Animations/WatchingTV_128x64/frame_rate
  18. BIN
      assets/icons/Animations/Wink_128x64/frame_01.png
  19. BIN
      assets/icons/Animations/Wink_128x64/frame_02.png
  20. BIN
      assets/icons/Animations/Wink_128x64/frame_03.png
  21. BIN
      assets/icons/Animations/Wink_128x64/frame_04.png
  22. BIN
      assets/icons/Animations/Wink_128x64/frame_05.png
  23. BIN
      assets/icons/Animations/Wink_128x64/frame_06.png
  24. BIN
      assets/icons/Animations/Wink_128x64/frame_07.png
  25. BIN
      assets/icons/Animations/Wink_128x64/frame_08.png
  26. BIN
      assets/icons/Animations/Wink_128x64/frame_09.png
  27. 1 0
      assets/icons/Animations/Wink_128x64/frame_rate
  28. BIN
      assets/icons/Dolphin/BigBurger_24x24.png
  29. BIN
      assets/icons/Dolphin/BigGames_24x24.png
  30. BIN
      assets/icons/Dolphin/BigProfile_24x24.png
  31. BIN
      assets/icons/Dolphin/DolphinOkay_41x43.png
  32. BIN
      assets/icons/Interface/DoorLeft_8x56.png
  33. BIN
      assets/icons/Interface/DoorLocked_10x56.png
  34. BIN
      assets/icons/Interface/DoorRight_8x56.png
  35. BIN
      assets/icons/Interface/PassportBottom_128x17.png
  36. BIN
      assets/icons/Interface/PassportLeft_6x47.png
  37. BIN
      assets/icons/StatusBar/Background_128x11.png
  38. BIN
      assets/icons/StatusBar/Background_128x8.png
  39. BIN
      assets/icons/StatusBar/Battery_26x8.png
  40. BIN
      assets/icons/StatusBar/Lock_8x8.png
  41. BIN
      assets/icons/StatusBar/PlaceholderL_11x13.png
  42. BIN
      assets/icons/StatusBar/PlaceholderR_30x13.png

+ 5 - 1
.github/workflows/ci.yml

@@ -20,7 +20,11 @@ jobs:
         uses: actions/checkout@v2
         with:
           fetch-depth: 0
-          submodules: true
+      - name: Checkout submodules
+        run: git submodule update --init --recursive
+      - name: Checkout submodules
+        run: git submodule sync
+
       - uses: satackey/action-docker-layer-caching@v0.0.11
         continue-on-error: true
         with:

+ 275 - 10
applications/dolphin/dolphin.c

@@ -1,4 +1,27 @@
 #include "dolphin_i.h"
+#include <stdlib.h>
+
+// temporary main screen animation managment
+void dolphin_scene_handler_set_scene(Dolphin* dolphin, IconName icon) {
+    with_view_model(
+        dolphin->idle_view_main, (DolphinViewMainModel * model) {
+            model->animation = assets_icons_get(icon);
+            icon_start_animation(model->animation);
+            return true;
+        });
+}
+
+void dolphin_scene_handler_switch_scene(Dolphin* dolphin) {
+    with_view_model(
+        dolphin->idle_view_main, (DolphinViewMainModel * model) {
+            if(icon_is_last_frame(model->animation)) {
+                model->animation = assets_icons_get(idle_scenes[model->scene_num]);
+                icon_start_animation(model->animation);
+                model->scene_num = random() % sizeof(idle_scenes);
+            }
+            return true;
+        });
+}
 
 bool dolphin_view_first_start_input(InputEvent* event, void* context) {
     furi_assert(event);
@@ -34,15 +57,38 @@ bool dolphin_view_idle_main_input(InputEvent* event, void* context) {
     Dolphin* dolphin = context;
 
     if(event->type == InputTypeShort) {
-        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, DolphinViewIdleUp);
-        } else if(event->key == InputKeyDown) {
-            view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleDown);
+        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) {
+                view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMeta);
+            } 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);
+                }
+            }
         }
+        dolphin_scene_handler_switch_scene(dolphin);
     }
+
     // All events consumed
     return true;
 }
@@ -67,6 +113,182 @@ bool dolphin_view_idle_up_input(InputEvent* event, void* context) {
     return true;
 }
 
+static void lock_menu_callback(void* context, uint8_t index) {
+    Dolphin* dolphin = context;
+    switch(index) {
+    case 0:
+        dolphin->locked = true;
+        view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain);
+        view_port_enabled_set(dolphin->lock_viewport, true);
+        break;
+
+    default:
+        break;
+    }
+}
+
+static void meta_menu_callback(void* context, uint8_t index) {
+    Dolphin* dolphin = context;
+    switch(index) {
+    case 0:
+        view_port_enabled_set(dolphin->passport, true);
+        break;
+
+    default:
+        break;
+    }
+}
+
+static void lock_icon_callback(Canvas* canvas, void* context) {
+    assert(context);
+    Dolphin* dolphin = context;
+    canvas_draw_icon(canvas, 0, 0, dolphin->lock_icon);
+}
+
+static void draw_passport_callback(Canvas* canvas, void* context) {
+    assert(context);
+    Dolphin* dolphin = context;
+
+    char level[20];
+    uint32_t current_level = dolphin_state_get_level(dolphin->state);
+    uint32_t prev_cap = dolphin_state_xp_to_levelup(dolphin->state, current_level - 1, false);
+    uint32_t exp = (dolphin_state_xp_to_levelup(dolphin->state, current_level, true) * 63) /
+                   (dolphin_state_xp_to_levelup(dolphin->state, current_level, false) - prev_cap);
+
+    canvas_clear(canvas);
+
+    // multipass
+    canvas_draw_icon_name(canvas, 0, 0, I_PassportLeft_6x47);
+    canvas_draw_icon_name(canvas, 0, 47, I_PassportBottom_128x17);
+    canvas_draw_line(canvas, 6, 0, 125, 0);
+    canvas_draw_line(canvas, 127, 2, 127, 47);
+    canvas_draw_dot(canvas, 126, 1);
+
+    //portrait frame
+    canvas_draw_line(canvas, 9, 6, 9, 53);
+    canvas_draw_line(canvas, 10, 5, 52, 5);
+    canvas_draw_line(canvas, 55, 8, 55, 53);
+    canvas_draw_line(canvas, 10, 54, 54, 54);
+    canvas_draw_line(canvas, 53, 5, 55, 7);
+
+    // portrait
+    canvas_draw_icon_name(canvas, 14, 11, I_DolphinOkay_41x43);
+    canvas_draw_line(canvas, 59, 18, 124, 18);
+    canvas_draw_line(canvas, 59, 31, 124, 31);
+    canvas_draw_line(canvas, 59, 44, 124, 44);
+
+    canvas_draw_str(canvas, 59, 15, api_hal_version_get_name_ptr());
+    canvas_draw_str(canvas, 59, 28, "Mood: OK");
+
+    snprintf(level, 20, "Level: %ld", current_level);
+
+    canvas_draw_str(canvas, 59, 41, level);
+    canvas_set_color(canvas, ColorWhite);
+    canvas_draw_box(canvas, 123 - exp, 48, exp + 1, 6);
+    canvas_set_color(canvas, ColorBlack);
+    canvas_draw_line(canvas, 123 - exp, 48, 123 - exp, 54);
+}
+
+static void passport_input_callback(InputEvent* event, void* context) {
+    Dolphin* dolphin = context;
+    if(event->type == InputTypeShort) {
+        if(event->key == InputKeyBack) {
+            view_port_enabled_set(dolphin->passport, false);
+        }
+    }
+}
+
+bool dolphin_view_lockmenu_input(InputEvent* event, void* context) {
+    furi_assert(event);
+    furi_assert(context);
+    Dolphin* dolphin = context;
+
+    if(event->type != InputTypeShort) return false;
+
+    if(event->key == InputKeyUp) {
+        with_view_model(
+            dolphin->view_lockmenu, (DolphinViewMenuModel * model) {
+                if(model->idx <= 0)
+                    model->idx = 0;
+                else
+                    --model->idx;
+                return true;
+            });
+    } 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;
+            });
+    } else if(event->key == InputKeyOk) {
+        with_view_model(
+            dolphin->view_lockmenu, (DolphinViewMenuModel * model) {
+                lock_menu_callback(context, model->idx);
+                return true;
+            });
+    } else if(event->key == InputKeyBack) {
+        with_view_model(
+            dolphin->view_lockmenu, (DolphinViewMenuModel * model) {
+                model->idx = 0;
+                return true;
+            });
+        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)]);
+    }
+
+    return true;
+}
+
+bool dolphin_view_idle_meta_input(InputEvent* event, void* context) {
+    furi_assert(event);
+    furi_assert(context);
+    Dolphin* dolphin = context;
+
+    if(event->type != InputTypeShort) return false;
+
+    if(event->key == InputKeyLeft) {
+        with_view_model(
+            dolphin->idle_view_meta, (DolphinViewMenuModel * model) {
+                if(model->idx <= 0)
+                    model->idx = 0;
+                else
+                    --model->idx;
+                return true;
+            });
+    } else if(event->key == InputKeyRight) {
+        with_view_model(
+            dolphin->idle_view_meta, (DolphinViewMenuModel * model) {
+                if(model->idx >= 2)
+                    model->idx = 2;
+                else
+                    ++model->idx;
+                return true;
+            });
+    } else if(event->key == InputKeyOk) {
+        with_view_model(
+            dolphin->idle_view_meta, (DolphinViewMenuModel * model) {
+                meta_menu_callback(context, model->idx);
+                return true;
+            });
+    } else if(event->key == InputKeyBack) {
+        with_view_model(
+            dolphin->idle_view_meta, (DolphinViewMenuModel * model) {
+                model->idx = 0;
+                return true;
+            });
+        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)]);
+    }
+
+    return true;
+}
+
 Dolphin* dolphin_alloc() {
     Dolphin* dolphin = furi_alloc(sizeof(Dolphin));
     // Message queue
@@ -90,6 +312,9 @@ Dolphin* dolphin_alloc() {
     // Main Idle View
     dolphin->idle_view_main = view_alloc();
     view_set_context(dolphin->idle_view_main, dolphin);
+    view_allocate_model(
+        dolphin->idle_view_main, ViewModelTypeLockFree, sizeof(DolphinViewIdleUpModel));
+
     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(
@@ -97,13 +322,34 @@ Dolphin* dolphin_alloc() {
     // 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(DolphinViewIdleUpModel));
+        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));
+    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);
+    // Meta View
+    dolphin->idle_view_meta = view_alloc();
+    view_set_context(dolphin->idle_view_meta, dolphin);
+    view_allocate_model(
+        dolphin->idle_view_meta, ViewModelTypeLockFree, sizeof(DolphinViewMenuModel));
+    view_set_draw_callback(dolphin->idle_view_meta, dolphin_view_idle_meta_draw);
+    view_set_input_callback(dolphin->idle_view_meta, dolphin_view_idle_meta_input);
+    view_set_previous_callback(dolphin->idle_view_meta, dolphin_view_idle_back);
+    view_dispatcher_add_view(
+        dolphin->idle_view_dispatcher, DolphinViewIdleMeta, dolphin->idle_view_meta);
     // Down Idle View
     dolphin->idle_view_down = view_alloc();
     view_set_draw_callback(dolphin->idle_view_down, dolphin_view_idle_down_draw);
@@ -117,6 +363,22 @@ Dolphin* dolphin_alloc() {
     view_dispatcher_add_view(
         dolphin->idle_view_dispatcher, DolphinViewHwMismatch, dolphin->view_hw_mismatch);
 
+    // Lock icon
+    dolphin->lock_icon = assets_icons_get(I_Lock_8x8);
+    dolphin->lock_viewport = view_port_alloc();
+    view_port_set_width(dolphin->lock_viewport, icon_get_width(dolphin->lock_icon));
+    view_port_draw_callback_set(dolphin->lock_viewport, lock_icon_callback, dolphin);
+    view_port_enabled_set(dolphin->lock_viewport, false);
+
+    // Passport
+    dolphin->passport = view_port_alloc();
+    view_port_draw_callback_set(dolphin->passport, draw_passport_callback, dolphin);
+    view_port_input_callback_set(dolphin->passport, passport_input_callback, dolphin);
+    view_port_enabled_set(dolphin->passport, false);
+
+    // Main screen animation
+    dolphin_scene_handler_set_scene(dolphin, idle_scenes[random() % sizeof(idle_scenes)]);
+
     return dolphin;
 }
 
@@ -139,7 +401,12 @@ int32_t dolphin_task() {
     Dolphin* dolphin = dolphin_alloc();
 
     Gui* gui = furi_record_open("gui");
+
     view_dispatcher_attach_to_gui(dolphin->idle_view_dispatcher, gui, ViewDispatcherTypeWindow);
+    gui_add_view_port(gui, dolphin->lock_viewport, GuiLayerStatusBarLeft);
+
+    gui_add_view_port(gui, dolphin->passport, GuiLayerFullscreen);
+
     if(dolphin_state_load(dolphin->state)) {
         view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain);
     } else {
@@ -157,7 +424,6 @@ 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);
@@ -173,6 +439,5 @@ int32_t dolphin_task() {
             dolphin_state_save(dolphin->state);
         }
     }
-
     return 0;
 }

+ 11 - 0
applications/dolphin/dolphin_i.h

@@ -39,9 +39,20 @@ struct Dolphin {
     View* idle_view_main;
     View* idle_view_up;
     View* idle_view_down;
+    View* idle_view_meta;
     View* view_hw_mismatch;
+    View* view_lockmenu;
+    ViewPort* passport;
+    ViewPort* lock_viewport;
+    Icon* lock_icon;
+
+    bool locked;
+    uint8_t lock_count;
 };
 
+// Temporary
+const IconName idle_scenes[] = {A_Wink_128x64, A_WatchingTV_128x64};
+
 Dolphin* dolphin_alloc();
 
 /* Save Dolphin state (write to permanent memory)

+ 14 - 0
applications/dolphin/dolphin_state.c

@@ -1,6 +1,7 @@
 #include "dolphin_state.h"
 #include <furi.h>
 #include <api-hal.h>
+#include <math.h>
 
 typedef struct {
     uint8_t magic;
@@ -17,6 +18,8 @@ typedef struct {
 #define DOLPHIN_DATA_HEADER_MAGIC 0xD0
 #define DOLPHIN_DATA_HEADER_VERSION 0x01
 
+#define DOLPHIN_LVL_THRESHOLD 20.0f
+
 typedef struct {
     uint32_t limit_ibutton;
     uint32_t limit_nfc;
@@ -119,3 +122,14 @@ uint32_t dolphin_state_get_icounter(DolphinState* dolphin_state) {
 uint32_t dolphin_state_get_butthurt(DolphinState* dolphin_state) {
     return dolphin_state->data.butthurt;
 }
+
+uint32_t dolphin_state_get_level(DolphinState* dolphin_state) {
+    return 0.5f +
+           sqrtf(1.0f + 8.0f * ((float)dolphin_state->data.icounter / DOLPHIN_LVL_THRESHOLD)) /
+               2.0f;
+}
+
+uint32_t dolphin_state_xp_to_levelup(DolphinState* dolphin_state, uint32_t level, bool remaining) {
+    return (DOLPHIN_LVL_THRESHOLD * level * (level + 1) / 2) -
+           (remaining ? dolphin_state->data.icounter : 0);
+}

+ 4 - 0
applications/dolphin/dolphin_state.h

@@ -21,3 +21,7 @@ void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed);
 uint32_t dolphin_state_get_icounter(DolphinState* dolphin_state);
 
 uint32_t dolphin_state_get_butthurt(DolphinState* dolphin_state);
+
+uint32_t dolphin_state_get_level(DolphinState* dolphin_state);
+
+uint32_t dolphin_state_xp_to_levelup(DolphinState* dolphin_state, uint32_t level, bool remaining);

+ 52 - 17
applications/dolphin/dolphin_views.c

@@ -2,9 +2,11 @@
 #include <gui/view.h>
 #include <gui/gui.h>
 #include <gui/elements.h>
-
 #include <api-hal.h>
 
+static char* Lockmenu_Items[3] = {"Lock", "Set PIN", "DUMB mode"};
+static char* Meta_Items[3] = {"Passport", "Games", "???"};
+
 void dolphin_view_first_start_draw(Canvas* canvas, void* model) {
     DolphinViewFirstStartModel* m = model;
     canvas_clear(canvas);
@@ -57,40 +59,73 @@ void dolphin_view_first_start_draw(Canvas* canvas, void* model) {
 
 void dolphin_view_idle_main_draw(Canvas* canvas, void* model) {
     canvas_clear(canvas);
-    canvas_set_color(canvas, ColorBlack);
-    canvas_draw_icon_name(
-        canvas, canvas_width(canvas) - 80, canvas_height(canvas) - 60 + 6, I_Flipper_young_80x60);
-    canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str(canvas, 2, 10, "/\\: Stats");
-    canvas_draw_str(canvas, 5, 32, "OK: Menu");
-    canvas_draw_str(canvas, 2, 52, "\\/: Version");
+    DolphinViewMainModel* m = model;
+    if(m->animation) canvas_draw_icon(canvas, 0, 0, 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, 10, "Dolphin stats:");
+    canvas_draw_str(canvas, 2, 15, "Dolphin stats:");
 
     char buffer[64];
     canvas_set_font(canvas, FontSecondary);
     snprintf(buffer, 64, "Icounter: %ld", m->icounter);
-    canvas_draw_str(canvas, 5, 22, buffer);
+    canvas_draw_str(canvas, 5, 30, buffer);
     snprintf(buffer, 64, "Butthurt: %ld", m->butthurt);
-    canvas_draw_str(canvas, 5, 32, buffer);
-    canvas_draw_str(canvas, 5, 40, "< > change icounter");
+    canvas_draw_str(canvas, 5, 40, buffer);
+    canvas_draw_str(canvas, 0, 53, "[< >] icounter value   [ok] save");
+}
+
+void dolphin_view_lockmenu_draw(Canvas* canvas, void* model) {
+    DolphinViewMenuModel* 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_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);
+    }
+}
+
+void dolphin_view_idle_meta_draw(Canvas* canvas, void* model) {
+    DolphinViewMenuModel* m = model;
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+    canvas_set_font(canvas, FontSecondary);
+
+    canvas_draw_icon_name(canvas, 20, 23, I_BigProfile_24x24);
+    canvas_draw_icon_name(canvas, 55, 23, I_BigGames_24x24);
+    canvas_draw_icon_name(canvas, 90, 23, I_BigBurger_24x24);
+
+    canvas_draw_str_aligned(canvas, 66, 12, AlignCenter, AlignCenter, Meta_Items[m->idx]);
+
+    canvas_draw_frame(canvas, 17 + (35 * m->idx), 20, 30, 30);
+    canvas_set_color(canvas, ColorWhite);
+
+    canvas_draw_dot(canvas, 17 + (35 * m->idx), 20);
+    canvas_draw_dot(canvas, 17 + (35 * m->idx), 49);
+    canvas_draw_dot(canvas, 46 + (35 * m->idx), 20);
+    canvas_draw_dot(canvas, 46 + (35 * m->idx), 49);
+
+    canvas_set_color(canvas, ColorBlack);
 }
 
 void dolphin_view_idle_down_draw(Canvas* canvas, void* model) {
     canvas_clear(canvas);
     canvas_set_color(canvas, ColorBlack);
     canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str(canvas, 2, 10, "Version info:");
+    canvas_draw_str(canvas, 2, 15, "Version info:");
     canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str(canvas, 5, 22, TARGET " " BUILD_DATE);
-    canvas_draw_str(canvas, 5, 32, GIT_BRANCH);
-    canvas_draw_str(canvas, 5, 42, GIT_BRANCH_NUM " " GIT_COMMIT);
+    canvas_draw_str(canvas, 5, 25, TARGET " " BUILD_DATE);
+    canvas_draw_str(canvas, 5, 35, GIT_BRANCH);
+    canvas_draw_str(canvas, 5, 45, GIT_BRANCH_NUM " " GIT_COMMIT);
 
     char buffer[64];
     snprintf(
@@ -101,7 +136,7 @@ void dolphin_view_idle_down_draw(Canvas* canvas, void* model) {
         api_hal_version_get_hw_target(),
         api_hal_version_get_hw_body(),
         api_hal_version_get_hw_connect());
-    canvas_draw_str(canvas, 5, 52, buffer);
+    canvas_draw_str(canvas, 5, 55, buffer);
 }
 
 void dolphin_view_hw_mismatch_draw(Canvas* canvas, void* model) {

+ 17 - 0
applications/dolphin/dolphin_views.h

@@ -13,6 +13,8 @@ typedef enum {
     DolphinViewIdleUp,
     DolphinViewIdleDown,
     DolphinViewHwMismatch,
+    DolphinViewLockMenu,
+    DolphinViewIdleMeta,
 } DolphinViewIdle;
 
 typedef struct {
@@ -27,12 +29,27 @@ typedef struct {
     uint32_t butthurt;
 } DolphinViewIdleUpModel;
 
+typedef struct {
+    uint8_t idx;
+} DolphinViewMenuModel;
+
+typedef struct {
+    Icon* animation;
+    uint8_t scene_num;
+
+} DolphinViewMainModel;
+
 void dolphin_view_idle_main_draw(Canvas* canvas, void* model);
 bool dolphin_view_idle_main_input(InputEvent* event, void* context);
 
 void dolphin_view_idle_up_draw(Canvas* canvas, void* model);
+
+void dolphin_view_lockmenu_draw(Canvas* canvas, void* model);
+
 void dolphin_view_idle_down_draw(Canvas* canvas, void* model);
 
+void dolphin_view_idle_meta_draw(Canvas* canvas, void* model);
+
 void dolphin_view_hw_mismatch_draw(Canvas* canvas, void* model);
 
 uint32_t dolphin_view_idle_back(void* context);

+ 51 - 2
applications/gui/gui.c

@@ -46,8 +46,12 @@ void gui_redraw_status_bar(Gui* gui) {
     uint8_t x_used = 0;
     uint8_t width;
     ViewPort* view_port;
+    canvas_frame_set(
+        gui->canvas, GUI_STATUS_BAR_X, GUI_STATUS_BAR_Y, GUI_DISPLAY_WIDTH, GUI_STATUS_BAR_HEIGHT);
+    canvas_draw_icon_name(gui->canvas, 0, 0, I_Background_128x11);
+
     // Right side
-    x = GUI_DISPLAY_WIDTH + 2;
+    x = GUI_DISPLAY_WIDTH;
     ViewPortArray_it(it, gui->layers[GuiLayerStatusBarRight]);
     while(!ViewPortArray_end_p(it) && x_used < GUI_STATUS_BAR_WIDTH) {
         // Render view_port;
@@ -57,7 +61,28 @@ void gui_redraw_status_bar(Gui* gui) {
             if(!width) width = 8;
             x_used += width;
             x -= (width + 2);
-            canvas_frame_set(gui->canvas, x, GUI_STATUS_BAR_Y, width, GUI_STATUS_BAR_HEIGHT);
+            canvas_frame_set(gui->canvas, x - 3, GUI_STATUS_BAR_Y, width, GUI_STATUS_BAR_HEIGHT);
+
+            canvas_set_color(gui->canvas, ColorWhite);
+            canvas_draw_box(gui->canvas, 1, 1, width + 3, 11);
+
+            canvas_set_color(gui->canvas, ColorBlack);
+
+            canvas_draw_box(gui->canvas, 1, 0, 1, 12);
+            canvas_draw_box(gui->canvas, 0, 1, 1, 11);
+
+            canvas_draw_box(gui->canvas, 1, 11, width + 4, 2);
+            canvas_draw_box(gui->canvas, 1, 0, width + 4, 1);
+            canvas_draw_box(gui->canvas, width + 4, 1, 1, 11);
+
+            canvas_set_color(gui->canvas, ColorWhite);
+            canvas_draw_dot(gui->canvas, width + 4, 0);
+            canvas_draw_dot(gui->canvas, width + 4, 12);
+
+            canvas_set_color(gui->canvas, ColorBlack);
+
+            canvas_frame_set(gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_HEIGHT);
+
             view_port_draw(view_port, gui->canvas);
         }
         ViewPortArray_next(it);
@@ -73,7 +98,31 @@ void gui_redraw_status_bar(Gui* gui) {
             if(!width) width = 8;
             x_used += width;
             canvas_frame_set(gui->canvas, x, GUI_STATUS_BAR_Y, width, GUI_STATUS_BAR_HEIGHT);
+
+            canvas_set_color(gui->canvas, ColorWhite);
+            canvas_draw_box(gui->canvas, 1, 1, width + 3, 11);
+
+            canvas_set_color(gui->canvas, ColorBlack);
+
+            if(x == 0) { // Frame start
+                canvas_draw_box(gui->canvas, 1, 0, 1, 12);
+                canvas_draw_box(gui->canvas, 0, 1, 1, 11);
+            }
+
+            canvas_draw_box(gui->canvas, 1, 11, width + 4, 2);
+            canvas_draw_box(gui->canvas, 1, 0, width + 4, 1);
+            canvas_draw_box(gui->canvas, width + 4, 0, 1, 12);
+
+            canvas_set_color(gui->canvas, ColorWhite);
+            canvas_draw_dot(gui->canvas, width + 4, 0);
+            canvas_draw_dot(gui->canvas, width + 4, 12);
+
+            canvas_set_color(gui->canvas, ColorBlack);
+
+            canvas_frame_set(
+                gui->canvas, x + 3, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_HEIGHT);
             view_port_draw(view_port, gui->canvas);
+
             x += (width + 2);
         }
         ViewPortArray_next(it);

+ 10 - 0
applications/gui/icon.c

@@ -58,3 +58,13 @@ void icon_stop_animation(Icon* icon) {
     icon->tick = 0;
     icon->frame = 0;
 }
+
+uint8_t icon_get_current_frame(Icon* icon) {
+    furi_assert(icon);
+    return icon->frame;
+}
+
+bool icon_is_last_frame(Icon* icon) {
+    furi_assert(icon);
+    return icon->data->frame_count - icon->frame <= 1;
+}

+ 10 - 0
applications/gui/icon.h

@@ -46,6 +46,16 @@ void icon_start_animation(Icon* icon);
  */
 void icon_stop_animation(Icon* icon);
 
+/*
+ * Get current frame
+ */
+uint8_t icon_get_current_frame(Icon* icon);
+
+/*
+ * Returns true if current frame is a last one
+ */
+bool icon_is_last_frame(Icon* icon);
+
 #ifdef __cplusplus
 }
 #endif

+ 3 - 4
applications/power/power.c

@@ -42,11 +42,10 @@ void power_draw_usb_callback(Canvas* canvas, void* context) {
 void power_draw_battery_callback(Canvas* canvas, void* context) {
     assert(context);
     Power* power = context;
-
     canvas_draw_icon(canvas, 0, 0, power->battery_icon);
     with_view_model(
         power->info_view, (PowerInfoModel * model) {
-            canvas_draw_box(canvas, 2, 2, (float)model->charge / 100 * 14, 4);
+            canvas_draw_box(canvas, 2, 2, (float)model->charge / 100 * 20, 4);
             return false;
         });
 }
@@ -130,11 +129,11 @@ Power* power_alloc() {
     view_port_set_width(power->usb_view_port, icon_get_width(power->usb_icon));
     view_port_draw_callback_set(power->usb_view_port, power_draw_usb_callback, power);
 
-    power->battery_icon = assets_icons_get(I_Battery_19x8);
+    power->battery_icon = assets_icons_get(I_Battery_26x8);
     power->battery_view_port = view_port_alloc();
+
     view_port_set_width(power->battery_view_port, icon_get_width(power->battery_icon));
     view_port_draw_callback_set(power->battery_view_port, power_draw_battery_callback, power);
-
     return power;
 }
 

BIN
assets/icons/Animations/WatchingTV_128x64/frame_01.png


BIN
assets/icons/Animations/WatchingTV_128x64/frame_02.png


BIN
assets/icons/Animations/WatchingTV_128x64/frame_03.png


BIN
assets/icons/Animations/WatchingTV_128x64/frame_04.png


BIN
assets/icons/Animations/WatchingTV_128x64/frame_05.png


+ 1 - 0
assets/icons/Animations/WatchingTV_128x64/frame_rate

@@ -0,0 +1 @@
+10

BIN
assets/icons/Animations/Wink_128x64/frame_01.png


BIN
assets/icons/Animations/Wink_128x64/frame_02.png


BIN
assets/icons/Animations/Wink_128x64/frame_03.png


BIN
assets/icons/Animations/Wink_128x64/frame_04.png


BIN
assets/icons/Animations/Wink_128x64/frame_05.png


BIN
assets/icons/Animations/Wink_128x64/frame_06.png


BIN
assets/icons/Animations/Wink_128x64/frame_07.png


BIN
assets/icons/Animations/Wink_128x64/frame_08.png


BIN
assets/icons/Animations/Wink_128x64/frame_09.png


+ 1 - 0
assets/icons/Animations/Wink_128x64/frame_rate

@@ -0,0 +1 @@
+10

BIN
assets/icons/Dolphin/BigBurger_24x24.png


BIN
assets/icons/Dolphin/BigGames_24x24.png


BIN
assets/icons/Dolphin/BigProfile_24x24.png


BIN
assets/icons/Dolphin/DolphinOkay_41x43.png


BIN
assets/icons/Interface/DoorLeft_8x56.png


BIN
assets/icons/Interface/DoorLocked_10x56.png


BIN
assets/icons/Interface/DoorRight_8x56.png


BIN
assets/icons/Interface/PassportBottom_128x17.png


BIN
assets/icons/Interface/PassportLeft_6x47.png


BIN
assets/icons/StatusBar/Background_128x11.png


BIN
assets/icons/StatusBar/Background_128x8.png


BIN
assets/icons/StatusBar/Battery_26x8.png


BIN
assets/icons/StatusBar/Lock_8x8.png


BIN
assets/icons/StatusBar/PlaceholderL_11x13.png


BIN
assets/icons/StatusBar/PlaceholderR_30x13.png