Browse Source

[FL-2882] BLE tiktok controller (#1859)

* bt hid: introduce tiktok controller
* assets: add bt tiktok assets
* bt hid: finish tiktok draw
* bt hid: add input process to tiktok view
* bt hid: add tiktok swipe emulation
* bt hid: fix exit from tiktok controller
* ble hid: add delay to emulate double tap
* ble hid: change options order
* bt hid: build as external application
* ble hid: fix naming

Co-authored-by: あく <alleteam@gmail.com>
gornekich 3 years ago
parent
commit
afff1adf8f
25 changed files with 250 additions and 9 deletions
  1. 0 1
      applications/plugins/application.fam
  2. 3 8
      applications/plugins/bt_hid_app/application.fam
  3. BIN
      applications/plugins/bt_hid_app/assets/Arr_dwn_7x9.png
  4. BIN
      applications/plugins/bt_hid_app/assets/Arr_up_7x9.png
  5. BIN
      applications/plugins/bt_hid_app/assets/Ble_connected_15x15.png
  6. BIN
      applications/plugins/bt_hid_app/assets/Ble_disconnected_15x15.png
  7. BIN
      applications/plugins/bt_hid_app/assets/Button_18x18.png
  8. BIN
      applications/plugins/bt_hid_app/assets/Circles_47x47.png
  9. BIN
      applications/plugins/bt_hid_app/assets/Left_mouse_icon_9x9.png
  10. BIN
      applications/plugins/bt_hid_app/assets/Like_def_11x9.png
  11. BIN
      applications/plugins/bt_hid_app/assets/Like_pressed_17x17.png
  12. BIN
      applications/plugins/bt_hid_app/assets/Ok_btn_pressed_13x13.png
  13. BIN
      applications/plugins/bt_hid_app/assets/Pressed_Button_13x13.png
  14. BIN
      applications/plugins/bt_hid_app/assets/Right_mouse_icon_9x9.png
  15. BIN
      applications/plugins/bt_hid_app/assets/Space_65x18.png
  16. BIN
      applications/plugins/bt_hid_app/assets/Voldwn_6x6.png
  17. BIN
      applications/plugins/bt_hid_app/assets/Volup_8x6.png
  18. 16 0
      applications/plugins/bt_hid_app/bt_hid.c
  19. 3 0
      applications/plugins/bt_hid_app/bt_hid.h
  20. 2 0
      applications/plugins/bt_hid_app/views/bt_hid_keyboard.c
  21. 2 0
      applications/plugins/bt_hid_app/views/bt_hid_keynote.c
  22. 2 0
      applications/plugins/bt_hid_app/views/bt_hid_media.c
  23. 2 0
      applications/plugins/bt_hid_app/views/bt_hid_mouse.c
  24. 207 0
      applications/plugins/bt_hid_app/views/bt_hid_tiktok.c
  25. 13 0
      applications/plugins/bt_hid_app/views/bt_hid_tiktok.h

+ 0 - 1
applications/plugins/application.fam

@@ -5,6 +5,5 @@ App(
     provides=[
         "music_player",
         "snake_game",
-        "bt_hid",
     ],
 )

+ 3 - 8
applications/plugins/bt_hid_app/application.fam

@@ -1,15 +1,10 @@
 App(
     appid="bt_hid",
     name="Bluetooth Remote",
-    apptype=FlipperAppType.PLUGIN,
+    apptype=FlipperAppType.EXTERNAL,
     entry_point="bt_hid_app",
     stack_size=1 * 1024,
-    cdefines=["APP_BLE_HID"],
-    requires=[
-        "bt",
-        "gui",
-    ],
-    order=10,
-    fap_icon="bt_remote_10px.png",
     fap_category="Tools",
+    fap_icon="bt_remote_10px.png",
+    fap_icon_assets="assets",
 )

BIN
applications/plugins/bt_hid_app/assets/Arr_dwn_7x9.png


BIN
applications/plugins/bt_hid_app/assets/Arr_up_7x9.png


BIN
applications/plugins/bt_hid_app/assets/Ble_connected_15x15.png


BIN
applications/plugins/bt_hid_app/assets/Ble_disconnected_15x15.png


BIN
applications/plugins/bt_hid_app/assets/Button_18x18.png


BIN
applications/plugins/bt_hid_app/assets/Circles_47x47.png


BIN
applications/plugins/bt_hid_app/assets/Left_mouse_icon_9x9.png


BIN
applications/plugins/bt_hid_app/assets/Like_def_11x9.png


BIN
applications/plugins/bt_hid_app/assets/Like_pressed_17x17.png


BIN
applications/plugins/bt_hid_app/assets/Ok_btn_pressed_13x13.png


BIN
applications/plugins/bt_hid_app/assets/Pressed_Button_13x13.png


BIN
applications/plugins/bt_hid_app/assets/Right_mouse_icon_9x9.png


BIN
applications/plugins/bt_hid_app/assets/Space_65x18.png


BIN
applications/plugins/bt_hid_app/assets/Voldwn_6x6.png


BIN
applications/plugins/bt_hid_app/assets/Volup_8x6.png


+ 16 - 0
applications/plugins/bt_hid_app/bt_hid.c

@@ -9,6 +9,7 @@ enum BtDebugSubmenuIndex {
     BtHidSubmenuIndexKeynote,
     BtHidSubmenuIndexKeyboard,
     BtHidSubmenuIndexMedia,
+    BtHidSubmenuIndexTikTok,
     BtHidSubmenuIndexMouse,
 };
 
@@ -27,6 +28,9 @@ void bt_hid_submenu_callback(void* context, uint32_t index) {
     } else if(index == BtHidSubmenuIndexMouse) {
         app->view_id = BtHidViewMouse;
         view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewMouse);
+    } else if(index == BtHidSubmenuIndexTikTok) {
+        app->view_id = BtHidViewTikTok;
+        view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok);
     }
 }
 
