BT Remote: Add Rename Option

by aaronjamt & WillyJL
This commit is contained in:
MX
2025-07-30 04:32:54 +03:00
parent 3b7d837e48
commit 59a823cbcd
10 changed files with 274 additions and 6 deletions

View File

@@ -0,0 +1,36 @@
#include "ble_hid_ext_profile.h"
#include <furi.h>
static FuriHalBleProfileBase* ble_profile_hid_ext_start(FuriHalBleProfileParams profile_params) {
UNUSED(profile_params);
return ble_profile_hid->start(NULL);
}
static void ble_profile_hid_ext_stop(FuriHalBleProfileBase* profile) {
ble_profile_hid->stop(profile);
}
static void
ble_profile_hid_ext_get_config(GapConfig* config, FuriHalBleProfileParams profile_params) {
furi_check(config);
furi_check(profile_params);
BleProfileHidExtParams* hid_ext_profile_params = profile_params;
// Setup config with basic profile
ble_profile_hid->get_gap_config(config, NULL);
if(hid_ext_profile_params->name[0] != '\0') {
// Set advertise name (skip first byte which is the ADV type)
strlcpy(config->adv_name + 1, hid_ext_profile_params->name, sizeof(config->adv_name) - 1);
}
}
static const FuriHalBleProfileTemplate profile_callbacks = {
.start = ble_profile_hid_ext_start,
.stop = ble_profile_hid_ext_stop,
.get_gap_config = ble_profile_hid_ext_get_config,
};
const FuriHalBleProfileTemplate* ble_profile_hid_ext = &profile_callbacks;

View File

@@ -0,0 +1,14 @@
#pragma once
#include <ble_profile/extra_profiles/hid_profile.h>
/**
* Optional arguments to pass along with profile template as
* FuriHalBleProfileParams for tuning profile behavior
**/
typedef struct {
char name[FURI_HAL_BT_ADV_NAME_LENGTH]; /**< Full device name */
} BleProfileHidExtParams;
/** Hid Keyboard Profile descriptor */
extern const FuriHalBleProfileTemplate* ble_profile_hid_ext;

View File

