[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:
Georgii Surkov 2022-04-14 15:20:41 +03:00 committed by GitHub
parent 779d319069
commit 917be9c6d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 386 additions and 166 deletions

3
.gitignore vendored
View File

@ -36,3 +36,6 @@ CMakeLists.txt
# bundle output # bundle output
dist dist
# kde
.directory

View File

@ -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) { furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
scene_manager_set_scene_state( }
desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER);
scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) {
} else { if(!loader_is_locked(desktop->loader)) {
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); desktop_auto_lock_arm(desktop);
} }
} else {
desktop_lock(desktop);
} }
if(desktop_is_first_start()) { if(desktop_is_first_start()) {

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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);
} }

View File

@ -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);

View File

@ -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;

View File

@ -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;
} }

View File

@ -38,4 +38,5 @@ typedef enum {
// Global events // Global events
DesktopGlobalBeforeAppStarted, DesktopGlobalBeforeAppStarted,
DesktopGlobalAfterAppFinished, DesktopGlobalAfterAppFinished,
DesktopGlobalAutoLock,
} DesktopEvent; } DesktopEvent;

View File

@ -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,51 +83,54 @@ 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(view_state == DesktopViewLockedStateLockedHintShown) {
} else if((now < m->hint_icon_expire_at) && !m->pin_locked) { 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) {
} canvas_set_font(canvas, FontPrimary);
} else { elements_multiline_text_framed(canvas, 42, 30 + STATUS_BAR_Y_SHIFT, "Unlocked");
if(m->unlocked_hint) {
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) { 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; DesktopViewLockedModel* model = view_get_model(locked_view->view);
bool locked_with_pin = false; if(model->view_state == DesktopViewLockedStateUnlockedHintShown &&
bool doors_closing = false; event->type == InputTypePress) {
uint32_t press_time = xTaskGetTickCount(); model->view_state = DesktopViewLockedStateUnlocked;
is_changed = true;
{
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);
} }
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;
}

View File

@ -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);

View File

@ -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;
notification_message(app, &sequence_display_on); if(event->type == InputTypePress) {
notification_message(app, &sequence_display_on);
}
} }
// App alloc // App alloc

View File

@ -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,

View File

@ -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;

View File

@ -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]);

View File

@ -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]);

View File

@ -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();

View File

@ -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
View 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
View 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