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

Added timezone configuration UI

alex.kopachov 3 лет назад
Родитель
Сommit
5ea454ea69

+ 3 - 10
scenes/add_new_token/totp_scene_add_new_token.c

@@ -100,8 +100,8 @@ void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_s
 
 
     ui_control_text_box_render(canvas, 10 - scene_state->screen_y_offset, scene_state->token_name, scene_state->selected_control == TokenNameTextBox);
     ui_control_text_box_render(canvas, 10 - scene_state->screen_y_offset, scene_state->token_name, scene_state->selected_control == TokenNameTextBox);
     ui_control_text_box_render(canvas, 27 - scene_state->screen_y_offset, scene_state->token_secret, scene_state->selected_control == TokenSecretTextBox);
     ui_control_text_box_render(canvas, 27 - scene_state->screen_y_offset, scene_state->token_secret, scene_state->selected_control == TokenSecretTextBox);
-    ui_control_select_render(canvas, 44 - scene_state->screen_y_offset, TOKEN_ALGO_LIST[scene_state->algo], scene_state->selected_control == TokenAlgoSelect);
-    ui_control_select_render(canvas, 63 - scene_state->screen_y_offset, TOKEN_DIGITS_LIST[scene_state->digits_count], scene_state->selected_control == TokenLengthSelect);
+    ui_control_select_render(canvas, 0, 44 - scene_state->screen_y_offset, SCREEN_WIDTH, TOKEN_ALGO_LIST[scene_state->algo], scene_state->selected_control == TokenAlgoSelect);
+    ui_control_select_render(canvas, 0, 63 - scene_state->screen_y_offset, SCREEN_WIDTH, TOKEN_DIGITS_LIST[scene_state->digits_count], scene_state->selected_control == TokenLengthSelect);
     ui_control_button_render(canvas, SCREEN_WIDTH_CENTER - 24, 85 - scene_state->screen_y_offset, 48, 13, "Confirm", scene_state->selected_control == ConfirmButton);
     ui_control_button_render(canvas, SCREEN_WIDTH_CENTER - 24, 85 - scene_state->screen_y_offset, 48, 13, "Confirm", scene_state->selected_control == ConfirmButton);
 
 
     canvas_set_color(canvas, ColorWhite);
     canvas_set_color(canvas, ColorWhite);
@@ -212,14 +212,7 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
                             }
                             }
                             plugin_state->tokens_count++;
                             plugin_state->tokens_count++;
 
 
-                            Storage* cfg_storage = totp_open_storage();
-                            FlipperFormat* cfg_file = totp_open_config_file(cfg_storage);
-
-                            flipper_format_seek_to_end(cfg_file);
-                            totp_config_file_save_new_token(cfg_file, tokenInfo);
-
-                            totp_close_config_file(cfg_file);
-                            totp_close_storage();
+                            totp_config_file_save_new_token(tokenInfo);
 
 
                             GenerateTokenSceneContext generate_scene_context = { .current_token_index = plugin_state->tokens_count - 1 };
                             GenerateTokenSceneContext generate_scene_context = { .current_token_index = plugin_state->tokens_count - 1 };
                             totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, &generate_scene_context);
                             totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, &generate_scene_context);

+ 154 - 0
scenes/app_settings/totp_app_settings.c

