From 4fb92b5383cc9b07c134bc29cdf085ed3f9f1c6f Mon Sep 17 00:00:00 2001 From: Jozef Mlich Date: Thu, 19 Oct 2023 07:54:22 +0200 Subject: [PATCH 01/21] Try to implement find my phone feature #343 Co-authored-by: Vyacheslav Chigrin --- src/CMakeLists.txt | 4 +- src/components/ble/ImmediateAlertService.cpp | 14 +++++ src/components/ble/ImmediateAlertService.h | 2 + src/components/ble/NimbleController.cpp | 4 +- src/components/ble/NimbleController.h | 6 ++- src/displayapp/DisplayApp.cpp | 1 + src/displayapp/apps/Apps.h.in | 1 + src/displayapp/apps/CMakeLists.txt | 1 + src/displayapp/fonts/fonts.json | 2 +- src/displayapp/screens/FindMyPhone.cpp | 55 ++++++++++++++++++++ src/displayapp/screens/FindMyPhone.h | 49 +++++++++++++++++ src/displayapp/screens/Symbols.h | 1 + 12 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 src/displayapp/screens/FindMyPhone.cpp create mode 100644 src/displayapp/screens/FindMyPhone.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5cd2e656a4..9c34a8bf6b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -386,6 +386,7 @@ list(APPEND SOURCE_FILES displayapp/screens/Notifications.cpp displayapp/screens/Twos.cpp displayapp/screens/HeartRate.cpp + displayapp/screens/FindMyPhone.cpp displayapp/screens/FlashLight.cpp displayapp/screens/List.cpp displayapp/screens/CheckboxList.cpp @@ -610,6 +611,7 @@ set(INCLUDE_FILES displayapp/Apps.h displayapp/screens/Notifications.h displayapp/screens/HeartRate.h + displayapp/screens/FindMyPhone.h displayapp/screens/Metronome.h displayapp/screens/Motion.h displayapp/screens/Timer.h @@ -841,7 +843,7 @@ if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") # add_definitions(-DCLOCK_CONFIG_LOG_LEVEL=4) # add_definitions(-DRTC_CONFIG_LOG_ENABLED=1) # add_definitions(-DRTC_CONFIG_LOG_LEVEL=4) - + # Nimble Logging add_definitions(-DMYNEWT_VAL_NEWT_FEATURE_LOGCFG=1) # add_definitions(-DMYNEWT_VAL_LOG_LEVEL=0) diff --git a/src/components/ble/ImmediateAlertService.cpp b/src/components/ble/ImmediateAlertService.cpp index b9de615aa0..b87c2adae8 100644 --- a/src/components/ble/ImmediateAlertService.cpp +++ b/src/components/ble/ImmediateAlertService.cpp @@ -73,3 +73,17 @@ int ImmediateAlertService::OnAlertLevelChanged(uint16_t attributeHandle, ble_gat return 0; } + +void ImmediateAlertService::sendImmediateAlert(ImmediateAlertService::Levels level) { + + auto* om = ble_hs_mbuf_from_flat(&level, 1); + + uint16_t connectionHandle = systemTask.nimble().connHandle(); + + if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) { + return; + } + + ble_gattc_notify_custom(connectionHandle, alertLevelHandle, om); + +} diff --git a/src/components/ble/ImmediateAlertService.h b/src/components/ble/ImmediateAlertService.h index 47a0543935..34483c6e3e 100644 --- a/src/components/ble/ImmediateAlertService.h +++ b/src/components/ble/ImmediateAlertService.h @@ -21,6 +21,8 @@ namespace Pinetime { void Init(); int OnAlertLevelChanged(uint16_t attributeHandle, ble_gatt_access_ctxt* context); + void sendImmediateAlert(Levels level); + private: Pinetime::System::SystemTask& systemTask; NotificationManager& notificationManager; diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index 5059007ab9..bca10b7d2c 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -45,7 +45,7 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask, musicService {*this}, weatherService {dateTimeController}, batteryInformationService {batteryController}, - immediateAlertService {systemTask, notificationManager}, + iaService {systemTask, notificationManager}, heartRateService {*this, heartRateController}, motionService {*this, motionController}, fsService {systemTask, fs}, @@ -94,7 +94,7 @@ void NimbleController::Init() { anService.Init(); dfuService.Init(); batteryInformationService.Init(); - immediateAlertService.Init(); + iaService.Init(); heartRateService.Init(); motionService.Init(); fsService.Init(); diff --git a/src/components/ble/NimbleController.h b/src/components/ble/NimbleController.h index 597ef0cc34..f04136d0d9 100644 --- a/src/components/ble/NimbleController.h +++ b/src/components/ble/NimbleController.h @@ -71,6 +71,10 @@ namespace Pinetime { return weatherService; }; + Pinetime::Controllers::ImmediateAlertService& immediateAlertService() { + return iaService; + } + uint16_t connHandle(); void NotifyBatteryLevel(uint8_t level); @@ -102,7 +106,7 @@ namespace Pinetime { SimpleWeatherService weatherService; NavigationService navService; BatteryInformationService batteryInformationService; - ImmediateAlertService immediateAlertService; + ImmediateAlertService iaService; HeartRateService heartRateService; MotionService motionService; FSService fsService; diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index bfd7dbed6d..29549dc087 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -1,6 +1,7 @@ #include "displayapp/DisplayApp.h" #include #include "displayapp/screens/HeartRate.h" +#include "displayapp/screens/FindMyPhone.h" #include "displayapp/screens/Motion.h" #include "displayapp/screens/Timer.h" #include "displayapp/screens/Alarm.h" diff --git a/src/displayapp/apps/Apps.h.in b/src/displayapp/apps/Apps.h.in index f6feeb7b6d..afeade4249 100644 --- a/src/displayapp/apps/Apps.h.in +++ b/src/displayapp/apps/Apps.h.in @@ -43,6 +43,7 @@ namespace Pinetime { SettingChimes, SettingShakeThreshold, SettingBluetooth, + FindMyPhone, Error }; diff --git a/src/displayapp/apps/CMakeLists.txt b/src/displayapp/apps/CMakeLists.txt index 93196ed6a0..57887a2d80 100644 --- a/src/displayapp/apps/CMakeLists.txt +++ b/src/displayapp/apps/CMakeLists.txt @@ -14,6 +14,7 @@ else () set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Metronome") set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Navigation") set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Calculator") + set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::FindMyPhone") set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Weather") #set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Motion") set(USERAPP_TYPES "${DEFAULT_USER_APP_TYPES}" CACHE STRING "List of user apps to build into the firmware") diff --git a/src/displayapp/fonts/fonts.json b/src/displayapp/fonts/fonts.json index fea3160572..092beaadb5 100644 --- a/src/displayapp/fonts/fonts.json +++ b/src/displayapp/fonts/fonts.json @@ -7,7 +7,7 @@ }, { "file": "FontAwesome5-Solid+Brands+Regular.woff", - "range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf0f3, 0xf522, 0xf743, 0xf1ec, 0xf55a" + "range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf0f3, 0xf522, 0xf743, 0xf1ec, 0xf55a, 0xf002" } ], "bpp": 1, diff --git a/src/displayapp/screens/FindMyPhone.cpp b/src/displayapp/screens/FindMyPhone.cpp new file mode 100644 index 0000000000..540f151afe --- /dev/null +++ b/src/displayapp/screens/FindMyPhone.cpp @@ -0,0 +1,55 @@ +#include "displayapp/screens/FindMyPhone.h" +#include + +#include "displayapp/DisplayApp.h" +#include "displayapp/InfiniTimeTheme.h" + +using namespace Pinetime::Applications::Screens; + +namespace { + void btnStartStopEventHandler(lv_obj_t* obj, lv_event_t event) { + auto* screen = static_cast(obj->user_data); + screen->OnStartStopEvent(event); + } +} + +FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertService& immediateAlertService) + : immediateAlertService {immediateAlertService} { + isFindMyPhoneRunning = false; + label_title = lv_label_create(lv_scr_act(), nullptr); + + lv_label_set_text_static(label_title, "Find my phone"); + lv_obj_align(label_title, nullptr, LV_ALIGN_CENTER, 0, -40); + + btn_startStop = lv_btn_create(lv_scr_act(), nullptr); + btn_startStop->user_data = this; + lv_obj_set_height(btn_startStop, 50); + lv_obj_set_event_cb(btn_startStop, btnStartStopEventHandler); + lv_obj_align(btn_startStop, label_title, LV_ALIGN_OUT_BOTTOM_MID, 0, 10); + + label_startStop = lv_label_create(btn_startStop, nullptr); + UpdateStartStopButton(isFindMyPhoneRunning); +} + +FindMyPhone::~FindMyPhone() { + lv_obj_clean(lv_scr_act()); +} + +void FindMyPhone::OnStartStopEvent(lv_event_t event) { + if (event == LV_EVENT_CLICKED) { + isFindMyPhoneRunning = !isFindMyPhoneRunning; + UpdateStartStopButton(isFindMyPhoneRunning); + } +} + +void FindMyPhone::UpdateStartStopButton(bool isRunning) { + if (isRunning) { + immediateAlertService.sendImmediateAlert(Pinetime::Controllers::ImmediateAlertService::Levels::HighAlert); + lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight); + lv_label_set_text_static(label_startStop, "Stop"); + } else { + lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); + lv_label_set_text_static(label_startStop, "Start"); + immediateAlertService.sendImmediateAlert(Pinetime::Controllers::ImmediateAlertService::Levels::NoAlert); + } +} diff --git a/src/displayapp/screens/FindMyPhone.h b/src/displayapp/screens/FindMyPhone.h new file mode 100644 index 0000000000..ff99885ced --- /dev/null +++ b/src/displayapp/screens/FindMyPhone.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include "displayapp/screens/Screen.h" +#include "Symbols.h" +#include "systemtask/SystemTask.h" +#include +#include + +namespace Pinetime { + + namespace Controllers { + class ImmediateAlertService; + } + + namespace Applications { + namespace Screens { + + class FindMyPhone : public Screen { + public: + FindMyPhone(Pinetime::Controllers::ImmediateAlertService& immediateAlertService); + ~FindMyPhone() override; + + void OnStartStopEvent(lv_event_t event); + + private: + Pinetime::Controllers::ImmediateAlertService& immediateAlertService; + + void UpdateStartStopButton(bool isRunning); + lv_obj_t* label_title; + lv_obj_t* btn_startStop; + lv_obj_t* label_startStop; + + bool isFindMyPhoneRunning; + }; + } + + template <> + struct AppTraits { + static constexpr Apps app = Apps::FindMyPhone; + static constexpr const char* icon = Screens::Symbols::magnifyingGlass ; + + static Screens::Screen* Create(AppControllers& controllers) { + return new Screens::FindMyPhone(controllers.systemTask->nimble().immediateAlertClient()); + }; + }; + } +} diff --git a/src/displayapp/screens/Symbols.h b/src/displayapp/screens/Symbols.h index 40699b3d65..66115ab92d 100644 --- a/src/displayapp/screens/Symbols.h +++ b/src/displayapp/screens/Symbols.h @@ -41,6 +41,7 @@ namespace Pinetime { static constexpr const char* sleep = "\xEE\xBD\x84"; static constexpr const char* calculator = "\xEF\x87\xAC"; static constexpr const char* backspace = "\xEF\x95\x9A"; + static constexpr const char* magnifyingGlass = "\xEF\x80\x82"; // f002 // fontawesome_weathericons.c // static constexpr const char* sun = "\xEF\x86\x85"; From 286c5a4776c48a385ea52341fd023defae6b08ea Mon Sep 17 00:00:00 2001 From: Jozef Mlich Date: Fri, 20 Oct 2023 16:13:40 +0200 Subject: [PATCH 02/21] update screen layout --- src/CMakeLists.txt | 3 + src/displayapp/screens/FindMyPhone.cpp | 84 +++++++++++++++++++------- src/displayapp/screens/FindMyPhone.h | 19 ++++-- 3 files changed, 79 insertions(+), 27 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9c34a8bf6b..fe806d27bb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -462,6 +462,7 @@ list(APPEND SOURCE_FILES components/ble/BatteryInformationService.cpp components/ble/FSService.cpp components/ble/ImmediateAlertService.cpp + components/ble/ImmediateAlertClient.cpp components/ble/ServiceDiscovery.cpp components/ble/HeartRateService.cpp components/ble/MotionService.cpp @@ -531,6 +532,7 @@ list(APPEND RECOVERY_SOURCE_FILES components/ble/BatteryInformationService.cpp components/ble/FSService.cpp components/ble/ImmediateAlertService.cpp + components/ble/ImmediateAlertClient.cpp components/ble/ServiceDiscovery.cpp components/ble/NavigationService.cpp components/ble/HeartRateService.cpp @@ -651,6 +653,7 @@ set(INCLUDE_FILES components/ble/BatteryInformationService.h components/ble/FSService.h components/ble/ImmediateAlertService.h + components/ble/ImmediateAlertClient.h components/ble/ServiceDiscovery.h components/ble/BleClient.h components/ble/HeartRateService.h diff --git a/src/displayapp/screens/FindMyPhone.cpp b/src/displayapp/screens/FindMyPhone.cpp index 540f151afe..0f7317f13c 100644 --- a/src/displayapp/screens/FindMyPhone.cpp +++ b/src/displayapp/screens/FindMyPhone.cpp @@ -7,49 +7,89 @@ using namespace Pinetime::Applications::Screens; namespace { - void btnStartStopEventHandler(lv_obj_t* obj, lv_event_t event) { + void btnImmediateAlertEventHandler(lv_obj_t* obj, lv_event_t event) { auto* screen = static_cast(obj->user_data); - screen->OnStartStopEvent(event); + screen->OnImmediateAlertEvent(obj, event); } } FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertService& immediateAlertService) : immediateAlertService {immediateAlertService} { - isFindMyPhoneRunning = false; + last_level = Pinetime::Controllers::ImmediateAlertService::Levels::NoAlert; + + 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); + lv_obj_set_style_local_pad_all(container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0); + lv_obj_set_style_local_pad_inner(container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0); + lv_obj_set_style_local_border_width(container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0); + label_title = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text_static(label_title, "Find my phone"); lv_obj_align(label_title, nullptr, LV_ALIGN_CENTER, 0, -40); - btn_startStop = lv_btn_create(lv_scr_act(), nullptr); - btn_startStop->user_data = this; - lv_obj_set_height(btn_startStop, 50); - lv_obj_set_event_cb(btn_startStop, btnStartStopEventHandler); - lv_obj_align(btn_startStop, label_title, LV_ALIGN_OUT_BOTTOM_MID, 0, 10); + bt_none = lv_btn_create(container, nullptr); + bt_none->user_data = this; + lv_obj_set_event_cb(bt_none, btnImmediateAlertEventHandler); + lv_obj_set_size(bt_none, 76, 76); + lv_obj_align(bt_none, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); + label_none = lv_label_create(bt_none, nullptr); + lv_label_set_text_static(label_none, "None"); + lv_obj_set_style_local_bg_color(bt_none, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); + + bt_mild = lv_btn_create(container, nullptr); + bt_mild->user_data = this; + lv_obj_set_event_cb(bt_mild, btnImmediateAlertEventHandler); + lv_obj_set_size(bt_mild, 76, 76); + lv_obj_align(bt_mild, nullptr, LV_ALIGN_IN_BOTTOM_MID, 0, 0); + label_mild = lv_label_create(bt_mild, nullptr); + lv_label_set_text_static(label_mild, "Mild"); + lv_obj_set_style_local_bg_color(bt_mild, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight); - label_startStop = lv_label_create(btn_startStop, nullptr); - UpdateStartStopButton(isFindMyPhoneRunning); + bt_high = lv_btn_create(container, nullptr); + bt_high->user_data = this; + lv_obj_set_event_cb(bt_high, btnImmediateAlertEventHandler); + lv_obj_set_size(bt_high, 76, 76); + lv_obj_align(bt_high, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); + label_high = lv_label_create(bt_high, nullptr); + lv_label_set_text_static(label_high, "High"); + lv_obj_set_style_local_bg_color(bt_high, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED ); + + UpdateImmediateAlerts(); } FindMyPhone::~FindMyPhone() { lv_obj_clean(lv_scr_act()); } -void FindMyPhone::OnStartStopEvent(lv_event_t event) { +void FindMyPhone::OnImmediateAlertEvent(lv_obj_t* obj, lv_event_t event) { if (event == LV_EVENT_CLICKED) { - isFindMyPhoneRunning = !isFindMyPhoneRunning; - UpdateStartStopButton(isFindMyPhoneRunning); + if (obj == bt_none) { + last_level = Pinetime::Controllers::ImmediateAlertService::Levels::NoAlert; + } else if (obj == bt_mild) { + last_level = Pinetime::Controllers::ImmediateAlertService::Levels::MildAlert; + } else if (obj == bt_high) { + last_level = Pinetime::Controllers::ImmediateAlertService::Levels::HighAlert; + } + UpdateImmediateAlerts(); } } -void FindMyPhone::UpdateStartStopButton(bool isRunning) { - if (isRunning) { - immediateAlertService.sendImmediateAlert(Pinetime::Controllers::ImmediateAlertService::Levels::HighAlert); - lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight); - lv_label_set_text_static(label_startStop, "Stop"); - } else { - lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); - lv_label_set_text_static(label_startStop, "Start"); - immediateAlertService.sendImmediateAlert(Pinetime::Controllers::ImmediateAlertService::Levels::NoAlert); +void FindMyPhone::UpdateImmediateAlerts() { + switch (last_level) { + case Pinetime::Controllers::ImmediateAlertService::Levels::NoAlert: + lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); + break; + case Pinetime::Controllers::ImmediateAlertService::Levels::MildAlert: + lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight); + break; + case Pinetime::Controllers::ImmediateAlertService::Levels::HighAlert: + lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); + break; } + immediateAlertService.sendImmediateAlert(last_level); + } + diff --git a/src/displayapp/screens/FindMyPhone.h b/src/displayapp/screens/FindMyPhone.h index ff99885ced..3549e60f32 100644 --- a/src/displayapp/screens/FindMyPhone.h +++ b/src/displayapp/screens/FindMyPhone.h @@ -5,6 +5,8 @@ #include "displayapp/screens/Screen.h" #include "Symbols.h" #include "systemtask/SystemTask.h" +#include "components/ble/ImmediateAlertService.h" + #include #include @@ -22,17 +24,24 @@ namespace Pinetime { FindMyPhone(Pinetime::Controllers::ImmediateAlertService& immediateAlertService); ~FindMyPhone() override; - void OnStartStopEvent(lv_event_t event); + void OnImmediateAlertEvent(lv_obj_t* obj, lv_event_t event); private: Pinetime::Controllers::ImmediateAlertService& immediateAlertService; - void UpdateStartStopButton(bool isRunning); + void UpdateImmediateAlerts(); + + lv_obj_t* container; lv_obj_t* label_title; - lv_obj_t* btn_startStop; - lv_obj_t* label_startStop; + lv_obj_t* bt_none; + lv_obj_t* bt_high; + lv_obj_t* bt_mild; + lv_obj_t* label_none; + lv_obj_t* label_high; + lv_obj_t* label_mild; + - bool isFindMyPhoneRunning; + Pinetime::Controllers::ImmediateAlertService::Levels last_level; }; } From 66c70780d502b3ce98ab14f07def88726cb56499 Mon Sep 17 00:00:00 2001 From: Jozef Mlich Date: Sat, 21 Oct 2023 14:29:48 +0200 Subject: [PATCH 03/21] Make it separate as Immediate Alert Client --- .gitignore | 1 + src/components/ble/ImmediateAlertClient.cpp | 113 +++++++++++++++++++ src/components/ble/ImmediateAlertClient.h | 61 ++++++++++ src/components/ble/ImmediateAlertService.cpp | 14 --- src/components/ble/ImmediateAlertService.h | 2 - src/components/ble/NimbleController.cpp | 6 +- src/components/ble/NimbleController.h | 8 +- src/displayapp/screens/FindMyPhone.cpp | 21 ++-- src/displayapp/screens/FindMyPhone.h | 12 +- 9 files changed, 200 insertions(+), 38 deletions(-) create mode 100644 src/components/ble/ImmediateAlertClient.cpp create mode 100644 src/components/ble/ImmediateAlertClient.h diff --git a/.gitignore b/.gitignore index d6f917cfc4..337b85d0f5 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ cmake-build-* cmake-*/ CMakeFiles **/CMakeCache.txt +CMakeLists.txt.user* cmake_install.cmake Makefile build diff --git a/src/components/ble/ImmediateAlertClient.cpp b/src/components/ble/ImmediateAlertClient.cpp new file mode 100644 index 0000000000..de25b8853f --- /dev/null +++ b/src/components/ble/ImmediateAlertClient.cpp @@ -0,0 +1,113 @@ +#include "components/ble/ImmediateAlertClient.h" +#include +#include +#include "systemtask/SystemTask.h" + +using namespace Pinetime::Controllers; + +constexpr ble_uuid16_t ImmediateAlertClient::immediateAlertClientUuid; +constexpr ble_uuid16_t ImmediateAlertClient::alertLevelCharacteristicUuid; + +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(arg); + return client->OnDiscoveryEvent(conn_handle, error, service); + } + + int OnImmediateAlertCharacteristicDiscoveredCallback(uint16_t conn_handle, + const struct ble_gatt_error* error, + const struct ble_gatt_chr* chr, + void* arg) { + auto client = static_cast(arg); + return client->OnCharacteristicDiscoveryEvent(conn_handle, error, chr); + } +} + +ImmediateAlertClient::ImmediateAlertClient(Pinetime::System::SystemTask& systemTask) + : systemTask {systemTask}, + characteristicDefinition {{ + .uuid = &alertLevelCharacteristicUuid.u, + .arg = this, + .flags = BLE_GATT_CHR_F_WRITE_NO_RSP, + }, + {0}}, + serviceDefinition { + {/* Device Information Service */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = &immediateAlertClientUuid.u, + .characteristics = characteristicDefinition}, + {0}, + } { +} + +void ImmediateAlertClient::Init() { +} + +bool ImmediateAlertClient::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("IAS found, starting characteristics discovery"); + + ble_gattc_disc_all_chrs(connectionHandle, iasStartHandle, iasEndHandle, OnImmediateAlertCharacteristicDiscoveredCallback, this); + } else { + NRF_LOG_INFO("IAS not found"); + onServiceDiscovered(connectionHandle); + } + return true; + } + + if (service != nullptr && ble_uuid_cmp(&immediateAlertClientUuid.u, &service->uuid.u) == 0) { + NRF_LOG_INFO("IAS discovered : 0x%x - 0x%x", service->start_handle, service->end_handle); + isDiscovered = true; + iasStartHandle = service->start_handle; + iasEndHandle = service->end_handle; + } + return false; +} + +int ImmediateAlertClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, + const ble_gatt_error* error, + const ble_gatt_chr* characteristic) { + + if (error->status != 0 && error->status != BLE_HS_EDONE) { + NRF_LOG_INFO("IAS Characteristic discovery ERROR"); + onServiceDiscovered(conn_handle); + return 0; + } + + if (characteristic == nullptr && error->status == BLE_HS_EDONE) { + if (!isCharacteristicDiscovered) { + NRF_LOG_INFO("IAS Characteristic discovery unsuccessful"); + onServiceDiscovered(conn_handle); + } + + return 0; + } + + if (characteristic != nullptr && ble_uuid_cmp(&alertLevelCharacteristicUuid.u, &characteristic->uuid.u) == 0) { + NRF_LOG_INFO("AIS Characteristic discovered : 0x%x", characteristic->val_handle); + isCharacteristicDiscovered = true; + alertLevelHandle = characteristic->val_handle; + } + return 0; +} + +void ImmediateAlertClient::Discover(uint16_t connectionHandle, std::function onServiceDiscovered) { + NRF_LOG_INFO("[IAS] Starting discovery"); + this->onServiceDiscovered = onServiceDiscovered; + ble_gattc_disc_svc_by_uuid(connectionHandle, &immediateAlertClientUuid.u, OnDiscoveryEventCallback, this); +} + +void ImmediateAlertClient::sendImmediateAlert(ImmediateAlertClient::Levels level) { + + auto* om = ble_hs_mbuf_from_flat(&level, 1); + + uint16_t connectionHandle = systemTask.nimble().connHandle(); + + if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) { + return; + } + + ble_gattc_notify_custom(connectionHandle, alertLevelHandle, om); + +} diff --git a/src/components/ble/ImmediateAlertClient.h b/src/components/ble/ImmediateAlertClient.h new file mode 100644 index 0000000000..9e2cc021ea --- /dev/null +++ b/src/components/ble/ImmediateAlertClient.h @@ -0,0 +1,61 @@ +#pragma once +#define min // workaround: nimble's min/max macros conflict with libstdc++ +#define max +#include +#undef max +#undef min +#include +#include "components/ble/BleClient.h" + +namespace Pinetime { + namespace System { + class SystemTask; + } + + namespace Controllers { + class NotificationManager; + + class ImmediateAlertClient : public BleClient { + public: + enum class Levels : uint8_t { NoAlert = 0, MildAlert = 1, HighAlert = 2 }; + + ImmediateAlertClient(Pinetime::System::SystemTask& systemTask); + void Init(); + + bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service); + int OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error* error, const ble_gatt_chr* characteristic); + + void sendImmediateAlert(Levels level); + + static constexpr const ble_uuid16_t* Uuid() { + return &ImmediateAlertClient::immediateAlertClientUuid; + } + + static constexpr const ble_uuid16_t* AlertLevelCharacteristicUuid() { + return &ImmediateAlertClient::alertLevelCharacteristicUuid; + } + + void Discover(uint16_t connectionHandle, std::function lambda) override; + + private: + Pinetime::System::SystemTask& systemTask; + + static constexpr uint16_t immediateAlertClientId {0x1802}; + static constexpr uint16_t alertLevelId {0x2A06}; + + static constexpr ble_uuid16_t immediateAlertClientUuid {.u {.type = BLE_UUID_TYPE_16}, .value = immediateAlertClientId}; + static constexpr ble_uuid16_t alertLevelCharacteristicUuid {.u {.type = BLE_UUID_TYPE_16}, .value = alertLevelId}; + + bool isDiscovered = false; + uint16_t iasStartHandle; + uint16_t iasEndHandle; + bool isCharacteristicDiscovered = false; + + struct ble_gatt_chr_def characteristicDefinition[3]; + struct ble_gatt_svc_def serviceDefinition[2]; + + uint16_t alertLevelHandle; + std::function onServiceDiscovered; + }; + } +} diff --git a/src/components/ble/ImmediateAlertService.cpp b/src/components/ble/ImmediateAlertService.cpp index b87c2adae8..b9de615aa0 100644 --- a/src/components/ble/ImmediateAlertService.cpp +++ b/src/components/ble/ImmediateAlertService.cpp @@ -73,17 +73,3 @@ int ImmediateAlertService::OnAlertLevelChanged(uint16_t attributeHandle, ble_gat return 0; } - -void ImmediateAlertService::sendImmediateAlert(ImmediateAlertService::Levels level) { - - auto* om = ble_hs_mbuf_from_flat(&level, 1); - - uint16_t connectionHandle = systemTask.nimble().connHandle(); - - if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) { - return; - } - - ble_gattc_notify_custom(connectionHandle, alertLevelHandle, om); - -} diff --git a/src/components/ble/ImmediateAlertService.h b/src/components/ble/ImmediateAlertService.h index 34483c6e3e..47a0543935 100644 --- a/src/components/ble/ImmediateAlertService.h +++ b/src/components/ble/ImmediateAlertService.h @@ -21,8 +21,6 @@ namespace Pinetime { void Init(); int OnAlertLevelChanged(uint16_t attributeHandle, ble_gatt_access_ctxt* context); - void sendImmediateAlert(Levels level); - private: Pinetime::System::SystemTask& systemTask; NotificationManager& notificationManager; diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index bca10b7d2c..7f105c304f 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -45,7 +45,8 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask, musicService {*this}, weatherService {dateTimeController}, batteryInformationService {batteryController}, - iaService {systemTask, notificationManager}, + immediateAlertService {systemTask, notificationManager}, + iaClient {systemTask}, heartRateService {*this, heartRateController}, motionService {*this, motionController}, fsService {systemTask, fs}, @@ -94,7 +95,8 @@ void NimbleController::Init() { anService.Init(); dfuService.Init(); batteryInformationService.Init(); - iaService.Init(); + immediateAlertService.Init(); + iaClient.Init(); heartRateService.Init(); motionService.Init(); fsService.Init(); diff --git a/src/components/ble/NimbleController.h b/src/components/ble/NimbleController.h index f04136d0d9..46ec0dcffe 100644 --- a/src/components/ble/NimbleController.h +++ b/src/components/ble/NimbleController.h @@ -17,6 +17,7 @@ #include "components/ble/FSService.h" #include "components/ble/HeartRateService.h" #include "components/ble/ImmediateAlertService.h" +#include "components/ble/ImmediateAlertClient.h" #include "components/ble/MusicService.h" #include "components/ble/NavigationService.h" #include "components/ble/ServiceDiscovery.h" @@ -71,8 +72,8 @@ namespace Pinetime { return weatherService; }; - Pinetime::Controllers::ImmediateAlertService& immediateAlertService() { - return iaService; + Pinetime::Controllers::ImmediateAlertClient& immediateAlertClient() { + return iaClient; } uint16_t connHandle(); @@ -106,7 +107,8 @@ namespace Pinetime { SimpleWeatherService weatherService; NavigationService navService; BatteryInformationService batteryInformationService; - ImmediateAlertService iaService; + ImmediateAlertService immediateAlertService; + ImmediateAlertClient iaClient; HeartRateService heartRateService; MotionService motionService; FSService fsService; diff --git a/src/displayapp/screens/FindMyPhone.cpp b/src/displayapp/screens/FindMyPhone.cpp index 0f7317f13c..d5fcdc0316 100644 --- a/src/displayapp/screens/FindMyPhone.cpp +++ b/src/displayapp/screens/FindMyPhone.cpp @@ -13,9 +13,8 @@ namespace { } } -FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertService& immediateAlertService) - : immediateAlertService {immediateAlertService} { - last_level = Pinetime::Controllers::ImmediateAlertService::Levels::NoAlert; +FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertClient& immediateAlertClient) : immediateAlertClient {immediateAlertClient} { + last_level = Pinetime::Controllers::ImmediateAlertClient::Levels::NoAlert; container = lv_cont_create(lv_scr_act(), nullptr); @@ -55,7 +54,7 @@ FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertService& immediate lv_obj_align(bt_high, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); label_high = lv_label_create(bt_high, nullptr); lv_label_set_text_static(label_high, "High"); - lv_obj_set_style_local_bg_color(bt_high, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED ); + lv_obj_set_style_local_bg_color(bt_high, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); UpdateImmediateAlerts(); } @@ -67,11 +66,11 @@ FindMyPhone::~FindMyPhone() { void FindMyPhone::OnImmediateAlertEvent(lv_obj_t* obj, lv_event_t event) { if (event == LV_EVENT_CLICKED) { if (obj == bt_none) { - last_level = Pinetime::Controllers::ImmediateAlertService::Levels::NoAlert; + last_level = Pinetime::Controllers::ImmediateAlertClient::Levels::NoAlert; } else if (obj == bt_mild) { - last_level = Pinetime::Controllers::ImmediateAlertService::Levels::MildAlert; + last_level = Pinetime::Controllers::ImmediateAlertClient::Levels::MildAlert; } else if (obj == bt_high) { - last_level = Pinetime::Controllers::ImmediateAlertService::Levels::HighAlert; + last_level = Pinetime::Controllers::ImmediateAlertClient::Levels::HighAlert; } UpdateImmediateAlerts(); } @@ -79,17 +78,17 @@ void FindMyPhone::OnImmediateAlertEvent(lv_obj_t* obj, lv_event_t event) { void FindMyPhone::UpdateImmediateAlerts() { switch (last_level) { - case Pinetime::Controllers::ImmediateAlertService::Levels::NoAlert: + case Pinetime::Controllers::ImmediateAlertClient::Levels::NoAlert: lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); break; - case Pinetime::Controllers::ImmediateAlertService::Levels::MildAlert: + case Pinetime::Controllers::ImmediateAlertClient::Levels::MildAlert: lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight); break; - case Pinetime::Controllers::ImmediateAlertService::Levels::HighAlert: + case Pinetime::Controllers::ImmediateAlertClient::Levels::HighAlert: lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); break; } - immediateAlertService.sendImmediateAlert(last_level); + immediateAlertClient.sendImmediateAlert(last_level); } diff --git a/src/displayapp/screens/FindMyPhone.h b/src/displayapp/screens/FindMyPhone.h index 3549e60f32..82b2f3b5f0 100644 --- a/src/displayapp/screens/FindMyPhone.h +++ b/src/displayapp/screens/FindMyPhone.h @@ -5,7 +5,7 @@ #include "displayapp/screens/Screen.h" #include "Symbols.h" #include "systemtask/SystemTask.h" -#include "components/ble/ImmediateAlertService.h" +#include "components/ble/ImmediateAlertClient.h" #include #include @@ -13,7 +13,7 @@ namespace Pinetime { namespace Controllers { - class ImmediateAlertService; + class ImmediateAlertClient; } namespace Applications { @@ -21,13 +21,13 @@ namespace Pinetime { class FindMyPhone : public Screen { public: - FindMyPhone(Pinetime::Controllers::ImmediateAlertService& immediateAlertService); + explicit FindMyPhone(Pinetime::Controllers::ImmediateAlertClient& immediateAlertClient); ~FindMyPhone() override; void OnImmediateAlertEvent(lv_obj_t* obj, lv_event_t event); private: - Pinetime::Controllers::ImmediateAlertService& immediateAlertService; + Pinetime::Controllers::ImmediateAlertClient& immediateAlertClient; void UpdateImmediateAlerts(); @@ -41,14 +41,14 @@ namespace Pinetime { lv_obj_t* label_mild; - Pinetime::Controllers::ImmediateAlertService::Levels last_level; + Pinetime::Controllers::ImmediateAlertClient::Levels last_level; }; } template <> struct AppTraits { static constexpr Apps app = Apps::FindMyPhone; - static constexpr const char* icon = Screens::Symbols::magnifyingGlass ; + static constexpr const char* icon = Screens::Symbols::magnifyingGlass; static Screens::Screen* Create(AppControllers& controllers) { return new Screens::FindMyPhone(controllers.systemTask->nimble().immediateAlertClient()); From 69381ca388ee5a8414fa086d63126008c480b5a0 Mon Sep 17 00:00:00 2001 From: Jozef Mlich Date: Sat, 21 Oct 2023 17:56:49 +0200 Subject: [PATCH 04/21] enable discovery --- src/components/ble/NimbleController.cpp | 2 +- src/components/ble/ServiceDiscovery.cpp | 4 ++-- src/components/ble/ServiceDiscovery.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index 7f105c304f..fb01931b4d 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -50,7 +50,7 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask, heartRateService {*this, heartRateController}, motionService {*this, motionController}, fsService {systemTask, fs}, - serviceDiscovery({¤tTimeClient, &alertNotificationClient}) { + serviceDiscovery({¤tTimeClient, &alertNotificationClient, &iaClient}) { } void nimble_on_reset(int reason) { diff --git a/src/components/ble/ServiceDiscovery.cpp b/src/components/ble/ServiceDiscovery.cpp index 03bcfeb47b..554cf3e7b2 100644 --- a/src/components/ble/ServiceDiscovery.cpp +++ b/src/components/ble/ServiceDiscovery.cpp @@ -4,7 +4,7 @@ using namespace Pinetime::Controllers; -ServiceDiscovery::ServiceDiscovery(std::array&& clients) : clients {clients} { +ServiceDiscovery::ServiceDiscovery(std::array&& clients) : clients {clients} { } void ServiceDiscovery::StartDiscovery(uint16_t connectionHandle) { @@ -29,4 +29,4 @@ void ServiceDiscovery::DiscoverNextService(uint16_t connectionHandle) { this->OnServiceDiscovered(connectionHandle); }; (*clientIterator)->Discover(connectionHandle, discoverNextService); -} \ No newline at end of file +} diff --git a/src/components/ble/ServiceDiscovery.h b/src/components/ble/ServiceDiscovery.h index fc3b38c0a6..039862a442 100644 --- a/src/components/ble/ServiceDiscovery.h +++ b/src/components/ble/ServiceDiscovery.h @@ -9,13 +9,13 @@ namespace Pinetime { class ServiceDiscovery { public: - ServiceDiscovery(std::array&& bleClients); + ServiceDiscovery(std::array&& bleClients); void StartDiscovery(uint16_t connectionHandle); private: BleClient** clientIterator; - std::array clients; + std::array clients; void OnServiceDiscovered(uint16_t connectionHandle); void DiscoverNextService(uint16_t connectionHandle); }; From 29f91d9e139df7d0381ab76eaefc2a18e955ebbf Mon Sep 17 00:00:00 2001 From: Vyacheslav Chigrin Date: Wed, 24 Apr 2024 00:08:57 +0300 Subject: [PATCH 05/21] Fix delivering notification level change in ImmediateAlertClient. --- src/components/ble/ImmediateAlertClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ble/ImmediateAlertClient.cpp b/src/components/ble/ImmediateAlertClient.cpp index de25b8853f..3b266d540f 100644 --- a/src/components/ble/ImmediateAlertClient.cpp +++ b/src/components/ble/ImmediateAlertClient.cpp @@ -108,6 +108,6 @@ void ImmediateAlertClient::sendImmediateAlert(ImmediateAlertClient::Levels level return; } - ble_gattc_notify_custom(connectionHandle, alertLevelHandle, om); + ble_gattc_write_no_rsp(connectionHandle, alertLevelHandle, om); } From 080f1fb8f9b05c6e44024f8de879ba171b19844f Mon Sep 17 00:00:00 2001 From: Vyacheslav Chigrin Date: Wed, 24 Apr 2024 00:34:57 +0300 Subject: [PATCH 06/21] Restore FindMyPhone UI after task sent. --- src/components/ble/ImmediateAlertClient.cpp | 6 ++-- src/components/ble/ImmediateAlertClient.h | 2 +- src/displayapp/screens/FindMyPhone.cpp | 40 ++++++++++++++++++--- src/displayapp/screens/FindMyPhone.h | 6 +++- 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/components/ble/ImmediateAlertClient.cpp b/src/components/ble/ImmediateAlertClient.cpp index 3b266d540f..9190f46031 100644 --- a/src/components/ble/ImmediateAlertClient.cpp +++ b/src/components/ble/ImmediateAlertClient.cpp @@ -98,16 +98,16 @@ void ImmediateAlertClient::Discover(uint16_t connectionHandle, std::function(obj->user_data); screen->OnImmediateAlertEvent(obj, event); } + + void RestoreLabelTaskCallback(lv_task_t* task) { + auto* screen = static_cast(task->user_data); + screen->RestoreLabelText(); + screen->StopRestoreLabelTask(); + } } FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertClient& immediateAlertClient) : immediateAlertClient {immediateAlertClient} { @@ -26,7 +37,8 @@ FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertClient& immediateA label_title = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text_static(label_title, "Find my phone"); + lv_label_set_text_static(label_title, defaultLabelText); + lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); lv_obj_align(label_title, nullptr, LV_ALIGN_CENTER, 0, -40); bt_none = lv_btn_create(container, nullptr); @@ -55,8 +67,6 @@ FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertClient& immediateA label_high = lv_label_create(bt_high, nullptr); lv_label_set_text_static(label_high, "High"); lv_obj_set_style_local_bg_color(bt_high, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); - - UpdateImmediateAlerts(); } FindMyPhone::~FindMyPhone() { @@ -88,7 +98,29 @@ void FindMyPhone::UpdateImmediateAlerts() { lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); break; } - immediateAlertClient.sendImmediateAlert(last_level); + if (immediateAlertClient.sendImmediateAlert(last_level)) { + lv_label_set_text_static(label_title, alertSentLabelText); + } else { + lv_label_set_text_static(label_title, noConnectionLabelText); + } + ScheduleRestoreLabelTask(); +} +void FindMyPhone::ScheduleRestoreLabelTask() { + if (taskRestoreLabelText) { + return; + } + taskRestoreLabelText = lv_task_create(RestoreLabelTaskCallback, restoreLabelTimeoutTicks, LV_TASK_PRIO_MID, this); } +void FindMyPhone::StopRestoreLabelTask() { + if (taskRestoreLabelText) { + lv_task_del(taskRestoreLabelText); + taskRestoreLabelText = nullptr; + } +} + +void FindMyPhone::RestoreLabelText() { + lv_label_set_text_static(label_title, defaultLabelText); + lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); +} diff --git a/src/displayapp/screens/FindMyPhone.h b/src/displayapp/screens/FindMyPhone.h index 82b2f3b5f0..a96bfc650e 100644 --- a/src/displayapp/screens/FindMyPhone.h +++ b/src/displayapp/screens/FindMyPhone.h @@ -26,6 +26,10 @@ namespace Pinetime { void OnImmediateAlertEvent(lv_obj_t* obj, lv_event_t event); + void ScheduleRestoreLabelTask(); + void StopRestoreLabelTask(); + void RestoreLabelText(); + private: Pinetime::Controllers::ImmediateAlertClient& immediateAlertClient; @@ -39,7 +43,7 @@ namespace Pinetime { lv_obj_t* label_none; lv_obj_t* label_high; lv_obj_t* label_mild; - + lv_task_t* taskRestoreLabelText = nullptr; Pinetime::Controllers::ImmediateAlertClient::Levels last_level; }; From d6c94cf2c03da3dabde9df9f92035b312217e21e Mon Sep 17 00:00:00 2001 From: Vyacheslav Chigrin Date: Wed, 1 May 2024 16:59:16 +0300 Subject: [PATCH 07/21] Update documentation. --- doc/ble.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/ble.md b/doc/ble.md index 2a4ecf4cb8..8918aadd43 100644 --- a/doc/ble.md +++ b/doc/ble.md @@ -14,6 +14,8 @@ This page describes the BLE implementation and API built in this firmware. - [BLE Services](#ble-services) - [CTS](#cts) - [ANS](#ans) +- [BLE Clients](#ble-clients) + - [IAC](#iac) - [Getting Information](#getting-information) - [Firmware Version](#firmware-version) - [Battery Level](#battery-level) @@ -113,6 +115,16 @@ The following custom services are implemented in InfiniTime: ![ANS sequence diagram](./ble/ans_sequence.png "ANS sequence diagram") +## BLE clients + +### IAC + +InfiniTime implements Immediade Alert Service client, that can be used to send notifications to companion app. +This is useful for "Find my Phone" functionality. + +More documentation about this service can be found here. + +[Immediade Alert Service](https://www.bluetooth.com/specifications/specs/immediate-alert-service-1-0/) --- ### Getting Information From b4c1a909d9b7065cf3a3bb6f9752f780f8316e5c Mon Sep 17 00:00:00 2001 From: Vyacheslav Chigrin Date: Thu, 28 Nov 2024 00:42:03 +0300 Subject: [PATCH 08/21] Update UI. Remove "Mild" level button. --- src/displayapp/screens/FindMyPhone.cpp | 28 +++++++++----------------- src/displayapp/screens/FindMyPhone.h | 2 -- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/displayapp/screens/FindMyPhone.cpp b/src/displayapp/screens/FindMyPhone.cpp index 943522a138..4e04aac052 100644 --- a/src/displayapp/screens/FindMyPhone.cpp +++ b/src/displayapp/screens/FindMyPhone.cpp @@ -10,6 +10,8 @@ namespace { static constexpr char defaultLabelText[] = "Find my phone"; static constexpr char alertSentLabelText[] = "Alert sent"; static constexpr char noConnectionLabelText[] = "No connection"; + static constexpr char noneLabelText[] = "Stop"; + static constexpr char highLabelText[] = "Ring"; static constexpr auto restoreLabelTimeoutTicks = pdMS_TO_TICKS(2 * 1000); void btnImmediateAlertEventHandler(lv_obj_t* obj, lv_event_t event) { @@ -44,28 +46,19 @@ FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertClient& immediateA bt_none = lv_btn_create(container, nullptr); bt_none->user_data = this; lv_obj_set_event_cb(bt_none, btnImmediateAlertEventHandler); - lv_obj_set_size(bt_none, 76, 76); + lv_obj_set_size(bt_none, 114, 76); lv_obj_align(bt_none, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); label_none = lv_label_create(bt_none, nullptr); - lv_label_set_text_static(label_none, "None"); + lv_label_set_text_static(label_none, noneLabelText); lv_obj_set_style_local_bg_color(bt_none, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); - bt_mild = lv_btn_create(container, nullptr); - bt_mild->user_data = this; - lv_obj_set_event_cb(bt_mild, btnImmediateAlertEventHandler); - lv_obj_set_size(bt_mild, 76, 76); - lv_obj_align(bt_mild, nullptr, LV_ALIGN_IN_BOTTOM_MID, 0, 0); - label_mild = lv_label_create(bt_mild, nullptr); - lv_label_set_text_static(label_mild, "Mild"); - lv_obj_set_style_local_bg_color(bt_mild, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight); - bt_high = lv_btn_create(container, nullptr); bt_high->user_data = this; lv_obj_set_event_cb(bt_high, btnImmediateAlertEventHandler); - lv_obj_set_size(bt_high, 76, 76); + lv_obj_set_size(bt_high, 114, 76); lv_obj_align(bt_high, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); label_high = lv_label_create(bt_high, nullptr); - lv_label_set_text_static(label_high, "High"); + lv_label_set_text_static(label_high, highLabelText); lv_obj_set_style_local_bg_color(bt_high, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); } @@ -77,8 +70,6 @@ void FindMyPhone::OnImmediateAlertEvent(lv_obj_t* obj, lv_event_t event) { if (event == LV_EVENT_CLICKED) { if (obj == bt_none) { last_level = Pinetime::Controllers::ImmediateAlertClient::Levels::NoAlert; - } else if (obj == bt_mild) { - last_level = Pinetime::Controllers::ImmediateAlertClient::Levels::MildAlert; } else if (obj == bt_high) { last_level = Pinetime::Controllers::ImmediateAlertClient::Levels::HighAlert; } @@ -91,12 +82,13 @@ void FindMyPhone::UpdateImmediateAlerts() { case Pinetime::Controllers::ImmediateAlertClient::Levels::NoAlert: lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); break; - case Pinetime::Controllers::ImmediateAlertClient::Levels::MildAlert: - lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight); - break; case Pinetime::Controllers::ImmediateAlertClient::Levels::HighAlert: lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); break; + case Pinetime::Controllers::ImmediateAlertClient::Levels::MildAlert: + // Not supported. + ASSERT(false); + break; } if (immediateAlertClient.sendImmediateAlert(last_level)) { lv_label_set_text_static(label_title, alertSentLabelText); diff --git a/src/displayapp/screens/FindMyPhone.h b/src/displayapp/screens/FindMyPhone.h index a96bfc650e..b9b79ed29a 100644 --- a/src/displayapp/screens/FindMyPhone.h +++ b/src/displayapp/screens/FindMyPhone.h @@ -39,10 +39,8 @@ namespace Pinetime { lv_obj_t* label_title; lv_obj_t* bt_none; lv_obj_t* bt_high; - lv_obj_t* bt_mild; lv_obj_t* label_none; lv_obj_t* label_high; - lv_obj_t* label_mild; lv_task_t* taskRestoreLabelText = nullptr; Pinetime::Controllers::ImmediateAlertClient::Levels last_level; From 6eb25b3640c93c36dfad6866450a62d4dec0079c Mon Sep 17 00:00:00 2001 From: Vyacheslav Chigrin Date: Thu, 5 Dec 2024 00:01:26 +0300 Subject: [PATCH 09/21] Fix spelling in documentation. Co-authored-by: mark9064 <30447455+mark9064@users.noreply.github.com> --- doc/ble.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ble.md b/doc/ble.md index 8918aadd43..da2c1ec527 100644 --- a/doc/ble.md +++ b/doc/ble.md @@ -119,12 +119,12 @@ The following custom services are implemented in InfiniTime: ### IAC -InfiniTime implements Immediade Alert Service client, that can be used to send notifications to companion app. +InfiniTime implements an Immediate Alert Service client, which can be used to send alerts to the companion app. This is useful for "Find my Phone" functionality. More documentation about this service can be found here. -[Immediade Alert Service](https://www.bluetooth.com/specifications/specs/immediate-alert-service-1-0/) +[Immediate Alert Service](https://www.bluetooth.com/specifications/specs/immediate-alert-service-1-0/) --- ### Getting Information From e6c72779f68064a30df619a1882aa1f63c184a82 Mon Sep 17 00:00:00 2001 From: Vyacheslav Chigrin Date: Thu, 5 Dec 2024 00:15:43 +0300 Subject: [PATCH 10/21] Fix few style issues. --- src/components/ble/ImmediateAlertClient.cpp | 2 +- src/components/ble/ImmediateAlertClient.h | 2 +- src/displayapp/screens/FindMyPhone.cpp | 72 ++++++++++----------- src/displayapp/screens/FindMyPhone.h | 12 ++-- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/components/ble/ImmediateAlertClient.cpp b/src/components/ble/ImmediateAlertClient.cpp index 9190f46031..803855a82f 100644 --- a/src/components/ble/ImmediateAlertClient.cpp +++ b/src/components/ble/ImmediateAlertClient.cpp @@ -98,7 +98,7 @@ void ImmediateAlertClient::Discover(uint16_t connectionHandle, std::functionuser_data = this; - lv_obj_set_event_cb(bt_none, btnImmediateAlertEventHandler); - lv_obj_set_size(bt_none, 114, 76); - lv_obj_align(bt_none, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); - label_none = lv_label_create(bt_none, nullptr); - lv_label_set_text_static(label_none, noneLabelText); - lv_obj_set_style_local_bg_color(bt_none, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); - - bt_high = lv_btn_create(container, nullptr); - bt_high->user_data = this; - lv_obj_set_event_cb(bt_high, btnImmediateAlertEventHandler); - lv_obj_set_size(bt_high, 114, 76); - lv_obj_align(bt_high, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); - label_high = lv_label_create(bt_high, nullptr); - lv_label_set_text_static(label_high, highLabelText); - lv_obj_set_style_local_bg_color(bt_high, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); + lblTitle = lv_label_create(lv_scr_act(), nullptr); + + lv_label_set_text_static(lblTitle, defaultLabelText); + lv_obj_set_style_local_text_color(lblTitle, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); + lv_obj_align(lblTitle, nullptr, LV_ALIGN_CENTER, 0, -40); + + btnNone = lv_btn_create(container, nullptr); + btnNone->user_data = this; + lv_obj_set_event_cb(btnNone, btnImmediateAlertEventHandler); + lv_obj_set_size(btnNone, 114, 76); + lv_obj_align(btnNone, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); + lblNone = lv_label_create(btnNone, nullptr); + lv_label_set_text_static(lblNone, noneLabelText); + lv_obj_set_style_local_bg_color(btnNone, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); + + btnHigh = lv_btn_create(container, nullptr); + btnHigh->user_data = this; + lv_obj_set_event_cb(btnHigh, btnImmediateAlertEventHandler); + lv_obj_set_size(btnHigh, 114, 76); + lv_obj_align(btnHigh, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); + lblHigh = lv_label_create(btnHigh, nullptr); + lv_label_set_text_static(lblHigh, highLabelText); + lv_obj_set_style_local_bg_color(btnHigh, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); } FindMyPhone::~FindMyPhone() { @@ -68,32 +68,32 @@ FindMyPhone::~FindMyPhone() { void FindMyPhone::OnImmediateAlertEvent(lv_obj_t* obj, lv_event_t event) { if (event == LV_EVENT_CLICKED) { - if (obj == bt_none) { - last_level = Pinetime::Controllers::ImmediateAlertClient::Levels::NoAlert; - } else if (obj == bt_high) { - last_level = Pinetime::Controllers::ImmediateAlertClient::Levels::HighAlert; + if (obj == btnNone) { + lastLevel = Pinetime::Controllers::ImmediateAlertClient::Levels::NoAlert; + } else if (obj == btnHigh) { + lastLevel = Pinetime::Controllers::ImmediateAlertClient::Levels::HighAlert; } UpdateImmediateAlerts(); } } void FindMyPhone::UpdateImmediateAlerts() { - switch (last_level) { + switch (lastLevel) { case Pinetime::Controllers::ImmediateAlertClient::Levels::NoAlert: - lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); + lv_obj_set_style_local_text_color(lblTitle, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); break; case Pinetime::Controllers::ImmediateAlertClient::Levels::HighAlert: - lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); + lv_obj_set_style_local_text_color(lblTitle, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); break; case Pinetime::Controllers::ImmediateAlertClient::Levels::MildAlert: // Not supported. ASSERT(false); break; } - if (immediateAlertClient.sendImmediateAlert(last_level)) { - lv_label_set_text_static(label_title, alertSentLabelText); + if (immediateAlertClient.SendImmediateAlert(lastLevel)) { + lv_label_set_text_static(lblTitle, alertSentLabelText); } else { - lv_label_set_text_static(label_title, noConnectionLabelText); + lv_label_set_text_static(lblTitle, noConnectionLabelText); } ScheduleRestoreLabelTask(); } @@ -113,6 +113,6 @@ void FindMyPhone::StopRestoreLabelTask() { } void FindMyPhone::RestoreLabelText() { - lv_label_set_text_static(label_title, defaultLabelText); - lv_obj_set_style_local_text_color(label_title, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); + lv_label_set_text_static(lblTitle, defaultLabelText); + lv_obj_set_style_local_text_color(lblTitle, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); } diff --git a/src/displayapp/screens/FindMyPhone.h b/src/displayapp/screens/FindMyPhone.h index b9b79ed29a..75d130b284 100644 --- a/src/displayapp/screens/FindMyPhone.h +++ b/src/displayapp/screens/FindMyPhone.h @@ -36,14 +36,14 @@ namespace Pinetime { void UpdateImmediateAlerts(); lv_obj_t* container; - lv_obj_t* label_title; - lv_obj_t* bt_none; - lv_obj_t* bt_high; - lv_obj_t* label_none; - lv_obj_t* label_high; + lv_obj_t* lblTitle; + lv_obj_t* btnNone; + lv_obj_t* btnHigh; + lv_obj_t* lblNone; + lv_obj_t* lblHigh; lv_task_t* taskRestoreLabelText = nullptr; - Pinetime::Controllers::ImmediateAlertClient::Levels last_level; + Pinetime::Controllers::ImmediateAlertClient::Levels lastLevel; }; } From 4f7d0c478cf8f253c5ed572a23566b76ed83c859 Mon Sep 17 00:00:00 2001 From: Vyacheslav Chigrin Date: Thu, 5 Dec 2024 00:31:29 +0300 Subject: [PATCH 11/21] Small cosmetic changes. --- src/components/ble/ImmediateAlertClient.cpp | 16 +--------------- src/components/ble/ImmediateAlertClient.h | 3 --- src/displayapp/screens/FindMyPhone.cpp | 3 ++- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/components/ble/ImmediateAlertClient.cpp b/src/components/ble/ImmediateAlertClient.cpp index 803855a82f..dcd1833785 100644 --- a/src/components/ble/ImmediateAlertClient.cpp +++ b/src/components/ble/ImmediateAlertClient.cpp @@ -23,21 +23,7 @@ namespace { } } -ImmediateAlertClient::ImmediateAlertClient(Pinetime::System::SystemTask& systemTask) - : systemTask {systemTask}, - characteristicDefinition {{ - .uuid = &alertLevelCharacteristicUuid.u, - .arg = this, - .flags = BLE_GATT_CHR_F_WRITE_NO_RSP, - }, - {0}}, - serviceDefinition { - {/* Device Information Service */ - .type = BLE_GATT_SVC_TYPE_PRIMARY, - .uuid = &immediateAlertClientUuid.u, - .characteristics = characteristicDefinition}, - {0}, - } { +ImmediateAlertClient::ImmediateAlertClient(Pinetime::System::SystemTask& systemTask) : systemTask {systemTask} { } void ImmediateAlertClient::Init() { diff --git a/src/components/ble/ImmediateAlertClient.h b/src/components/ble/ImmediateAlertClient.h index 70b637194f..b3f944b32e 100644 --- a/src/components/ble/ImmediateAlertClient.h +++ b/src/components/ble/ImmediateAlertClient.h @@ -51,9 +51,6 @@ namespace Pinetime { uint16_t iasEndHandle; bool isCharacteristicDiscovered = false; - struct ble_gatt_chr_def characteristicDefinition[3]; - struct ble_gatt_svc_def serviceDefinition[2]; - uint16_t alertLevelHandle; std::function onServiceDiscovered; }; diff --git a/src/displayapp/screens/FindMyPhone.cpp b/src/displayapp/screens/FindMyPhone.cpp index 506576d6d0..7b4c66aa3d 100644 --- a/src/displayapp/screens/FindMyPhone.cpp +++ b/src/displayapp/screens/FindMyPhone.cpp @@ -12,7 +12,8 @@ namespace { static constexpr char noConnectionLabelText[] = "No connection"; static constexpr char noneLabelText[] = "Stop"; static constexpr char highLabelText[] = "Ring"; - static constexpr auto restoreLabelTimeoutTicks = pdMS_TO_TICKS(2 * 1000); + static constexpr int restoreLabelTimeoutSec = 2; + static constexpr TickType_t restoreLabelTimeoutTicks = restoreLabelTimeoutSec * configTICK_RATE_HZ; void btnImmediateAlertEventHandler(lv_obj_t* obj, lv_event_t event) { auto* screen = static_cast(obj->user_data); From d5316745490c196090224d24531066cf176159db Mon Sep 17 00:00:00 2001 From: Vyacheslav Chigrin Date: Thu, 5 Dec 2024 00:33:22 +0300 Subject: [PATCH 12/21] Fix log messages. --- src/components/ble/ImmediateAlertClient.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/ble/ImmediateAlertClient.cpp b/src/components/ble/ImmediateAlertClient.cpp index dcd1833785..c870737e46 100644 --- a/src/components/ble/ImmediateAlertClient.cpp +++ b/src/components/ble/ImmediateAlertClient.cpp @@ -32,18 +32,18 @@ void ImmediateAlertClient::Init() { bool ImmediateAlertClient::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("IAS found, starting characteristics discovery"); + NRF_LOG_INFO("[IAS] service found, starting characteristics discovery"); ble_gattc_disc_all_chrs(connectionHandle, iasStartHandle, iasEndHandle, OnImmediateAlertCharacteristicDiscoveredCallback, this); } else { - NRF_LOG_INFO("IAS not found"); + NRF_LOG_INFO("[IAS] service not found"); onServiceDiscovered(connectionHandle); } return true; } if (service != nullptr && ble_uuid_cmp(&immediateAlertClientUuid.u, &service->uuid.u) == 0) { - NRF_LOG_INFO("IAS discovered : 0x%x - 0x%x", service->start_handle, service->end_handle); + NRF_LOG_INFO("[IAS] discovered : 0x%x - 0x%x", service->start_handle, service->end_handle); isDiscovered = true; iasStartHandle = service->start_handle; iasEndHandle = service->end_handle; @@ -56,14 +56,14 @@ int ImmediateAlertClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_chr* characteristic) { if (error->status != 0 && error->status != BLE_HS_EDONE) { - NRF_LOG_INFO("IAS Characteristic discovery ERROR"); + NRF_LOG_INFO("[IAS] Characteristic discovery ERROR"); onServiceDiscovered(conn_handle); return 0; } if (characteristic == nullptr && error->status == BLE_HS_EDONE) { if (!isCharacteristicDiscovered) { - NRF_LOG_INFO("IAS Characteristic discovery unsuccessful"); + NRF_LOG_INFO("[IAS] Characteristic discovery unsuccessful"); onServiceDiscovered(conn_handle); } @@ -71,7 +71,7 @@ int ImmediateAlertClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, } if (characteristic != nullptr && ble_uuid_cmp(&alertLevelCharacteristicUuid.u, &characteristic->uuid.u) == 0) { - NRF_LOG_INFO("AIS Characteristic discovered : 0x%x", characteristic->val_handle); + NRF_LOG_INFO("[IAS] Characteristic discovered : 0x%x", characteristic->val_handle); isCharacteristicDiscovered = true; alertLevelHandle = characteristic->val_handle; } From 262cb44b61ddb22a19d4470dad6b057919c484ef Mon Sep 17 00:00:00 2001 From: Vyacheslav Chigrin Date: Thu, 5 Dec 2024 00:35:19 +0300 Subject: [PATCH 13/21] Remove accidental change. --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 337b85d0f5..d6f917cfc4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ cmake-build-* cmake-*/ CMakeFiles **/CMakeCache.txt -CMakeLists.txt.user* cmake_install.cmake Makefile build From d8797848c39bffd75cc237c452262ceb6f8f86b4 Mon Sep 17 00:00:00 2001 From: Vyacheslav Chigrin Date: Thu, 5 Dec 2024 00:46:58 +0300 Subject: [PATCH 14/21] Change colors in UI. --- src/displayapp/screens/FindMyPhone.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/displayapp/screens/FindMyPhone.cpp b/src/displayapp/screens/FindMyPhone.cpp index 7b4c66aa3d..70911ddeaa 100644 --- a/src/displayapp/screens/FindMyPhone.cpp +++ b/src/displayapp/screens/FindMyPhone.cpp @@ -41,7 +41,7 @@ FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertClient& immediateA lblTitle = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text_static(lblTitle, defaultLabelText); - lv_obj_set_style_local_text_color(lblTitle, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); + lv_obj_set_style_local_text_color(lblTitle, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); lv_obj_align(lblTitle, nullptr, LV_ALIGN_CENTER, 0, -40); btnNone = lv_btn_create(container, nullptr); @@ -51,7 +51,7 @@ FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertClient& immediateA lv_obj_align(btnNone, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); lblNone = lv_label_create(btnNone, nullptr); lv_label_set_text_static(lblNone, noneLabelText); - lv_obj_set_style_local_bg_color(btnNone, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); + lv_obj_set_style_local_bg_color(btnNone, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt); btnHigh = lv_btn_create(container, nullptr); btnHigh->user_data = this; @@ -81,7 +81,7 @@ void FindMyPhone::OnImmediateAlertEvent(lv_obj_t* obj, lv_event_t event) { void FindMyPhone::UpdateImmediateAlerts() { switch (lastLevel) { case Pinetime::Controllers::ImmediateAlertClient::Levels::NoAlert: - lv_obj_set_style_local_text_color(lblTitle, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); + lv_obj_set_style_local_text_color(lblTitle, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); break; case Pinetime::Controllers::ImmediateAlertClient::Levels::HighAlert: lv_obj_set_style_local_text_color(lblTitle, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); @@ -115,5 +115,5 @@ void FindMyPhone::StopRestoreLabelTask() { void FindMyPhone::RestoreLabelText() { lv_label_set_text_static(lblTitle, defaultLabelText); - lv_obj_set_style_local_text_color(lblTitle, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); + lv_obj_set_style_local_text_color(lblTitle, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); } From ce833679731ec644d9f89cc4ac1ac8d9a424859b Mon Sep 17 00:00:00 2001 From: Vyacheslav Chigrin Date: Fri, 6 Dec 2024 00:07:20 +0300 Subject: [PATCH 15/21] Avoid using uninitialized value in ImmediateAlertClient. --- src/components/ble/ImmediateAlertClient.cpp | 25 +++++++++++++-------- src/components/ble/ImmediateAlertClient.h | 11 ++++----- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/components/ble/ImmediateAlertClient.cpp b/src/components/ble/ImmediateAlertClient.cpp index c870737e46..a73c7c511d 100644 --- a/src/components/ble/ImmediateAlertClient.cpp +++ b/src/components/ble/ImmediateAlertClient.cpp @@ -31,10 +31,14 @@ void ImmediateAlertClient::Init() { bool ImmediateAlertClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service) { if (service == nullptr && error->status == BLE_HS_EDONE) { - if (isDiscovered) { + if (iasHandles.has_value()) { NRF_LOG_INFO("[IAS] service found, starting characteristics discovery"); - ble_gattc_disc_all_chrs(connectionHandle, iasStartHandle, iasEndHandle, OnImmediateAlertCharacteristicDiscoveredCallback, this); + ble_gattc_disc_all_chrs(connectionHandle, + iasHandles->startHandle, + iasHandles->endHandle, + OnImmediateAlertCharacteristicDiscoveredCallback, + this); } else { NRF_LOG_INFO("[IAS] service not found"); onServiceDiscovered(connectionHandle); @@ -44,9 +48,10 @@ bool ImmediateAlertClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble if (service != nullptr && ble_uuid_cmp(&immediateAlertClientUuid.u, &service->uuid.u) == 0) { NRF_LOG_INFO("[IAS] discovered : 0x%x - 0x%x", service->start_handle, service->end_handle); - isDiscovered = true; - iasStartHandle = service->start_handle; - iasEndHandle = service->end_handle; + iasHandles.emplace(HandleRange { + .startHandle = service->start_handle, + .endHandle = service->end_handle, + }); } return false; } @@ -62,8 +67,8 @@ int ImmediateAlertClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, } if (characteristic == nullptr && error->status == BLE_HS_EDONE) { - if (!isCharacteristicDiscovered) { - NRF_LOG_INFO("[IAS] Characteristic discovery unsuccessful"); + if (!alertLevelHandle.has_value()) { + NRF_LOG_INFO("[IAS] Alert level characteristic not found."); onServiceDiscovered(conn_handle); } @@ -72,7 +77,6 @@ int ImmediateAlertClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, if (characteristic != nullptr && ble_uuid_cmp(&alertLevelCharacteristicUuid.u, &characteristic->uuid.u) == 0) { NRF_LOG_INFO("[IAS] Characteristic discovered : 0x%x", characteristic->val_handle); - isCharacteristicDiscovered = true; alertLevelHandle = characteristic->val_handle; } return 0; @@ -93,7 +97,10 @@ bool ImmediateAlertClient::SendImmediateAlert(ImmediateAlertClient::Levels level if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) { return false; } + if (!alertLevelHandle.has_value()) { + return false; + } - ble_gattc_write_no_rsp(connectionHandle, alertLevelHandle, om); + ble_gattc_write_no_rsp(connectionHandle, *alertLevelHandle, om); return true; } diff --git a/src/components/ble/ImmediateAlertClient.h b/src/components/ble/ImmediateAlertClient.h index b3f944b32e..616413a71f 100644 --- a/src/components/ble/ImmediateAlertClient.h +++ b/src/components/ble/ImmediateAlertClient.h @@ -46,12 +46,13 @@ namespace Pinetime { static constexpr ble_uuid16_t immediateAlertClientUuid {.u {.type = BLE_UUID_TYPE_16}, .value = immediateAlertClientId}; static constexpr ble_uuid16_t alertLevelCharacteristicUuid {.u {.type = BLE_UUID_TYPE_16}, .value = alertLevelId}; - bool isDiscovered = false; - uint16_t iasStartHandle; - uint16_t iasEndHandle; - bool isCharacteristicDiscovered = false; + struct HandleRange { + uint16_t startHandle; + uint16_t endHandle; + }; - uint16_t alertLevelHandle; + std::optional iasHandles; + std::optional alertLevelHandle; std::function onServiceDiscovered; }; } From d1b154214bbf352ba72c98b26765e93bd0261d79 Mon Sep 17 00:00:00 2001 From: Vyacheslav Chigrin Date: Fri, 6 Dec 2024 00:16:17 +0300 Subject: [PATCH 16/21] Make more ImmediateAlertClient private. --- src/components/ble/ImmediateAlertClient.cpp | 31 +++++++++++---------- src/components/ble/ImmediateAlertClient.h | 17 +++++++---- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/components/ble/ImmediateAlertClient.cpp b/src/components/ble/ImmediateAlertClient.cpp index a73c7c511d..7ac5945024 100644 --- a/src/components/ble/ImmediateAlertClient.cpp +++ b/src/components/ble/ImmediateAlertClient.cpp @@ -8,21 +8,6 @@ using namespace Pinetime::Controllers; constexpr ble_uuid16_t ImmediateAlertClient::immediateAlertClientUuid; constexpr ble_uuid16_t ImmediateAlertClient::alertLevelCharacteristicUuid; -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(arg); - return client->OnDiscoveryEvent(conn_handle, error, service); - } - - int OnImmediateAlertCharacteristicDiscoveredCallback(uint16_t conn_handle, - const struct ble_gatt_error* error, - const struct ble_gatt_chr* chr, - void* arg) { - auto client = static_cast(arg); - return client->OnCharacteristicDiscoveryEvent(conn_handle, error, chr); - } -} - ImmediateAlertClient::ImmediateAlertClient(Pinetime::System::SystemTask& systemTask) : systemTask {systemTask} { } @@ -104,3 +89,19 @@ bool ImmediateAlertClient::SendImmediateAlert(ImmediateAlertClient::Levels level ble_gattc_write_no_rsp(connectionHandle, *alertLevelHandle, om); return true; } + +int ImmediateAlertClient::OnDiscoveryEventCallback(uint16_t conn_handle, + const struct ble_gatt_error* error, + const struct ble_gatt_svc* service, + void* arg) { + auto client = static_cast(arg); + return client->OnDiscoveryEvent(conn_handle, error, service); +} + +int ImmediateAlertClient::OnImmediateAlertCharacteristicDiscoveredCallback(uint16_t conn_handle, + const struct ble_gatt_error* error, + const struct ble_gatt_chr* chr, + void* arg) { + auto client = static_cast(arg); + return client->OnCharacteristicDiscoveryEvent(conn_handle, error, chr); +} diff --git a/src/components/ble/ImmediateAlertClient.h b/src/components/ble/ImmediateAlertClient.h index 616413a71f..d773255367 100644 --- a/src/components/ble/ImmediateAlertClient.h +++ b/src/components/ble/ImmediateAlertClient.h @@ -19,14 +19,17 @@ namespace Pinetime { public: enum class Levels : uint8_t { NoAlert = 0, MildAlert = 1, HighAlert = 2 }; - ImmediateAlertClient(Pinetime::System::SystemTask& systemTask); + explicit ImmediateAlertClient(Pinetime::System::SystemTask& systemTask); void Init(); + bool SendImmediateAlert(Levels level); + + void Discover(uint16_t connectionHandle, std::function lambda) override; + + private: bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service); int OnCharacteristicDiscoveryEvent(uint16_t conn_handle, const ble_gatt_error* error, const ble_gatt_chr* characteristic); - bool SendImmediateAlert(Levels level); - static constexpr const ble_uuid16_t* Uuid() { return &ImmediateAlertClient::immediateAlertClientUuid; } @@ -35,9 +38,13 @@ namespace Pinetime { return &ImmediateAlertClient::alertLevelCharacteristicUuid; } - void Discover(uint16_t connectionHandle, std::function lambda) override; + static int + OnDiscoveryEventCallback(uint16_t conn_handle, const struct ble_gatt_error* error, const struct ble_gatt_svc* service, void* arg); + static int OnImmediateAlertCharacteristicDiscoveredCallback(uint16_t conn_handle, + const struct ble_gatt_error* error, + const struct ble_gatt_chr* chr, + void* arg); - private: Pinetime::System::SystemTask& systemTask; static constexpr uint16_t immediateAlertClientId {0x1802}; From be30da1f0bb39e755522bab1dc7e737b2e3f5b72 Mon Sep 17 00:00:00 2001 From: Vyacheslav Chigrin Date: Fri, 6 Dec 2024 01:40:40 +0300 Subject: [PATCH 17/21] Update FindMyPhone UI in case disconnect. --- src/components/ble/ImmediateAlertClient.cpp | 8 ++ src/components/ble/ImmediateAlertClient.h | 11 ++ src/components/ble/NimbleController.cpp | 2 + src/displayapp/screens/FindMyPhone.cpp | 119 ++++++++++++-------- src/displayapp/screens/FindMyPhone.h | 19 +++- 5 files changed, 105 insertions(+), 54 deletions(-) diff --git a/src/components/ble/ImmediateAlertClient.cpp b/src/components/ble/ImmediateAlertClient.cpp index 7ac5945024..ad3ac189fc 100644 --- a/src/components/ble/ImmediateAlertClient.cpp +++ b/src/components/ble/ImmediateAlertClient.cpp @@ -63,6 +63,7 @@ int ImmediateAlertClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, if (characteristic != nullptr && ble_uuid_cmp(&alertLevelCharacteristicUuid.u, &characteristic->uuid.u) == 0) { NRF_LOG_INFO("[IAS] Characteristic discovered : 0x%x", characteristic->val_handle); alertLevelHandle = characteristic->val_handle; + state = State::Connected; } return 0; } @@ -70,9 +71,16 @@ int ImmediateAlertClient::OnCharacteristicDiscoveryEvent(uint16_t conn_handle, void ImmediateAlertClient::Discover(uint16_t connectionHandle, std::function onServiceDiscovered) { NRF_LOG_INFO("[IAS] Starting discovery"); this->onServiceDiscovered = onServiceDiscovered; + state = State::NoIAS; ble_gattc_disc_svc_by_uuid(connectionHandle, &immediateAlertClientUuid.u, OnDiscoveryEventCallback, this); } +void ImmediateAlertClient::Reset() { + state = State::NoConnection; + iasHandles = std::nullopt; + alertLevelHandle = std::nullopt; +} + bool ImmediateAlertClient::SendImmediateAlert(ImmediateAlertClient::Levels level) { auto* om = ble_hs_mbuf_from_flat(&level, 1); diff --git a/src/components/ble/ImmediateAlertClient.h b/src/components/ble/ImmediateAlertClient.h index d773255367..33a2bbf6e0 100644 --- a/src/components/ble/ImmediateAlertClient.h +++ b/src/components/ble/ImmediateAlertClient.h @@ -18,13 +18,23 @@ namespace Pinetime { class ImmediateAlertClient : public BleClient { public: enum class Levels : uint8_t { NoAlert = 0, MildAlert = 1, HighAlert = 2 }; + enum class State { + NoConnection, + NoIAS, + Connected, + }; explicit ImmediateAlertClient(Pinetime::System::SystemTask& systemTask); void Init(); bool SendImmediateAlert(Levels level); + State GetState() const { + return state; + } + void Discover(uint16_t connectionHandle, std::function lambda) override; + void Reset(); private: bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service); @@ -61,6 +71,7 @@ namespace Pinetime { std::optional iasHandles; std::optional alertLevelHandle; std::function onServiceDiscovered; + State state {State::NoConnection}; }; } } diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index fb01931b4d..db084990c3 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -202,6 +202,7 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { /* Connection failed; resume advertising. */ currentTimeClient.Reset(); alertNotificationClient.Reset(); + iaClient.Reset(); connectionHandle = BLE_HS_CONN_HANDLE_NONE; bleController.Disconnect(); fastAdvCount = 0; @@ -225,6 +226,7 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) { currentTimeClient.Reset(); alertNotificationClient.Reset(); + iaClient.Reset(); connectionHandle = BLE_HS_CONN_HANDLE_NONE; if (bleController.IsConnected()) { bleController.Disconnect(); diff --git a/src/displayapp/screens/FindMyPhone.cpp b/src/displayapp/screens/FindMyPhone.cpp index 70911ddeaa..c37d1ac80f 100644 --- a/src/displayapp/screens/FindMyPhone.cpp +++ b/src/displayapp/screens/FindMyPhone.cpp @@ -7,29 +7,43 @@ using namespace Pinetime::Applications::Screens; namespace { - static constexpr char defaultLabelText[] = "Find my phone"; - static constexpr char alertSentLabelText[] = "Alert sent"; - static constexpr char noConnectionLabelText[] = "No connection"; + static constexpr char alertSentLabelText[] = "Alerting"; + static constexpr char noneLabelText[] = "Stop"; static constexpr char highLabelText[] = "Ring"; - static constexpr int restoreLabelTimeoutSec = 2; - static constexpr TickType_t restoreLabelTimeoutTicks = restoreLabelTimeoutSec * configTICK_RATE_HZ; void btnImmediateAlertEventHandler(lv_obj_t* obj, lv_event_t event) { auto* screen = static_cast(obj->user_data); screen->OnImmediateAlertEvent(obj, event); } - - void RestoreLabelTaskCallback(lv_task_t* task) { - auto* screen = static_cast(task->user_data); - screen->RestoreLabelText(); - screen->StopRestoreLabelTask(); - } } -FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertClient& immediateAlertClient) : immediateAlertClient {immediateAlertClient} { - lastLevel = Pinetime::Controllers::ImmediateAlertClient::Levels::NoAlert; +const FindMyPhone::LabelState FindMyPhone::stoppedLabelState { + .text = "Alert stopped", + .color = LV_COLOR_WHITE, +}; + +const FindMyPhone::LabelState FindMyPhone::noConnectionLabelState { + .text = "No connection", + .color = LV_COLOR_WHITE, +}; + +const FindMyPhone::LabelState FindMyPhone::noServiceLabelState { + .text = "No service", + .color = LV_COLOR_WHITE, +}; + +const FindMyPhone::LabelState FindMyPhone::defaultLabelState { + .text = "Ready", + .color = LV_COLOR_WHITE, +}; +const FindMyPhone::LabelState FindMyPhone::alertingLabelState { + .text = "Alerting", + .color = LV_COLOR_RED, +}; + +FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertClient& immediateAlertClient) : immediateAlertClient {immediateAlertClient} { container = lv_cont_create(lv_scr_act(), nullptr); lv_obj_set_size(container, LV_HOR_RES, LV_VER_RES); @@ -40,10 +54,6 @@ FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertClient& immediateA lblTitle = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text_static(lblTitle, defaultLabelText); - lv_obj_set_style_local_text_color(lblTitle, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); - lv_obj_align(lblTitle, nullptr, LV_ALIGN_CENTER, 0, -40); - btnNone = lv_btn_create(container, nullptr); btnNone->user_data = this; lv_obj_set_event_cb(btnNone, btnImmediateAlertEventHandler); @@ -61,59 +71,68 @@ FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertClient& immediateA lblHigh = lv_label_create(btnHigh, nullptr); lv_label_set_text_static(lblHigh, highLabelText); lv_obj_set_style_local_bg_color(btnHigh, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); + refreshTask = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); } FindMyPhone::~FindMyPhone() { + lv_task_del(refreshTask); lv_obj_clean(lv_scr_act()); } void FindMyPhone::OnImmediateAlertEvent(lv_obj_t* obj, lv_event_t event) { if (event == LV_EVENT_CLICKED) { if (obj == btnNone) { - lastLevel = Pinetime::Controllers::ImmediateAlertClient::Levels::NoAlert; + lastUserInitiatedLevel = Pinetime::Controllers::ImmediateAlertClient::Levels::NoAlert; } else if (obj == btnHigh) { - lastLevel = Pinetime::Controllers::ImmediateAlertClient::Levels::HighAlert; + lastUserInitiatedLevel = Pinetime::Controllers::ImmediateAlertClient::Levels::HighAlert; + } else { + // Unknown button? + ASSERT(false); + return; } - UpdateImmediateAlerts(); + immediateAlertClient.SendImmediateAlert(*lastUserInitiatedLevel); } } -void FindMyPhone::UpdateImmediateAlerts() { - switch (lastLevel) { - case Pinetime::Controllers::ImmediateAlertClient::Levels::NoAlert: - lv_obj_set_style_local_text_color(lblTitle, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); +const FindMyPhone::LabelState& FindMyPhone::GetLabelState() const { + const auto service_state = immediateAlertClient.GetState(); + switch (service_state) { + case Pinetime::Controllers::ImmediateAlertClient::State::NoConnection: + return noConnectionLabelState; + case Pinetime::Controllers::ImmediateAlertClient::State::NoIAS: + return noServiceLabelState; + case Pinetime::Controllers::ImmediateAlertClient::State::Connected: break; + } + // Conntected state handling. + if (!lastUserInitiatedLevel.has_value()) { + return defaultLabelState; + } + switch (*lastUserInitiatedLevel) { + case Pinetime::Controllers::ImmediateAlertClient::Levels::NoAlert: + return stoppedLabelState; case Pinetime::Controllers::ImmediateAlertClient::Levels::HighAlert: - lv_obj_set_style_local_text_color(lblTitle, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); - break; + return alertingLabelState; case Pinetime::Controllers::ImmediateAlertClient::Levels::MildAlert: - // Not supported. + // Not supported + default: ASSERT(false); - break; + return alertingLabelState; } - if (immediateAlertClient.SendImmediateAlert(lastLevel)) { - lv_label_set_text_static(lblTitle, alertSentLabelText); - } else { - lv_label_set_text_static(lblTitle, noConnectionLabelText); - } - ScheduleRestoreLabelTask(); } -void FindMyPhone::ScheduleRestoreLabelTask() { - if (taskRestoreLabelText) { - return; - } - taskRestoreLabelText = lv_task_create(RestoreLabelTaskCallback, restoreLabelTimeoutTicks, LV_TASK_PRIO_MID, this); -} - -void FindMyPhone::StopRestoreLabelTask() { - if (taskRestoreLabelText) { - lv_task_del(taskRestoreLabelText); - taskRestoreLabelText = nullptr; +void FindMyPhone::Refresh() { + const auto service_state = immediateAlertClient.GetState(); + if (service_state == Pinetime::Controllers::ImmediateAlertClient::State::Connected) { + lv_obj_clear_state(btnNone, LV_STATE_DISABLED); + lv_obj_clear_state(btnHigh, LV_STATE_DISABLED); + } else { + lv_obj_add_state(btnNone, LV_STATE_DISABLED); + lv_obj_add_state(btnHigh, LV_STATE_DISABLED); + lastUserInitiatedLevel = std::nullopt; } -} - -void FindMyPhone::RestoreLabelText() { - lv_label_set_text_static(lblTitle, defaultLabelText); - lv_obj_set_style_local_text_color(lblTitle, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + const auto& label_state = GetLabelState(); + lv_obj_set_style_local_text_color(lblTitle, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, label_state.color); + lv_label_set_text_static(lblTitle, label_state.text); + lv_obj_align(lblTitle, nullptr, LV_ALIGN_CENTER, 0, -40); } diff --git a/src/displayapp/screens/FindMyPhone.h b/src/displayapp/screens/FindMyPhone.h index 75d130b284..687ccf203c 100644 --- a/src/displayapp/screens/FindMyPhone.h +++ b/src/displayapp/screens/FindMyPhone.h @@ -2,6 +2,7 @@ #include #include +#include #include "displayapp/screens/Screen.h" #include "Symbols.h" #include "systemtask/SystemTask.h" @@ -26,14 +27,24 @@ namespace Pinetime { void OnImmediateAlertEvent(lv_obj_t* obj, lv_event_t event); - void ScheduleRestoreLabelTask(); void StopRestoreLabelTask(); void RestoreLabelText(); + void Refresh() override; private: Pinetime::Controllers::ImmediateAlertClient& immediateAlertClient; - void UpdateImmediateAlerts(); + struct LabelState { + const char* text; + lv_color_t color; + }; + + static const LabelState stoppedLabelState; + static const LabelState noConnectionLabelState; + static const LabelState noServiceLabelState; + static const LabelState defaultLabelState; + static const LabelState alertingLabelState; + const LabelState& GetLabelState() const; lv_obj_t* container; lv_obj_t* lblTitle; @@ -41,9 +52,9 @@ namespace Pinetime { lv_obj_t* btnHigh; lv_obj_t* lblNone; lv_obj_t* lblHigh; - lv_task_t* taskRestoreLabelText = nullptr; + lv_task_t* refreshTask = nullptr; - Pinetime::Controllers::ImmediateAlertClient::Levels lastLevel; + std::optional lastUserInitiatedLevel; }; } From e62f6489caf32e5901a7e91a11b04ca95bb5c8f8 Mon Sep 17 00:00:00 2001 From: Vyacheslav Chigrin Date: Sat, 7 Dec 2024 22:48:47 +0300 Subject: [PATCH 18/21] Rename members in FindMyPhoneUI. --- src/displayapp/screens/FindMyPhone.cpp | 50 +++++++++++++------------- src/displayapp/screens/FindMyPhone.h | 8 ++--- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/displayapp/screens/FindMyPhone.cpp b/src/displayapp/screens/FindMyPhone.cpp index c37d1ac80f..dca3bff9a0 100644 --- a/src/displayapp/screens/FindMyPhone.cpp +++ b/src/displayapp/screens/FindMyPhone.cpp @@ -9,8 +9,8 @@ using namespace Pinetime::Applications::Screens; namespace { static constexpr char alertSentLabelText[] = "Alerting"; - static constexpr char noneLabelText[] = "Stop"; - static constexpr char highLabelText[] = "Ring"; + static constexpr char stopLabelText[] = "Stop"; + static constexpr char ringLabelText[] = "Ring"; void btnImmediateAlertEventHandler(lv_obj_t* obj, lv_event_t event) { auto* screen = static_cast(obj->user_data); @@ -54,23 +54,23 @@ FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertClient& immediateA lblTitle = lv_label_create(lv_scr_act(), nullptr); - btnNone = lv_btn_create(container, nullptr); - btnNone->user_data = this; - lv_obj_set_event_cb(btnNone, btnImmediateAlertEventHandler); - lv_obj_set_size(btnNone, 114, 76); - lv_obj_align(btnNone, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); - lblNone = lv_label_create(btnNone, nullptr); - lv_label_set_text_static(lblNone, noneLabelText); - lv_obj_set_style_local_bg_color(btnNone, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt); - - btnHigh = lv_btn_create(container, nullptr); - btnHigh->user_data = this; - lv_obj_set_event_cb(btnHigh, btnImmediateAlertEventHandler); - lv_obj_set_size(btnHigh, 114, 76); - lv_obj_align(btnHigh, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); - lblHigh = lv_label_create(btnHigh, nullptr); - lv_label_set_text_static(lblHigh, highLabelText); - lv_obj_set_style_local_bg_color(btnHigh, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); + btnStop = lv_btn_create(container, nullptr); + btnStop->user_data = this; + lv_obj_set_event_cb(btnStop, btnImmediateAlertEventHandler); + lv_obj_set_size(btnStop, 114, 76); + lv_obj_align(btnStop, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); + lblStop = lv_label_create(btnStop, nullptr); + lv_label_set_text_static(lblStop, stopLabelText); + lv_obj_set_style_local_bg_color(btnStop, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt); + + btnRing = lv_btn_create(container, nullptr); + btnRing->user_data = this; + lv_obj_set_event_cb(btnRing, btnImmediateAlertEventHandler); + lv_obj_set_size(btnRing, 114, 76); + lv_obj_align(btnRing, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); + lblRing = lv_label_create(btnRing, nullptr); + lv_label_set_text_static(lblRing, ringLabelText); + lv_obj_set_style_local_bg_color(btnRing, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); refreshTask = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); } @@ -81,9 +81,9 @@ FindMyPhone::~FindMyPhone() { void FindMyPhone::OnImmediateAlertEvent(lv_obj_t* obj, lv_event_t event) { if (event == LV_EVENT_CLICKED) { - if (obj == btnNone) { + if (obj == btnStop) { lastUserInitiatedLevel = Pinetime::Controllers::ImmediateAlertClient::Levels::NoAlert; - } else if (obj == btnHigh) { + } else if (obj == btnRing) { lastUserInitiatedLevel = Pinetime::Controllers::ImmediateAlertClient::Levels::HighAlert; } else { // Unknown button? @@ -124,11 +124,11 @@ const FindMyPhone::LabelState& FindMyPhone::GetLabelState() const { void FindMyPhone::Refresh() { const auto service_state = immediateAlertClient.GetState(); if (service_state == Pinetime::Controllers::ImmediateAlertClient::State::Connected) { - lv_obj_clear_state(btnNone, LV_STATE_DISABLED); - lv_obj_clear_state(btnHigh, LV_STATE_DISABLED); + lv_obj_clear_state(btnStop, LV_STATE_DISABLED); + lv_obj_clear_state(btnRing, LV_STATE_DISABLED); } else { - lv_obj_add_state(btnNone, LV_STATE_DISABLED); - lv_obj_add_state(btnHigh, LV_STATE_DISABLED); + lv_obj_add_state(btnStop, LV_STATE_DISABLED); + lv_obj_add_state(btnRing, LV_STATE_DISABLED); lastUserInitiatedLevel = std::nullopt; } const auto& label_state = GetLabelState(); diff --git a/src/displayapp/screens/FindMyPhone.h b/src/displayapp/screens/FindMyPhone.h index 687ccf203c..92c3b92897 100644 --- a/src/displayapp/screens/FindMyPhone.h +++ b/src/displayapp/screens/FindMyPhone.h @@ -48,10 +48,10 @@ namespace Pinetime { lv_obj_t* container; lv_obj_t* lblTitle; - lv_obj_t* btnNone; - lv_obj_t* btnHigh; - lv_obj_t* lblNone; - lv_obj_t* lblHigh; + lv_obj_t* btnStop; + lv_obj_t* btnRing; + lv_obj_t* lblStop; + lv_obj_t* lblRing; lv_task_t* refreshTask = nullptr; std::optional lastUserInitiatedLevel; From 0b78017fea230df9697f3dd5f8e997097bcf09d7 Mon Sep 17 00:00:00 2001 From: Vyacheslav Chigrin Date: Sun, 8 Dec 2024 02:44:34 +0300 Subject: [PATCH 19/21] Avoid blinking during FindMyPhone app start. --- src/displayapp/screens/FindMyPhone.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/displayapp/screens/FindMyPhone.cpp b/src/displayapp/screens/FindMyPhone.cpp index dca3bff9a0..45dc9570f3 100644 --- a/src/displayapp/screens/FindMyPhone.cpp +++ b/src/displayapp/screens/FindMyPhone.cpp @@ -72,6 +72,8 @@ FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertClient& immediateA lv_label_set_text_static(lblRing, ringLabelText); lv_obj_set_style_local_bg_color(btnRing, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); refreshTask = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); + // Refresh ASAP to properly set buttons state. + Refresh(); } FindMyPhone::~FindMyPhone() { From ace894c93c460856363b737f7c420a47550540cf Mon Sep 17 00:00:00 2001 From: Vyacheslav Chigrin Date: Sun, 8 Dec 2024 03:09:44 +0300 Subject: [PATCH 20/21] Show communication failure in UI. --- src/components/ble/ImmediateAlertClient.cpp | 3 +-- src/displayapp/screens/FindMyPhone.cpp | 15 ++++++++++++++- src/displayapp/screens/FindMyPhone.h | 2 ++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/components/ble/ImmediateAlertClient.cpp b/src/components/ble/ImmediateAlertClient.cpp index ad3ac189fc..4b50f6c537 100644 --- a/src/components/ble/ImmediateAlertClient.cpp +++ b/src/components/ble/ImmediateAlertClient.cpp @@ -94,8 +94,7 @@ bool ImmediateAlertClient::SendImmediateAlert(ImmediateAlertClient::Levels level return false; } - ble_gattc_write_no_rsp(connectionHandle, *alertLevelHandle, om); - return true; + return ble_gattc_write_no_rsp(connectionHandle, *alertLevelHandle, om) == 0; } int ImmediateAlertClient::OnDiscoveryEventCallback(uint16_t conn_handle, diff --git a/src/displayapp/screens/FindMyPhone.cpp b/src/displayapp/screens/FindMyPhone.cpp index 45dc9570f3..d6fca72669 100644 --- a/src/displayapp/screens/FindMyPhone.cpp +++ b/src/displayapp/screens/FindMyPhone.cpp @@ -43,6 +43,11 @@ const FindMyPhone::LabelState FindMyPhone::alertingLabelState { .color = LV_COLOR_RED, }; +const FindMyPhone::LabelState FindMyPhone::sendFailedLabelState { + .text = "Communication fail", + .color = LV_COLOR_WHITE, +}; + FindMyPhone::FindMyPhone(Pinetime::Controllers::ImmediateAlertClient& immediateAlertClient) : immediateAlertClient {immediateAlertClient} { container = lv_cont_create(lv_scr_act(), nullptr); @@ -92,7 +97,11 @@ void FindMyPhone::OnImmediateAlertEvent(lv_obj_t* obj, lv_event_t event) { ASSERT(false); return; } - immediateAlertClient.SendImmediateAlert(*lastUserInitiatedLevel); + if (immediateAlertClient.SendImmediateAlert(*lastUserInitiatedLevel)) { + lastSendFailed = false; + } else { + lastSendFailed = true; + } } } @@ -106,6 +115,9 @@ const FindMyPhone::LabelState& FindMyPhone::GetLabelState() const { case Pinetime::Controllers::ImmediateAlertClient::State::Connected: break; } + if (lastSendFailed) { + return sendFailedLabelState; + } // Conntected state handling. if (!lastUserInitiatedLevel.has_value()) { return defaultLabelState; @@ -132,6 +144,7 @@ void FindMyPhone::Refresh() { lv_obj_add_state(btnStop, LV_STATE_DISABLED); lv_obj_add_state(btnRing, LV_STATE_DISABLED); lastUserInitiatedLevel = std::nullopt; + lastSendFailed = false; } const auto& label_state = GetLabelState(); lv_obj_set_style_local_text_color(lblTitle, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, label_state.color); diff --git a/src/displayapp/screens/FindMyPhone.h b/src/displayapp/screens/FindMyPhone.h index 92c3b92897..b0561be1de 100644 --- a/src/displayapp/screens/FindMyPhone.h +++ b/src/displayapp/screens/FindMyPhone.h @@ -44,6 +44,7 @@ namespace Pinetime { static const LabelState noServiceLabelState; static const LabelState defaultLabelState; static const LabelState alertingLabelState; + static const LabelState sendFailedLabelState; const LabelState& GetLabelState() const; lv_obj_t* container; @@ -54,6 +55,7 @@ namespace Pinetime { lv_obj_t* lblRing; lv_task_t* refreshTask = nullptr; + bool lastSendFailed = false; std::optional lastUserInitiatedLevel; }; } From c491f842b10ca6f66a843b41da6ac136c59f23b4 Mon Sep 17 00:00:00 2001 From: Vyacheslav Chigrin Date: Sun, 24 Aug 2025 23:05:36 +0300 Subject: [PATCH 21/21] Fix after rebase. --- src/displayapp/screens/FindMyPhone.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/displayapp/screens/FindMyPhone.h b/src/displayapp/screens/FindMyPhone.h index b0561be1de..586532aa35 100644 --- a/src/displayapp/screens/FindMyPhone.h +++ b/src/displayapp/screens/FindMyPhone.h @@ -68,6 +68,10 @@ namespace Pinetime { static Screens::Screen* Create(AppControllers& controllers) { return new Screens::FindMyPhone(controllers.systemTask->nimble().immediateAlertClient()); }; + + static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) { + return true; + }; }; } }