[FL-1968] Pin code locking (#788)
* Gui: code input module * Gui: fix size to fit frame * Desktop: PIN config and lock option * Gui: code input: cleanup, offset input fields if no header present * Desktop: move code unlock to desktop_locked scene * Desktop: fix unlock with back key * Desktop: bump settings version * Desktop: correct scene usage. Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									400d672e81
								
							
						
					
					
						commit
						fae8d8f23c
					
				| @ -41,6 +41,7 @@ Desktop* desktop_alloc() { | |||||||
|     desktop->debug_view = desktop_debug_alloc(); |     desktop->debug_view = desktop_debug_alloc(); | ||||||
|     desktop->first_start_view = desktop_first_start_alloc(); |     desktop->first_start_view = desktop_first_start_alloc(); | ||||||
|     desktop->hw_mismatch_popup = popup_alloc(); |     desktop->hw_mismatch_popup = popup_alloc(); | ||||||
|  |     desktop->code_input = code_input_alloc(); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         desktop->view_dispatcher, DesktopViewMain, desktop_main_get_view(desktop->main_view)); |         desktop->view_dispatcher, DesktopViewMain, desktop_main_get_view(desktop->main_view)); | ||||||
| @ -62,7 +63,8 @@ Desktop* desktop_alloc() { | |||||||
|         desktop->view_dispatcher, |         desktop->view_dispatcher, | ||||||
|         DesktopViewHwMismatch, |         DesktopViewHwMismatch, | ||||||
|         popup_get_view(desktop->hw_mismatch_popup)); |         popup_get_view(desktop->hw_mismatch_popup)); | ||||||
| 
 |     view_dispatcher_add_view( | ||||||
|  |         desktop->view_dispatcher, DesktopViewPinSetup, code_input_get_view(desktop->code_input)); | ||||||
|     // Lock icon
 |     // Lock icon
 | ||||||
|     desktop->lock_viewport = view_port_alloc(); |     desktop->lock_viewport = view_port_alloc(); | ||||||
|     view_port_set_width(desktop->lock_viewport, icon_get_width(&I_Lock_8x8)); |     view_port_set_width(desktop->lock_viewport, icon_get_width(&I_Lock_8x8)); | ||||||
| @ -82,6 +84,7 @@ void desktop_free(Desktop* desktop) { | |||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewDebug); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewDebug); | ||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewFirstStart); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewFirstStart); | ||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewHwMismatch); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewHwMismatch); | ||||||
|  |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewPinSetup); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_free(desktop->view_dispatcher); |     view_dispatcher_free(desktop->view_dispatcher); | ||||||
|     scene_manager_free(desktop->scene_manager); |     scene_manager_free(desktop->scene_manager); | ||||||
| @ -92,6 +95,7 @@ void desktop_free(Desktop* desktop) { | |||||||
|     desktop_debug_free(desktop->debug_view); |     desktop_debug_free(desktop->debug_view); | ||||||
|     desktop_first_start_free(desktop->first_start_view); |     desktop_first_start_free(desktop->first_start_view); | ||||||
|     popup_free(desktop->hw_mismatch_popup); |     popup_free(desktop->hw_mismatch_popup); | ||||||
|  |     code_input_free(desktop->code_input); | ||||||
| 
 | 
 | ||||||
|     furi_record_close("gui"); |     furi_record_close("gui"); | ||||||
|     desktop->gui = NULL; |     desktop->gui = NULL; | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ | |||||||
| #include <gui/gui.h> | #include <gui/gui.h> | ||||||
| #include <gui/view_dispatcher.h> | #include <gui/view_dispatcher.h> | ||||||
| #include <gui/modules/popup.h> | #include <gui/modules/popup.h> | ||||||
|  | #include <gui/modules/code_input.h> | ||||||
| #include <gui/scene_manager.h> | #include <gui/scene_manager.h> | ||||||
| #include <assets_icons.h> | #include <assets_icons.h> | ||||||
| #include <storage/storage.h> | #include <storage/storage.h> | ||||||
| @ -29,6 +30,7 @@ typedef enum { | |||||||
|     DesktopViewDebug, |     DesktopViewDebug, | ||||||
|     DesktopViewFirstStart, |     DesktopViewFirstStart, | ||||||
|     DesktopViewHwMismatch, |     DesktopViewHwMismatch, | ||||||
|  |     DesktopViewPinSetup, | ||||||
|     DesktopViewTotal, |     DesktopViewTotal, | ||||||
| } DesktopViewEnum; | } DesktopViewEnum; | ||||||
| 
 | 
 | ||||||
| @ -46,7 +48,10 @@ struct Desktop { | |||||||
|     DesktopLockMenuView* lock_menu; |     DesktopLockMenuView* lock_menu; | ||||||
|     DesktopLockedView* locked_view; |     DesktopLockedView* locked_view; | ||||||
|     DesktopDebugView* debug_view; |     DesktopDebugView* debug_view; | ||||||
|  |     CodeInput* code_input; | ||||||
|  | 
 | ||||||
|     DesktopSettings settings; |     DesktopSettings settings; | ||||||
|  |     PinCode pincode_buffer; | ||||||
| 
 | 
 | ||||||
|     ViewPort* lock_viewport; |     ViewPort* lock_viewport; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -3,11 +3,20 @@ | |||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| 
 | 
 | ||||||
| #define DESKTOP_SETTINGS_VER (0) | #define DESKTOP_SETTINGS_VER (1) | ||||||
|  | #define PIN_MAX_LENGTH 12 | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t length; | ||||||
|  |     uint8_t data[PIN_MAX_LENGTH]; | ||||||
|  | } PinCode; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     uint8_t version; |     uint8_t version; | ||||||
|     uint16_t favorite; |     uint16_t favorite; | ||||||
|  | 
 | ||||||
|  |     PinCode pincode; | ||||||
|  |     bool locked; | ||||||
| } DesktopSettings; | } DesktopSettings; | ||||||
| 
 | 
 | ||||||
| bool desktop_settings_load(DesktopSettings* desktop_settings); | bool desktop_settings_load(DesktopSettings* desktop_settings); | ||||||
|  | |||||||
| @ -33,10 +33,13 @@ DesktopSettingsApp* desktop_settings_app_alloc() { | |||||||
| 
 | 
 | ||||||
|     app->submenu = submenu_alloc(); |     app->submenu = submenu_alloc(); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         app->view_dispatcher, DesktopSettingsAppViewMain, submenu_get_view(app->submenu)); |         app->view_dispatcher, DesktopSettingsAppViewMenu, submenu_get_view(app->submenu)); | ||||||
| 
 | 
 | ||||||
|  |     app->code_input = code_input_alloc(); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         app->view_dispatcher, DesktopSettingsAppViewFavorite, submenu_get_view(app->submenu)); |         app->view_dispatcher, | ||||||
|  |         DesktopSettingsAppViewPincodeInput, | ||||||
|  |         code_input_get_view(app->code_input)); | ||||||
| 
 | 
 | ||||||
|     scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart); |     scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart); | ||||||
|     return app; |     return app; | ||||||
| @ -45,9 +48,10 @@ DesktopSettingsApp* desktop_settings_app_alloc() { | |||||||
| void desktop_settings_app_free(DesktopSettingsApp* app) { | void desktop_settings_app_free(DesktopSettingsApp* app) { | ||||||
|     furi_assert(app); |     furi_assert(app); | ||||||
|     // Variable item list
 |     // Variable item list
 | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMain); |     view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMenu); | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewFavorite); |  | ||||||