@@ -0,0 +1,154 @@
+#include "totp_app_settings.h"
+#include "../../services/ui/ui_controls.h"
+#include "../scene_director.h"
+#include "../token_menu/totp_scene_token_menu.h"
+#include "../../services/ui/constants.h"
+#include "../../services/config/config.h"
+
+#define DIGIT_TO_CHAR(digit) ((digit) + '0')
+
+typedef enum {
+    HoursInput,
+    MinutesInput,
+    ConfirmButton
+} Control;
+
+typedef struct {
+    int8_t tz_offset_hours;
+    uint8_t tz_offset_minutes;
+    int16_t current_token_index;
+    Control selected_control;
+} SceneState;
+
+void totp_scene_app_settings_init(PluginState* plugin_state) {
+    UNUSED(plugin_state);
+}
+
+void totp_scene_app_settings_activate(PluginState* plugin_state, const AppSettingsSceneContext* context) {
+    SceneState* scene_state = malloc(sizeof(SceneState));
+    plugin_state->current_scene_state = scene_state;
+    if (context != NULL) {
+        scene_state->current_token_index = context->current_token_index;
+    } else {
+        scene_state->current_token_index = -1;
+    }
+
+    float off_int;
+    float off_dec = modff(plugin_state->timezone_offset, &off_int);
+    scene_state->tz_offset_hours = off_int;
+    scene_state->tz_offset_minutes = 60.0f * off_dec;
+}
+
+static void two_digit_to_str(int8_t num, char* str) {
+    uint8_t index = 0;
+    if (num < 0) {
+        str[0] = '-';
+        index++;
+        num = -num;
+    }
+
+    uint8_t d1 = (num / 10) % 10;
+    uint8_t d2 = num % 10;
+    str[index] = DIGIT_TO_CHAR(d1);
+    str[index + 1] = DIGIT_TO_CHAR(d2);
+    str[index + 2] = '\0';
+}
+
+void totp_scene_app_settings_render(Canvas* const canvas, PluginState* plugin_state) {
+    SceneState* scene_state = (SceneState *)plugin_state->current_scene_state;
+
+    canvas_set_font(canvas, FontPrimary);
+    canvas_draw_str_aligned(canvas, 0, 0, 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, 16, AlignLeft, AlignTop, "Hours:");
+    ui_control_select_render(canvas, 36, 10, 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, 34, AlignLeft, AlignTop, "Minutes:");
+    ui_control_select_render(canvas, 36, 28, SCREEN_WIDTH - 36, &tmp_str[0], scene_state->selected_control == MinutesInput);
+
+    ui_control_button_render(canvas, SCREEN_WIDTH_CENTER - 24, 50, 48, 13, "Confirm", scene_state->selected_control == ConfirmButton);
+}
+
+bool totp_scene_app_settings_handle_event(PluginEvent* const event, PluginState* plugin_state) {
+    if (event->type == EventTypeKey) {
+        SceneState* scene_state = (SceneState *)plugin_state->current_scene_state;
+        if(event->input.type == InputTypePress) {
+            switch(event->input.key) {
+                case InputKeyUp:
+                    if (scene_state->selected_control > HoursInput) {
+                        scene_state->selected_control--;
+                    }
+                    break;
+                case InputKeyDown:
+                    if (scene_state->selected_control < ConfirmButton) {
+                        scene_state->selected_control++;
+                    }
+                    break;
+                case InputKeyRight:
+                    if (scene_state->selected_control == HoursInput) {
+                        if (scene_state->tz_offset_hours < 12) {
+                            scene_state->tz_offset_hours++;
+                        }
+                    } else if (scene_state->selected_control == MinutesInput) {
+                        if (scene_state->tz_offset_minutes < 45) {
+                            scene_state->tz_offset_minutes += 15;
+                        } else {
+                            scene_state->tz_offset_minutes = 0;
+                        }
+                    }
+                    break;
+                case InputKeyLeft:
+                    if (scene_state->selected_control == HoursInput) {
+                        if (scene_state->tz_offset_hours > -12) {
+                            scene_state->tz_offset_hours--;
+                        }
+                    } else if (scene_state->selected_control == MinutesInput) {
+                        if (scene_state->tz_offset_minutes >= 15) {
+                            scene_state->tz_offset_minutes -= 15;
+                        } else {
+                            scene_state->tz_offset_minutes = 45;
+                        }
+                    }
+                    break;
+                case InputKeyOk:
+                    if (scene_state->selected_control == ConfirmButton) {
+                        plugin_state->timezone_offset = (float)scene_state->tz_offset_hours + (float)scene_state->tz_offset_minutes / 60.0f;
+                        totp_config_file_update_timezone_offset(plugin_state->timezone_offset);
+
+                        if (scene_state->current_token_index >= 0) {
+                            TokenMenuSceneContext generate_scene_context = { .current_token_index = scene_state->current_token_index };
+                            totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, &generate_scene_context);
+                        } else {
+                            totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL);
+                        }
+                    }
+                    break;
+                case InputKeyBack: {
+                    if (scene_state->current_token_index >= 0) {
+                        TokenMenuSceneContext generate_scene_context = { .current_token_index = scene_state->current_token_index };
+                        totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, &generate_scene_context);
+                    } else {
+                        totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL);
+                    }
+                    break;
+                }
+            }
+        }
+    }
+    return true;
+}
+
+void totp_scene_app_settings_deactivate(PluginState* plugin_state) {
+    if (plugin_state->current_scene_state == NULL) return;
+    
+    free(plugin_state->current_scene_state);
+    plugin_state->current_scene_state = NULL;
+}
+
+void totp_scene_app_settings_free(PluginState* plugin_state) {
+    UNUSED(plugin_state);
+}

