diff --git a/.ofwcatalog/CHANGELOG.md b/.ofwcatalog/CHANGELOG.md index 7660aae4abf..8e232a3df6c 100644 --- a/.ofwcatalog/CHANGELOG.md +++ b/.ofwcatalog/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## v3.1.0 - 31 Jul 2023 + +* Fixed [#171](https://github.com/akopachov/flipper-zero_authenticator/issues/171) + +## v3.0.2 - 28 Jul 2023 + +* Fixed [#169](https://github.com/akopachov/flipper-zero_authenticator/issues/169) +* Fixed [#172](https://github.com/akopachov/flipper-zero_authenticator/issues/172) + ## v3.0.0 - 26 Jul 2023 * Implemented better encryption [#167](https://github.com/akopachov/flipper-zero_authenticator/issues/167) diff --git a/application.fam b/application.fam index 039f1da3162..3156c81ce17 100644 --- a/application.fam +++ b/application.fam @@ -15,7 +15,7 @@ App( ], stack_size=2 * 1024, order=20, - fap_version="3.2", + fap_version="3.10", fap_author="Alexander Kopachov (@akopachov)", fap_description="Software-based TOTP authenticator for Flipper Zero device", fap_weburl="https://github.com/akopachov/flipper-zero_authenticator", diff --git a/cli/commands/automation/automation.c b/cli/commands/automation/automation.c index c9f6ac34b35..b3f2c33d63b 100644 --- a/cli/commands/automation/automation.c +++ b/cli/commands/automation/automation.c @@ -10,14 +10,20 @@ #ifdef TOTP_BADBT_TYPE_ENABLED #define TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT "bt" #endif +#define TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_QWERTY "QWERTY" +#define TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_AZERTY "AZERTY" +#define TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT_PREFIX "-k" +#define TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT "layout" void totp_cli_command_automation_docopt_commands() { - TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_AUTOMATION " Get or set automation method\r\n"); + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_AUTOMATION " Get or set automation settings\r\n"); } void totp_cli_command_automation_docopt_usage() { - TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_AUTOMATION " " DOCOPT_OPTIONAL( - DOCOPT_MULTIPLE(DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD))) "\r\n"); + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_AUTOMATION " " DOCOPT_OPTIONAL(DOCOPT_OPTION( + TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT_PREFIX, + DOCOPT_ARGUMENT( + TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT))) " " DOCOPT_OPTIONAL(DOCOPT_MULTIPLE(DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD))) "\r\n"); } void totp_cli_command_automation_docopt_arguments() { @@ -31,7 +37,16 @@ void totp_cli_command_automation_docopt_arguments() { "\r\n"); } -static void totp_cli_command_automation_print_method(AutomationMethod method, const char* color) { +void totp_cli_command_automation_docopt_options() { + TOTP_CLI_PRINTF(" " DOCOPT_OPTION( + TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT_PREFIX, + DOCOPT_ARGUMENT( + TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT)) " Automation keyboard layout. Must be one of: " TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_QWERTY + ", " TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_AZERTY + "\r\n"); +} + +static void print_method(AutomationMethod method, const char* color) { #ifdef TOTP_BADBT_TYPE_ENABLED bool has_previous_method = false; #endif @@ -57,6 +72,36 @@ static void totp_cli_command_automation_print_method(AutomationMethod method, co } } +static void print_kb_layout(AutomationKeyboardLayout layout, const char* color) { + char* layoutToPrint; + switch(layout) { + case AutomationKeyboardLayoutQWERTY: + layoutToPrint = TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_QWERTY; + break; + case AutomationKeyboardLayoutAZERTY: + layoutToPrint = TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_AZERTY; + break; + default: + furi_crash("Unknown automation keyboard layout"); + break; + } + + TOTP_CLI_PRINTF_COLORFUL(color, "%s", layoutToPrint); +} + +static bool parse_automation_keyboard_layout(const FuriString* str, AutomationKeyboardLayout* out) { + bool result = true; + if(furi_string_cmpi_str(str, TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_QWERTY) == 0) { + *out = AutomationKeyboardLayoutQWERTY; + } else if(furi_string_cmpi_str(str, TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_AZERTY) == 0) { + *out = AutomationKeyboardLayoutAZERTY; + } else { + result = false; + } + + return result; +} + void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) { return; @@ -65,6 +110,7 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a FuriString* temp_str = furi_string_alloc(); bool new_method_provided = false; AutomationMethod new_method = AutomationMethodNone; + AutomationKeyboardLayout new_kb_layout = plugin_state->automation_kb_layout; bool args_valid = true; while(args_read_string_and_trim(args, temp_str)) { if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE) == 0) { @@ -80,7 +126,13 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a new_method |= AutomationMethodBadBt; } #endif - else { + else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT_PREFIX) == 0) { + if(!args_read_string_and_trim(args, temp_str) || + !parse_automation_keyboard_layout(temp_str, &new_kb_layout)) { + args_valid = false; + break; + } + } else { args_valid = false; break; } @@ -96,9 +148,13 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a TOTP_CLI_LOCK_UI(plugin_state); plugin_state->automation_method = new_method; + plugin_state->automation_kb_layout = new_kb_layout; if(totp_config_file_update_automation_method(plugin_state)) { TOTP_CLI_PRINTF_SUCCESS("Automation method is set to "); - totp_cli_command_automation_print_method(new_method, TOTP_CLI_COLOR_SUCCESS); + print_method(new_method, TOTP_CLI_COLOR_SUCCESS); + TOTP_CLI_PRINTF_SUCCESS(" ("); + print_kb_layout(plugin_state->automation_kb_layout, TOTP_CLI_COLOR_SUCCESS); + TOTP_CLI_PRINTF_SUCCESS(")"); cli_nl(); } else { totp_cli_print_error_updating_config_file(); @@ -115,8 +171,10 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a TOTP_CLI_UNLOCK_UI(plugin_state); } else { TOTP_CLI_PRINTF_INFO("Current automation method is "); - totp_cli_command_automation_print_method( - plugin_state->automation_method, TOTP_CLI_COLOR_INFO); + print_method(plugin_state->automation_method, TOTP_CLI_COLOR_INFO); + TOTP_CLI_PRINTF_INFO(" ("); + print_kb_layout(plugin_state->automation_kb_layout, TOTP_CLI_COLOR_INFO); + TOTP_CLI_PRINTF_INFO(")"); cli_nl(); } } while(false); diff --git a/cli/commands/automation/automation.h b/cli/commands/automation/automation.h index fb62e638ef2..4a713d49be0 100644 --- a/cli/commands/automation/automation.h +++ b/cli/commands/automation/automation.h @@ -8,4 +8,5 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* args, Cli* cli); void totp_cli_command_automation_docopt_commands(); void totp_cli_command_automation_docopt_usage(); -void totp_cli_command_automation_docopt_arguments(); \ No newline at end of file +void totp_cli_command_automation_docopt_arguments(); +void totp_cli_command_automation_docopt_options(); \ No newline at end of file diff --git a/cli/commands/help/help.c b/cli/commands/help/help.c index 747f51d60a3..7093877ea8f 100644 --- a/cli/commands/help/help.c +++ b/cli/commands/help/help.c @@ -65,4 +65,5 @@ void totp_cli_command_help_handle() { totp_cli_command_update_docopt_options(); totp_cli_command_delete_docopt_options(); totp_cli_command_pin_docopt_options(); + totp_cli_command_automation_docopt_options(); } \ No newline at end of file diff --git a/images/totp_arrow_bottom_10x5.png b/images/totp_arrow_bottom_10x5.png deleted file mode 100644 index 54e22f5efb6..00000000000 Binary files a/images/totp_arrow_bottom_10x5.png and /dev/null differ diff --git a/services/config/config.c b/services/config/config.c index 3fe4d3999c9..bb6c831c276 100644 --- a/services/config/config.c +++ b/services/config/config.c @@ -162,7 +162,11 @@ static bool totp_open_config_file(Storage* storage, FlipperFormat** file) { flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1); - tmp_uint32 = 0; + tmp_uint32 = AutomationKeyboardLayoutQWERTY; + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, &tmp_uint32, 1); + + tmp_uint32 = 0; //-V1048 flipper_format_write_uint32(fff_data_file, TOTP_CONFIG_KEY_FONT, &tmp_uint32, 1); if(!flipper_format_rewind(fff_data_file)) { @@ -241,6 +245,12 @@ bool totp_config_file_update_automation_method(const PluginState* plugin_state) break; } + tmp_uint32 = plugin_state->automation_kb_layout; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, &tmp_uint32, 1)) { + break; + } + update_result = true; } while(false); @@ -273,6 +283,12 @@ bool totp_config_file_update_user_settings(const PluginState* plugin_state) { break; } + tmp_uint32 = plugin_state->automation_kb_layout; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, &tmp_uint32, 1)) { + break; + } + update_result = true; } while(false); @@ -458,6 +474,17 @@ bool totp_config_file_load(PluginState* const plugin_state) { break; } + if(!flipper_format_read_uint32( + fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, &tmp_uint32, 1)) { + tmp_uint32 = AutomationKeyboardLayoutQWERTY; + } + + plugin_state->automation_kb_layout = tmp_uint32; + + if(!flipper_format_rewind(fff_data_file)) { + break; + } + if(!flipper_format_read_uint32(fff_data_file, TOTP_CONFIG_KEY_FONT, &tmp_uint32, 1)) { tmp_uint32 = 0; } diff --git a/services/config/constants.h b/services/config/constants.h index 62340c5c5c2..6a950caee41 100644 --- a/services/config/constants.h +++ b/services/config/constants.h @@ -4,7 +4,7 @@ #define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("authenticator") #define CONFIG_FILE_HEADER "Flipper TOTP plugin config file" -#define CONFIG_FILE_ACTUAL_VERSION (7) +#define CONFIG_FILE_ACTUAL_VERSION (8) #define TOTP_CONFIG_KEY_TIMEZONE "Timezone" #define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName" @@ -18,6 +18,7 @@ #define TOTP_CONFIG_KEY_PINSET "PinIsSet" #define TOTP_CONFIG_KEY_NOTIFICATION_METHOD "NotificationMethod" #define TOTP_CONFIG_KEY_AUTOMATION_METHOD "AutomationMethod" +#define TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT "AutomationKbLayout" #define TOTP_CONFIG_KEY_FONT "Font" #define TOTP_CONFIG_KEY_CRYPTO_VERSION "CryptoVersion" #define TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT "CryptoKeySlot" diff --git a/services/config/migrations/common_migration.c b/services/config/migrations/common_migration.c index 554cf48ad7f..4a12cbd3a98 100644 --- a/services/config/migrations/common_migration.c +++ b/services/config/migrations/common_migration.c @@ -1,6 +1,7 @@ #include "common_migration.h" #include "../constants.h" #include "../../../types/token_info.h" +#include "../../../types/automation_kb_layout.h" #include bool totp_config_migrate_to_latest( @@ -90,6 +91,21 @@ bool totp_config_migrate_to_latest( flipper_format_rewind(fff_backup_data_file); + if(flipper_format_read_string( + fff_backup_data_file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, temp_str)) { + flipper_format_write_string( + fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, temp_str); + } else { + uint32_t default_automation_kb_layout = AutomationKeyboardLayoutQWERTY; + flipper_format_write_uint32( + fff_data_file, + TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, + &default_automation_kb_layout, + 1); + } + + flipper_format_rewind(fff_backup_data_file); + while(true) { if(!flipper_format_read_string( fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) { diff --git a/types/automation_kb_layout.h b/types/automation_kb_layout.h new file mode 100644 index 00000000000..9c23e91ab44 --- /dev/null +++ b/types/automation_kb_layout.h @@ -0,0 +1,8 @@ +#pragma once + +typedef uint8_t AutomationKeyboardLayout; + +enum AutomationKeyboardLayouts { + AutomationKeyboardLayoutQWERTY = 0, + AutomationKeyboardLayoutAZERTY = 1 +}; \ No newline at end of file diff --git a/types/plugin_state.h b/types/plugin_state.h index 8d110276b30..f9250a3fa15 100644 --- a/types/plugin_state.h +++ b/types/plugin_state.h @@ -9,6 +9,7 @@ #include "../services/idle_timeout/idle_timeout.h" #include "notification_method.h" #include "automation_method.h" +#include "automation_kb_layout.h" #ifdef TOTP_BADBT_TYPE_ENABLED #include "../workers/bt_type_code/bt_type_code.h" #endif @@ -83,6 +84,11 @@ typedef struct { */ AutomationMethod automation_method; + /** + * @brief Automation keyboard layout to be used + */ + AutomationKeyboardLayout automation_kb_layout; + #ifdef TOTP_BADBT_TYPE_ENABLED /** * @brief Bad-Bluetooth worker context diff --git a/ui/scenes/app_settings/totp_app_settings.c b/ui/scenes/app_settings/totp_app_settings.c index 7575d0f2df9..dbef1e8d788 100644 --- a/ui/scenes/app_settings/totp_app_settings.c +++ b/ui/scenes/app_settings/totp_app_settings.c @@ -17,7 +17,23 @@ #endif static const char* YES_NO_LIST[] = {"NO", "YES"}; -static const char* ON_OFF_LIST[] = {"OFF", "ON"}; +static const char* AUTOMATION_LIST[] = { + "None", + "USB" +#ifdef TOTP_BADBT_TYPE_ENABLED + , + "Bluetooth", + "BT and USB" +#endif +}; + +#ifdef TOTP_BADBT_TYPE_ENABLED +#define AUTOMATION_LIST_MAX_INDEX (3) +#else +#define AUTOMATION_LIST_MAX_INDEX (1) +#endif + +static const char* BAD_KB_LAYOUT_LIST[] = {"QWERTY", "AZERTY"}; static const char* FONT_TEST_STR = "0123BCD"; static const uint8_t FONT_TEST_STR_LENGTH = 7; @@ -27,10 +43,8 @@ typedef enum { FontSelect, SoundSwitch, VibroSwitch, - BadUsbSwitch, -#ifdef TOTP_BADBT_TYPE_ENABLED - BadBtSwitch, -#endif + AutomationSwitch, + BadKeyboardLayoutSelect, ConfirmButton } Control; @@ -39,11 +53,9 @@ typedef struct { uint8_t tz_offset_minutes; bool notification_sound; bool notification_vibro; - bool badusb_enabled; -#ifdef TOTP_BADBT_TYPE_ENABLED - bool badbt_enabled; -#endif - uint8_t y_offset; + AutomationMethod automation_method; + uint16_t y_offset; + AutomationKeyboardLayout automation_kb_layout; Control selected_control; uint8_t active_font; } SceneState; @@ -59,10 +71,9 @@ void totp_scene_app_settings_activate(PluginState* plugin_state) { scene_state->tz_offset_minutes = 60.0f * off_dec; scene_state->notification_sound = plugin_state->notification_method & NotificationMethodSound; scene_state->notification_vibro = plugin_state->notification_method & NotificationMethodVibro; - scene_state->badusb_enabled = plugin_state->automation_method & AutomationMethodBadUsb; -#ifdef TOTP_BADBT_TYPE_ENABLED - scene_state->badbt_enabled = plugin_state->automation_method & AutomationMethodBadBt; -#endif + scene_state->automation_method = plugin_state->automation_method; + scene_state->automation_kb_layout = plugin_state->automation_kb_layout; + scene_state->active_font = plugin_state->active_font_index; } @@ -82,127 +93,121 @@ static void two_digit_to_str(int8_t num, char* str) { void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plugin_state) { const SceneState* scene_state = plugin_state->current_scene_state; + if(scene_state->selected_control < FontSelect) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 0, 0 - scene_state->y_offset, AlignLeft, AlignTop, "Timezone offset"); + canvas_set_font(canvas, FontSecondary); + + char tmp_str[4]; + two_digit_to_str(scene_state->tz_offset_hours, &tmp_str[0]); + canvas_draw_str_aligned( + canvas, 0, 17 - scene_state->y_offset, AlignLeft, AlignTop, "Hours:"); + ui_control_select_render( + canvas, + 36, + 10 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + &tmp_str[0], + scene_state->selected_control == HoursInput); + + two_digit_to_str(scene_state->tz_offset_minutes, &tmp_str[0]); + canvas_draw_str_aligned( + canvas, 0, 35 - scene_state->y_offset, AlignLeft, AlignTop, "Minutes:"); + ui_control_select_render( + canvas, + 36, + 28 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + &tmp_str[0], + scene_state->selected_control == MinutesInput); + + } else if(scene_state->selected_control < SoundSwitch) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 0, 64 - scene_state->y_offset, AlignLeft, AlignTop, "Font"); + canvas_set_font(canvas, FontSecondary); + + const FONT_INFO* const font = available_fonts[scene_state->active_font]; + ui_control_select_render( + canvas, + 0, + 74 - scene_state->y_offset, + SCREEN_WIDTH - UI_CONTROL_VSCROLL_WIDTH, + font->name, + scene_state->selected_control == FontSelect); + + uint8_t font_x_offset = + SCREEN_WIDTH_CENTER - + (((font->charInfo[0].width + font->spacePixels) * FONT_TEST_STR_LENGTH) >> 1); + uint8_t font_y_offset = 108 - scene_state->y_offset - (font->height >> 1); + canvas_draw_str_ex( + canvas, font_x_offset, font_y_offset, FONT_TEST_STR, FONT_TEST_STR_LENGTH, font); + + } else if(scene_state->selected_control < AutomationSwitch) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 0, 128 - scene_state->y_offset, AlignLeft, AlignTop, "Notifications"); + canvas_set_font(canvas, FontSecondary); + + canvas_draw_str_aligned( + canvas, 0, 145 - scene_state->y_offset, AlignLeft, AlignTop, "Sound:"); + ui_control_select_render( + canvas, + 36, + 138 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + YES_NO_LIST[scene_state->notification_sound], + scene_state->selected_control == SoundSwitch); + + canvas_draw_str_aligned( + canvas, 0, 163 - scene_state->y_offset, AlignLeft, AlignTop, "Vibro:"); + ui_control_select_render( + canvas, + 36, + 156 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + YES_NO_LIST[scene_state->notification_vibro], + scene_state->selected_control == VibroSwitch); + } else { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 0, 192 - scene_state->y_offset, AlignLeft, AlignTop, "Automation"); + canvas_set_font(canvas, FontSecondary); + + canvas_draw_str_aligned( + canvas, 0, 209 - scene_state->y_offset, AlignLeft, AlignTop, "Method:"); + ui_control_select_render( + canvas, + 36, + 202 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + AUTOMATION_LIST[scene_state->automation_method], + scene_state->selected_control == AutomationSwitch); + + canvas_draw_str_aligned( + canvas, 0, 227 - scene_state->y_offset, AlignLeft, AlignTop, "Layout:"); + + ui_control_select_render( + canvas, + 36, + 220 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + BAD_KB_LAYOUT_LIST[scene_state->automation_kb_layout], + scene_state->selected_control == BadKeyboardLayoutSelect); + + ui_control_button_render( + canvas, + SCREEN_WIDTH_CENTER - 24, + 242 - scene_state->y_offset, + 48, + 13, + "Confirm", + scene_state->selected_control == ConfirmButton); + } - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 0, 0 - scene_state->y_offset, AlignLeft, AlignTop, "Timezone offset"); - canvas_set_font(canvas, FontSecondary); - - char tmp_str[4]; - two_digit_to_str(scene_state->tz_offset_hours, &tmp_str[0]); - canvas_draw_str_aligned(canvas, 0, 17 - scene_state->y_offset, AlignLeft, AlignTop, "Hours:"); - ui_control_select_render( - canvas, - 36, - 10 - scene_state->y_offset, - SCREEN_WIDTH - 36, - &tmp_str[0], - scene_state->selected_control == HoursInput); - - two_digit_to_str(scene_state->tz_offset_minutes, &tmp_str[0]); - canvas_draw_str_aligned( - canvas, 0, 35 - scene_state->y_offset, AlignLeft, AlignTop, "Minutes:"); - ui_control_select_render( - canvas, - 36, - 28 - scene_state->y_offset, - SCREEN_WIDTH - 36, - &tmp_str[0], - scene_state->selected_control == MinutesInput); - - canvas_draw_icon( - canvas, - SCREEN_WIDTH_CENTER - 5, - SCREEN_HEIGHT - 5 - scene_state->y_offset, - &I_totp_arrow_bottom_10x5); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 0, 64 - scene_state->y_offset, AlignLeft, AlignTop, "Font"); - canvas_set_font(canvas, FontSecondary); - - const FONT_INFO* const font = available_fonts[scene_state->active_font]; - ui_control_select_render( - canvas, - 0, - 74 - scene_state->y_offset, - SCREEN_WIDTH, - font->name, - scene_state->selected_control == FontSelect); - - uint8_t font_x_offset = - SCREEN_WIDTH_CENTER - - (((font->charInfo[0].width + font->spacePixels) * FONT_TEST_STR_LENGTH) >> 1); - uint8_t font_y_offset = 108 - scene_state->y_offset - (font->height >> 1); - canvas_draw_str_ex( - canvas, font_x_offset, font_y_offset, FONT_TEST_STR, FONT_TEST_STR_LENGTH, font); - - canvas_draw_icon( - canvas, SCREEN_WIDTH_CENTER - 5, 123 - scene_state->y_offset, &I_totp_arrow_bottom_10x5); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 0, 128 - scene_state->y_offset, AlignLeft, AlignTop, "Notifications"); - canvas_set_font(canvas, FontSecondary); - - canvas_draw_str_aligned(canvas, 0, 145 - scene_state->y_offset, AlignLeft, AlignTop, "Sound:"); - ui_control_select_render( - canvas, - 36, - 138 - scene_state->y_offset, - SCREEN_WIDTH - 36, - YES_NO_LIST[scene_state->notification_sound], - scene_state->selected_control == SoundSwitch); - - canvas_draw_str_aligned(canvas, 0, 163 - scene_state->y_offset, AlignLeft, AlignTop, "Vibro:"); - ui_control_select_render( - canvas, - 36, - 156 - scene_state->y_offset, - SCREEN_WIDTH - 36, - YES_NO_LIST[scene_state->notification_vibro], - scene_state->selected_control == VibroSwitch); - - canvas_draw_icon( - canvas, SCREEN_WIDTH_CENTER - 5, 187 - scene_state->y_offset, &I_totp_arrow_bottom_10x5); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 0, 192 - scene_state->y_offset, AlignLeft, AlignTop, "Automation"); - canvas_set_font(canvas, FontSecondary); - - canvas_draw_str_aligned( - canvas, 0, 209 - scene_state->y_offset, AlignLeft, AlignTop, "BadUSB:"); - ui_control_select_render( - canvas, - 36, - 202 - scene_state->y_offset, - SCREEN_WIDTH - 36, - ON_OFF_LIST[scene_state->badusb_enabled], - scene_state->selected_control == BadUsbSwitch); - -#ifdef TOTP_BADBT_TYPE_ENABLED - canvas_draw_str_aligned(canvas, 0, 227 - scene_state->y_offset, AlignLeft, AlignTop, "BadBT:"); - ui_control_select_render( - canvas, - 36, - 220 - scene_state->y_offset, - SCREEN_WIDTH - 36, - ON_OFF_LIST[scene_state->badbt_enabled], - scene_state->selected_control == BadBtSwitch); -#endif - - ui_control_button_render( - canvas, - SCREEN_WIDTH_CENTER - 24, -#ifdef TOTP_BADBT_TYPE_ENABLED - 242 - scene_state->y_offset, -#else - 229 - scene_state->y_offset, -#endif - 48, - 13, - "Confirm", - scene_state->selected_control == ConfirmButton); + ui_control_vscroll_render( + canvas, SCREEN_WIDTH - 3, 0, SCREEN_HEIGHT, scene_state->selected_control, ConfirmButton); } bool totp_scene_app_settings_handle_event( @@ -267,14 +272,17 @@ bool totp_scene_app_settings_handle_event( scene_state->notification_sound = !scene_state->notification_sound; } else if(scene_state->selected_control == VibroSwitch) { scene_state->notification_vibro = !scene_state->notification_vibro; - } else if(scene_state->selected_control == BadUsbSwitch) { - scene_state->badusb_enabled = !scene_state->badusb_enabled; - } -#ifdef TOTP_BADBT_TYPE_ENABLED - else if(scene_state->selected_control == BadBtSwitch) { - scene_state->badbt_enabled = !scene_state->badbt_enabled; + } else if(scene_state->selected_control == AutomationSwitch) { + totp_roll_value_uint8_t( + &scene_state->automation_method, + 1, + 0, + AUTOMATION_LIST_MAX_INDEX, + RollOverflowBehaviorRoll); + } else if(scene_state->selected_control == BadKeyboardLayoutSelect) { + totp_roll_value_uint8_t( + &scene_state->automation_kb_layout, 1, 0, 1, RollOverflowBehaviorRoll); } -#endif break; case InputKeyLeft: if(scene_state->selected_control == HoursInput) { @@ -294,14 +302,17 @@ bool totp_scene_app_settings_handle_event( scene_state->notification_sound = !scene_state->notification_sound; } else if(scene_state->selected_control == VibroSwitch) { scene_state->notification_vibro = !scene_state->notification_vibro; - } else if(scene_state->selected_control == BadUsbSwitch) { - scene_state->badusb_enabled = !scene_state->badusb_enabled; - } -#ifdef TOTP_BADBT_TYPE_ENABLED - else if(scene_state->selected_control == BadBtSwitch) { - scene_state->badbt_enabled = !scene_state->badbt_enabled; + } else if(scene_state->selected_control == AutomationSwitch) { + totp_roll_value_uint8_t( + &scene_state->automation_method, + -1, + 0, + AUTOMATION_LIST_MAX_INDEX, + RollOverflowBehaviorRoll); + } else if(scene_state->selected_control == BadKeyboardLayoutSelect) { + totp_roll_value_uint8_t( + &scene_state->automation_kb_layout, -1, 0, 1, RollOverflowBehaviorRoll); } -#endif break; case InputKeyOk: break; @@ -322,14 +333,9 @@ bool totp_scene_app_settings_handle_event( (scene_state->notification_sound ? NotificationMethodSound : NotificationMethodNone) | (scene_state->notification_vibro ? NotificationMethodVibro : NotificationMethodNone); - plugin_state->automation_method = scene_state->badusb_enabled ? AutomationMethodBadUsb : - AutomationMethodNone; -#ifdef TOTP_BADBT_TYPE_ENABLED - plugin_state->automation_method |= scene_state->badbt_enabled ? AutomationMethodBadBt : - AutomationMethodNone; -#endif - + plugin_state->automation_method = scene_state->automation_method; plugin_state->active_font_index = scene_state->active_font; + plugin_state->automation_kb_layout = scene_state->automation_kb_layout; if(!totp_config_file_update_user_settings(plugin_state)) { totp_dialogs_config_updating_error(plugin_state); @@ -337,7 +343,8 @@ bool totp_scene_app_settings_handle_event( } #ifdef TOTP_BADBT_TYPE_ENABLED - if(!scene_state->badbt_enabled && plugin_state->bt_type_code_worker_context != NULL) { + if((scene_state->automation_method & AutomationMethodBadBt) == 0 && + plugin_state->bt_type_code_worker_context != NULL) { totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context); plugin_state->bt_type_code_worker_context = NULL; } diff --git a/ui/scenes/generate_token/totp_scene_generate_token.c b/ui/scenes/generate_token/totp_scene_generate_token.c index 63074e0a00f..517086bc770 100644 --- a/ui/scenes/generate_token/totp_scene_generate_token.c +++ b/ui/scenes/generate_token/totp_scene_generate_token.c @@ -205,7 +205,10 @@ void totp_scene_generate_token_activate(PluginState* plugin_state) { scene_state->last_code_update_sync = furi_mutex_alloc(FuriMutexTypeNormal); if(plugin_state->automation_method & AutomationMethodBadUsb) { scene_state->usb_type_code_worker_context = totp_usb_type_code_worker_start( - scene_state->last_code, TokenDigitsCountMax + 1, scene_state->last_code_update_sync); + scene_state->last_code, + TokenDigitsCountMax + 1, + scene_state->last_code_update_sync, + plugin_state->automation_kb_layout); } scene_state->active_font = available_fonts[plugin_state->active_font_index]; @@ -221,7 +224,8 @@ void totp_scene_generate_token_activate(PluginState* plugin_state) { plugin_state->bt_type_code_worker_context, scene_state->last_code, TokenDigitsCountMax + 1, - scene_state->last_code_update_sync); + scene_state->last_code_update_sync, + plugin_state->automation_kb_layout); } #endif const TokenInfoIteratorContext* iterator_context = diff --git a/ui/ui_controls.c b/ui/ui_controls.c index d5e86aa581d..28a7d57dae2 100644 --- a/ui/ui_controls.c +++ b/ui/ui_controls.c @@ -112,3 +112,27 @@ void ui_control_button_render( canvas_set_color(canvas, ColorBlack); } } + +void ui_control_vscroll_render( + Canvas* const canvas, + uint8_t x, + uint8_t y, + uint8_t height, + uint8_t position, + uint8_t max_position) { + canvas_draw_line(canvas, x, y, x, y + height); + uint8_t block_height = height / MIN(10, max_position); + uint8_t block_position_y = + height * ((float)position / (float)max_position) - (block_height >> 1); + uint8_t block_position_y_abs = y + block_position_y; + if(block_position_y_abs + block_height > height) { + block_position_y_abs = height - block_height; + } + + canvas_draw_box( + canvas, + x - (UI_CONTROL_VSCROLL_WIDTH >> 1), + block_position_y_abs, + UI_CONTROL_VSCROLL_WIDTH, + block_height); +} diff --git a/ui/ui_controls.h b/ui/ui_controls.h index b97006a03c5..ccee4edfca2 100644 --- a/ui/ui_controls.h +++ b/ui/ui_controls.h @@ -3,6 +3,8 @@ #include #include +#define UI_CONTROL_VSCROLL_WIDTH (3) + /** * @brief Renders TextBox control * @param canvas canvas to render control at @@ -51,3 +53,20 @@ void ui_control_select_render( uint8_t width, const char* text, bool is_selected); + +/** + * @brief Renders vertical scroll bar + * @param canvas canvas to render control at + * @param x horizontal position of a control to be rendered at + * @param y vertical position of a control to be rendered at + * @param height control height + * @param position current position + * @param max_position maximal position + */ +void ui_control_vscroll_render( + Canvas* const canvas, + uint8_t x, + uint8_t y, + uint8_t height, + uint8_t position, + uint8_t max_position); diff --git a/workers/bt_type_code/bt_type_code.c b/workers/bt_type_code/bt_type_code.c index a27467ed697..97a10b0d5e0 100644 --- a/workers/bt_type_code/bt_type_code.c +++ b/workers/bt_type_code/bt_type_code.c @@ -33,6 +33,7 @@ struct TotpBtTypeCodeWorkerContext { char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN]; uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; #endif + AutomationKeyboardLayout keyboard_layout; }; static inline bool totp_type_code_worker_stop_requested() { @@ -73,7 +74,8 @@ static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context &furi_hal_bt_hid_kb_release, context->code_buffer, context->code_buffer_size, - context->flags); + context->flags, + context->keyboard_layout); furi_mutex_release(context->code_buffer_sync); } } @@ -119,11 +121,13 @@ void totp_bt_type_code_worker_start( TotpBtTypeCodeWorkerContext* context, char* code_buffer, uint8_t code_buffer_size, - FuriMutex* code_buffer_sync) { + FuriMutex* code_buffer_sync, + AutomationKeyboardLayout keyboard_layout) { furi_check(context != NULL); context->code_buffer = code_buffer; context->code_buffer_size = code_buffer_size; context->code_buffer_sync = code_buffer_sync; + context->keyboard_layout = keyboard_layout; context->thread = furi_thread_alloc(); furi_thread_set_name(context->thread, "TOTPBtHidWorker"); furi_thread_set_stack_size(context->thread, 1024); diff --git a/workers/bt_type_code/bt_type_code.h b/workers/bt_type_code/bt_type_code.h index 85016592e3e..1b9db9bee97 100644 --- a/workers/bt_type_code/bt_type_code.h +++ b/workers/bt_type_code/bt_type_code.h @@ -3,6 +3,7 @@ #include #include #include +#include "../../types/automation_kb_layout.h" typedef uint8_t TotpBtTypeCodeWorkerEvent; @@ -47,12 +48,14 @@ void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context); * @param code_buffer code buffer to be used to automate * @param code_buffer_size code buffer size * @param code_buffer_sync code buffer synchronization primitive + * @param keyboard_layout keyboard layout to be used */ void totp_bt_type_code_worker_start( TotpBtTypeCodeWorkerContext* context, char* code_buffer, uint8_t code_buffer_size, - FuriMutex* code_buffer_sync); + FuriMutex* code_buffer_sync, + AutomationKeyboardLayout keyboard_layout); /** * @brief Stops bluetooth token input automation worker diff --git a/workers/type_code_common.c b/workers/type_code_common.c index 82a5a028e40..122c0b2a513 100644 --- a/workers/type_code_common.c +++ b/workers/type_code_common.c @@ -3,7 +3,9 @@ #include #include "../../services/convert/convert.h" -static const uint8_t hid_number_keys[] = { +#define HID_KEYS_MAP_LENGTH (36) + +static const uint8_t hid_qwerty_keys_map[HID_KEYS_MAP_LENGTH] = { HID_KEYBOARD_0, HID_KEYBOARD_1, HID_KEYBOARD_2, HID_KEYBOARD_3, HID_KEYBOARD_4, HID_KEYBOARD_5, HID_KEYBOARD_6, HID_KEYBOARD_7, HID_KEYBOARD_8, HID_KEYBOARD_9, HID_KEYBOARD_A, HID_KEYBOARD_B, HID_KEYBOARD_C, HID_KEYBOARD_D, HID_KEYBOARD_E, @@ -13,6 +15,16 @@ static const uint8_t hid_number_keys[] = { HID_KEYBOARD_U, HID_KEYBOARD_V, HID_KEYBOARD_W, HID_KEYBOARD_X, HID_KEYBOARD_Y, HID_KEYBOARD_Z}; +static const uint8_t hid_azerty_keys_map[HID_KEYS_MAP_LENGTH] = { + HID_KEYBOARD_0, HID_KEYBOARD_1, HID_KEYBOARD_2, HID_KEYBOARD_3, HID_KEYBOARD_4, + HID_KEYBOARD_5, HID_KEYBOARD_6, HID_KEYBOARD_7, HID_KEYBOARD_8, HID_KEYBOARD_9, + HID_KEYBOARD_Q, HID_KEYBOARD_B, HID_KEYBOARD_C, HID_KEYBOARD_D, HID_KEYBOARD_E, + HID_KEYBOARD_F, HID_KEYBOARD_G, HID_KEYBOARD_H, HID_KEYBOARD_I, HID_KEYBOARD_J, + HID_KEYBOARD_K, HID_KEYBOARD_L, HID_KEYBOARD_SEMICOLON, HID_KEYBOARD_N, HID_KEYBOARD_O, + HID_KEYBOARD_P, HID_KEYBOARD_A, HID_KEYBOARD_R, HID_KEYBOARD_S, HID_KEYBOARD_T, + HID_KEYBOARD_U, HID_KEYBOARD_V, HID_KEYBOARD_Z, HID_KEYBOARD_X, HID_KEYBOARD_Y, + HID_KEYBOARD_W}; + static uint32_t get_keystroke_delay(TokenAutomationFeature features) { if(features & TokenAutomationFeatureTypeSlower) { return 100; @@ -44,21 +56,38 @@ void totp_type_code_worker_execute_automation( TOTP_AUTOMATION_KEY_HANDLER key_release_fn, const char* code_buffer, uint8_t code_buffer_size, - TokenAutomationFeature features) { + TokenAutomationFeature features, + AutomationKeyboardLayout keyboard_layout) { furi_delay_ms(500); uint8_t i = 0; char cb_char; + const uint8_t* keyboard_layout_dict; + switch(keyboard_layout) { + case AutomationKeyboardLayoutQWERTY: + keyboard_layout_dict = &hid_qwerty_keys_map[0]; + break; + case AutomationKeyboardLayoutAZERTY: + keyboard_layout_dict = &hid_azerty_keys_map[0]; + break; + + default: + return; + } + while(i < code_buffer_size && (cb_char = code_buffer[i]) != 0) { uint8_t char_index = CONVERT_CHAR_TO_DIGIT(cb_char); if(char_index > 9) { char_index = cb_char - 'A' + 10; } - if(char_index >= sizeof(hid_number_keys)) break; + if(char_index >= HID_KEYS_MAP_LENGTH) break; - uint16_t hid_kb_key = hid_number_keys[char_index]; - if(char_index > 9) { + uint16_t hid_kb_key = keyboard_layout_dict[char_index]; + + // For non-AZERTY press shift for all non-digit chars + // For AZERTY press shift for all characters + if(char_index > 9 || keyboard_layout == AutomationKeyboardLayoutAZERTY) { hid_kb_key |= KEY_MOD_LEFT_SHIFT; } diff --git a/workers/type_code_common.h b/workers/type_code_common.h index db357329a89..81b273c367d 100644 --- a/workers/type_code_common.h +++ b/workers/type_code_common.h @@ -1,6 +1,7 @@ #pragma once #include #include "../types/token_info.h" +#include "../types/automation_kb_layout.h" typedef bool (*TOTP_AUTOMATION_KEY_HANDLER)(uint16_t key); @@ -11,10 +12,12 @@ typedef bool (*TOTP_AUTOMATION_KEY_HANDLER)(uint16_t key); * @param code_buffer code buffer to be typed * @param code_buffer_size code buffer size * @param features automation features + * @param keyboard_layout keyboard layout to be used */ void totp_type_code_worker_execute_automation( TOTP_AUTOMATION_KEY_HANDLER key_press_fn, TOTP_AUTOMATION_KEY_HANDLER key_release_fn, const char* code_buffer, uint8_t code_buffer_size, - TokenAutomationFeature features); \ No newline at end of file + TokenAutomationFeature features, + AutomationKeyboardLayout keyboard_layout); \ No newline at end of file diff --git a/workers/usb_type_code/usb_type_code.c b/workers/usb_type_code/usb_type_code.c index a391bdf8275..4e3259424d7 100644 --- a/workers/usb_type_code/usb_type_code.c +++ b/workers/usb_type_code/usb_type_code.c @@ -15,6 +15,7 @@ struct TotpUsbTypeCodeWorkerContext { FuriThread* thread; FuriMutex* code_buffer_sync; FuriHalUsbInterface* usb_mode_prev; + AutomationKeyboardLayout keyboard_layout; }; static void totp_type_code_worker_restore_usb_mode(TotpUsbTypeCodeWorkerContext* context) { @@ -45,7 +46,8 @@ static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* contex &furi_hal_hid_kb_release, context->code_buffer, context->code_buffer_size, - context->flags); + context->flags, + context->keyboard_layout); furi_mutex_release(context->code_buffer_sync); furi_delay_ms(100); @@ -83,7 +85,8 @@ static int32_t totp_type_code_worker_callback(void* context) { TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( char* code_buffer, uint8_t code_buffer_size, - FuriMutex* code_buffer_sync) { + FuriMutex* code_buffer_sync, + AutomationKeyboardLayout keyboard_layout) { TotpUsbTypeCodeWorkerContext* context = malloc(sizeof(TotpUsbTypeCodeWorkerContext)); furi_check(context != NULL); context->code_buffer = code_buffer; @@ -91,6 +94,7 @@ TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( context->code_buffer_sync = code_buffer_sync; context->thread = furi_thread_alloc(); context->usb_mode_prev = NULL; + context->keyboard_layout = keyboard_layout; furi_thread_set_name(context->thread, "TOTPUsbHidWorker"); furi_thread_set_stack_size(context->thread, 1024); furi_thread_set_context(context->thread, context); diff --git a/workers/usb_type_code/usb_type_code.h b/workers/usb_type_code/usb_type_code.h index 0a700e7fea9..d2d1bdf82bb 100644 --- a/workers/usb_type_code/usb_type_code.h +++ b/workers/usb_type_code/usb_type_code.h @@ -3,6 +3,7 @@ #include #include #include +#include "../../types/automation_kb_layout.h" typedef uint8_t TotpUsbTypeCodeWorkerEvent; @@ -34,12 +35,14 @@ enum TotpUsbTypeCodeWorkerEvents { * @param code_buffer code buffer to be used to automate * @param code_buffer_size code buffer size * @param code_buffer_sync code buffer synchronization primitive + * @param keyboard_layout keyboard layout to be used * @return worker context */ TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( char* code_buffer, uint8_t code_buffer_size, - FuriMutex* code_buffer_sync); + FuriMutex* code_buffer_sync, + AutomationKeyboardLayout keyboard_layout); /** * @brief Stops USB token input automation worker