[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
|
||||
dist
|
||||
|
||||
# kde
|
||||
.directory
|
||||
|
@ -1,6 +1,9 @@
|
||||
#include <storage/storage.h>
|
||||
#include <assets_icons.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_stack.h>
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
@ -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 {
|
||||
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()) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
#include <loader/loader.h>
|
||||
#include <notification/notification_app.h>
|
||||
|
||||
#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);
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <stdbool.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_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) {
|
||||
|
@ -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);
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
|
||||
#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;
|
||||
|
@ -1,35 +1,65 @@
|
||||
#include <applications.h>
|
||||
#include <lib/toolbox/value_index.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -38,4 +38,5 @@ typedef enum {
|
||||
// Global events
|
||||
DesktopGlobalBeforeAppStarted,
|
||||
DesktopGlobalAfterAppFinished,
|
||||
DesktopGlobalAutoLock,
|
||||
} DesktopEvent;
|
||||
|
@ -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,53 +83,56 @@ 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) {
|
||||
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((now < m->hint_icon_expire_at) && !m->pin_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(m->unlocked_hint) {
|
||||
} else if(view_state == DesktopViewLockedStateUnlockedHintShown) {
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Unlocked");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
View* desktop_view_locked_get_view(DesktopViewLocked* locked_view) {
|
||||
furi_assert(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) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <storage/storage.h>
|
||||
#include <input/input.h>
|
||||
#include "notification.h"
|
||||
#include "notification_messages.h"
|
||||
#include "notification_app.h"
|
||||
@ -416,9 +417,14 @@ 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;
|
||||
if(event->type == InputTypePress) {
|
||||
notification_message(app, &sequence_display_on);
|
||||
}
|
||||
}
|
||||
|
||||
// App alloc
|
||||
static NotificationApp* notification_app_alloc() {
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "notification_app.h"
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <lib/toolbox/value_index.h>
|
||||
|
||||
#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]);
|
||||
|
||||
|
@ -1,19 +1,6 @@
|
||||
#include "system_settings.h"
|
||||
#include <loader/loader.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;
|
||||
}
|
||||
#include <lib/toolbox/value_index.h>
|
||||
|
||||
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]);
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
*
|
||||
|
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…
Reference in New Issue
Block a user