+ 18 - 0
scenes/app_settings/totp_app_settings.h

@@ -0,0 +1,18 @@
+#pragma once
+
+#include <gui/gui.h>
+#include <furi.h>
+#include <furi_hal.h>
+#include "../../types/plugin_state.h"
+#include "../../types/plugin_event.h"
+
+typedef struct {
+    uint8_t current_token_index;
+} AppSettingsSceneContext;
+
+void totp_scene_app_settings_init(PluginState* plugin_state);
+void totp_scene_app_settings_activate(PluginState* plugin_state, const AppSettingsSceneContext* context);
+void totp_scene_app_settings_render(Canvas* const canvas, PluginState* plugin_state);
+bool totp_scene_app_settings_handle_event(PluginEvent* const event, PluginState* plugin_state);
+void totp_scene_app_settings_deactivate(PluginState* plugin_state);
+void totp_scene_app_settings_free(PluginState* plugin_state);

+ 1 - 1
scenes/generate_token/totp_scene_generate_token.c

@@ -211,7 +211,7 @@ bool totp_scene_generate_token_handle_event(PluginEvent* const event, PluginStat
                     break;
                     break;
                 case InputKeyOk:
                 case InputKeyOk:
                     if (plugin_state->tokens_count == 0) {
                     if (plugin_state->tokens_count == 0) {
-                        totp_scene_director_activate_scene(plugin_state, TotpSceneAddNewToken, NULL);
+                        totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL);
                     } else {
                     } else {
                         TokenMenuSceneContext ctx = { .current_token_index = scene_state->current_token_index };
                         TokenMenuSceneContext ctx = { .current_token_index = scene_state->current_token_index };
                         totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, &ctx);
                         totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, &ctx);

+ 15 - 0
scenes/scene_director.c

@@ -4,6 +4,7 @@
 #include "generate_token/totp_scene_generate_token.h"
 #include "generate_token/totp_scene_generate_token.h"
 #include "add_new_token/totp_scene_add_new_token.h"
 #include "add_new_token/totp_scene_add_new_token.h"
 #include "token_menu/totp_scene_token_menu.h"
 #include "token_menu/totp_scene_token_menu.h"