@@ -65,6 +69,7 @@ void bt_hid_connection_status_changed_callback(BtStatus status, void* context) {
     bt_hid_keyboard_set_connected_status(bt_hid->bt_hid_keyboard, connected);
     bt_hid_media_set_connected_status(bt_hid->bt_hid_media, connected);
     bt_hid_mouse_set_connected_status(bt_hid->bt_hid_mouse, connected);
+    bt_hid_tiktok_set_connected_status(bt_hid->bt_hid_tiktok, connected);
 }
 
 BtHid* bt_hid_app_alloc() {
@@ -91,6 +96,8 @@ BtHid* bt_hid_app_alloc() {
     submenu_add_item(
         app->submenu, "Keyboard", BtHidSubmenuIndexKeyboard, bt_hid_submenu_callback, app);
     submenu_add_item(app->submenu, "Media", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app);
+    submenu_add_item(
+        app->submenu, "TikTok Controller", BtHidSubmenuIndexTikTok, bt_hid_submenu_callback, app);
     submenu_add_item(app->submenu, "Mouse", BtHidSubmenuIndexMouse, bt_hid_submenu_callback, app);
     view_set_previous_callback(submenu_get_view(app->submenu), bt_hid_exit);
     view_dispatcher_add_view(
@@ -127,6 +134,13 @@ BtHid* bt_hid_app_alloc() {
     view_dispatcher_add_view(
         app->view_dispatcher, BtHidViewMedia, bt_hid_media_get_view(app->bt_hid_media));
 
+    // TikTok view
+    app->bt_hid_tiktok = bt_hid_tiktok_alloc();
+    view_set_previous_callback(
+        bt_hid_tiktok_get_view(app->bt_hid_tiktok), bt_hid_exit_confirm_view);
+    view_dispatcher_add_view(
+        app->view_dispatcher, BtHidViewTikTok, bt_hid_tiktok_get_view(app->bt_hid_tiktok));
+
     // Mouse view
     app->bt_hid_mouse = bt_hid_mouse_alloc();
     view_set_previous_callback(bt_hid_mouse_get_view(app->bt_hid_mouse), bt_hid_exit_confirm_view);
@@ -159,6 +173,8 @@ void bt_hid_app_free(BtHid* app) {
     bt_hid_media_free(app->bt_hid_media);
     view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMouse);
     bt_hid_mouse_free(app->bt_hid_mouse);
+    view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok);
+    bt_hid_tiktok_free(app->bt_hid_tiktok);
     view_dispatcher_free(app->view_dispatcher);
 
     // Close records

+ 3 - 0
applications/plugins/bt_hid_app/bt_hid.h

@@ -13,6 +13,7 @@
 #include "views/bt_hid_keyboard.h"
 #include "views/bt_hid_media.h"
 #include "views/bt_hid_mouse.h"
+#include "views/bt_hid_tiktok.h"
 
 typedef struct {
     Bt* bt;
@@ -25,6 +26,7 @@ typedef struct {
     BtHidKeyboard* bt_hid_keyboard;
     BtHidMedia* bt_hid_media;
     BtHidMouse* bt_hid_mouse;
+    BtHidTikTok* bt_hid_tiktok;
     uint32_t view_id;
 } BtHid;
 
@@ -34,5 +36,6 @@ typedef enum {
     BtHidViewKeyboard,
     BtHidViewMedia,
     BtHidViewMouse,
+    BtHidViewTikTok,
     BtHidViewExitConfirm,
 } BtHidView;

+ 2 - 0
applications/plugins/bt_hid_app/views/bt_hid_keyboard.c

@@ -5,6 +5,8 @@
 #include <gui/elements.h>
 #include <gui/icon_i.h>
 
+#include "bt_hid_icons.h"
+
 struct BtHidKeyboard {
     View* view;
 };

+ 2 - 0
applications/plugins/bt_hid_app/views/bt_hid_keynote.c

@@ -4,6 +4,8 @@
 #include <furi_hal_usb_hid.h>
 #include <gui/elements.h>
 
+#include "bt_hid_icons.h"
+
 struct BtHidKeynote {
     View* view;
 };

+ 2 - 0
applications/plugins/bt_hid_app/views/bt_hid_media.c

@@ -4,6 +4,8 @@
 #include <furi_hal_usb_hid.h>
 #include <gui/elements.h>
 
+#include "bt_hid_icons.h"
+
 struct BtHidMedia {
     View* view;
 };

+ 2 - 0
applications/plugins/bt_hid_app/views/bt_hid_mouse.c

@@ -4,6 +4,8 @@
 #include <furi_hal_usb_hid.h>
 #include <gui/elements.h>
 
+#include "bt_hid_icons.h"
+
 struct BtHidMouse {
     View* view;
 };

+ 207 - 0
applications/plugins/bt_hid_app/views/bt_hid_tiktok.c

@@ -0,0 +1,207 @@
+#include "bt_hid_tiktok.h"
+#include <furi.h>
+#include <furi_hal_bt_hid.h>
+#include <furi_hal_usb_hid.h>
+#include <gui/elements.h>
+
+#include "bt_hid_icons.h"
+
+struct BtHidTikTok {
+    View* view;
+};
+
+typedef struct {
+    bool left_pressed;
+    bool up_pressed;
+    bool right_pressed;
+    bool down_pressed;
+    bool ok_pressed;
+    bool connected;
+} BtHidTikTokModel;
+
+static void bt_hid_tiktok_draw_callback(Canvas* canvas, void* context) {
+    furi_assert(context);
+    BtHidTikTokModel* model = context;
+
+    // Header
+    if(model->connected) {
+        canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
+    } else {
+        canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
+    }
+    canvas_set_font(canvas, FontPrimary);
+    elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "TikTok");
+    canvas_set_font(canvas, FontSecondary);
+
+    // Keypad circles
+    canvas_draw_icon(canvas, 76, 8, &I_Circles_47x47);
+
+    // Up
+    if(model->up_pressed) {
+        canvas_set_bitmap_mode(canvas, 1);
+        canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13);
+        canvas_set_bitmap_mode(canvas, 0);
+        canvas_set_color(canvas, ColorWhite);
+    }
+    canvas_draw_icon(canvas, 96, 11, &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_set_bitmap_mode(canvas, 0);
+        canvas_set_color(canvas, ColorWhite);
+    }
+    canvas_draw_icon(canvas, 96, 44, &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_set_bitmap_mode(canvas, 0);
+        canvas_set_color(canvas, ColorWhite);
+    }
+    canvas_draw_icon(canvas, 81, 29, &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_set_bitmap_mode(canvas, 0);
+        canvas_set_color(canvas, ColorWhite);
+    }
+    canvas_draw_icon(canvas, 111, 29, &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);
+    }
+    // Exit
+    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");
+}
+
+static void bt_hid_tiktok_process_press(BtHidTikTok* bt_hid_tiktok, InputEvent* event) {
+    with_view_model(
+        bt_hid_tiktok->view,
+        BtHidTikTokModel * model,
+        {
+            if(event->key == InputKeyUp) {
+                model->up_pressed = true;
+            } else if(event->key == InputKeyDown) {
+                model->down_pressed = true;
+            } else if(event->key == InputKeyLeft) {
+                model->left_pressed = true;
+                furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_DECREMENT);
+            } else if(event->key == InputKeyRight) {
+                model->right_pressed = true;
+                furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT);
+            } else if(event->key == InputKeyOk) {
+                model->ok_pressed = true;
+            }
+        },
+        true);
+}
+
+static void bt_hid_tiktok_process_release(BtHidTikTok* bt_hid_tiktok, InputEvent* event) {
+    with_view_model(
+        bt_hid_tiktok->view,
+        BtHidTikTokModel * model,
+        {
+            if(event->key == InputKeyUp) {
+                model->up_pressed = false;
+            } else if(event->key == InputKeyDown) {
+                model->down_pressed = false;
+            } else if(event->key == InputKeyLeft) {
+                model->left_pressed = false;
+                furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_DECREMENT);
+            } else if(event->key == InputKeyRight) {
+                model->right_pressed = false;
+                furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT);
+            } else if(event->key == InputKeyOk) {
+                model->ok_pressed = false;
+            }
+        },
+        true);
+}
+
+static bool bt_hid_tiktok_input_callback(InputEvent* event, void* context) {
+    furi_assert(context);
+    BtHidTikTok* bt_hid_tiktok = context;
+    bool consumed = false;
+
+    if(event->type == InputTypePress) {
+        bt_hid_tiktok_process_press(bt_hid_tiktok, event);
+        consumed = true;
+    } else if(event->type == InputTypeRelease) {
+        bt_hid_tiktok_process_release(bt_hid_tiktok, event);
+        consumed = true;
+    } else if(event->type == InputTypeShort) {
+        if(event->key == InputKeyOk) {
+            furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT);
+            furi_delay_ms(50);
+            furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT);
+            furi_delay_ms(50);
+            furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT);
+            furi_delay_ms(50);
+            furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT);
+            consumed = true;
+        } else if(event->key == InputKeyUp) {
+            // Emulate up swipe
+            furi_hal_bt_hid_mouse_scroll(-6);
+            furi_hal_bt_hid_mouse_scroll(-12);
+            furi_hal_bt_hid_mouse_scroll(-19);
+            furi_hal_bt_hid_mouse_scroll(-12);
+            furi_hal_bt_hid_mouse_scroll(-6);
+            consumed = true;
+        } else if(event->key == InputKeyDown) {
+            // Emulate down swipe
+            furi_hal_bt_hid_mouse_scroll(6);
+            furi_hal_bt_hid_mouse_scroll(12);
+            furi_hal_bt_hid_mouse_scroll(19);
+            furi_hal_bt_hid_mouse_scroll(12);
+            furi_hal_bt_hid_mouse_scroll(6);
+            consumed = true;
+        } else if(event->key == InputKeyBack) {
+            furi_hal_bt_hid_consumer_key_release_all();
+            consumed = true;
+        }
+    }
+
+    return consumed;
+}
+
+BtHidTikTok* bt_hid_tiktok_alloc() {
+    BtHidTikTok* bt_hid_tiktok = malloc(sizeof(BtHidTikTok));
+    bt_hid_tiktok->view = view_alloc();
+    view_set_context(bt_hid_tiktok->view, bt_hid_tiktok);
+    view_allocate_model(bt_hid_tiktok->view, ViewModelTypeLocking, sizeof(BtHidTikTokModel));
+    view_set_draw_callback(bt_hid_tiktok->view, bt_hid_tiktok_draw_callback);
+    view_set_input_callback(bt_hid_tiktok->view, bt_hid_tiktok_input_callback);
+
+    return bt_hid_tiktok;
+}
+
+void bt_hid_tiktok_free(BtHidTikTok* bt_hid_tiktok) {
+    furi_assert(bt_hid_tiktok);
+    view_free(bt_hid_tiktok->view);
+    free(bt_hid_tiktok);
+}
+
+View* bt_hid_tiktok_get_view(BtHidTikTok* bt_hid_tiktok) {
+    furi_assert(bt_hid_tiktok);
+    return bt_hid_tiktok->view;
+}
+
+void bt_hid_tiktok_set_connected_status(BtHidTikTok* bt_hid_tiktok, bool connected) {
+    furi_assert(bt_hid_tiktok);
+    with_view_model(
+        bt_hid_tiktok->view, BtHidTikTokModel * model, { model->connected = connected; }, true);
+}

+ 13 - 0
applications/plugins/bt_hid_app/views/bt_hid_tiktok.h

@@ -0,0 +1,13 @@
+#pragma once
+
+#include <gui/view.h>
+
+typedef struct BtHidTikTok BtHidTikTok;
+
+BtHidTikTok* bt_hid_tiktok_alloc();
+
+void bt_hid_tiktok_free(BtHidTikTok* bt_hid_tiktok);
+
+View* bt_hid_tiktok_get_view(BtHidTikTok* bt_hid_tiktok);
+
+void bt_hid_tiktok_set_connected_status(BtHidTikTok* bt_hid_tiktok, bool connected);