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

Apply previous xtreme tweaks to new apps

Willy-JL 2 лет назад
Родитель
Сommit
72da156f9d
77 измененных файлов с 830 добавлено и 304 удалено
  1. BIN
      hid_app/assets/Alt_11x7.png
  2. BIN
      hid_app/assets/Ble_connected_15x15.png
  3. BIN
      hid_app/assets/Ble_disconnected_15x15.png
  4. BIN
      hid_app/assets/ButtonDown_7x4.png
  5. BIN
      hid_app/assets/ButtonLeft_4x7.png
  6. BIN
      hid_app/assets/ButtonRight_4x7.png
  7. BIN
      hid_app/assets/ButtonUp_7x4.png
  8. BIN
      hid_app/assets/Button_18x18.png
  9. BIN
      hid_app/assets/Circles_47x47.png
  10. BIN
      hid_app/assets/Cmd_15x7.png
  11. BIN
      hid_app/assets/Ctrl_15x7.png
  12. BIN
      hid_app/assets/Del_12x7.png
  13. BIN
      hid_app/assets/Esc_14x7.png
  14. BIN
      hid_app/assets/KB_key_Alt_17x10.png
  15. BIN
      hid_app/assets/KB_key_Cmd_17x10.png
  16. BIN
      hid_app/assets/KB_key_Ctl_17x10.png
  17. BIN
      hid_app/assets/KB_key_Del_17x10.png
  18. BIN
      hid_app/assets/KB_key_Esc_17x10.png
  19. BIN
      hid_app/assets/KB_key_Tab_17x10.png
  20. BIN
      hid_app/assets/Left_mouse_icon_9x9.png
  21. BIN
      hid_app/assets/Ok_btn_9x9.png
  22. BIN
      hid_app/assets/Ok_btn_pressed_13x13.png
  23. BIN
      hid_app/assets/OutCircles_70x51.png
  24. BIN
      hid_app/assets/Pause_icon_9x9.png
  25. BIN
      hid_app/assets/Pin_arrow_up_7x9.png
  26. BIN
      hid_app/assets/Pin_back_arrow_10x10.png
  27. BIN
      hid_app/assets/Pin_back_arrow_10x8.png
  28. BIN
      hid_app/assets/Pressed_Button_13x13.png
  29. BIN
      hid_app/assets/Pressed_Button_19x19.png
  30. BIN
      hid_app/assets/Right_mouse_icon_9x9.png
  31. BIN
      hid_app/assets/S_DOWN_31x15.png
  32. BIN
      hid_app/assets/S_LEFT_15x31.png
  33. BIN
      hid_app/assets/S_RIGHT_15x31.png
  34. BIN
      hid_app/assets/S_UP_31x15.png
  35. BIN
      hid_app/assets/Space_65x18.png
  36. BIN
      hid_app/assets/Tab_15x7.png
  37. BIN
      hid_app/assets/Voldwn_6x6.png
  38. BIN
      hid_app/assets/Volup_8x6.png
  39. 16 1
      hid_app/hid.c
  40. 2 0
      hid_app/hid.h
  41. 1 0
      hid_app/views.h
  42. 12 12
      hid_app/views/hid_keyboard.c
  43. 31 20
      hid_app/views/hid_media.c
  44. 21 15
      hid_app/views/hid_mouse.c
  45. 50 26
      hid_app/views/hid_mouse_jiggler.c
  46. 318 0
      hid_app/views/hid_numpad.c
  47. 14 0
      hid_app/views/hid_numpad.h
  48. 46 23
      hid_app/views/hid_tiktok.c
  49. BIN
      mass_storage/assets/ActiveConnection_50x64.png
  50. BIN
      mass_storage/assets/Drive_112x35.png
  51. 26 9
      mass_storage/mass_storage_app.c
  52. 6 8
      mass_storage/mass_storage_app_i.h
  53. 2 2
      mass_storage/scenes/mass_storage_scene_config.h
  54. 187 0
      mass_storage/scenes/mass_storage_scene_create_image.c
  55. 52 0
      mass_storage/scenes/mass_storage_scene_create_image_name.c
  56. 0 87
      mass_storage/scenes/mass_storage_scene_file_name.c
  57. 2 7
      mass_storage/scenes/mass_storage_scene_file_select.c
  58. 32 42
      mass_storage/scenes/mass_storage_scene_start.c
  59. 0 40
      mass_storage/scenes/mass_storage_scene_usb_locked.c
  60. 10 10
      mass_storage/views/mass_storage_view.c
  61. 1 1
      music_player/application.fam
  62. 0 0
      music_player/music_10px.png
  63. 0 0
      nfc_magic/125_10px.png
  64. 1 1
      nfc_magic/application.fam
  65. BIN
      nfc_magic/assets/DolphinCommon_56x48.png
  66. BIN
      nfc_magic/assets/DolphinNice_96x59.png
  67. BIN
      nfc_magic/assets/NFC_manual_60x50.png
  68. BIN
      nfc_rfid_detector/images/Modern_reader_18x34.png
  69. BIN
      nfc_rfid_detector/images/Move_flipper_26x39.png
  70. BIN
      picopass/icons/DolphinMafia_115x62.png
  71. BIN
      picopass/icons/DolphinNice_96x59.png
  72. BIN
      picopass/icons/Nfc_10px.png
  73. BIN
      picopass/icons/RFIDDolphinReceive_97x61.png
  74. BIN
      picopass/icons/RFIDDolphinSend_97x61.png
  75. BIN
      spi_mem_manager/images/DolphinMafia_115x62.png
  76. BIN
      spi_mem_manager/images/DolphinNice_96x59.png
  77. BIN
      spi_mem_manager/images/SDQuestion_35x43.png

BIN
hid_app/assets/Alt_11x7.png


BIN
hid_app/assets/Ble_connected_15x15.png


BIN
hid_app/assets/Ble_disconnected_15x15.png


BIN
hid_app/assets/ButtonDown_7x4.png


BIN
hid_app/assets/ButtonLeft_4x7.png


BIN
hid_app/assets/ButtonRight_4x7.png


BIN
hid_app/assets/ButtonUp_7x4.png


BIN
hid_app/assets/Button_18x18.png


BIN
hid_app/assets/Circles_47x47.png


BIN
hid_app/assets/Cmd_15x7.png


BIN
hid_app/assets/Ctrl_15x7.png


BIN
hid_app/assets/Del_12x7.png


BIN
hid_app/assets/Esc_14x7.png


BIN
hid_app/assets/KB_key_Alt_17x10.png


BIN
hid_app/assets/KB_key_Cmd_17x10.png


BIN
hid_app/assets/KB_key_Ctl_17x10.png


BIN
hid_app/assets/KB_key_Del_17x10.png


BIN
hid_app/assets/KB_key_Esc_17x10.png


BIN
hid_app/assets/KB_key_Tab_17x10.png


BIN
hid_app/assets/Left_mouse_icon_9x9.png


BIN
hid_app/assets/Ok_btn_9x9.png


BIN
hid_app/assets/Ok_btn_pressed_13x13.png


BIN
hid_app/assets/OutCircles_70x51.png


BIN
hid_app/assets/Pause_icon_9x9.png


BIN
hid_app/assets/Pin_arrow_up_7x9.png


BIN
dap_link/icons/ActiveConnection_50x64.png → hid_app/assets/Pin_back_arrow_10x10.png


BIN
hid_app/assets/Pin_back_arrow_10x8.png


BIN
hid_app/assets/Pressed_Button_13x13.png


BIN
hid_app/assets/Pressed_Button_19x19.png


BIN
hid_app/assets/Right_mouse_icon_9x9.png


BIN
hid_app/assets/S_DOWN_31x15.png


BIN
hid_app/assets/S_LEFT_15x31.png


BIN
hid_app/assets/S_RIGHT_15x31.png


BIN
hid_app/assets/S_UP_31x15.png


BIN
hid_app/assets/Space_65x18.png


BIN
hid_app/assets/Tab_15x7.png


BIN
hid_app/assets/Voldwn_6x6.png


BIN
hid_app/assets/Volup_8x6.png


+ 16 - 1
hid_app/hid.c

@@ -9,6 +9,7 @@ enum HidDebugSubmenuIndex {
     HidSubmenuIndexKeynote,
     HidSubmenuIndexKeynoteVertical,
     HidSubmenuIndexKeyboard,
+    HidSubmenuIndexNumpad,
     HidSubmenuIndexMedia,
     HidSubmenuIndexTikTok,
     HidSubmenuIndexMouse,
@@ -30,6 +31,9 @@ static void hid_submenu_callback(void* context, uint32_t index) {
     } else if(index == HidSubmenuIndexKeyboard) {
         app->view_id = HidViewKeyboard;
         view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard);
+    } else if(index == HidSubmenuIndexNumpad) {
+        app->view_id = HidViewNumpad;
+        view_dispatcher_switch_to_view(app->view_dispatcher, HidViewNumpad);
     } else if(index == HidSubmenuIndexMedia) {
         app->view_id = HidViewMedia;
         view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMedia);
@@ -61,6 +65,7 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con
     }
     hid_keynote_set_connected_status(hid->hid_keynote, connected);
     hid_keyboard_set_connected_status(hid->hid_keyboard, connected);
+    hid_numpad_set_connected_status(hid->hid_numpad, connected);
     hid_media_set_connected_status(hid->hid_media, connected);
     hid_mouse_set_connected_status(hid->hid_mouse, connected);
     hid_mouse_clicker_set_connected_status(hid->hid_mouse_clicker, connected);
@@ -119,6 +124,8 @@ Hid* hid_alloc(HidTransport transport) {
         app);
     submenu_add_item(
         app->device_type_submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app);