+#include "app_settings/totp_app_settings.h"
 
 
 void totp_scene_director_activate_scene(PluginState* const plugin_state, Scene scene, const void* context) {   
 void totp_scene_director_activate_scene(PluginState* const plugin_state, Scene scene, const void* context) {   
     plugin_state->changing_scene = true;
     plugin_state->changing_scene = true;
@@ -21,6 +22,9 @@ void totp_scene_director_activate_scene(PluginState* const plugin_state, Scene s
         case TotpSceneTokenMenu:
         case TotpSceneTokenMenu:
             totp_scene_token_menu_activate(plugin_state, context);
             totp_scene_token_menu_activate(plugin_state, context);
             break;
             break;
+        case TotpSceneAppSettings:
+            totp_scene_app_settings_activate(plugin_state, context);
+            break;
     }
     }
 
 
     plugin_state->current_scene = scene;
     plugin_state->current_scene = scene;
@@ -41,6 +45,9 @@ void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state
         case TotpSceneTokenMenu:
         case TotpSceneTokenMenu:
             totp_scene_token_menu_deactivate(plugin_state);
             totp_scene_token_menu_deactivate(plugin_state);
             break;
             break;
+        case TotpSceneAppSettings:
+            totp_scene_app_settings_deactivate(plugin_state);
+            break;
     }
     }
 }
 }
 
 
@@ -49,6 +56,7 @@ void totp_scene_director_init_scenes(PluginState* const plugin_state) {
     totp_scene_generate_token_init(plugin_state);
     totp_scene_generate_token_init(plugin_state);
     totp_scene_add_new_token_init(plugin_state);
     totp_scene_add_new_token_init(plugin_state);
     totp_scene_token_menu_init(plugin_state);
     totp_scene_token_menu_init(plugin_state);
+    totp_scene_app_settings_init(plugin_state);
 }
 }
 
 
 void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_state) {
 void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_state) {
@@ -65,6 +73,9 @@ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_
         case TotpSceneTokenMenu: 
         case TotpSceneTokenMenu: 
             totp_scene_token_menu_render(canvas, plugin_state);
             totp_scene_token_menu_render(canvas, plugin_state);
             break;
             break;
+        case TotpSceneAppSettings: 
+            totp_scene_app_settings_render(canvas, plugin_state);
+            break;
     }
     }
 }
 }
 
 
@@ -73,6 +84,7 @@ void totp_scene_director_dispose(PluginState* const plugin_state) {
     totp_scene_authenticate_free(plugin_state);
     totp_scene_authenticate_free(plugin_state);
     totp_scene_add_new_token_free(plugin_state);
     totp_scene_add_new_token_free(plugin_state);
     totp_scene_token_menu_free(plugin_state);
     totp_scene_token_menu_free(plugin_state);
+    totp_scene_app_settings_free(plugin_state);
 }
 }
 
 
 bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* const plugin_state) {
 bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* const plugin_state) {
@@ -90,6 +102,9 @@ bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* con
         case TotpSceneTokenMenu:
         case TotpSceneTokenMenu:
             processing = totp_scene_token_menu_handle_event(event, plugin_state);
             processing = totp_scene_token_menu_handle_event(event, plugin_state);
             break;
             break;
+        case TotpSceneAppSettings:
+            processing = totp_scene_app_settings_handle_event(event, plugin_state);
+            break;
     }
     }
 
 
     return processing;
     return processing;

+ 46 - 8
scenes/token_menu/totp_scene_token_menu.c

@@ -9,15 +9,20 @@
 #include "../../types/token_info.h"
 #include "../../types/token_info.h"
 #include "../generate_token/totp_scene_generate_token.h"
 #include "../generate_token/totp_scene_generate_token.h"
 #include "../add_new_token/totp_scene_add_new_token.h"
 #include "../add_new_token/totp_scene_add_new_token.h"
