diff options
| author | Michele Bini <michele.bini@gmail.com> | 2022-04-13 18:53:16 (GMT) |
|---|---|---|
| committer | Michele Bini <michele.bini@gmail.com> | 2022-04-13 18:53:16 (GMT) |
| commit | 531c1172a2357d53b214aaf7b29efee47d9b32e5 (patch) | |
| tree | e9c008f13900e1d3c63332a3bbc305754f8ae35c | |
| parent | 27fa6bba08766831fe143fe5ca13767bedcd9072 (diff) | |
Revert "Sans notification (notification manager retained as it seems to be used by the dfu manager)"ultraredux2
This reverts commit 569e6fea41c13f33ad1374bb80ca489aaf4a7037.
| -rw-r--r-- | src/CMakeLists.txt | 12 | ||||
| -rw-r--r-- | src/components/ble/AlertNotificationClient.cpp | 186 | ||||
| -rw-r--r-- | src/components/ble/AlertNotificationClient.h | 70 | ||||
| -rw-r--r-- | src/components/ble/AlertNotificationService.cpp | 124 | ||||
| -rw-r--r-- | src/components/ble/AlertNotificationService.h | 68 | ||||
| -rw-r--r-- | src/components/ble/ImmediateAlertService.cpp | 75 | ||||
| -rw-r--r-- | src/components/ble/ImmediateAlertService.h | 39 | ||||
| -rw-r--r-- | src/components/ble/NimbleController.cpp | 9 | ||||
| -rw-r--r-- | src/components/ble/NimbleController.h | 10 | ||||
| -rw-r--r-- | src/components/motor/MotorController.cpp | 12 | ||||
| -rw-r--r-- | src/components/motor/MotorController.h | 2 | ||||
| -rw-r--r-- | src/displayapp/DisplayApp.cpp | 27 | ||||
| -rw-r--r-- | src/displayapp/screens/NotificationIcon.cpp | 10 | ||||
| -rw-r--r-- | src/displayapp/screens/NotificationIcon.h | 12 | ||||
| -rw-r--r-- | src/displayapp/screens/Notifications.cpp | 290 | ||||
| -rw-r--r-- | src/displayapp/screens/Notifications.h | 84 | ||||
| -rw-r--r-- | src/systemtask/Messages.h | 2 | ||||
| -rw-r--r-- | src/systemtask/SystemTask.cpp | 8 |
18 files changed, 1038 insertions, 2 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e0e334a..c47cda7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -389,10 +389,12 @@ list(APPEND SOURCE_FILES displayapp/screens/Tile.cpp displayapp/screens/BatteryIcon.cpp displayapp/screens/BleIcon.cpp + displayapp/screens/NotificationIcon.cpp displayapp/screens/Label.cpp displayapp/screens/FirmwareUpdate.cpp displayapp/screens/FirmwareValidation.cpp displayapp/screens/ApplicationList.cpp + displayapp/screens/Notifications.cpp displayapp/screens/List.cpp displayapp/screens/Error.cpp displayapp/screens/Styles.cpp @@ -429,9 +431,12 @@ list(APPEND SOURCE_FILES components/ble/NimbleController.cpp components/ble/DeviceInformationService.cpp components/ble/CurrentTimeClient.cpp + components/ble/AlertNotificationClient.cpp components/ble/DfuService.cpp components/ble/CurrentTimeService.cpp + components/ble/AlertNotificationService.cpp components/ble/BatteryInformationService.cpp + components/ble/ImmediateAlertService.cpp components/ble/ServiceDiscovery.cpp components/firmwarevalidator/FirmwareValidator.cpp components/motor/MotorController.cpp @@ -479,9 +484,12 @@ list(APPEND RECOVERY_SOURCE_FILES components/ble/NimbleController.cpp components/ble/DeviceInformationService.cpp components/ble/CurrentTimeClient.cpp + components/ble/AlertNotificationClient.cpp components/ble/DfuService.cpp components/ble/CurrentTimeService.cpp + components/ble/AlertNotificationService.cpp components/ble/BatteryInformationService.cpp + components/ble/ImmediateAlertService.cpp components/ble/ServiceDiscovery.cpp components/firmwarevalidator/FirmwareValidator.cpp components/settings/Settings.cpp @@ -536,12 +544,14 @@ set(INCLUDE_FILES displayapp/screens/Meter.h displayapp/screens/BatteryIcon.h displayapp/screens/BleIcon.h + displayapp/screens/NotificationIcon.h displayapp/screens/ScreenList.h displayapp/screens/Label.h displayapp/screens/FirmwareUpdate.h displayapp/screens/FirmwareValidation.h displayapp/screens/ApplicationList.h displayapp/Apps.h + displayapp/screens/Notifications.h displayapp/Colors.h drivers/St7789.h drivers/SpiNorFlash.h @@ -567,9 +577,11 @@ set(INCLUDE_FILES components/ble/NimbleController.h components/ble/DeviceInformationService.h components/ble/CurrentTimeClient.h + components/ble/AlertNotificationClient.h components/ble/DfuService.h components/firmwarevalidator/FirmwareValidator.h components/ble/BatteryInformationService.h + components/ble/ImmediateAlertService.h components/ble/ServiceDiscovery.h components/ble/BleClient.h components/settings/Settings.h diff --git a/src/components/ble/AlertNotificationClient.cpp b/src/components/ble/AlertNotificationClient.cpp new file mode 100644 index 0000000..335845e --- /dev/null +++ b/src/components/ble/AlertNotificationClient.cpp @@ -0,0 +1,186 @@ +#include "components/ble/AlertNotificationClient.h" +#include <algorithm> +#include "components/ble/NotificationManager.h" +#include "systemtask/SystemTask.h" +#include <nrf_log.h> + +using namespace Pinetime::Controllers; +constexpr ble_uuid16_t AlertNotificationClient::ansServiceUuid; +constexpr ble_uuid16_t AlertNotificationClient::supportedNewAlertCategoryUuid; +constexpr ble_uuid16_t AlertNotificationClient::supportedUnreadAlertCategoryUuid; +constexpr ble_uuid16_t AlertNotificationClient::newAlertUuid; +constexpr ble_uuid16_t AlertNotificationClient::unreadAlertStatusUuid; +constexpr ble_uuid16_t AlertNotificationClient::controlPointUuid; + +namespace { + int OnDiscoveryEventCallback(uint16_t conn_handle, const struct ble_gatt_error* error, const struct ble_gatt_svc* service, void* arg) { + auto client = static_cast<AlertNotificationClient*>(arg); + return client->OnDiscoveryEvent(conn_handle, error, service); + } + + int OnAlertNotificationCharacteristicDiscoveredCallback(uint16_t conn_handle, + const struct ble_gatt_error* error, + const struct ble_gatt_chr* chr, + void* arg) { + auto client = static_cast<AlertNotificationClient*>(arg); + return client->OnCharacteristicsDiscoveryEvent(conn_handle, error, chr); + } + + int OnAlertNotificationDescriptorDiscoveryEventCallback( + uint16_t conn_handle, const struct ble_gatt_error* error, uint16_t chr_val_handle, const struct ble_gatt_dsc* dsc, void* arg) { + auto client = static_cast<AlertNotificationClient*>(arg); + return client->OnDescriptorDiscoveryEventCallback(conn_handle, error, chr_val_handle, dsc); + } + + int NewAlertSubcribeCallback(uint16_t conn_handle, const struct ble_gatt_error* error, struct ble_gatt_attr* attr, void* arg) { + auto client = static_cast<AlertNotificationClient*>(arg); + return client->OnNewAlertSubcribe(conn_handle, error, attr); + } +} + +AlertNotificationClient::AlertNotificationClient(Pinetime::System::SystemTask& systemTask, + Pinetime::Controllers::NotificationManager& notificationManager) + : systemTask {systemTask}, notificationManager {notificationManager} { +} + +bool AlertNotificationClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service) { + if (service == nullptr && error->status == BLE_HS_EDONE) { + if (isDiscovered) { + NRF_LOG_INFO("ANS Discovery found, starting characteristics discovery"); + + ble_gattc_disc_all_chrs(connectionHandle, ansStartHandle, ansEndHandle, OnAlertNotificationCharacteristicDiscoveredCallback, this); + } else { + NRF_LOG_INFO("ANS not found"); + onServiceDiscovered(connectionHandle); + } + return true; + } + + if (service != nullptr && ble_uuid_cmp(&ansServiceUuid.u, &service->uuid.u) == 0) { + NRF_LOG_INFO("ANS discovered : 0x%x - 0x%x", service->start_handle, service->end_handle); + ansStartHandle = service->start_handle; + ansEndHandle = service->end_handle; + isDiscovered = true; + } + return false; +} + +int AlertNotificationClient::OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, + const ble_gatt_error* error, + const ble_gatt_chr* characteristic) { + if (error->status != 0 && error->status != BLE_HS_EDONE) { + NRF_LOG_INFO("ANS Characteristic discovery ERROR"); + onServiceDiscovered(connectionHandle); + return 0; + } + + if (characteristic == nullptr && error->status == BLE_HS_EDONE) { + NRF_LOG_INFO("ANS Characteristic discovery complete"); + if (isCharacteristicDiscovered) { + ble_gattc_disc_all_dscs(connectionHandle, newAlertHandle, ansEndHandle, OnAlertNotificationDescriptorDiscoveryEventCallback, this); + } else + onServiceDiscovered(connectionHandle); + } else { + if (characteristic != nullptr && ble_uuid_cmp(&supportedNewAlertCategoryUuid.u, &characteristic->uuid.u) == 0) { + NRF_LOG_INFO("ANS Characteristic discovered : supportedNewAlertCategoryUuid"); + supportedNewAlertCategoryHandle = characteristic->val_handle; + } else if (characteristic != nullptr && ble_uuid_cmp(&supportedUnreadAlertCategoryUuid.u, &characteristic->uuid.u) == 0) { + NRF_LOG_INFO("ANS Characteristic discovered : supportedUnreadAlertCategoryUuid"); + supportedUnreadAlertCategoryHandle = characteristic->val_handle; + } else if (characteristic != nullptr && ble_uuid_cmp(&newAlertUuid.u, &characteristic->uuid.u) == 0) { + NRF_LOG_INFO("ANS Characteristic discovered : newAlertUuid"); + newAlertHandle = characteristic->val_handle; + newAlertDefHandle = characteristic->def_handle; + isCharacteristicDiscovered = true; + } else if (characteristic != nullptr && ble_uuid_cmp(&unreadAlertStatusUuid.u, &characteristic->uuid.u) == 0) { + NRF_LOG_INFO("ANS Characteristic discovered : unreadAlertStatusUuid"); + unreadAlertStatusHandle = characteristic->val_handle; + } else if (characteristic != nullptr && ble_uuid_cmp(&controlPointUuid.u, &characteristic->uuid.u) == 0) { + NRF_LOG_INFO("ANS Characteristic discovered : controlPointUuid"); + controlPointHandle = characteristic->val_handle; + } else + NRF_LOG_INFO("ANS Characteristic discovered : 0x%x", characteristic->val_handle); + } + return 0; +} + +int AlertNotificationClient::OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* attribute) { + if (error->status == 0) { + NRF_LOG_INFO("ANS New alert subscribe OK"); + } else { + NRF_LOG_INFO("ANS New alert subscribe ERROR"); + } + onServiceDiscovered(connectionHandle); + + return 0; +} + +int AlertNotificationClient::OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle, + const ble_gatt_error* error, + uint16_t characteristicValueHandle, + const ble_gatt_dsc* descriptor) { + if (error->status == 0) { + if (characteristicValueHandle == newAlertHandle && ble_uuid_cmp(&newAlertUuid.u, &descriptor->uuid.u)) { + if (newAlertDescriptorHandle == 0) { + NRF_LOG_INFO("ANS Descriptor discovered : %d", descriptor->handle); + newAlertDescriptorHandle = descriptor->handle; + isDescriptorFound = true; + uint8_t value[2]; + value[0] = 1; + value[1] = 0; + ble_gattc_write_flat(connectionHandle, newAlertDescriptorHandle, value, sizeof(value), NewAlertSubcribeCallback, this); + } + } + } else { + if (!isDescriptorFound) + onServiceDiscovered(connectionHandle); + } + return 0; +} + +void AlertNotificationClient::OnNotification(ble_gap_event* event) { + if (event->notify_rx.attr_handle == newAlertHandle) { + constexpr size_t stringTerminatorSize = 1; // end of string '\0' + constexpr size_t headerSize = 3; + const auto maxMessageSize {NotificationManager::MaximumMessageSize()}; + const auto maxBufferSize {maxMessageSize + headerSize}; + + // Ignore notifications with empty message + const auto packetLen = OS_MBUF_PKTLEN(event->notify_rx.om); + if (packetLen <= headerSize) + return; + + size_t bufferSize = std::min(packetLen + stringTerminatorSize, maxBufferSize); + auto messageSize = std::min(maxMessageSize, (bufferSize - headerSize)); + + NotificationManager::Notification notif; + os_mbuf_copydata(event->notify_rx.om, headerSize, messageSize - 1, notif.message.data()); + notif.message[messageSize - 1] = '\0'; + notif.size = messageSize; + notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert; + notificationManager.Push(std::move(notif)); + + systemTask.PushMessage(Pinetime::System::Messages::OnNewNotification); + } +} + +void AlertNotificationClient::Reset() { + ansStartHandle = 0; + ansEndHandle = 0; + supportedNewAlertCategoryHandle = 0; + supportedUnreadAlertCategoryHandle = 0; + newAlertHandle = 0; + newAlertDescriptorHandle = 0; + newAlertDefHandle = 0; + unreadAlertStatusHandle = 0; + controlPointHandle = 0; + isDiscovered = false; + isCharacteristicDiscovered = false; + isDescriptorFound = false; +} + +void AlertNotificationClient::Discover(uint16_t connectionHandle, std::function<void(uint16_t)> onServiceDiscovered) { + NRF_LOG_INFO("[ANS] Starting discovery"); + this->onServiceDiscovered = onServiceDiscovered; + ble_gattc_disc_svc_by_uuid(connectionHandle, &ansServiceUuid.u, OnDiscoveryEventCallback, this); +} diff --git a/src/components/ble/AlertNotificationClient.h b/src/components/ble/AlertNotificationClient.h new file mode 100644 index 0000000..2d6a387 --- /dev/null +++ b/src/components/ble/AlertNotificationClient.h @@ -0,0 +1,70 @@ +#pragma once + +#include <cstdint> +#include <functional> +#define min // workaround: nimble's min/max macros conflict with libstdc++ +#define max +#include <host/ble_gap.h> +#undef max +#undef min +#include "components/ble/BleClient.h" + +namespace Pinetime { + + namespace System { + class SystemTask; + } + + namespace Controllers { + class NotificationManager; + + class AlertNotificationClient : public BleClient { + public: + explicit AlertNotificationClient(Pinetime::System::SystemTask& systemTask, + Pinetime::Controllers::NotificationManager& notificationManager); + + bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service); + int OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_chr* characteristic); + int OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* attribute); + int OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle, + const ble_gatt_error* error, + uint16_t characteristicValueHandle, + const ble_gatt_dsc* descriptor); + void OnNotification(ble_gap_event* event); + void Reset(); + void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) override; + + private: + static constexpr uint16_t ansServiceId {0x1811}; + static constexpr uint16_t supportedNewAlertCategoryId = 0x2a47; + static constexpr uint16_t supportedUnreadAlertCategoryId = 0x2a48; + static constexpr uint16_t newAlertId = 0x2a46; + static constexpr uint16_t unreadAlertStatusId = 0x2a45; + static constexpr uint16_t controlPointId = 0x2a44; + + static constexpr ble_uuid16_t ansServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ansServiceId}; + static constexpr ble_uuid16_t supportedNewAlertCategoryUuid {.u {.type = BLE_UUID_TYPE_16}, .value = supportedNewAlertCategoryId}; + static constexpr ble_uuid16_t supportedUnreadAlertCategoryUuid {.u {.type = BLE_UUID_TYPE_16}, + .value = supportedUnreadAlertCategoryId}; + static constexpr ble_uuid16_t newAlertUuid {.u {.type = BLE_UUID_TYPE_16}, .value = newAlertId}; + static constexpr ble_uuid16_t unreadAlertStatusUuid {.u {.type = BLE_UUID_TYPE_16}, .value = unreadAlertStatusId}; + static constexpr ble_uuid16_t controlPointUuid {.u {.type = BLE_UUID_TYPE_16}, .value = controlPointId}; + + uint16_t ansStartHandle = 0; + uint16_t ansEndHandle = 0; + uint16_t supportedNewAlertCategoryHandle = 0; + uint16_t supportedUnreadAlertCategoryHandle = 0; + uint16_t newAlertHandle = 0; + uint16_t newAlertDescriptorHandle = 0; + uint16_t newAlertDefHandle = 0; + uint16_t unreadAlertStatusHandle = 0; + uint16_t controlPointHandle = 0; + bool isDiscovered = false; + Pinetime::System::SystemTask& systemTask; + Pinetime::Controllers::NotificationManager& notificationManager; + std::function<void(uint16_t)> onServiceDiscovered; + bool isCharacteristicDiscovered = false; + bool isDescriptorFound = false; + }; + } +} diff --git a/src/components/ble/AlertNotificationService.cpp b/src/components/ble/AlertNotificationService.cpp new file mode 100644 index 0000000..0481912 --- /dev/null +++ b/src/components/ble/AlertNotificationService.cpp @@ -0,0 +1,124 @@ +#include "components/ble/AlertNotificationService.h" +#include <hal/nrf_rtc.h> +#include <cstring> +#include <algorithm> +#include "components/ble/NotificationManager.h" +#include "systemtask/SystemTask.h" + +using namespace Pinetime::Controllers; + +constexpr ble_uuid16_t AlertNotificationService::ansUuid; +constexpr ble_uuid16_t AlertNotificationService::ansCharUuid; +constexpr ble_uuid128_t AlertNotificationService::notificationEventUuid; + +int AlertNotificationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) { + auto anService = static_cast<AlertNotificationService*>(arg); + return anService->OnAlert(conn_handle, attr_handle, ctxt); +} + +void AlertNotificationService::Init() { + int res; + res = ble_gatts_count_cfg(serviceDefinition); + ASSERT(res == 0); + + res = ble_gatts_add_svcs(serviceDefinition); + ASSERT(res == 0); +} + +AlertNotificationService::AlertNotificationService(System::SystemTask& systemTask, NotificationManager& notificationManager) + : characteristicDefinition {{.uuid = &ansCharUuid.u, .access_cb = AlertNotificationCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE}, + {.uuid = ¬ificationEventUuid.u, + .access_cb = AlertNotificationCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_NOTIFY, + .val_handle = &eventHandle}, + {0}}, + serviceDefinition { + {/* Device Information Service */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = &ansUuid.u, + .characteristics = characteristicDefinition}, + {0}, + }, + systemTask {systemTask}, + notificationManager {notificationManager} { +} + +int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) { + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + constexpr size_t stringTerminatorSize = 1; // end of string '\0' + constexpr size_t headerSize = 3; + const auto maxMessageSize {NotificationManager::MaximumMessageSize()}; + const auto maxBufferSize {maxMessageSize + headerSize}; + + // Ignore notifications with empty message + const auto packetLen = OS_MBUF_PKTLEN(ctxt->om); + if (packetLen <= headerSize) { + return 0; + } + + size_t bufferSize = std::min(packetLen + stringTerminatorSize, maxBufferSize); + auto messageSize = std::min(maxMessageSize, (bufferSize - headerSize)); + Categories category; + + NotificationManager::Notification notif; + os_mbuf_copydata(ctxt->om, headerSize, messageSize - 1, notif.message.data()); + os_mbuf_copydata(ctxt->om, 0, 1, &category); + notif.message[messageSize - 1] = '\0'; + notif.size = messageSize; + + // TODO convert all ANS categories to NotificationController categories + switch (category) { + case Categories::Call: + notif.category = Pinetime::Controllers::NotificationManager::Categories::IncomingCall; + break; + default: + notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert; + break; + } + + auto event = Pinetime::System::Messages::OnNewNotification; + notificationManager.Push(std::move(notif)); + systemTask.PushMessage(event); + } + return 0; +} + +void AlertNotificationService::AcceptIncomingCall() { + auto response = IncomingCallResponses::Answer; + auto* om = ble_hs_mbuf_from_flat(&response, 1); + + 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::RejectIncomingCall() { + auto response = IncomingCallResponses::Reject; + auto* om = ble_hs_mbuf_from_flat(&response, 1); + + 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::MuteIncomingCall() { + auto response = IncomingCallResponses::Mute; + auto* om = ble_hs_mbuf_from_flat(&response, 1); + + 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 new file mode 100644 index 0000000..5c7d428 --- /dev/null +++ b/src/components/ble/AlertNotificationService.h @@ -0,0 +1,68 @@ +#pragma once +#include <cstdint> +#include <array> +#define min // workaround: nimble's min/max macros conflict with libstdc++ +#define max +#include <host/ble_gap.h> +#undef max +#undef min + +// 00020001-78fc-48fe-8e23-433b3a1942d0 +#define NOTIFICATION_EVENT_SERVICE_UUID_BASE \ + { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x01, 0x00, 0x02, 0x00 } + +namespace Pinetime { + + namespace System { + class SystemTask; + } + namespace Controllers { + class NotificationManager; + + class AlertNotificationService { + public: + AlertNotificationService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::NotificationManager& notificationManager); + void Init(); + + int OnAlert(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt); + + void AcceptIncomingCall(); + void RejectIncomingCall(); + void MuteIncomingCall(); + + enum class IncomingCallResponses : uint8_t { Reject = 0x00, Answer = 0x01, Mute = 0x02 }; + + private: + enum class Categories : uint8_t { + SimpleAlert = 0x00, + Email = 0x01, + News = 0x02, + Call = 0x03, + MissedCall = 0x04, + MmsSms = 0x05, + VoiceMail = 0x06, + Schedule = 0x07, + HighPrioritizedAlert = 0x08, + InstantMessage = 0x09, + All = 0xff + }; + + static constexpr uint16_t ansId {0x1811}; + static constexpr uint16_t ansCharId {0x2a46}; + + static constexpr ble_uuid16_t ansUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ansId}; + + static constexpr ble_uuid16_t ansCharUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ansCharId}; + + static constexpr ble_uuid128_t notificationEventUuid {.u {.type = BLE_UUID_TYPE_128}, .value = NOTIFICATION_EVENT_SERVICE_UUID_BASE}; + + struct ble_gatt_chr_def characteristicDefinition[3]; + struct ble_gatt_svc_def serviceDefinition[2]; + + Pinetime::System::SystemTask& systemTask; + NotificationManager& notificationManager; + + uint16_t eventHandle; + }; + } +} diff --git a/src/components/ble/ImmediateAlertService.cpp b/src/components/ble/ImmediateAlertService.cpp new file mode 100644 index 0000000..c80b378 --- /dev/null +++ b/src/components/ble/ImmediateAlertService.cpp @@ -0,0 +1,75 @@ +#include "components/ble/ImmediateAlertService.h" +#include <cstring> +#include "components/ble/NotificationManager.h" +#include "systemtask/SystemTask.h" + +using namespace Pinetime::Controllers; + +constexpr ble_uuid16_t ImmediateAlertService::immediateAlertServiceUuid; +constexpr ble_uuid16_t ImmediateAlertService::alertLevelUuid; + +namespace { + int AlertLevelCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) { + auto* immediateAlertService = static_cast<ImmediateAlertService*>(arg); + return immediateAlertService->OnAlertLevelChanged(conn_handle, attr_handle, ctxt); + } + + const char* ToString(ImmediateAlertService::Levels level) { + switch (level) { + case ImmediateAlertService::Levels::NoAlert: + return "Alert : None"; + case ImmediateAlertService::Levels::HighAlert: + return "Alert : High"; + case ImmediateAlertService::Levels::MildAlert: + return "Alert : Mild"; + default: + return ""; + } + } +} + +ImmediateAlertService::ImmediateAlertService(Pinetime::System::SystemTask& systemTask, + Pinetime::Controllers::NotificationManager& notificationManager) + : systemTask {systemTask}, + notificationManager {notificationManager}, + characteristicDefinition {{.uuid = &alertLevelUuid.u, + .access_cb = AlertLevelCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_WRITE_NO_RSP, + .val_handle = &alertLevelHandle}, + {0}}, + serviceDefinition { + {/* Device Information Service */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = &immediateAlertServiceUuid.u, + .characteristics = characteristicDefinition}, + {0}, + } { +} + +void ImmediateAlertService::Init() { + int res = 0; + res = ble_gatts_count_cfg(serviceDefinition); + ASSERT(res == 0); + + res = ble_gatts_add_svcs(serviceDefinition); + ASSERT(res == 0); +} + +int ImmediateAlertService::OnAlertLevelChanged(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context) { + if (attributeHandle == alertLevelHandle) { + if (context->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + auto alertLevel = static_cast<Levels>(context->om->om_data[0]); + auto* alertString = ToString(alertLevel); + + NotificationManager::Notification notif; + std::memcpy(notif.message.data(), alertString, strlen(alertString)); + notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert; + notificationManager.Push(std::move(notif)); + + systemTask.PushMessage(Pinetime::System::Messages::OnNewNotification); + } + } + + return 0; +} diff --git a/src/components/ble/ImmediateAlertService.h b/src/components/ble/ImmediateAlertService.h new file mode 100644 index 0000000..1f778ac --- /dev/null +++ b/src/components/ble/ImmediateAlertService.h @@ -0,0 +1,39 @@ +#pragma once +#define min // workaround: nimble's min/max macros conflict with libstdc++ +#define max +#include <host/ble_gap.h> +#undef max +#undef min + +namespace Pinetime { + namespace System { + class SystemTask; + } + namespace Controllers { + class NotificationManager; + class ImmediateAlertService { + public: + enum class Levels : uint8_t { NoAlert = 0, MildAlert = 1, HighAlert = 2 }; + + ImmediateAlertService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::NotificationManager& notificationManager); + void Init(); + int OnAlertLevelChanged(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context); + + private: + Pinetime::System::SystemTask& systemTask; + NotificationManager& notificationManager; + + static constexpr uint16_t immediateAlertServiceId {0x1802}; + static constexpr uint16_t alertLevelId {0x2A06}; + + static constexpr ble_uuid16_t immediateAlertServiceUuid {.u {.type = BLE_UUID_TYPE_16}, .value = immediateAlertServiceId}; + + static constexpr ble_uuid16_t alertLevelUuid {.u {.type = BLE_UUID_TYPE_16}, .value = alertLevelId}; + + struct ble_gatt_chr_def characteristicDefinition[3]; + struct ble_gatt_svc_def serviceDefinition[2]; + + uint16_t alertLevelHandle; + }; + } +} diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index 5236619..f63d98c 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -38,9 +38,11 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask, currentTimeClient {dateTimeController}, anService {systemTask, notificationManager}, + alertNotificationClient {systemTask, notificationManager}, currentTimeService {dateTimeController}, batteryInformationService {batteryController}, - serviceDiscovery({¤tTimeClient}) { + immediateAlertService {systemTask, notificationManager}, + serviceDiscovery({¤tTimeClient, &alertNotificationClient}) { } void nimble_on_reset(int reason) { @@ -81,6 +83,7 @@ void NimbleController::Init() { anService.Init(); dfuService.Init(); batteryInformationService.Init(); + immediateAlertService.Init(); int rc; rc = ble_hs_util_ensure_addr(0); @@ -163,6 +166,7 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { if (event->connect.status != 0) { /* Connection failed; resume advertising. */ currentTimeClient.Reset(); + alertNotificationClient.Reset(); connectionHandle = BLE_HS_CONN_HANDLE_NONE; bleController.Disconnect(); fastAdvCount = 0; @@ -181,6 +185,7 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { NRF_LOG_INFO("disconnect reason=%d", event->disconnect.reason); currentTimeClient.Reset(); + alertNotificationClient.Reset(); connectionHandle = BLE_HS_CONN_HANDLE_NONE; if(bleController.IsConnected()) { bleController.Disconnect(); @@ -271,6 +276,8 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { event->notify_rx.conn_handle, event->notify_rx.attr_handle, notifSize); + + alertNotificationClient.OnNotification(event); } break; case BLE_GAP_EVENT_NOTIFY_TX: diff --git a/src/components/ble/NimbleController.h b/src/components/ble/NimbleController.h index 2069d8c..c9d6420 100644 --- a/src/components/ble/NimbleController.h +++ b/src/components/ble/NimbleController.h @@ -7,11 +7,14 @@ #include <host/ble_gap.h> #undef max #undef min +#include "components/ble/AlertNotificationClient.h" +#include "components/ble/AlertNotificationService.h" #include "components/ble/BatteryInformationService.h" #include "components/ble/CurrentTimeClient.h" #include "components/ble/CurrentTimeService.h" #include "components/ble/DeviceInformationService.h" #include "components/ble/DfuService.h" +#include "components/ble/ImmediateAlertService.h" #include "components/ble/ServiceDiscovery.h" namespace Pinetime { @@ -42,6 +45,10 @@ namespace Pinetime { int OnGAPEvent(ble_gap_event* event); void StartDiscovery(); + Pinetime::Controllers::AlertNotificationService& alertService() { + return anService; + }; + uint16_t connHandle(); void NotifyBatteryLevel(uint8_t level); @@ -60,8 +67,11 @@ namespace Pinetime { DeviceInformationService deviceInformationService; CurrentTimeClient currentTimeClient; + AlertNotificationService anService; + AlertNotificationClient alertNotificationClient; CurrentTimeService currentTimeService; BatteryInformationService batteryInformationService; + ImmediateAlertService immediateAlertService; ServiceDiscovery serviceDiscovery; uint8_t addrType; diff --git a/src/components/motor/MotorController.cpp b/src/components/motor/MotorController.cpp index 24aeeb0..bee07fb 100644 --- a/src/components/motor/MotorController.cpp +++ b/src/components/motor/MotorController.cpp @@ -5,6 +5,7 @@ #include "drivers/PinMap.h" APP_TIMER_DEF(shortVibTimer); +APP_TIMER_DEF(longVibTimer); using namespace Pinetime::Controllers; @@ -13,6 +14,7 @@ void MotorController::Init() { nrf_gpio_pin_set(PinMap::Motor); app_timer_create(&shortVibTimer, APP_TIMER_MODE_SINGLE_SHOT, StopMotor); + app_timer_create(&longVibTimer, APP_TIMER_MODE_REPEATED, Ring); } void MotorController::Ring(void* p_context) { @@ -25,6 +27,16 @@ void MotorController::RunForDuration(uint8_t motorDuration) { app_timer_start(shortVibTimer, APP_TIMER_TICKS(motorDuration), nullptr); } +void MotorController::StartRinging() { + Ring(this); + app_timer_start(longVibTimer, APP_TIMER_TICKS(1000), this); +} + +void MotorController::StopRinging() { + app_timer_stop(longVibTimer); + nrf_gpio_pin_set(PinMap::Motor); +} + void MotorController::StopMotor(void* p_context) { nrf_gpio_pin_set(PinMap::Motor); } diff --git a/src/components/motor/MotorController.h b/src/components/motor/MotorController.h index 3117f5c..b5a592b 100644 --- a/src/components/motor/MotorController.h +++ b/src/components/motor/MotorController.h @@ -11,6 +11,8 @@ namespace Pinetime { void Init(); void RunForDuration(uint8_t motorDuration); + void StartRinging(); + void StopRinging(); private: static void Ring(void* p_context); diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index 8f6ab6e..e7b3416 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -10,6 +10,7 @@ #include "displayapp/screens/Clock.h" #include "displayapp/screens/FirmwareUpdate.h" #include "displayapp/screens/FirmwareValidation.h" +#include "displayapp/screens/Notifications.h" #include "displayapp/screens/Tile.h" #include "displayapp/screens/Error.h" @@ -141,6 +142,9 @@ void DisplayApp::Refresh() { // clockScreen.SetBleConnectionState(bleController.IsConnected() ? Screens::Clock::BleConnectionStates::Connected : // Screens::Clock::BleConnectionStates::NotConnected); break; + case Messages::NewNotification: + LoadApp(Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down); + break; case Messages::TouchEvent: { if (state != States::Running) { break; @@ -155,6 +159,9 @@ void DisplayApp::Refresh() { case TouchEvents::SwipeUp: LoadApp(Apps::Launcher, DisplayApp::FullRefreshDirections::Up); break; + case TouchEvents::SwipeDown: + LoadApp(Apps::Notifications, DisplayApp::FullRefreshDirections::Down); + break; case TouchEvents::SwipeRight: LoadApp(Apps::QuickSettings, DisplayApp::FullRefreshDirections::RightAnim); break; @@ -177,7 +184,9 @@ void DisplayApp::Refresh() { break; case Messages::ButtonLongPressed: if (currentApp != Apps::Clock) { - if (currentApp == Apps::QuickSettings) { + if (currentApp == Apps::Notifications) { + LoadApp(Apps::Clock, DisplayApp::FullRefreshDirections::Up); + } else if (currentApp == Apps::QuickSettings) { LoadApp(Apps::Clock, DisplayApp::FullRefreshDirections::LeftAnim); } else { LoadApp(Apps::Clock, DisplayApp::FullRefreshDirections::Down); @@ -188,6 +197,12 @@ void DisplayApp::Refresh() { // Open up Firmware window, before possible reboot if press continues LoadApp(Apps::FirmwareValidation, DisplayApp::FullRefreshDirections::Up); break; + case Messages::ButtonDoubleClicked: + if (currentApp != Apps::Notifications && currentApp != Apps::NotificationsPreview) { + LoadApp(Apps::Notifications, DisplayApp::FullRefreshDirections::Down); + } + break; + case Messages::BleFirmwareUpdateStarted: LoadApp(Apps::FirmwareUpdate, DisplayApp::FullRefreshDirections::Down); break; @@ -259,6 +274,16 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) currentScreen = std::make_unique<Screens::FirmwareUpdate>(this, bleController); ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::None); break; + case Apps::Notifications: + currentScreen = std::make_unique<Screens::Notifications>( + this, notificationManager, systemTask->nimble().alertService(), motorController, *systemTask, Screens::Notifications::Modes::Normal); + ReturnApp(Apps::Clock, FullRefreshDirections::Up, TouchEvents::SwipeUp); + break; + case Apps::NotificationsPreview: + currentScreen = std::make_unique<Screens::Notifications>( + this, notificationManager, systemTask->nimble().alertService(), motorController, *systemTask, Screens::Notifications::Modes::Preview); + ReturnApp(Apps::Clock, FullRefreshDirections::Up, TouchEvents::SwipeUp); + break; // Settings case Apps::QuickSettings: currentScreen = std::make_unique<Screens::QuickSettings>( diff --git a/src/displayapp/screens/NotificationIcon.cpp b/src/displayapp/screens/NotificationIcon.cpp new file mode 100644 index 0000000..0e913ae --- /dev/null +++ b/src/displayapp/screens/NotificationIcon.cpp @@ -0,0 +1,10 @@ +#include "displayapp/screens/NotificationIcon.h" +#include "displayapp/screens/Symbols.h" +using namespace Pinetime::Applications::Screens; + +const char* NotificationIcon::GetIcon(bool newNotificationAvailable) { + if (newNotificationAvailable) + return Symbols::info; + else + return ""; +}
\ No newline at end of file diff --git a/src/displayapp/screens/NotificationIcon.h b/src/displayapp/screens/NotificationIcon.h new file mode 100644 index 0000000..dc34c3f --- /dev/null +++ b/src/displayapp/screens/NotificationIcon.h @@ -0,0 +1,12 @@ +#pragma once + +namespace Pinetime { + namespace Applications { + namespace Screens { + class NotificationIcon { + public: + static const char* GetIcon(bool newNotificationAvailable); + }; + } + } +}
\ No newline at end of file diff --git a/src/displayapp/screens/Notifications.cpp b/src/displayapp/screens/Notifications.cpp new file mode 100644 index 0000000..81ee234 --- /dev/null +++ b/src/displayapp/screens/Notifications.cpp @@ -0,0 +1,290 @@ +#include "displayapp/screens/Notifications.h" +#include "displayapp/DisplayApp.h" +#include "components/ble/AlertNotificationService.h" +#include "displayapp/screens/Symbols.h" + +using namespace Pinetime::Applications::Screens; + +Notifications::Notifications(DisplayApp* app, + Pinetime::Controllers::NotificationManager& notificationManager, + Pinetime::Controllers::AlertNotificationService& alertNotificationService, + Pinetime::Controllers::MotorController& motorController, + System::SystemTask& systemTask, + Modes mode) + : Screen(app), + notificationManager {notificationManager}, + alertNotificationService {alertNotificationService}, + motorController {motorController}, + systemTask {systemTask}, + mode {mode} { + notificationManager.ClearNewNotificationFlag(); + auto notification = notificationManager.GetLastNotification(); + if (notification.valid) { + currentId = notification.id; + currentItem = std::make_unique<NotificationItem>(notification.Title(), + notification.Message(), + notification.index, + notification.category, + notificationManager.NbNotifications(), + mode, + alertNotificationService, + motorController); + validDisplay = true; + } else { + currentItem = std::make_unique<NotificationItem>("Notification", + "No notification to display", + 0, + notification.category, + notificationManager.NbNotifications(), + Modes::Preview, + alertNotificationService, + motorController); + } + + if (mode == Modes::Preview) { + if (notification.category == Controllers::NotificationManager::Categories::IncomingCall) { + motorController.StartRinging(); + } else { + motorController.RunForDuration(35); + } + + timeoutLine = lv_line_create(lv_scr_act(), nullptr); + + lv_obj_set_style_local_line_width(timeoutLine, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3); + lv_obj_set_style_local_line_color(timeoutLine, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_obj_set_style_local_line_rounded(timeoutLine, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, true); + + lv_line_set_points(timeoutLine, timeoutLinePoints, 2); + timeoutTickCountStart = xTaskGetTickCount(); + interacted = false; + } + + taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); +} + +Notifications::~Notifications() { + lv_task_del(taskRefresh); + // make sure we stop any vibrations before exiting + motorController.StopRinging(); + lv_obj_clean(lv_scr_act()); +} + +void Notifications::Refresh() { + if (mode == Modes::Preview && timeoutLine != nullptr) { + TickType_t tick = xTaskGetTickCount(); + int32_t pos = 240 - ((tick - timeoutTickCountStart) / (timeoutLength / 240)); + if (pos <= 0) { + running = false; + } else { + timeoutLinePoints[1].x = pos; + lv_line_set_points(timeoutLine, timeoutLinePoints, 2); + } + } + running = currentItem->IsRunning() && running; +} + +void Notifications::OnPreviewInteraction() { + motorController.StopRinging(); + if (timeoutLine != nullptr) { + lv_obj_del(timeoutLine); + timeoutLine = nullptr; + } +} + +bool Notifications::OnTouchEvent(Pinetime::Applications::TouchEvents event) { + if (mode != Modes::Normal) { + if (!interacted && event == TouchEvents::Tap) { + interacted = true; + OnPreviewInteraction(); + return true; + } + return false; + } + + switch (event) { + case Pinetime::Applications::TouchEvents::SwipeDown: { + Controllers::NotificationManager::Notification previousNotification; + if (validDisplay) + previousNotification = notificationManager.GetPrevious(currentId); + else + previousNotification = notificationManager.GetLastNotification(); + + if (!previousNotification.valid) + return true; + + validDisplay = true; + currentId = previousNotification.id; + currentItem.reset(nullptr); + app->SetFullRefresh(DisplayApp::FullRefreshDirections::Down); + currentItem = std::make_unique<NotificationItem>(previousNotification.Title(), + previousNotification.Message(), + previousNotification.index, + previousNotification.category, + notificationManager.NbNotifications(), + mode, + alertNotificationService, + motorController); + } + return true; + case Pinetime::Applications::TouchEvents::SwipeUp: { + Controllers::NotificationManager::Notification nextNotification; + if (validDisplay) + nextNotification = notificationManager.GetNext(currentId); + else + nextNotification = notificationManager.GetLastNotification(); + + if (!nextNotification.valid) { + running = false; + return false; + } + + validDisplay = true; + currentId = nextNotification.id; + currentItem.reset(nullptr); + app->SetFullRefresh(DisplayApp::FullRefreshDirections::Up); + currentItem = std::make_unique<NotificationItem>(nextNotification.Title(), + nextNotification.Message(), + nextNotification.index, + nextNotification.category, + notificationManager.NbNotifications(), + mode, + alertNotificationService, + motorController); + } + return true; + default: + return false; + } +} + +namespace { + void CallEventHandler(lv_obj_t* obj, lv_event_t event) { + auto* item = static_cast<Notifications::NotificationItem*>(obj->user_data); + item->OnCallButtonEvent(obj, event); + } +} + +Notifications::NotificationItem::NotificationItem(const char* title, + const char* msg, + uint8_t notifNr, + Controllers::NotificationManager::Categories category, + uint8_t notifNb, + Modes mode, + Pinetime::Controllers::AlertNotificationService& alertNotificationService, + Pinetime::Controllers::MotorController& motorController) + : mode {mode}, alertNotificationService {alertNotificationService}, motorController {motorController} { + lv_obj_t* container1 = lv_cont_create(lv_scr_act(), NULL); + + lv_obj_set_style_local_bg_color(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x222222)); + lv_obj_set_style_local_pad_all(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 10); + lv_obj_set_style_local_pad_inner(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5); + lv_obj_set_style_local_border_width(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0); + + lv_obj_set_pos(container1, 0, 50); + lv_obj_set_size(container1, LV_HOR_RES, 190); + + lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT); + lv_cont_set_fit(container1, LV_FIT_NONE); + + lv_obj_t* alert_count = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text_fmt(alert_count, "%i/%i", notifNr, notifNb); + lv_obj_align(alert_count, NULL, LV_ALIGN_IN_TOP_RIGHT, 0, 16); + + lv_obj_t* alert_type = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(alert_type, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x888888)); + if(title == nullptr) { + lv_label_set_text_static(alert_type, "Notification"); + } else { + // copy title to label and replace newlines with spaces + lv_label_set_text(alert_type, title); + char *pchar = strchr(lv_label_get_text(alert_type), '\n'); + while (pchar != nullptr) { + *pchar = ' '; + pchar = strchr(pchar + 1, '\n'); + } + lv_label_refr_text(alert_type); + } + lv_label_set_long_mode(alert_type, LV_LABEL_LONG_SROLL_CIRC); + lv_obj_set_width(alert_type, 180); + lv_obj_align(alert_type, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 16); + + ///////// + switch (category) { + default: { + lv_obj_t* alert_subject = lv_label_create(container1, nullptr); + lv_obj_set_style_local_text_color(alert_subject, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE); + lv_label_set_long_mode(alert_subject, LV_LABEL_LONG_BREAK); + lv_obj_set_width(alert_subject, LV_HOR_RES - 20); + lv_label_set_text(alert_subject, msg); + } break; + case Controllers::NotificationManager::Categories::IncomingCall: { + lv_obj_set_height(container1, 108); + lv_obj_t* alert_subject = lv_label_create(container1, nullptr); + lv_obj_set_style_local_text_color(alert_subject, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE); + lv_label_set_long_mode(alert_subject, LV_LABEL_LONG_BREAK); + lv_obj_set_width(alert_subject, LV_HOR_RES - 20); + lv_label_set_text(alert_subject, "Incoming call from"); + + lv_obj_t* alert_caller = lv_label_create(container1, nullptr); + lv_obj_align(alert_caller, alert_subject, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0); + lv_label_set_long_mode(alert_caller, LV_LABEL_LONG_BREAK); + lv_obj_set_width(alert_caller, LV_HOR_RES - 20); + lv_label_set_text(alert_caller, msg); + + bt_accept = lv_btn_create(lv_scr_act(), nullptr); + bt_accept->user_data = this; + lv_obj_set_event_cb(bt_accept, CallEventHandler); + lv_obj_set_size(bt_accept, 76, 76); + lv_obj_align(bt_accept, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); + label_accept = lv_label_create(bt_accept, nullptr); + lv_label_set_text(label_accept, Symbols::phone); + lv_obj_set_style_local_bg_color(bt_accept, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN); + + bt_reject = lv_btn_create(lv_scr_act(), nullptr); + bt_reject->user_data = this; + lv_obj_set_event_cb(bt_reject, CallEventHandler); + lv_obj_set_size(bt_reject, 76, 76); + lv_obj_align(bt_reject, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0); + label_reject = lv_label_create(bt_reject, nullptr); + lv_label_set_text(label_reject, Symbols::phoneSlash); + lv_obj_set_style_local_bg_color(bt_reject, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); + + bt_mute = lv_btn_create(lv_scr_act(), nullptr); + bt_mute->user_data = this; + lv_obj_set_event_cb(bt_mute, CallEventHandler); + lv_obj_set_size(bt_mute, 76, 76); + lv_obj_align(bt_mute, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); + label_mute = lv_label_create(bt_mute, nullptr); + lv_label_set_text(label_mute, Symbols::volumMute); + lv_obj_set_style_local_bg_color(bt_mute, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); + } break; + } + + lv_obj_t* backgroundLabel = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP); + lv_obj_set_size(backgroundLabel, 240, 240); + lv_obj_set_pos(backgroundLabel, 0, 0); + lv_label_set_text(backgroundLabel, ""); +} + +void Notifications::NotificationItem::OnCallButtonEvent(lv_obj_t* obj, lv_event_t event) { + if (event != LV_EVENT_CLICKED) { + return; + } + + motorController.StopRinging(); + + if (obj == bt_accept) { + alertNotificationService.AcceptIncomingCall(); + } else if (obj == bt_reject) { + alertNotificationService.RejectIncomingCall(); + } else if (obj == bt_mute) { + alertNotificationService.MuteIncomingCall(); + } + + running = false; +} + +Notifications::NotificationItem::~NotificationItem() { + lv_obj_clean(lv_scr_act()); +} diff --git a/src/displayapp/screens/Notifications.h b/src/displayapp/screens/Notifications.h new file mode 100644 index 0000000..7416035 --- /dev/null +++ b/src/displayapp/screens/Notifications.h @@ -0,0 +1,84 @@ +#pragma once + +#include <lvgl/lvgl.h> +#include <FreeRTOS.h> +#include <cstdint> +#include <memory> +#include "displayapp/screens/Screen.h" +#include "components/ble/NotificationManager.h" +#include "components/motor/MotorController.h" +#include "systemtask/SystemTask.h" + +namespace Pinetime { + namespace Controllers { + class AlertNotificationService; + } + namespace Applications { + namespace Screens { + + class Notifications : public Screen { + public: + enum class Modes { Normal, Preview }; + explicit Notifications(DisplayApp* app, + Pinetime::Controllers::NotificationManager& notificationManager, + Pinetime::Controllers::AlertNotificationService& alertNotificationService, + Pinetime::Controllers::MotorController& motorController, + System::SystemTask& systemTask, + Modes mode); + ~Notifications() override; + + void Refresh() override; + bool OnTouchEvent(Pinetime::Applications::TouchEvents event) override; + void OnPreviewInteraction(); + + class NotificationItem { + public: + NotificationItem(const char* title, + const char* msg, + uint8_t notifNr, + Controllers::NotificationManager::Categories, + uint8_t notifNb, + Modes mode, + Pinetime::Controllers::AlertNotificationService& alertNotificationService, + Pinetime::Controllers::MotorController& motorController); + ~NotificationItem(); + bool IsRunning() const { + return running; + } + void OnCallButtonEvent(lv_obj_t*, lv_event_t event); + + private: + lv_obj_t* container1; + lv_obj_t* bt_accept; + lv_obj_t* bt_mute; + lv_obj_t* bt_reject; + lv_obj_t* label_accept; + lv_obj_t* label_mute; + lv_obj_t* label_reject; + Modes mode; + Pinetime::Controllers::AlertNotificationService& alertNotificationService; + Pinetime::Controllers::MotorController& motorController; + bool running = true; + }; + + private: + Pinetime::Controllers::NotificationManager& notificationManager; + Pinetime::Controllers::AlertNotificationService& alertNotificationService; + Pinetime::Controllers::MotorController& motorController; + System::SystemTask& systemTask; + Modes mode = Modes::Normal; + std::unique_ptr<NotificationItem> currentItem; + Controllers::NotificationManager::Notification::Id currentId; + bool validDisplay = false; + + lv_point_t timeoutLinePoints[2] {{0, 1}, {239, 1}}; + lv_obj_t* timeoutLine = nullptr; + TickType_t timeoutTickCountStart; + static const TickType_t timeoutLength = pdMS_TO_TICKS(7000); + bool interacted = true; + + lv_task_t* taskRefresh; + }; + } + } +} diff --git a/src/systemtask/Messages.h b/src/systemtask/Messages.h index a55acf4..3c99a89 100644 --- a/src/systemtask/Messages.h +++ b/src/systemtask/Messages.h @@ -4,6 +4,7 @@ namespace Pinetime { namespace System { enum class Messages { OnNewTime, + OnNewNotification, OnNewCall, BleConnected, BleFirmwareUpdateStarted, @@ -13,6 +14,7 @@ namespace Pinetime { HandleButtonTimerEvent, OnNewDay, OnChargingEvent, + StopRinging, MeasureBatteryTimerExpired, BatteryPercentageUpdated, StartFileTransfer, diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 48b7d6e..60b4cc9 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -186,6 +186,14 @@ void SystemTask::Work() { case Messages::OnNewTime: displayApp.PushMessage(Pinetime::Applications::Display::Messages::UpdateDateTime); break; + case Messages::OnNewNotification: + if (settingsController.GetNotificationStatus() == Pinetime::Controllers::Settings::Notification::ON) { + displayApp.PushMessage(Pinetime::Applications::Display::Messages::NewNotification); + } + break; + case Messages::StopRinging: + motorController.StopRinging(); + break; case Messages::BleConnected: isBleDiscoveryTimerRunning = true; bleDiscoveryTimer = 5; |
