Просмотр исходного кода

feat: space between groups of digits (#259)

* Initial implementation of #259

* fix: adjust button position and improve token code spacing calculations

* chore: CLang format changes

---------

Co-authored-by: akopachov <1217426+akopachov@users.noreply.github.com>
Alexander Kopachov 8 месяцев назад
Родитель
Сommit
7816472414

+ 19 - 0
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
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
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
ui/scenes/app_settings/totp_app_settings.c

@@ -19,6 +19,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"
@@ -40,6 +43,7 @@ typedef enum {
     BadKeyboardLayoutSelect,
     AutomationDelaySelect,
     BadBtProfileSelect,
+    SplitTokenIntoGroupsSelect,
     ConfirmButton
 } Control;
 
@@ -62,6 +66,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) {
@@ -124,6 +129,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) {
@@ -204,7 +212,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");
@@ -289,16 +297,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",
@@ -332,7 +350,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;
@@ -355,7 +382,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;
@@ -415,6 +451,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) {
@@ -466,6 +510,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) {
@@ -482,6 +534,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
ui/scenes/generate_token/totp_scene_generate_token.c

@@ -20,6 +20,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;
@@ -27,6 +28,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 {
@@ -121,13 +123,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) {
@@ -142,10 +162,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);