+    submenu_add_item(
+        app->device_type_submenu, "Numpad", HidSubmenuIndexNumpad, hid_submenu_callback, app);
     submenu_add_item(
         app->device_type_submenu, "Media", HidSubmenuIndexMedia, hid_submenu_callback, app);
     submenu_add_item(
@@ -126,7 +133,7 @@ Hid* hid_alloc(HidTransport transport) {
     if(app->transport == HidTransportBle) {
         submenu_add_item(
             app->device_type_submenu,
-            "TikTok Controller",
+            "TikTok / YT Shorts",
             HidSubmenuIndexTikTok,
             hid_submenu_callback,
             app);
@@ -177,6 +184,12 @@ Hid* hid_app_alloc_view(void* context) {
     view_dispatcher_add_view(
         app->view_dispatcher, HidViewKeyboard, hid_keyboard_get_view(app->hid_keyboard));
 
+    //Numpad keyboard view
+    app->hid_numpad = hid_numpad_alloc(app);
+    view_set_previous_callback(hid_numpad_get_view(app->hid_numpad), hid_exit_confirm_view);
+    view_dispatcher_add_view(
+        app->view_dispatcher, HidViewNumpad, hid_numpad_get_view(app->hid_numpad));
+
     // Media view
     app->hid_media = hid_media_alloc(app);
     view_set_previous_callback(hid_media_get_view(app->hid_media), hid_exit_confirm_view);
@@ -233,6 +246,8 @@ void hid_free(Hid* app) {
     hid_keynote_free(app->hid_keynote);
     view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard);
     hid_keyboard_free(app->hid_keyboard);
+    view_dispatcher_remove_view(app->view_dispatcher, HidViewNumpad);
+    hid_numpad_free(app->hid_numpad);
     view_dispatcher_remove_view(app->view_dispatcher, HidViewMedia);
     hid_media_free(app->hid_media);
     view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse);

+ 2 - 0
hid_app/hid.h

@@ -18,6 +18,7 @@
 #include <gui/modules/popup.h>
 #include "views/hid_keynote.h"
 #include "views/hid_keyboard.h"
+#include "views/hid_numpad.h"
 #include "views/hid_media.h"
 #include "views/hid_mouse.h"
 #include "views/hid_mouse_clicker.h"
@@ -42,6 +43,7 @@ struct Hid {
     DialogEx* dialog;
     HidKeynote* hid_keynote;
     HidKeyboard* hid_keyboard;
+    HidNumpad* hid_numpad;
     HidMedia* hid_media;
     HidMouse* hid_mouse;
     HidMouseClicker* hid_mouse_clicker;

+ 1 - 0
hid_app/views.h

@@ -2,6 +2,7 @@ typedef enum {
     HidViewSubmenu,
     HidViewKeynote,
     HidViewKeyboard,
+    HidViewNumpad,
     HidViewMedia,
     HidViewMouse,
     HidViewMouseClicker,

+ 12 - 12
hid_app/views/hid_keyboard.c

@@ -140,18 +140,18 @@ const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {
         {.width = 1, .icon = &I_ButtonRight_4x7, .value = HID_KEYBOARD_RIGHT_ARROW},
     },
     {
-        {.width = 2, .icon = &I_Ctrl_15x7, .value = HID_KEYBOARD_L_CTRL},
-        {.width = 0, .value = HID_KEYBOARD_L_CTRL},
-        {.width = 2, .icon = &I_Alt_11x7, .value = HID_KEYBOARD_L_ALT},
-        {.width = 0, .value = HID_KEYBOARD_L_ALT},
-        {.width = 2, .icon = &I_Cmd_15x7, .value = HID_KEYBOARD_L_GUI},
-        {.width = 0, .value = HID_KEYBOARD_L_GUI},
-        {.width = 2, .icon = &I_Tab_15x7, .value = HID_KEYBOARD_TAB},
-        {.width = 0, .value = HID_KEYBOARD_TAB},
-        {.width = 2, .icon = &I_Esc_14x7, .value = HID_KEYBOARD_ESCAPE},
-        {.width = 0, .value = HID_KEYBOARD_ESCAPE},
-        {.width = 2, .icon = &I_Del_12x7, .value = HID_KEYBOARD_DELETE_FORWARD},
-        {.width = 0, .value = HID_KEYBOARD_DELETE_FORWARD},
+        {.width = 2, .icon = &I_KB_key_Ctl_17x10, .value = HID_KEYBOARD_L_CTRL},
+        {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_CTRL},
+        {.width = 2, .icon = &I_KB_key_Alt_17x10, .value = HID_KEYBOARD_L_ALT},
+        {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_ALT},
+        {.width = 2, .icon = &I_KB_key_Cmd_17x10, .value = HID_KEYBOARD_L_GUI},
+        {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_GUI},
+        {.width = 2, .icon = &I_KB_key_Tab_17x10, .value = HID_KEYBOARD_TAB},
+        {.width = 0, .icon = NULL, .value = HID_KEYBOARD_TAB},
+        {.width = 2, .icon = &I_KB_key_Esc_17x10, .value = HID_KEYBOARD_ESCAPE},
+        {.width = 0, .icon = NULL, .value = HID_KEYBOARD_ESCAPE},
+        {.width = 2, .icon = &I_KB_key_Del_17x10, .value = HID_KEYBOARD_DELETE_FORWARD},
+        {.width = 0, .icon = NULL, .value = HID_KEYBOARD_DELETE_FORWARD},
     },
 };
 

+ 31 - 20
hid_app/views/hid_media.c

@@ -21,6 +21,7 @@ typedef struct {
     bool down_pressed;
     bool ok_pressed;
     bool connected;
+    bool back_pressed;
     HidTransport transport;
 } HidMediaModel;
 
@@ -55,61 +56,72 @@ static void hid_media_draw_callback(Canvas* canvas, void* context) {
     canvas_set_font(canvas, FontSecondary);
 
     // Keypad circles
-    canvas_draw_icon(canvas, 76, 8, &I_Circles_47x47);
+    canvas_draw_icon(canvas, 58, 3, &I_OutCircles_70x51);
 
     // Up
     if(model->up_pressed) {
         canvas_set_bitmap_mode(canvas, 1);
-        canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13);
+        canvas_draw_icon(canvas, 68, 6, &I_S_UP_31x15);
         canvas_set_bitmap_mode(canvas, 0);
         canvas_set_color(canvas, ColorWhite);
     }
-    canvas_draw_icon(canvas, 96, 12, &I_Volup_8x6);
+    canvas_draw_icon(canvas, 79, 9, &I_Volup_8x6);
     canvas_set_color(canvas, ColorBlack);
 
     // Down
     if(model->down_pressed) {
         canvas_set_bitmap_mode(canvas, 1);
-        canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13);
+        canvas_draw_icon(canvas, 68, 36, &I_S_DOWN_31x15);
         canvas_set_bitmap_mode(canvas, 0);
         canvas_set_color(canvas, ColorWhite);
     }
-    canvas_draw_icon(canvas, 96, 45, &I_Voldwn_6x6);
+    canvas_draw_icon(canvas, 80, 41, &I_Voldwn_6x6);
     canvas_set_color(canvas, ColorBlack);
 
     // Left
     if(model->left_pressed) {
         canvas_set_bitmap_mode(canvas, 1);
-        canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13);
+        canvas_draw_icon(canvas, 61, 13, &I_S_LEFT_15x31);
         canvas_set_bitmap_mode(canvas, 0);
         canvas_set_color(canvas, ColorWhite);
     }
-    hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft);
-    hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft);
+    hid_media_draw_arrow(canvas, 65, 28, CanvasDirectionRightToLeft);
+    hid_media_draw_arrow(canvas, 70, 28, CanvasDirectionRightToLeft);
     canvas_set_color(canvas, ColorBlack);
 
     // Right
     if(model->right_pressed) {
         canvas_set_bitmap_mode(canvas, 1);
-        canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13);
+        canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT_15x31);
         canvas_set_bitmap_mode(canvas, 0);
         canvas_set_color(canvas, ColorWhite);
     }
-    hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight);
-    hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight);
+    hid_media_draw_arrow(canvas, 96, 28, CanvasDirectionLeftToRight);
+    hid_media_draw_arrow(canvas, 101, 28, CanvasDirectionLeftToRight);
     canvas_set_color(canvas, ColorBlack);
 
     // Ok
     if(model->ok_pressed) {
-        canvas_draw_icon(canvas, 93, 25, &I_Pressed_Button_13x13);
+        canvas_set_bitmap_mode(canvas, 1);
+        canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19);
+        canvas_set_bitmap_mode(canvas, 0);
         canvas_set_color(canvas, ColorWhite);
     }
-    hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight);
-    canvas_draw_line(canvas, 100, 29, 100, 33);
-    canvas_draw_line(canvas, 102, 29, 102, 33);
+    hid_media_draw_arrow(canvas, 80, 28, CanvasDirectionLeftToRight);
+    canvas_draw_line(canvas, 84, 26, 84, 30);
+    canvas_draw_line(canvas, 86, 26, 86, 30);
     canvas_set_color(canvas, ColorBlack);
 
     // Exit
+    if(model->back_pressed) {
+        canvas_set_bitmap_mode(canvas, 1);
+        canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19);
+        canvas_set_bitmap_mode(canvas, 0);
+        canvas_set_color(canvas, ColorWhite);
+    }
+    canvas_draw_icon(canvas, 111, 38, &I_Pin_back_arrow_10x10);
+    canvas_set_color(canvas, ColorBlack);
+
     canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);
     canvas_set_font(canvas, FontSecondary);
     elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit");
@@ -135,6 +147,8 @@ static void hid_media_process_press(HidMedia* hid_media, InputEvent* event) {
             } else if(event->key == InputKeyOk) {
                 model->ok_pressed = true;
                 hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_PLAY_PAUSE);
+            } else if(event->key == InputKeyBack) {
+                model->back_pressed = true;
             }
         },
         true);
