From 3191b327084b464685257426339e48e24c48e7f6 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 14 Apr 2023 03:35:42 +0300 Subject: [PATCH] Youtube shorts remote, swap tiktok control buttons --- applications/external/hid_app/hid.c | 19 ++ applications/external/hid_app/hid.h | 2 + applications/external/hid_app/views.h | 1 + .../external/hid_app/views/hid_tiktok.c | 16 +- .../external/hid_app/views/hid_ytshorts.c | 268 ++++++++++++++++++ .../external/hid_app/views/hid_ytshorts.h | 14 + 6 files changed, 312 insertions(+), 8 deletions(-) create mode 100644 applications/external/hid_app/views/hid_ytshorts.c create mode 100644 applications/external/hid_app/views/hid_ytshorts.h diff --git a/applications/external/hid_app/hid.c b/applications/external/hid_app/hid.c index 949ff63b3..b679c395c 100644 --- a/applications/external/hid_app/hid.c +++ b/applications/external/hid_app/hid.c @@ -10,6 +10,7 @@ enum HidDebugSubmenuIndex { HidSubmenuIndexKeyboard, HidSubmenuIndexMedia, HidSubmenuIndexTikTok, + HidSubmenuIndexYTShorts, HidSubmenuIndexMouse, HidSubmenuIndexMouseJiggler, }; @@ -32,6 +33,9 @@ static void hid_submenu_callback(void* context, uint32_t index) { } else if(index == HidSubmenuIndexTikTok) { app->view_id = BtHidViewTikTok; view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); + } else if(index == HidSubmenuIndexYTShorts) { + app->view_id = BtHidViewYTShorts; + view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewYTShorts); } else if(index == HidSubmenuIndexMouseJiggler) { app->view_id = HidViewMouseJiggler; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler); @@ -55,6 +59,7 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con hid_mouse_set_connected_status(hid->hid_mouse, connected); hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected); hid_tiktok_set_connected_status(hid->hid_tiktok, connected); + hid_ytshorts_set_connected_status(hid->hid_ytshorts, connected); } static void hid_dialog_callback(DialogExResult result, void* context) { @@ -113,6 +118,12 @@ Hid* hid_alloc(HidTransport transport) { HidSubmenuIndexTikTok, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, + "YT Shorts Controller", + HidSubmenuIndexYTShorts, + hid_submenu_callback, + app); } submenu_add_item( app->device_type_submenu, @@ -166,6 +177,12 @@ Hid* hid_app_alloc_view(void* context) { view_dispatcher_add_view( app->view_dispatcher, BtHidViewTikTok, hid_tiktok_get_view(app->hid_tiktok)); + // YTShorts view + app->hid_ytshorts = hid_ytshorts_alloc(app); + view_set_previous_callback(hid_ytshorts_get_view(app->hid_ytshorts), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, BtHidViewYTShorts, hid_ytshorts_get_view(app->hid_ytshorts)); + // Mouse view app->hid_mouse = hid_mouse_alloc(app); view_set_previous_callback(hid_mouse_get_view(app->hid_mouse), hid_exit_confirm_view); @@ -209,6 +226,8 @@ void hid_free(Hid* app) { hid_mouse_jiggler_free(app->hid_mouse_jiggler); view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); hid_tiktok_free(app->hid_tiktok); + view_dispatcher_remove_view(app->view_dispatcher, BtHidViewYTShorts); + hid_ytshorts_free(app->hid_ytshorts); view_dispatcher_free(app->view_dispatcher); // Close records diff --git a/applications/external/hid_app/hid.h b/applications/external/hid_app/hid.h index 8ed1664a3..1ff42c940 100644 --- a/applications/external/hid_app/hid.h +++ b/applications/external/hid_app/hid.h @@ -22,6 +22,7 @@ #include "views/hid_mouse.h" #include "views/hid_mouse_jiggler.h" #include "views/hid_tiktok.h" +#include "views/hid_ytshorts.h" #define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys" @@ -45,6 +46,7 @@ struct Hid { HidMouse* hid_mouse; HidMouseJiggler* hid_mouse_jiggler; HidTikTok* hid_tiktok; + HidYTShorts* hid_ytshorts; HidTransport transport; uint32_t view_id; diff --git a/applications/external/hid_app/views.h b/applications/external/hid_app/views.h index 2a44832e1..14cfa126f 100644 --- a/applications/external/hid_app/views.h +++ b/applications/external/hid_app/views.h @@ -6,5 +6,6 @@ typedef enum { HidViewMouse, HidViewMouseJiggler, BtHidViewTikTok, + BtHidViewYTShorts, HidViewExitConfirm, } HidView; \ No newline at end of file diff --git a/applications/external/hid_app/views/hid_tiktok.c b/applications/external/hid_app/views/hid_tiktok.c index d3cf4a110..3b48d32cb 100644 --- a/applications/external/hid_app/views/hid_tiktok.c +++ b/applications/external/hid_app/views/hid_tiktok.c @@ -187,20 +187,20 @@ static bool hid_tiktok_input_callback(InputEvent* event, void* context) { consumed = true; } else if(event->key == InputKeyDown) { // Swipe to new video - 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, -6); - consumed = true; - } else if(event->key == InputKeyUp) { - // Swipe to previous video 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, 6); consumed = true; + } else if(event->key == InputKeyUp) { + // Swipe to previous video + 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, -6); + consumed = true; } else if(event->key == InputKeyBack) { // Pause hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_RIGHT); diff --git a/applications/external/hid_app/views/hid_ytshorts.c b/applications/external/hid_app/views/hid_ytshorts.c new file mode 100644 index 000000000..44ad02ff0 --- /dev/null +++ b/applications/external/hid_app/views/hid_ytshorts.c @@ -0,0 +1,268 @@ +#include "hid_ytshorts.h" +#include "../hid.h" +#include + +#include "hid_icons.h" + +#define TAG "HidYTShorts" + +struct HidYTShorts { + View* view; + Hid* hid; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool ok_pressed; + bool connected; + bool is_cursor_set; + bool back_mouse_pressed; + HidTransport transport; +} HidYTShortsModel; + +static void hid_ytshorts_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidYTShortsModel* model = context; + + // Header + 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); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Shorts"); + canvas_set_font(canvas, FontSecondary); + + // Keypad circles + canvas_draw_icon(canvas, 66, 8, &I_Circles_47x47); + + // Pause + if(model->back_mouse_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 106, 46, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 108, 48, &I_Pause_icon_9x9); + canvas_set_color(canvas, ColorBlack); + + // Up + if(model->up_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 83, 9, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 86, 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, 83, 41, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 86, 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, 67, 25, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 71, 29, &I_Voldwn_6x6); + canvas_set_color(canvas, ColorBlack); + + // Right + if(model->right_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 99, 25, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 101, 29, &I_Volup_8x6); + canvas_set_color(canvas, ColorBlack); + + // Ok + if(model->ok_pressed) { + canvas_draw_icon(canvas, 81, 23, &I_Like_pressed_17x17); + } else { + canvas_draw_icon(canvas, 84, 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 hid_ytshorts_reset_cursor(HidYTShorts* hid_ytshorts) { + // Set cursor to the phone's left up corner + // Delays to guarantee one packet per connection interval + for(size_t i = 0; i < 8; i++) { + hid_hal_mouse_move(hid_ytshorts->hid, -127, -127); + furi_delay_ms(50); + } + // Move cursor from the corner + hid_hal_mouse_move(hid_ytshorts->hid, 40, 120); + hid_hal_mouse_move(hid_ytshorts->hid, 0, 120); + furi_delay_ms(50); +} + +static void hid_ytshorts_process_press( + HidYTShorts* hid_ytshorts, + HidYTShortsModel* model, + InputEvent* event) { + 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; + hid_hal_consumer_key_press(hid_ytshorts->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + hid_hal_consumer_key_press(hid_ytshorts->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyOk) { + model->ok_pressed = true; + } else if(event->key == InputKeyBack) { + model->back_mouse_pressed = true; + } +} + +static void hid_ytshorts_process_release( + HidYTShorts* hid_ytshorts, + HidYTShortsModel* model, + InputEvent* event) { + 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; + hid_hal_consumer_key_release(hid_ytshorts->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + hid_hal_consumer_key_release(hid_ytshorts->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyOk) { + model->ok_pressed = false; + } else if(event->key == InputKeyBack) { + model->back_mouse_pressed = false; + } +} + +static bool hid_ytshorts_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidYTShorts* hid_ytshorts = context; + bool consumed = false; + + with_view_model( + hid_ytshorts->view, + HidYTShortsModel * model, + { + if(event->type == InputTypePress) { + hid_ytshorts_process_press(hid_ytshorts, model, event); + if(model->connected && !model->is_cursor_set) { + hid_ytshorts_reset_cursor(hid_ytshorts); + model->is_cursor_set = true; + } + consumed = true; + } else if(event->type == InputTypeRelease) { + hid_ytshorts_process_release(hid_ytshorts, model, event); + consumed = true; + } else if(event->type == InputTypeShort) { + if(event->key == InputKeyOk) { + hid_hal_mouse_press(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + hid_hal_mouse_release(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + hid_hal_mouse_press(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + hid_hal_mouse_release(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + consumed = true; + } else if(event->key == InputKeyDown) { + // Swipe to new video + hid_hal_mouse_scroll(hid_ytshorts->hid, 6); + hid_hal_mouse_scroll(hid_ytshorts->hid, 8); + hid_hal_mouse_scroll(hid_ytshorts->hid, 10); + hid_hal_mouse_scroll(hid_ytshorts->hid, 8); + hid_hal_mouse_scroll(hid_ytshorts->hid, 6); + consumed = true; + } else if(event->key == InputKeyUp) { + // Swipe to previous video + hid_hal_mouse_scroll(hid_ytshorts->hid, -6); + hid_hal_mouse_scroll(hid_ytshorts->hid, -8); + hid_hal_mouse_scroll(hid_ytshorts->hid, -10); + hid_hal_mouse_scroll(hid_ytshorts->hid, -8); + hid_hal_mouse_scroll(hid_ytshorts->hid, -6); + consumed = true; + } else if(event->key == InputKeyBack) { + // Pause + hid_hal_mouse_press(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + hid_hal_mouse_release(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + consumed = true; + } + } else if(event->type == InputTypeLong) { + if(event->key == InputKeyBack) { + hid_hal_consumer_key_release_all(hid_ytshorts->hid); + model->is_cursor_set = false; + consumed = false; + } + } + }, + true); + + return consumed; +} + +HidYTShorts* hid_ytshorts_alloc(Hid* bt_hid) { + HidYTShorts* hid_ytshorts = malloc(sizeof(HidYTShorts)); + hid_ytshorts->hid = bt_hid; + hid_ytshorts->view = view_alloc(); + view_set_context(hid_ytshorts->view, hid_ytshorts); + view_allocate_model(hid_ytshorts->view, ViewModelTypeLocking, sizeof(HidYTShortsModel)); + view_set_draw_callback(hid_ytshorts->view, hid_ytshorts_draw_callback); + view_set_input_callback(hid_ytshorts->view, hid_ytshorts_input_callback); + + with_view_model( + hid_ytshorts->view, + HidYTShortsModel * model, + { model->transport = bt_hid->transport; }, + true); + + return hid_ytshorts; +} + +void hid_ytshorts_free(HidYTShorts* hid_ytshorts) { + furi_assert(hid_ytshorts); + view_free(hid_ytshorts->view); + free(hid_ytshorts); +} + +View* hid_ytshorts_get_view(HidYTShorts* hid_ytshorts) { + furi_assert(hid_ytshorts); + return hid_ytshorts->view; +} + +void hid_ytshorts_set_connected_status(HidYTShorts* hid_ytshorts, bool connected) { + furi_assert(hid_ytshorts); + with_view_model( + hid_ytshorts->view, + HidYTShortsModel * model, + { + model->connected = connected; + model->is_cursor_set = false; + }, + true); +} diff --git a/applications/external/hid_app/views/hid_ytshorts.h b/applications/external/hid_app/views/hid_ytshorts.h new file mode 100644 index 000000000..03264dd36 --- /dev/null +++ b/applications/external/hid_app/views/hid_ytshorts.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidYTShorts HidYTShorts; + +HidYTShorts* hid_ytshorts_alloc(Hid* bt_hid); + +void hid_ytshorts_free(HidYTShorts* hid_ytshorts); + +View* hid_ytshorts_get_view(HidYTShorts* hid_ytshorts); + +void hid_ytshorts_set_connected_status(HidYTShorts* hid_ytshorts, bool connected);