From 917be9c6d3c9b85c40eb9b40f1060d1b3c43e45a Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Thu, 14 Apr 2022 15:20:41 +0300 Subject: [PATCH] [FL-2430] Automatic Desktop Locking (#1101) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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: あく --- .gitignore | 3 + applications/desktop/desktop.c | 109 +++++++++++++- applications/desktop/desktop_i.h | 9 ++ .../desktop_settings/desktop_settings.h | 3 +- .../desktop_settings/desktop_settings_app.c | 7 + .../desktop_settings/desktop_settings_app.h | 3 + .../scenes/desktop_settings_scene_start.c | 64 ++++++-- .../desktop/scenes/desktop_scene_lock_menu.c | 8 +- .../desktop/scenes/desktop_scene_locked.c | 8 +- .../desktop/scenes/desktop_scene_pin_input.c | 7 +- applications/desktop/views/desktop_events.h | 1 + .../desktop/views/desktop_view_locked.c | 142 +++++++++--------- .../desktop/views/desktop_view_locked.h | 1 + applications/notification/notification_app.c | 8 +- .../notification/notification_messages.c | 10 +- .../notification/notification_messages.h | 2 + .../notification/notification_settings_app.c | 49 +----- applications/system/system_settings.c | 17 +-- firmware/targets/f7/furi_hal/furi_hal_delay.c | 4 + .../targets/furi_hal_include/furi_hal_delay.h | 7 + lib/toolbox/value_index.c | 39 +++++ lib/toolbox/value_index.h | 51 +++++++ 22 files changed, 386 insertions(+), 166 deletions(-) create mode 100644 lib/toolbox/value_index.c create mode 100644 lib/toolbox/value_index.h diff --git a/.gitignore b/.gitignore index 319cf90c..6577101d 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,6 @@ CMakeLists.txt # bundle output dist + +# kde +.directory diff --git a/applications/desktop/desktop.c b/applications/desktop/desktop.c index 0f969ce2..d6c22083 100644 --- a/applications/desktop/desktop.c +++ b/applications/desktop/desktop.c @@ -1,6 +1,9 @@ #include #include +#include #include +#include +#include #include #include @@ -13,6 +16,10 @@ #include "desktop_i.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) { furi_assert(context); Desktop* desktop = context; @@ -37,9 +44,19 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) { switch(event) { case DesktopGlobalBeforeAppStarted: animation_manager_unload_and_stall_animation(desktop->animation_manager); + desktop_auto_lock_inhibit(desktop); return true; case DesktopGlobalAfterAppFinished: 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; } @@ -58,6 +75,63 @@ static void desktop_tick_event_callback(void* context) { 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 = malloc(sizeof(Desktop)); @@ -146,9 +220,17 @@ Desktop* desktop_alloc() { animation_manager_is_animation_loaded(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( 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; } @@ -157,8 +239,17 @@ void desktop_free(Desktop* desktop) { furi_pubsub_unsubscribe( 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->input_events_pubsub = NULL; 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, DesktopViewIdLockMenu); @@ -191,6 +282,8 @@ void desktop_free(Desktop* desktop) { furi_record_close("menu"); + osTimerDelete(desktop->auto_lock_timer); + free(desktop); } @@ -214,14 +307,16 @@ int32_t desktop_srv(void* p) { scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { - 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); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock) && !desktop->settings.pin_code.length) { + 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()) { diff --git a/applications/desktop/desktop_i.h b/applications/desktop/desktop_i.h index 8eb7be21..2888f099 100644 --- a/applications/desktop/desktop_i.h +++ b/applications/desktop/desktop_i.h @@ -19,6 +19,7 @@ #include #include +#include #define STATUS_BAR_Y_SHIFT 13 @@ -59,10 +60,18 @@ struct Desktop { ViewPort* lock_viewport; AnimationManager* animation_manager; + Loader* loader; + NotificationApp* notification; + FuriPubSubSubscription* app_start_stop_subscription; + FuriPubSub* input_events_pubsub; + FuriPubSubSubscription* input_events_subscription; + osTimerId_t auto_lock_timer; }; Desktop* desktop_alloc(); void desktop_free(Desktop* desktop); +void desktop_lock(Desktop* desktop); +void desktop_unlock(Desktop* desktop); diff --git a/applications/desktop/desktop_settings/desktop_settings.h b/applications/desktop/desktop_settings/desktop_settings.h index 24165fe2..b56d63a7 100644 --- a/applications/desktop/desktop_settings/desktop_settings.h +++ b/applications/desktop/desktop_settings/desktop_settings.h @@ -5,7 +5,7 @@ #include #include -#define DESKTOP_SETTINGS_VER (1) +#define DESKTOP_SETTINGS_VER (2) #define DESKTOP_SETTINGS_PATH "/int/desktop.settings" #define DESKTOP_SETTINGS_MAGIC (0x17) #define PIN_MAX_LENGTH 12 @@ -39,6 +39,7 @@ typedef struct { typedef struct { uint16_t favorite; PinCode pin_code; + uint32_t auto_lock_delay_ms; } DesktopSettings; static inline bool pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2) { diff --git a/applications/desktop/desktop_settings/desktop_settings_app.c b/applications/desktop/desktop_settings/desktop_settings_app.c index 46e0bdc5..c52f1947 100644 --- a/applications/desktop/desktop_settings/desktop_settings_app.c +++ b/applications/desktop/desktop_settings/desktop_settings_app.c @@ -36,12 +36,17 @@ DesktopSettingsApp* desktop_settings_app_alloc() { app->popup = popup_alloc(); app->submenu = submenu_alloc(); + app->variable_item_list = variable_item_list_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_howto2_view = desktop_settings_view_pin_setup_howto2_alloc(); view_dispatcher_add_view( 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( app->view_dispatcher, DesktopSettingsAppViewIdPopup, popup_get_view(app->popup)); view_dispatcher_add_view( @@ -63,10 +68,12 @@ void desktop_settings_app_free(DesktopSettingsApp* app) { furi_assert(app); // Variable item list 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, DesktopSettingsAppViewIdPinInput); view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto); view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto2); + variable_item_list_free(app->variable_item_list); submenu_free(app->submenu); popup_free(app->popup); desktop_view_pin_input_free(app->pin_input_view); diff --git a/applications/desktop/desktop_settings/desktop_settings_app.h b/applications/desktop/desktop_settings/desktop_settings_app.h index 6297e42d..a1ca9f6f 100644 --- a/applications/desktop/desktop_settings/desktop_settings_app.h +++ b/applications/desktop/desktop_settings/desktop_settings_app.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "desktop_settings.h" #include "desktop/views/desktop_view_pin_input.h" @@ -13,6 +14,7 @@ typedef enum { DesktopSettingsAppViewMenu, + DesktopSettingsAppViewVarItemList, DesktopSettingsAppViewIdPopup, DesktopSettingsAppViewIdPinInput, DesktopSettingsAppViewIdPinSetupHowto, @@ -25,6 +27,7 @@ typedef struct { Gui* gui; SceneManager* scene_manager; ViewDispatcher* view_dispatcher; + VariableItemList* variable_item_list; Submenu* submenu; Popup* popup; DesktopViewPinInput* pin_input_view; diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c index 8f856b6a..63363497 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c @@ -1,35 +1,65 @@ #include +#include #include "../desktop_settings_app.h" #include "desktop_settings_scene.h" #define SCENE_EVENT_SELECT_FAVORITE 0 #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; 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) { DesktopSettingsApp* app = context; - Submenu* submenu = app->submenu; + VariableItemList* variable_item_list = app->variable_item_list; - submenu_add_item( - submenu, - "Favorite App", - SCENE_EVENT_SELECT_FAVORITE, - desktop_settings_scene_start_submenu_callback, + VariableItem* item; + uint8_t value_index; + + variable_item_list_add(variable_item_list, "Favorite App", 1, NULL, NULL); + + 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); - submenu_add_item( - submenu, - "PIN Setup", - SCENE_EVENT_SELECT_PIN_SETUP, - desktop_settings_scene_start_submenu_callback, - app); + variable_item_list_set_enter_callback( + variable_item_list, desktop_settings_scene_start_var_list_enter_callback, app); + value_index = value_index_uint32( + app->settings.auto_lock_delay_ms, auto_lock_delay_value, AUTO_LOCK_DELAY_COUNT); + variable_item_set_current_value_index(item, value_index); + 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) { @@ -46,6 +76,9 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu); consumed = true; break; + case SCENE_EVENT_SELECT_AUTO_LOCK_DELAY: + consumed = true; + break; } } 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) { DesktopSettingsApp* app = context; - submenu_reset(app->submenu); + variable_item_list_reset(app->variable_item_list); + SAVE_DESKTOP_SETTINGS(&app->settings); } diff --git a/applications/desktop/scenes/desktop_scene_lock_menu.c b/applications/desktop/scenes/desktop_scene_lock_menu.c index 058081eb..7b7606b1 100644 --- a/applications/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/desktop/scenes/desktop_scene_lock_menu.c @@ -48,17 +48,13 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { switch(event.event) { case DesktopLockMenuEventLock: scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); - scene_manager_set_scene_state( - desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); - scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); + desktop_lock(desktop); consumed = true; break; case DesktopLockMenuEventPinLock: if(desktop->settings.pin_code.length > 0) { furi_hal_rtc_set_flag(FuriHalRtcFlagLock); - scene_manager_set_scene_state( - desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); - scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); + desktop_lock(desktop); } else { LoaderStatus status = loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG); diff --git a/applications/desktop/scenes/desktop_scene_locked.c b/applications/desktop/scenes/desktop_scene_locked.c index 98d5fa0a..65d064d8 100644 --- a/applications/desktop/scenes/desktop_scene_locked.c +++ b/applications/desktop/scenes/desktop_scene_locked.c @@ -81,13 +81,13 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case DesktopLockedEventUnlocked: - furi_hal_rtc_set_pin_fails(0); - desktop_helpers_unlock_system(desktop); - scene_manager_search_and_switch_to_previous_scene( - desktop->scene_manager, DesktopSceneMain); + desktop_unlock(desktop); consumed = true; break; 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); consumed = true; break; diff --git a/applications/desktop/scenes/desktop_scene_pin_input.c b/applications/desktop/scenes/desktop_scene_pin_input.c index 60935f07..827b74a6 100644 --- a/applications/desktop/scenes/desktop_scene_pin_input.c +++ b/applications/desktop/scenes/desktop_scene_pin_input.c @@ -129,16 +129,13 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case DesktopPinInputEventUnlocked: - desktop_view_locked_unlock(desktop->locked_view); - furi_hal_rtc_set_pin_fails(0); - desktop_helpers_unlock_system(desktop); - scene_manager_search_and_switch_to_previous_scene( - desktop->scene_manager, DesktopSceneMain); + desktop_unlock(desktop); consumed = true; break; case DesktopPinInputEventBack: scene_manager_search_and_switch_to_previous_scene( desktop->scene_manager, DesktopSceneLocked); + notification_message(desktop->notification, &sequence_display_off); consumed = true; break; } diff --git a/applications/desktop/views/desktop_events.h b/applications/desktop/views/desktop_events.h index 6e5eb2f6..f38a5417 100644 --- a/applications/desktop/views/desktop_events.h +++ b/applications/desktop/views/desktop_events.h @@ -38,4 +38,5 @@ typedef enum { // Global events DesktopGlobalBeforeAppStarted, DesktopGlobalAfterAppFinished, + DesktopGlobalAutoLock, } DesktopEvent; diff --git a/applications/desktop/views/desktop_view_locked.c b/applications/desktop/views/desktop_view_locked.c index 766f2b20..9f39c2ce 100644 --- a/applications/desktop/views/desktop_view_locked.c +++ b/applications/desktop/views/desktop_view_locked.c @@ -11,6 +11,7 @@ #include "desktop_view_locked.h" #define DOOR_MOVING_INTERVAL_MS (1000 / 16) +#define LOCKED_HINT_TIMEOUT_MS (1000) #define UNLOCKED_HINT_TIMEOUT_MS (2000) #define DOOR_OFFSET_START -55 @@ -32,14 +33,18 @@ struct DesktopViewLocked { uint32_t lock_lastpress; }; -typedef struct { - uint32_t hint_icon_expire_at; - bool unlocked_hint; - bool locked; - bool pin_locked; +typedef enum { + DesktopViewLockedStateUnlocked, + DesktopViewLockedStateLocked, + DesktopViewLockedStateDoorsClosing, + DesktopViewLockedStateLockedHintShown, + DesktopViewLockedStateUnlockedHintShown +} DesktopViewLockedState; +typedef struct { + bool pin_locked; int8_t door_offset; - bool doors_closing; + DesktopViewLockedState view_state; } DesktopViewLockedModel; void desktop_view_locked_set_callback( @@ -78,51 +83,54 @@ static bool desktop_view_locked_doors_move(DesktopViewLockedModel* model) { static void desktop_view_locked_update_hint_icon_timeout(DesktopViewLocked* locked_view) { DesktopViewLockedModel* model = view_get_model(locked_view->view); - model->hint_icon_expire_at = osKernelGetTickCount() + osKernelGetTickFreq(); - view_commit_model(locked_view->view, true); + const bool change_state = (model->view_state == DesktopViewLockedStateLocked) && + !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) { - bool stop_timer = false; - DesktopViewLockedModel* model = view_get_model(locked_view->view); - if(model->locked) { - model->doors_closing = desktop_view_locked_doors_move(model); - stop_timer = !model->doors_closing; - } else { - model->unlocked_hint = false; - stop_timer = true; + DesktopViewLockedState view_state = model->view_state; + + if(view_state == DesktopViewLockedStateDoorsClosing && + !desktop_view_locked_doors_move(model)) { + model->view_state = DesktopViewLockedStateLocked; + } 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); - if(stop_timer) { + if(view_state != DesktopViewLockedStateDoorsClosing) { xTimerStop(locked_view->timer, portMAX_DELAY); } } static void desktop_view_locked_draw(Canvas* canvas, void* model) { DesktopViewLockedModel* m = model; - uint32_t now = osKernelGetTickCount(); + DesktopViewLockedState view_state = m->view_state; canvas_set_color(canvas, ColorBlack); - if(m->locked) { - if(m->doors_closing) { - desktop_view_locked_doors_draw(canvas, m); - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Locked"); - } else if((now < m->hint_icon_expire_at) && !m->pin_locked) { - canvas_set_font(canvas, FontSecondary); - 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:"); - 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_dot(canvas, 17, 61); - } - } else { - if(m->unlocked_hint) { - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Unlocked"); - } + if(view_state == DesktopViewLockedStateDoorsClosing) { + desktop_view_locked_doors_draw(canvas, m); + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Locked"); + } else if(view_state == DesktopViewLockedStateLockedHintShown) { + canvas_set_font(canvas, FontSecondary); + 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:"); + 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_dot(canvas, 17, 61); + } else if(view_state == DesktopViewLockedStateUnlockedHintShown) { + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Unlocked"); } } @@ -134,43 +142,38 @@ View* desktop_view_locked_get_view(DesktopViewLocked* locked_view) { static bool desktop_view_locked_input(InputEvent* event, void* context) { furi_assert(event); furi_assert(context); + + bool is_changed = false; + const uint32_t press_time = xTaskGetTickCount(); 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); - bool changed = false; - locked = model->locked; - locked_with_pin = model->pin_locked; - doors_closing = model->doors_closing; - if(!locked && model->unlocked_hint && event->type == InputTypePress) { - model->unlocked_hint = false; - changed = true; - } - view_commit_model(locked_view->view, changed); + DesktopViewLockedModel* model = view_get_model(locked_view->view); + if(model->view_state == DesktopViewLockedStateUnlockedHintShown && + event->type == InputTypePress) { + model->view_state = DesktopViewLockedStateUnlocked; + is_changed = true; } + 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)) { - return locked; - } - - if(locked_with_pin) { + if(view_state == DesktopViewLockedStateUnlocked || event->type != InputTypeShort) { + return view_state != DesktopViewLockedStateUnlocked; + } else if(view_state == DesktopViewLockedStateLocked && pin_locked) { 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) { locked_view->lock_lastpress = press_time; locked_view->lock_count = 0; } desktop_view_locked_update_hint_icon_timeout(locked_view); + if(event->key == InputKeyBack) { locked_view->lock_lastpress = press_time; locked_view->lock_count++; if(locked_view->lock_count == UNLOCK_CNT) { - desktop_view_locked_unlock(locked_view); locked_view->callback(DesktopLockedEventUnlocked, locked_view->context); } } else { @@ -180,7 +183,7 @@ static bool desktop_view_locked_input(InputEvent* event, void* context) { locked_view->lock_lastpress = press_time; } - return locked; + return true; } DesktopViewLocked* desktop_view_locked_alloc() { @@ -189,7 +192,6 @@ DesktopViewLocked* desktop_view_locked_alloc() { locked_view->timer = 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_set_context(locked_view->view, locked_view); 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) { 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; view_commit_model(locked_view->view, true); 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) { 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; view_commit_model(locked_view->view, true); } void desktop_view_locked_unlock(DesktopViewLocked* locked_view) { - furi_assert(locked_view); - locked_view->lock_count = 0; DesktopViewLockedModel* model = view_get_model(locked_view->view); - model->locked = false; + model->view_state = DesktopViewLockedStateUnlockedHintShown; model->pin_locked = false; - model->unlocked_hint = true; view_commit_model(locked_view->view, true); 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; +} diff --git a/applications/desktop/views/desktop_view_locked.h b/applications/desktop/views/desktop_view_locked.h index 60fe791b..b0a0aa30 100644 --- a/applications/desktop/views/desktop_view_locked.h +++ b/applications/desktop/views/desktop_view_locked.h @@ -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_unlock(DesktopViewLocked* locked_view); void desktop_view_locked_close_doors(DesktopViewLocked* locked_view); +bool desktop_view_locked_is_locked_hint_visible(DesktopViewLocked* locked_view); diff --git a/applications/notification/notification_app.c b/applications/notification/notification_app.c index d0594490..00136ec5 100644 --- a/applications/notification/notification_app.c +++ b/applications/notification/notification_app.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "notification.h" #include "notification_messages.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) { + furi_assert(value); + furi_assert(context); + const InputEvent* event = value; NotificationApp* app = context; - notification_message(app, &sequence_display_on); + if(event->type == InputTypePress) { + notification_message(app, &sequence_display_on); + } } // App alloc diff --git a/applications/notification/notification_messages.c b/applications/notification/notification_messages.c index 3934f5d0..733b7f9c 100644 --- a/applications/notification/notification_messages.c +++ b/applications/notification/notification_messages.c @@ -21,7 +21,7 @@ const NotificationMessage message_display_lock = { }; const NotificationMessage message_display_unlock = { - .type = NotificationMessageTypeLedDisplayLock, + .type = NotificationMessageTypeLedDisplayUnlock, .data.led.value = 0x00, }; @@ -208,6 +208,12 @@ const NotificationSequence sequence_display_unlock = { NULL, }; +const NotificationSequence sequence_display_off_delay_1000 = { + &message_delay_1000, + &message_display_off, + NULL, +}; + // Charging const NotificationSequence sequence_charging = { &message_red_255, @@ -436,4 +442,4 @@ const NotificationSequence sequence_audiovisual_alert = { &message_sound_off, &message_vibro_off, NULL, -}; \ No newline at end of file +}; diff --git a/applications/notification/notification_messages.h b/applications/notification/notification_messages.h index 54215e47..b9b0d72d 100644 --- a/applications/notification/notification_messages.h +++ b/applications/notification/notification_messages.h @@ -78,6 +78,8 @@ extern const NotificationSequence sequence_display_off; extern const NotificationSequence sequence_display_lock; /** Display: backlight always on 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 extern const NotificationSequence sequence_charging; diff --git a/applications/notification/notification_settings_app.c b/applications/notification/notification_settings_app.c index fc2c1627..1522628d 100644 --- a/applications/notification/notification_settings_app.c +++ b/applications/notification/notification_settings_app.c @@ -2,6 +2,7 @@ #include "notification_app.h" #include #include +#include #define MAX_NOTIFICATION_SETTINGS 4 @@ -63,44 +64,6 @@ const char* const vibro_text[VIBRO_COUNT] = { }; 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) { NotificationAppSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -164,21 +127,21 @@ static NotificationAppSettings* alloc_settings() { item = variable_item_list_add( 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); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, backlight_text[value_index]); item = variable_item_list_add( 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); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, delay_text[value_index]); item = variable_item_list_add( 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); variable_item_set_current_value_index(item, 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( app->variable_item_list, "Volume", VOLUME_COUNT, volume_changed, app); 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_text(item, volume_text[value_index]); item = 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_text(item, vibro_text[value_index]); diff --git a/applications/system/system_settings.c b/applications/system/system_settings.c index fde07745..f06292ec 100644 --- a/applications/system/system_settings.c +++ b/applications/system/system_settings.c @@ -1,19 +1,6 @@ #include "system_settings.h" #include - -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; -} +#include const char* const log_level_text[] = { "Default", @@ -80,7 +67,7 @@ SystemSettings* system_settings_alloc() { item = variable_item_list_add( 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)); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, log_level_text[value_index]); diff --git a/firmware/targets/f7/furi_hal/furi_hal_delay.c b/firmware/targets/f7/furi_hal/furi_hal_delay.c index 8a26d8dc..94ef7865 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_delay.c +++ b/firmware/targets/f7/furi_hal/furi_hal_delay.c @@ -26,6 +26,10 @@ uint32_t furi_hal_get_tick(void) { return tick_cnt; } +uint32_t furi_hal_ms_to_ticks(float milliseconds) { + return milliseconds / (1000.0f / osKernelGetTickFreq()); +} + void furi_hal_delay_us(float microseconds) { uint32_t start = DWT->CYCCNT; uint32_t time_ticks = microseconds * furi_hal_delay_instructions_per_microsecond(); diff --git a/firmware/targets/furi_hal_include/furi_hal_delay.h b/firmware/targets/furi_hal_include/furi_hal_delay.h index 58b4ed9b..f34a7053 100644 --- a/firmware/targets/furi_hal_include/furi_hal_delay.h +++ b/firmware/targets/furi_hal_include/furi_hal_delay.h @@ -31,6 +31,13 @@ void furi_hal_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 * @warning Cannot be used from ISR * diff --git a/lib/toolbox/value_index.c b/lib/toolbox/value_index.c new file mode 100644 index 00000000..e0745e43 --- /dev/null +++ b/lib/toolbox/value_index.c @@ -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; +} diff --git a/lib/toolbox/value_index.h b/lib/toolbox/value_index.h new file mode 100644 index 00000000..9459292a --- /dev/null +++ b/lib/toolbox/value_index.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +#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