@@ -160,6 +174,8 @@ static void hid_media_process_release(HidMedia* hid_media, InputEvent* event) {
             } else if(event->key == InputKeyOk) {
                 model->ok_pressed = false;
                 hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_PLAY_PAUSE);
+            } else if(event->key == InputKeyBack) {
+                model->back_pressed = false;
             }
         },
         true);
@@ -176,12 +192,7 @@ static bool hid_media_input_callback(InputEvent* event, void* context) {
     } else if(event->type == InputTypeRelease) {
         hid_media_process_release(hid_media, event);
         consumed = true;
-    } else if(event->type == InputTypeShort) {
-        if(event->key == InputKeyBack) {
-            hid_hal_consumer_key_release_all(hid_media->hid);
-        }
     }
-
     return consumed;
 }
 

+ 21 - 15
hid_app/views/hid_mouse.c

@@ -49,61 +49,67 @@ static void hid_mouse_draw_callback(Canvas* canvas, void* context) {
     }
 
     // Keypad circles
-    canvas_draw_icon(canvas, 64, 8, &I_Circles_47x47);
+    canvas_draw_icon(canvas, 58, 3, &I_OutCircles_70x51);
 
     // Up
     if(model->up_pressed) {
         canvas_set_bitmap_mode(canvas, 1);
-        canvas_draw_icon(canvas, 81, 9, &I_Pressed_Button_13x13);
+        canvas_draw_icon(canvas, 68, 6, &I_S_UP_31x15);
         canvas_set_bitmap_mode(canvas, 0);
         canvas_set_color(canvas, ColorWhite);
     }
-    canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up_7x9);
+    canvas_draw_icon(canvas, 80, 8, &I_Pin_arrow_up_7x9);
     canvas_set_color(canvas, ColorBlack);
 
     // Down
     if(model->down_pressed) {
         canvas_set_bitmap_mode(canvas, 1);
-        canvas_draw_icon(canvas, 81, 41, &I_Pressed_Button_13x13);
+        canvas_draw_icon(canvas, 68, 36, &I_S_DOWN_31x15);
         canvas_set_bitmap_mode(canvas, 0);
         canvas_set_color(canvas, ColorWhite);
     }
-    canvas_draw_icon(canvas, 84, 43, &I_Pin_arrow_down_7x9);
+    canvas_draw_icon(canvas, 80, 40, &I_Pin_arrow_down_7x9);
     canvas_set_color(canvas, ColorBlack);
 
     // Left
     if(model->left_pressed) {
         canvas_set_bitmap_mode(canvas, 1);
-        canvas_draw_icon(canvas, 65, 25, &I_Pressed_Button_13x13);
+        canvas_draw_icon(canvas, 61, 13, &I_S_LEFT_15x31);
         canvas_set_bitmap_mode(canvas, 0);
         canvas_set_color(canvas, ColorWhite);
     }
-    canvas_draw_icon(canvas, 67, 28, &I_Pin_arrow_left_9x7);
+    canvas_draw_icon(canvas, 63, 25, &I_Pin_arrow_left_9x7);
     canvas_set_color(canvas, ColorBlack);
 
     // Right
     if(model->right_pressed) {
         canvas_set_bitmap_mode(canvas, 1);
-        canvas_draw_icon(canvas, 97, 25, &I_Pressed_Button_13x13);
+        canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT_15x31);
         canvas_set_bitmap_mode(canvas, 0);
         canvas_set_color(canvas, ColorWhite);
     }
-    canvas_draw_icon(canvas, 99, 28, &I_Pin_arrow_right_9x7);
+    canvas_draw_icon(canvas, 95, 25, &I_Pin_arrow_right_9x7);
     canvas_set_color(canvas, ColorBlack);
 
     // Ok
     if(model->left_mouse_pressed) {
-        canvas_draw_icon(canvas, 81, 25, &I_Ok_btn_pressed_13x13);
-    } else {
-        canvas_draw_icon(canvas, 83, 27, &I_Left_mouse_icon_9x9);
+        canvas_set_bitmap_mode(canvas, 1);
+        canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19);
+        canvas_set_bitmap_mode(canvas, 0);
+        canvas_set_color(canvas, ColorWhite);
     }
+    canvas_draw_icon(canvas, 79, 24, &I_Left_mouse_icon_9x9);
+    canvas_set_color(canvas, ColorBlack);
 
     // Back
     if(model->right_mouse_pressed) {
-        canvas_draw_icon(canvas, 108, 48, &I_Ok_btn_pressed_13x13);
-    } else {
-        canvas_draw_icon(canvas, 110, 50, &I_Right_mouse_icon_9x9);
+        canvas_set_bitmap_mode(canvas, 1);
+        canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19);
+        canvas_set_bitmap_mode(canvas, 0);
+        canvas_set_color(canvas, ColorWhite);
     }
+    canvas_draw_icon(canvas, 112, 38, &I_Right_mouse_icon_9x9);
+    canvas_set_color(canvas, ColorBlack);
 }
 
 static void hid_mouse_process(HidMouse* hid_mouse, InputEvent* event) {

+ 50 - 26
hid_app/views/hid_mouse_jiggler.c

@@ -15,10 +15,13 @@ struct HidMouseJiggler {
 typedef struct {
     bool connected;
     bool running;
+    int interval_idx;
     uint8_t counter;
     HidTransport transport;
 } HidMouseJigglerModel;
 
+const int intervals[6] = {500, 2000, 5000, 10000, 30000, 60000};
+
 static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {
     furi_assert(context);
     HidMouseJigglerModel* model = context;
@@ -33,29 +36,39 @@ static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {
     }
 
     canvas_set_font(canvas, FontPrimary);
-    elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Jiggler");
+    elements_multiline_text_aligned(canvas, 17, 2, AlignLeft, AlignTop, "Mouse Jiggler");
+
+    // Timeout
+    elements_multiline_text(canvas, AlignLeft, 26, "Interval (ms):");
+    canvas_set_font(canvas, FontSecondary);
+    if(model->interval_idx != 0) canvas_draw_icon(canvas, 74, 19, &I_ButtonLeft_4x7);
+    if(model->interval_idx != (int)COUNT_OF(intervals) - 1)
+        canvas_draw_icon(canvas, 80, 19, &I_ButtonRight_4x7);
+    FuriString* interval_str = furi_string_alloc_printf("%d", intervals[model->interval_idx]);
+    elements_multiline_text(canvas, 91, 26, furi_string_get_cstr(interval_str));
+    furi_string_free(interval_str);
 
     canvas_set_font(canvas, FontPrimary);
-    elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto jiggle");
+    elements_multiline_text(canvas, AlignLeft, 40, "Press Start\nto jiggle");
     canvas_set_font(canvas, FontSecondary);
 
     // Ok
-    canvas_draw_icon(canvas, 63, 25, &I_Space_65x18);
+    canvas_draw_icon(canvas, 63, 30, &I_Space_65x18);
     if(model->running) {
-        elements_slightly_rounded_box(canvas, 66, 27, 60, 13);
+        elements_slightly_rounded_box(canvas, 66, 32, 60, 13);
         canvas_set_color(canvas, ColorWhite);
     }
-    canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9);
+    canvas_draw_icon(canvas, 74, 34, &I_Ok_btn_9x9);
     if(model->running) {
-        elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop");
+        elements_multiline_text_aligned(canvas, 91, 41, AlignLeft, AlignBottom, "Stop");
     } else {
-        elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start");
+        elements_multiline_text_aligned(canvas, 91, 41, AlignLeft, AlignBottom, "Start");
     }
     canvas_set_color(canvas, ColorBlack);
 
     // Back
-    canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8);
-    elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit");
+    canvas_draw_icon(canvas, 74, 54, &I_Pin_back_arrow_10x8);
+    elements_multiline_text_aligned(canvas, 91, 62, AlignLeft, AlignBottom, "Quit");
 }
 
 static void hid_mouse_jiggler_timer_callback(void* context) {
@@ -76,13 +89,6 @@ static void hid_mouse_jiggler_timer_callback(void* context) {
         false);
 }
 
-static void hid_mouse_jiggler_enter_callback(void* context) {
-    furi_assert(context);
-    HidMouseJiggler* hid_mouse_jiggler = context;
-
-    furi_timer_start(hid_mouse_jiggler->timer, 500);
-}
-
 static void hid_mouse_jiggler_exit_callback(void* context) {
     furi_assert(context);
     HidMouseJiggler* hid_mouse_jiggler = context;
@@ -95,14 +101,30 @@ static bool hid_mouse_jiggler_input_callback(InputEvent* event, void* context) {
 
     bool consumed = false;
 
-    if(event->type == InputTypeShort && event->key == InputKeyOk) {
-        with_view_model(
-            hid_mouse_jiggler->view,
-            HidMouseJigglerModel * model,
-            { model->running = !model->running; },
-            true);
-        consumed = true;
-    }
+    with_view_model(
+        hid_mouse_jiggler->view,
+        HidMouseJigglerModel * model,
+        {
+            if(event->type == InputTypePress && event->key == InputKeyOk) {
+                model->running = !model->running;
+                if(model->running) {
+                    furi_timer_stop(hid_mouse_jiggler->timer);
+                    furi_timer_start(hid_mouse_jiggler->timer, intervals[model->interval_idx]);
+                };
+                consumed = true;
+            }
+            if(event->type == InputTypePress && event->key == InputKeyRight && !model->running &&
+               model->interval_idx < (int)COUNT_OF(intervals) - 1) {
+                model->interval_idx++;
+                consumed = true;
+            }
+            if(event->type == InputTypePress && event->key == InputKeyLeft && !model->running &&
+               model->interval_idx > 0) {
+                model->interval_idx--;
+                consumed = true;
+            }
+        },
+        true);
 
     return consumed;
 }
@@ -116,7 +138,6 @@ HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) {
         hid_mouse_jiggler->view, ViewModelTypeLocking, sizeof(HidMouseJigglerModel));
     view_set_draw_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_draw_callback);
     view_set_input_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_input_callback);