|     submenu_free(app->submenu); |     submenu_free(app->submenu); | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewPincodeInput); | ||||||
|  |     code_input_free(app->code_input); | ||||||
|     // View dispatcher
 |     // View dispatcher
 | ||||||
|     view_dispatcher_free(app->view_dispatcher); |     view_dispatcher_free(app->view_dispatcher); | ||||||
|     scene_manager_free(app->scene_manager); |     scene_manager_free(app->scene_manager); | ||||||
|  | |||||||
| @ -6,20 +6,32 @@ | |||||||
| #include <gui/view_dispatcher.h> | #include <gui/view_dispatcher.h> | ||||||
| #include <gui/scene_manager.h> | #include <gui/scene_manager.h> | ||||||
| #include <gui/modules/submenu.h> | #include <gui/modules/submenu.h> | ||||||
|  | #include <gui/modules/code_input.h> | ||||||
| 
 | 
 | ||||||
| #include "desktop_settings.h" | #include "desktop_settings.h" | ||||||
|  | 
 | ||||||
| #include "scenes/desktop_settings_scene.h" | #include "scenes/desktop_settings_scene.h" | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     DesktopSettingsAppViewMain, |     CodeEventsSetPin, | ||||||
|     DesktopSettingsAppViewFavorite, |     CodeEventsChangePin, | ||||||
|  |     CodeEventsDisablePin, | ||||||
|  | } CodeEventsEnum; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     DesktopSettingsAppViewMenu, | ||||||
|  |     DesktopSettingsAppViewPincodeInput, | ||||||
| } DesktopSettingsAppView; | } DesktopSettingsAppView; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     DesktopSettings settings; |     DesktopSettings settings; | ||||||
|  | 
 | ||||||
|     Gui* gui; |     Gui* gui; | ||||||
|     SceneManager* scene_manager; |     SceneManager* scene_manager; | ||||||
|     ViewDispatcher* view_dispatcher; |     ViewDispatcher* view_dispatcher; | ||||||
|     Submenu* submenu; |     Submenu* submenu; | ||||||
|  |     CodeInput* code_input; | ||||||
|  | 
 | ||||||
|  |     uint8_t menu_idx; | ||||||
| 
 | 
 | ||||||
| } DesktopSettingsApp; | } DesktopSettingsApp; | ||||||
|  | |||||||
| @ -1,2 +1,4 @@ | |||||||
| ADD_SCENE(desktop_settings, start, Start) | ADD_SCENE(desktop_settings, start, Start) | ||||||
| ADD_SCENE(desktop_settings, favorite, Favorite) | ADD_SCENE(desktop_settings, favorite, Favorite) | ||||||
|  | ADD_SCENE(desktop_settings, pincode_menu, PinCodeMenu) | ||||||
|  | ADD_SCENE(desktop_settings, pincode_input, PinCodeInput) | ||||||
|  | |||||||
| @ -22,7 +22,7 @@ void desktop_settings_scene_favorite_on_enter(void* context) { | |||||||
| 
 | 
 | ||||||
|     submenu_set_header(app->submenu, "Quick access app:"); |     submenu_set_header(app->submenu, "Quick access app:"); | ||||||
|     submenu_set_selected_item(app->submenu, app->settings.favorite); |     submenu_set_selected_item(app->submenu, app->settings.favorite); | ||||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewFavorite); |     view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) { | bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) { | ||||||
|  | |||||||
| @ -0,0 +1,62 @@ | |||||||
|  | #include "../desktop_settings_app.h" | ||||||
|  | 
 | ||||||
|  | #define SCENE_EXIT_EVENT (0U) | ||||||
|  | 
 | ||||||
|  | void desktop_settings_scene_ok_callback(void* context) { | ||||||
|  |     DesktopSettingsApp* app = context; | ||||||
|  |     uint32_t state = | ||||||
|  |         scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinCodeInput); | ||||||
|  | 
 | ||||||
|  |     if(state == CodeEventsDisablePin) { | ||||||
|  |         memset(app->settings.pincode.data, 0, app->settings.pincode.length * sizeof(uint8_t)); | ||||||
|  |         app->settings.pincode.length = 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void desktop_settings_scene_pincode_input_on_enter(void* context) { | ||||||
|  |     DesktopSettingsApp* app = context; | ||||||
|  |     CodeInput* code_input = app->code_input; | ||||||
|  | 
 | ||||||
|  |     uint32_t state = | ||||||
|  |         scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinCodeInput); | ||||||
|  |     bool update = state != CodeEventsDisablePin; | ||||||
|  | 
 | ||||||
|  |     code_input_set_header_text(code_input, "PIN Code Setup"); | ||||||
|  |     code_input_set_result_callback( | ||||||
|  |         code_input, | ||||||
|  |         desktop_settings_scene_ok_callback, | ||||||
|  |         NULL, | ||||||
|  |         app, | ||||||
|  |         app->settings.pincode.data, | ||||||
|  |         &app->settings.pincode.length, | ||||||
|  |         update); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewPincodeInput); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool desktop_settings_scene_pincode_input_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     DesktopSettingsApp* app = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         switch(event.event) { | ||||||
|  |         case SCENE_EXIT_EVENT: | ||||||
|  |             scene_manager_previous_scene(app->scene_manager); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         default: | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void desktop_settings_scene_pincode_input_on_exit(void* context) { | ||||||
|  |     DesktopSettingsApp* app = context; | ||||||
|  |     code_input_set_result_callback(app->code_input, NULL, NULL, NULL, NULL, NULL, 0); | ||||||
|  |     code_input_set_header_text(app->code_input, ""); | ||||||
|  | } | ||||||
| @ -0,0 +1,78 @@ | |||||||
|  | #include "../desktop_settings_app.h" | ||||||
|  | #include "applications.h" | ||||||
|  | 
 | ||||||
|  | static void desktop_settings_scene_pincode_menu_submenu_callback(void* context, uint32_t index) { | ||||||
|  |     DesktopSettingsApp* app = context; | ||||||
|  |     view_dispatcher_send_custom_event(app->view_dispatcher, index); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void desktop_settings_scene_pincode_menu_on_enter(void* context) { | ||||||
|  |     DesktopSettingsApp* app = context; | ||||||
|  |     Submenu* submenu = app->submenu; | ||||||
|  |     submenu_clean(submenu); | ||||||
|  | 
 | ||||||
|  |     if(!app->settings.pincode.length) { | ||||||
|  |         submenu_add_item( | ||||||
|  |             submenu, | ||||||
|  |             "Set Pin", | ||||||
|  |             CodeEventsSetPin, | ||||||
|  |             desktop_settings_scene_pincode_menu_submenu_callback, | ||||||
|  |             app); | ||||||
|  | 
 | ||||||
|  |     } else { | ||||||
|  |         submenu_add_item( | ||||||
|  |             submenu, | ||||||
|  |             "Change Pin", | ||||||
|  |             CodeEventsChangePin, | ||||||
|  |             desktop_settings_scene_pincode_menu_submenu_callback, | ||||||
|  |             app); | ||||||
|  | 
 | ||||||
|  |         submenu_add_item( | ||||||
|  |             submenu, | ||||||
|  |             "Disable", | ||||||
|  |             CodeEventsDisablePin, | ||||||
|  |             desktop_settings_scene_pincode_menu_submenu_callback, | ||||||
|  |             app); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     submenu_set_header(app->submenu, "Pin code settings:"); | ||||||
|  |     submenu_set_selected_item(app->submenu, app->menu_idx); | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool desktop_settings_scene_pincode_menu_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     DesktopSettingsApp* app = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         switch(event.event) { | ||||||
|  |         case CodeEventsSetPin: | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event); | ||||||
|  |             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case CodeEventsChangePin: | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event); | ||||||
|  |             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case CodeEventsDisablePin: | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 app->scene_manager, DesktopSettingsAppScenePinCodeInput, event.event); | ||||||
|  |             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeInput); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void desktop_settings_scene_pincode_menu_on_exit(void* context) { | ||||||
|  |     DesktopSettingsApp* app = context; | ||||||
|  |     submenu_clean(app->submenu); | ||||||
|  | } | ||||||
| @ -29,7 +29,7 @@ void desktop_settings_scene_start_on_enter(void* context) { | |||||||
|         desktop_settings_scene_start_submenu_callback, |         desktop_settings_scene_start_submenu_callback, | ||||||
|         app); |         app); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMain); |     view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) { | bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||||
| @ -39,7 +39,11 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even | |||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case DesktopSettingsStartSubmenuIndexFavorite: |         case DesktopSettingsStartSubmenuIndexFavorite: | ||||||
|             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppViewFavorite); |             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case DesktopSettingsStartSubmenuIndexPinSetup: | ||||||
|  |             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinCodeMenu); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -4,3 +4,4 @@ ADD_SCENE(desktop, locked, Locked) | |||||||
| ADD_SCENE(desktop, debug, Debug) | ADD_SCENE(desktop, debug, Debug) | ||||||
| ADD_SCENE(desktop, first_start, FirstStart) | ADD_SCENE(desktop, first_start, FirstStart) | ||||||
| ADD_SCENE(desktop, hw_mismatch, HwMismatch) | ADD_SCENE(desktop, hw_mismatch, HwMismatch) | ||||||
|  | ADD_SCENE(desktop, pinsetup, PinSetup) | ||||||
|  | |||||||
| @ -9,7 +9,10 @@ void desktop_scene_lock_menu_callback(DesktopLockMenuEvent event, void* context) | |||||||
| void desktop_scene_lock_menu_on_enter(void* context) { | void desktop_scene_lock_menu_on_enter(void* context) { | ||||||
|     Desktop* desktop = (Desktop*)context; |     Desktop* desktop = (Desktop*)context; | ||||||
| 
 | 
 | ||||||
|  |     desktop_settings_load(&desktop->settings); | ||||||
|  | 
 | ||||||
|     desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop); |     desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop); | ||||||
|  |     desktop_lock_menu_pin_set(desktop->lock_menu, desktop->settings.pincode.length > 0); | ||||||
|     view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLockMenu); |     view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLockMenu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -20,10 +23,25 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { | |||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case DesktopLockMenuEventLock: |         case DesktopLockMenuEventLock: | ||||||
|  |             scene_manager_set_scene_state( | ||||||
|  |                 desktop->scene_manager, DesktopSceneLocked, DesktopLockedNoPin); | ||||||
|             scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); |             scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|  |         case DesktopLockMenuEventPinLock: | ||||||
| 
 | 
 | ||||||