+#include "../app_settings/totp_app_settings.h"
+
+#define SCREEN_HEIGHT_THIRD (SCREEN_HEIGHT / 3)
+#define SCREEN_HEIGHT_THIRD_CENTER (SCREEN_HEIGHT_THIRD >> 1)
 
 
 typedef enum {
 typedef enum {
     AddNewToken,
     AddNewToken,
-    DeleteToken
+    DeleteToken,
+    AppSettings
 } Control;
 } Control;
 
 
 typedef struct {
 typedef struct {
     Control selected_control;
     Control selected_control;
-    uint8_t current_token_index;
+    int16_t current_token_index;
 } SceneState;
 } SceneState;
 
 
 void totp_scene_token_menu_init(PluginState* plugin_state) {
 void totp_scene_token_menu_init(PluginState* plugin_state) {
@@ -27,13 +32,23 @@ void totp_scene_token_menu_init(PluginState* plugin_state) {
 void totp_scene_token_menu_activate(PluginState* plugin_state, const TokenMenuSceneContext* context) {
 void totp_scene_token_menu_activate(PluginState* plugin_state, const TokenMenuSceneContext* context) {
     SceneState* scene_state = malloc(sizeof(SceneState));
     SceneState* scene_state = malloc(sizeof(SceneState));
     plugin_state->current_scene_state = scene_state;
     plugin_state->current_scene_state = scene_state;
-    scene_state->current_token_index = context->current_token_index;
+    if (context != NULL) {
+        scene_state->current_token_index = context->current_token_index;
+    } else {
+        scene_state->current_token_index = -1;
+    }
 }
 }
 
 
 void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state) {
 void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state) {
     SceneState* scene_state = (SceneState *)plugin_state->current_scene_state;
     SceneState* scene_state = (SceneState *)plugin_state->current_scene_state;
-    ui_control_button_render(canvas, SCREEN_WIDTH_CENTER - 36, 5, 72, 21, "Add new token", scene_state->selected_control == AddNewToken);
-    ui_control_button_render(canvas, SCREEN_WIDTH_CENTER - 36, 39, 72, 21, "Delete token", scene_state->selected_control == DeleteToken);
+    if (scene_state->current_token_index < 0) {
+        ui_control_button_render(canvas, SCREEN_WIDTH_CENTER - 36, 5, 72, 21, "Add new token", scene_state->selected_control == AddNewToken);
+        ui_control_button_render(canvas, SCREEN_WIDTH_CENTER - 36, 39, 72, 21, "Settings", scene_state->selected_control == AppSettings);
+    } else {
+        ui_control_button_render(canvas, SCREEN_WIDTH_CENTER - 36, SCREEN_HEIGHT_THIRD_CENTER - 8, 72, 16, "Add new token", scene_state->selected_control == AddNewToken);
+        ui_control_button_render(canvas, SCREEN_WIDTH_CENTER - 36, SCREEN_HEIGHT_THIRD + SCREEN_HEIGHT_THIRD_CENTER - 8, 72, 16, "Delete token", scene_state->selected_control == DeleteToken);
+        ui_control_button_render(canvas, SCREEN_WIDTH_CENTER - 36, SCREEN_HEIGHT_THIRD + SCREEN_HEIGHT_THIRD + SCREEN_HEIGHT_THIRD_CENTER - 8, 72, 16, "Settings", scene_state->selected_control == AppSettings);
+    }
 }
 }
 
 
 bool totp_scene_token_menu_handle_event(PluginEvent* const event, PluginState* plugin_state) {
 bool totp_scene_token_menu_handle_event(PluginEvent* const event, PluginState* plugin_state) {
@@ -44,11 +59,21 @@ bool totp_scene_token_menu_handle_event(PluginEvent* const event, PluginState* p
                 case InputKeyUp:
                 case InputKeyUp:
                     if (scene_state->selected_control > AddNewToken) {
                     if (scene_state->selected_control > AddNewToken) {
                         scene_state->selected_control--;
                         scene_state->selected_control--;
+                        if (scene_state->selected_control == DeleteToken && scene_state->current_token_index < 0) {
+                            scene_state->selected_control--;
+                        }
+                    } else {
+                        scene_state->selected_control = AppSettings;
                     }
                     }
                     break;
                     break;
                 case InputKeyDown:
                 case InputKeyDown:
-                    if (scene_state->selected_control < DeleteToken) {
+                    if (scene_state->selected_control < AppSettings) {
                         scene_state->selected_control++;
                         scene_state->selected_control++;
+                        if (scene_state->selected_control == DeleteToken && scene_state->current_token_index < 0) {
+                            scene_state->selected_control++;
+                        }
+                    } else {
+                        scene_state->selected_control = AddNewToken;
                     }
                     }
                     break;
                     break;
                 case InputKeyRight:
                 case InputKeyRight:
@@ -88,11 +113,24 @@ bool totp_scene_token_menu_handle_event(PluginEvent* const event, PluginState* p
                             }
                             }
                             break;
                             break;
                         }
                         }
+                        case AppSettings: {
+                            if (scene_state->current_token_index >= 0) {
+                                AppSettingsSceneContext app_settings_context = { .current_token_index = scene_state->current_token_index };
+                                totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings, &app_settings_context);
+                            } else {
+                                totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings, NULL);
+                            }
+                            break;
+                        }
                     }
                     }
                     break;
                     break;
                 case InputKeyBack: {
                 case InputKeyBack: {
-                    GenerateTokenSceneContext generate_scene_context = { .current_token_index = scene_state->current_token_index };
-                    totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, &generate_scene_context);
+                    if (scene_state->current_token_index >= 0) {
+                        GenerateTokenSceneContext generate_scene_context = { .current_token_index = scene_state->current_token_index };
+                        totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, &generate_scene_context);
+                    } else {
+                        totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL);
+                    }
                     break;
                     break;
                 }
                 }
             }
             }