-    view_set_enter_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_enter_callback);
     view_set_exit_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_exit_callback);
 
     hid_mouse_jiggler->hid = hid;
@@ -127,7 +148,10 @@ HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) {
     with_view_model(
         hid_mouse_jiggler->view,
         HidMouseJigglerModel * model,
-        { model->transport = hid->transport; },
+        {
+            model->transport = hid->transport;
+            model->interval_idx = 2;
+        },
         true);
 
     return hid_mouse_jiggler;

+ 318 - 0
hid_app/views/hid_numpad.c

@@ -0,0 +1,318 @@
+#include "hid_numpad.h"
+#include <furi.h>
+#include <gui/elements.h>
+#include <gui/icon_i.h>
+#include "../hid.h"
+#include "hid_icons.h"
+
+#define TAG "HidNumpad"
+
+struct HidNumpad {
+    View* view;
+    Hid* hid;
+};
+
+typedef struct {
+    uint8_t last_x;
+    uint8_t last_y;
+    uint8_t x;
+    uint8_t y;
+    uint8_t last_key_code;
+    uint16_t modifier_code;
+    bool ok_pressed;
+    bool back_pressed;
+    bool connected;
+    char key_string[5];
+    HidTransport transport;
+} HidNumpadModel;
+
+typedef struct {
+    uint8_t width;
+    char* key;
+    uint8_t height;
+    const Icon* icon;
+    uint8_t value;
+} HidNumpadKey;
+
+typedef struct {
+    int8_t x;
+    int8_t y;
+} HidNumpadPoint;
+
+#define MARGIN_TOP 32
+#define MARGIN_LEFT 1
+#define KEY_WIDTH 20
+#define KEY_HEIGHT 15
+#define KEY_PADDING 1
+#define ROW_COUNT 6
+#define COLUMN_COUNT 3
+
+const HidNumpadKey hid_numpad_keyset[ROW_COUNT][COLUMN_COUNT] = {
+    {
+        {.width = 1, .height = 1, .icon = NULL, .key = "NL", .value = HID_KEYPAD_NUMLOCK},
+        {.width = 1, .height = 1, .icon = NULL, .key = "/", .value = HID_KEYPAD_SLASH},
+        {.width = 1, .height = 1, .icon = NULL, .key = "*", .value = HID_KEYPAD_ASTERISK},
+        // {.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS},
+    },
+    {
+        {.width = 1, .height = 1, .icon = NULL, .key = "7", .value = HID_KEYPAD_7},
+        {.width = 1, .height = 1, .icon = NULL, .key = "8", .value = HID_KEYBOARD_8},
+        {.width = 1, .height = 1, .icon = NULL, .key = "9", .value = HID_KEYBOARD_9},
+        // {.width = 1, .height = 2, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS},
+    },
+    {
+        {.width = 1, .height = 1, .icon = NULL, .key = "4", .value = HID_KEYPAD_4},
+        {.width = 1, .height = 1, .icon = NULL, .key = "5", .value = HID_KEYPAD_5},
+        {.width = 1, .height = 1, .icon = NULL, .key = "6", .value = HID_KEYPAD_6},
+    },
+    {
+        {.width = 1, .height = 1, .icon = NULL, .key = "1", .value = HID_KEYPAD_1},
+        {.width = 1, .height = 1, .icon = NULL, .key = "2", .value = HID_KEYPAD_2},
+        {.width = 1, .height = 1, .icon = NULL, .key = "3", .value = HID_KEYPAD_3},
+        // {.width = 1, .height = 2, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER},
+    },
+    {
+        {.width = 2, .height = 1, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0},
+        {.width = 0, .height = 0, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0},
+        {.width = 1, .height = 1, .icon = NULL, .key = ".", .value = HID_KEYPAD_DOT},
+    },
+    {
+        {.width = 1, .height = 1, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER},
+        {.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS},
+        {.width = 1, .height = 1, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS},
+    },
+};
+
+static void hid_numpad_draw_key(
+    Canvas* canvas,
+    HidNumpadModel* model,
+    uint8_t x,
+    uint8_t y,
+    HidNumpadKey key,
+    bool selected) {
+    if(!key.width || !key.height) return;
+
+    canvas_set_color(canvas, ColorBlack);
+    uint8_t keyWidth = KEY_WIDTH * key.width + KEY_PADDING * (key.width - 1);
+    uint8_t keyHeight = KEY_HEIGHT * key.height + KEY_PADDING * (key.height - 1);
+    if(selected) {
+        elements_slightly_rounded_box(
+            canvas,
+            MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING),
+            MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING),
+            keyWidth,
+            keyHeight);
+        canvas_set_color(canvas, ColorWhite);
+    } else {
+        elements_slightly_rounded_frame(
+            canvas,
+            MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING),
+            MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING),
+            keyWidth,
+            keyHeight);
+    }
+    if(key.icon != NULL) {
+        canvas_draw_icon(
+            canvas,
+            MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 - key.icon->width / 2,
+            MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + keyHeight / 2 - key.icon->height / 2,
+            key.icon);
+    } else {
+        strcpy(model->key_string, key.key);
+        canvas_draw_str_aligned(
+            canvas,
+            MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 + 1,
+            MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + keyHeight / 2 + 1,
+            AlignCenter,
+            AlignCenter,
+            model->key_string);
+    }
+}
+
+static void hid_numpad_draw_callback(Canvas* canvas, void* context) {
+    furi_assert(context);
+    HidNumpadModel* model = context;
+
+    // Header
+    canvas_set_font(canvas, FontPrimary);
+    if(model->transport == HidTransportBle) {
+        if(model->connected) {
+            canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
+        } else {
+            canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
+            elements_multiline_text_aligned(
+                canvas, 7, 60, AlignLeft, AlignBottom, "Waiting for\nConnection...");
+        }
+        elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Numpad");
+
+    } else {
+        elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Numpad");
+    }
+
+    canvas_draw_icon(canvas, 3, 18, &I_Pin_back_arrow_10x8);
+    canvas_set_font(canvas, FontSecondary);
+    elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit");
+
+    if(!model->connected && (model->transport == HidTransportBle)) {
+        return;
+    }
+
+    canvas_set_font(canvas, FontKeyboard);
+    uint8_t initY = 0; // = model->y == 0 ? 0 : 1;
+
+    // if(model->y > ROW_COUNT) {
+    //     initY = model->y - (ROW_COUNT - 1);
+    // }
+
+    for(uint8_t y = initY; y < ROW_COUNT; y++) {
+        const HidNumpadKey* numpadKeyRow = hid_numpad_keyset[y];
+        uint8_t x = 0;
+        for(uint8_t i = 0; i < COLUMN_COUNT; i++) {
+            HidNumpadKey key = numpadKeyRow[i];
+            bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y;
+            bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE;
+            hid_numpad_draw_key(
+                canvas,
+                model,
+                x,
+                y - initY,
+                key,
+                (!model->ok_pressed && keySelected) || backSelected);
+            x += key.width;
+        }
+    }
+}
+
+static uint8_t hid_numpad_get_selected_key(HidNumpadModel* model) {
+    HidNumpadKey key = hid_numpad_keyset[model->y][model->x];
+    return key.value;
+}
+
+static void hid_numpad_get_select_key(HidNumpadModel* model, HidNumpadPoint delta) {
+    do {
+        const int delta_sum = model->y + delta.y;
+        model->y = delta_sum < 0 ? ROW_COUNT - 1 : delta_sum % ROW_COUNT;
+    } while(delta.y != 0 && hid_numpad_keyset[model->y][model->x].value == 0);
+
+    do {
+        const int delta_sum = model->x + delta.x;
+        model->x = delta_sum < 0 ? COLUMN_COUNT - 1 : delta_sum % COLUMN_COUNT;
+    } while(delta.x != 0 && hid_numpad_keyset[model->y][model->x].width == 0);
+}
+
+static void hid_numpad_process(HidNumpad* hid_numpad, InputEvent* event) {
+    with_view_model(
+        hid_numpad->view,
+        HidNumpadModel * model,
+        {
+            if(event->key == InputKeyOk) {
+                if(event->type == InputTypePress) {
+                    model->ok_pressed = true;
+                } else if(event->type == InputTypeLong || event->type == InputTypeShort) {
+                    model->last_key_code = hid_numpad_get_selected_key(model);
+                    hid_hal_keyboard_press(
+                        hid_numpad->hid, model->modifier_code | model->last_key_code);
+                } else if(event->type == InputTypeRelease) {
+                    hid_hal_keyboard_release(
+                        hid_numpad->hid, model->modifier_code | model->last_key_code);
+                    model->ok_pressed = false;
+                }
+            } else if(event->key == InputKeyBack) {
+                if(event->type == InputTypePress) {
+                    model->back_pressed = true;
+                } else if(event->type == InputTypeShort) {
+                    hid_hal_keyboard_press(hid_numpad->hid, HID_KEYBOARD_DELETE);
+                    hid_hal_keyboard_release(hid_numpad->hid, HID_KEYBOARD_DELETE);
+                } else if(event->type == InputTypeRelease) {
+                    model->back_pressed = false;
+                }
+            } else if(event->type == InputTypePress || event->type == InputTypeRepeat) {
+                if(event->key == InputKeyUp) {
+                    hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 0, .y = -1});
+                } else if(event->key == InputKeyDown) {
+                    hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 0, .y = 1});
+                } else if(event->key == InputKeyLeft) {
+                    if(model->last_x == 2 && model->last_y == 2 && model->y == 1 &&
+                       model->x == 3) {
+                        model->x = model->last_x;
+                        model->y = model->last_y;
+                    } else if(
+                        model->last_x == 2 && model->last_y == 4 && model->y == 3 &&
+                        model->x == 3) {
+                        model->x = model->last_x;
+                        model->y = model->last_y;
+                    } else
+                        hid_numpad_get_select_key(model, (HidNumpadPoint){.x = -1, .y = 0});
+                    model->last_x = 0;
+                    model->last_y = 0;
+                } else if(event->key == InputKeyRight) {
+                    if(model->x == 2 && model->y == 2) {
+                        model->last_x = model->x;
+                        model->last_y = model->y;
+                        hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 1, .y = -1});
+                    } else if(model->x == 2 && model->y == 4) {
+                        model->last_x = model->x;
+                        model->last_y = model->y;
+                        hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 1, .y = -1});
+                    } else {
+                        hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 1, .y = 0});
+                    }
+                }
+            }
+        },
+        true);
+}
+
+static bool hid_numpad_input_callback(InputEvent* event, void* context) {
+    furi_assert(context);
+    HidNumpad* hid_numpad = context;
+    bool consumed = false;
+
+    if(event->type == InputTypeLong && event->key == InputKeyBack) {
+        hid_hal_keyboard_release_all(hid_numpad->hid);
+    } else {
+        hid_numpad_process(hid_numpad, event);
+        consumed = true;
+    }
+
+    return consumed;
+}
+
+HidNumpad* hid_numpad_alloc(Hid* bt_hid) {
+    HidNumpad* hid_numpad = malloc(sizeof(HidNumpad));
+    hid_numpad->view = view_alloc();
+    hid_numpad->hid = bt_hid;
+    view_set_context(hid_numpad->view, hid_numpad);
+    view_allocate_model(hid_numpad->view, ViewModelTypeLocking, sizeof(HidNumpadModel));
+    view_set_orientation(hid_numpad->view, ViewOrientationVertical);
+    view_set_draw_callback(hid_numpad->view, hid_numpad_draw_callback);
+    view_set_input_callback(hid_numpad->view, hid_numpad_input_callback);
+
+    with_view_model(
+        hid_numpad->view,
+        HidNumpadModel * model,
+        {
+            model->transport = bt_hid->transport;
+            model->y = 0;
+        },
+        true);
+
+    return hid_numpad;
+}
+
+void hid_numpad_free(HidNumpad* hid_numpad) {
+    furi_assert(hid_numpad);
+    view_free(hid_numpad->view);
+    free(hid_numpad);
+}
+
+View* hid_numpad_get_view(HidNumpad* hid_numpad) {
+    furi_assert(hid_numpad);
+    return hid_numpad->view;
+}
+
+void hid_numpad_set_connected_status(HidNumpad* hid_numpad, bool connected) {
+    furi_assert(hid_numpad);
+    with_view_model(
+        hid_numpad->view, HidNumpadModel * model, { model->connected = connected; }, true);
+}