|  |             if(desktop->settings.pincode.length > 0) { | ||||||
|  |                 desktop->settings.locked = true; | ||||||
|  |                 desktop_settings_save(&desktop->settings); | ||||||
|  |                 scene_manager_set_scene_state( | ||||||
|  |                     desktop->scene_manager, DesktopSceneLocked, DesktopLockedWithPin); | ||||||
|  |                 scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); | ||||||
|  |             } else { | ||||||
|  |                 scene_manager_next_scene(desktop->scene_manager, DesktopScenePinSetup); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|         case DesktopLockMenuEventExit: |         case DesktopLockMenuEventExit: | ||||||
|             scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); |             scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|  | |||||||
| @ -15,12 +15,39 @@ void desktop_scene_locked_on_enter(void* context) { | |||||||
|     desktop_locked_update_hint_timeout(locked_view); |     desktop_locked_update_hint_timeout(locked_view); | ||||||
|     desktop_locked_set_dolphin_animation(locked_view); |     desktop_locked_set_dolphin_animation(locked_view); | ||||||
| 
 | 
 | ||||||
|  |     uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopViewLocked); | ||||||
|  | 
 | ||||||
|  |     desktop_locked_with_pin(desktop->locked_view, state == DesktopLockedWithPin); | ||||||
|  | 
 | ||||||
