[FL-2430] Automatic Desktop Locking (#1101)
* Add Auto Lock Time setting * Update .gitignore * Add value_index toolbox module * Auto locking basic implementation * Better AutoLock implementation, edge cases and cleanup * Fix NULL pointer crash * Turn off backlight shortly in locked mode * Re-enable auto lock after pin lock * Correctly handle start when pin locked * Use timer to hide locked hint * Use a single state variable instead of multiple bools * Do not call update callback recursively * Allow input when the Unlocked hint is shown * Add a delay to backlight switch off while locking * Better user input handling * Switch backlight off after pin timeout * Correct grammar in notification settings Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									779d319069
								
							
						
					
					
						commit
						917be9c6d3
					
				
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -36,3 +36,6 @@ CMakeLists.txt | |||||||
| 
 | 
 | ||||||
| # bundle output | # bundle output | ||||||
| dist | dist | ||||||
|  | 
 | ||||||
|  | # kde | ||||||
|  | .directory | ||||||
|  | |||||||
| @ -1,6 +1,9 @@ | |||||||
| #include <storage/storage.h> | #include <storage/storage.h> | ||||||
| #include <assets_icons.h> | #include <assets_icons.h> | ||||||
|  | #include <gui/gui.h> | ||||||
| #include <gui/view_stack.h> | #include <gui/view_stack.h> | ||||||
|  | #include <notification/notification.h> | ||||||
|  | #include <notification/notification_messages.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| 
 | 
 | ||||||
| @ -13,6 +16,10 @@ | |||||||
| #include "desktop_i.h" | #include "desktop_i.h" | ||||||
| #include "desktop_helpers.h" | #include "desktop_helpers.h" | ||||||
| 
 | 
 | ||||||
|  | static void desktop_auto_lock_arm(Desktop*); | ||||||
|  | static void desktop_auto_lock_inhibit(Desktop*); | ||||||
|  | static void desktop_start_auto_lock_timer(Desktop*); | ||||||
|  | 
 | ||||||
| static void desktop_loader_callback(const void* message, void* context) { | static void desktop_loader_callback(const void* message, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     Desktop* desktop = context; |     Desktop* desktop = context; | ||||||
| @ -37,9 +44,19 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) { | |||||||
|     switch(event) { |     switch(event) { | ||||||
|     case DesktopGlobalBeforeAppStarted: |     case DesktopGlobalBeforeAppStarted: | ||||||
|         animation_manager_unload_and_stall_animation(desktop->animation_manager); |         animation_manager_unload_and_stall_animation(desktop->animation_manager); | ||||||
|  |         desktop_auto_lock_inhibit(desktop); | ||||||
|         return true; |         return true; | ||||||
|     case DesktopGlobalAfterAppFinished: |     case DesktopGlobalAfterAppFinished: | ||||||
|         animation_manager_load_and_continue_animation(desktop->animation_manager); |         animation_manager_load_and_continue_animation(desktop->animation_manager); | ||||||
|  |         // TODO: Implement a message mechanism for loading settings and (optionally)
 | ||||||
|  |         // locking and unlocking
 | ||||||
|  |         LOAD_DESKTOP_SETTINGS(&desktop->settings); | ||||||
|  |         desktop_auto_lock_arm(desktop); | ||||||
|  |         return true; | ||||||
|  |     case DesktopGlobalAutoLock: | ||||||
|  |         if(!loader_is_locked(desktop->loader)) { | ||||||
|  |             desktop_lock(desktop); | ||||||
|  |         } | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -58,6 +75,63 @@ static void desktop_tick_event_callback(void* context) { | |||||||
|     scene_manager_handle_tick_event(app->scene_manager); |     scene_manager_handle_tick_event(app->scene_manager); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void desktop_input_event_callback(const void* value, void* context) { | ||||||
|  |     furi_assert(value); | ||||||
|  |     furi_assert(context); | ||||||
|  |     const InputEvent* event = value; | ||||||
|  |     Desktop* desktop = context; | ||||||
|  |     if(event->type == InputTypePress) { | ||||||
|  |         desktop_start_auto_lock_timer(desktop); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void desktop_auto_lock_timer_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Desktop* desktop = context; | ||||||
|  |     view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAutoLock); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void desktop_start_auto_lock_timer(Desktop* desktop) { | ||||||
|  |     osTimerStart( | ||||||
|  |         desktop->auto_lock_timer, furi_hal_ms_to_ticks(desktop->settings.auto_lock_delay_ms)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void desktop_stop_auto_lock_timer(Desktop* desktop) { | ||||||
|  |     osTimerStop(desktop->auto_lock_timer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void desktop_auto_lock_arm(Desktop* desktop) { | ||||||
|  |     if(desktop->settings.auto_lock_delay_ms) { | ||||||
|  |         desktop->input_events_subscription = furi_pubsub_subscribe( | ||||||
|  |             desktop->input_events_pubsub, desktop_input_event_callback, desktop); | ||||||
|  |         desktop_start_auto_lock_timer(desktop); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void desktop_auto_lock_inhibit(Desktop* desktop) { | ||||||
|  |     desktop_stop_auto_lock_timer(desktop); | ||||||
|  |     if(desktop->input_events_subscription) { | ||||||
|  |         furi_pubsub_unsubscribe(desktop->input_events_pubsub, desktop->input_events_subscription); | ||||||
|  |         desktop->input_events_subscription = NULL; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void desktop_lock(Desktop* desktop) { | ||||||
|  |     desktop_auto_lock_inhibit(desktop); | ||||||
|  |     scene_manager_set_scene_state( | ||||||
|  |         desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); | ||||||
|  |     scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); | ||||||
|  |     notification_message(desktop->notification, &sequence_display_off_delay_1000); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void desktop_unlock(Desktop* desktop) { | ||||||
|  |     furi_hal_rtc_set_pin_fails(0); | ||||||
|  |     desktop_helpers_unlock_system(desktop); | ||||||
|  |     desktop_view_locked_unlock(desktop->locked_view); | ||||||
|  |     scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain); | ||||||
|  |     desktop_auto_lock_arm(desktop); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Desktop* desktop_alloc() { | Desktop* desktop_alloc() { | ||||||
|     Desktop* desktop = malloc(sizeof(Desktop)); |     Desktop* desktop = malloc(sizeof(Desktop)); | ||||||
| 
 | 
 | ||||||
| @ -146,9 +220,17 @@ Desktop* desktop_alloc() { | |||||||
|        animation_manager_is_animation_loaded(desktop->animation_manager)) { |        animation_manager_is_animation_loaded(desktop->animation_manager)) { | ||||||
|         animation_manager_unload_and_stall_animation(desktop->animation_manager); |         animation_manager_unload_and_stall_animation(desktop->animation_manager); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     desktop->notification = furi_record_open("notification"); | ||||||
|     desktop->app_start_stop_subscription = furi_pubsub_subscribe( |     desktop->app_start_stop_subscription = furi_pubsub_subscribe( | ||||||
|         loader_get_pubsub(desktop->loader), desktop_loader_callback, desktop); |         loader_get_pubsub(desktop->loader), desktop_loader_callback, desktop); | ||||||
| 
 | 
 | ||||||
|  |     desktop->input_events_pubsub = furi_record_open("input_events"); | ||||||
|  |     desktop->input_events_subscription = NULL; | ||||||
|  | 
 | ||||||
|  |     desktop->auto_lock_timer = | ||||||
|  |         osTimerNew(desktop_auto_lock_timer_callback, osTimerOnce, desktop, NULL); | ||||||
|  | 
 | ||||||
|     return desktop; |     return desktop; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -157,8 +239,17 @@ void desktop_free(Desktop* desktop) { | |||||||
| 
 | 
 | ||||||
|     furi_pubsub_unsubscribe( |     furi_pubsub_unsubscribe( | ||||||
|         loader_get_pubsub(desktop->loader), desktop->app_start_stop_subscription); |         loader_get_pubsub(desktop->loader), desktop->app_start_stop_subscription); | ||||||
|  | 
 | ||||||
|  |     if(desktop->input_events_subscription) { | ||||||
|  |         furi_pubsub_unsubscribe(desktop->input_events_pubsub, desktop->input_events_subscription); | ||||||
|  |         desktop->input_events_subscription = NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     desktop->loader = NULL; |     desktop->loader = NULL; | ||||||
|  |     desktop->input_events_pubsub = NULL; | ||||||
|     furi_record_close("loader"); |     furi_record_close("loader"); | ||||||
|  |     furi_record_close("notification"); | ||||||
|  |     furi_record_close("input_events"); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdMain); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdMain); | ||||||
|     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLockMenu); |     view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLockMenu); | ||||||
| @ -191,6 +282,8 @@ void desktop_free(Desktop* desktop) { | |||||||
| 
 | 
 | ||||||
|     furi_record_close("menu"); |     furi_record_close("menu"); | ||||||
| 
 | 
 | ||||||
|  |     osTimerDelete(desktop->auto_lock_timer); | ||||||
|  | 
 | ||||||
|     free(desktop); |     free(desktop); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -214,14 +307,16 @@ int32_t desktop_srv(void* p) { | |||||||
| 
 | 
 | ||||||
|     scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); |     scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); | ||||||
| 
 | 
 | ||||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { |     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock) && !desktop->settings.pin_code.length) { | ||||||
|         if(desktop->settings.pin_code.length > 0) { |  | ||||||
|             scene_manager_set_scene_state( |  | ||||||
|                 desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); |  | ||||||
|             scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); |  | ||||||
|         } else { |  | ||||||
|         furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); |         furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { | ||||||
|  |         if(!loader_is_locked(desktop->loader)) { | ||||||
|  |             desktop_auto_lock_arm(desktop); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         desktop_lock(desktop); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(desktop_is_first_start()) { |     if(desktop_is_first_start()) { | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ | |||||||
| #include <gui/scene_manager.h> | #include <gui/scene_manager.h> | ||||||
| 
 | 
 | ||||||
| #include <loader/loader.h> | #include <loader/loader.h> | ||||||
|  | #include <notification/notification_app.h> | ||||||
| 
 | 
 | ||||||
| #define STATUS_BAR_Y_SHIFT 13 | #define STATUS_BAR_Y_SHIFT 13 | ||||||
| 
 | 
 | ||||||
| @ -59,10 +60,18 @@ struct Desktop { | |||||||
|     ViewPort* lock_viewport; |     ViewPort* lock_viewport; | ||||||
| 
 | 
 | ||||||
|     AnimationManager* animation_manager; |     AnimationManager* animation_manager; | ||||||
|  | 
 | ||||||
|     Loader* loader; |     Loader* loader; | ||||||
|  |     NotificationApp* notification; | ||||||
|  | 
 | ||||||
|     FuriPubSubSubscription* app_start_stop_subscription; |     FuriPubSubSubscription* app_start_stop_subscription; | ||||||
|  |     FuriPubSub* input_events_pubsub; | ||||||
|  |     FuriPubSubSubscription* input_events_subscription; | ||||||
|  |     osTimerId_t auto_lock_timer; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| Desktop* desktop_alloc(); | Desktop* desktop_alloc(); | ||||||
| 
 | 
 | ||||||
| void desktop_free(Desktop* desktop); | void desktop_free(Desktop* desktop); | ||||||
|  | void desktop_lock(Desktop* desktop); | ||||||
|  | void desktop_unlock(Desktop* desktop); | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ | |||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <toolbox/saved_struct.h> | #include <toolbox/saved_struct.h> | ||||||
| 
 | 
 | ||||||
| #define DESKTOP_SETTINGS_VER (1) | #define DESKTOP_SETTINGS_VER (2) | ||||||
| #define DESKTOP_SETTINGS_PATH "/int/desktop.settings" | #define DESKTOP_SETTINGS_PATH "/int/desktop.settings" | ||||||
| #define DESKTOP_SETTINGS_MAGIC (0x17) | #define DESKTOP_SETTINGS_MAGIC (0x17) | ||||||
| #define PIN_MAX_LENGTH 12 | #define PIN_MAX_LENGTH 12 | ||||||
| @ -39,6 +39,7 @@ typedef struct { | |||||||
| typedef struct { | typedef struct { | ||||||
|     uint16_t favorite; |     uint16_t favorite; | ||||||
|     PinCode pin_code; |     PinCode pin_code; | ||||||
|  |     uint32_t auto_lock_delay_ms; | ||||||
| } DesktopSettings; | } DesktopSettings; | ||||||
| 
 | 
 | ||||||
| static inline bool pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2) { | static inline bool pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2) { | ||||||
|  | |||||||
| @ -36,12 +36,17 @@ DesktopSettingsApp* desktop_settings_app_alloc() { | |||||||
| 
 | 
 | ||||||
|     app->popup = popup_alloc(); |     app->popup = popup_alloc(); | ||||||
|     app->submenu = submenu_alloc(); |     app->submenu = submenu_alloc(); | ||||||
|  |     app->variable_item_list = variable_item_list_alloc(); | ||||||
|     app->pin_input_view = desktop_view_pin_input_alloc(); |     app->pin_input_view = desktop_view_pin_input_alloc(); | ||||||
|     app->pin_setup_howto_view = desktop_settings_view_pin_setup_howto_alloc(); |     app->pin_setup_howto_view = desktop_settings_view_pin_setup_howto_alloc(); | ||||||
|     app->pin_setup_howto2_view = desktop_settings_view_pin_setup_howto2_alloc(); |     app->pin_setup_howto2_view = desktop_settings_view_pin_setup_howto2_alloc(); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         app->view_dispatcher, DesktopSettingsAppViewMenu, submenu_get_view(app->submenu)); |         app->view_dispatcher, DesktopSettingsAppViewMenu, submenu_get_view(app->submenu)); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, | ||||||
|  |         DesktopSettingsAppViewVarItemList, | ||||||
|  |         variable_item_list_get_view(app->variable_item_list)); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
|         app->view_dispatcher, DesktopSettingsAppViewIdPopup, popup_get_view(app->popup)); |         app->view_dispatcher, DesktopSettingsAppViewIdPopup, popup_get_view(app->popup)); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
| @ -63,10 +68,12 @@ 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, DesktopSettingsAppViewMenu); |     view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMenu); | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList); | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup); |     view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup); | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput); |     view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput); | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto); |     view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto); | ||||||
|     view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto2); |     view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto2); | ||||||
|  |     variable_item_list_free(app->variable_item_list); | ||||||
|     submenu_free(app->submenu); |     submenu_free(app->submenu); | ||||||
|     popup_free(app->popup); |     popup_free(app->popup); | ||||||
|     desktop_view_pin_input_free(app->pin_input_view); |     desktop_view_pin_input_free(app->pin_input_view); | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ | |||||||
| #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/variable_item_list.h> | ||||||
| 
 | 
 | ||||||
| #include "desktop_settings.h" | #include "desktop_settings.h" | ||||||
| #include "desktop/views/desktop_view_pin_input.h" | #include "desktop/views/desktop_view_pin_input.h" | ||||||
| @ -13,6 +14,7 @@ | |||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     DesktopSettingsAppViewMenu, |     DesktopSettingsAppViewMenu, | ||||||
|  |     DesktopSettingsAppViewVarItemList, | ||||||
|     DesktopSettingsAppViewIdPopup, |     DesktopSettingsAppViewIdPopup, | ||||||
|     DesktopSettingsAppViewIdPinInput, |     DesktopSettingsAppViewIdPinInput, | ||||||
|     DesktopSettingsAppViewIdPinSetupHowto, |     DesktopSettingsAppViewIdPinSetupHowto, | ||||||
| @ -25,6 +27,7 @@ typedef struct { | |||||||
|     Gui* gui; |     Gui* gui; | ||||||
|     SceneManager* scene_manager; |     SceneManager* scene_manager; | ||||||
|     ViewDispatcher* view_dispatcher; |     ViewDispatcher* view_dispatcher; | ||||||
|  |     VariableItemList* variable_item_list; | ||||||
|     Submenu* submenu; |     Submenu* submenu; | ||||||
|     Popup* popup; |     Popup* popup; | ||||||
|     DesktopViewPinInput* pin_input_view; |     DesktopViewPinInput* pin_input_view; | ||||||
|  | |||||||
| @ -1,35 +1,65 @@ | |||||||
| #include <applications.h> | #include <applications.h> | ||||||
|  | #include <lib/toolbox/value_index.h> | ||||||
| 
 | 
 | ||||||
| #include "../desktop_settings_app.h" | #include "../desktop_settings_app.h" | ||||||
| #include "desktop_settings_scene.h" | #include "desktop_settings_scene.h" | ||||||
| 
 | 
 | ||||||
| #define SCENE_EVENT_SELECT_FAVORITE 0 | #define SCENE_EVENT_SELECT_FAVORITE 0 | ||||||
| #define SCENE_EVENT_SELECT_PIN_SETUP 1 | #define SCENE_EVENT_SELECT_PIN_SETUP 1 | ||||||
|  | #define SCENE_EVENT_SELECT_AUTO_LOCK_DELAY 2 | ||||||
| 
 | 
 | ||||||
| static void desktop_settings_scene_start_submenu_callback(void* context, uint32_t index) { | #define AUTO_LOCK_DELAY_COUNT 6 | ||||||
|  | const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = { | ||||||
|  |     "OFF", | ||||||
|  |     "30s", | ||||||
|  |     "60s", | ||||||
|  |     "2min", | ||||||
|  |     "5min", | ||||||
|  |     "10min", | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const uint32_t auto_lock_delay_value[AUTO_LOCK_DELAY_COUNT] = | ||||||
|  |     {0, 30000, 60000, 120000, 300000, 600000}; | ||||||
|  | 
 | ||||||
|  | static void desktop_settings_scene_start_var_list_enter_callback(void* context, uint32_t index) { | ||||||
|     DesktopSettingsApp* app = context; |     DesktopSettingsApp* app = context; | ||||||
|     view_dispatcher_send_custom_event(app->view_dispatcher, index); |     view_dispatcher_send_custom_event(app->view_dispatcher, index); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void desktop_settings_scene_start_auto_lock_delay_changed(VariableItem* item) { | ||||||
|  |     DesktopSettingsApp* app = variable_item_get_context(item); | ||||||
|  |     uint8_t index = variable_item_get_current_value_index(item); | ||||||
|  | 
 | ||||||
|  |     variable_item_set_current_value_text(item, auto_lock_delay_text[index]); | ||||||
|  |     app->settings.auto_lock_delay_ms = auto_lock_delay_value[index]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void desktop_settings_scene_start_on_enter(void* context) { | void desktop_settings_scene_start_on_enter(void* context) { | ||||||
|     DesktopSettingsApp* app = context; |     DesktopSettingsApp* app = context; | ||||||
|     Submenu* submenu = app->submenu; |     VariableItemList* variable_item_list = app->variable_item_list; | ||||||
| 
 | 
 | ||||||
|     submenu_add_item( |     VariableItem* item; | ||||||
|         submenu, |     uint8_t value_index; | ||||||
|         "Favorite App", | 
 | ||||||
|         SCENE_EVENT_SELECT_FAVORITE, |     variable_item_list_add(variable_item_list, "Favorite App", 1, NULL, NULL); | ||||||
|         desktop_settings_scene_start_submenu_callback, | 
 | ||||||
|  |     variable_item_list_add(variable_item_list, "PIN Setup", 1, NULL, NULL); | ||||||
|  | 
 | ||||||
|  |     item = variable_item_list_add( | ||||||
|  |         variable_item_list, | ||||||
|  |         "Auto Lock Time", | ||||||
|  |         AUTO_LOCK_DELAY_COUNT, | ||||||
|  |         desktop_settings_scene_start_auto_lock_delay_changed, | ||||||
|         app); |         app); | ||||||
| 
 | 
 | ||||||
|     submenu_add_item( |     variable_item_list_set_enter_callback( | ||||||
|         submenu, |         variable_item_list, desktop_settings_scene_start_var_list_enter_callback, app); | ||||||
|         "PIN Setup", |     value_index = value_index_uint32( | ||||||
|         SCENE_EVENT_SELECT_PIN_SETUP, |         app->settings.auto_lock_delay_ms, auto_lock_delay_value, AUTO_LOCK_DELAY_COUNT); | ||||||
|         desktop_settings_scene_start_submenu_callback, |     variable_item_set_current_value_index(item, value_index); | ||||||
|         app); |     variable_item_set_current_value_text(item, auto_lock_delay_text[value_index]); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); |     view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) { | bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||||
| @ -46,6 +76,9 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even | |||||||
|             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu); |             scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|  |         case SCENE_EVENT_SELECT_AUTO_LOCK_DELAY: | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return consumed; |     return consumed; | ||||||
| @ -53,5 +86,6 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even | |||||||
| 
 | 
 | ||||||
| void desktop_settings_scene_start_on_exit(void* context) { | void desktop_settings_scene_start_on_exit(void* context) { | ||||||
|     DesktopSettingsApp* app = context; |     DesktopSettingsApp* app = context; | ||||||
|     submenu_reset(app->submenu); |     variable_item_list_reset(app->variable_item_list); | ||||||
|  |     SAVE_DESKTOP_SETTINGS(&app->settings); | ||||||
| } | } | ||||||
|  | |||||||
| @ -48,17 +48,13 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { | |||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case DesktopLockMenuEventLock: |         case DesktopLockMenuEventLock: | ||||||
|             scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); |             scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); | ||||||
|             scene_manager_set_scene_state( |             desktop_lock(desktop); | ||||||
|                 desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); |  | ||||||
|             scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); |  | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case DesktopLockMenuEventPinLock: |         case DesktopLockMenuEventPinLock: | ||||||
|             if(desktop->settings.pin_code.length > 0) { |             if(desktop->settings.pin_code.length > 0) { | ||||||
|                 furi_hal_rtc_set_flag(FuriHalRtcFlagLock); |                 furi_hal_rtc_set_flag(FuriHalRtcFlagLock); | ||||||
|                 scene_manager_set_scene_state( |                 desktop_lock(desktop); | ||||||
|                     desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); |  | ||||||
|                 scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); |  | ||||||
|             } else { |             } else { | ||||||
|                 LoaderStatus status = |                 LoaderStatus status = | ||||||
|                     loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG); |                     loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG); | ||||||
|  | |||||||
| @ -81,13 +81,13 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { | |||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case DesktopLockedEventUnlocked: |         case DesktopLockedEventUnlocked: | ||||||
|             furi_hal_rtc_set_pin_fails(0); |             desktop_unlock(desktop); | ||||||
|             desktop_helpers_unlock_system(desktop); |  | ||||||
|             scene_manager_search_and_switch_to_previous_scene( |  | ||||||
|                 desktop->scene_manager, DesktopSceneMain); |  | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case DesktopLockedEventUpdate: |         case DesktopLockedEventUpdate: | ||||||
|  |             if(desktop_view_locked_is_locked_hint_visible(desktop->locked_view)) { | ||||||
|  |                 notification_message(desktop->notification, &sequence_display_off); | ||||||
|  |             } | ||||||
|             desktop_view_locked_update(desktop->locked_view); |             desktop_view_locked_update(desktop->locked_view); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|  | |||||||
| @ -129,16 +129,13 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) { | |||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case DesktopPinInputEventUnlocked: |         case DesktopPinInputEventUnlocked: | ||||||
|             desktop_view_locked_unlock(desktop->locked_view); |             desktop_unlock(desktop); | ||||||
|             furi_hal_rtc_set_pin_fails(0); |  | ||||||
|             desktop_helpers_unlock_system(desktop); |  | ||||||
|             scene_manager_search_and_switch_to_previous_scene( |  | ||||||
|                 desktop->scene_manager, DesktopSceneMain); |  | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case DesktopPinInputEventBack: |         case DesktopPinInputEventBack: | ||||||
|             scene_manager_search_and_switch_to_previous_scene( |             scene_manager_search_and_switch_to_previous_scene( | ||||||
|                 desktop->scene_manager, DesktopSceneLocked); |                 desktop->scene_manager, DesktopSceneLocked); | ||||||
|  |             notification_message(desktop->notification, &sequence_display_off); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -38,4 +38,5 @@ typedef enum { | |||||||
|     // Global events
 |     // Global events
 | ||||||
|     DesktopGlobalBeforeAppStarted, |     DesktopGlobalBeforeAppStarted, | ||||||
|     DesktopGlobalAfterAppFinished, |     DesktopGlobalAfterAppFinished, | ||||||
|  |     DesktopGlobalAutoLock, | ||||||
| } DesktopEvent; | } DesktopEvent; | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ | |||||||
| #include "desktop_view_locked.h" | #include "desktop_view_locked.h" | ||||||
| 
 | 
 | ||||||
| #define DOOR_MOVING_INTERVAL_MS (1000 / 16) | #define DOOR_MOVING_INTERVAL_MS (1000 / 16) | ||||||
|  | #define LOCKED_HINT_TIMEOUT_MS (1000) | ||||||
| #define UNLOCKED_HINT_TIMEOUT_MS (2000) | #define UNLOCKED_HINT_TIMEOUT_MS (2000) | ||||||
| 
 | 
 | ||||||
| #define DOOR_OFFSET_START -55 | #define DOOR_OFFSET_START -55 | ||||||
| @ -32,14 +33,18 @@ struct DesktopViewLocked { | |||||||
|     uint32_t lock_lastpress; |     uint32_t lock_lastpress; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef enum { | ||||||
|     uint32_t hint_icon_expire_at; |     DesktopViewLockedStateUnlocked, | ||||||
|     bool unlocked_hint; |     DesktopViewLockedStateLocked, | ||||||
|     bool locked; |     DesktopViewLockedStateDoorsClosing, | ||||||
|     bool pin_locked; |     DesktopViewLockedStateLockedHintShown, | ||||||
|  |     DesktopViewLockedStateUnlockedHintShown | ||||||
|  | } DesktopViewLockedState; | ||||||
| 
 | 
 | ||||||
|  | typedef struct { | ||||||
|  |     bool pin_locked; | ||||||
|     int8_t door_offset; |     int8_t door_offset; | ||||||
|     bool doors_closing; |     DesktopViewLockedState view_state; | ||||||
| } DesktopViewLockedModel; | } DesktopViewLockedModel; | ||||||
| 
 | 
 | ||||||
| void desktop_view_locked_set_callback( | void desktop_view_locked_set_callback( | ||||||
| @ -78,52 +83,55 @@ static bool desktop_view_locked_doors_move(DesktopViewLockedModel* model) { | |||||||
| 
 | 
 | ||||||
| static void desktop_view_locked_update_hint_icon_timeout(DesktopViewLocked* locked_view) { | static void desktop_view_locked_update_hint_icon_timeout(DesktopViewLocked* locked_view) { | ||||||
|     DesktopViewLockedModel* model = view_get_model(locked_view->view); |     DesktopViewLockedModel* model = view_get_model(locked_view->view); | ||||||
|     model->hint_icon_expire_at = osKernelGetTickCount() + osKernelGetTickFreq(); |     const bool change_state = (model->view_state == DesktopViewLockedStateLocked) && | ||||||
|     view_commit_model(locked_view->view, true); |                               !model->pin_locked; | ||||||
|  |     if(change_state) { | ||||||
|  |         model->view_state = DesktopViewLockedStateLockedHintShown; | ||||||
|  |     } | ||||||
|  |     view_commit_model(locked_view->view, change_state); | ||||||
|  |     xTimerChangePeriod(locked_view->timer, pdMS_TO_TICKS(LOCKED_HINT_TIMEOUT_MS), portMAX_DELAY); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void desktop_view_locked_update(DesktopViewLocked* locked_view) { | void desktop_view_locked_update(DesktopViewLocked* locked_view) { | ||||||
|     bool stop_timer = false; |  | ||||||
| 
 |  | ||||||
|     DesktopViewLockedModel* model = view_get_model(locked_view->view); |     DesktopViewLockedModel* model = view_get_model(locked_view->view); | ||||||
|     if(model->locked) { |     DesktopViewLockedState view_state = model->view_state; | ||||||
|         model->doors_closing = desktop_view_locked_doors_move(model); | 
 | ||||||
|         stop_timer = !model->doors_closing; |     if(view_state == DesktopViewLockedStateDoorsClosing && | ||||||
|     } else { |        !desktop_view_locked_doors_move(model)) { | ||||||
|         model->unlocked_hint = false; |         model->view_state = DesktopViewLockedStateLocked; | ||||||
|         stop_timer = true; |     } else if(view_state == DesktopViewLockedStateLockedHintShown) { | ||||||
|  |         model->view_state = DesktopViewLockedStateLocked; | ||||||
|  |     } else if(view_state == DesktopViewLockedStateUnlockedHintShown) { | ||||||
|  |         model->view_state = DesktopViewLockedStateUnlocked; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     view_commit_model(locked_view->view, true); |     view_commit_model(locked_view->view, true); | ||||||
| 
 | 
 | ||||||
|     if(stop_timer) { |     if(view_state != DesktopViewLockedStateDoorsClosing) { | ||||||
|         xTimerStop(locked_view->timer, portMAX_DELAY); |         xTimerStop(locked_view->timer, portMAX_DELAY); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void desktop_view_locked_draw(Canvas* canvas, void* model) { | static void desktop_view_locked_draw(Canvas* canvas, void* model) { | ||||||
|     DesktopViewLockedModel* m = model; |     DesktopViewLockedModel* m = model; | ||||||
|     uint32_t now = osKernelGetTickCount(); |     DesktopViewLockedState view_state = m->view_state; | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
| 
 | 
 | ||||||
|     if(m->locked) { |     if(view_state == DesktopViewLockedStateDoorsClosing) { | ||||||
|         if(m->doors_closing) { |  | ||||||
|         desktop_view_locked_doors_draw(canvas, m); |         desktop_view_locked_doors_draw(canvas, m); | ||||||
|         canvas_set_font(canvas, FontPrimary); |         canvas_set_font(canvas, FontPrimary); | ||||||
|         elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Locked"); |         elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Locked"); | ||||||
|         } else if((now < m->hint_icon_expire_at) && !m->pin_locked) { |     } else if(view_state == DesktopViewLockedStateLockedHintShown) { | ||||||
|         canvas_set_font(canvas, FontSecondary); |         canvas_set_font(canvas, FontSecondary); | ||||||
|         elements_bold_rounded_frame(canvas, 14, 2 + STATUS_BAR_Y_SHIFT, 99, 48); |         elements_bold_rounded_frame(canvas, 14, 2 + STATUS_BAR_Y_SHIFT, 99, 48); | ||||||
|         elements_multiline_text(canvas, 65, 20 + STATUS_BAR_Y_SHIFT, "To unlock\npress:"); |         elements_multiline_text(canvas, 65, 20 + STATUS_BAR_Y_SHIFT, "To unlock\npress:"); | ||||||
|         canvas_draw_icon(canvas, 65, 36 + STATUS_BAR_Y_SHIFT, &I_Back3_45x8); |         canvas_draw_icon(canvas, 65, 36 + STATUS_BAR_Y_SHIFT, &I_Back3_45x8); | ||||||
|         canvas_draw_icon(canvas, 16, 7 + STATUS_BAR_Y_SHIFT, &I_WarningDolphin_45x42); |         canvas_draw_icon(canvas, 16, 7 + STATUS_BAR_Y_SHIFT, &I_WarningDolphin_45x42); | ||||||
|         canvas_draw_dot(canvas, 17, 61); |         canvas_draw_dot(canvas, 17, 61); | ||||||
|         } |     } else if(view_state == DesktopViewLockedStateUnlockedHintShown) { | ||||||
|     } else { |  | ||||||
|         if(m->unlocked_hint) { |  | ||||||
|         canvas_set_font(canvas, FontPrimary); |         canvas_set_font(canvas, FontPrimary); | ||||||
|         elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Unlocked"); |         elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Unlocked"); | ||||||
|     } |     } | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| View* desktop_view_locked_get_view(DesktopViewLocked* locked_view) { | View* desktop_view_locked_get_view(DesktopViewLocked* locked_view) { | ||||||
| @ -134,43 +142,38 @@ View* desktop_view_locked_get_view(DesktopViewLocked* locked_view) { | |||||||
| static bool desktop_view_locked_input(InputEvent* event, void* context) { | static bool desktop_view_locked_input(InputEvent* event, void* context) { | ||||||
|     furi_assert(event); |     furi_assert(event); | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|  | 
 | ||||||
|  |     bool is_changed = false; | ||||||
|  |     const uint32_t press_time = xTaskGetTickCount(); | ||||||
|     DesktopViewLocked* locked_view = context; |     DesktopViewLocked* locked_view = context; | ||||||
|     bool locked = false; |  | ||||||
|     bool locked_with_pin = false; |  | ||||||
|     bool doors_closing = false; |  | ||||||
|     uint32_t press_time = xTaskGetTickCount(); |  | ||||||
| 
 |  | ||||||
|     { |  | ||||||
|     DesktopViewLockedModel* model = view_get_model(locked_view->view); |     DesktopViewLockedModel* model = view_get_model(locked_view->view); | ||||||
|         bool changed = false; |     if(model->view_state == DesktopViewLockedStateUnlockedHintShown && | ||||||
|         locked = model->locked; |        event->type == InputTypePress) { | ||||||
|         locked_with_pin = model->pin_locked; |         model->view_state = DesktopViewLockedStateUnlocked; | ||||||
|         doors_closing = model->doors_closing; |         is_changed = true; | ||||||
|         if(!locked && model->unlocked_hint && event->type == InputTypePress) { |  | ||||||
|             model->unlocked_hint = false; |  | ||||||
|             changed = true; |  | ||||||
|         } |  | ||||||
|         view_commit_model(locked_view->view, changed); |  | ||||||
|     } |     } | ||||||
|  |     const DesktopViewLockedState view_state = model->view_state; | ||||||
|  |     const bool pin_locked = model->pin_locked; | ||||||
|  |     view_commit_model(locked_view->view, is_changed); | ||||||
| 
 | 
 | ||||||
|     if(!locked || doors_closing || (event->type != InputTypeShort)) { |     if(view_state == DesktopViewLockedStateUnlocked || event->type != InputTypeShort) { | ||||||
|         return locked; |         return view_state != DesktopViewLockedStateUnlocked; | ||||||
|     } |     } else if(view_state == DesktopViewLockedStateLocked && pin_locked) { | ||||||
| 
 |  | ||||||
|     if(locked_with_pin) { |  | ||||||
|         locked_view->callback(DesktopLockedEventShowPinInput, locked_view->context); |         locked_view->callback(DesktopLockedEventShowPinInput, locked_view->context); | ||||||
|     } else { |     } else if( | ||||||
|  |         view_state == DesktopViewLockedStateLocked || | ||||||
|  |         view_state == DesktopViewLockedStateLockedHintShown) { | ||||||
|         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_lastpress = press_time; | ||||||
|             locked_view->lock_count = 0; |             locked_view->lock_count = 0; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         desktop_view_locked_update_hint_icon_timeout(locked_view); |         desktop_view_locked_update_hint_icon_timeout(locked_view); | ||||||
|  | 
 | ||||||
|         if(event->key == InputKeyBack) { |         if(event->key == InputKeyBack) { | ||||||
|             locked_view->lock_lastpress = press_time; |             locked_view->lock_lastpress = press_time; | ||||||
|             locked_view->lock_count++; |             locked_view->lock_count++; | ||||||
|             if(locked_view->lock_count == UNLOCK_CNT) { |             if(locked_view->lock_count == UNLOCK_CNT) { | ||||||
|                 desktop_view_locked_unlock(locked_view); |  | ||||||
|                 locked_view->callback(DesktopLockedEventUnlocked, locked_view->context); |                 locked_view->callback(DesktopLockedEventUnlocked, locked_view->context); | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
| @ -180,7 +183,7 @@ static bool desktop_view_locked_input(InputEvent* event, void* context) { | |||||||
|         locked_view->lock_lastpress = press_time; |         locked_view->lock_lastpress = press_time; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return locked; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| DesktopViewLocked* desktop_view_locked_alloc() { | DesktopViewLocked* desktop_view_locked_alloc() { | ||||||
| @ -189,7 +192,6 @@ DesktopViewLocked* desktop_view_locked_alloc() { | |||||||
|     locked_view->timer = |     locked_view->timer = | ||||||
|         xTimerCreate(NULL, 1000 / 16, pdTRUE, locked_view, locked_view_timer_callback); |         xTimerCreate(NULL, 1000 / 16, pdTRUE, locked_view, locked_view_timer_callback); | ||||||
| 
 | 
 | ||||||
|     locked_view->view = view_alloc(); |  | ||||||
|     view_allocate_model(locked_view->view, ViewModelTypeLocking, sizeof(DesktopViewLockedModel)); |     view_allocate_model(locked_view->view, ViewModelTypeLocking, sizeof(DesktopViewLockedModel)); | ||||||
|     view_set_context(locked_view->view, locked_view); |     view_set_context(locked_view->view, locked_view); | ||||||
|     view_set_draw_callback(locked_view->view, desktop_view_locked_draw); |     view_set_draw_callback(locked_view->view, desktop_view_locked_draw); | ||||||
| @ -207,7 +209,8 @@ void desktop_view_locked_free(DesktopViewLocked* locked_view) { | |||||||
| 
 | 
 | ||||||
| void desktop_view_locked_close_doors(DesktopViewLocked* locked_view) { | void desktop_view_locked_close_doors(DesktopViewLocked* locked_view) { | ||||||
|     DesktopViewLockedModel* model = view_get_model(locked_view->view); |     DesktopViewLockedModel* model = view_get_model(locked_view->view); | ||||||
|     model->doors_closing = true; |     furi_assert(model->view_state == DesktopViewLockedStateLocked); | ||||||
|  |     model->view_state = DesktopViewLockedStateDoorsClosing; | ||||||
|     model->door_offset = DOOR_OFFSET_START; |     model->door_offset = DOOR_OFFSET_START; | ||||||
|     view_commit_model(locked_view->view, true); |     view_commit_model(locked_view->view, true); | ||||||
|     xTimerChangePeriod(locked_view->timer, pdMS_TO_TICKS(DOOR_MOVING_INTERVAL_MS), portMAX_DELAY); |     xTimerChangePeriod(locked_view->timer, pdMS_TO_TICKS(DOOR_MOVING_INTERVAL_MS), portMAX_DELAY); | ||||||
| @ -215,19 +218,24 @@ void desktop_view_locked_close_doors(DesktopViewLocked* locked_view) { | |||||||
| 
 | 
 | ||||||
| void desktop_view_locked_lock(DesktopViewLocked* locked_view, bool pin_locked) { | void desktop_view_locked_lock(DesktopViewLocked* locked_view, bool pin_locked) { | ||||||
|     DesktopViewLockedModel* model = view_get_model(locked_view->view); |     DesktopViewLockedModel* model = view_get_model(locked_view->view); | ||||||
|     model->locked = true; |     furi_assert(model->view_state == DesktopViewLockedStateUnlocked); | ||||||
|  |     model->view_state = DesktopViewLockedStateLocked; | ||||||
|     model->pin_locked = pin_locked; |     model->pin_locked = pin_locked; | ||||||
|     view_commit_model(locked_view->view, true); |     view_commit_model(locked_view->view, true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void desktop_view_locked_unlock(DesktopViewLocked* locked_view) { | void desktop_view_locked_unlock(DesktopViewLocked* locked_view) { | ||||||
|     furi_assert(locked_view); |  | ||||||
| 
 |  | ||||||
|     locked_view->lock_count = 0; |     locked_view->lock_count = 0; | ||||||
|     DesktopViewLockedModel* model = view_get_model(locked_view->view); |     DesktopViewLockedModel* model = view_get_model(locked_view->view); | ||||||
|     model->locked = false; |     model->view_state = DesktopViewLockedStateUnlockedHintShown; | ||||||
|     model->pin_locked = false; |     model->pin_locked = false; | ||||||
|     model->unlocked_hint = true; |  | ||||||
|     view_commit_model(locked_view->view, true); |     view_commit_model(locked_view->view, true); | ||||||
|     xTimerChangePeriod(locked_view->timer, pdMS_TO_TICKS(UNLOCKED_HINT_TIMEOUT_MS), portMAX_DELAY); |     xTimerChangePeriod(locked_view->timer, pdMS_TO_TICKS(UNLOCKED_HINT_TIMEOUT_MS), portMAX_DELAY); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | bool desktop_view_locked_is_locked_hint_visible(DesktopViewLocked* locked_view) { | ||||||
|  |     DesktopViewLockedModel* model = view_get_model(locked_view->view); | ||||||
|  |     const DesktopViewLockedState view_state = model->view_state; | ||||||
|  |     view_commit_model(locked_view->view, false); | ||||||
|  |     return view_state == DesktopViewLockedStateLockedHintShown; | ||||||
|  | } | ||||||
|  | |||||||
| @ -19,3 +19,4 @@ void desktop_view_locked_free(DesktopViewLocked* locked_view); | |||||||
| void desktop_view_locked_lock(DesktopViewLocked* locked_view, bool pin_locked); | void desktop_view_locked_lock(DesktopViewLocked* locked_view, bool pin_locked); | ||||||
| void desktop_view_locked_unlock(DesktopViewLocked* locked_view); | void desktop_view_locked_unlock(DesktopViewLocked* locked_view); | ||||||
| void desktop_view_locked_close_doors(DesktopViewLocked* locked_view); | void desktop_view_locked_close_doors(DesktopViewLocked* locked_view); | ||||||
|  | bool desktop_view_locked_is_locked_hint_visible(DesktopViewLocked* locked_view); | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| #include <storage/storage.h> | #include <storage/storage.h> | ||||||
|  | #include <input/input.h> | ||||||
| #include "notification.h" | #include "notification.h" | ||||||
| #include "notification_messages.h" | #include "notification_messages.h" | ||||||
| #include "notification_app.h" | #include "notification_app.h" | ||||||
| @ -416,8 +417,13 @@ static bool notification_save_settings(NotificationApp* app) { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void input_event_callback(const void* value, void* context) { | static void input_event_callback(const void* value, void* context) { | ||||||
|  |     furi_assert(value); | ||||||
|  |     furi_assert(context); | ||||||
|  |     const InputEvent* event = value; | ||||||
|     NotificationApp* app = context; |     NotificationApp* app = context; | ||||||
|  |     if(event->type == InputTypePress) { | ||||||
|         notification_message(app, &sequence_display_on); |         notification_message(app, &sequence_display_on); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // App alloc
 | // App alloc
 | ||||||
|  | |||||||
| @ -21,7 +21,7 @@ const NotificationMessage message_display_lock = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const NotificationMessage message_display_unlock = { | const NotificationMessage message_display_unlock = { | ||||||
|     .type = NotificationMessageTypeLedDisplayLock, |     .type = NotificationMessageTypeLedDisplayUnlock, | ||||||
|     .data.led.value = 0x00, |     .data.led.value = 0x00, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -208,6 +208,12 @@ const NotificationSequence sequence_display_unlock = { | |||||||
|     NULL, |     NULL, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | const NotificationSequence sequence_display_off_delay_1000 = { | ||||||
|  |     &message_delay_1000, | ||||||
|  |     &message_display_off, | ||||||
|  |     NULL, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| // Charging
 | // Charging
 | ||||||
| const NotificationSequence sequence_charging = { | const NotificationSequence sequence_charging = { | ||||||
|     &message_red_255, |     &message_red_255, | ||||||
|  | |||||||
| @ -78,6 +78,8 @@ extern const NotificationSequence sequence_display_off; | |||||||
| extern const NotificationSequence sequence_display_lock; | extern const NotificationSequence sequence_display_lock; | ||||||
| /** Display: backlight always on unlock */ | /** Display: backlight always on unlock */ | ||||||
| extern const NotificationSequence sequence_display_unlock; | extern const NotificationSequence sequence_display_unlock; | ||||||
|  | /** Display: backlight force off after a delay of 1000ms */ | ||||||
|  | extern const NotificationSequence sequence_display_off_delay_1000; | ||||||
| 
 | 
 | ||||||
| // Charging
 | // Charging
 | ||||||
| extern const NotificationSequence sequence_charging; | extern const NotificationSequence sequence_charging; | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| #include "notification_app.h" | #include "notification_app.h" | ||||||
| #include <gui/modules/variable_item_list.h> | #include <gui/modules/variable_item_list.h> | ||||||
| #include <gui/view_dispatcher.h> | #include <gui/view_dispatcher.h> | ||||||
|  | #include <lib/toolbox/value_index.h> | ||||||
| 
 | 
 | ||||||
| #define MAX_NOTIFICATION_SETTINGS 4 | #define MAX_NOTIFICATION_SETTINGS 4 | ||||||
| 
 | 
 | ||||||
| @ -63,44 +64,6 @@ const char* const vibro_text[VIBRO_COUNT] = { | |||||||
| }; | }; | ||||||
| const bool vibro_value[VIBRO_COUNT] = {false, true}; | const bool vibro_value[VIBRO_COUNT] = {false, true}; | ||||||
| 
 | 
 | ||||||
| uint8_t float_value_index(const float value, const float values[], uint8_t values_count) { |  | ||||||
|     const float epsilon = 0.01f; |  | ||||||
|     float last_value = values[0]; |  | ||||||
|     uint8_t index = 0; |  | ||||||
|     for(uint8_t i = 0; i < values_count; i++) { |  | ||||||
|         if((value >= last_value - epsilon) && (value <= values[i] + epsilon)) { |  | ||||||
|             index = i; |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         last_value = values[i]; |  | ||||||
|     } |  | ||||||
|     return index; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| uint8_t uint32_value_index(const uint32_t value, const uint32_t values[], uint8_t values_count) { |  | ||||||
|     int64_t last_value = INT64_MIN; |  | ||||||
|     uint8_t index = 0; |  | ||||||
|     for(uint8_t i = 0; i < values_count; i++) { |  | ||||||
|         if((value >= last_value) && (value <= values[i])) { |  | ||||||
|             index = i; |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         last_value = values[i]; |  | ||||||
|     } |  | ||||||
|     return index; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| uint8_t bool_value_index(const bool value, const bool values[], uint8_t values_count) { |  | ||||||
|     uint8_t index = 0; |  | ||||||
|     for(uint8_t i = 0; i < values_count; i++) { |  | ||||||
|         if(value == values[i]) { |  | ||||||
|             index = i; |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return index; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void backlight_changed(VariableItem* item) { | static void backlight_changed(VariableItem* item) { | ||||||
|     NotificationAppSettings* app = variable_item_get_context(item); |     NotificationAppSettings* app = variable_item_get_context(item); | ||||||
|     uint8_t index = variable_item_get_current_value_index(item); |     uint8_t index = variable_item_get_current_value_index(item); | ||||||
| @ -164,21 +127,21 @@ static NotificationAppSettings* alloc_settings() { | |||||||
| 
 | 
 | ||||||
|     item = variable_item_list_add( |     item = variable_item_list_add( | ||||||
|         app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app); |         app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app); | ||||||
|     value_index = float_value_index( |     value_index = value_index_float( | ||||||
|         app->notification->settings.display_brightness, backlight_value, BACKLIGHT_COUNT); |         app->notification->settings.display_brightness, backlight_value, BACKLIGHT_COUNT); | ||||||
|     variable_item_set_current_value_index(item, value_index); |     variable_item_set_current_value_index(item, value_index); | ||||||
|     variable_item_set_current_value_text(item, backlight_text[value_index]); |     variable_item_set_current_value_text(item, backlight_text[value_index]); | ||||||
| 
 | 
 | ||||||
|     item = variable_item_list_add( |     item = variable_item_list_add( | ||||||
|         app->variable_item_list, "Backlight Time", DELAY_COUNT, screen_changed, app); |         app->variable_item_list, "Backlight Time", DELAY_COUNT, screen_changed, app); | ||||||
|     value_index = uint32_value_index( |     value_index = value_index_uint32( | ||||||
|         app->notification->settings.display_off_delay_ms, delay_value, DELAY_COUNT); |         app->notification->settings.display_off_delay_ms, delay_value, DELAY_COUNT); | ||||||
|     variable_item_set_current_value_index(item, value_index); |     variable_item_set_current_value_index(item, value_index); | ||||||
|     variable_item_set_current_value_text(item, delay_text[value_index]); |     variable_item_set_current_value_text(item, delay_text[value_index]); | ||||||
| 
 | 
 | ||||||
|     item = variable_item_list_add( |     item = variable_item_list_add( | ||||||
|         app->variable_item_list, "LED Brightness", BACKLIGHT_COUNT, led_changed, app); |         app->variable_item_list, "LED Brightness", BACKLIGHT_COUNT, led_changed, app); | ||||||
|     value_index = float_value_index( |     value_index = value_index_float( | ||||||
|         app->notification->settings.led_brightness, backlight_value, BACKLIGHT_COUNT); |         app->notification->settings.led_brightness, backlight_value, BACKLIGHT_COUNT); | ||||||
|     variable_item_set_current_value_index(item, value_index); |     variable_item_set_current_value_index(item, value_index); | ||||||
|     variable_item_set_current_value_text(item, backlight_text[value_index]); |     variable_item_set_current_value_text(item, backlight_text[value_index]); | ||||||
| @ -186,13 +149,13 @@ static NotificationAppSettings* alloc_settings() { | |||||||
|     item = variable_item_list_add( |     item = variable_item_list_add( | ||||||
|         app->variable_item_list, "Volume", VOLUME_COUNT, volume_changed, app); |         app->variable_item_list, "Volume", VOLUME_COUNT, volume_changed, app); | ||||||
|     value_index = |     value_index = | ||||||
|         float_value_index(app->notification->settings.speaker_volume, volume_value, VOLUME_COUNT); |         value_index_float(app->notification->settings.speaker_volume, volume_value, VOLUME_COUNT); | ||||||
|     variable_item_set_current_value_index(item, value_index); |     variable_item_set_current_value_index(item, value_index); | ||||||
|     variable_item_set_current_value_text(item, volume_text[value_index]); |     variable_item_set_current_value_text(item, volume_text[value_index]); | ||||||
| 
 | 
 | ||||||
|     item = |     item = | ||||||
|         variable_item_list_add(app->variable_item_list, "Vibro", VIBRO_COUNT, vibro_changed, app); |         variable_item_list_add(app->variable_item_list, "Vibro", VIBRO_COUNT, vibro_changed, app); | ||||||
|     value_index = bool_value_index(app->notification->settings.vibro_on, vibro_value, VIBRO_COUNT); |     value_index = value_index_bool(app->notification->settings.vibro_on, vibro_value, VIBRO_COUNT); | ||||||
|     variable_item_set_current_value_index(item, value_index); |     variable_item_set_current_value_index(item, value_index); | ||||||
|     variable_item_set_current_value_text(item, vibro_text[value_index]); |     variable_item_set_current_value_text(item, vibro_text[value_index]); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,19 +1,6 @@ | |||||||
| #include "system_settings.h" | #include "system_settings.h" | ||||||
| #include <loader/loader.h> | #include <loader/loader.h> | ||||||
| 
 | #include <lib/toolbox/value_index.h> | ||||||
| static uint8_t |  | ||||||
|     uint32_value_index(const uint32_t value, const uint32_t values[], uint8_t values_count) { |  | ||||||
|     int64_t last_value = INT64_MIN; |  | ||||||
|     uint8_t index = 0; |  | ||||||
|     for(uint8_t i = 0; i < values_count; i++) { |  | ||||||
|         if((value >= last_value) && (value <= values[i])) { |  | ||||||
|             index = i; |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         last_value = values[i]; |  | ||||||
|     } |  | ||||||
|     return index; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| const char* const log_level_text[] = { | const char* const log_level_text[] = { | ||||||
|     "Default", |     "Default", | ||||||
| @ -80,7 +67,7 @@ SystemSettings* system_settings_alloc() { | |||||||
| 
 | 
 | ||||||
|     item = variable_item_list_add( |     item = variable_item_list_add( | ||||||
|         app->var_item_list, "Log Level", COUNT_OF(log_level_text), log_level_changed, app); |         app->var_item_list, "Log Level", COUNT_OF(log_level_text), log_level_changed, app); | ||||||
|     value_index = uint32_value_index( |     value_index = value_index_uint32( | ||||||
|         furi_hal_rtc_get_log_level(), log_level_value, COUNT_OF(log_level_text)); |         furi_hal_rtc_get_log_level(), log_level_value, COUNT_OF(log_level_text)); | ||||||
|     variable_item_set_current_value_index(item, value_index); |     variable_item_set_current_value_index(item, value_index); | ||||||
|     variable_item_set_current_value_text(item, log_level_text[value_index]); |     variable_item_set_current_value_text(item, log_level_text[value_index]); | ||||||
|  | |||||||
| @ -26,6 +26,10 @@ uint32_t furi_hal_get_tick(void) { | |||||||
|     return tick_cnt; |     return tick_cnt; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | uint32_t furi_hal_ms_to_ticks(float milliseconds) { | ||||||
|  |     return milliseconds / (1000.0f / osKernelGetTickFreq()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void furi_hal_delay_us(float microseconds) { | void furi_hal_delay_us(float microseconds) { | ||||||
|     uint32_t start = DWT->CYCCNT; |     uint32_t start = DWT->CYCCNT; | ||||||
|     uint32_t time_ticks = microseconds * furi_hal_delay_instructions_per_microsecond(); |     uint32_t time_ticks = microseconds * furi_hal_delay_instructions_per_microsecond(); | ||||||
|  | |||||||
| @ -31,6 +31,13 @@ void furi_hal_tick(void); | |||||||
|  */ |  */ | ||||||
| uint32_t furi_hal_get_tick(void); | uint32_t furi_hal_get_tick(void); | ||||||
| 
 | 
 | ||||||
|  | /** Convert milliseconds to ticks
 | ||||||
|  |  * | ||||||
|  |  * @param[in]   milliseconds    time in milliseconds | ||||||
|  |  * @return      time in ticks | ||||||
|  |  */ | ||||||
|  | uint32_t furi_hal_ms_to_ticks(float milliseconds); | ||||||
|  | 
 | ||||||
| /** Delay in milliseconds
 | /** Delay in milliseconds
 | ||||||
|  * @warning    Cannot be used from ISR |  * @warning    Cannot be used from ISR | ||||||
|  * |  * | ||||||
|  | |||||||
							
								
								
									
										39
									
								
								lib/toolbox/value_index.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								lib/toolbox/value_index.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | #include "value_index.h" | ||||||
|  | 
 | ||||||
|  | uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_t values_count) { | ||||||
|  |     int64_t last_value = INT64_MIN; | ||||||
|  |     uint8_t index = 0; | ||||||
|  |     for(uint8_t i = 0; i < values_count; i++) { | ||||||
|  |         if((value >= last_value) && (value <= values[i])) { | ||||||
|  |             index = i; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         last_value = values[i]; | ||||||
|  |     } | ||||||
|  |     return index; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t value_index_float(const float value, const float values[], uint8_t values_count) { | ||||||
|  |     const float epsilon = 0.01f; | ||||||
|  |     float last_value = values[0]; | ||||||
|  |     uint8_t index = 0; | ||||||
|  |     for(uint8_t i = 0; i < values_count; i++) { | ||||||
|  |         if((value >= last_value - epsilon) && (value <= values[i] + epsilon)) { | ||||||
|  |             index = i; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         last_value = values[i]; | ||||||
|  |     } | ||||||
|  |     return index; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t value_index_bool(const bool value, const bool values[], uint8_t values_count) { | ||||||
|  |     uint8_t index = 0; | ||||||
|  |     for(uint8_t i = 0; i < values_count; i++) { | ||||||
|  |         if(value == values[i]) { | ||||||
|  |             index = i; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return index; | ||||||
|  | } | ||||||
							
								
								
									
										51
									
								
								lib/toolbox/value_index.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								lib/toolbox/value_index.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /** Get the index of a uint32_t array element which is closest to the given value.
 | ||||||
|  |  * | ||||||
|  |  * Returned index corresponds to the first element found. | ||||||
|  |  * If no suitable elements were found, the function returns 0. | ||||||
|  |  * | ||||||
|  |  * @param   value           value to be searched. | ||||||
|  |  * @param   values          pointer to the array to perform the search in. | ||||||
|  |  * @param   values_count    array size. | ||||||
|  |  * | ||||||
|  |  * @return value's index. | ||||||
|  |  */ | ||||||
|  | uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_t values_count); | ||||||
|  | 
 | ||||||
|  | /** Get the index of a float array element which is closest to the given value.
 | ||||||
|  |  * | ||||||
|  |  * Returned index corresponds to the first element found. | ||||||
|  |  * If no suitable elements were found, the function returns 0. | ||||||
|  |  * | ||||||
|  |  * @param   value           value to be searched. | ||||||
|  |  * @param   values          pointer to the array to perform the search in. | ||||||
|  |  * @param   values_count    array size. | ||||||
|  |  * | ||||||
|  |  * @return value's index. | ||||||
|  |  */ | ||||||
|  | uint8_t value_index_float(const float value, const float values[], uint8_t values_count); | ||||||
|  | 
 | ||||||
|  | /** Get the index of a bool array element which is equal to the given value.
 | ||||||
|  |  * | ||||||
|  |  * Returned index corresponds to the first element found. | ||||||
|  |  * If no suitable elements were found, the function returns 0. | ||||||
|  |  * | ||||||
|  |  * @param   value           value to be searched. | ||||||
|  |  * @param   values          pointer to the array to perform the search in. | ||||||
|  |  * @param   values_count    array size. | ||||||
|  |  * | ||||||
|  |  * @return value's index. | ||||||
|  |  */ | ||||||
|  | uint8_t value_index_bool(const bool value, const bool values[], uint8_t values_count); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Georgii Surkov
						Georgii Surkov