+ 14 - 0
hid_app/views/hid_numpad.h

@@ -0,0 +1,14 @@
+#pragma once
+
+#include <gui/view.h>
+
+typedef struct Hid Hid;
+typedef struct HidNumpad HidNumpad;
+
+HidNumpad* hid_numpad_alloc(Hid* bt_hid);
+
+void hid_numpad_free(HidNumpad* hid_numpad);
+
+View* hid_numpad_get_view(HidNumpad* hid_numpad);
+
+void hid_numpad_set_connected_status(HidNumpad* hid_numpad, bool connected);

+ 46 - 23
hid_app/views/hid_tiktok.c

@@ -19,6 +19,7 @@ typedef struct {
     bool ok_pressed;
     bool connected;
     bool is_cursor_set;
+    bool back_mouse_pressed;
     HidTransport transport;
 } HidTikTokModel;
 
@@ -36,58 +37,73 @@ static void hid_tiktok_draw_callback(Canvas* canvas, void* context) {
     }
 
     canvas_set_font(canvas, FontPrimary);
-    elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "TikTok");
+    elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "TikTok /");
+    elements_multiline_text_aligned(canvas, 3, 18, AlignLeft, AlignTop, "YT Shorts");
     canvas_set_font(canvas, FontSecondary);
 
     // Keypad circles
-    canvas_draw_icon(canvas, 76, 8, &I_Circles_47x47);
+    canvas_draw_icon(canvas, 58, 3, &I_OutCircles_70x51);
+
+    // Pause
+    if(model->back_mouse_pressed) {
+        canvas_set_bitmap_mode(canvas, 1);
+        canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19);
+        canvas_set_bitmap_mode(canvas, 0);
+        canvas_set_color(canvas, ColorWhite);
+    }
+    canvas_draw_icon(canvas, 113, 37, &I_Pause_icon_9x9);
+    canvas_set_color(canvas, ColorBlack);
 
     // Up
     if(model->up_pressed) {
         canvas_set_bitmap_mode(canvas, 1);
-        canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13);
+        canvas_draw_icon(canvas, 68, 6, &I_S_UP_31x15);
         canvas_set_bitmap_mode(canvas, 0);
         canvas_set_color(canvas, ColorWhite);
     }
-    canvas_draw_icon(canvas, 96, 11, &I_Arr_up_7x9);
+    canvas_draw_icon(canvas, 80, 8, &I_Arr_up_7x9);
     canvas_set_color(canvas, ColorBlack);
 
     // Down
     if(model->down_pressed) {
         canvas_set_bitmap_mode(canvas, 1);
-        canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13);
+        canvas_draw_icon(canvas, 68, 36, &I_S_DOWN_31x15);
         canvas_set_bitmap_mode(canvas, 0);
         canvas_set_color(canvas, ColorWhite);
     }
-    canvas_draw_icon(canvas, 96, 44, &I_Arr_dwn_7x9);
+    canvas_draw_icon(canvas, 80, 40, &I_Arr_dwn_7x9);
     canvas_set_color(canvas, ColorBlack);
 
     // Left
     if(model->left_pressed) {
         canvas_set_bitmap_mode(canvas, 1);
-        canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13);
+        canvas_draw_icon(canvas, 61, 13, &I_S_LEFT_15x31);
         canvas_set_bitmap_mode(canvas, 0);
         canvas_set_color(canvas, ColorWhite);
     }
-    canvas_draw_icon(canvas, 81, 29, &I_Voldwn_6x6);
+    canvas_draw_icon(canvas, 64, 25, &I_Voldwn_6x6);
     canvas_set_color(canvas, ColorBlack);
 
     // Right
     if(model->right_pressed) {
         canvas_set_bitmap_mode(canvas, 1);
-        canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13);
+        canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT_15x31);
         canvas_set_bitmap_mode(canvas, 0);
         canvas_set_color(canvas, ColorWhite);
     }
-    canvas_draw_icon(canvas, 111, 29, &I_Volup_8x6);
+    canvas_draw_icon(canvas, 95, 25, &I_Volup_8x6);
     canvas_set_color(canvas, ColorBlack);
 
     // Ok
     if(model->ok_pressed) {
-        canvas_draw_icon(canvas, 91, 23, &I_Like_pressed_17x17);
-    } else {
-        canvas_draw_icon(canvas, 94, 27, &I_Like_def_11x9);
+        canvas_set_bitmap_mode(canvas, 1);
+        canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19);
+        canvas_set_bitmap_mode(canvas, 0);
+        canvas_set_color(canvas, ColorWhite);
     }
+    canvas_draw_icon(canvas, 78, 25, &I_Like_def_11x9);
+    canvas_set_color(canvas, ColorBlack);
+
     // Exit
     canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);
     canvas_set_font(canvas, FontSecondary);
@@ -120,6 +136,8 @@ static void
         hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT);
     } else if(event->key == InputKeyOk) {
         model->ok_pressed = true;
+    } else if(event->key == InputKeyBack) {
+        model->back_mouse_pressed = true;
     }
 }
 
@@ -137,6 +155,8 @@ static void
         hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT);
     } else if(event->key == InputKeyOk) {
         model->ok_pressed = false;
+    } else if(event->key == InputKeyBack) {
+        model->back_mouse_pressed = false;
     }
 }
 
