diff --git a/src/fw/apps/system_apps/settings/settings_system.c b/src/fw/apps/system_apps/settings/settings_system.c index 94df3391a..0dc15dc98 100644 --- a/src/fw/apps/system_apps/settings/settings_system.c +++ b/src/fw/apps/system_apps/settings/settings_system.c @@ -152,6 +152,7 @@ typedef enum { SystemMenuItemInformation, SystemMenuItemCertification, SystemMenuItemStationaryToggle, + SystemMenuItemPanicMode, SystemMenuItemDebugging, SystemMenuItemShutDown, SystemMenuItemFactoryReset, @@ -162,6 +163,7 @@ static const char *s_item_titles[SystemMenuItem_Count] = { [SystemMenuItemInformation] = i18n_noop("Information"), [SystemMenuItemCertification] = i18n_noop("Certification"), [SystemMenuItemStationaryToggle] = i18n_noop("Stand-By Mode"), + [SystemMenuItemPanicMode] = i18n_noop("Panic Mode"), [SystemMenuItemDebugging] = i18n_noop("Debugging"), [SystemMenuItemShutDown] = i18n_noop("Shut Down"), [SystemMenuItemFactoryReset] = i18n_noop("Factory Reset"), @@ -384,6 +386,79 @@ static void prv_maybe_trigger_core_dump() { app_confirmation_dialog_push(confirmation_dialog); } +// Panic Mode confirmation +//////////////////////////// + +static void prv_panic_mode_dialog_load_cb(void *context) { + ExpandableDialog *expandable_dialog = (ExpandableDialog *)context; + Dialog *dialog = expandable_dialog_get_dialog(expandable_dialog); + + // Center the main text after the dialog window loads + text_layer_set_text_alignment(&dialog->text_layer, GTextAlignmentCenter); + + // Center the header text + text_layer_set_text_alignment(&expandable_dialog->header_layer, GTextAlignmentCenter); + + // Center the icon by repositioning it, accounting for the action bar + KinoLayer *icon_layer = &dialog->icon_layer; + if (kino_layer_get_reel(icon_layer)) { + GRect icon_frame = icon_layer->layer.frame; + GRect window_frame = dialog->window.layer.frame; + + // Check if action bar is shown and account for its width + bool show_action_bar = expandable_dialog->show_action_bar; + uint16_t action_bar_width = show_action_bar ? ACTION_BAR_WIDTH : 0; + + // Center horizontally within the content area (excluding action bar) + uint16_t content_width = window_frame.size.w - action_bar_width; + icon_frame.origin.x = (content_width - icon_frame.size.w) / 2; + layer_set_frame(&icon_layer->layer, &icon_frame); + } +} + +static void prv_panic_mode_confirm_cb(ClickRecognizerRef recognizer, void *context) { + ExpandableDialog *dialog = (ExpandableDialog *)context; + + expandable_dialog_pop(dialog); + + // Toggle panic mode setting + bool current_state = shell_prefs_get_panic_mode_enabled(); + shell_prefs_set_panic_mode_enabled(!current_state); + + // Reload the menu to reflect the change + settings_menu_reload_data(SettingsMenuItemSystem); +} + +static void prv_panic_mode_interstitial_trigger(SettingsSystemData *context) { + const char *message; + const char *header; + + // Show appropriate message based on current state + if (shell_prefs_get_panic_mode_enabled()) { + header = i18n_noop("Disable Panic Mode?"); + message = i18n_noop("Notifications will no longer be cleared."); + } else { + header = i18n_noop("Enable Panic Mode?"); + message = i18n_noop("Clears notifications on airplane, stand-by, and charging."); + } + + // Set up callbacks to center text after window loads + static DialogCallbacks callbacks = { + .load = prv_panic_mode_dialog_load_cb, + .unload = NULL + }; + + // Create expandable dialog with scrolling support + ExpandableDialog *expandable_dialog = expandable_dialog_create_with_params( + "Panic Mode", RESOURCE_ID_RESULT_DELETED_SMALL, message, GColorWhite, + GColorRed, &callbacks, RESOURCE_ID_ACTION_BAR_ICON_CHECK, + prv_panic_mode_confirm_cb); + expandable_dialog_set_header(expandable_dialog, header); + expandable_dialog_show_action_bar(expandable_dialog, true); + + app_expandable_dialog_push(expandable_dialog); +} + // ALS Threshold Settings ///////////////////////////// @@ -1320,6 +1395,9 @@ static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx, case SystemMenuItemStationaryToggle: subtitle = stationary_get_enabled() ? i18n_get("On", data) : i18n_get("Off", data); break; + case SystemMenuItemPanicMode: + subtitle = shell_prefs_get_panic_mode_enabled() ? i18n_get("On", data) : i18n_get("Off", data); + break; case SystemMenuItemShutDown: if (!prv_shutdown_enabled()) { // XXX: For now, gray out the Shut Down item if unusable. @@ -1355,6 +1433,9 @@ static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) { case SystemMenuItemStationaryToggle: stationary_set_enabled(!stationary_get_enabled()); break; + case SystemMenuItemPanicMode: + prv_panic_mode_interstitial_trigger(data); + break; case SystemMenuItemShutDown: if (prv_shutdown_enabled()) { launcher_task_add_callback(prv_shutdown_cb, 0); diff --git a/src/fw/apps/system_apps/toggle/airplane_mode.c b/src/fw/apps/system_apps/toggle/airplane_mode.c index fc1642250..4e8e04729 100644 --- a/src/fw/apps/system_apps/toggle/airplane_mode.c +++ b/src/fw/apps/system_apps/toggle/airplane_mode.c @@ -8,13 +8,23 @@ #include "process_management/app_manager.h" #include "services/common/bluetooth/bluetooth_ctl.h" #include "services/common/i18n/i18n.h" +#include "services/normal/notifications/notification_storage.h" +#include "shell/prefs.h" static bool prv_get_state(void *context) { return bt_ctl_is_airplane_mode_on(); } static void prv_set_state(bool enabled, void *context) { - bt_ctl_set_airplane_mode_async(!bt_ctl_is_airplane_mode_on()); + // Store the current state before toggling + bool will_be_enabled = !bt_ctl_is_airplane_mode_on(); + + bt_ctl_set_airplane_mode_async(will_be_enabled); + + // If panic mode is enabled and we're turning on airplane mode, wipe notifications + if (will_be_enabled && shell_prefs_get_panic_mode_enabled()) { + notification_storage_reset_and_init(); + } } static const ActionToggleImpl s_airplane_mode_action_toggle_impl = { diff --git a/src/fw/services/normal/stationary.c b/src/fw/services/normal/stationary.c index 60146708d..92bd2c440 100644 --- a/src/fw/services/normal/stationary.c +++ b/src/fw/services/normal/stationary.c @@ -18,6 +18,7 @@ #include "services/common/i18n/i18n.h" #include "services/common/regular_timer.h" #include "services/common/system_task.h" +#include "services/normal/notifications/notification_storage.h" #include "services/runlevel.h" #include "shell/prefs.h" #include "system/logging.h" @@ -103,6 +104,11 @@ void stationary_handle_battery_connection_change_event(void) { "Stationary mode battery state change event received"); if (battery_is_usb_connected()) { analytics_event_stationary_state_change(rtc_get_time(), StationaryAnalyticsEnterCharging); + + // If panic mode is enabled, wipe notifications when charger is connected + if (shell_prefs_get_panic_mode_enabled()) { + notification_storage_reset_and_init(); + } } else { analytics_event_stationary_state_change(rtc_get_time(), StationaryAnalyticsExitCharging); } @@ -218,6 +224,11 @@ static void prv_enter_stationary_state(void) { services_set_runlevel(RunLevel_Stationary); accel_enable_high_sensitivity(true); s_current_state = StationaryStateStationary; + + // If panic mode is enabled, wipe notifications when entering stationary mode + if (shell_prefs_get_panic_mode_enabled()) { + notification_storage_reset_and_init(); + } } static void prv_exit_stationary(void) { diff --git a/src/fw/shell/normal/prefs.c b/src/fw/shell/normal/prefs.c index 1dc1a0fca..06a121701 100644 --- a/src/fw/shell/normal/prefs.c +++ b/src/fw/shell/normal/prefs.c @@ -97,6 +97,9 @@ static bool s_stationary_mode_enabled = false; static bool s_stationary_mode_enabled = true; #endif +#define PREF_KEY_PANIC_MODE "panicMode" +static bool s_panic_mode_enabled = false; + #define PREF_KEY_DEFAULT_WORKER "workerId" static Uuid s_default_worker = UUID_INVALID_INIT; @@ -377,6 +380,11 @@ static bool prv_set_s_stationary_mode_enabled(bool *enabled) { return true; } +static bool prv_set_s_panic_mode_enabled(bool *enabled) { + s_panic_mode_enabled = *enabled; + return true; +} + static bool prv_set_s_default_worker(Uuid *uuid) { s_default_worker = *uuid; return true; @@ -1060,6 +1068,14 @@ void shell_prefs_set_stationary_enabled(bool enabled) { prv_pref_set(PREF_KEY_STATIONARY, &enabled, sizeof(enabled)); } +bool shell_prefs_get_panic_mode_enabled(void) { + return s_panic_mode_enabled; +} + +void shell_prefs_set_panic_mode_enabled(bool enabled) { + prv_pref_set(PREF_KEY_PANIC_MODE, &enabled, sizeof(enabled)); +} + AppInstallId worker_preferences_get_default_worker(void) { return app_install_get_id_for_uuid(&s_default_worker); } diff --git a/src/fw/shell/normal/prefs_values.h.inc b/src/fw/shell/normal/prefs_values.h.inc index 443900ae6..a4bb39614 100644 --- a/src/fw/shell/normal/prefs_values.h.inc +++ b/src/fw/shell/normal/prefs_values.h.inc @@ -16,6 +16,7 @@ #endif PREFS_MACRO(PREF_KEY_BACKLIGHT_AMBIENT_THRESHOLD, s_backlight_ambient_threshold) PREFS_MACRO(PREF_KEY_STATIONARY, s_stationary_mode_enabled) + PREFS_MACRO(PREF_KEY_PANIC_MODE, s_panic_mode_enabled) PREFS_MACRO(PREF_KEY_DEFAULT_WORKER, s_default_worker) PREFS_MACRO(PREF_KEY_TEXT_STYLE, s_text_style) PREFS_MACRO(PREF_KEY_LANG_ENGLISH, s_language_english) diff --git a/src/fw/shell/prefs.h b/src/fw/shell/prefs.h index 9d0f2db8c..524ced389 100644 --- a/src/fw/shell/prefs.h +++ b/src/fw/shell/prefs.h @@ -102,6 +102,10 @@ void backlight_set_ambient_threshold(uint32_t threshold); bool shell_prefs_get_stationary_enabled(void); void shell_prefs_set_stationary_enabled(bool enabled); +// Panic mode will wipe all notifications when entering airplane mode, stationary mode, or charging. +bool shell_prefs_get_panic_mode_enabled(void); +void shell_prefs_set_panic_mode_enabled(bool enabled); + // The default worker setting is used by process_management. AppInstallId worker_preferences_get_default_worker(void); void worker_preferences_set_default_worker(AppInstallId id);