+ 2 - 1
scenes/totp_scenes_enum.h

@@ -4,5 +4,6 @@ typedef enum {
     TotpSceneAuthentication,
     TotpSceneAuthentication,
     TotpSceneGenerateToken,
     TotpSceneGenerateToken,
     TotpSceneAddNewToken,
     TotpSceneAddNewToken,
-    TotpSceneTokenMenu
+    TotpSceneTokenMenu,
+    TotpSceneAppSettings
 } Scene;
 } Scene;

+ 23 - 2
services/config/config.c

@@ -129,7 +129,8 @@ FlipperFormat* totp_open_config_file(Storage* storage) {
     return fff_data_file;
     return fff_data_file;
 }
 }
 
 
-void totp_config_file_save_new_token(FlipperFormat* file, TokenInfo* token_info) {
+void totp_config_file_save_new_token_i(FlipperFormat* file, TokenInfo* token_info) {
+    flipper_format_seek_to_end(file);
     flipper_format_write_string_cstr(file, TOTP_CONFIG_KEY_TOKEN_NAME, token_info->name);
     flipper_format_write_string_cstr(file, TOTP_CONFIG_KEY_TOKEN_NAME, token_info->name);
     flipper_format_write_hex(file, TOTP_CONFIG_KEY_TOKEN_SECRET, token_info->token, token_info->token_length);
     flipper_format_write_hex(file, TOTP_CONFIG_KEY_TOKEN_SECRET, token_info->token, token_info->token_length);
     flipper_format_write_string_cstr(file, TOTP_CONFIG_KEY_TOKEN_ALGO, token_info_get_algo_as_cstr(token_info));
     flipper_format_write_string_cstr(file, TOTP_CONFIG_KEY_TOKEN_ALGO, token_info_get_algo_as_cstr(token_info));
@@ -137,6 +138,26 @@ void totp_config_file_save_new_token(FlipperFormat* file, TokenInfo* token_info)
     flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &digits_count_as_uint32, 1);
     flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &digits_count_as_uint32, 1);
 }
 }
 
 
