From 0706d4fd86e8fe7fff1dae445be850ed1974b220 Mon Sep 17 00:00:00 2001 From: Victor Kareh Date: Thu, 19 Mar 2026 15:13:06 -0400 Subject: [PATCH 1/2] colors: Move violet to InfiniTimeTheme to allow reuse --- src/displayapp/InfiniTimeTheme.h | 1 + src/displayapp/screens/settings/QuickSettings.cpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/displayapp/InfiniTimeTheme.h b/src/displayapp/InfiniTimeTheme.h index 57680e8738..d105458436 100644 --- a/src/displayapp/InfiniTimeTheme.h +++ b/src/displayapp/InfiniTimeTheme.h @@ -7,6 +7,7 @@ namespace Colors { static constexpr lv_color_t orange = LV_COLOR_MAKE(0xff, 0xb0, 0x0); static constexpr lv_color_t green = LV_COLOR_MAKE(0x0, 0xb0, 0x0); static constexpr lv_color_t blue = LV_COLOR_MAKE(0x0, 0x50, 0xff); + static constexpr lv_color_t violet = LV_COLOR_MAKE(0x60, 0x00, 0xff); static constexpr lv_color_t lightGray = LV_COLOR_MAKE(0xb0, 0xb0, 0xb0); static constexpr lv_color_t gray = LV_COLOR_MAKE(0x50, 0x50, 0x50); diff --git a/src/displayapp/screens/settings/QuickSettings.cpp b/src/displayapp/screens/settings/QuickSettings.cpp index c5c3071aef..ec236b6413 100644 --- a/src/displayapp/screens/settings/QuickSettings.cpp +++ b/src/displayapp/screens/settings/QuickSettings.cpp @@ -90,8 +90,7 @@ QuickSettings::QuickSettings(Pinetime::Applications::DisplayApp* app, lv_obj_set_event_cb(btn3, ButtonEventHandler); lv_obj_add_style(btn3, LV_BTN_PART_MAIN, &btn_style); lv_obj_set_style_local_bg_color(btn3, LV_BTN_PART_MAIN, static_cast(ButtonState::NotificationsOff), LV_COLOR_RED); - static constexpr lv_color_t violet = LV_COLOR_MAKE(0x60, 0x00, 0xff); - lv_obj_set_style_local_bg_color(btn3, LV_BTN_PART_MAIN, static_cast(ButtonState::Sleep), violet); + lv_obj_set_style_local_bg_color(btn3, LV_BTN_PART_MAIN, static_cast(ButtonState::Sleep), Colors::violet); lv_obj_set_size(btn3, buttonWidth, buttonHeight); lv_obj_align(btn3, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, buttonXOffset, 0); From fa97dad79158d0681e47337f0d01e7496c9fb84e Mon Sep 17 00:00:00 2001 From: Victor Kareh Date: Thu, 19 Mar 2026 15:17:34 -0400 Subject: [PATCH 2/2] notifications: Add phone alarm support with dismiss/snooze Map HighPrioritizedAlert category to a new PhoneAlarm notification type that shows dismiss and snooze buttons and rings until the user interacts or times out. Watch sends 2-byte BLE responses back to the phone to dismiss or snooze the alarm. --- .../ble/AlertNotificationService.cpp | 29 ++++++++++ src/components/ble/AlertNotificationService.h | 5 ++ src/components/ble/NotificationManager.h | 2 +- src/displayapp/screens/Notifications.cpp | 53 ++++++++++++++++--- src/displayapp/screens/Notifications.h | 1 + 5 files changed, 81 insertions(+), 9 deletions(-) diff --git a/src/components/ble/AlertNotificationService.cpp b/src/components/ble/AlertNotificationService.cpp index d9f28698bc..2d5df0f06d 100644 --- a/src/components/ble/AlertNotificationService.cpp +++ b/src/components/ble/AlertNotificationService.cpp @@ -72,6 +72,9 @@ int AlertNotificationService::OnAlert(struct ble_gatt_access_ctxt* ctxt) { case Categories::Call: notif.category = Pinetime::Controllers::NotificationManager::Categories::IncomingCall; break; + case Categories::HighPrioritizedAlert: + notif.category = Pinetime::Controllers::NotificationManager::Categories::PhoneAlarm; + break; default: notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert; break; @@ -122,3 +125,29 @@ void AlertNotificationService::MuteIncomingCall() { ble_gattc_notify_custom(connectionHandle, eventHandle, om); } + +void AlertNotificationService::DismissPhoneAlarm() { + uint8_t response[2] = {0x01, static_cast(PhoneAlarmResponses::Dismiss)}; + auto* om = ble_hs_mbuf_from_flat(response, sizeof(response)); + + uint16_t connectionHandle = systemTask.nimble().connHandle(); + + if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) { + return; + } + + ble_gattc_notify_custom(connectionHandle, eventHandle, om); +} + +void AlertNotificationService::SnoozePhoneAlarm() { + uint8_t response[2] = {0x01, static_cast(PhoneAlarmResponses::Snooze)}; + auto* om = ble_hs_mbuf_from_flat(response, sizeof(response)); + + uint16_t connectionHandle = systemTask.nimble().connHandle(); + + if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) { + return; + } + + ble_gattc_notify_custom(connectionHandle, eventHandle, om); +} diff --git a/src/components/ble/AlertNotificationService.h b/src/components/ble/AlertNotificationService.h index 4b3c638590..679d5f41c0 100644 --- a/src/components/ble/AlertNotificationService.h +++ b/src/components/ble/AlertNotificationService.h @@ -33,6 +33,11 @@ namespace Pinetime { enum class IncomingCallResponses : uint8_t { Reject = 0x00, Answer = 0x01, Mute = 0x02 }; + void DismissPhoneAlarm(); + void SnoozePhoneAlarm(); + + enum class PhoneAlarmResponses : uint8_t { Dismiss = 0x00, Snooze = 0x01 }; + private: enum class Categories : uint8_t { SimpleAlert = 0x00, diff --git a/src/components/ble/NotificationManager.h b/src/components/ble/NotificationManager.h index 57a9c715d2..1930197f2e 100644 --- a/src/components/ble/NotificationManager.h +++ b/src/components/ble/NotificationManager.h @@ -19,7 +19,7 @@ namespace Pinetime { Sms, VoiceMail, Schedule, - HighProriotyAlert, + PhoneAlarm, // ANS HighPriorityAlert (0x08) is only used for phone alarms in Gadgetbridge InstantMessage }; static constexpr uint8_t MessageSize {100}; diff --git a/src/displayapp/screens/Notifications.cpp b/src/displayapp/screens/Notifications.cpp index 837c4683aa..80c1b4217f 100644 --- a/src/displayapp/screens/Notifications.cpp +++ b/src/displayapp/screens/Notifications.cpp @@ -41,7 +41,8 @@ Notifications::Notifications(DisplayApp* app, } if (mode == Modes::Preview) { wakeLock.Lock(); - if (notification.category == Controllers::NotificationManager::Categories::IncomingCall) { + if (notification.category == Controllers::NotificationManager::Categories::IncomingCall || + notification.category == Controllers::NotificationManager::Categories::PhoneAlarm) { motorController.StartRinging(); } else { motorController.RunForDuration(35); @@ -262,7 +263,7 @@ Notifications::NotificationItem::NotificationItem(const char* title, uint8_t notifNb, Pinetime::Controllers::AlertNotificationService& alertNotificationService, Pinetime::Controllers::MotorController& motorController) - : alertNotificationService {alertNotificationService}, motorController {motorController} { + : alertNotificationService {alertNotificationService}, motorController {motorController}, category {category} { 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); @@ -348,6 +349,34 @@ Notifications::NotificationItem::NotificationItem(const char* title, lv_label_set_text_static(label_mute, Symbols::volumMute); lv_obj_set_style_local_bg_color(bt_mute, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); } break; + case Controllers::NotificationManager::Categories::PhoneAlarm: { + lv_obj_set_height(subject_container, 108); + lv_label_set_text_static(alert_subject, "Phone Alarm"); + + lv_obj_t* alert_label = lv_label_create(subject_container, nullptr); + lv_obj_align(alert_label, alert_subject, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0); + lv_label_set_long_mode(alert_label, LV_LABEL_LONG_BREAK); + lv_obj_set_width(alert_label, LV_HOR_RES - 20); + lv_label_set_text(alert_label, msg); + + bt_reject = lv_btn_create(container, nullptr); + bt_reject->user_data = this; + lv_obj_set_event_cb(bt_reject, CallEventHandler); + lv_obj_set_size(bt_reject, 115, 76); + lv_obj_align(bt_reject, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); + label_reject = lv_label_create(bt_reject, nullptr); + lv_label_set_text_static(label_reject, Symbols::stop); + lv_obj_set_style_local_bg_color(bt_reject, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); + + bt_mute = lv_btn_create(container, nullptr); + bt_mute->user_data = this; + lv_obj_set_event_cb(bt_mute, CallEventHandler); + lv_obj_set_size(bt_mute, 115, 76); + lv_obj_align(bt_mute, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); + label_mute = lv_label_create(bt_mute, nullptr); + lv_label_set_text_static(label_mute, "zzz"); + lv_obj_set_style_local_bg_color(bt_mute, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::violet); + } break; } } @@ -358,12 +387,20 @@ void Notifications::NotificationItem::OnCallButtonEvent(lv_obj_t* obj, lv_event_ motorController.StopRinging(); - if (obj == bt_accept) { - alertNotificationService.AcceptIncomingCall(); - } else if (obj == bt_reject) { - alertNotificationService.RejectIncomingCall(); - } else if (obj == bt_mute) { - alertNotificationService.MuteIncomingCall(); + if (category == Controllers::NotificationManager::Categories::PhoneAlarm) { + if (obj == bt_reject) { + alertNotificationService.DismissPhoneAlarm(); + } else if (obj == bt_mute) { + alertNotificationService.SnoozePhoneAlarm(); + } + } else { + if (obj == bt_accept) { + alertNotificationService.AcceptIncomingCall(); + } else if (obj == bt_reject) { + alertNotificationService.RejectIncomingCall(); + } else if (obj == bt_mute) { + alertNotificationService.MuteIncomingCall(); + } } running = false; diff --git a/src/displayapp/screens/Notifications.h b/src/displayapp/screens/Notifications.h index 8488dc5bb2..2eab40aaae 100644 --- a/src/displayapp/screens/Notifications.h +++ b/src/displayapp/screens/Notifications.h @@ -66,6 +66,7 @@ namespace Pinetime { Pinetime::Controllers::AlertNotificationService& alertNotificationService; Pinetime::Controllers::MotorController& motorController; + Controllers::NotificationManager::Categories category = Controllers::NotificationManager::Categories::Unknown; bool running = true; };