@@ -162,31 +182,34 @@ static bool hid_tiktok_input_callback(InputEvent* event, void* context) {
             } else if(event->type == InputTypeShort) {
                 if(event->key == InputKeyOk) {
                     hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
-                    furi_delay_ms(50);
+                    furi_delay_ms(25);
                     hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
-                    furi_delay_ms(50);
+                    furi_delay_ms(100);
                     hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
-                    furi_delay_ms(50);
+                    furi_delay_ms(25);
                     hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
                     consumed = true;
                 } else if(event->key == InputKeyUp) {
                     // Emulate up swipe
                     hid_hal_mouse_scroll(hid_tiktok->hid, -6);
-                    hid_hal_mouse_scroll(hid_tiktok->hid, -12);
-                    hid_hal_mouse_scroll(hid_tiktok->hid, -19);
-                    hid_hal_mouse_scroll(hid_tiktok->hid, -12);
+                    hid_hal_mouse_scroll(hid_tiktok->hid, -8);
+                    hid_hal_mouse_scroll(hid_tiktok->hid, -10);
+                    hid_hal_mouse_scroll(hid_tiktok->hid, -8);
                     hid_hal_mouse_scroll(hid_tiktok->hid, -6);
                     consumed = true;
                 } else if(event->key == InputKeyDown) {
                     // Emulate down swipe
                     hid_hal_mouse_scroll(hid_tiktok->hid, 6);
-                    hid_hal_mouse_scroll(hid_tiktok->hid, 12);
-                    hid_hal_mouse_scroll(hid_tiktok->hid, 19);
-                    hid_hal_mouse_scroll(hid_tiktok->hid, 12);
+                    hid_hal_mouse_scroll(hid_tiktok->hid, 8);
+                    hid_hal_mouse_scroll(hid_tiktok->hid, 10);
+                    hid_hal_mouse_scroll(hid_tiktok->hid, 8);
                     hid_hal_mouse_scroll(hid_tiktok->hid, 6);
                     consumed = true;
                 } else if(event->key == InputKeyBack) {
-                    hid_hal_consumer_key_release_all(hid_tiktok->hid);
+                    // Pause
+                    hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
+                    furi_delay_ms(50);
+                    hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT);
                     consumed = true;
                 }
             } else if(event->type == InputTypeLong) {

BIN
mass_storage/assets/ActiveConnection_50x64.png


BIN
mass_storage/assets/Drive_112x35.png


+ 26 - 9
mass_storage/mass_storage_app.c

@@ -48,6 +48,25 @@ MassStorageApp* mass_storage_app_alloc(char* arg) {
     app->fs_api = furi_record_open(RECORD_STORAGE);
     app->dialogs = furi_record_open(RECORD_DIALOGS);
 
+    app->create_image_size = (uint8_t)-1;
+    SDInfo sd_info;
+    if(storage_sd_info(app->fs_api, &sd_info) == FSE_OK) {
+        switch(sd_info.fs_type) {
+        case FST_FAT12:
+            app->create_image_max = 16LL * 1024 * 1024;
+            break;
+        case FST_FAT16:
+            app->create_image_max = 2LL * 1024 * 1024 * 1024;
+            break;
+        case FST_FAT32:
+            app->create_image_max = 4LL * 1024 * 1024 * 1024;
+            break;
+        default:
+            app->create_image_max = 0;
+            break;
+        }
+    }
+
     app->view_dispatcher = view_dispatcher_alloc();
     view_dispatcher_enable_queue(app->view_dispatcher);
 
@@ -81,18 +100,16 @@ MassStorageApp* mass_storage_app_alloc(char* arg) {
         MassStorageAppViewStart,
         variable_item_list_get_view(app->variable_item_list));
 
-    app->widget = widget_alloc();
+    app->popup = popup_alloc();
     view_dispatcher_add_view(
-        app->view_dispatcher, MassStorageAppViewWidget, widget_get_view(app->widget));
+        app->view_dispatcher, MassStorageAppViewPopup, popup_get_view(app->popup));
 
     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
 
+    scene_manager_set_scene_state(
+        app->scene_manager, MassStorageSceneStart, MassStorageSceneFileSelect);
     if(storage_file_exists(app->fs_api, furi_string_get_cstr(app->file_path))) {
-        if(!furi_hal_usb_is_locked()) {
-            scene_manager_next_scene(app->scene_manager, MassStorageSceneWork);
-        } else {
-            scene_manager_next_scene(app->scene_manager, MassStorageSceneUsbLocked);
-        }
+        scene_manager_next_scene(app->scene_manager, MassStorageSceneWork);
     } else {
         scene_manager_next_scene(app->scene_manager, MassStorageSceneStart);
     }
@@ -108,13 +125,13 @@ void mass_storage_app_free(MassStorageApp* app) {
     view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewTextInput);
     view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewStart);
     view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewLoading);
-    view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewWidget);
+    view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewPopup);
 
     mass_storage_free(app->mass_storage_view);
     text_input_free(app->text_input);
     variable_item_list_free(app->variable_item_list);
     loading_free(app->loading);
-    widget_free(app->widget);
+    popup_free(app->popup);
 
     // View dispatcher
     view_dispatcher_free(app->view_dispatcher);

+ 6 - 8
mass_storage/mass_storage_app_i.h

@@ -13,7 +13,7 @@
 #include <gui/modules/variable_item_list.h>
 #include <gui/modules/text_input.h>
 #include <gui/modules/loading.h>
-#include <gui/modules/widget.h>
+#include <gui/modules/popup.h>
 #include <storage/storage.h>
 #include "views/mass_storage_view.h"
 #include <mass_storage_icons.h>
@@ -27,7 +27,7 @@ struct MassStorageApp {
     Storage* fs_api;
     ViewDispatcher* view_dispatcher;
     SceneManager* scene_manager;
-    Widget* widget;
+    Popup* popup;
     DialogsApp* dialogs;
     TextInput* text_input;
     VariableItemList* variable_item_list;
@@ -40,8 +40,9 @@ struct MassStorageApp {
     FuriMutex* usb_mutex;
     MassStorageUsb* usb;
 
-    char new_file_name[MASS_STORAGE_FILE_NAME_LEN + 1];
-    uint32_t new_file_size;
+    uint64_t create_image_max;
+    uint8_t create_image_size;
+    char create_image_name[MASS_STORAGE_FILE_NAME_LEN];
 
     uint32_t bytes_read, bytes_written;
 };
@@ -51,7 +52,7 @@ typedef enum {
     MassStorageAppViewTextInput,
     MassStorageAppViewWork,
     MassStorageAppViewLoading,
-    MassStorageAppViewWidget,
+    MassStorageAppViewPopup,
 } MassStorageAppView;
 
 enum MassStorageCustomEvent {
@@ -59,9 +60,6 @@ enum MassStorageCustomEvent {
     MassStorageCustomEventReserved = 100,
 
     MassStorageCustomEventEject,
-    MassStorageCustomEventFileSelect,
-    MassStorageCustomEventNewImage,
-    MassStorageCustomEventNameInput,
 };
 
 void mass_storage_app_show_loading_popup(MassStorageApp* app, bool show);

+ 2 - 2
mass_storage/scenes/mass_storage_scene_config.h

@@ -1,5 +1,5 @@
 ADD_SCENE(mass_storage, start, Start)
 ADD_SCENE(mass_storage, file_select, FileSelect)
 ADD_SCENE(mass_storage, work, Work)
-ADD_SCENE(mass_storage, file_name, FileName)
-ADD_SCENE(mass_storage, usb_locked, UsbLocked)
+ADD_SCENE(mass_storage, create_image, CreateImage)
+ADD_SCENE(mass_storage, create_image_name, CreateImageName)

+ 187 - 0
mass_storage/scenes/mass_storage_scene_create_image.c

@@ -0,0 +1,187 @@
+#include "../mass_storage_app_i.h"
+#include <lib/toolbox/value_index.h>
+
+enum VarItemListIndex {
+    VarItemListIndexImageSize,
+    VarItemListIndexImageName,
+    VarItemListIndexCreateImage,
+};
+
+void mass_storage_scene_create_image_variable_item_list_callback(void* context, uint32_t index) {
+    MassStorageApp* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+static const struct {
+    char* name;
+    uint64_t value;
+} image_sizes[] = {
+    {"1MB", 1LL * 1024 * 1024},
+    {"2MB", 2LL * 1024 * 1024},
+    {"4MB", 4LL * 1024 * 1024},
+    {"8MB", 8LL * 1024 * 1024},
+    {"16MB", 16LL * 1024 * 1024},
+    {"32MB", 32LL * 1024 * 1024},
+    {"64MB", 64LL * 1024 * 1024},
+    {"128MB", 128LL * 1024 * 1024},
+    {"256MB", 256LL * 1024 * 1024},
+    {"512MB", 512LL * 1024 * 1024},
+    {"1GB", 1LL * 1024 * 1024 * 1024},
+    {"2GB", 2LL * 1024 * 1024 * 1024},
+    {"4GB", 4LL * 1024 * 1024 * 1024},
+    {"8GB", 8LL * 1024 * 1024 * 1024},
+    {"16GB", 16LL * 1024 * 1024 * 1024},
+    {"32GB", 32LL * 1024 * 1024 * 1024},
+    {"64GB", 64LL * 1024 * 1024 * 1024},
+    {"128GB", 128LL * 1024 * 1024 * 1024},
+    {"256GB", 256LL * 1024 * 1024 * 1024},
+    {"512GB", 512LL * 1024 * 1024 * 1024},
+};
+static void mass_storage_scene_create_image_image_size_changed(VariableItem* item) {
+    MassStorageApp* app = variable_item_get_context(item);
+    app->create_image_size = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, image_sizes[app->create_image_size].name);
+}
+
+void mass_storage_scene_create_image_on_enter(void* context) {
+    MassStorageApp* app = context;
+    VariableItemList* variable_item_list = app->variable_item_list;
+    VariableItem* item;
+
+    uint8_t size_count = COUNT_OF(image_sizes);
+    if(app->create_image_max) {
+        for(size_t i = 1; i < size_count; i++) {
+            if(image_sizes[i].value > app->create_image_max) {
+                size_count = i;
+                break;
+            }
+        }
+    }
+    if(app->create_image_size == (uint8_t)-1) {
+        app->create_image_size = CLAMP(7, size_count - 2, 0); // 7 = 128MB
+    }
+    item = variable_item_list_add(
+        variable_item_list,
+        "Image Size",
+        size_count,
+        mass_storage_scene_create_image_image_size_changed,
+        app);
+    variable_item_set_current_value_index(item, app->create_image_size);
+    variable_item_set_current_value_text(item, image_sizes[app->create_image_size].name);
+
+    item = variable_item_list_add(variable_item_list, "Image Name", 0, NULL, app);
+    variable_item_set_current_value_text(item, app->create_image_name);
+
+    variable_item_list_add(variable_item_list, "Create Image", 0, NULL, app);
+
+    variable_item_list_set_enter_callback(
+        variable_item_list, mass_storage_scene_create_image_variable_item_list_callback, app);
+
+    variable_item_list_set_header(variable_item_list, "Create Disk Image");
+
+    variable_item_list_set_selected_item(
+        variable_item_list,
+        scene_manager_get_scene_state(app->scene_manager, MassStorageSceneCreateImage));
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewStart);
+}
+
+static void popup_callback_ok(void* context) {
+    MassStorageApp* app = context;
+    scene_manager_set_scene_state(
+        app->scene_manager, MassStorageSceneStart, MassStorageSceneFileSelect);
+    scene_manager_previous_scene(app->scene_manager);
+    scene_manager_next_scene(app->scene_manager, MassStorageSceneFileSelect);
+}
+
+static void popup_callback_error(void* context) {
+    MassStorageApp* app = context;
+    view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewStart);
+}
+
+bool mass_storage_scene_create_image_on_event(void* context, SceneManagerEvent event) {
+    MassStorageApp* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        scene_manager_set_scene_state(
+            app->scene_manager, MassStorageSceneCreateImage, event.event);
+        consumed = true;
+        switch(event.event) {
+        case VarItemListIndexImageName:
+            scene_manager_next_scene(app->scene_manager, MassStorageSceneCreateImageName);
+            break;
+        case VarItemListIndexCreateImage: {
+            mass_storage_app_show_loading_popup(app, true);
+            const char* name = strnlen(app->create_image_name, sizeof(app->create_image_name)) ?
+                                   app->create_image_name :
+                                   image_sizes[app->create_image_size].name;
+            furi_string_printf(
+                app->file_path,
+                "%s/%s%s",
+                MASS_STORAGE_APP_PATH_FOLDER,
+                name,
+                MASS_STORAGE_APP_EXTENSION);
+
+            app->file = storage_file_alloc(app->fs_api);
+            const char* error = NULL;
+            bool success = false;
+
+            size_t wipe_4k = 4096;
+            uint8_t* buffer = malloc(wipe_4k);
+            do {
+                if(!storage_file_open(
+                       app->file,
+                       furi_string_get_cstr(app->file_path),
+                       FSAM_WRITE,
+                       FSOM_CREATE_NEW))
+                    break;
+
+                uint64_t size = image_sizes[app->create_image_size].value;
+                if(size == app->create_image_max) size--;
+                if(!storage_file_expand(app->file, size)) break;
+
+                // Zero out first 4k - partition table and adjacent data
+                if(!storage_file_seek(app->file, 0, true)) break;
+                if(!storage_file_write(app->file, buffer, wipe_4k)) break;
+
+                success = true;
+            } while(false);
+            free(buffer);
+
+            if(!success) {
+                error = storage_file_get_error_desc(app->file);
+                storage_file_close(app->file);
+                storage_common_remove(app->fs_api, furi_string_get_cstr(app->file_path));
+            }
+            storage_file_free(app->file);
+            mass_storage_app_show_loading_popup(app, false);
+
+            if(error) {
+                popup_set_header(
+                    app->popup, "Error Creating Image!", 64, 26, AlignCenter, AlignCenter);
+                popup_set_text(app->popup, error, 64, 40, AlignCenter, AlignCenter);
+                popup_set_callback(app->popup, popup_callback_error);
+            } else {
+                popup_set_header(app->popup, "Image Created!", 64, 32, AlignCenter, AlignCenter);
+                popup_set_text(app->popup, "", 0, 0, AlignLeft, AlignBottom);
+                popup_set_callback(app->popup, popup_callback_ok);
+            }
+            popup_set_context(app->popup, app);
+            popup_set_timeout(app->popup, 0);
+            popup_disable_timeout(app->popup);
+            view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewPopup);
+            break;
+        }
+        default:
+            break;
+        }
+    }
+
+    return consumed;
+}
+
+void mass_storage_scene_create_image_on_exit(void* context) {
+    MassStorageApp* app = context;
+    variable_item_list_reset(app->variable_item_list);
+}