+void totp_config_file_save_new_token(TokenInfo* token_info) {
+    Storage* cfg_storage = totp_open_storage();
+    FlipperFormat* file = totp_open_config_file(cfg_storage);
+
+    totp_config_file_save_new_token_i(file, token_info);
+
+    totp_close_config_file(file);
+    totp_close_storage();
+}
+
+void totp_config_file_update_timezone_offset(float new_timezone_offset) {
+    Storage* cfg_storage = totp_open_storage();
+    FlipperFormat* file = totp_open_config_file(cfg_storage);
+
+    flipper_format_insert_or_update_float(file, TOTP_CONFIG_KEY_TIMEZONE, &new_timezone_offset, 1);
+
+    totp_close_config_file(file);
+    totp_close_storage();
+}
+
 void totp_full_save_config_file(PluginState* const plugin_state) {
 void totp_full_save_config_file(PluginState* const plugin_state) {
     Storage* storage = totp_open_storage();
     Storage* storage = totp_open_storage();
     FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
     FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
@@ -149,7 +170,7 @@ void totp_full_save_config_file(PluginState* const plugin_state) {
     ListNode* node = plugin_state->tokens_list;
     ListNode* node = plugin_state->tokens_list;
     while (node != NULL) {
     while (node != NULL) {
         TokenInfo* token_info = node->data;
         TokenInfo* token_info = node->data;
-        totp_config_file_save_new_token(fff_data_file, token_info);
+        totp_config_file_save_new_token_i(fff_data_file, token_info);
         node = node->next;
         node = node->next;
     } 
     } 
 
 

+ 2 - 1
services/config/config.h

@@ -13,4 +13,5 @@ void totp_close_config_file(FlipperFormat* file);
 void totp_full_save_config_file(PluginState* const plugin_state);
 void totp_full_save_config_file(PluginState* const plugin_state);
 void totp_config_file_load_base(PluginState* const plugin_state);
 void totp_config_file_load_base(PluginState* const plugin_state);
 void totp_config_file_load_tokens(PluginState* const plugin_state);
 void totp_config_file_load_tokens(PluginState* const plugin_state);
-void totp_config_file_save_new_token(FlipperFormat* file, TokenInfo* token_info);
+void totp_config_file_save_new_token(TokenInfo* token_info);
+void totp_config_file_update_timezone_offset(float new_timezone_offset);

+ 8 - 8
services/ui/ui_controls.c

@@ -20,24 +20,24 @@ void ui_control_text_box_render(Canvas* const canvas, int8_t y, char* text, bool
     canvas_draw_str_aligned(canvas, TEXT_BOX_MARGIN + 2, TEXT_BOX_MARGIN + 3 + y, AlignLeft, AlignTop, text);
     canvas_draw_str_aligned(canvas, TEXT_BOX_MARGIN + 2, TEXT_BOX_MARGIN + 3 + y, AlignLeft, AlignTop, text);
 }
 }
 
 
-void ui_control_select_render(Canvas* const canvas, int8_t y, char* text, bool is_selected) {
+void ui_control_select_render(Canvas* const canvas, int16_t x, int16_t y, uint8_t width, char* text, bool is_selected) {
     if (y < -TEXT_BOX_HEIGHT) {
     if (y < -TEXT_BOX_HEIGHT) {
         return;
         return;
     }
     }
 
 
     if (is_selected) {
     if (is_selected) {
-        canvas_draw_rframe(canvas, TEXT_BOX_MARGIN, TEXT_BOX_MARGIN + y, SCREEN_WIDTH - TEXT_BOX_MARGIN - TEXT_BOX_MARGIN, TEXT_BOX_HEIGHT, 0);
-        canvas_draw_rframe(canvas, TEXT_BOX_MARGIN - 1, TEXT_BOX_MARGIN + y - 1, SCREEN_WIDTH - TEXT_BOX_MARGIN - TEXT_BOX_MARGIN + 2, TEXT_BOX_HEIGHT + 2, 1);
+        canvas_draw_rframe(canvas, x + TEXT_BOX_MARGIN, TEXT_BOX_MARGIN + y, width - TEXT_BOX_MARGIN - TEXT_BOX_MARGIN, TEXT_BOX_HEIGHT, 0);
+        canvas_draw_rframe(canvas, x + TEXT_BOX_MARGIN - 1, TEXT_BOX_MARGIN + y - 1, width - TEXT_BOX_MARGIN - TEXT_BOX_MARGIN + 2, TEXT_BOX_HEIGHT + 2, 1);
     } else {
     } else {
-        canvas_draw_rframe(canvas, TEXT_BOX_MARGIN, TEXT_BOX_MARGIN + y, SCREEN_WIDTH - TEXT_BOX_MARGIN - TEXT_BOX_MARGIN, TEXT_BOX_HEIGHT, 1);
+        canvas_draw_rframe(canvas, x + TEXT_BOX_MARGIN, TEXT_BOX_MARGIN + y, width - TEXT_BOX_MARGIN - TEXT_BOX_MARGIN, TEXT_BOX_HEIGHT, 1);
     }
     }
 
 
-    canvas_draw_str_aligned(canvas, SCREEN_WIDTH_CENTER, TEXT_BOX_MARGIN + 3 + y, AlignCenter, AlignTop, text);
-    canvas_draw_xbm(canvas, TEXT_BOX_MARGIN + 2, TEXT_BOX_MARGIN + 2 + y, ICON_ARROW_LEFT_8x9_WIDTH, ICON_ARROW_LEFT_8x9_HEIGHT, &ICON_ARROW_LEFT_8x9[0]);
-    canvas_draw_xbm(canvas, SCREEN_WIDTH - TEXT_BOX_MARGIN - 10, TEXT_BOX_MARGIN + 2 + y, ICON_ARROW_RIGHT_8x9_WIDTH, ICON_ARROW_RIGHT_8x9_HEIGHT, &ICON_ARROW_RIGHT_8x9[0]);
+    canvas_draw_str_aligned(canvas, x + (width >> 1), TEXT_BOX_MARGIN + 3 + y, AlignCenter, AlignTop, text);
+    canvas_draw_xbm(canvas, x + TEXT_BOX_MARGIN + 2, TEXT_BOX_MARGIN + 2 + y, ICON_ARROW_LEFT_8x9_WIDTH, ICON_ARROW_LEFT_8x9_HEIGHT, &ICON_ARROW_LEFT_8x9[0]);
+    canvas_draw_xbm(canvas, x + width - TEXT_BOX_MARGIN - 10, TEXT_BOX_MARGIN + 2 + y, ICON_ARROW_RIGHT_8x9_WIDTH, ICON_ARROW_RIGHT_8x9_HEIGHT, &ICON_ARROW_RIGHT_8x9[0]);
 }
 }
 
 
-void ui_control_button_render(Canvas* const canvas, uint8_t x, int8_t y, uint8_t width, uint8_t height, char* text, bool is_selected) {
+void ui_control_button_render(Canvas* const canvas, int16_t x, int16_t y, uint8_t width, uint8_t height, char* text, bool is_selected) {
     if (y < -height) {
     if (y < -height) {
         return;
         return;
     }
     }

+ 2 - 2
services/ui/ui_controls.h

@@ -4,5 +4,5 @@
 #include <gui/gui.h>
 #include <gui/gui.h>
 
 
 void ui_control_text_box_render(Canvas* const canvas, int8_t y, char* text, bool is_selected);
 void ui_control_text_box_render(Canvas* const canvas, int8_t y, char* text, bool is_selected);
-void ui_control_button_render(Canvas* const canvas, uint8_t x, int8_t y, uint8_t width, uint8_t height, char* text, bool is_selected);
-void ui_control_select_render(Canvas* const canvas, int8_t y, char* text, bool is_selected);
+void ui_control_button_render(Canvas* const canvas, int16_t x, int16_t y, uint8_t width, uint8_t height, char* text, bool is_selected);
+void ui_control_select_render(Canvas* const canvas, int16_t x, int16_t y, uint8_t width, char* text, bool is_selected);