@@ -4,9 +4,14 @@
#include "views.h"
#include <notification/notification_messages.h>
#include <dolphin/dolphin.h>
#include <flipper_format/flipper_format.h>
#define TAG "HidApp"
#define HID_BT_CFG_PATH APP_DATA_PATH(".bt_hid.cfg")
#define HID_BT_CFG_FILE_TYPE "Flipper BT Remote Settings File"
#define HID_BT_CFG_VERSION 1
bool hid_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
Hid* app = context;
@@ -33,6 +38,60 @@ void bt_hid_remove_pairing(Hid* app) {
furi_hal_bt_start_advertising();
}
static void bt_hid_load_cfg(Hid* app) {
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff = flipper_format_file_alloc(storage);
bool loaded = false;
FuriString* temp_str = furi_string_alloc();
uint32_t temp_uint = 0;
do {
if(!flipper_format_file_open_existing(fff, HID_BT_CFG_PATH)) break;
if(!flipper_format_read_header(fff, temp_str, &temp_uint)) break;
if((strcmp(furi_string_get_cstr(temp_str), HID_BT_CFG_FILE_TYPE) != 0) ||
(temp_uint != HID_BT_CFG_VERSION))
break;
if(flipper_format_read_string(fff, "name", temp_str)) {
strlcpy(
app->ble_hid_cfg.name,
furi_string_get_cstr(temp_str),
sizeof(app->ble_hid_cfg.name));
} else {
flipper_format_rewind(fff);
}
loaded = true;
} while(0);
furi_string_free(temp_str);
flipper_format_free(fff);
furi_record_close(RECORD_STORAGE);
if(!loaded) {
app->ble_hid_cfg.name[0] = '\0';
}
}
void bt_hid_save_cfg(Hid* app) {
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff = flipper_format_file_alloc(storage);
if(flipper_format_file_open_always(fff, HID_BT_CFG_PATH)) {
do {
if(!flipper_format_write_header_cstr(fff, HID_BT_CFG_FILE_TYPE, HID_BT_CFG_VERSION))
break;
if(!flipper_format_write_string_cstr(fff, "name", app->ble_hid_cfg.name)) break;
} while(0);
}
flipper_format_free(fff);
furi_record_close(RECORD_STORAGE);
}
static void bt_hid_connection_status_changed_callback(BtStatus status, void* context) {
furi_assert(context);
Hid* hid = context;
@@ -89,6 +148,11 @@ Hid* hid_alloc() {
app->dialog = dialog_ex_alloc();
view_dispatcher_add_view(app->view_dispatcher, HidViewDialog, dialog_ex_get_view(app->dialog));
// Text input
app->text_input = text_input_alloc();
view_dispatcher_add_view(
app->view_dispatcher, HidViewTextInput, text_input_get_view(app->text_input));
// Popup view
app->popup = popup_alloc();
view_dispatcher_add_view(app->view_dispatcher, HidViewPopup, popup_get_view(app->popup));
@@ -177,6 +241,8 @@ void hid_free(Hid* app) {
submenu_free(app->submenu);
view_dispatcher_remove_view(app->view_dispatcher, HidViewDialog);
dialog_ex_free(app->dialog);
view_dispatcher_remove_view(app->view_dispatcher, HidViewTextInput);
text_input_free(app->text_input);
view_dispatcher_remove_view(app->view_dispatcher, HidViewPopup);
popup_free(app->popup);
view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote);
@@ -266,7 +332,9 @@ int32_t hid_ble_app(void* p) {
furi_record_close(RECORD_STORAGE);
app->ble_hid_profile = bt_profile_start(app->bt, ble_profile_hid, NULL);
bt_hid_load_cfg(app);
app->ble_hid_profile = bt_profile_start(app->bt, ble_profile_hid_ext, &app->ble_hid_cfg);
furi_check(app->ble_hid_profile);

View File

@@ -5,7 +5,7 @@
#include <furi_hal_usb.h>
#include <furi_hal_usb_hid.h>
#include <extra_profiles/hid_profile.h>
#include "helpers/ble_hid_ext_profile.h"
#include <bt/bt_service/bt.h>
#include <gui/gui.h>
@@ -18,6 +18,7 @@
#include <gui/modules/submenu.h>
#include <gui/modules/dialog_ex.h>
#include <gui/modules/popup.h>
#include <gui/modules/text_input.h>
#include "views/hid_keynote.h"
#include "views/hid_keyboard.h"
#include "views/hid_numpad.h"
@@ -40,6 +41,7 @@ typedef struct Hid Hid;
struct Hid {
FuriHalBleProfileBase* ble_hid_profile;
BleProfileHidExtParams ble_hid_cfg;
Bt* bt;
Gui* gui;
NotificationApp* notifications;
@@ -47,6 +49,7 @@ struct Hid {
SceneManager* scene_manager;
Submenu* submenu;
DialogEx* dialog;
TextInput* text_input;
Popup* popup;
HidKeynote* hid_keynote;
HidKeyboard* hid_keyboard;
@@ -64,6 +67,7 @@ struct Hid {
};
void bt_hid_remove_pairing(Hid* app);
void bt_hid_save_cfg(Hid* app);
void hid_hal_keyboard_press(Hid* instance, uint16_t event);
void hid_hal_keyboard_release(Hid* instance, uint16_t event);

View File

@@ -1,3 +1,4 @@
ADD_SCENE(hid, start, Start)
ADD_SCENE(hid, main, Main)
ADD_SCENE(hid, unpair, Unpair)
ADD_SCENE(hid, rename, Rename)

View File

@@ -0,0 +1,82 @@
#include "../hid.h"
#include "../views.h"
#include "hid_icons.h"
enum HidSceneRenameEvent {
HidSceneRenameEventTextInput,
HidSceneRenameEventPopup,
};
static void hid_scene_rename_text_input_callback(void* context) {
Hid* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, HidSceneRenameEventTextInput);
}
void hid_scene_rename_popup_callback(void* context) {
Hid* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, HidSceneRenameEventPopup);
}
void hid_scene_rename_on_enter(void* context) {
Hid* app = context;
// Rename text input view
text_input_reset(app->text_input);
text_input_set_result_callback(
app->text_input,
hid_scene_rename_text_input_callback,
app,
app->ble_hid_cfg.name,
sizeof(app->ble_hid_cfg.name),
true);
text_input_set_header_text(app->text_input, "Bluetooth Name");
// Rename success popup view
popup_set_icon(app->popup, 48, 6, &I_DolphinDone_80x58);
popup_set_header(app->popup, "Done", 14, 15, AlignLeft, AlignTop);
popup_set_timeout(app->popup, 1500);
popup_set_context(app->popup, app);
popup_set_callback(app->popup, hid_scene_rename_popup_callback);
popup_enable_timeout(app->popup);
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewTextInput);
}
bool hid_scene_rename_on_event(void* context, SceneManagerEvent event) {
Hid* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == HidSceneRenameEventTextInput) {
#ifdef HID_TRANSPORT_BLE
furi_hal_bt_stop_advertising();
app->ble_hid_profile =
bt_profile_start(app->bt, ble_profile_hid_ext, &app->ble_hid_cfg);
furi_check(app->ble_hid_profile);
furi_hal_bt_start_advertising();
#endif
bt_hid_save_cfg(app);
// Show popup
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewPopup);
} else if(event.event == HidSceneRenameEventPopup) {
scene_manager_previous_scene(app->scene_manager);
}
}
return consumed;
}
void hid_scene_rename_on_exit(void* context) {
Hid* app = context;
text_input_reset(app->text_input);
popup_reset(app->popup);
}