+ 52 - 0
mass_storage/scenes/mass_storage_scene_create_image_name.c

@@ -0,0 +1,52 @@
+#include "../mass_storage_app_i.h"
+
+enum TextInputIndex {
+    TextInputResultOk,
+};
+
+static void mass_storage_scene_create_image_name_text_input_callback(void* context) {
+    MassStorageApp* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk);
+}
+
+void mass_storage_scene_create_image_name_on_enter(void* context) {
+    MassStorageApp* app = context;
+    TextInput* text_input = app->text_input;
+
+    text_input_set_header_text(text_input, "Image name, empty = default");
+
+    text_input_set_minimum_length(text_input, 0);
+
+    text_input_set_result_callback(
+        text_input,
+        mass_storage_scene_create_image_name_text_input_callback,
+        app,
+        app->create_image_name,
+        sizeof(app->create_image_name),
+        false);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewTextInput);
+}
+
+bool mass_storage_scene_create_image_name_on_event(void* context, SceneManagerEvent event) {
+    MassStorageApp* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        switch(event.event) {
+        case TextInputResultOk:
+            scene_manager_previous_scene(app->scene_manager);
+            break;
+        default:
+            break;
+        }
+    }
+
+    return consumed;
+}
+
+void mass_storage_scene_create_image_name_on_exit(void* context) {
+    MassStorageApp* app = context;
+    text_input_reset(app->text_input);
+}

+ 0 - 87
mass_storage/scenes/mass_storage_scene_file_name.c

@@ -1,87 +0,0 @@
-#include "../mass_storage_app_i.h"
-
-#define WRITE_BUF_LEN 4096
-
-static void mass_storage_file_name_text_callback(void* context) {
-    furi_assert(context);
-
-    MassStorageApp* app = context;
-    view_dispatcher_send_custom_event(app->view_dispatcher, MassStorageCustomEventNameInput);
-}
-
-static bool mass_storage_create_image(Storage* storage, const char* file_path, uint32_t size) {
-    FURI_LOG_I("TAG", "Creating image %s, len:%lu", file_path, size);
-    File* file = storage_file_alloc(storage);
-
-    bool success = false;
-    uint8_t* buffer = malloc(WRITE_BUF_LEN);
-    do {
-        if(!storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) break;
-        if(!storage_file_seek(file, size, true)) break;
-        if(!storage_file_seek(file, 0, true)) break;
-        // Zero out first 4k - partition table and adjacent data
-        if(!storage_file_write(file, buffer, WRITE_BUF_LEN)) break;
-
-        success = true;
-    } while(false);
-
-    free(buffer);
-    storage_file_close(file);
-    storage_file_free(file);
-    return success;
-}
-
-void mass_storage_scene_file_name_on_enter(void* context) {
-    MassStorageApp* app = context;
-
-    text_input_set_header_text(app->text_input, "Enter image name");
-    ValidatorIsFile* validator_is_file =
-        validator_is_file_alloc_init(MASS_STORAGE_APP_PATH_FOLDER, MASS_STORAGE_APP_EXTENSION, "");
-    text_input_set_validator(app->text_input, validator_is_file_callback, validator_is_file);
-
-    text_input_set_result_callback(
-        app->text_input,
-        mass_storage_file_name_text_callback,
-        app,
-        app->new_file_name,
-        MASS_STORAGE_FILE_NAME_LEN,
-        true);
-    view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewTextInput);
-}
-
-bool mass_storage_scene_file_name_on_event(void* context, SceneManagerEvent event) {
-    UNUSED(event);
-    MassStorageApp* app = context;
-
-    bool consumed = false;
-
-    if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == MassStorageCustomEventNameInput) {
-            mass_storage_app_show_loading_popup(app, true);
-            furi_string_printf(
-                app->file_path,
-                "%s/%s%s",
-                MASS_STORAGE_APP_PATH_FOLDER,
-                app->new_file_name,
-                MASS_STORAGE_APP_EXTENSION);
-            if(mass_storage_create_image(
-                   app->fs_api, furi_string_get_cstr(app->file_path), app->new_file_size)) {
-                if(!furi_hal_usb_is_locked()) {
-                    scene_manager_next_scene(app->scene_manager, MassStorageSceneWork);
-                } else {
-                    scene_manager_next_scene(app->scene_manager, MassStorageSceneUsbLocked);
-                }
-            } // TODO: error message screen
-        }
-    }
-    return consumed;
-}
-
-void mass_storage_scene_file_name_on_exit(void* context) {
-    UNUSED(context);
-    MassStorageApp* app = context;
-    void* validator_context = text_input_get_validator_callback_context(app->text_input);
-    text_input_set_validator(app->text_input, NULL, NULL);
-    validator_is_file_free(validator_context);
-    text_input_reset(app->text_input);
-}

+ 2 - 7
mass_storage/scenes/mass_storage_scene_file_select.c

