diff --git a/application.fam b/application.fam index 08f54619c10..5b026649331 100644 --- a/application.fam +++ b/application.fam @@ -15,5 +15,6 @@ App( stack_size=2 * 1024, order=20, fap_category="Misc", + fap_icon_assets="images", fap_icon="totp_10px.png" ) diff --git a/images/DolphinCommon_56x48.png b/images/DolphinCommon_56x48.png new file mode 100644 index 00000000000..089aaed8350 Binary files /dev/null and b/images/DolphinCommon_56x48.png differ diff --git a/images/totp_arrow_left_8x9.png b/images/totp_arrow_left_8x9.png new file mode 100644 index 00000000000..3bf9121c0b3 Binary files /dev/null and b/images/totp_arrow_left_8x9.png differ diff --git a/images/totp_arrow_right_8x9.png b/images/totp_arrow_right_8x9.png new file mode 100644 index 00000000000..8c6a8bfeb91 Binary files /dev/null and b/images/totp_arrow_right_8x9.png differ diff --git a/scenes/add_new_token/totp_scene_add_new_token.c b/scenes/add_new_token/totp_scene_add_new_token.c index cc4e6a69d81..8f3aad6b507 100644 --- a/scenes/add_new_token/totp_scene_add_new_token.c +++ b/scenes/add_new_token/totp_scene_add_new_token.c @@ -8,6 +8,8 @@ #include "../../services/base32/base32.h" #include "../../services/config/config.h" #include "../../services/ui/ui_controls.h" +#include "../../services/roll_value/roll_value.h" +#include "../../services/nullable/nullable.h" #include "../generate_token/totp_scene_generate_token.h" #define TOKEN_ALGO_LIST_LENGTH 3 @@ -34,7 +36,7 @@ typedef struct { InputTextSceneContext* token_secret_input_context; InputTextSceneState* input_state; uint32_t input_started_at; - int16_t current_token_index; + TotpNullable_uint16_t current_token_index; int16_t screen_y_offset; TokenHashAlgo algo; TokenDigitsCount digits_count; @@ -87,9 +89,9 @@ void totp_scene_add_new_token_activate( scene_state->input_state = NULL; if(context == NULL) { - scene_state->current_token_index = -1; + TOTP_NULLABLE_NULL(scene_state->current_token_index); } else { - scene_state->current_token_index = context->current_token_index; + TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index); } } @@ -150,144 +152,150 @@ void update_screen_y_offset(SceneState* scene_state) { } bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state) { - if(event->type == EventTypeKey) { - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - if(scene_state->input_started_at > 0 && - furi_get_tick() - scene_state->input_started_at > 300) { - return totp_input_text_handle_event(event, scene_state->input_state); + if(event->type != EventTypeKey) { + return true; + } + + SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + if(scene_state->input_started_at > 0 && + furi_get_tick() - scene_state->input_started_at > 300) { + return totp_input_text_handle_event(event, scene_state->input_state); + } + + if(event->input.type == InputTypeLong && event->input.key == InputKeyBack) { + return false; + } + + if(event->input.type != InputTypePress) { + return true; + } + + switch(event->input.key) { + case InputKeyUp: + totp_roll_value_uint8_t( + &scene_state->selected_control, + -1, + TokenNameTextBox, + ConfirmButton, + RollOverflowBehaviorStop); + update_screen_y_offset(scene_state); + break; + case InputKeyDown: + totp_roll_value_uint8_t( + &scene_state->selected_control, + 1, + TokenNameTextBox, + ConfirmButton, + RollOverflowBehaviorStop); + update_screen_y_offset(scene_state); + break; + case InputKeyRight: + if(scene_state->selected_control == TokenAlgoSelect) { + totp_roll_value_uint8_t(&scene_state->algo, 1, SHA1, SHA512, RollOverflowBehaviorRoll); + } else if(scene_state->selected_control == TokenLengthSelect) { + totp_roll_value_uint8_t( + &scene_state->digits_count, + 1, + TOTP_6_DIGITS, + TOTP_8_DIGITS, + RollOverflowBehaviorRoll); } + break; + case InputKeyLeft: + if(scene_state->selected_control == TokenAlgoSelect) { + totp_roll_value_uint8_t( + &scene_state->algo, -1, SHA1, SHA512, RollOverflowBehaviorRoll); + } else if(scene_state->selected_control == TokenLengthSelect) { + totp_roll_value_uint8_t( + &scene_state->digits_count, + -1, + TOTP_6_DIGITS, + TOTP_8_DIGITS, + RollOverflowBehaviorRoll); + } + break; + case InputKeyOk: + switch(scene_state->selected_control) { + case TokenNameTextBox: + if(scene_state->input_state != NULL) { + totp_input_text_free(scene_state->input_state); + } + scene_state->input_state = + totp_input_text_activate(scene_state->token_name_input_context); + scene_state->input_started_at = furi_get_tick(); + break; + case TokenSecretTextBox: + if(scene_state->input_state != NULL) { + totp_input_text_free(scene_state->input_state); + } + scene_state->input_state = + totp_input_text_activate(scene_state->token_secret_input_context); + scene_state->input_started_at = furi_get_tick(); + break; + case TokenAlgoSelect: + break; + case TokenLengthSelect: + break; + case ConfirmButton: { + TokenInfo* tokenInfo = token_info_alloc(); + bool token_secret_set = token_info_set_secret( + tokenInfo, + scene_state->token_secret, + scene_state->token_secret_length, + &plugin_state->iv[0]); - if(event->input.type == InputTypeLong && event->input.key == InputKeyBack) { - return false; - } else if(event->input.type == InputTypePress) { - switch(event->input.key) { - case InputKeyUp: - if(scene_state->selected_control > TokenNameTextBox) { - scene_state->selected_control--; - update_screen_y_offset(scene_state); - } - break; - case InputKeyDown: - if(scene_state->selected_control < ConfirmButton) { - scene_state->selected_control++; - update_screen_y_offset(scene_state); - } - break; - case InputKeyRight: - if(scene_state->selected_control == TokenAlgoSelect) { - if(scene_state->algo < SHA512) { - scene_state->algo++; - } else { - scene_state->algo = SHA1; - } - } else if(scene_state->selected_control == TokenLengthSelect) { - if(scene_state->digits_count < TOTP_8_DIGITS) { - scene_state->digits_count++; - } else { - scene_state->digits_count = TOTP_6_DIGITS; - } - } - break; - case InputKeyLeft: - if(scene_state->selected_control == TokenAlgoSelect) { - if(scene_state->algo > SHA1) { - scene_state->algo--; - } else { - scene_state->algo = SHA512; - } - } else if(scene_state->selected_control == TokenLengthSelect) { - if(scene_state->digits_count > TOTP_6_DIGITS) { - scene_state->digits_count--; - } else { - scene_state->digits_count = TOTP_8_DIGITS; - } - } - break; - case InputKeyOk: - switch(scene_state->selected_control) { - case TokenNameTextBox: - if(scene_state->input_state != NULL) { - totp_input_text_free(scene_state->input_state); - } - scene_state->input_state = - totp_input_text_activate(scene_state->token_name_input_context); - scene_state->input_started_at = furi_get_tick(); - break; - case TokenSecretTextBox: - if(scene_state->input_state != NULL) { - totp_input_text_free(scene_state->input_state); - } - scene_state->input_state = - totp_input_text_activate(scene_state->token_secret_input_context); - scene_state->input_started_at = furi_get_tick(); - break; - case TokenAlgoSelect: - break; - case TokenLengthSelect: - break; - case ConfirmButton: { - TokenInfo* tokenInfo = token_info_alloc(); - bool token_secret_set = token_info_set_secret( - tokenInfo, - scene_state->token_secret, - scene_state->token_secret_length, - &plugin_state->iv[0]); - - if(token_secret_set) { - tokenInfo->name = malloc(scene_state->token_name_length + 1); - strlcpy( - tokenInfo->name, - scene_state->token_name, - scene_state->token_name_length + 1); - tokenInfo->algo = scene_state->algo; - tokenInfo->digits = scene_state->digits_count; - - if(plugin_state->tokens_list == NULL) { - plugin_state->tokens_list = list_init_head(tokenInfo); - } else { - list_add(plugin_state->tokens_list, tokenInfo); - } - plugin_state->tokens_count++; - - totp_config_file_save_new_token(tokenInfo); - - GenerateTokenSceneContext generate_scene_context = { - .current_token_index = plugin_state->tokens_count - 1}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneGenerateToken, &generate_scene_context); - } else { - token_info_free(tokenInfo); - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_buttons(message, "Back", NULL, NULL); - dialog_message_set_text( - message, - "Token secret is invalid", - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER, - AlignCenter, - AlignCenter); - dialog_message_show(plugin_state->dialogs, message); - dialog_message_free(message); - scene_state->selected_control = TokenSecretTextBox; - update_screen_y_offset(scene_state); - } - break; - } - } - break; - case InputKeyBack: - 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); + if(token_secret_set) { + tokenInfo->name = malloc(scene_state->token_name_length + 1); + strlcpy( + tokenInfo->name, scene_state->token_name, scene_state->token_name_length + 1); + tokenInfo->algo = scene_state->algo; + tokenInfo->digits = scene_state->digits_count; + + if(plugin_state->tokens_list == NULL) { + plugin_state->tokens_list = list_init_head(tokenInfo); } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + list_add(plugin_state->tokens_list, tokenInfo); } - break; + plugin_state->tokens_count++; + + totp_config_file_save_new_token(tokenInfo); + + GenerateTokenSceneContext generate_scene_context = { + .current_token_index = plugin_state->tokens_count - 1}; + totp_scene_director_activate_scene( + plugin_state, TotpSceneGenerateToken, &generate_scene_context); + } else { + token_info_free(tokenInfo); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, "Back", NULL, NULL); + dialog_message_set_text( + message, + "Token secret is invalid", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + dialog_message_show(plugin_state->dialogs, message); + dialog_message_free(message); + scene_state->selected_control = TokenSecretTextBox; + update_screen_y_offset(scene_state); } + break; } + } + break; + case InputKeyBack: + if(!scene_state->current_token_index.is_null) { + GenerateTokenSceneContext generate_scene_context = { + .current_token_index = scene_state->current_token_index.value}; + totp_scene_director_activate_scene( + plugin_state, TotpSceneGenerateToken, &generate_scene_context); + } else { + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } + break; } + return true; } diff --git a/scenes/add_new_token/totp_scene_add_new_token.h b/scenes/add_new_token/totp_scene_add_new_token.h index 7a0b0e68afa..9f53fe7997c 100644 --- a/scenes/add_new_token/totp_scene_add_new_token.h +++ b/scenes/add_new_token/totp_scene_add_new_token.h @@ -7,7 +7,7 @@ #include "../../types/plugin_event.h" typedef struct { - uint8_t current_token_index; + uint16_t current_token_index; } TokenAddEditSceneContext; void totp_scene_add_new_token_init(const PluginState* plugin_state); diff --git a/scenes/app_settings/totp_app_settings.c b/scenes/app_settings/totp_app_settings.c index 4e03de433ce..a8ca1a167c3 100644 --- a/scenes/app_settings/totp_app_settings.c +++ b/scenes/app_settings/totp_app_settings.c @@ -4,6 +4,8 @@ #include "../token_menu/totp_scene_token_menu.h" #include "../../services/ui/constants.h" #include "../../services/config/config.h" +#include "../../services/roll_value/roll_value.h" +#include "../../services/nullable/nullable.h" #define DIGIT_TO_CHAR(digit) ((digit) + '0') @@ -12,7 +14,7 @@ typedef enum { HoursInput, MinutesInput, ConfirmButton } Control; typedef struct { int8_t tz_offset_hours; uint8_t tz_offset_minutes; - int16_t current_token_index; + TotpNullable_uint16_t current_token_index; Control selected_control; } SceneState; @@ -26,9 +28,9 @@ void totp_scene_app_settings_activate( 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; + TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index); } else { - scene_state->current_token_index = -1; + TOTP_NULLABLE_NULL(scene_state->current_token_index); } float off_int; @@ -90,77 +92,78 @@ void totp_scene_app_settings_render(Canvas* const canvas, PluginState* plugin_st scene_state->selected_control == ConfirmButton); } -bool totp_scene_app_settings_handle_event(const 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; - } +bool totp_scene_app_settings_handle_event( + const PluginEvent* const event, + PluginState* plugin_state) { + if(event->type != EventTypeKey) { + return true; + } + + SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + if(event->input.type != InputTypePress) { + return true; + } + + switch(event->input.key) { + case InputKeyUp: + totp_roll_value_uint8_t( + &scene_state->selected_control, + -1, + HoursInput, + ConfirmButton, + RollOverflowBehaviorStop); + break; + case InputKeyDown: + totp_roll_value_uint8_t( + &scene_state->selected_control, 1, HoursInput, ConfirmButton, RollOverflowBehaviorStop); + break; + case InputKeyRight: + if(scene_state->selected_control == HoursInput) { + totp_roll_value_int8_t( + &scene_state->tz_offset_hours, 1, -12, 12, RollOverflowBehaviorStop); + } else if(scene_state->selected_control == MinutesInput) { + totp_roll_value_uint8_t( + &scene_state->tz_offset_minutes, 15, 0, 45, RollOverflowBehaviorRoll); + } + break; + case InputKeyLeft: + if(scene_state->selected_control == HoursInput) { + totp_roll_value_int8_t( + &scene_state->tz_offset_hours, -1, -12, 12, RollOverflowBehaviorStop); + } else if(scene_state->selected_control == MinutesInput) { + totp_roll_value_uint8_t( + &scene_state->tz_offset_minutes, -15, 0, 45, RollOverflowBehaviorRoll); + } + 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.is_null) { + TokenMenuSceneContext generate_scene_context = { + .current_token_index = scene_state->current_token_index.value}; + 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.is_null) { + TokenMenuSceneContext generate_scene_context = { + .current_token_index = scene_state->current_token_index.value}; + totp_scene_director_activate_scene( + plugin_state, TotpSceneTokenMenu, &generate_scene_context); + } else { + totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); + } + break; } + } + return true; } diff --git a/scenes/app_settings/totp_app_settings.h b/scenes/app_settings/totp_app_settings.h index be51e9dc90d..bcf930839a9 100644 --- a/scenes/app_settings/totp_app_settings.h +++ b/scenes/app_settings/totp_app_settings.h @@ -7,7 +7,7 @@ #include "../../types/plugin_event.h" typedef struct { - uint8_t current_token_index; + uint16_t current_token_index; } AppSettingsSceneContext; void totp_scene_app_settings_init(const PluginState* plugin_state); @@ -15,6 +15,8 @@ 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(const PluginEvent* const event, PluginState* plugin_state); +bool totp_scene_app_settings_handle_event( + const PluginEvent* const event, + PluginState* plugin_state); void totp_scene_app_settings_deactivate(PluginState* plugin_state); void totp_scene_app_settings_free(const PluginState* plugin_state); \ No newline at end of file diff --git a/scenes/authenticate/totp_scene_authenticate.c b/scenes/authenticate/totp_scene_authenticate.c index c03f87542ca..8c7715059df 100644 --- a/scenes/authenticate/totp_scene_authenticate.c +++ b/scenes/authenticate/totp_scene_authenticate.c @@ -1,7 +1,7 @@ #include "totp_scene_authenticate.h" #include +#include #include "../../types/common.h" -#include "../../services/ui/icons.h" #include "../../services/ui/constants.h" #include "../../services/config/config.h" #include "../scene_director.h" @@ -9,6 +9,10 @@ #include "../../services/crypto/crypto.h" #define MAX_CODE_LENGTH TOTP_IV_SIZE +#define ARROW_UP_CODE 2 +#define ARROW_RIGHT_CODE 8 +#define ARROW_DOWN_CODE 11 +#define ARROW_LEFT_CODE 5 typedef struct { uint8_t code_input[MAX_CODE_LENGTH]; @@ -73,78 +77,80 @@ void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_st } } -bool totp_scene_authenticate_handle_event(const PluginEvent* const event, PluginState* plugin_state) { - if(event->type == EventTypeKey) { - if(event->input.type == InputTypeLong && event->input.key == InputKeyBack) { - return false; - } else if(event->input.type == InputTypePress) { - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - - const uint8_t ARROW_UP_CODE = 2; - const uint8_t ARROW_RIGHT_CODE = 8; - const uint8_t ARROW_DOWN_CODE = 11; - const uint8_t ARROW_LEFT_CODE = 5; - - switch(event->input.key) { - case InputKeyUp: - if(scene_state->code_length < MAX_CODE_LENGTH) { - scene_state->code_input[scene_state->code_length] = ARROW_UP_CODE; - scene_state->code_length++; - } - break; - case InputKeyDown: - if(scene_state->code_length < MAX_CODE_LENGTH) { - scene_state->code_input[scene_state->code_length] = ARROW_DOWN_CODE; - scene_state->code_length++; - } - break; - case InputKeyRight: - if(scene_state->code_length < MAX_CODE_LENGTH) { - scene_state->code_input[scene_state->code_length] = ARROW_RIGHT_CODE; - scene_state->code_length++; - } - break; - case InputKeyLeft: - if(scene_state->code_length < MAX_CODE_LENGTH) { - scene_state->code_input[scene_state->code_length] = ARROW_LEFT_CODE; - scene_state->code_length++; - } - break; - case InputKeyOk: - totp_crypto_seed_iv( - plugin_state, &scene_state->code_input[0], scene_state->code_length); - - if(totp_crypto_verify_key(plugin_state)) { - FURI_LOG_D(LOGGING_TAG, "PIN is valid"); - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } else { - FURI_LOG_D(LOGGING_TAG, "PIN is NOT valid"); - memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH); - memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); - scene_state->code_length = 0; - - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_buttons(message, "Try again", NULL, NULL); - dialog_message_set_header( - message, - "You entered\ninvalid PIN", - SCREEN_WIDTH_CENTER - 25, - SCREEN_HEIGHT_CENTER - 5, - AlignCenter, - AlignCenter); - dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17); - dialog_message_show(plugin_state->dialogs, message); - dialog_message_free(message); - } - break; - case InputKeyBack: - if(scene_state->code_length > 0) { - scene_state->code_input[scene_state->code_length - 1] = 0; - scene_state->code_length--; - } - break; - } +bool totp_scene_authenticate_handle_event( + const PluginEvent* const event, + PluginState* plugin_state) { + if(event->type != EventTypeKey) { + return true; + } + + if(event->input.type == InputTypeLong && event->input.key == InputKeyBack) { + return false; + } + + if(event->input.type != InputTypePress) { + return true; + } + + SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + + switch(event->input.key) { + case InputKeyUp: + if(scene_state->code_length < MAX_CODE_LENGTH) { + scene_state->code_input[scene_state->code_length] = ARROW_UP_CODE; + scene_state->code_length++; + } + break; + case InputKeyDown: + if(scene_state->code_length < MAX_CODE_LENGTH) { + scene_state->code_input[scene_state->code_length] = ARROW_DOWN_CODE; + scene_state->code_length++; + } + break; + case InputKeyRight: + if(scene_state->code_length < MAX_CODE_LENGTH) { + scene_state->code_input[scene_state->code_length] = ARROW_RIGHT_CODE; + scene_state->code_length++; + } + break; + case InputKeyLeft: + if(scene_state->code_length < MAX_CODE_LENGTH) { + scene_state->code_input[scene_state->code_length] = ARROW_LEFT_CODE; + scene_state->code_length++; + } + break; + case InputKeyOk: + totp_crypto_seed_iv(plugin_state, &scene_state->code_input[0], scene_state->code_length); + + if(totp_crypto_verify_key(plugin_state)) { + FURI_LOG_D(LOGGING_TAG, "PIN is valid"); + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } else { + FURI_LOG_D(LOGGING_TAG, "PIN is NOT valid"); + memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH); + memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); + scene_state->code_length = 0; + + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, "Try again", NULL, NULL); + dialog_message_set_header( + message, + "You entered\ninvalid PIN", + SCREEN_WIDTH_CENTER - 25, + SCREEN_HEIGHT_CENTER - 5, + AlignCenter, + AlignCenter); + dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17); + dialog_message_show(plugin_state->dialogs, message); + dialog_message_free(message); + } + break; + case InputKeyBack: + if(scene_state->code_length > 0) { + scene_state->code_input[scene_state->code_length - 1] = 0; + scene_state->code_length--; } + break; } return true; diff --git a/scenes/authenticate/totp_scene_authenticate.h b/scenes/authenticate/totp_scene_authenticate.h index aa308d983c9..c54152f6260 100644 --- a/scenes/authenticate/totp_scene_authenticate.h +++ b/scenes/authenticate/totp_scene_authenticate.h @@ -9,6 +9,8 @@ void totp_scene_authenticate_init(PluginState* plugin_state); void totp_scene_authenticate_activate(PluginState* plugin_state); void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_state); -bool totp_scene_authenticate_handle_event(const PluginEvent* const event, PluginState* plugin_state); +bool totp_scene_authenticate_handle_event( + const PluginEvent* const event, + PluginState* plugin_state); void totp_scene_authenticate_deactivate(PluginState* plugin_state); void totp_scene_authenticate_free(const PluginState* plugin_state); diff --git a/scenes/generate_token/totp_scene_generate_token.c b/scenes/generate_token/totp_scene_generate_token.c index 7b5f7391b3e..0ccab0d98d3 100644 --- a/scenes/generate_token/totp_scene_generate_token.c +++ b/scenes/generate_token/totp_scene_generate_token.c @@ -1,15 +1,16 @@ #include #include #include +#include #include "totp_scene_generate_token.h" #include "../../types/token_info.h" #include "../../types/common.h" -#include "../../services/ui/icons.h" #include "../../services/ui/constants.h" #include "../../services/totp/totp.h" #include "../../services/config/config.h" #include "../../services/crypto/crypto.h" #include "../../services/crypto/memset_s.h" +#include "../../services/roll_value/roll_value.h" #include "../scene_director.h" #include "../token_menu/totp_scene_token_menu.h" @@ -17,7 +18,7 @@ #define DIGIT_TO_CHAR(digit) ((digit) + '0') typedef struct { - uint8_t current_token_index; + uint16_t current_token_index; char last_code[9]; char* last_code_name; bool need_token_update; @@ -249,65 +250,61 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ canvas_draw_box(canvas, barX, SCREEN_HEIGHT - BAR_MARGIN - BAR_HEIGHT, barWidth, BAR_HEIGHT); if(plugin_state->tokens_count > 1) { - canvas_draw_xbm( - canvas, - 0, - SCREEN_HEIGHT_CENTER - 24, - ICON_ARROW_LEFT_8x9_WIDTH, - ICON_ARROW_LEFT_8x9_HEIGHT, - &ICON_ARROW_LEFT_8x9[0]); - canvas_draw_xbm( - canvas, - SCREEN_WIDTH - 9, - SCREEN_HEIGHT_CENTER - 24, - ICON_ARROW_RIGHT_8x9_WIDTH, - ICON_ARROW_RIGHT_8x9_HEIGHT, - &ICON_ARROW_RIGHT_8x9[0]); + canvas_draw_icon(canvas, 0, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_left_8x9); + canvas_draw_icon( + canvas, SCREEN_WIDTH - 9, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_right_8x9); } } bool totp_scene_generate_token_handle_event( const PluginEvent* const event, PluginState* plugin_state) { - if(event->type == EventTypeKey) { - if(event->input.type == InputTypeLong && event->input.key == InputKeyBack) { - return false; - } else if(event->input.type == InputTypePress) { - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - switch(event->input.key) { - case InputKeyUp: - break; - case InputKeyDown: - break; - case InputKeyRight: - if(scene_state->current_token_index < plugin_state->tokens_count - 1) { - scene_state->current_token_index++; - } else { - scene_state->current_token_index = 0; - } - update_totp_params(plugin_state); - break; - case InputKeyLeft: - if(scene_state->current_token_index > 0) { - scene_state->current_token_index--; - } else { - scene_state->current_token_index = plugin_state->tokens_count - 1; - } - update_totp_params(plugin_state); - break; - case InputKeyOk: - if(plugin_state->tokens_count == 0) { - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); - } else { - TokenMenuSceneContext ctx = { - .current_token_index = scene_state->current_token_index}; - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, &ctx); - } - break; - case InputKeyBack: - break; - } + if(event->type != EventTypeKey) { + return true; + } + + if(event->input.type == InputTypeLong && event->input.key == InputKeyBack) { + return false; + } + + if(event->input.type != InputTypePress) { + return true; + } + + SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + switch(event->input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyRight: + totp_roll_value_uint16_t( + &scene_state->current_token_index, + 1, + 0, + plugin_state->tokens_count - 1, + RollOverflowBehaviorRoll); + update_totp_params(plugin_state); + break; + case InputKeyLeft: + totp_roll_value_uint16_t( + &scene_state->current_token_index, + -1, + 0, + plugin_state->tokens_count - 1, + RollOverflowBehaviorRoll); + update_totp_params(plugin_state); + break; + case InputKeyOk: + if(plugin_state->tokens_count == 0) { + totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); + } else { + TokenMenuSceneContext ctx = {.current_token_index = scene_state->current_token_index}; + totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, &ctx); } + break; + case InputKeyBack: + break; } return true; diff --git a/scenes/generate_token/totp_scene_generate_token.h b/scenes/generate_token/totp_scene_generate_token.h index e4ca818b673..65f9b84e07b 100644 --- a/scenes/generate_token/totp_scene_generate_token.h +++ b/scenes/generate_token/totp_scene_generate_token.h @@ -7,7 +7,7 @@ #include "../../types/plugin_event.h" typedef struct { - uint8_t current_token_index; + uint16_t current_token_index; } GenerateTokenSceneContext; void totp_scene_generate_token_init(const PluginState* plugin_state); diff --git a/scenes/token_menu/totp_scene_token_menu.c b/scenes/token_menu/totp_scene_token_menu.c index c4286ded95d..44a429e2fac 100644 --- a/scenes/token_menu/totp_scene_token_menu.c +++ b/scenes/token_menu/totp_scene_token_menu.c @@ -10,6 +10,8 @@ #include "../generate_token/totp_scene_generate_token.h" #include "../add_new_token/totp_scene_add_new_token.h" #include "../app_settings/totp_app_settings.h" +#include "../../services/nullable/nullable.h" +#include "../../services/roll_value/roll_value.h" #define SCREEN_HEIGHT_THIRD (SCREEN_HEIGHT / 3) #define SCREEN_HEIGHT_THIRD_CENTER (SCREEN_HEIGHT_THIRD >> 1) @@ -18,7 +20,7 @@ typedef enum { AddNewToken, DeleteToken, AppSettings } Control; typedef struct { Control selected_control; - int16_t current_token_index; + TotpNullable_uint16_t current_token_index; } SceneState; void totp_scene_token_menu_init(const PluginState* plugin_state) { @@ -31,15 +33,15 @@ void totp_scene_token_menu_activate( 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; + TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index); } else { - scene_state->current_token_index = -1; + TOTP_NULLABLE_NULL(scene_state->current_token_index); } } void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state) { const SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - if(scene_state->current_token_index < 0) { + if(scene_state->current_token_index.is_null) { ui_control_button_render( canvas, SCREEN_WIDTH_CENTER - 36, @@ -85,103 +87,104 @@ void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_stat } bool totp_scene_token_menu_handle_event(const 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 > AddNewToken) { - 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; - case InputKeyDown: - if(scene_state->selected_control < AppSettings) { - 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; - case InputKeyRight: - break; - case InputKeyLeft: - break; - case InputKeyOk: - switch(scene_state->selected_control) { - case AddNewToken: { - TokenAddEditSceneContext add_new_token_scene_context = { - .current_token_index = scene_state->current_token_index}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneAddNewToken, &add_new_token_scene_context); - break; - } - case DeleteToken: { - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_buttons(message, "No", NULL, "Yes"); - dialog_message_set_header(message, "Confirmation", 0, 0, AlignLeft, AlignTop); - dialog_message_set_text( - message, - "Are you sure want to delete?", - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER, - AlignCenter, - AlignCenter); - DialogMessageButton dialog_result = - dialog_message_show(plugin_state->dialogs, message); - dialog_message_free(message); - if(dialog_result == DialogMessageButtonRight) { - ListNode* list_node = list_element_at( - plugin_state->tokens_list, scene_state->current_token_index); + if(event->type != EventTypeKey) { + return true; + } - TokenInfo* tokenInfo = list_node->data; - token_info_free(tokenInfo); - plugin_state->tokens_list = - list_remove(plugin_state->tokens_list, list_node); - plugin_state->tokens_count--; + SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + if(event->input.type != InputTypePress) { + return true; + } - totp_full_save_config_file(plugin_state); - totp_scene_director_activate_scene( - plugin_state, TotpSceneGenerateToken, NULL); - } - 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; - case InputKeyBack: { - 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; + switch(event->input.key) { + case InputKeyUp: + totp_roll_value_uint8_t( + &scene_state->selected_control, -1, AddNewToken, AppSettings, RollOverflowBehaviorRoll); + if(scene_state->selected_control == DeleteToken && + scene_state->current_token_index.is_null) { + scene_state->selected_control--; + } + break; + case InputKeyDown: + totp_roll_value_uint8_t( + &scene_state->selected_control, 1, AddNewToken, AppSettings, RollOverflowBehaviorRoll); + if(scene_state->selected_control == DeleteToken && + scene_state->current_token_index.is_null) { + scene_state->selected_control++; + } + break; + case InputKeyRight: + break; + case InputKeyLeft: + break; + case InputKeyOk: + switch(scene_state->selected_control) { + case AddNewToken: { + if(scene_state->current_token_index.is_null) { + totp_scene_director_activate_scene(plugin_state, TotpSceneAddNewToken, NULL); + } else { + TokenAddEditSceneContext add_new_token_scene_context = { + .current_token_index = scene_state->current_token_index.value}; + totp_scene_director_activate_scene( + plugin_state, TotpSceneAddNewToken, &add_new_token_scene_context); } + break; + } + case DeleteToken: { + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, "No", NULL, "Yes"); + dialog_message_set_header(message, "Confirmation", 0, 0, AlignLeft, AlignTop); + dialog_message_set_text( + message, + "Are you sure want to delete?", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + DialogMessageButton dialog_result = + dialog_message_show(plugin_state->dialogs, message); + dialog_message_free(message); + if(dialog_result == DialogMessageButtonRight && + !scene_state->current_token_index.is_null) { + ListNode* list_node = list_element_at( + plugin_state->tokens_list, scene_state->current_token_index.value); + + TokenInfo* tokenInfo = list_node->data; + token_info_free(tokenInfo); + plugin_state->tokens_list = list_remove(plugin_state->tokens_list, list_node); + plugin_state->tokens_count--; + + totp_full_save_config_file(plugin_state); + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); } + break; } + case AppSettings: { + if(!scene_state->current_token_index.is_null) { + AppSettingsSceneContext app_settings_context = { + .current_token_index = scene_state->current_token_index.value}; + totp_scene_director_activate_scene( + plugin_state, TotpSceneAppSettings, &app_settings_context); + } else { + totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings, NULL); + } + break; + } + } + break; + case InputKeyBack: { + if(!scene_state->current_token_index.is_null) { + GenerateTokenSceneContext generate_scene_context = { + .current_token_index = scene_state->current_token_index.value}; + totp_scene_director_activate_scene( + plugin_state, TotpSceneGenerateToken, &generate_scene_context); + } else { + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + } + break; + } } + return true; } diff --git a/scenes/token_menu/totp_scene_token_menu.h b/scenes/token_menu/totp_scene_token_menu.h index 52e33e72808..acb499be832 100644 --- a/scenes/token_menu/totp_scene_token_menu.h +++ b/scenes/token_menu/totp_scene_token_menu.h @@ -7,7 +7,7 @@ #include "../../types/plugin_event.h" typedef struct { - uint8_t current_token_index; + uint16_t current_token_index; } TokenMenuSceneContext; void totp_scene_token_menu_init(const PluginState* plugin_state); diff --git a/services/config/config.c b/services/config/config.c index bb6cffde04b..36501abbd03 100644 --- a/services/config/config.c +++ b/services/config/config.c @@ -333,7 +333,7 @@ TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state) } TokenLoadingResult result = TokenLoadingResultSuccess; - uint8_t index = 0; + uint16_t index = 0; bool has_any_plain_secret = false; while(true) { @@ -421,7 +421,7 @@ TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state) plugin_state->tokens_count = index; plugin_state->token_list_loaded = true; - FURI_LOG_D(LOGGING_TAG, "Found %" PRIu8 " tokens", index); + FURI_LOG_D(LOGGING_TAG, "Found %" PRIu16 " tokens", index); furi_string_free(temp_str); totp_close_config_file(fff_data_file); diff --git a/services/nullable/nullable.h b/services/nullable/nullable.h new file mode 100644 index 00000000000..4f9b7bc012c --- /dev/null +++ b/services/nullable/nullable.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#define TOTP_NULLABLE_STRUCT(value_type) \ + typedef struct TotpNullable_##value_type { \ + bool is_null; \ + value_type value; \ + } TotpNullable_##value_type + +#define TOTP_NULLABLE_NULL(s) s.is_null = true +#define TOTP_NULLABLE_VALUE(s, v) \ + s.is_null = false; \ + s.value = v + +TOTP_NULLABLE_STRUCT(uint16_t); diff --git a/services/roll_value/roll_value.c b/services/roll_value/roll_value.c new file mode 100644 index 00000000000..b8f30e07871 --- /dev/null +++ b/services/roll_value/roll_value.c @@ -0,0 +1,28 @@ +#include "roll_value.h" + +#define TOTP_ROLL_VALUE_FN(type, step_type) \ + TOTP_ROLL_VALUE_FN_HEADER(type, step_type) { \ + type v = *value; \ + if(step > 0 && v > max - step) { \ + if(overflow_behavior == RollOverflowBehaviorRoll) { \ + v = min; \ + } else if(overflow_behavior == RollOverflowBehaviorStop) { \ + v = max; \ + } \ + } else if(step < 0 && v < min - step) { \ + if(overflow_behavior == RollOverflowBehaviorRoll) { \ + v = max; \ + } else if(overflow_behavior == RollOverflowBehaviorStop) { \ + v = min; \ + } \ + } else { \ + v += step; \ + } \ + *value = v; \ + } + +TOTP_ROLL_VALUE_FN(int8_t, int8_t) + +TOTP_ROLL_VALUE_FN(uint8_t, int8_t) + +TOTP_ROLL_VALUE_FN(uint16_t, int16_t); \ No newline at end of file diff --git a/services/roll_value/roll_value.h b/services/roll_value/roll_value.h new file mode 100644 index 00000000000..87337597f07 --- /dev/null +++ b/services/roll_value/roll_value.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +typedef enum { RollOverflowBehaviorStop, RollOverflowBehaviorRoll } TotpRollValueOverflowBehavior; + +#define TOTP_ROLL_VALUE_FN_HEADER(type, step_type) \ + void totp_roll_value_##type( \ + type* value, \ + step_type step, \ + type min, \ + type max, \ + TotpRollValueOverflowBehavior overflow_behavior) + +TOTP_ROLL_VALUE_FN_HEADER(int8_t, int8_t); +TOTP_ROLL_VALUE_FN_HEADER(uint8_t, int8_t); +TOTP_ROLL_VALUE_FN_HEADER(uint16_t, int16_t); \ No newline at end of file diff --git a/services/ui/icons.h b/services/ui/icons.h deleted file mode 100644 index a9139403fa4..00000000000 --- a/services/ui/icons.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -#define ICON_ARROW_LEFT_8x9_WIDTH 8 -#define ICON_ARROW_LEFT_8x9_HEIGHT 9 -static const uint8_t ICON_ARROW_LEFT_8x9[] = {0x80, 0xe0, 0xf8, 0xfe, 0xff, 0xfe, 0xf8, 0xe0, 0x80}; - -#define ICON_ARROW_RIGHT_8x9_WIDTH 8 -#define ICON_ARROW_RIGHT_8x9_HEIGHT 9 -static const uint8_t ICON_ARROW_RIGHT_8x9[] = - {0x01, 0x07, 0x1f, 0x7f, 0xff, 0x7f, 0x1f, 0x07, 0x01}; diff --git a/services/ui/ui_controls.c b/services/ui/ui_controls.c index 8ed73498c56..af029dd9fc8 100644 --- a/services/ui/ui_controls.c +++ b/services/ui/ui_controls.c @@ -1,11 +1,15 @@ #include "ui_controls.h" +#include #include "constants.h" -#include "icons.h" #define TEXT_BOX_HEIGHT 13 #define TEXT_BOX_MARGIN 4 -void ui_control_text_box_render(Canvas* const canvas, int16_t y, const char* text, bool is_selected) { +void ui_control_text_box_render( + Canvas* const canvas, + int16_t y, + const char* text, + bool is_selected) { if(y < -TEXT_BOX_HEIGHT) { return; } @@ -77,20 +81,10 @@ void ui_control_select_render( 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]); + canvas_draw_icon( + canvas, x + TEXT_BOX_MARGIN + 2, TEXT_BOX_MARGIN + 2 + y, &I_totp_arrow_left_8x9); + canvas_draw_icon( + canvas, x + width - TEXT_BOX_MARGIN - 10, TEXT_BOX_MARGIN + 2 + y, &I_totp_arrow_right_8x9); } void ui_control_button_render( diff --git a/services/ui/ui_controls.h b/services/ui/ui_controls.h index 02b278cf91f..ef3af5f5545 100644 --- a/services/ui/ui_controls.h +++ b/services/ui/ui_controls.h @@ -3,7 +3,11 @@ #include #include -void ui_control_text_box_render(Canvas* const canvas, int16_t y, const char* text, bool is_selected); +void ui_control_text_box_render( + Canvas* const canvas, + int16_t y, + const char* text, + bool is_selected); void ui_control_button_render( Canvas* const canvas, int16_t x, diff --git a/types/plugin_state.h b/types/plugin_state.h index 26e7e244bfb..9b16d7d57b8 100644 --- a/types/plugin_state.h +++ b/types/plugin_state.h @@ -19,7 +19,7 @@ typedef struct { float timezone_offset; ListNode* tokens_list; bool token_list_loaded; - uint8_t tokens_count; + uint16_t tokens_count; uint8_t* crypto_verify_data; size_t crypto_verify_data_length;