diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 1233af893..30e7253da 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -8,6 +9,7 @@ #include #include #include +#include #include "animations/animation_manager.h" #include "desktop/scenes/desktop_scene.h" @@ -36,7 +38,6 @@ static void desktop_loader_callback(const void* message, void* context) { view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAfterAppFinished); } } - static void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) { UNUSED(context); furi_assert(canvas); @@ -49,6 +50,65 @@ static void desktop_dummy_mode_icon_draw_callback(Canvas* canvas, void* context) canvas_draw_icon(canvas, 0, 0, &I_GameMode_11x8); } +static void desktop_clock_update(Desktop* desktop) { + furi_assert(desktop); + + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + bool time_format_12 = locale_get_time_format() == LocaleTimeFormat12h; + + if(desktop->time_hour != curr_dt.hour || desktop->time_minute != curr_dt.minute || + desktop->time_format_12 != time_format_12) { + desktop->time_format_12 = time_format_12; + desktop->time_hour = curr_dt.hour; + desktop->time_minute = curr_dt.minute; + view_port_update(desktop->clock_viewport); + } +} + +static void desktop_clock_reconfigure(Desktop* desktop) { + furi_assert(desktop); + + desktop_clock_update(desktop); + + if(desktop->settings.display_clock) { + furi_timer_start(desktop->update_clock_timer, furi_ms_to_ticks(1000)); + } else { + furi_timer_stop(desktop->update_clock_timer); + } + + view_port_enabled_set(desktop->clock_viewport, desktop->settings.display_clock); +} + +static void desktop_clock_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + furi_assert(canvas); + + Desktop* desktop = context; + + canvas_set_font(canvas, FontPrimary); + + uint8_t hour = desktop->time_hour; + if(desktop->time_format_12) { + if(hour > 12) { + hour -= 12; + } + if(hour == 0) { + hour = 12; + } + } + + char buffer[20]; + snprintf(buffer, sizeof(buffer), "%02u:%02u", hour, desktop->time_minute); + + // ToDo: never do that, may cause visual glitches + view_port_set_width( + desktop->clock_viewport, + canvas_string_width(canvas, buffer) - 1 + (desktop->time_minute % 10 == 1)); + + canvas_draw_str_aligned(canvas, 0, 8, AlignLeft, AlignBottom, buffer); +} + static void desktop_stealth_mode_icon_draw_callback(Canvas* canvas, void* context) { UNUSED(context); furi_assert(canvas); @@ -69,6 +129,9 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) { // TODO: Implement a message mechanism for loading settings and (optionally) // locking and unlocking DESKTOP_SETTINGS_LOAD(&desktop->settings); + + desktop_clock_reconfigure(desktop); + desktop_auto_lock_arm(desktop); return true; case DesktopGlobalAutoLock: @@ -134,6 +197,19 @@ static void desktop_auto_lock_inhibit(Desktop* desktop) { } } +static void desktop_clock_timer_callback(void* context) { + furi_assert(context); + Desktop* desktop = context; + + if(gui_active_view_port_count(desktop->gui, GuiLayerStatusBarLeft) < 6) { + desktop_clock_update(desktop); + + view_port_enabled_set(desktop->clock_viewport, true); + } else { + view_port_enabled_set(desktop->clock_viewport, false); + } +} + void desktop_lock(Desktop* desktop) { furi_hal_rtc_set_flag(FuriHalRtcFlagLock); @@ -286,6 +362,13 @@ Desktop* desktop_alloc() { view_port_enabled_set(desktop->dummy_mode_icon_viewport, false); gui_add_view_port(desktop->gui, desktop->dummy_mode_icon_viewport, GuiLayerStatusBarLeft); + // Clock + desktop->clock_viewport = view_port_alloc(); + view_port_set_width(desktop->clock_viewport, 25); + view_port_draw_callback_set(desktop->clock_viewport, desktop_clock_draw_callback, desktop); + view_port_enabled_set(desktop->clock_viewport, false); + gui_add_view_port(desktop->gui, desktop->clock_viewport, GuiLayerStatusBarRight); + // Stealth mode icon desktop->stealth_mode_icon_viewport = view_port_alloc(); view_port_set_width(desktop->stealth_mode_icon_viewport, icon_get_width(&I_Muted_8x8)); @@ -317,6 +400,9 @@ Desktop* desktop_alloc() { desktop->status_pubsub = furi_pubsub_alloc(); + desktop->update_clock_timer = + furi_timer_alloc(desktop_clock_timer_callback, FuriTimerTypePeriodic, desktop); + furi_record_create(RECORD_DESKTOP, desktop); return desktop; @@ -362,6 +448,9 @@ int32_t desktop_srv(void* p) { } view_port_enabled_set(desktop->dummy_mode_icon_viewport, desktop->settings.dummy_mode); + + desktop_clock_reconfigure(desktop); + desktop_main_set_dummy_mode_state(desktop->main_view, desktop->settings.dummy_mode); animation_manager_set_dummy_mode_state( desktop->animation_manager, desktop->settings.dummy_mode); diff --git a/applications/services/desktop/desktop_i.h b/applications/services/desktop/desktop_i.h index 0b3d56801..bb495c920 100644 --- a/applications/services/desktop/desktop_i.h +++ b/applications/services/desktop/desktop_i.h @@ -59,6 +59,7 @@ struct Desktop { ViewPort* lock_icon_viewport; ViewPort* dummy_mode_icon_viewport; + ViewPort* clock_viewport; ViewPort* stealth_mode_icon_viewport; AnimationManager* animation_manager; @@ -70,10 +71,15 @@ struct Desktop { FuriPubSub* input_events_pubsub; FuriPubSubSubscription* input_events_subscription; FuriTimer* auto_lock_timer; + FuriTimer* update_clock_timer; FuriPubSub* status_pubsub; - bool in_transition; + uint8_t time_hour; + uint8_t time_minute; + bool time_format_12 : 1; // 1 - 12 hour, 0 - 24H + + bool in_transition : 1; }; Desktop* desktop_alloc(); diff --git a/applications/services/desktop/desktop_settings.h b/applications/services/desktop/desktop_settings.h index 9b88868a8..a189f9f05 100644 --- a/applications/services/desktop/desktop_settings.h +++ b/applications/services/desktop/desktop_settings.h @@ -8,7 +8,7 @@ #include #include -#define DESKTOP_SETTINGS_VER (8) +#define DESKTOP_SETTINGS_VER (9) #define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME) #define DESKTOP_SETTINGS_MAGIC (0x17) @@ -51,4 +51,5 @@ typedef struct { PinCode pin_code; uint32_t auto_lock_delay_ms; uint8_t dummy_mode; + uint8_t display_clock; } DesktopSettings; diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 392011620..b96f89db9 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -17,6 +17,26 @@ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) { return NULL; } +size_t gui_active_view_port_count(Gui* gui, GuiLayer layer) { + furi_assert(gui); + furi_check(layer < GuiLayerMAX); + size_t ret = 0; + + gui_lock(gui); + ViewPortArray_it_t it; + ViewPortArray_it_last(it, gui->layers[layer]); + while(!ViewPortArray_end_p(it)) { + ViewPort* view_port = *ViewPortArray_ref(it); + if(view_port_is_enabled(view_port)) { + ret++; + } + ViewPortArray_previous(it); + } + gui_unlock(gui); + + return ret; +} + void gui_update(Gui* gui) { furi_assert(gui); if(!gui->direct_draw) furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_DRAW); diff --git a/applications/services/gui/gui_i.h b/applications/services/gui/gui_i.h index a5cd84120..a5e269e03 100644 --- a/applications/services/gui/gui_i.h +++ b/applications/services/gui/gui_i.h @@ -75,6 +75,12 @@ struct Gui { ViewPort* ongoing_input_view_port; }; +/** Find enabled ViewPort in ViewPortArray + * + * @param[in] array The ViewPortArray instance + * + * @return ViewPort instance or NULL + */ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array); /** Update GUI, request redraw @@ -83,8 +89,30 @@ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array); */ void gui_update(Gui* gui); +/** Input event callback + * + * Used to receive input from input service or to inject new input events + * + * @param[in] value The value pointer (InputEvent*) + * @param ctx The context (Gui instance) + */ void gui_input_events_callback(const void* value, void* ctx); +/** Get count of view ports in layer + * + * @param gui The Gui instance + * @param[in] layer GuiLayer that we want to get count of view ports + */ +size_t gui_active_view_port_count(Gui* gui, GuiLayer layer); + +/** Lock GUI + * + * @param gui The Gui instance + */ void gui_lock(Gui* gui); +/** Unlock GUI + * + * @param gui The Gui instance + */ void gui_unlock(Gui* gui); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c index 8530a1a7d..02acd51ca 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c @@ -8,6 +8,7 @@ #define SCENE_EVENT_SELECT_FAVORITE_SECONDARY 1 #define SCENE_EVENT_SELECT_PIN_SETUP 2 #define SCENE_EVENT_SELECT_AUTO_LOCK_DELAY 3 +#define SCENE_EVENT_SELECT_CLOCK_DISPLAY 4 #define AUTO_LOCK_DELAY_COUNT 6 const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = { @@ -22,11 +23,27 @@ const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = { const uint32_t auto_lock_delay_value[AUTO_LOCK_DELAY_COUNT] = {0, 30000, 60000, 120000, 300000, 600000}; +#define CLOCK_ENABLE_COUNT 2 +const char* const clock_enable_text[CLOCK_ENABLE_COUNT] = { + "OFF", + "ON", +}; + +const uint32_t clock_enable_value[CLOCK_ENABLE_COUNT] = {0, 1}; + 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_clock_enable_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, clock_enable_text[index]); + app->settings.display_clock = 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); @@ -62,6 +79,18 @@ void desktop_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, auto_lock_delay_text[value_index]); + item = variable_item_list_add( + variable_item_list, + "Show Clock", + CLOCK_ENABLE_COUNT, + desktop_settings_scene_start_clock_enable_changed, // + app); + + value_index = + value_index_uint32(app->settings.display_clock, clock_enable_value, CLOCK_ENABLE_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, clock_enable_text[value_index]); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList); } @@ -86,6 +115,7 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even consumed = true; break; case SCENE_EVENT_SELECT_AUTO_LOCK_DELAY: + case SCENE_EVENT_SELECT_CLOCK_DISPLAY: consumed = true; break; } diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 081fe502f..eab140d5c 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1471,6 +1471,7 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" Function,+,gui_direct_draw_acquire,Canvas*,Gui* Function,+,gui_direct_draw_release,void,Gui* +Function,-,gui_active_view_port_count,size_t,"Gui*, GuiLayer" Function,+,gui_get_framebuffer_size,size_t,const Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 535582c0c..8efe980a7 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1642,6 +1642,7 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" Function,+,gui_direct_draw_acquire,Canvas*,Gui* Function,+,gui_direct_draw_release,void,Gui* +Function,-,gui_active_view_port_count,size_t,"Gui*, GuiLayer" Function,+,gui_get_framebuffer_size,size_t,const Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*"