@@ -5,8 +5,7 @@ static bool mass_storage_file_select(MassStorageApp* mass_storage) {
     furi_assert(mass_storage);
 
     DialogsFileBrowserOptions browser_options;
-    dialog_file_browser_set_basic_options(
-        &browser_options, MASS_STORAGE_APP_EXTENSION, &I_mass_storage_10px);
+    dialog_file_browser_set_basic_options(&browser_options, "*", &I_mass_storage_10px);
     browser_options.base_path = MASS_STORAGE_APP_PATH_FOLDER;
     browser_options.hide_ext = false;
 
@@ -20,11 +19,7 @@ void mass_storage_scene_file_select_on_enter(void* context) {
     MassStorageApp* mass_storage = context;
 
     if(mass_storage_file_select(mass_storage)) {
-        if(!furi_hal_usb_is_locked()) {
-            scene_manager_next_scene(mass_storage->scene_manager, MassStorageSceneWork);
-        } else {
-            scene_manager_next_scene(mass_storage->scene_manager, MassStorageSceneUsbLocked);
-        }
+        scene_manager_next_scene(mass_storage->scene_manager, MassStorageSceneWork);
     } else {
         scene_manager_previous_scene(mass_storage->scene_manager);
     }

+ 32 - 42
mass_storage/scenes/mass_storage_scene_start.c

@@ -1,54 +1,32 @@
 #include "../mass_storage_app_i.h"
 
-static const struct {
-    char* name;
-    uint32_t value;
-} image_size[] = {
-    {"1.44M", 1440 * 1024},
-    {"2M", 2 * 1024 * 1024},
-    {"4M", 4 * 1024 * 1024},
-    {"8M", 8 * 1024 * 1024},
-    {"16M", 16 * 1024 * 1024},
-    {"32M", 32 * 1024 * 1024},
-    {"64M", 64 * 1024 * 1024},
-    {"128M", 128 * 1024 * 1024},
-    {"256M", 256 * 1024 * 1024},
-    {"512M", 512 * 1024 * 1024},
-    {"700M", 700 * 1024 * 1024},
-    {"1G", 1024 * 1024 * 1024},
-    {"2G", 2u * 1024 * 1024 * 1024},
+enum VarItemListIndex {
+    VarItemListIndexSelectDiskImage,
+    VarItemListIndexCreateDiskImage,
 };
 
-static void mass_storage_item_select(void* context, uint32_t index) {
+static void mass_storage_scene_start_variable_item_list_callback(void* context, uint32_t index) {
     MassStorageApp* app = context;
-    if(index == 0) {
-        view_dispatcher_send_custom_event(app->view_dispatcher, MassStorageCustomEventFileSelect);
-    } else {
-        view_dispatcher_send_custom_event(app->view_dispatcher, MassStorageCustomEventNewImage);
-    }
-}
-
-static void mass_storage_image_size(VariableItem* item) {
-    MassStorageApp* app = variable_item_get_context(item);
-    uint8_t index = variable_item_get_current_value_index(item);
-    variable_item_set_current_value_text(item, image_size[index].name);
-    app->new_file_size = image_size[index].value;
+    view_dispatcher_send_custom_event(app->view_dispatcher, index);
 }
 
 void mass_storage_scene_start_on_enter(void* context) {
     MassStorageApp* app = context;
+    VariableItemList* variable_item_list = app->variable_item_list;
+    VariableItem* item;
+
+    variable_item_list_add(variable_item_list, "Select Disk Image", 0, NULL, app);
 
-    VariableItem* item =
-        variable_item_list_add(app->variable_item_list, "Select disk image", 0, NULL, NULL);
+    variable_item_list_add(variable_item_list, "Create Disk Image", 0, NULL, app);
 
-    item = variable_item_list_add(
-        app->variable_item_list, "New image", COUNT_OF(image_size), mass_storage_image_size, app);
+    variable_item_list_set_enter_callback(
+        variable_item_list, mass_storage_scene_create_image_variable_item_list_callback, app);
 
-    variable_item_list_set_enter_callback(app->variable_item_list, mass_storage_item_select, app);
+    variable_item_list_set_header(variable_item_list, "USB Mass Storage");
+
+    variable_item_list_set_selected_item(
+        variable_item_list, scene_manager_get_scene_state(app->scene_manager, MassStorageSceneStart));
 
-    variable_item_set_current_value_index(item, 2);
-    variable_item_set_current_value_text(item, image_size[2].name);
-    app->new_file_size = image_size[2].value;
     view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewStart);
 }
 
@@ -57,14 +35,26 @@ bool mass_storage_scene_start_on_event(void* context, SceneManagerEvent event) {
     UNUSED(event);
     MassStorageApp* app = context;
 
+    bool consumed = false;
+
     if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == MassStorageCustomEventFileSelect) {
+        scene_manager_set_scene_state(
+            app->scene_manager, MassStorageSceneStart, event.event);
+        consumed = true;
+        switch(event.event) {
+        case VarItemListIndexSelectDiskImage:
             scene_manager_next_scene(app->scene_manager, MassStorageSceneFileSelect);
-        } else if(event.event == MassStorageCustomEventNewImage) {
-            scene_manager_next_scene(app->scene_manager, MassStorageSceneFileName);
+            break;
+        case VarItemListIndexCreateDiskImage:
+            scene_manager_set_scene_state(app->scene_manager, MassStorageSceneCreateImage, 0);
+            scene_manager_next_scene(app->scene_manager, MassStorageSceneCreateImage);
+            break;
+        default:
+            break;
         }
     }
-    return false;
+
+    return consumed;
 }
 
 void mass_storage_scene_start_on_exit(void* context) {

+ 0 - 40
mass_storage/scenes/mass_storage_scene_usb_locked.c

@@ -1,40 +0,0 @@
-#include "../mass_storage_app_i.h"
-
-void mass_storage_scene_usb_locked_on_enter(void* context) {
-    MassStorageApp* app = context;
-
-    widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64);
-    widget_add_string_multiline_element(
-        app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!");
-    widget_add_string_multiline_element(
-        app->widget,
-        3,
-        30,
-        AlignLeft,
-        AlignTop,
-        FontSecondary,
-        "Disconnect from\nPC or phone to\nuse this function.");
-
-    view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewWidget);
-}
-
-bool mass_storage_scene_usb_locked_on_event(void* context, SceneManagerEvent event) {
-    MassStorageApp* app = context;
-    bool consumed = false;
-
-    if(event.type == SceneManagerEventTypeBack) {
-        consumed = scene_manager_search_and_switch_to_previous_scene(
-            app->scene_manager, MassStorageSceneFileSelect);
-        if(!consumed) {
-            consumed = scene_manager_search_and_switch_to_previous_scene(
-                app->scene_manager, MassStorageSceneStart);
-        }
-    }
-
-    return consumed;
-}
-
-void mass_storage_scene_usb_locked_on_exit(void* context) {
-    MassStorageApp* app = context;
-    widget_reset(app->widget);
-}

+ 10 - 10
mass_storage/views/mass_storage_view.c

@@ -19,9 +19,9 @@ static void append_suffixed_byte_count(FuriString* string, uint32_t count) {
     } else if(count < 1024 * 1024) {
         furi_string_cat_printf(string, "%luK", count / 1024);
     } else if(count < 1024 * 1024 * 1024) {
-        furi_string_cat_printf(string, "%.3fM", (double)count / (1024 * 1024));
+        furi_string_cat_printf(string, "%.1fM", (double)count / (1024 * 1024));
     } else {
-        furi_string_cat_printf(string, "%.3fG", (double)count / (1024 * 1024 * 1024));
+        furi_string_cat_printf(string, "%.1fG", (double)count / (1024 * 1024 * 1024));
     }
 }
 
@@ -34,28 +34,28 @@ static void mass_storage_draw_callback(Canvas* canvas, void* _model) {
     canvas_draw_str_aligned(
         canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "USB Mass Storage");
 
-    canvas_set_font(canvas, FontSecondary);
+    canvas_set_font(canvas, FontBatteryPercent);
     elements_string_fit_width(canvas, model->file_name, 89 - 2);
     canvas_draw_str_aligned(
-        canvas, 50, 23, AlignCenter, AlignBottom, furi_string_get_cstr(model->file_name));
+        canvas, 92, 24, AlignRight, AlignBottom, furi_string_get_cstr(model->file_name));
 
     furi_string_set_str(model->status_string, "R:");
     append_suffixed_byte_count(model->status_string, model->bytes_read);
     if(model->read_speed) {
-        furi_string_cat_str(model->status_string, "; ");
+        furi_string_cat_str(model->status_string, "/");
         append_suffixed_byte_count(model->status_string, model->read_speed);
-        furi_string_cat_str(model->status_string, "ps");
+        furi_string_cat_str(model->status_string, "s");
     }
-    canvas_draw_str(canvas, 12, 34, furi_string_get_cstr(model->status_string));
+    canvas_draw_str(canvas, 14, 34, furi_string_get_cstr(model->status_string));
 
     furi_string_set_str(model->status_string, "W:");
     append_suffixed_byte_count(model->status_string, model->bytes_written);
     if(model->write_speed) {
-        furi_string_cat_str(model->status_string, "; ");
+        furi_string_cat_str(model->status_string, "/");
         append_suffixed_byte_count(model->status_string, model->write_speed);
-        furi_string_cat_str(model->status_string, "ps");
+        furi_string_cat_str(model->status_string, "s");
     }
-    canvas_draw_str(canvas, 12, 44, furi_string_get_cstr(model->status_string));
+    canvas_draw_str(canvas, 14, 43, furi_string_get_cstr(model->status_string));
 }
 
 MassStorage* mass_storage_alloc() {

+ 1 - 1
music_player/application.fam

@@ -10,7 +10,7 @@ App(
     stack_size=2 * 1024,
     targets=["f7"],
     fap_version="1.1",
-    fap_icon="icons/music_10px.png",
+    fap_icon="music_10px.png",
     fap_category="Media",
     fap_description="An app to play RTTL music files",
     fap_icon_assets="icons",

+ 0 - 0
music_player/icons/music_10px.png → music_player/music_10px.png


+ 0 - 0
nfc_magic/assets/125_10px.png → nfc_magic/125_10px.png


+ 1 - 1
nfc_magic/application.fam

@@ -11,7 +11,7 @@ App(
     stack_size=4 * 1024,
     fap_description="Application for writing to NFC tags with modifiable sector 0",
     fap_version="1.0",
-    fap_icon="assets/125_10px.png",
+    fap_icon="125_10px.png",
     fap_category="NFC",
     fap_private_libs=[
         Lib(

BIN
nfc_magic/assets/DolphinCommon_56x48.png


BIN
nfc_magic/assets/DolphinNice_96x59.png


BIN
nfc_magic/assets/NFC_manual_60x50.png


BIN
nfc_rfid_detector/images/Modern_reader_18x34.png


BIN
nfc_rfid_detector/images/Move_flipper_26x39.png


BIN
picopass/icons/DolphinMafia_115x62.png


BIN
picopass/icons/DolphinNice_96x59.png


BIN
picopass/icons/Nfc_10px.png


BIN
picopass/icons/RFIDDolphinReceive_97x61.png


BIN
picopass/icons/RFIDDolphinSend_97x61.png


BIN
spi_mem_manager/images/DolphinMafia_115x62.png


BIN
spi_mem_manager/images/DolphinNice_96x59.png


BIN
spi_mem_manager/images/SDQuestion_35x43.png