View File

@@ -15,6 +15,7 @@ enum HidSubmenuIndex {
HidSubmenuIndexMouseJiggler,
HidSubmenuIndexMouseJigglerStealth,
HidSubmenuIndexPushToTalk,
HidSubmenuIndexRename,
HidSubmenuIndexRemovePairing,
};
@@ -81,6 +82,12 @@ void hid_scene_start_on_enter(void* context) {
hid_scene_start_submenu_callback,
app);
#ifdef HID_TRANSPORT_BLE
submenu_add_item(
app->submenu,
"Bluetooth Remote Name",
HidSubmenuIndexRename,
hid_scene_start_submenu_callback,
app);
submenu_add_item(
app->submenu,
"Bluetooth Unpairing",
@@ -101,6 +108,8 @@ bool hid_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == HidSubmenuIndexRemovePairing) {
scene_manager_next_scene(app->scene_manager, HidSceneUnpair);
} else if(event.event == HidSubmenuIndexRename) {
scene_manager_next_scene(app->scene_manager, HidSceneRename);
} else {
HidView view_id;

View File

@@ -16,4 +16,5 @@ typedef enum {
HidViewPushToTalkHelp,
HidViewDialog,
HidViewPopup,
HidViewTextInput,
} HidView;

View File

@@ -2,9 +2,10 @@
#include <gui/view.h>
typedef struct Hid Hid;
typedef struct HidMusicMacos HidMusicMacos;
HidMusicMacos* hid_music_macos_alloc();
HidMusicMacos* hid_music_macos_alloc(Hid* hid);
void hid_music_macos_free(HidMusicMacos* hid_music_macos);

View File

@@ -42,6 +42,7 @@ typedef struct {
enum HidPushToTalkAppIndex {
HidPushToTalkAppIndexDiscord,
HidPushToTalkAppIndexFaceTime,
HidPushToTalkAppIndexGather,
HidPushToTalkAppIndexGoogleMeet,
HidPushToTalkAppIndexGoogleHangouts,
HidPushToTalkAppIndexJamulus,
@@ -308,7 +309,6 @@ static void hid_ptt_trigger_mute_jamulus(HidPushToTalk* hid_ptt) {
}
// webex
static void hid_ptt_trigger_camera_webex(HidPushToTalk* hid_ptt) {
hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V);
hid_hal_keyboard_release(
@@ -325,6 +325,30 @@ static void hid_ptt_trigger_hand_linux_webex(HidPushToTalk* hid_ptt) {
hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_RIGHT_SHIFT | HID_KEYBOARD_R);
}
// Gather
static void hid_ptt_trigger_hand_gather(HidPushToTalk* hid_ptt) {
hid_hal_keyboard_press(hid_ptt->hid, HID_KEYBOARD_H);
hid_hal_keyboard_release(hid_ptt->hid, HID_KEYBOARD_H);
}
static void hid_ptt_trigger_camera_macos_gather(HidPushToTalk* hid_ptt) {
hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V);
hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V);
}
static void hid_ptt_trigger_mute_macos_gather(HidPushToTalk* hid_ptt) {
hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A);
hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A);
}
static void hid_ptt_trigger_camera_linux_gather(HidPushToTalk* hid_ptt) {
hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V);
hid_hal_keyboard_release(
hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V);
}
static void hid_ptt_trigger_mute_linux_gather(HidPushToTalk* hid_ptt) {
hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A);
hid_hal_keyboard_release(
hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A);
}
static void hid_ptt_menu_callback(
void* context,
uint32_t osIndex,
@@ -359,6 +383,13 @@ static void hid_ptt_menu_callback(
model->callback_start_ptt = hid_ptt_trigger_cmd_shift_m;
model->callback_stop_ptt = hid_ptt_trigger_cmd_shift_m;
break;
case HidPushToTalkAppIndexGather:
model->callback_trigger_mute = hid_ptt_trigger_mute_macos_gather;
model->callback_trigger_camera = hid_ptt_trigger_camera_macos_gather;
model->callback_trigger_hand = hid_ptt_trigger_hand_gather;
model->callback_start_ptt = hid_ptt_trigger_mute_macos_gather;
model->callback_stop_ptt = hid_ptt_trigger_mute_macos_gather;
break;
case HidPushToTalkAppIndexGoogleHangouts:
model->callback_trigger_mute = hid_ptt_trigger_mute_macos_hangouts;
model->callback_trigger_camera = hid_ptt_trigger_camera_macos_hangouts;
@@ -434,6 +465,13 @@ static void hid_ptt_menu_callback(
model->callback_start_ptt = hid_ptt_start_ptt_linux_discord;
model->callback_stop_ptt = hid_ptt_stop_ptt_linux_discord;
break;
case HidPushToTalkAppIndexGather:
model->callback_trigger_mute = hid_ptt_trigger_mute_linux_gather;
model->callback_trigger_camera = hid_ptt_trigger_camera_linux_gather;
model->callback_trigger_hand = hid_ptt_trigger_hand_gather;
model->callback_start_ptt = hid_ptt_trigger_mute_linux_gather;
model->callback_stop_ptt = hid_ptt_trigger_mute_linux_gather;
break;
case HidPushToTalkAppIndexGoogleHangouts:
model->callback_trigger_mute = hid_ptt_trigger_mute_linux_hangouts;
model->callback_trigger_camera = hid_ptt_trigger_camera_linux_hangouts;
@@ -873,6 +911,20 @@ HidPushToTalk* hid_ptt_alloc(Hid* hid) {
HidPushToTalkAppIndexFaceTime,
hid_ptt_menu_callback,
hid_ptt);
ptt_menu_add_item_to_list(
hid->hid_ptt_menu,
HidPushToTalkMacOS,
"Gather",
HidPushToTalkAppIndexGather,
hid_ptt_menu_callback,
hid_ptt);
ptt_menu_add_item_to_list(
hid->hid_ptt_menu,
HidPushToTalkLinux,
"Gather",
HidPushToTalkAppIndexGather,
hid_ptt_menu_callback,
hid_ptt);
ptt_menu_add_item_to_list(
hid->hid_ptt_menu,
HidPushToTalkMacOS,
@@ -932,14 +984,14 @@ HidPushToTalk* hid_ptt_alloc(Hid* hid) {
ptt_menu_add_item_to_list(
hid->hid_ptt_menu,
HidPushToTalkMacOS,
"Slack Hubble",
"Slack Huddle",
HidPushToTalkAppIndexSlackHubble,
hid_ptt_menu_callback,
hid_ptt);
ptt_menu_add_item_to_list(
hid->hid_ptt_menu,
HidPushToTalkLinux,
"Slack Hubble",
"Slack Huddle",
HidPushToTalkAppIndexSlackHubble,
hid_ptt_menu_callback,
hid_ptt);