diff --git a/src/components/ble/AgendaService.cpp b/src/components/ble/AgendaService.cpp index 77b5137d..e8c71aa2 100644 --- a/src/components/ble/AgendaService.cpp +++ b/src/components/ble/AgendaService.cpp @@ -66,7 +66,7 @@ void Pinetime::Controllers::AgendaService::Init() { ASSERT(res == 0); m_agenda_times[0] = 1655327841; - m_agenda_pieces[0] = "Write AgendaService"; + m_agenda_pieces[0] = "C++ Fumbling"; } int Pinetime::Controllers::AgendaService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) { @@ -81,7 +81,7 @@ int Pinetime::Controllers::AgendaService::OnCommand(uint16_t conn_handle, uint16 uint8_t itemIndex = (uint8_t) data[0]; uint32_t itemTime = ((uint32_t) data[1]) << 24 | ((uint32_t) data[2]) << 16 | ((uint32_t) data[3]) << 8 | ((uint32_t) data[4]); char* sAgendaItem = (char*) &data[5]; - if (itemIndex <= 15) { // don't allow a buffer overflow + if (itemIndex < AGENDA_CAPACITY) { // don't allow a buffer overflow m_agenda_times[itemIndex] = itemTime; // implicit std::string copy/conversion here: m_agenda_pieces[itemIndex] = sAgendaItem; @@ -93,7 +93,7 @@ int Pinetime::Controllers::AgendaService::OnCommand(uint16_t conn_handle, uint16 } uint32_t Pinetime::Controllers::AgendaService::getAgendaTime(uint8_t index) { - if (index <= 15) { + if (index < AGENDA_CAPACITY) { return m_agenda_times[index]; } else { return 0; @@ -101,7 +101,7 @@ uint32_t Pinetime::Controllers::AgendaService::getAgendaTime(uint8_t index) { } std::string Pinetime::Controllers::AgendaService::getAgendaPiece(uint8_t index) { - if (index <= 15) { + if (index < AGENDA_CAPACITY) { return m_agenda_pieces[index]; } else { return 0; diff --git a/src/components/ble/AgendaService.h b/src/components/ble/AgendaService.h index e1390a1c..89884b70 100644 --- a/src/components/ble/AgendaService.h +++ b/src/components/ble/AgendaService.h @@ -34,6 +34,8 @@ namespace Pinetime { class AgendaService { public: + static const uint8_t AGENDA_CAPACITY = 35; + explicit AgendaService(Pinetime::System::SystemTask& system); void Init(); @@ -48,8 +50,8 @@ namespace Pinetime { struct ble_gatt_chr_def characteristicDefinition[2]; struct ble_gatt_svc_def serviceDefinition[2]; - uint32_t m_agenda_times[16]; - std::string m_agenda_pieces[16]; + uint32_t m_agenda_times[AGENDA_CAPACITY]; + std::string m_agenda_pieces[AGENDA_CAPACITY]; Pinetime::System::SystemTask& m_system; }; diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index 22f8402d..4aceb3af 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -325,7 +325,8 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) notificationManager, settingsController, heartRateController, - motionController); + motionController, + systemTask->nimble().agenda()); break; case Apps::Error: @@ -517,6 +518,9 @@ void DisplayApp::SetFullRefresh(DisplayApp::FullRefreshDirections direction) { case DisplayApp::FullRefreshDirections::RightAnim: lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::RightAnim); break; +// case DisplayApp::FullRefreshDirections::None: +// lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); +// break; default: break; } diff --git a/src/displayapp/screens/Agenda.cpp b/src/displayapp/screens/Agenda.cpp index 232e174c..5d91a8d6 100644 --- a/src/displayapp/screens/Agenda.cpp +++ b/src/displayapp/screens/Agenda.cpp @@ -1,9 +1,8 @@ #include "displayapp/screens/Agenda.h" -#include "displayapp/DisplayApp.h" -#include "components/ble/AgendaService.h" #include #include +using Pinetime::Applications::TouchEvents; using namespace Pinetime::Applications::Screens; using namespace Pinetime::Controllers; @@ -11,7 +10,7 @@ using namespace Pinetime::Controllers; Agenda::Agenda(DisplayApp* app, AgendaService& agenda, Controllers::DateTime& dateTimeController) : Screen(app), agendaSvc(agenda), dateTimeController(dateTimeController) { lv_obj_t* alignNextTo = lv_scr_act(); - for (uint8_t i = 0; i < 4; ++i) { + for (uint8_t i = 0; i < AGENDA_ONSCREEN; ++i) { lv_obj_t* agendaItem = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text_static(agendaItem, ".\n."); //lv_label_set_align(agendaItem, LV_LABEL_ALIGN_CENTER); @@ -25,26 +24,76 @@ Agenda::Agenda(DisplayApp* app, AgendaService& agenda, Controllers::DateTime& da pagingOffset = 0; - Refresh(); + using namespace date; + using namespace std::chrono; + + std::chrono::system_clock::time_point nowTp = dateTimeController.CurrentDateTime(); + //sys_seconds now = nowTp; + + // Find first entry that is just after the current time (- 5 min)! + for (uint8_t i = 0; i < AgendaService::AGENDA_CAPACITY; ++i) { + uint32_t time = agendaSvc.getAgendaTime(i); + sys_seconds timestamp = sys_days{1970_y/1/1} + seconds{time}; + + if (timestamp > nowTp - minutes{5}) { + uint8_t positionInPage = i % AGENDA_ONSCREEN; + pagingOffset = i - positionInPage; + break; + } + } + + taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); + Relabel(); + //Refresh(); +} + +bool Agenda::OnTouchEvent(TouchEvents event) { +// NRF_LOG_INFO("touch"); + switch (event) { + case TouchEvents::SwipeDown: + if (pagingOffset >= AGENDA_ONSCREEN) { +// NRF_LOG_INFO("swdn"); + pagingOffset -= AGENDA_ONSCREEN; + app->SetFullRefresh(DisplayApp::FullRefreshDirections::Down); + Relabel(); + } + break; + + case TouchEvents::SwipeUp: + if (pagingOffset + 2*AGENDA_ONSCREEN <= AgendaService::AGENDA_CAPACITY) { +// NRF_LOG_INFO("swup"); + pagingOffset += AGENDA_ONSCREEN; + app->SetFullRefresh(DisplayApp::FullRefreshDirections::Up); + Relabel(); + } + break; + + default: + break; + + } + + return true; } Agenda::~Agenda() { + lv_task_del(taskRefresh); lv_obj_clean(lv_scr_act()); } -void Agenda::Refresh() { +void Agenda::Relabel() { using namespace date; using namespace std::chrono; std::chrono::system_clock::time_point now = dateTimeController.CurrentDateTime(); auto todayDate = floor(now); - auto nowTimePart = make_time(now - todayDate); + //auto nowTimePart = make_time(now - todayDate); - auto todayYmd = year_month_day(todayDate); + //auto todayYmd = year_month_day(todayDate); // NRF_LOG_INFO("date CURRENT %d-%u-%u %ldh%ldh%ld", (int) todayYmd.year(), (unsigned) todayYmd.month(), (unsigned) todayYmd.day(), nowTimePart.hours().count(), nowTimePart.minutes().count(), nowTimePart.seconds().count()); //std::cout << todayDate << ' ' << nowTimePart << '\n'; - for (uint8_t i = 0; i < 4; ++i) { + for (uint8_t i = 0; i < AGENDA_ONSCREEN; ++i) { uint8_t itemIdx = i + pagingOffset; uint32_t time = agendaSvc.getAgendaTime(itemIdx); @@ -91,3 +140,8 @@ void Agenda::Refresh() { } } } + +/* Refresh seems to refer to re-render, but changing any LVGL widgets triggers a refresh again! */ +void Agenda::Refresh() { + //NRF_LOG_INFO("refr"); +} diff --git a/src/displayapp/screens/Agenda.h b/src/displayapp/screens/Agenda.h index 1e180d19..cd625b78 100644 --- a/src/displayapp/screens/Agenda.h +++ b/src/displayapp/screens/Agenda.h @@ -1,30 +1,39 @@ #pragma once #include "displayapp/screens/Screen.h" +#include "components/ble/AgendaService.h" +#include "displayapp/DisplayApp.h" #include "components/datetime/DateTimeController.h" #include namespace Pinetime { namespace Controllers { - class AgendaService; class DateTime; } namespace Applications { namespace Screens { class Agenda : public Screen { public: + static const uint8_t AGENDA_ONSCREEN = 5; + Agenda(DisplayApp* app, Pinetime::Controllers::AgendaService& agendaSvc, Pinetime::Controllers::DateTime &dateTimeController); ~Agenda() override; + bool OnTouchEvent(TouchEvents event) override; + void Refresh() override; + void Relabel(); + private: Pinetime::Controllers::AgendaService& agendaSvc; Controllers::DateTime& dateTimeController; - lv_obj_t* agendaItems[4]; + lv_obj_t* agendaItems[AGENDA_ONSCREEN]; uint8_t pagingOffset; + + lv_task_t* taskRefresh; }; } } diff --git a/src/displayapp/screens/Clock.cpp b/src/displayapp/screens/Clock.cpp index 1687dccf..f8381c81 100644 --- a/src/displayapp/screens/Clock.cpp +++ b/src/displayapp/screens/Clock.cpp @@ -22,7 +22,8 @@ Clock::Clock(DisplayApp* app, Controllers::NotificationManager& notificatioManager, Controllers::Settings& settingsController, Controllers::HeartRateController& heartRateController, - Controllers::MotionController& motionController) + Controllers::MotionController& motionController, + Controllers::AgendaService& agendaService) : Screen(app), dateTimeController {dateTimeController}, batteryController {batteryController}, @@ -31,6 +32,7 @@ Clock::Clock(DisplayApp* app, settingsController {settingsController}, heartRateController {heartRateController}, motionController {motionController}, + agendaService {agendaService}, screen {[this, &settingsController]() { switch (settingsController.GetClockFace()) { case 0: @@ -71,7 +73,8 @@ std::unique_ptr Clock::WatchFaceDigitalScreen() { notificatioManager, settingsController, heartRateController, - motionController); + motionController, + agendaService); } std::unique_ptr Clock::WatchFaceAnalogScreen() { diff --git a/src/displayapp/screens/Clock.h b/src/displayapp/screens/Clock.h index 1ba752c7..95aed510 100644 --- a/src/displayapp/screens/Clock.h +++ b/src/displayapp/screens/Clock.h @@ -15,6 +15,7 @@ namespace Pinetime { class Ble; class NotificationManager; class MotionController; + class AgendaService; } namespace Applications { @@ -28,7 +29,8 @@ namespace Pinetime { Controllers::NotificationManager& notificatioManager, Controllers::Settings& settingsController, Controllers::HeartRateController& heartRateController, - Controllers::MotionController& motionController); + Controllers::MotionController& motionController, + Controllers::AgendaService& agendaService); ~Clock() override; bool OnTouchEvent(TouchEvents event) override; @@ -42,6 +44,7 @@ namespace Pinetime { Controllers::Settings& settingsController; Controllers::HeartRateController& heartRateController; Controllers::MotionController& motionController; + Controllers::AgendaService& agendaService; std::unique_ptr screen; std::unique_ptr WatchFaceDigitalScreen(); diff --git a/src/displayapp/screens/WatchFaceDigital.cpp b/src/displayapp/screens/WatchFaceDigital.cpp index d10f8532..1ebb9577 100644 --- a/src/displayapp/screens/WatchFaceDigital.cpp +++ b/src/displayapp/screens/WatchFaceDigital.cpp @@ -1,5 +1,6 @@ #include "displayapp/screens/WatchFaceDigital.h" +#include #include #include #include @@ -22,7 +23,8 @@ WatchFaceDigital::WatchFaceDigital(DisplayApp* app, Controllers::NotificationManager& notificatioManager, Controllers::Settings& settingsController, Controllers::HeartRateController& heartRateController, - Controllers::MotionController& motionController) + Controllers::MotionController& motionController, + Controllers::AgendaService& agendaService) : Screen(app), currentDateTime {{}}, dateTimeController {dateTimeController}, @@ -31,7 +33,8 @@ WatchFaceDigital::WatchFaceDigital(DisplayApp* app, notificatioManager {notificatioManager}, settingsController {settingsController}, heartRateController {heartRateController}, - motionController {motionController} { + motionController {motionController}, + agendaService {agendaService} { batteryIcon.Create(lv_scr_act()); lv_obj_align(batteryIcon.GetObject(), lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, 0, 0); @@ -84,6 +87,19 @@ WatchFaceDigital::WatchFaceDigital(DisplayApp* app, lv_label_set_text_static(stepIcon, Symbols::shoe); lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0); + + agendaEntries[0] = lv_label_create(lv_scr_act(), nullptr); + lv_obj_align(agendaEntries[0], lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 0, 0); + lv_obj_set_style_local_text_color(agendaEntries[0], LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x332222)); + lv_label_set_text_static(agendaEntries[0], ".\n."); + + + agendaEntries[1] = lv_label_create(lv_scr_act(), nullptr); + lv_obj_align(agendaEntries[1], agendaEntries[0], LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0); + lv_obj_set_style_local_text_color(agendaEntries[1], LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x332222)); + lv_label_set_text_static(agendaEntries[1], ".\n."); + + taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); Refresh(); } @@ -93,6 +109,89 @@ WatchFaceDigital::~WatchFaceDigital() { lv_obj_clean(lv_scr_act()); } +void WatchFaceDigital::RefreshMiniAgenda() { + using namespace date; + using namespace std::chrono; + + // Set our offset to just before the newest event + // We only show events destined for today. + + bool found = false; + uint8_t firstFutureEvent = 0; + + std::chrono::system_clock::time_point now = dateTimeController.CurrentDateTime(); + auto today = floor(now); + + // Find first entry that is just after the current time (- 5 min)! + for (uint8_t i = 0; i < Pinetime::Controllers::AgendaService::AGENDA_CAPACITY; ++i) { + uint32_t time = agendaService.getAgendaTime(i); + firstFutureEvent = i; + if (time == 0) break; + sys_seconds timestamp = sys_days{1970_y/1/1} + seconds{time}; + sys_days dayOfEvent = floor(timestamp); + + if (dayOfEvent > today) { + break; + } + + if (timestamp > now) { + found = true; + break; + } + } + + bool suitable[2] = {false, found}; + + if (firstFutureEvent != 0) { + uint32_t time = agendaService.getAgendaTime(firstFutureEvent - 1); + if (time != 0) { + sys_seconds timestamp = sys_days{1970_y/1/1} + seconds{time}; + + sys_days dayOfEvent = floor(timestamp); + + if (dayOfEvent == today) { + suitable[0] = true; + } + + } + // No earlier events in the day. + lv_label_set_text_static(agendaEntries[0], "--h--\n ....."); + lv_obj_set_style_local_text_color(agendaEntries[0], LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x332222)); + } + + for (uint8_t i = 0; i < 2; ++i) { + if (! suitable[i]) { + lv_label_set_text_static(agendaEntries[i], "--h--\n ....."); + lv_obj_set_style_local_text_color(agendaEntries[i], LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x332222)); + continue; + } + + uint8_t itemIdx = firstFutureEvent - (1 - i); + + uint32_t time = agendaService.getAgendaTime(itemIdx); + + sys_seconds timestamp = sys_days{1970_y/1/1} + seconds{time}; + //auto timestamp = std::chrono::system_clock::time_point{time * 1s}; + sys_days agendaDate = floor(timestamp); + time_of_day agendaTimePart = make_time(timestamp - agendaDate); + + lv_label_set_text_fmt(agendaEntries[i], "%02lldh%02lld\n %s", agendaTimePart.hours().count(), agendaTimePart.minutes().count(), agendaService.getAgendaPiece(itemIdx).data()); + + if (timestamp + 30min < now) { + lv_obj_set_style_local_text_color(agendaEntries[i], LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x334433)); + } else if (timestamp + 5min < now) { + lv_obj_set_style_local_text_color(agendaEntries[i], LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FFE7)); + } else if (timestamp - 5min < now && now < timestamp + 5min) { + lv_obj_set_style_local_text_color(agendaEntries[i], LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFA500)); + } else if (now + 5min < timestamp) { + lv_obj_set_style_local_text_color(agendaEntries[i], LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFF00)); + } else if (now + 30min < timestamp) { + lv_obj_set_style_local_text_color(agendaEntries[i], LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFFFFFF)); + } + } + +} + void WatchFaceDigital::Refresh() { powerPresent = batteryController.IsPowerPresent(); if (powerPresent.IsUpdated()) { @@ -136,6 +235,8 @@ void WatchFaceDigital::Refresh() { uint8_t minute = time.minutes().count(); if (displayedHour != hour || displayedMinute != minute) { + RefreshMiniAgenda(); + displayedHour = hour; displayedMinute = minute; diff --git a/src/displayapp/screens/WatchFaceDigital.h b/src/displayapp/screens/WatchFaceDigital.h index bd27806f..65199b6c 100644 --- a/src/displayapp/screens/WatchFaceDigital.h +++ b/src/displayapp/screens/WatchFaceDigital.h @@ -17,6 +17,7 @@ namespace Pinetime { class NotificationManager; class HeartRateController; class MotionController; + class AgendaService; } namespace Applications { @@ -31,11 +32,14 @@ namespace Pinetime { Controllers::NotificationManager& notificatioManager, Controllers::Settings& settingsController, Controllers::HeartRateController& heartRateController, - Controllers::MotionController& motionController); + Controllers::MotionController& motionController, + Controllers::AgendaService& agendaService); ~WatchFaceDigital() override; void Refresh() override; + void RefreshMiniAgenda(); + private: uint8_t displayedHour = -1; uint8_t displayedMinute = -1; @@ -67,6 +71,8 @@ namespace Pinetime { lv_obj_t* stepValue; lv_obj_t* notificationIcon; + lv_obj_t* agendaEntries[2]; + BatteryIcon batteryIcon; Controllers::DateTime& dateTimeController; @@ -76,6 +82,7 @@ namespace Pinetime { Controllers::Settings& settingsController; Controllers::HeartRateController& heartRateController; Controllers::MotionController& motionController; + Controllers::AgendaService& agendaService; lv_task_t* taskRefresh; };