From fdc97dbb558886581e21e17e93be1a582cafea99 Mon Sep 17 00:00:00 2001 From: Jeffrey van Pelt Date: Sun, 15 Mar 2026 17:35:02 +0100 Subject: [PATCH 1/4] Initial version of configurable notification timeout --- src/CMakeLists.txt | 1 + src/components/settings/Settings.h | 12 ++++ src/displayapp/DisplayApp.cpp | 8 +++ src/displayapp/apps/Apps.h.in | 1 + src/displayapp/screens/Notifications.cpp | 29 ++++++--- src/displayapp/screens/Notifications.h | 12 +++- .../settings/SettingNotificationTimeout.cpp | 62 +++++++++++++++++++ .../settings/SettingNotificationTimeout.h | 26 ++++++++ src/displayapp/screens/settings/Settings.h | 3 +- 9 files changed, 140 insertions(+), 14 deletions(-) create mode 100644 src/displayapp/screens/settings/SettingNotificationTimeout.cpp create mode 100644 src/displayapp/screens/settings/SettingNotificationTimeout.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e4a354df64..9c9c88e647 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -409,6 +409,7 @@ list(APPEND SOURCE_FILES displayapp/screens/settings/Settings.cpp displayapp/screens/settings/SettingWatchFace.cpp displayapp/screens/settings/SettingTimeFormat.cpp + displayapp/screens/settings/SettingNotificationTimeout.cpp displayapp/screens/settings/SettingWeatherFormat.cpp displayapp/screens/settings/SettingWakeUp.cpp displayapp/screens/settings/SettingDisplay.cpp diff --git a/src/components/settings/Settings.h b/src/components/settings/Settings.h index 9133d3fea1..2c80117aa0 100644 --- a/src/components/settings/Settings.h +++ b/src/components/settings/Settings.h @@ -196,6 +196,17 @@ namespace Pinetime { return settings.clockType; }; + void SetNotificationTimeout(uint32_t notificationTimeout) { + if (notificationTimeout != settings.notificationTimeout) { + settingsChanged = true; + } + settings.notificationTimeout = notificationTimeout; + }; + + uint32_t GetNotificationTimeout() const { + return settings.notificationTimeout; + }; + void SetWeatherFormat(WeatherFormat weatherFormat) { if (weatherFormat != settings.weatherFormat) { settingsChanged = true; @@ -360,6 +371,7 @@ namespace Pinetime { uint32_t version = settingsVersion; uint32_t stepsGoal = 10000; uint32_t screenTimeOut = 15000; + uint32_t notificationTimeout = 7000; bool alwaysOnDisplay = false; diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index 84fa603622..2e8e4b347c 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -42,6 +42,7 @@ #include "displayapp/screens/settings/Settings.h" #include "displayapp/screens/settings/SettingWatchFace.h" #include "displayapp/screens/settings/SettingTimeFormat.h" +#include "displayapp/screens/settings/SettingNotificationTimeout.h" #include "displayapp/screens/settings/SettingWeatherFormat.h" #include "displayapp/screens/settings/SettingWakeUp.h" #include "displayapp/screens/settings/SettingDisplay.h" @@ -367,6 +368,7 @@ void DisplayApp::Refresh() { // Only used for recovery firmware break; case Messages::NewNotification: + printf("NewNotification message received\n"); LoadNewScreen(Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down); break; case Messages::TimerDone: { @@ -471,6 +473,7 @@ void DisplayApp::Refresh() { break; case Messages::ButtonDoubleClicked: if (currentApp != Apps::Notifications && currentApp != Apps::NotificationsPreview) { + printf("Loading NotificationsPreview\n"); LoadNewScreen(Apps::Notifications, DisplayApp::FullRefreshDirections::Down); } break; @@ -568,6 +571,7 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio notificationManager, systemTask->nimble().alertService(), motorController, + settingsController, *systemTask, Screens::Notifications::Modes::Normal); break; @@ -576,6 +580,7 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio notificationManager, systemTask->nimble().alertService(), motorController, + settingsController, *systemTask, Screens::Notifications::Modes::Preview); break; @@ -601,6 +606,9 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio }); currentScreen = std::make_unique(this, std::move(items), settingsController, filesystem); } break; + case Apps::SettingNotificationTimeout: + currentScreen = std::make_unique(settingsController); + break; case Apps::SettingTimeFormat: currentScreen = std::make_unique(settingsController); break; diff --git a/src/displayapp/apps/Apps.h.in b/src/displayapp/apps/Apps.h.in index d440b598d1..7ba7d15efb 100644 --- a/src/displayapp/apps/Apps.h.in +++ b/src/displayapp/apps/Apps.h.in @@ -35,6 +35,7 @@ namespace Pinetime { Settings, SettingWatchFace, SettingTimeFormat, + SettingNotificationTimeout, SettingWeatherFormat, SettingHeartRate, SettingDisplay, diff --git a/src/displayapp/screens/Notifications.cpp b/src/displayapp/screens/Notifications.cpp index 837c4683aa..bb042f6387 100644 --- a/src/displayapp/screens/Notifications.cpp +++ b/src/displayapp/screens/Notifications.cpp @@ -14,15 +14,17 @@ Notifications::Notifications(DisplayApp* app, Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::AlertNotificationService& alertNotificationService, Pinetime::Controllers::MotorController& motorController, + Pinetime::Controllers::Settings& settingsController, System::SystemTask& systemTask, Modes mode) : app {app}, notificationManager {notificationManager}, alertNotificationService {alertNotificationService}, motorController {motorController}, + settingsController {settingsController}, wakeLock(systemTask), mode {mode} { - + printf("Notifications: mode=%d, timeoutLength=%lu\n", (int)mode, timeoutLength); notificationManager.ClearNewNotificationFlag(); auto notification = notificationManager.GetLastNotification(); if (notification.valid) { @@ -33,10 +35,11 @@ Notifications::Notifications(DisplayApp* app, notification.category, notificationManager.NbNotifications(), alertNotificationService, - motorController); + motorController, + settingsController); validDisplay = true; } else { - currentItem = std::make_unique(alertNotificationService, motorController); + currentItem = std::make_unique(alertNotificationService, motorController, settingsController); validDisplay = false; } if (mode == Modes::Preview) { @@ -109,7 +112,8 @@ void Notifications::Refresh() { notification.category, notificationManager.NbNotifications(), alertNotificationService, - motorController); + motorController, + settingsController); } else { running = false; } @@ -202,7 +206,8 @@ bool Notifications::OnTouchEvent(Pinetime::Applications::TouchEvents event) { previousNotification.category, notificationManager.NbNotifications(), alertNotificationService, - motorController); + motorController, + settingsController); } return true; case Pinetime::Applications::TouchEvents::SwipeUp: { @@ -229,7 +234,8 @@ bool Notifications::OnTouchEvent(Pinetime::Applications::TouchEvents event) { nextNotification.category, notificationManager.NbNotifications(), alertNotificationService, - motorController); + motorController, + settingsController); } return true; default: @@ -245,14 +251,16 @@ namespace { } Notifications::NotificationItem::NotificationItem(Pinetime::Controllers::AlertNotificationService& alertNotificationService, - Pinetime::Controllers::MotorController& motorController) + Pinetime::Controllers::MotorController& motorController, + Pinetime::Controllers::Settings& settingsController) : NotificationItem("Notifications", "No notifications to display", 0, Controllers::NotificationManager::Categories::Unknown, 0, alertNotificationService, - motorController) { + motorController, + settingsController) { } Notifications::NotificationItem::NotificationItem(const char* title, @@ -261,8 +269,9 @@ Notifications::NotificationItem::NotificationItem(const char* title, Controllers::NotificationManager::Categories category, uint8_t notifNb, Pinetime::Controllers::AlertNotificationService& alertNotificationService, - Pinetime::Controllers::MotorController& motorController) - : alertNotificationService {alertNotificationService}, motorController {motorController} { + Pinetime::Controllers::MotorController& motorController, + Pinetime::Controllers::Settings& settingsController) + : alertNotificationService {alertNotificationService}, motorController {motorController}, settingsController {settingsController} { container = lv_cont_create(lv_scr_act(), nullptr); lv_obj_set_size(container, LV_HOR_RES, LV_VER_RES); lv_obj_set_style_local_bg_color(container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); diff --git a/src/displayapp/screens/Notifications.h b/src/displayapp/screens/Notifications.h index 8488dc5bb2..a192633ed6 100644 --- a/src/displayapp/screens/Notifications.h +++ b/src/displayapp/screens/Notifications.h @@ -7,6 +7,7 @@ #include "displayapp/screens/Screen.h" #include "components/ble/NotificationManager.h" #include "components/motor/MotorController.h" +#include "components/settings/Settings.h" #include "systemtask/SystemTask.h" #include "systemtask/WakeLock.h" @@ -25,6 +26,7 @@ namespace Pinetime { Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::AlertNotificationService& alertNotificationService, Pinetime::Controllers::MotorController& motorController, + Pinetime::Controllers::Settings& settingsController, System::SystemTask& systemTask, Modes mode); ~Notifications() override; @@ -38,14 +40,16 @@ namespace Pinetime { class NotificationItem { public: NotificationItem(Pinetime::Controllers::AlertNotificationService& alertNotificationService, - Pinetime::Controllers::MotorController& motorController); + Pinetime::Controllers::MotorController& motorController, + Pinetime::Controllers::Settings& settingsController); NotificationItem(const char* title, const char* msg, uint8_t notifNr, Controllers::NotificationManager::Categories, uint8_t notifNb, Pinetime::Controllers::AlertNotificationService& alertNotificationService, - Pinetime::Controllers::MotorController& motorController); + Pinetime::Controllers::MotorController& motorController, + Pinetime::Controllers::Settings& settingsController); ~NotificationItem(); bool IsRunning() const { @@ -65,6 +69,7 @@ namespace Pinetime { lv_obj_t* label_reject; Pinetime::Controllers::AlertNotificationService& alertNotificationService; Pinetime::Controllers::MotorController& motorController; + Pinetime::Controllers::Settings& settingsController; bool running = true; }; @@ -74,6 +79,7 @@ namespace Pinetime { Pinetime::Controllers::NotificationManager& notificationManager; Pinetime::Controllers::AlertNotificationService& alertNotificationService; Pinetime::Controllers::MotorController& motorController; + Pinetime::Controllers::Settings& settingsController; System::WakeLock wakeLock; Modes mode = Modes::Normal; std::unique_ptr currentItem; @@ -85,7 +91,7 @@ namespace Pinetime { lv_obj_t* timeoutLine = nullptr; TickType_t timeoutTickCountStart; - static const TickType_t timeoutLength = pdMS_TO_TICKS(7000); + const TickType_t timeoutLength = pdMS_TO_TICKS(settingsController.GetNotificationTimeout()); bool interacted = true; bool dismissingNotification = false; diff --git a/src/displayapp/screens/settings/SettingNotificationTimeout.cpp b/src/displayapp/screens/settings/SettingNotificationTimeout.cpp new file mode 100644 index 0000000000..0f2ec21f8d --- /dev/null +++ b/src/displayapp/screens/settings/SettingNotificationTimeout.cpp @@ -0,0 +1,62 @@ +#include "displayapp/screens/settings/SettingNotificationTimeout.h" +#include +#include "displayapp/DisplayApp.h" +#include "displayapp/screens/Styles.h" +#include "displayapp/screens/Screen.h" +#include "displayapp/screens/Symbols.h" + +using namespace Pinetime::Applications::Screens; + +namespace { + struct Option { + uint32_t notificationTimeout; + const char* name; + }; + + constexpr std::array options = {{ + {7000, "7s"}, + {15000, "15s"}, + {30000, "30s"}, + }}; + + std::array CreateOptionArray() { + std::array optionArray; + for (size_t i = 0; i < CheckboxList::MaxItems; i++) { + if (i >= options.size()) { + optionArray[i].name = ""; + optionArray[i].enabled = false; + } else { + optionArray[i].name = options[i].name; + optionArray[i].enabled = true; + } + } + return optionArray; + } + + uint32_t GetDefaultOption(uint32_t currentOption) { + for (size_t i = 0; i < options.size(); i++) { + if (options[i].notificationTimeout == currentOption) { + return i; + } + } + return 0; + } +} + +SettingNotificationTimeout::SettingNotificationTimeout(Pinetime::Controllers::Settings& settingsController) + : checkboxList( + 0, + 1, + "Notification\nTimeout", + Symbols::bell, + GetDefaultOption(settingsController.GetNotificationTimeout()), + [&settings = settingsController](uint32_t index) { + settings.SetNotificationTimeout(options[index].notificationTimeout); + settings.SaveSettings(); + }, + CreateOptionArray()) { +} + +SettingNotificationTimeout::~SettingNotificationTimeout() { + lv_obj_clean(lv_scr_act()); +} diff --git a/src/displayapp/screens/settings/SettingNotificationTimeout.h b/src/displayapp/screens/settings/SettingNotificationTimeout.h new file mode 100644 index 0000000000..e7594f6319 --- /dev/null +++ b/src/displayapp/screens/settings/SettingNotificationTimeout.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +#include "components/settings/Settings.h" +#include "displayapp/screens/Screen.h" +#include "displayapp/screens/CheckboxList.h" + +namespace Pinetime { + + namespace Applications { + namespace Screens { + + class SettingNotificationTimeout : public Screen { + public: + SettingNotificationTimeout(Pinetime::Controllers::Settings& settingsController); + ~SettingNotificationTimeout() override; + + private: + CheckboxList checkboxList; + }; + } + } +} diff --git a/src/displayapp/screens/settings/Settings.h b/src/displayapp/screens/settings/Settings.h index 32ac3ca943..a9b2051d47 100644 --- a/src/displayapp/screens/settings/Settings.h +++ b/src/displayapp/screens/settings/Settings.h @@ -44,9 +44,10 @@ namespace Pinetime { {Symbols::batteryHalf, "Battery", Apps::BatteryInfo}, {Symbols::clock, "Chimes", Apps::SettingChimes}, + {Symbols::bell, "Notifications", Apps::SettingNotificationTimeout}, {Symbols::tachometer, "Shake Calib.", Apps::SettingShakeThreshold}, - {Symbols::check, "Firmware", Apps::FirmwareValidation}, + {Symbols::check, "Firmware", Apps::FirmwareValidation}, {Symbols::shieldAlt, "Over-the-air", Apps::SettingOTA}, {Symbols::bluetooth, "Bluetooth", Apps::SettingBluetooth}, {Symbols::list, "About", Apps::SysInfo}, From f32673b0164c3d9138277957a7b65d823a7bd579 Mon Sep 17 00:00:00 2001 From: Jeffrey van Pelt Date: Sun, 15 Mar 2026 18:04:12 +0100 Subject: [PATCH 2/4] Remove debug stuff --- src/displayapp/DisplayApp.cpp | 2 -- src/displayapp/screens/Notifications.cpp | 1 - 2 files changed, 3 deletions(-) diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index 2e8e4b347c..881f7a9036 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -368,7 +368,6 @@ void DisplayApp::Refresh() { // Only used for recovery firmware break; case Messages::NewNotification: - printf("NewNotification message received\n"); LoadNewScreen(Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down); break; case Messages::TimerDone: { @@ -473,7 +472,6 @@ void DisplayApp::Refresh() { break; case Messages::ButtonDoubleClicked: if (currentApp != Apps::Notifications && currentApp != Apps::NotificationsPreview) { - printf("Loading NotificationsPreview\n"); LoadNewScreen(Apps::Notifications, DisplayApp::FullRefreshDirections::Down); } break; diff --git a/src/displayapp/screens/Notifications.cpp b/src/displayapp/screens/Notifications.cpp index bb042f6387..cdb79bf863 100644 --- a/src/displayapp/screens/Notifications.cpp +++ b/src/displayapp/screens/Notifications.cpp @@ -24,7 +24,6 @@ Notifications::Notifications(DisplayApp* app, settingsController {settingsController}, wakeLock(systemTask), mode {mode} { - printf("Notifications: mode=%d, timeoutLength=%lu\n", (int)mode, timeoutLength); notificationManager.ClearNewNotificationFlag(); auto notification = notificationManager.GetLastNotification(); if (notification.valid) { From 97dbe9bb2603327b3311c77b5d4a68d62662e452 Mon Sep 17 00:00:00 2001 From: Jeffrey van Pelt Date: Sun, 15 Mar 2026 18:26:06 +0100 Subject: [PATCH 3/4] Fix whitespace --- src/displayapp/screens/Notifications.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/displayapp/screens/Notifications.cpp b/src/displayapp/screens/Notifications.cpp index cdb79bf863..a2c9e55fdf 100644 --- a/src/displayapp/screens/Notifications.cpp +++ b/src/displayapp/screens/Notifications.cpp @@ -24,6 +24,7 @@ Notifications::Notifications(DisplayApp* app, settingsController {settingsController}, wakeLock(systemTask), mode {mode} { + notificationManager.ClearNewNotificationFlag(); auto notification = notificationManager.GetLastNotification(); if (notification.valid) { From 6a27e84541605a3b457e66cce5cc840fc651ce0d Mon Sep 17 00:00:00 2001 From: Jeffrey van Pelt Date: Tue, 17 Mar 2026 22:20:59 +0100 Subject: [PATCH 4/4] Bump settingsVersion --- src/components/settings/Settings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/settings/Settings.h b/src/components/settings/Settings.h index 2c80117aa0..63d0a22f93 100644 --- a/src/components/settings/Settings.h +++ b/src/components/settings/Settings.h @@ -365,7 +365,7 @@ namespace Pinetime { private: Pinetime::Controllers::FS& fs; - static constexpr uint32_t settingsVersion = 0x000a; + static constexpr uint32_t settingsVersion = 0x000b; struct SettingsData { uint32_t version = settingsVersion;