Browse Source

Merge totp from https://github.com/akopachov/flipper-zero_authenticator

WillyJL 8 months ago
parent
commit
84ef69906b

+ 1 - 1
totp/.gitsubtree

@@ -1,2 +1,2 @@
 https://github.com/xMasterX/all-the-plugins dev base_pack/totp 8b9d3e75faa8fbd1ed251476f2e7b410dd99a285
-https://github.com/akopachov/flipper-zero_authenticator master totp 8099f41067f628fce98123f56d2c853e9d250e52
+https://github.com/akopachov/flipper-zero_authenticator master totp 753e953baaaacad67e9054ef62177ef36a8f5f7e

+ 4 - 0
totp/.ofwcatalog/CHANGELOG.md

@@ -1,5 +1,9 @@
 # Changelog
 
+## v5.19.0 - May 7 2025
+
+* feat: space between groups of digits ([#259](https://github.com/akopachov/flipper-zero_authenticator/issues/259))
+
 ## v5.18.0 - Apr 29 2025
 
 * fix: tokens longer than 64 bytes are leading to incorrect codes ([#260](https://github.com/akopachov/flipper-zero_authenticator/issues/260))

+ 1 - 1
totp/application.fam

@@ -7,7 +7,7 @@ App(
     requires=["gui", "cli", "dialogs", "storage", "input", "notification", "bt"],
     stack_size=2 * 1024,
     order=20,
-    fap_version="5.180",
+    fap_version="5.190",
     fap_author="Alexander Kopachov (@akopachov)",
     fap_description="Software-based TOTP/HOTP authenticator for Flipper Zero device",
     fap_weburl="https://github.com/akopachov/flipper-zero_authenticator",

+ 19 - 0
totp/services/config/config.c

@@ -190,6 +190,8 @@ static bool totp_open_config_file(Storage* storage, FlipperFormat** file) {
 
         tmp_uint32 = 0; //-V1048
         flipper_format_write_uint32(fff_data_file, TOTP_CONFIG_KEY_FONT, &tmp_uint32, 1);
+        flipper_format_write_uint32(
+            fff_data_file, TOTP_CONFIG_KEY_UI_TOKEN_DIGIT_GROUPING, &tmp_uint32, 1);
 
         if(!flipper_format_rewind(fff_data_file)) {
             totp_close_config_file(fff_data_file);
@@ -339,6 +341,12 @@ bool totp_config_file_update_user_settings(const PluginState* plugin_state) {
             break;
         }
 
+        tmp_uint32 = plugin_state->split_token_into_groups;
+        if(!flipper_format_insert_or_update_uint32(
+               file, TOTP_CONFIG_KEY_UI_TOKEN_DIGIT_GROUPING, &tmp_uint32, 1)) {
+            break;
+        }
+
         update_result = true;
     } while(false);
 
@@ -569,6 +577,17 @@ bool totp_config_file_load(PluginState* const plugin_state) {
 
         plugin_state->active_font_index = tmp_uint32;
 
+        if(!flipper_format_rewind(fff_data_file)) {
+            break;
+        }
+
+        if(!flipper_format_read_uint32(
+               fff_data_file, TOTP_CONFIG_KEY_UI_TOKEN_DIGIT_GROUPING, &tmp_uint32, 1)) {
+            tmp_uint32 = 0;
+        }
+
+        plugin_state->split_token_into_groups = tmp_uint32;
+
         plugin_state->config_file_context = malloc(sizeof(ConfigFileContext));
         furi_check(plugin_state->config_file_context != NULL);
         plugin_state->config_file_context->storage = storage;

+ 1 - 0
totp/services/config/constants.h

@@ -27,6 +27,7 @@
 #endif
 #define TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT "AutomationKbLayout"
 #define TOTP_CONFIG_KEY_AUTOMATION_INITIAL_DELAY "AutomationInitialDelay"
+#define TOTP_CONFIG_KEY_UI_TOKEN_DIGIT_GROUPING "UITokenDigitGrouping"
 #define TOTP_CONFIG_KEY_FONT "Font"
 #define TOTP_CONFIG_KEY_CRYPTO_VERSION "CryptoVersion"
 #define TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT "CryptoKeySlot"

+ 5 - 0
totp/types/plugin_state.h

@@ -100,4 +100,9 @@ typedef struct {
      * @brief Crypto settings
      */
     CryptoSettings crypto_settings;
+
+    /**
+     * @brief Indicates the amount of groups to split the token into in UI. 0 if no grouping is needed.
+     */
+    uint8_t split_token_into_groups;
 } PluginState;

+ 63 - 9
totp/ui/scenes/app_settings/totp_app_settings.c

@@ -21,6 +21,9 @@
 #define FONT_TEST_STR_LENGTH (7)
 
 static const char* YES_NO_LIST[] = {"NO", "YES"};
+static const char* TOKEN_GROUPING_LIST[] = {"NO", "2", "3"};
+static const uint8_t TOKEN_GROUPING_LIST_VALUES[] = {0, 2, 3};
+static const uint8_t TOKEN_GROUPING_LIST_VALUES_INDEXES[] = {0, 0, 1, 2};
 static const char* AUTOMATION_LIST[] = {
     "None",
     "USB"
@@ -42,6 +45,7 @@ typedef enum {
     BadKeyboardLayoutSelect,
     AutomationDelaySelect,
     BadBtProfileSelect,
+    SplitTokenIntoGroupsSelect,
     ConfirmButton
 } Control;
 
@@ -64,6 +68,7 @@ typedef struct {
     uint8_t badbt_profile_index;
 #endif
     char automation_initial_delay_formatted[10];
+    uint8_t split_token_into_groups;
 } SceneState;
 
 static void two_digit_to_str(int8_t num, char* str) {
@@ -126,6 +131,9 @@ void totp_scene_app_settings_activate(PluginState* plugin_state) {
 
     update_formatted_automation_initial_delay(scene_state);
     update_formatted_automation_kb_layout_name(scene_state);
+
+    scene_state->split_token_into_groups =
+        TOKEN_GROUPING_LIST_VALUES_INDEXES[plugin_state->split_token_into_groups];
 }
 
 void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plugin_state) {
@@ -206,7 +214,7 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu
             SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH,
             YES_NO_LIST[scene_state->notification_vibro],
             scene_state->selected_control == VibroSwitch);
-    } else {
+    } else if(scene_state->selected_control < SplitTokenIntoGroupsSelect) {
         canvas_set_font(canvas, FontPrimary);
         canvas_draw_str_aligned(
             canvas, 0, 192 - scene_state->y_offset, AlignLeft, AlignTop, "Automation");
@@ -291,16 +299,26 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu
                 scene_state->selected_control == BadBtProfileSelect);
         }
 #endif
+    } else {
+        canvas_set_font(canvas, FontPrimary);
+        canvas_draw_str_aligned(canvas, 0, 256 - scene_state->y_offset, AlignLeft, AlignTop, "UI");
+        canvas_set_font(canvas, FontSecondary);
+
+        canvas_draw_str_aligned(
+            canvas, 0, 273 - scene_state->y_offset, AlignLeft, AlignTop, "Digit grouping:");
+
+        ui_control_select_render(
+            canvas,
+            64,
+            266 - scene_state->y_offset,
+            SCREEN_WIDTH - 64 - UI_CONTROL_VSCROLL_WIDTH,
+            TOKEN_GROUPING_LIST[scene_state->split_token_into_groups],
+            scene_state->selected_control == SplitTokenIntoGroupsSelect);
 
         ui_control_button_render(
             canvas,
             SCREEN_WIDTH_CENTER - 24,
-#ifdef TOTP_BADBT_AUTOMATION_ENABLED
-            ((scene_state->automation_method & AutomationMethodBadBt) == 0 ? 260 : 278)
-#else
-            260
-#endif
-                - scene_state->y_offset - group_offset,
+            306 - scene_state->y_offset,
             48,
             13,
             "Confirm",
@@ -334,7 +352,16 @@ bool totp_scene_app_settings_handle_event(
                 scene_state->selected_control--;
             }
 #endif
-            if(scene_state->selected_control > VibroSwitch) {
+
+            if(scene_state->selected_control >
+#ifdef TOTP_BADBT_AUTOMATION_ENABLED
+               BadBtProfileSelect
+#else
+               AutomationDelaySelect
+#endif
+            ) {
+                scene_state->y_offset = SCREEN_HEIGHT * 4;
+            } else if(scene_state->selected_control > VibroSwitch) {
                 scene_state->y_offset = SCREEN_HEIGHT * 3;
             } else if(scene_state->selected_control > FontSelect) {
                 scene_state->y_offset = SCREEN_HEIGHT * 2;
@@ -357,7 +384,16 @@ bool totp_scene_app_settings_handle_event(
                 scene_state->selected_control++;
             }
 #endif
-            if(scene_state->selected_control > VibroSwitch) {
+
+            if(scene_state->selected_control >
+#ifdef TOTP_BADBT_AUTOMATION_ENABLED
+               BadBtProfileSelect
+#else
+               AutomationDelaySelect
+#endif
+            ) {
+                scene_state->y_offset = SCREEN_HEIGHT * 4;
+            } else if(scene_state->selected_control > VibroSwitch) {
                 scene_state->y_offset = SCREEN_HEIGHT * 3;
             } else if(scene_state->selected_control > FontSelect) {
                 scene_state->y_offset = SCREEN_HEIGHT * 2;
@@ -417,6 +453,14 @@ bool totp_scene_app_settings_handle_event(
                     &scene_state->badbt_profile_index, 1, 0, UINT8_MAX, RollOverflowBehaviorRoll);
             }
 #endif
+            else if(scene_state->selected_control == SplitTokenIntoGroupsSelect) {
+                totp_roll_value_uint8_t(
+                    &scene_state->split_token_into_groups,
+                    1,
+                    0,
+                    COUNT_OF(TOKEN_GROUPING_LIST) - 1,
+                    RollOverflowBehaviorRoll);
+            }
             break;
         case InputKeyLeft:
             if(scene_state->selected_control == HoursInput) {
@@ -468,6 +512,14 @@ bool totp_scene_app_settings_handle_event(
                     &scene_state->badbt_profile_index, -1, 0, UINT8_MAX, RollOverflowBehaviorRoll);
             }
 #endif
+            else if(scene_state->selected_control == SplitTokenIntoGroupsSelect) {
+                totp_roll_value_uint8_t(
+                    &scene_state->split_token_into_groups,
+                    -1,
+                    0,
+                    COUNT_OF(TOKEN_GROUPING_LIST) - 1,
+                    RollOverflowBehaviorRoll);
+            }
             break;
         case InputKeyOk:
             if(scene_state->selected_control == ConfirmButton) {
@@ -484,6 +536,8 @@ bool totp_scene_app_settings_handle_event(
                 plugin_state->active_font_index = scene_state->active_font_index;
                 plugin_state->automation_kb_layout = scene_state->automation_kb_layout;
                 plugin_state->automation_initial_delay = scene_state->automation_initial_delay;
+                plugin_state->split_token_into_groups =
+                    TOKEN_GROUPING_LIST_VALUES[scene_state->split_token_into_groups];
 
 #ifdef TOTP_BADBT_AUTOMATION_ENABLED
                 if(((scene_state->automation_method & AutomationMethodBadBt) == 0 ||

+ 41 - 8
totp/ui/scenes/generate_token/totp_scene_generate_token.c

@@ -22,6 +22,7 @@
 
 #define PROGRESS_BAR_MARGIN (3)
 #define PROGRESS_BAR_HEIGHT (4)
+#define DIV_CEIL(a, b) (((a) + (b) - 1) / (b))
 
 typedef struct {
     uint8_t progress_bar_x;
@@ -29,6 +30,7 @@ typedef struct {
     uint8_t code_total_length;
     uint8_t code_offset_x;
     uint8_t code_offset_y;
+    uint8_t group_space_width;
 } UiPrecalculatedDimensions;
 
 typedef struct {
@@ -123,13 +125,31 @@ static void draw_totp_code(Canvas* const canvas, const PluginState* const plugin
         totp_config_get_token_iterator_context(plugin_state);
     uint8_t code_length = totp_token_info_iterator_get_current_token(iterator_context)->digits;
 
-    canvas_draw_str_ex(
-        canvas,
-        scene_state->ui_precalculated_dimensions.code_offset_x,
-        scene_state->ui_precalculated_dimensions.code_offset_y,
-        scene_state->last_code,
-        code_length,
-        scene_state->active_font);
+    if(plugin_state->split_token_into_groups > 0) {
+        uint8_t offset_x = scene_state->ui_precalculated_dimensions.code_offset_x;
+        uint8_t char_width = scene_state->active_font->char_info[0].width;
+        uint8_t group_length = DIV_CEIL(code_length, plugin_state->split_token_into_groups);
+        for(uint8_t i = 0; i < code_length; i += group_length) {
+            canvas_draw_str_ex(
+                canvas,
+                offset_x,
+                scene_state->ui_precalculated_dimensions.code_offset_y,
+                &scene_state->last_code[i],
+                MIN(group_length, code_length - i),
+                scene_state->active_font);
+            offset_x +=
+                (group_length * (char_width + scene_state->active_font->space_width) +
+                 scene_state->ui_precalculated_dimensions.group_space_width);
+        }
+    } else {
+        canvas_draw_str_ex(
+            canvas,
+            scene_state->ui_precalculated_dimensions.code_offset_x,
+            scene_state->ui_precalculated_dimensions.code_offset_y,
+            scene_state->last_code,
+            code_length,
+            scene_state->active_font);
+    }
 }
 
 static void on_new_token_code_generated(bool time_left, void* context) {
@@ -144,10 +164,23 @@ static void on_new_token_code_generated(bool time_left, void* context) {
     const TokenInfo* current_token = totp_token_info_iterator_get_current_token(iterator_context);
 
     uint8_t char_width = scene_state->active_font->char_info[0].width;
+    uint8_t group_spaces =
+        plugin_state->split_token_into_groups > 0 ? plugin_state->split_token_into_groups - 1 : 0;
+    uint8_t group_space_width = char_width;
     scene_state->ui_precalculated_dimensions.code_total_length =
         current_token->digits * (char_width + scene_state->active_font->space_width);
+    uint8_t rest_available_width =
+        SCREEN_WIDTH -
+        MIN(SCREEN_WIDTH, scene_state->ui_precalculated_dimensions.code_total_length);
+    if(group_space_width * group_spaces > rest_available_width) {
+        group_space_width = rest_available_width / group_spaces;
+    }
+    scene_state->ui_precalculated_dimensions.code_total_length += group_space_width * group_spaces;
+    scene_state->ui_precalculated_dimensions.group_space_width = group_space_width;
     scene_state->ui_precalculated_dimensions.code_offset_x =
-        (SCREEN_WIDTH - scene_state->ui_precalculated_dimensions.code_total_length) >> 1;
+        (SCREEN_WIDTH -
+         MIN(SCREEN_WIDTH, scene_state->ui_precalculated_dimensions.code_total_length)) >>
+        1;
     scene_state->ui_precalculated_dimensions.code_offset_y =
         SCREEN_HEIGHT_CENTER - (scene_state->active_font->height >> 1);
 

+ 1 - 1
totp/version.h

@@ -1,5 +1,5 @@
 #pragma once
 
 #define TOTP_APP_VERSION_MAJOR (5)
-#define TOTP_APP_VERSION_MINOR (18)
+#define TOTP_APP_VERSION_MINOR (19)
 #define TOTP_APP_VERSION_PATCH (0)