|     view_port_enabled_set(desktop->lock_viewport, true); |     view_port_enabled_set(desktop->lock_viewport, true); | ||||||
|     osTimerStart(locked_view->timer, 63); |     osTimerStart(locked_view->timer, 63); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLocked); |     view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLocked); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static bool desktop_scene_locked_check_pin(Desktop* desktop, DesktopMainEvent event) { | ||||||
|  |     bool match = false; | ||||||
|  | 
 | ||||||
|  |     size_t length = desktop->pincode_buffer.length; | ||||||
|  |     length = code_input_push(desktop->pincode_buffer.data, length, event); | ||||||
|  |     desktop->pincode_buffer.length = length; | ||||||
|  | 
 | ||||||
|  |     match = code_input_compare( | ||||||
|  |         desktop->pincode_buffer.data, | ||||||
|  |         length, | ||||||
|  |         desktop->settings.pincode.data, | ||||||
|  |         desktop->settings.pincode.length); | ||||||
|  | 
 | ||||||
|  |     if(match) { | ||||||
|  |         desktop->pincode_buffer.length = 0; | ||||||
|  |         desktop->settings.locked = false; | ||||||
|  |         desktop_settings_save(&desktop->settings); | ||||||
|  |         desktop_main_unlocked(desktop->main_view); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return match; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { | bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { | ||||||
|     Desktop* desktop = (Desktop*)context; |     Desktop* desktop = (Desktop*)context; | ||||||
| 
 | 
 | ||||||
| @ -36,7 +63,17 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { | |||||||
|         case DesktopLockedEventUpdate: |         case DesktopLockedEventUpdate: | ||||||
|             desktop_locked_manage_redraw(desktop->locked_view); |             desktop_locked_manage_redraw(desktop->locked_view); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case DesktopLockedEventInputReset: | ||||||
|  |             desktop->pincode_buffer.length = 0; | ||||||
|  |             break; | ||||||
|         default: |         default: | ||||||
|  |             if(desktop_scene_locked_check_pin(desktop, event.event)) { | ||||||
|  |                 scene_manager_set_scene_state( | ||||||
|  |                     desktop->scene_manager, DesktopSceneMain, DesktopMainEventUnlocked); | ||||||
|  |                 scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); | ||||||
|  |                 consumed = true; | ||||||
|  |             } | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -34,6 +34,8 @@ void desktop_scene_main_on_enter(void* context) { | |||||||
|     desktop_main_set_callback(main_view, desktop_scene_main_callback, desktop); |     desktop_main_set_callback(main_view, desktop_scene_main_callback, desktop); | ||||||
|     view_port_enabled_set(desktop->lock_viewport, false); |     view_port_enabled_set(desktop->lock_viewport, false); | ||||||
| 
 | 
 | ||||||
|  |     desktop_settings_load(&desktop->settings); | ||||||
|  | 
 | ||||||
|     if(scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneMain) == |     if(scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneMain) == | ||||||
|        DesktopMainEventUnlocked) { |        DesktopMainEventUnlocked) { | ||||||
|         desktop_main_unlocked(desktop->main_view); |         desktop_main_unlocked(desktop->main_view); | ||||||
|  | |||||||
							
								
								
									
										50
									
								
								applications/desktop/scenes/desktop_scene_pinsetup.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								applications/desktop/scenes/desktop_scene_pinsetup.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | |||||||
|  | #include "../desktop_i.h" | ||||||
|  | 
 | ||||||
|  | #define SCENE_EXIT_EVENT (0U) | ||||||
|  | 
 | ||||||
|  | void desktop_scene_ok_callback(void* context) { | ||||||
|  |     Desktop* app = context; | ||||||
|  |     desktop_settings_save(&app->settings); | ||||||
|  |     view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void desktop_scene_pinsetup_on_enter(void* context) { | ||||||
|  |     Desktop* app = context; | ||||||
|  |     CodeInput* code_input = app->code_input; | ||||||
|  | 
 | ||||||
|  |     code_input_set_result_callback( | ||||||
|  |         code_input, | ||||||
|  |         desktop_scene_ok_callback, | ||||||
|  |         NULL, | ||||||
|  |         app, | ||||||
|  |         app->settings.pincode.data, | ||||||
|  |         &app->settings.pincode.length, | ||||||
|  |         true); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, DesktopViewPinSetup); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool desktop_scene_pinsetup_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Desktop* app = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         switch(event.event) { | ||||||
|  |         case SCENE_EXIT_EVENT: | ||||||
|  |             scene_manager_previous_scene(app->scene_manager); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         default: | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void desktop_scene_pinsetup_on_exit(void* context) { | ||||||
|  |     Desktop* app = context; | ||||||
|  |     code_input_set_result_callback(app->code_input, NULL, NULL, NULL, NULL, NULL, 0); | ||||||
|  |     code_input_set_header_text(app->code_input, ""); | ||||||
|  | } | ||||||
| @ -12,6 +12,14 @@ void desktop_lock_menu_set_callback( | |||||||
|     lock_menu->context = context; |     lock_menu->context = context; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void desktop_lock_menu_pin_set(DesktopLockMenuView* lock_menu, bool pin_is_set) { | ||||||
|  |     with_view_model( | ||||||
|  |         lock_menu->view, (DesktopLockMenuViewModel * model) { | ||||||
|  |             model->pin_set = pin_is_set; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu) { | void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu) { | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         lock_menu->view, (DesktopLockMenuViewModel * model) { |         lock_menu->view, (DesktopLockMenuViewModel * model) { | ||||||
| @ -26,6 +34,10 @@ static void lock_menu_callback(void* context, uint8_t index) { | |||||||
|     switch(index) { |     switch(index) { | ||||||
|     case 0: // lock
 |     case 0: // lock
 | ||||||
|         lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context); |         lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context); | ||||||
|  |         break; | ||||||
|  |     case 1: // lock
 | ||||||
|  |         lock_menu->callback(DesktopLockMenuEventPinLock, lock_menu->context); | ||||||
|  |         break; | ||||||
|     default: // wip message
 |     default: // wip message
 | ||||||
|         with_view_model( |         with_view_model( | ||||||
|             lock_menu->view, (DesktopLockMenuViewModel * model) { |             lock_menu->view, (DesktopLockMenuViewModel * model) { | ||||||
| @ -37,7 +49,7 @@ static void lock_menu_callback(void* context, uint8_t index) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void desktop_lock_menu_render(Canvas* canvas, void* model) { | void desktop_lock_menu_render(Canvas* canvas, void* model) { | ||||||
|     const char* Lockmenu_Items[3] = {"Lock", "Set PIN", "DUMB mode"}; |     const char* Lockmenu_Items[3] = {"Lock", "Lock with PIN", "DUMB mode"}; | ||||||
| 
 | 
 | ||||||
|     DesktopLockMenuViewModel* m = model; |     DesktopLockMenuViewModel* m = model; | ||||||
|     canvas_clear(canvas); |     canvas_clear(canvas); | ||||||
| @ -47,13 +59,13 @@ void desktop_lock_menu_render(Canvas* canvas, void* model) { | |||||||
|     canvas_set_font(canvas, FontSecondary); |     canvas_set_font(canvas, FontSecondary); | ||||||
| 
 | 
 | ||||||
|     for(uint8_t i = 0; i < 3; ++i) { |     for(uint8_t i = 0; i < 3; ++i) { | ||||||
|         canvas_draw_str_aligned( |         const char* str = Lockmenu_Items[i]; | ||||||
|             canvas, | 
 | ||||||
|             64, |         if(i == 1 && !m->pin_set) str = "Set PIN"; | ||||||
|             13 + (i * 17), |         if(m->hint_timeout && m->idx == 2 && m->idx == i) str = "Not implemented"; | ||||||
|             AlignCenter, | 
 | ||||||
|             AlignCenter, |         canvas_draw_str_aligned(canvas, 64, 13 + (i * 17), AlignCenter, AlignCenter, str); | ||||||
|             (m->hint_timeout && m->idx == i && m->idx) ? "Not implemented" : Lockmenu_Items[i]); | 
 | ||||||
|         if(m->idx == i) elements_frame(canvas, 15, 5 + (i * 17), 98, 15); |         if(m->idx == i) elements_frame(canvas, 15, 5 + (i * 17), 98, 15); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ | |||||||
| typedef enum { | typedef enum { | ||||||
|     DesktopLockMenuEventLock, |     DesktopLockMenuEventLock, | ||||||
|     DesktopLockMenuEventUnlock, |     DesktopLockMenuEventUnlock, | ||||||
|  |     DesktopLockMenuEventPinLock, | ||||||
|     DesktopLockMenuEventExit, |     DesktopLockMenuEventExit, | ||||||
| } DesktopLockMenuEvent; | } DesktopLockMenuEvent; | ||||||
| 
 | 
 | ||||||
| @ -27,6 +28,7 @@ struct DesktopLockMenuView { | |||||||
| typedef struct { | typedef struct { | ||||||
|     uint8_t idx; |     uint8_t idx; | ||||||
|     uint8_t hint_timeout; |     uint8_t hint_timeout; | ||||||
|  |     bool pin_set; | ||||||
| } DesktopLockMenuViewModel; | } DesktopLockMenuViewModel; | ||||||
| 
 | 
 | ||||||
| void desktop_lock_menu_set_callback( | void desktop_lock_menu_set_callback( | ||||||
| @ -35,6 +37,7 @@ void desktop_lock_menu_set_callback( | |||||||
|     void* context); |     void* context); | ||||||
| 
 | 
 | ||||||
| View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu); | View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu); | ||||||
|  | void desktop_lock_menu_pin_set(DesktopLockMenuView* lock_menu, bool pin_is_set); | ||||||
| void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu); | void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu); | ||||||
| DesktopLockMenuView* desktop_lock_menu_alloc(); | DesktopLockMenuView* desktop_lock_menu_alloc(); | ||||||
| void desktop_lock_menu_free(DesktopLockMenuView* lock_menu); | void desktop_lock_menu_free(DesktopLockMenuView* lock_menu); | ||||||
|  | |||||||
| @ -80,6 +80,14 @@ void desktop_locked_reset_counter(DesktopLockedView* locked_view) { | |||||||
|         }); |         }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void desktop_locked_with_pin(DesktopLockedView* locked_view, bool locked) { | ||||||
|  |     with_view_model( | ||||||
|  |         locked_view->view, (DesktopLockedViewModel * model) { | ||||||
|  |             model->pin_lock = locked; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void desktop_locked_render(Canvas* canvas, void* model) { | void desktop_locked_render(Canvas* canvas, void* model) { | ||||||
|     DesktopLockedViewModel* m = model; |     DesktopLockedViewModel* m = model; | ||||||
|     uint32_t now = osKernelGetTickCount(); |     uint32_t now = osKernelGetTickCount(); | ||||||
| @ -100,7 +108,7 @@ void desktop_locked_render(Canvas* canvas, void* model) { | |||||||
|             canvas_set_font(canvas, FontPrimary); |             canvas_set_font(canvas, FontPrimary); | ||||||
|             elements_multiline_text_framed(canvas, 42, 30, "Locked"); |             elements_multiline_text_framed(canvas, 42, 30, "Locked"); | ||||||
| 
 | 
 | ||||||
|         } else { |         } else if(!m->pin_lock) { | ||||||
|             canvas_set_font(canvas, FontSecondary); |             canvas_set_font(canvas, FontSecondary); | ||||||
|             canvas_draw_icon(canvas, 13, 5, &I_LockPopup_100x49); |             canvas_draw_icon(canvas, 13, 5, &I_LockPopup_100x49); | ||||||
|             elements_multiline_text(canvas, 65, 20, "To unlock\npress:"); |             elements_multiline_text(canvas, 65, 20, "To unlock\npress:"); | ||||||
| @ -116,18 +124,34 @@ View* desktop_locked_get_view(DesktopLockedView* locked_view) { | |||||||
| bool desktop_locked_input(InputEvent* event, void* context) { | bool desktop_locked_input(InputEvent* event, void* context) { | ||||||
|     furi_assert(event); |     furi_assert(event); | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| 
 |  | ||||||
|     DesktopLockedView* locked_view = context; |     DesktopLockedView* locked_view = context; | ||||||
|  | 
 | ||||||
|  |     uint32_t press_time = 0; | ||||||
|  |     bool locked_with_pin = false; | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         locked_view->view, (DesktopLockedViewModel * model) { | ||||||
|  |             locked_with_pin = model->pin_lock; | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|     if(event->type == InputTypeShort) { |     if(event->type == InputTypeShort) { | ||||||
|  |         if(locked_with_pin) { | ||||||
|  |             press_time = osKernelGetTickCount(); | ||||||
|  | 
 | ||||||
|  |             if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT * 3) { | ||||||
|  |                 locked_view->lock_lastpress = press_time; | ||||||
|  |                 locked_view->callback(DesktopLockedEventInputReset, locked_view->context); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             locked_view->callback(event->key, locked_view->context); | ||||||
|  |         } else { | ||||||
|             desktop_locked_update_hint_timeout(locked_view); |             desktop_locked_update_hint_timeout(locked_view); | ||||||
| 
 | 
 | ||||||
|             if(event->key == InputKeyBack) { |             if(event->key == InputKeyBack) { | ||||||
|             uint32_t press_time = osKernelGetTickCount(); |                 press_time = osKernelGetTickCount(); | ||||||
|                 // check if pressed sequentially
 |                 // check if pressed sequentially
 | ||||||
|             if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) { |                 if(press_time - locked_view->lock_lastpress < UNLOCK_RST_TIMEOUT) { | ||||||
|                 locked_view->lock_lastpress = press_time; |  | ||||||
|                 locked_view->lock_count = 0; |  | ||||||
|             } else if(press_time - locked_view->lock_lastpress < UNLOCK_RST_TIMEOUT) { |  | ||||||
|                     locked_view->lock_lastpress = press_time; |                     locked_view->lock_lastpress = press_time; | ||||||
|                     locked_view->lock_count++; |                     locked_view->lock_count++; | ||||||
|                 } |                 } | ||||||
| @ -138,6 +162,12 @@ bool desktop_locked_input(InputEvent* event, void* context) { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         if(press_time - locked_view->lock_lastpress > UNLOCK_RST_TIMEOUT) { | ||||||
|  |             locked_view->lock_lastpress = press_time; | ||||||
|  |             locked_view->lock_count = 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|     // All events consumed
 |     // All events consumed
 | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  | |||||||
| @ -15,10 +15,16 @@ | |||||||
| #define DOOR_R_POS_MIN 60 | #define DOOR_R_POS_MIN 60 | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     DesktopLockedEventUnlock, |     DesktopLockedEventUnlock = 10U, | ||||||
|     DesktopLockedEventUpdate, |     DesktopLockedEventUpdate = 11U, | ||||||
|  |     DesktopLockedEventInputReset = 12U, | ||||||
| } DesktopLockedEvent; | } DesktopLockedEvent; | ||||||
| 
 | 
 | ||||||
|  | typedef enum { | ||||||
|  |     DesktopLockedWithPin, | ||||||
|  |     DesktopLockedNoPin, | ||||||
|  | } DesktopLockedSceneState; | ||||||
|  | 
 | ||||||
| typedef struct DesktopLockedView DesktopLockedView; | typedef struct DesktopLockedView DesktopLockedView; | ||||||
| 
 | 
 | ||||||
| typedef void (*DesktopLockedViewCallback)(DesktopLockedEvent event, void* context); | typedef void (*DesktopLockedViewCallback)(DesktopLockedEvent event, void* context); | ||||||
| @ -42,6 +48,7 @@ typedef struct { | |||||||
|     int8_t door_right_x; |     int8_t door_right_x; | ||||||
|     bool animation_seq_end; |     bool animation_seq_end; | ||||||
| 
 | 
 | ||||||
|  |     bool pin_lock; | ||||||
| } DesktopLockedViewModel; | } DesktopLockedViewModel; | ||||||
| 
 | 
 | ||||||
| void desktop_locked_set_callback( | void desktop_locked_set_callback( | ||||||
| @ -58,5 +65,4 @@ void desktop_locked_manage_redraw(DesktopLockedView* locked_view); | |||||||
| View* desktop_locked_get_view(DesktopLockedView* locked_view); | View* desktop_locked_get_view(DesktopLockedView* locked_view); | ||||||
| DesktopLockedView* desktop_locked_alloc(); | DesktopLockedView* desktop_locked_alloc(); | ||||||
| void desktop_locked_free(DesktopLockedView* locked_view); | void desktop_locked_free(DesktopLockedView* locked_view); | ||||||
| void desktop_main_unlocked(DesktopMainView* main_view); | void desktop_locked_with_pin(DesktopLockedView* lock_menu, bool locked); | ||||||
| void desktop_main_reset_hint(DesktopMainView* main_view); |  | ||||||
| @ -67,6 +67,7 @@ bool desktop_main_input(InputEvent* event, void* context) { | |||||||
|     } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { |     } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { | ||||||
|         main_view->callback(DesktopMainEventOpenFavorite, main_view->context); |         main_view->callback(DesktopMainEventOpenFavorite, main_view->context); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     desktop_main_reset_hint(main_view); |     desktop_main_reset_hint(main_view); | ||||||
| 
 | 
 | ||||||
|     return true; |     return true; | ||||||
|  | |||||||
| @ -7,12 +7,12 @@ | |||||||
| #include <furi.h> | #include <furi.h> | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     DesktopMainEventOpenMenu, |  | ||||||
|     DesktopMainEventOpenLockMenu, |     DesktopMainEventOpenLockMenu, | ||||||
|     DesktopMainEventOpenDebug, |  | ||||||
|     DesktopMainEventUnlocked, |  | ||||||
|     DesktopMainEventOpenArchive, |     DesktopMainEventOpenArchive, | ||||||
|     DesktopMainEventOpenFavorite, |     DesktopMainEventOpenFavorite, | ||||||
|  |     DesktopMainEventOpenMenu, | ||||||
|  |     DesktopMainEventOpenDebug, | ||||||
|  |     DesktopMainEventUnlocked, | ||||||
| } DesktopMainEvent; | } DesktopMainEvent; | ||||||
| 
 | 
 | ||||||
| typedef struct DesktopMainView DesktopMainView; | typedef struct DesktopMainView DesktopMainView; | ||||||
| @ -37,9 +37,8 @@ void desktop_main_set_callback( | |||||||
|     void* context); |     void* context); | ||||||
| 
 | 
 | ||||||
| View* desktop_main_get_view(DesktopMainView* main_view); | View* desktop_main_get_view(DesktopMainView* main_view); | ||||||
| 
 |  | ||||||
| DesktopMainView* desktop_main_alloc(); | DesktopMainView* desktop_main_alloc(); | ||||||
| 
 |  | ||||||
| void desktop_main_free(DesktopMainView* main_view); | void desktop_main_free(DesktopMainView* main_view); | ||||||
| 
 |  | ||||||
| void desktop_main_switch_dolphin_animation(DesktopMainView* main_view); | void desktop_main_switch_dolphin_animation(DesktopMainView* main_view); | ||||||
|  | void desktop_main_unlocked(DesktopMainView* main_view); | ||||||
|  | void desktop_main_reset_hint(DesktopMainView* main_view); | ||||||
|  | |||||||
							
								
								
									
										475
									
								
								applications/gui/modules/code_input.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										475
									
								
								applications/gui/modules/code_input.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,475 @@ | |||||||
|  | #include "code_input.h" | ||||||
|  | #include <gui/elements.h> | ||||||
|  | #include <furi.h> | ||||||
|  | 
 | ||||||
|  | #define MAX_CODE_LEN 10 | ||||||
|  | 
 | ||||||
|  | struct CodeInput { | ||||||
|  |     View* view; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     CodeInputStateVerify, | ||||||
|  |     CodeInputStateUpdate, | ||||||
|  |     CodeInputStateTotal, | ||||||
|  | } CodeInputStateEnum; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     CodeInputFirst, | ||||||
|  |     CodeInputSecond, | ||||||
|  |     CodeInputTotal, | ||||||
|  | } CodeInputsEnum; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t state; | ||||||
|  |     uint8_t current; | ||||||
|  |     bool ext_update; | ||||||
|  | 
 | ||||||
|  |     uint8_t input_length[CodeInputTotal]; | ||||||
|  |     uint8_t local_buffer[CodeInputTotal][MAX_CODE_LEN]; | ||||||
|  | 
 | ||||||
|  |     CodeInputOkCallback ok_callback; | ||||||
|  |     CodeInputFailCallback fail_callback; | ||||||
|  |     void* callback_context; | ||||||
|  | 
 | ||||||
|  |     const char* header; | ||||||
|  | 
 | ||||||
|  |     uint8_t* ext_buffer; | ||||||
|  |     uint8_t* ext_buffer_length; | ||||||
|  | } CodeInputModel; | ||||||
|  | 
 | ||||||
|  | static const Icon* keys_assets[] = { | ||||||
|  |     [InputKeyUp] = &I_ButtonUp_7x4, | ||||||
|  |     [InputKeyDown] = &I_ButtonDown_7x4, | ||||||
|  |     [InputKeyRight] = &I_ButtonRight_4x7, | ||||||
|  |     [InputKeyLeft] = &I_ButtonLeft_4x7, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Compare buffers | ||||||
|  |  *  | ||||||
|  |  * @param in Input buffer pointer | ||||||
|  |  * @param len_in Input array length | ||||||
|  |  * @param src Source buffer pointer | ||||||
|  |  * @param len_src Source array length | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | bool code_input_compare(uint8_t* in, size_t len_in, uint8_t* src, size_t len_src) { | ||||||
|  |     bool result = false; | ||||||
|  |     do { | ||||||
|  |         result = (len_in && len_src); | ||||||
|  |         if(!result) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         result = (len_in == len_src); | ||||||
|  |         if(!result) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         for(size_t i = 0; i < len_in; i++) { | ||||||
|  |             result = (in[i] == src[i]); | ||||||
|  |             if(!result) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Compare local buffers | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static bool code_input_compare_local(CodeInputModel* model) { | ||||||
|  |     uint8_t* source = model->local_buffer[CodeInputFirst]; | ||||||
|  |     size_t source_length = model->input_length[CodeInputFirst]; | ||||||
|  | 
 | ||||||
|  |     uint8_t* input = model->local_buffer[CodeInputSecond]; | ||||||
|  |     size_t input_length = model->input_length[CodeInputSecond]; | ||||||
|  | 
 | ||||||
|  |     return code_input_compare(input, input_length, source, source_length); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Compare ext with local | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static bool code_input_compare_ext(CodeInputModel* model) { | ||||||
|  |     uint8_t* input = model->local_buffer[CodeInputFirst]; | ||||||
|  |     size_t input_length = model->input_length[CodeInputFirst]; | ||||||
|  | 
 | ||||||
|  |     uint8_t* source = model->ext_buffer; | ||||||
|  |     size_t source_length = *model->ext_buffer_length; | ||||||
|  | 
 | ||||||
|  |     return code_input_compare(input, input_length, source, source_length); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Set ext buffer | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static void code_input_set_ext(CodeInputModel* model) { | ||||||
|  |     *model->ext_buffer_length = model->input_length[CodeInputFirst]; | ||||||
|  |     for(size_t i = 0; i <= model->input_length[CodeInputFirst]; i++) { | ||||||
|  |         model->ext_buffer[i] = model->local_buffer[CodeInputFirst][i]; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Draw input sequence | ||||||
|  |  *  | ||||||
|  |  * @param canvas  | ||||||
|  |  * @param buffer  | ||||||
|  |  * @param length  | ||||||
|  |  * @param x  | ||||||
|  |  * @param y  | ||||||
|  |  * @param active | ||||||
|  |  */ | ||||||
|  | static void code_input_draw_sequence( | ||||||
|  |     Canvas* canvas, | ||||||
|  |     uint8_t* buffer, | ||||||
|  |     uint8_t length, | ||||||
|  |     uint8_t x, | ||||||
|  |     uint8_t y, | ||||||
|  |     bool active) { | ||||||
|  |     uint8_t pos_x = x + 6; | ||||||
|  |     uint8_t pos_y = y + 3; | ||||||
|  | 
 | ||||||
|  |     if(active) canvas_draw_icon(canvas, x - 4, y + 5, &I_ButtonRightSmall_3x5); | ||||||
|  | 
 | ||||||
|  |     elements_slightly_rounded_frame(canvas, x, y, 116, 15); | ||||||
|  | 
 | ||||||
|  |     for(size_t i = 0; i < length; i++) { | ||||||
|  |         // maybe symmetrical assets? :-/
 | ||||||
|  |         uint8_t offset_y = buffer[i] < 2 ? 2 + (buffer[i] * 2) : 1; | ||||||
|  |         canvas_draw_icon(canvas, pos_x, pos_y + offset_y, keys_assets[buffer[i]]); | ||||||
|  |         pos_x += buffer[i] > 1 ? 9 : 11; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Reset input count | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static void code_input_reset_count(CodeInputModel* model) { | ||||||
|  |     model->input_length[model->current] = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Call input callback | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static void code_input_call_ok_callback(CodeInputModel* model) { | ||||||
|  |     if(model->ok_callback != NULL) { | ||||||
|  |         model->ok_callback(model->callback_context); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Call changed callback | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static void code_input_call_fail_callback(CodeInputModel* model) { | ||||||
|  |     if(model->fail_callback != NULL) { | ||||||
|  |         model->fail_callback(model->callback_context); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Handle Back button | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static bool code_input_handle_back(CodeInputModel* model) { | ||||||
|  |     if(model->current && !model->input_length[model->current]) { | ||||||
|  |         --model->current; | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(model->input_length[model->current]) { | ||||||
|  |         code_input_reset_count(model); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     code_input_call_fail_callback(model); | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Handle OK button | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  */ | ||||||
|  | static void code_input_handle_ok(CodeInputModel* model) { | ||||||
|  |     switch(model->state) { | ||||||
|  |     case CodeInputStateVerify: | ||||||
|  | 
 | ||||||
|  |         if(code_input_compare_ext(model)) { | ||||||
|  |             if(model->ext_update) { | ||||||
|  |                 model->state = CodeInputStateUpdate; | ||||||
|  |             } else { | ||||||
|  |                 code_input_call_ok_callback(model); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         code_input_reset_count(model); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     case CodeInputStateUpdate: | ||||||
|  | 
 | ||||||
|  |         if(!model->current && model->input_length[model->current]) { | ||||||
|  |             model->current++; | ||||||
|  |         } else { | ||||||
|  |             if(code_input_compare_local(model)) { | ||||||
|  |                 if(model->ext_update) { | ||||||
|  |                     code_input_set_ext(model); | ||||||
|  |                 } | ||||||
|  |                 code_input_call_ok_callback(model); | ||||||
|  |             } else { | ||||||
|  |                 code_input_reset_count(model); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Handle input | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  * @param key  | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | size_t code_input_push(uint8_t* buffer, size_t length, InputKey key) { | ||||||
|  |     buffer[length] = key; | ||||||
|  |     length = CLAMP(length + 1, MAX_CODE_LEN, 0); | ||||||
|  |     return length; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Handle D-pad keys | ||||||
|  |  *  | ||||||
|  |  * @param model  | ||||||
|  |  * @param key  | ||||||
|  |  */ | ||||||
|  | static void code_input_handle_dpad(CodeInputModel* model, InputKey key) { | ||||||
|  |     uint8_t at = model->current; | ||||||
|  |     size_t new_length = code_input_push(model->local_buffer[at], model->input_length[at], key); | ||||||
|  |     model->input_length[at] = new_length; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Draw callback | ||||||
|  |  *  | ||||||
|  |  * @param canvas  | ||||||
|  |  * @param _model  | ||||||
|  |  */ | ||||||
|  | static void code_input_view_draw_callback(Canvas* canvas, void* _model) { | ||||||
|  |     CodeInputModel* model = _model; | ||||||
|  |     uint8_t y_offset = 0; | ||||||
|  |     if(!strlen(model->header)) y_offset = 5; | ||||||
|  |     canvas_clear(canvas); | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  | 
 | ||||||
|  |     canvas_draw_str(canvas, 2, 9, model->header); | ||||||
|  | 
 | ||||||
|  |     canvas_set_font(canvas, FontSecondary); | ||||||
|  | 
 | ||||||
|  |     switch(model->state) { | ||||||
|  |     case CodeInputStateVerify: | ||||||
|  |         code_input_draw_sequence( | ||||||
|  |             canvas, | ||||||
|  |             model->local_buffer[CodeInputFirst], | ||||||
|  |             model->input_length[CodeInputFirst], | ||||||
|  |             6, | ||||||
|  |             30 - y_offset, | ||||||
|  |             true); | ||||||
|  |         break; | ||||||
|  |     case CodeInputStateUpdate: | ||||||
|  |         code_input_draw_sequence( | ||||||
|  |             canvas, | ||||||
|  |             model->local_buffer[CodeInputFirst], | ||||||
|  |             model->input_length[CodeInputFirst], | ||||||
|  |             6, | ||||||
|  |             14 - y_offset, | ||||||
|  |             !model->current); | ||||||
|  |         code_input_draw_sequence( | ||||||
|  |             canvas, | ||||||
|  |             model->local_buffer[CodeInputSecond], | ||||||
|  |             model->input_length[CodeInputSecond], | ||||||
|  |             6, | ||||||
|  |             44 - y_offset, | ||||||
|  |             model->current); | ||||||
|  | 
 | ||||||
|  |         if(model->current) canvas_draw_str(canvas, 2, 39 - y_offset, "Repeat code"); | ||||||
|  | 
 | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Input callback | ||||||
|  |  *  | ||||||
|  |  * @param event  | ||||||
|  |  * @param context  | ||||||
|  |  * @return true  | ||||||
|  |  * @return false  | ||||||
|  |  */ | ||||||
|  | static bool code_input_view_input_callback(InputEvent* event, void* context) { | ||||||
|  |     CodeInput* code_input = context; | ||||||
|  |     furi_assert(code_input); | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == InputTypeShort || event->type == InputTypeRepeat) { | ||||||
|  |         switch(event->key) { | ||||||
|  |         case InputKeyBack: | ||||||
|  |             with_view_model( | ||||||
|  |                 code_input->view, (CodeInputModel * model) { | ||||||
|  |                     consumed = code_input_handle_back(model); | ||||||
|  |                     return true; | ||||||
|  |                 }); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case InputKeyOk: | ||||||
|  |             with_view_model( | ||||||
|  |                 code_input->view, (CodeInputModel * model) { | ||||||
|  |                     code_input_handle_ok(model); | ||||||
|  |                     return true; | ||||||
|  |                 }); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  | 
 | ||||||
|  |             with_view_model( | ||||||
|  |                 code_input->view, (CodeInputModel * model) { | ||||||
|  |                     code_input_handle_dpad(model, event->key); | ||||||
|  |                     return true; | ||||||
|  |                 }); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Reset all input-related data in model | ||||||
|  |  *  | ||||||
|  |  * @param model CodeInputModel | ||||||
|  |  */ | ||||||
|  | static void code_input_reset_model_input_data(CodeInputModel* model) { | ||||||
|  |     model->current = 0; | ||||||
|  |     model->input_length[CodeInputFirst] = 0; | ||||||
|  |     model->input_length[CodeInputSecond] = 0; | ||||||
|  |     model->ext_buffer = NULL; | ||||||
|  |     model->ext_update = false; | ||||||
|  |     model->state = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * @brief Allocate and initialize code input. This code input is used to enter codes. | ||||||
|  |  *  | ||||||
|  |  * @return CodeInput instance pointer | ||||||
|  |  */ | ||||||
|  | CodeInput* code_input_alloc() { | ||||||
|  |     CodeInput* code_input = furi_alloc(sizeof(CodeInput)); | ||||||
|  |     code_input->view = view_alloc(); | ||||||
|  |     view_set_context(code_input->view, code_input); | ||||||
|  |     view_allocate_model(code_input->view, ViewModelTypeLocking, sizeof(CodeInputModel)); | ||||||
|  |     view_set_draw_callback(code_input->view, code_input_view_draw_callback); | ||||||
|  |     view_set_input_callback(code_input->view, code_input_view_input_callback); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         code_input->view, (CodeInputModel * model) { | ||||||
|  |             model->header = ""; | ||||||
|  |             model->ok_callback = NULL; | ||||||
|  |             model->fail_callback = NULL; | ||||||
|  |             model->callback_context = NULL; | ||||||
|  |             code_input_reset_model_input_data(model); | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     return code_input; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * @brief Deinitialize and free code input | ||||||
|  |  *  | ||||||
|  |  * @param code_input Code input instance | ||||||
|  |  */ | ||||||
|  | void code_input_free(CodeInput* code_input) { | ||||||
|  |     furi_assert(code_input); | ||||||
|  |     view_free(code_input->view); | ||||||
|  |     free(code_input); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * @brief Get code input view | ||||||
|  |  *  | ||||||
|  |  * @param code_input code input instance | ||||||
|  |  * @return View instance that can be used for embedding | ||||||
|  |  */ | ||||||
|  | View* code_input_get_view(CodeInput* code_input) { | ||||||
|  |     furi_assert(code_input); | ||||||
|  |     return code_input->view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * @brief Set code input callbacks | ||||||
|  |  *  | ||||||
|  |  * @param code_input code input instance | ||||||
|  |  * @param ok_callback input callback fn | ||||||
|  |  * @param fail_callback code match callback fn | ||||||
|  |  * @param callback_context callback context | ||||||
|  |  * @param buffer buffer  | ||||||
|  |  * @param buffer_length ptr to buffer length uint | ||||||
|  |  * @param ext_update  true to update buffer  | ||||||
|  |  */ | ||||||
|  | void code_input_set_result_callback( | ||||||
|  |     CodeInput* code_input, | ||||||
|  |     CodeInputOkCallback ok_callback, | ||||||
|  |     CodeInputFailCallback fail_callback, | ||||||
|  |     void* callback_context, | ||||||
|  |     uint8_t* buffer, | ||||||
|  |     uint8_t* buffer_length, | ||||||
|  |     bool ext_update) { | ||||||
|  |     with_view_model( | ||||||
|  |         code_input->view, (CodeInputModel * model) { | ||||||
|  |             code_input_reset_model_input_data(model); | ||||||
|  |             model->ok_callback = ok_callback; | ||||||
|  |             model->fail_callback = fail_callback; | ||||||
|  |             model->callback_context = callback_context; | ||||||
|  | 
 | ||||||
|  |             model->ext_buffer = buffer; | ||||||
|  |             model->ext_buffer_length = buffer_length; | ||||||
|  |             model->state = (*buffer_length == 0) ? 1 : 0; | ||||||
|  |             model->ext_update = ext_update; | ||||||
|  | 
 | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Set code input header text | ||||||
|  |  *  | ||||||
|  |  * @param code_input code input instance | ||||||
|  |  * @param text text to be shown | ||||||
|  |  */ | ||||||
|  | void code_input_set_header_text(CodeInput* code_input, const char* text) { | ||||||
|  |     with_view_model( | ||||||
|  |         code_input->view, (CodeInputModel * model) { | ||||||
|  |             model->header = text; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | } | ||||||
							
								
								
									
										91
									
								
								applications/gui/modules/code_input.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								applications/gui/modules/code_input.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,91 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file code_input.h | ||||||
|  |  * GUI: CodeInput keyboard view module API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /** Code input anonymous structure  */ | ||||||
|  | typedef struct CodeInput CodeInput; | ||||||
|  | 
 | ||||||
|  | /** callback that is executed when entered code matches ext buffer */ | ||||||
|  | typedef void (*CodeInputOkCallback)(void* context); | ||||||
|  | 
 | ||||||
|  | /** callback that is executed when entered code does not matches ext buffer */ | ||||||
|  | typedef void (*CodeInputFailCallback)(void* context); | ||||||
|  | 
 | ||||||
|  | /** Allocate and initialize code input. This code input is used to enter codes.
 | ||||||
|  |  * | ||||||
|  |  * @return     CodeInput instance pointer | ||||||
|  |  */ | ||||||
|  | CodeInput* code_input_alloc(); | ||||||
|  | 
 | ||||||
|  | /** Deinitialize and free code input
 | ||||||
|  |  * | ||||||
|  |  * @param      code_input  Code input instance | ||||||
|  |  */ | ||||||
|  | void code_input_free(CodeInput* code_input); | ||||||
|  | 
 | ||||||
|  | /** Get code input view
 | ||||||
|  |  * | ||||||
|  |  * @param      code_input  code input instance | ||||||
|  |  * | ||||||
|  |  * @return     View instance that can be used for embedding | ||||||
|  |  */ | ||||||
|  | View* code_input_get_view(CodeInput* code_input); | ||||||
|  | 
 | ||||||
|  | /** Set code input result callback
 | ||||||
|  |  * | ||||||
|  |  * @param      code_input        code input instance | ||||||
|  |  * @param      ok_callback    ok callback fn | ||||||
|  |  * @param      fail_callback  fail callback fn | ||||||
|  |  * @param      callback_context  callback context | ||||||
|  |  * @param      buffer       buffer to use | ||||||
|  |  * @param      buffer_length       buffer length | ||||||
|  |  * @param      update  set true to update buffer  | ||||||
|  |  */ | ||||||
|  | void code_input_set_result_callback( | ||||||
|  |     CodeInput* code_input, | ||||||
|  |     CodeInputOkCallback ok_callback, | ||||||
|  |     CodeInputFailCallback fail_callback, | ||||||
|  |     void* callback_context, | ||||||
|  |     uint8_t* buffer, | ||||||
|  |     uint8_t* buffer_length, | ||||||
|  |     bool update); | ||||||
|  | 
 | ||||||
|  | /** Set code input header text
 | ||||||
|  |  * | ||||||
|  |  * @param      code_input  code input instance | ||||||
|  |  * @param      text        text to be shown | ||||||
|  |  */ | ||||||
|  | void code_input_set_header_text(CodeInput* code_input, const char* text); | ||||||
|  | 
 | ||||||
|  | /** Compare two buffers
 | ||||||
|  |  * | ||||||
|  |  * @param      in       buffer to compare to source | ||||||
|  |  * @param      len_in   length of input buffer | ||||||
|  |  * @param      src      source buffer | ||||||
|  |  * @param      len_src  length of insourceput buffer | ||||||
|  |  * @return     true if buffers match | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | bool code_input_compare(uint8_t* in, size_t len_in, uint8_t* src, size_t len_src); | ||||||
|  | 
 | ||||||
|  | /** Push input into the end of array
 | ||||||
|  |  * | ||||||
|  |  * @param      buffer   buffer | ||||||
|  |  * @param      length   length of buffer | ||||||
|  |  * @param      key      input key | ||||||
|  |  * @return     new length of input buffer | ||||||
|  |  */ | ||||||
|  | size_t code_input_push(uint8_t* buffer, size_t length, InputKey key); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 its your bedtime
						its your bedtime