From 936bd77c79a56297483748b891586d2af091f43d Mon Sep 17 00:00:00 2001 From: Zorvalt Date: Fri, 12 Mar 2021 22:33:19 +0100 Subject: [PATCH 01/12] CalendarManager + test screen + ble service --- src/CMakeLists.txt | 6 + src/components/ble/CalendarManager.cpp | 147 +++++++++++++++++++++ src/components/ble/CalendarManager.h | 98 ++++++++++++++ src/components/ble/CalendarService.cpp | 74 +++++++++++ src/components/ble/CalendarService.h | 48 +++++++ src/components/ble/NimbleController.cpp | 4 + src/components/ble/NimbleController.h | 3 + src/displayapp/Apps.h | 1 + src/displayapp/DisplayApp.cpp | 7 + src/displayapp/DisplayApp.h | 3 + src/displayapp/screens/ApplicationList.cpp | 12 +- src/displayapp/screens/ApplicationList.h | 4 +- src/displayapp/screens/Timeline.cpp | 77 +++++++++++ src/displayapp/screens/Timeline.h | 26 ++++ src/main.cpp | 3 + src/systemtask/SystemTask.cpp | 3 + src/systemtask/SystemTask.h | 2 + 17 files changed, 511 insertions(+), 7 deletions(-) create mode 100644 src/components/ble/CalendarManager.cpp create mode 100644 src/components/ble/CalendarManager.h create mode 100644 src/components/ble/CalendarService.cpp create mode 100644 src/components/ble/CalendarService.h create mode 100644 src/displayapp/screens/Timeline.cpp create mode 100644 src/displayapp/screens/Timeline.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ff0c9b0cf8..1edb35073a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -435,6 +435,7 @@ list(APPEND SOURCE_FILES displayapp/screens/Error.cpp displayapp/screens/Alarm.cpp displayapp/screens/Styles.cpp + displayapp/screens/Timeline.cpp displayapp/Colors.cpp ## Settings @@ -495,6 +496,8 @@ list(APPEND SOURCE_FILES components/ble/ServiceDiscovery.cpp components/ble/HeartRateService.cpp components/ble/MotionService.cpp + components/ble/CalendarService.cpp + components/ble/CalendarManager.cpp components/firmwarevalidator/FirmwareValidator.cpp components/motor/MotorController.cpp components/settings/Settings.cpp @@ -647,6 +650,7 @@ set(INCLUDE_FILES displayapp/screens/Motion.h displayapp/screens/Timer.h displayapp/screens/Alarm.h + displayapp/screens/Timeline.h displayapp/Colors.h drivers/St7789.h drivers/SpiNorFlash.h @@ -683,6 +687,8 @@ set(INCLUDE_FILES components/ble/HeartRateService.h components/ble/MotionService.h components/ble/weather/WeatherService.h + components/ble/CalendarManager.h + components/ble/CalendarService.h components/settings/Settings.h components/timer/TimerController.h components/alarm/AlarmController.h diff --git a/src/components/ble/CalendarManager.cpp b/src/components/ble/CalendarManager.cpp new file mode 100644 index 0000000000..822a69236d --- /dev/null +++ b/src/components/ble/CalendarManager.cpp @@ -0,0 +1,147 @@ +#include "CalendarManager.h" + +using namespace Pinetime::Controllers; + +/** + * Determines if the event `event1` should be placed before event `event2` + * @param event1 The event to be inserted + * @param event2 The comparison event + * @return + */ +bool CalendarManager::isBefore(CalendarEvent& event1, CalendarEvent& event2) { + // TODO Use date instead of id for chronological order + return event1.id < event2.id; +} + +CalendarManager::CalendarManager() { + // TODO Load events from filesystem ? + + // Init memory pool linked list + itemsPool[0].prev = nullptr; + for (int i = 1; i < CAPACITY; ++i) { + itemsPool[i].prev = &itemsPool[i - 1]; + itemsPool[i - 1].next = &itemsPool[i]; + } + itemsPool[CAPACITY - 1].next = nullptr; +} + +/** + * Inserts a new event if enough space + * @param event The event to insert + * @return true if the operation succeeded (enough space), false otherwise + */ +bool CalendarManager::addEvent(CalendarEvent& event) { + if (mCount >= CAPACITY) { + return false; + } + + // Init event + auto* item = mFreePoolHead; + mFreePoolHead = mFreePoolHead->next; + item->event = event; + + // Insert event + auto* it = mEventsHead; + // If list is empty + if (it == nullptr) { + item->prev = nullptr; + item->next = nullptr; + mEventsHead = item; + } + // Else if insert in first place + else if (isBefore(event, it->event)) { + item->prev = nullptr; + item->next = mEventsHead; + mEventsHead->prev = item; + mEventsHead = item; + } else { + while (it->next != nullptr && !isBefore(event, it->event)) { + it = it->next; + } + + if (isBefore(event, it->event)) { + item->prev = it->prev; + item->next = it; + it->prev->next = item; + it->prev = item; + } else { + item->next = nullptr; + item->prev = it; + it->next = item; + } + } + + mCount++; + return true; +} + +/** + * Removes the event corresponding to the given id if found + * @param id The id of the event to remove + * @return true if the event is found, false otherwise + */ +bool CalendarManager::deleteEvent(CalendarManager::CalendarEvent::Id id) { + auto* item = mEventsHead; + if (item == nullptr) { + return false; + } + + // Special case for deleting the first element + if (item->event.id == id) { + // Delete item from event list + mEventsHead = item->next; + if (mEventsHead != nullptr) { + mEventsHead->prev = nullptr; + } + + // Add item's memory back to free pool + if (mFreePoolHead != nullptr) { + mFreePoolHead->prev = item; + } + item->next = mFreePoolHead; + mFreePoolHead = item; + + mCount--; + return true; + } + + // Searching for a matching event + while (item != nullptr) { + if (item->event.id == id) { + // Delete item from event list + item->prev->next = item->next; + if (item->next != nullptr) { + item->next->prev = item->prev; + } + + // Add item's memory back to free pool + if (mFreePoolHead != nullptr) { + mFreePoolHead->prev = item; + } + item->next = mFreePoolHead; + mFreePoolHead = item; + + mCount--; + return true; + } + item = item->next; + } + + return false; +} + +CalendarManager::CalendarIterator CalendarManager::begin() { + return CalendarIterator(mEventsHead); +} + +CalendarManager::CalendarIterator CalendarManager::end() { + return CalendarIterator(nullptr); +} + +bool CalendarManager::empty() const { + return mEventsHead == nullptr; +} + +uint8_t CalendarManager::getCount() const { + return mCount; +} diff --git a/src/components/ble/CalendarManager.h b/src/components/ble/CalendarManager.h new file mode 100644 index 0000000000..0571bb7324 --- /dev/null +++ b/src/components/ble/CalendarManager.h @@ -0,0 +1,98 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace Pinetime { + namespace Controllers { + class CalendarManager { + public: + static constexpr uint8_t CAPACITY = 15; + static constexpr uint8_t TITLE_SIZE {100}; + struct CalendarEvent { + using Id = uint8_t; + Id id; + std::string title; + }; + + private: + static bool isBefore(CalendarEvent& event1, CalendarEvent& event2); + struct CalendarItem { + CalendarEvent event; + CalendarItem* prev; + CalendarItem* next; + }; + uint8_t mCount = 0; + CalendarItem itemsPool[CAPACITY] {}; + CalendarItem* mEventsHead = nullptr; + CalendarItem* mFreePoolHead = &itemsPool[0]; + + public: + class CalendarIterator : public std::iterator { + friend class CalendarManager; + CalendarIterator(CalendarItem* i) : item(i) { + } + + public: + CalendarIterator() : item(nullptr) { + } + + CalendarIterator(const CalendarIterator& i) : item(i.item) { + } + + CalendarIterator& operator--() { + item = item->prev; + return *this; + } + + CalendarIterator operator--(int) { + auto tmp {*this}; + operator--(); + return tmp; + } + + CalendarIterator& operator++() { + item = item->next; + return *this; + } + + CalendarIterator operator++(int) { + auto tmp {*this}; + operator++(); + return tmp; + } + + CalendarIterator& operator=(const CalendarIterator& i) { + item = i.item; + return *this; + } + + bool operator==(const CalendarIterator& i) { + return item == i.item; + } + + bool operator!=(const CalendarIterator& i) { + return item != i.item; + } + + CalendarEvent const& operator*() { + return item->event; + } + + private: + CalendarItem* item; + }; + + CalendarManager(); + bool addEvent(CalendarEvent& event); + bool deleteEvent(CalendarEvent::Id id); + CalendarIterator begin(); + static CalendarIterator end(); + bool empty() const; + uint8_t getCount() const; + }; + } +} \ No newline at end of file diff --git a/src/components/ble/CalendarService.cpp b/src/components/ble/CalendarService.cpp new file mode 100644 index 0000000000..0ae19737cd --- /dev/null +++ b/src/components/ble/CalendarService.cpp @@ -0,0 +1,74 @@ +#include +#include "CalendarService.h" + +using namespace Pinetime::Controllers; + +int CalCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) { + auto pCalendarEventService = static_cast(arg); + return pCalendarEventService->OnCommand(conn_handle, attr_handle, ctxt); +} + +CalendarService::CalendarService(Pinetime::System::SystemTask& system, CalendarManager& calendarManager) + : m_system(system), m_calendarManager(calendarManager) { + calUuid.value[14] = calId[0]; + calUuid.value[15] = calId[1]; + + calAddEventUuid.value[12] = calAddEventId[0]; + calAddEventUuid.value[13] = calAddEventId[1]; + calAddEventUuid.value[14] = calId[0]; + calAddEventUuid.value[15] = calId[1]; + + calDeleteEventUuid.value[12] = calDeleteEventId[0]; + calDeleteEventUuid.value[13] = calDeleteEventId[1]; + calDeleteEventUuid.value[14] = calId[0]; + calDeleteEventUuid.value[15] = calId[1]; + + characteristicDefinition[0] = { + .uuid = (ble_uuid_t*) (&calAddEventUuid), + .access_cb = CalCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_WRITE, + }; + characteristicDefinition[1] = { + .uuid = (ble_uuid_t*) (&calDeleteEventUuid), + .access_cb = CalCallback, + .arg = this, + .flags = BLE_GATT_CHR_F_WRITE, + }; + characteristicDefinition[2] = {0}; + + serviceDefinition[0] = { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = (ble_uuid_t*) &calUuid, + .characteristics = characteristicDefinition, + }; + serviceDefinition[1] = {0}; +} +void CalendarService::Init() { + int res = 0; + res = ble_gatts_count_cfg(serviceDefinition); + ASSERT(res == 0); + + res = ble_gatts_add_svcs(serviceDefinition); + ASSERT(res == 0); +} +int CalendarService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) { + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + size_t notifSize = OS_MBUF_PKTLEN(ctxt->om); + uint8_t data[notifSize + 1]; + data[notifSize] = '\0'; + os_mbuf_copydata(ctxt->om, 0, notifSize, data); + char* s = (char*) &data[0]; + NRF_LOG_INFO("DATA : %s", s); + if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &calAddEventUuid) == 0) { + CalendarManager::CalendarEvent event { + .id = s[0], + .title = &s[1], + }; + m_calendarManager.addEvent(event); + } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &calDeleteEventUuid) == 0) { + m_calendarManager.deleteEvent(s[0]); + } + } + return 0; +} diff --git a/src/components/ble/CalendarService.h b/src/components/ble/CalendarService.h new file mode 100644 index 0000000000..494524385a --- /dev/null +++ b/src/components/ble/CalendarService.h @@ -0,0 +1,48 @@ +#pragma once + +// 00000000-78fc-48fe-8e23-433b3a1942d0 +#include +#include +#include +#include "CalendarManager.h" + +#define CALENDAR_SERVICE_UUID_BASE \ + { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0x00, 0x00 } + +namespace Pinetime { + namespace System { + class SystemTask; + } + namespace Controllers { + + class CalendarService { + public: + explicit CalendarService(Pinetime::System::SystemTask& system, CalendarManager& calendarManager); + + void Init(); + + int OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt); + + void event(char event); + + static const char EVENT_CALENDAR_ADD = 0x01; + static const char EVENT_CALENDAR_DELETE = 0x02; + + private: + static constexpr uint8_t TotalNbEvents = 50; + + static constexpr uint8_t calId[2] = {0x00, 0x00}; + static constexpr uint8_t calAddEventId[2] = {0x01, 0x00}; + static constexpr uint8_t calDeleteEventId[2] = {0x02, 0x00}; + ble_uuid128_t calUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = CALENDAR_SERVICE_UUID_BASE}; + ble_uuid128_t calAddEventUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = CALENDAR_SERVICE_UUID_BASE}; + ble_uuid128_t calDeleteEventUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = CALENDAR_SERVICE_UUID_BASE}; + + struct ble_gatt_chr_def characteristicDefinition[3]; + struct ble_gatt_svc_def serviceDefinition[2]; + + Pinetime::System::SystemTask& m_system; + CalendarManager& m_calendarManager; + }; + } +}; diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index 0be7c0f7fb..b9adda8fde 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -16,6 +16,7 @@ #undef max #undef min #include "components/ble/BleController.h" +#include "components/ble/CalendarManager.h" #include "components/ble/NotificationManager.h" #include "components/datetime/DateTimeController.h" #include "components/fs/FS.h" @@ -25,6 +26,7 @@ using namespace Pinetime::Controllers; NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask, Ble& bleController, + CalendarManager& calendarManager, DateTime& dateTimeController, NotificationManager& notificationManager, Battery& batteryController, @@ -43,6 +45,7 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask, currentTimeClient {dateTimeController}, anService {systemTask, notificationManager}, alertNotificationClient {systemTask, notificationManager}, + calendarService{systemTask, calendarManager}, currentTimeService {dateTimeController}, musicService {systemTask}, weatherService {systemTask, dateTimeController}, @@ -88,6 +91,7 @@ void NimbleController::Init() { ble_svc_gatt_init(); deviceInformationService.Init(); + calendarService.Init(); currentTimeClient.Init(); currentTimeService.Init(); musicService.Init(); diff --git a/src/components/ble/NimbleController.h b/src/components/ble/NimbleController.h index ad1942121a..ff3a7b8c4f 100644 --- a/src/components/ble/NimbleController.h +++ b/src/components/ble/NimbleController.h @@ -10,6 +10,7 @@ #include "components/ble/AlertNotificationClient.h" #include "components/ble/AlertNotificationService.h" #include "components/ble/BatteryInformationService.h" +#include "components/ble/CalendarService.h" #include "components/ble/CurrentTimeClient.h" #include "components/ble/CurrentTimeService.h" #include "components/ble/DeviceInformationService.h" @@ -43,6 +44,7 @@ namespace Pinetime { public: NimbleController(Pinetime::System::SystemTask& systemTask, Ble& bleController, + CalendarManager& calendarManager, DateTime& dateTimeController, NotificationManager& notificationManager, Battery& batteryController, @@ -95,6 +97,7 @@ namespace Pinetime { CurrentTimeClient currentTimeClient; AlertNotificationService anService; AlertNotificationClient alertNotificationClient; + CalendarService calendarService; CurrentTimeService currentTimeService; MusicService musicService; WeatherService weatherService; diff --git a/src/displayapp/Apps.h b/src/displayapp/Apps.h index dc9e62536b..aaf292239e 100644 --- a/src/displayapp/Apps.h +++ b/src/displayapp/Apps.h @@ -25,6 +25,7 @@ namespace Pinetime { Metronome, Motion, Steps, + Timeline, Weather, PassKey, QuickSettings, diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index fdc6376c5d..7ced838935 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -25,6 +25,7 @@ #include "displayapp/screens/Notifications.h" #include "displayapp/screens/SystemInfo.h" #include "displayapp/screens/Tile.h" +#include "displayapp/screens/Timeline.h" #include "displayapp/screens/Twos.h" #include "displayapp/screens/FlashLight.h" #include "displayapp/screens/BatteryInfo.h" @@ -89,6 +90,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd, Drivers::Cst816S& touchPanel, Controllers::Battery& batteryController, Controllers::Ble& bleController, + Controllers::CalendarManager& calendarManager, Controllers::DateTime& dateTimeController, Drivers::WatchdogView& watchdog, Pinetime::Controllers::NotificationManager& notificationManager, @@ -105,6 +107,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd, touchPanel {touchPanel}, batteryController {batteryController}, bleController {bleController}, + calendarManager {calendarManager}, dateTimeController {dateTimeController}, watchdog {watchdog}, notificationManager {notificationManager}, @@ -482,6 +485,10 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) case Apps::Steps: currentScreen = std::make_unique(this, motionController, settingsController); break; + + case Apps::Timeline: + currentScreen = std::make_unique(this, calendarManager); + break; } currentApp = app; } diff --git a/src/displayapp/DisplayApp.h b/src/displayapp/DisplayApp.h index 1eaefaa106..c6f8691750 100644 --- a/src/displayapp/DisplayApp.h +++ b/src/displayapp/DisplayApp.h @@ -8,6 +8,7 @@ #include "displayapp/Apps.h" #include "displayapp/LittleVgl.h" #include "displayapp/TouchEvents.h" +#include "components/ble/CalendarManager.h" #include "components/brightness/BrightnessController.h" #include "components/motor/MotorController.h" #include "components/firmwarevalidator/FirmwareValidator.h" @@ -52,6 +53,7 @@ namespace Pinetime { Drivers::Cst816S&, Controllers::Battery& batteryController, Controllers::Ble& bleController, + Controllers::CalendarManager& calendarManager, Controllers::DateTime& dateTimeController, Drivers::WatchdogView& watchdog, Pinetime::Controllers::NotificationManager& notificationManager, @@ -78,6 +80,7 @@ namespace Pinetime { Pinetime::Drivers::Cst816S& touchPanel; Pinetime::Controllers::Battery& batteryController; Pinetime::Controllers::Ble& bleController; + Pinetime::Controllers::CalendarManager& calendarManager; Pinetime::Controllers::DateTime& dateTimeController; Pinetime::Drivers::WatchdogView& watchdog; Pinetime::System::SystemTask* systemTask = nullptr; diff --git a/src/displayapp/screens/ApplicationList.cpp b/src/displayapp/screens/ApplicationList.cpp index 29c8affbc6..80358c5bd5 100644 --- a/src/displayapp/screens/ApplicationList.cpp +++ b/src/displayapp/screens/ApplicationList.cpp @@ -25,7 +25,9 @@ ApplicationList::ApplicationList(Pinetime::Applications::DisplayApp* app, [this]() -> std::unique_ptr { return CreateScreen2(); }, - //[this]() -> std::unique_ptr { return CreateScreen3(); } + [this]() -> std::unique_ptr { + return CreateScreen3(); + } }, Screens::ScreenListModes::UpDown} { } @@ -64,16 +66,16 @@ std::unique_ptr ApplicationList::CreateScreen2() { return std::make_unique(1, 2, app, settingsController, batteryController, dateTimeController, applications); } -/*std::unique_ptr ApplicationList::CreateScreen3() { +std::unique_ptr ApplicationList::CreateScreen3() { std::array applications { - {{"A", Apps::Meter}, + {{"A", Apps::Timeline}, {"B", Apps::Navigation}, {"C", Apps::Clock}, {"D", Apps::Music}, {"E", Apps::SysInfo}, - {"F", Apps::Brightness} + {"F", Apps::Weather} } }; return std::make_unique(2, 3, app, settingsController, batteryController, dateTimeController, applications); -}*/ +} diff --git a/src/displayapp/screens/ApplicationList.h b/src/displayapp/screens/ApplicationList.h index f430a89ef1..27ebbac94d 100644 --- a/src/displayapp/screens/ApplicationList.h +++ b/src/displayapp/screens/ApplicationList.h @@ -25,10 +25,10 @@ namespace Pinetime { Pinetime::Controllers::Battery& batteryController; Controllers::DateTime& dateTimeController; - ScreenList<2> screens; + ScreenList<3> screens; std::unique_ptr CreateScreen1(); std::unique_ptr CreateScreen2(); - // std::unique_ptr CreateScreen3(); + std::unique_ptr CreateScreen3(); }; } } diff --git a/src/displayapp/screens/Timeline.cpp b/src/displayapp/screens/Timeline.cpp new file mode 100644 index 0000000000..6b657e2783 --- /dev/null +++ b/src/displayapp/screens/Timeline.cpp @@ -0,0 +1,77 @@ +#include "Timeline.h" + +#include +#include + +using namespace Pinetime::Applications::Screens; + +char superMegaStringForTesting[100]; + +Timeline::Timeline(DisplayApp* app, Controllers::CalendarManager& calendarManager) + : Screen(app), calendarManager(calendarManager) { + hello_world_label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text(hello_world_label, "Time to timeline :-)"); + lv_obj_set_auto_realign(hello_world_label, true); + lv_obj_align(hello_world_label, nullptr, LV_ALIGN_CENTER, 0, 0); + + for (int i = 0; i < 100; ++i) { + superMegaStringForTesting[i] = 0; + } +} +Timeline::~Timeline() { + lv_obj_clean(lv_scr_act()); +} + +bool Timeline::OnButtonPushed() { + running = false; + return true; +} +bool Timeline::OnTouchEvent(TouchEvents event) { + switch (event) { + case TouchEvents::SwipeUp: { + auto id = calendarManager.getCount(); + Controllers::CalendarManager::CalendarEvent tmpEvent {.id = id, .title = {"title"}}; + bool result = calendarManager.addEvent(tmpEvent); + sprintf(superMegaStringForTesting, "Added e%d: %d", id, result); + lv_label_set_text(hello_world_label, superMegaStringForTesting); + } break; + + case TouchEvents::SwipeDown: { + auto tmpEvent = *calendarManager.begin(); + bool result = calendarManager.deleteEvent(tmpEvent.id); + sprintf(superMegaStringForTesting, "Deleted e%d: %d", tmpEvent.id, result); + lv_label_set_text(hello_world_label, superMegaStringForTesting); + } break; + + case TouchEvents::SwipeLeft: { + auto nbEvents = calendarManager.getCount(); + sprintf(superMegaStringForTesting, "Nb events: %d", nbEvents); + lv_label_set_text(hello_world_label, superMegaStringForTesting); + } break; + + case TouchEvents::SwipeRight: { + if (calendarManager.empty()) { + strcpy(superMegaStringForTesting, "No event"); + } else { + strcpy(superMegaStringForTesting, "Evts: "); + auto tmpEvent = calendarManager.begin(); + int offset = 6; + while (tmpEvent != calendarManager.end()) { + superMegaStringForTesting[offset] = 48+(*tmpEvent).id; + superMegaStringForTesting[offset+1] = ','; + offset += 2; + tmpEvent++; + } + superMegaStringForTesting[offset] = '\0'; + } + lv_label_set_text(hello_world_label, superMegaStringForTesting); + } break; + + default: + return false; + } + return true; +} +bool Timeline::OnTouchEvent(uint16_t x, uint16_t y) { + return Screen::OnTouchEvent(x, y); +} diff --git a/src/displayapp/screens/Timeline.h b/src/displayapp/screens/Timeline.h new file mode 100644 index 0000000000..4605553ea7 --- /dev/null +++ b/src/displayapp/screens/Timeline.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include "Screen.h" + +namespace Pinetime { + namespace Applications { + namespace Screens { + class Timeline : public Screen { + public: + explicit Timeline(DisplayApp* app, Controllers::CalendarManager& calendarManager); + ~Timeline() override; + bool OnButtonPushed() override; + bool OnTouchEvent(TouchEvents event) override; + bool OnTouchEvent(uint16_t x, uint16_t y) override; + + private: + Controllers::CalendarManager& calendarManager; + bool running = true; + lv_obj_t * hello_world_label; + }; + } + } +} diff --git a/src/main.cpp b/src/main.cpp index fa492d08e0..0152317063 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -116,12 +116,14 @@ Pinetime::Controllers::AlarmController alarmController {dateTimeController}; Pinetime::Controllers::TouchHandler touchHandler(touchPanel, lvgl); Pinetime::Controllers::ButtonHandler buttonHandler; Pinetime::Controllers::BrightnessController brightnessController {}; +Pinetime::Controllers::CalendarManager calendarManager {}; Pinetime::Applications::DisplayApp displayApp(lcd, lvgl, touchPanel, batteryController, bleController, + calendarManager, dateTimeController, watchdogView, notificationManager, @@ -142,6 +144,7 @@ Pinetime::System::SystemTask systemTask(spi, lvgl, batteryController, bleController, + calendarManager, dateTimeController, timerController, alarmController, diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 1e45fac194..143a1df706 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -54,6 +54,7 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi, Components::LittleVgl& lvgl, Controllers::Battery& batteryController, Controllers::Ble& bleController, + Controllers::CalendarManager& calendarManager, Controllers::DateTime& dateTimeController, Controllers::TimerController& timerController, Controllers::AlarmController& alarmController, @@ -78,6 +79,7 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi, lvgl {lvgl}, batteryController {batteryController}, bleController {bleController}, + calendarManager {calendarManager}, dateTimeController {dateTimeController}, timerController {timerController}, alarmController {alarmController}, @@ -96,6 +98,7 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi, buttonHandler {buttonHandler}, nimbleController(*this, bleController, + calendarManager, dateTimeController, notificationManager, batteryController, diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index c5b0379240..968c5edec1 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -61,6 +61,7 @@ namespace Pinetime { Components::LittleVgl& lvgl, Controllers::Battery& batteryController, Controllers::Ble& bleController, + Controllers::CalendarManager& calendarManager, Controllers::DateTime& dateTimeController, Controllers::TimerController& timerController, Controllers::AlarmController& alarmController, @@ -106,6 +107,7 @@ namespace Pinetime { Pinetime::Controllers::Battery& batteryController; Pinetime::Controllers::Ble& bleController; + Pinetime::Controllers::CalendarManager& calendarManager; Pinetime::Controllers::DateTime& dateTimeController; Pinetime::Controllers::TimerController& timerController; Pinetime::Controllers::AlarmController& alarmController; From ed396dd17147b318b499b7b8cfc5a0e0e49658ad Mon Sep 17 00:00:00 2001 From: Zorvalt Date: Sat, 13 Mar 2021 00:15:41 +0100 Subject: [PATCH 02/12] Fix timeline uuids --- src/components/ble/CalendarService.cpp | 8 ++++---- src/components/ble/CalendarService.h | 25 ++++++++++++++++--------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/components/ble/CalendarService.cpp b/src/components/ble/CalendarService.cpp index 0ae19737cd..5db77a1d7a 100644 --- a/src/components/ble/CalendarService.cpp +++ b/src/components/ble/CalendarService.cpp @@ -13,13 +13,13 @@ CalendarService::CalendarService(Pinetime::System::SystemTask& system, CalendarM calUuid.value[14] = calId[0]; calUuid.value[15] = calId[1]; - calAddEventUuid.value[12] = calAddEventId[0]; - calAddEventUuid.value[13] = calAddEventId[1]; + calAddEventUuid.value[12] = calAddEventCharId[0]; + calAddEventUuid.value[13] = calAddEventCharId[1]; calAddEventUuid.value[14] = calId[0]; calAddEventUuid.value[15] = calId[1]; - calDeleteEventUuid.value[12] = calDeleteEventId[0]; - calDeleteEventUuid.value[13] = calDeleteEventId[1]; + calDeleteEventUuid.value[12] = calDeleteEventCharId[0]; + calDeleteEventUuid.value[13] = calDeleteEventCharId[1]; calDeleteEventUuid.value[14] = calId[0]; calDeleteEventUuid.value[15] = calId[1]; diff --git a/src/components/ble/CalendarService.h b/src/components/ble/CalendarService.h index 494524385a..0706d2e3f1 100644 --- a/src/components/ble/CalendarService.h +++ b/src/components/ble/CalendarService.h @@ -7,7 +7,7 @@ #include "CalendarManager.h" #define CALENDAR_SERVICE_UUID_BASE \ - { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0x00, 0x00 } + { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0x04, 0x00 } namespace Pinetime { namespace System { @@ -29,14 +29,21 @@ namespace Pinetime { static const char EVENT_CALENDAR_DELETE = 0x02; private: - static constexpr uint8_t TotalNbEvents = 50; - - static constexpr uint8_t calId[2] = {0x00, 0x00}; - static constexpr uint8_t calAddEventId[2] = {0x01, 0x00}; - static constexpr uint8_t calDeleteEventId[2] = {0x02, 0x00}; - ble_uuid128_t calUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = CALENDAR_SERVICE_UUID_BASE}; - ble_uuid128_t calAddEventUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = CALENDAR_SERVICE_UUID_BASE}; - ble_uuid128_t calDeleteEventUuid {.u = {.type = BLE_UUID_TYPE_128}, .value = CALENDAR_SERVICE_UUID_BASE}; + static constexpr uint8_t calId[2] = {0x04, 0x00}; + static constexpr uint8_t calAddEventCharId[2] = {0x01, 0x00}; + static constexpr uint8_t calDeleteEventCharId[2] = {0x02, 0x00}; + ble_uuid128_t calUuid { + .u = {.type = BLE_UUID_TYPE_128}, + .value = CALENDAR_SERVICE_UUID_BASE, + }; + ble_uuid128_t calAddEventUuid { + .u = {.type = BLE_UUID_TYPE_128}, + .value = CALENDAR_SERVICE_UUID_BASE, + }; + ble_uuid128_t calDeleteEventUuid { + .u = {.type = BLE_UUID_TYPE_128}, + .value = CALENDAR_SERVICE_UUID_BASE, + }; struct ble_gatt_chr_def characteristicDefinition[3]; struct ble_gatt_svc_def serviceDefinition[2]; From 244311222aeb20017b58eb68be0ae4912b06c26c Mon Sep 17 00:00:00 2001 From: Zorvalt Date: Sat, 13 Mar 2021 00:16:32 +0100 Subject: [PATCH 03/12] Timeline app displays event titles --- src/displayapp/screens/Timeline.cpp | 41 ++++++++++++++++++++--------- src/displayapp/screens/Timeline.h | 3 +++ 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/displayapp/screens/Timeline.cpp b/src/displayapp/screens/Timeline.cpp index 6b657e2783..5f12963e02 100644 --- a/src/displayapp/screens/Timeline.cpp +++ b/src/displayapp/screens/Timeline.cpp @@ -17,6 +17,8 @@ Timeline::Timeline(DisplayApp* app, Controllers::CalendarManager& calendarManage for (int i = 0; i < 100; ++i) { superMegaStringForTesting[i] = 0; } + + currentEvent = calendarManager.begin(); } Timeline::~Timeline() { lv_obj_clean(lv_scr_act()); @@ -26,21 +28,29 @@ bool Timeline::OnButtonPushed() { running = false; return true; } + bool Timeline::OnTouchEvent(TouchEvents event) { switch (event) { case TouchEvents::SwipeUp: { - auto id = calendarManager.getCount(); - Controllers::CalendarManager::CalendarEvent tmpEvent {.id = id, .title = {"title"}}; - bool result = calendarManager.addEvent(tmpEvent); - sprintf(superMegaStringForTesting, "Added e%d: %d", id, result); - lv_label_set_text(hello_world_label, superMegaStringForTesting); + auto calBegin = calendarManager.begin(); + if (currentEvent == calendarManager.end()) { + currentEvent = calBegin; + } else { + currentEvent--; + } + + displayCurrent(); } break; case TouchEvents::SwipeDown: { - auto tmpEvent = *calendarManager.begin(); - bool result = calendarManager.deleteEvent(tmpEvent.id); - sprintf(superMegaStringForTesting, "Deleted e%d: %d", tmpEvent.id, result); - lv_label_set_text(hello_world_label, superMegaStringForTesting); + auto calBegin = calendarManager.begin(); + if (currentEvent == calendarManager.end()) { + currentEvent = calBegin; + } else { + currentEvent++; + } + + displayCurrent(); } break; case TouchEvents::SwipeLeft: { @@ -57,9 +67,7 @@ bool Timeline::OnTouchEvent(TouchEvents event) { auto tmpEvent = calendarManager.begin(); int offset = 6; while (tmpEvent != calendarManager.end()) { - superMegaStringForTesting[offset] = 48+(*tmpEvent).id; - superMegaStringForTesting[offset+1] = ','; - offset += 2; + offset += sprintf(&superMegaStringForTesting[offset], "%d,", (*tmpEvent).id); tmpEvent++; } superMegaStringForTesting[offset] = '\0'; @@ -75,3 +83,12 @@ bool Timeline::OnTouchEvent(TouchEvents event) { bool Timeline::OnTouchEvent(uint16_t x, uint16_t y) { return Screen::OnTouchEvent(x, y); } +void Timeline::displayCurrent() { + if (currentEvent == calendarManager.end()) { + lv_label_set_text(hello_world_label, "No event"); + } else { + auto event = *currentEvent; + sprintf(superMegaStringForTesting, "e%d: %s", event.id, event.title.c_str()); + lv_label_set_text(hello_world_label, superMegaStringForTesting); + } +} diff --git a/src/displayapp/screens/Timeline.h b/src/displayapp/screens/Timeline.h index 4605553ea7..8704b55fe8 100644 --- a/src/displayapp/screens/Timeline.h +++ b/src/displayapp/screens/Timeline.h @@ -17,7 +17,10 @@ namespace Pinetime { bool OnTouchEvent(uint16_t x, uint16_t y) override; private: + void displayCurrent(); + Controllers::CalendarManager& calendarManager; + Controllers::CalendarManager::CalendarIterator currentEvent; bool running = true; lv_obj_t * hello_world_label; }; From b4fda1ffd3b19e0e5f88acf0df16c6858e318a8c Mon Sep 17 00:00:00 2001 From: Zorvalt Date: Sat, 13 Mar 2021 01:38:00 +0100 Subject: [PATCH 04/12] Add timestamp and duration to events --- src/components/ble/CalendarManager.cpp | 3 +-- src/components/ble/CalendarManager.h | 2 ++ src/components/ble/CalendarService.cpp | 8 +++++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/ble/CalendarManager.cpp b/src/components/ble/CalendarManager.cpp index 822a69236d..f6c7eb9833 100644 --- a/src/components/ble/CalendarManager.cpp +++ b/src/components/ble/CalendarManager.cpp @@ -9,8 +9,7 @@ using namespace Pinetime::Controllers; * @return */ bool CalendarManager::isBefore(CalendarEvent& event1, CalendarEvent& event2) { - // TODO Use date instead of id for chronological order - return event1.id < event2.id; + return event1.timestamp < event2.timestamp; } CalendarManager::CalendarManager() { diff --git a/src/components/ble/CalendarManager.h b/src/components/ble/CalendarManager.h index 0571bb7324..f5ea27354f 100644 --- a/src/components/ble/CalendarManager.h +++ b/src/components/ble/CalendarManager.h @@ -16,6 +16,8 @@ namespace Pinetime { using Id = uint8_t; Id id; std::string title; + uint32_t timestamp; + uint32_t duration; }; private: diff --git a/src/components/ble/CalendarService.cpp b/src/components/ble/CalendarService.cpp index 5db77a1d7a..59985d3d4a 100644 --- a/src/components/ble/CalendarService.cpp +++ b/src/components/ble/CalendarService.cpp @@ -8,6 +8,10 @@ int CalCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_acce return pCalendarEventService->OnCommand(conn_handle, attr_handle, ctxt); } +uint32_t bytesToInt(const char* bytes) { + return (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]; +} + CalendarService::CalendarService(Pinetime::System::SystemTask& system, CalendarManager& calendarManager) : m_system(system), m_calendarManager(calendarManager) { calUuid.value[14] = calId[0]; @@ -63,7 +67,9 @@ int CalendarService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struc if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &calAddEventUuid) == 0) { CalendarManager::CalendarEvent event { .id = s[0], - .title = &s[1], + .title = &s[9], + .timestamp = bytesToInt(&s[1]), + .duration = bytesToInt(&s[5]), }; m_calendarManager.addEvent(event); } else if (ble_uuid_cmp(ctxt->chr->uuid, (ble_uuid_t*) &calDeleteEventUuid) == 0) { From b942f6619c4e420e89948a9b95e73a950e351ab1 Mon Sep 17 00:00:00 2001 From: Zorvalt Date: Sat, 13 Mar 2021 01:38:35 +0100 Subject: [PATCH 05/12] Display start/end date time for events --- src/displayapp/screens/Timeline.cpp | 66 +++++++++++++++++------------ src/displayapp/screens/Timeline.h | 3 +- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/src/displayapp/screens/Timeline.cpp b/src/displayapp/screens/Timeline.cpp index 5f12963e02..b5f5f94225 100644 --- a/src/displayapp/screens/Timeline.cpp +++ b/src/displayapp/screens/Timeline.cpp @@ -2,20 +2,36 @@ #include #include +#include using namespace Pinetime::Applications::Screens; -char superMegaStringForTesting[100]; +char big_buffer[100]; + +void formatDateTime(char* buffer, time_t timestamp) { + auto time = gmtime(×tamp); + auto year = 1900 + time->tm_year; + auto month = 1 + time->tm_mon; + auto day = time->tm_mday; + auto hour = time->tm_hour; + auto minute = time->tm_min; + sprintf(buffer, "%d-%d-%d %d:%d", year, month, day, hour, minute); +} Timeline::Timeline(DisplayApp* app, Controllers::CalendarManager& calendarManager) : Screen(app), calendarManager(calendarManager) { - hello_world_label = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text(hello_world_label, "Time to timeline :-)"); - lv_obj_set_auto_realign(hello_world_label, true); - lv_obj_align(hello_world_label, nullptr, LV_ALIGN_CENTER, 0, 0); + title_label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text(title_label, "TIMELINE"); + lv_obj_set_auto_realign(title_label, true); + lv_obj_align(title_label, nullptr, LV_ALIGN_IN_TOP_MID, 0, 0); + + time_label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text(time_label, "Swipe right\nfor help"); + lv_obj_set_auto_realign(time_label, true); + lv_obj_align(time_label, nullptr, LV_ALIGN_CENTER, 0, 0); for (int i = 0; i < 100; ++i) { - superMegaStringForTesting[i] = 0; + big_buffer[i] = 0; } currentEvent = calendarManager.begin(); @@ -55,26 +71,15 @@ bool Timeline::OnTouchEvent(TouchEvents event) { case TouchEvents::SwipeLeft: { auto nbEvents = calendarManager.getCount(); - sprintf(superMegaStringForTesting, "Nb events: %d", nbEvents); - lv_label_set_text(hello_world_label, superMegaStringForTesting); + lv_label_set_text(title_label, "TIMELINE - Stats"); + sprintf(big_buffer, "Nb events: %d", nbEvents); + lv_label_set_text(time_label, big_buffer); } break; case TouchEvents::SwipeRight: { - if (calendarManager.empty()) { - strcpy(superMegaStringForTesting, "No event"); - } else { - strcpy(superMegaStringForTesting, "Evts: "); - auto tmpEvent = calendarManager.begin(); - int offset = 6; - while (tmpEvent != calendarManager.end()) { - offset += sprintf(&superMegaStringForTesting[offset], "%d,", (*tmpEvent).id); - tmpEvent++; - } - superMegaStringForTesting[offset] = '\0'; - } - lv_label_set_text(hello_world_label, superMegaStringForTesting); - } break; - + lv_label_set_text(title_label, "TIMELINE - Help"); + lv_label_set_text(time_label, "Swipe to use\nUp/Down: prev/next\nLeft: stats\nRight: help"); + } default: return false; } @@ -83,12 +88,21 @@ bool Timeline::OnTouchEvent(TouchEvents event) { bool Timeline::OnTouchEvent(uint16_t x, uint16_t y) { return Screen::OnTouchEvent(x, y); } + void Timeline::displayCurrent() { if (currentEvent == calendarManager.end()) { - lv_label_set_text(hello_world_label, "No event"); + lv_label_set_text(title_label, ""); + lv_label_set_text(time_label, "No event"); } else { auto event = *currentEvent; - sprintf(superMegaStringForTesting, "e%d: %s", event.id, event.title.c_str()); - lv_label_set_text(hello_world_label, superMegaStringForTesting); + sprintf(big_buffer, "%s", event.title.c_str()); + lv_label_set_text(title_label, big_buffer); + + + char begin[16], end[16]; + formatDateTime(begin, event.timestamp); + formatDateTime(end, event.timestamp + event.duration); + sprintf(big_buffer, "%s\n->\n%s", begin, end); + lv_label_set_text(time_label, big_buffer); } } diff --git a/src/displayapp/screens/Timeline.h b/src/displayapp/screens/Timeline.h index 8704b55fe8..c12482b9cf 100644 --- a/src/displayapp/screens/Timeline.h +++ b/src/displayapp/screens/Timeline.h @@ -22,7 +22,8 @@ namespace Pinetime { Controllers::CalendarManager& calendarManager; Controllers::CalendarManager::CalendarIterator currentEvent; bool running = true; - lv_obj_t * hello_world_label; + lv_obj_t * title_label; + lv_obj_t * time_label; }; } } From 449caeb2bb1478a20799169ce5cb17e7759b1d8a Mon Sep 17 00:00:00 2001 From: l4x1ty Date: Sat, 13 Mar 2021 19:30:48 +0100 Subject: [PATCH 06/12] Refactor CalendarManager to use std::list --- src/CMakeLists.txt | 4 +- src/components/ble/CalendarManager.cpp | 146 -------------------- src/components/ble/CalendarManager.h | 100 -------------- src/components/ble/CalendarService.h | 2 +- src/components/ble/NimbleController.cpp | 2 +- src/components/calendar/CalendarManager.cpp | 63 +++++++++ src/components/calendar/CalendarManager.h | 37 +++++ src/displayapp/DisplayApp.h | 2 +- src/displayapp/screens/Timeline.h | 4 +- 9 files changed, 107 insertions(+), 253 deletions(-) delete mode 100644 src/components/ble/CalendarManager.cpp delete mode 100644 src/components/ble/CalendarManager.h create mode 100644 src/components/calendar/CalendarManager.cpp create mode 100644 src/components/calendar/CalendarManager.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1edb35073a..a20595babb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -497,7 +497,7 @@ list(APPEND SOURCE_FILES components/ble/HeartRateService.cpp components/ble/MotionService.cpp components/ble/CalendarService.cpp - components/ble/CalendarManager.cpp + components/calendar/CalendarManager.cpp components/firmwarevalidator/FirmwareValidator.cpp components/motor/MotorController.cpp components/settings/Settings.cpp @@ -687,8 +687,8 @@ set(INCLUDE_FILES components/ble/HeartRateService.h components/ble/MotionService.h components/ble/weather/WeatherService.h - components/ble/CalendarManager.h components/ble/CalendarService.h + components/calendar/CalendarManager.h components/settings/Settings.h components/timer/TimerController.h components/alarm/AlarmController.h diff --git a/src/components/ble/CalendarManager.cpp b/src/components/ble/CalendarManager.cpp deleted file mode 100644 index f6c7eb9833..0000000000 --- a/src/components/ble/CalendarManager.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include "CalendarManager.h" - -using namespace Pinetime::Controllers; - -/** - * Determines if the event `event1` should be placed before event `event2` - * @param event1 The event to be inserted - * @param event2 The comparison event - * @return - */ -bool CalendarManager::isBefore(CalendarEvent& event1, CalendarEvent& event2) { - return event1.timestamp < event2.timestamp; -} - -CalendarManager::CalendarManager() { - // TODO Load events from filesystem ? - - // Init memory pool linked list - itemsPool[0].prev = nullptr; - for (int i = 1; i < CAPACITY; ++i) { - itemsPool[i].prev = &itemsPool[i - 1]; - itemsPool[i - 1].next = &itemsPool[i]; - } - itemsPool[CAPACITY - 1].next = nullptr; -} - -/** - * Inserts a new event if enough space - * @param event The event to insert - * @return true if the operation succeeded (enough space), false otherwise - */ -bool CalendarManager::addEvent(CalendarEvent& event) { - if (mCount >= CAPACITY) { - return false; - } - - // Init event - auto* item = mFreePoolHead; - mFreePoolHead = mFreePoolHead->next; - item->event = event; - - // Insert event - auto* it = mEventsHead; - // If list is empty - if (it == nullptr) { - item->prev = nullptr; - item->next = nullptr; - mEventsHead = item; - } - // Else if insert in first place - else if (isBefore(event, it->event)) { - item->prev = nullptr; - item->next = mEventsHead; - mEventsHead->prev = item; - mEventsHead = item; - } else { - while (it->next != nullptr && !isBefore(event, it->event)) { - it = it->next; - } - - if (isBefore(event, it->event)) { - item->prev = it->prev; - item->next = it; - it->prev->next = item; - it->prev = item; - } else { - item->next = nullptr; - item->prev = it; - it->next = item; - } - } - - mCount++; - return true; -} - -/** - * Removes the event corresponding to the given id if found - * @param id The id of the event to remove - * @return true if the event is found, false otherwise - */ -bool CalendarManager::deleteEvent(CalendarManager::CalendarEvent::Id id) { - auto* item = mEventsHead; - if (item == nullptr) { - return false; - } - - // Special case for deleting the first element - if (item->event.id == id) { - // Delete item from event list - mEventsHead = item->next; - if (mEventsHead != nullptr) { - mEventsHead->prev = nullptr; - } - - // Add item's memory back to free pool - if (mFreePoolHead != nullptr) { - mFreePoolHead->prev = item; - } - item->next = mFreePoolHead; - mFreePoolHead = item; - - mCount--; - return true; - } - - // Searching for a matching event - while (item != nullptr) { - if (item->event.id == id) { - // Delete item from event list - item->prev->next = item->next; - if (item->next != nullptr) { - item->next->prev = item->prev; - } - - // Add item's memory back to free pool - if (mFreePoolHead != nullptr) { - mFreePoolHead->prev = item; - } - item->next = mFreePoolHead; - mFreePoolHead = item; - - mCount--; - return true; - } - item = item->next; - } - - return false; -} - -CalendarManager::CalendarIterator CalendarManager::begin() { - return CalendarIterator(mEventsHead); -} - -CalendarManager::CalendarIterator CalendarManager::end() { - return CalendarIterator(nullptr); -} - -bool CalendarManager::empty() const { - return mEventsHead == nullptr; -} - -uint8_t CalendarManager::getCount() const { - return mCount; -} diff --git a/src/components/ble/CalendarManager.h b/src/components/ble/CalendarManager.h deleted file mode 100644 index f5ea27354f..0000000000 --- a/src/components/ble/CalendarManager.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace Pinetime { - namespace Controllers { - class CalendarManager { - public: - static constexpr uint8_t CAPACITY = 15; - static constexpr uint8_t TITLE_SIZE {100}; - struct CalendarEvent { - using Id = uint8_t; - Id id; - std::string title; - uint32_t timestamp; - uint32_t duration; - }; - - private: - static bool isBefore(CalendarEvent& event1, CalendarEvent& event2); - struct CalendarItem { - CalendarEvent event; - CalendarItem* prev; - CalendarItem* next; - }; - uint8_t mCount = 0; - CalendarItem itemsPool[CAPACITY] {}; - CalendarItem* mEventsHead = nullptr; - CalendarItem* mFreePoolHead = &itemsPool[0]; - - public: - class CalendarIterator : public std::iterator { - friend class CalendarManager; - CalendarIterator(CalendarItem* i) : item(i) { - } - - public: - CalendarIterator() : item(nullptr) { - } - - CalendarIterator(const CalendarIterator& i) : item(i.item) { - } - - CalendarIterator& operator--() { - item = item->prev; - return *this; - } - - CalendarIterator operator--(int) { - auto tmp {*this}; - operator--(); - return tmp; - } - - CalendarIterator& operator++() { - item = item->next; - return *this; - } - - CalendarIterator operator++(int) { - auto tmp {*this}; - operator++(); - return tmp; - } - - CalendarIterator& operator=(const CalendarIterator& i) { - item = i.item; - return *this; - } - - bool operator==(const CalendarIterator& i) { - return item == i.item; - } - - bool operator!=(const CalendarIterator& i) { - return item != i.item; - } - - CalendarEvent const& operator*() { - return item->event; - } - - private: - CalendarItem* item; - }; - - CalendarManager(); - bool addEvent(CalendarEvent& event); - bool deleteEvent(CalendarEvent::Id id); - CalendarIterator begin(); - static CalendarIterator end(); - bool empty() const; - uint8_t getCount() const; - }; - } -} \ No newline at end of file diff --git a/src/components/ble/CalendarService.h b/src/components/ble/CalendarService.h index 0706d2e3f1..8c9bcf03e8 100644 --- a/src/components/ble/CalendarService.h +++ b/src/components/ble/CalendarService.h @@ -4,7 +4,7 @@ #include #include #include -#include "CalendarManager.h" +#include "components/calendar/CalendarManager.h" #define CALENDAR_SERVICE_UUID_BASE \ { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x00, 0x00, 0x04, 0x00 } diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index b9adda8fde..288f7941aa 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -16,7 +16,7 @@ #undef max #undef min #include "components/ble/BleController.h" -#include "components/ble/CalendarManager.h" +#include "components/calendar/CalendarManager.h" #include "components/ble/NotificationManager.h" #include "components/datetime/DateTimeController.h" #include "components/fs/FS.h" diff --git a/src/components/calendar/CalendarManager.cpp b/src/components/calendar/CalendarManager.cpp new file mode 100644 index 0000000000..a8584b38bb --- /dev/null +++ b/src/components/calendar/CalendarManager.cpp @@ -0,0 +1,63 @@ +#include "CalendarManager.h" +#include + +using namespace Pinetime::Controllers; + +/** + * Determines if the event `event1` should be placed before event `event2` + * @param event1 The event to be inserted + * @param event2 The comparison event + * @return + */ +bool CalendarManager::isBefore(CalendarEvent& event1, CalendarEvent& event2) { + return event1.timestamp < event2.timestamp; +} + + +/** + * Inserts a new event if enough space + * @param event The event to insert + * @return true if the operation succeeded (enough space), false otherwise + */ +bool CalendarManager::addEvent(CalendarEvent& event) { + auto it = calendarEvents.begin(); + + while (calendarEvents.begin() != calendarEvents.end() && isBefore(*it, event)) { + ++it; + } + calendarEvents.insert(it, event); + return true; +} + +/** + * Removes the event corresponding to the given id if found + * @param id The id of the event to remove + * @return true if the event is found, false otherwise + */ +bool CalendarManager::deleteEvent(CalendarManager::CalendarEvent::Id id) { + auto matchId = [id](const CalendarEvent& event) { return event.id == id; }; + auto it = std::find_if(calendarEvents.begin(), calendarEvents.end(), matchId); + + if (it != calendarEvents.end()) { + calendarEvents.erase(it); + return true; + } + + return false; +} + +CalendarManager::CalendarEventIterator CalendarManager::begin() { + return calendarEvents.begin(); +} + +CalendarManager::CalendarEventIterator CalendarManager::end() { + return calendarEvents.end(); +} + +bool CalendarManager::empty() const { + return calendarEvents.empty(); +} + +unsigned int CalendarManager::getCount() const { + return calendarEvents.size(); +} diff --git a/src/components/calendar/CalendarManager.h b/src/components/calendar/CalendarManager.h new file mode 100644 index 0000000000..f66cf7f9f7 --- /dev/null +++ b/src/components/calendar/CalendarManager.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Pinetime { + namespace Controllers { + class CalendarManager { + public: + struct CalendarEvent { + using Id = uint8_t; + Id id; + std::string title; + uint32_t timestamp; + uint32_t duration; + }; + using CalendarEventIterator = std::list::iterator; + + private: + static bool isBefore(CalendarEvent& event1, CalendarEvent& event2); + + std::list calendarEvents; + + public: + bool addEvent(CalendarEvent& event); + bool deleteEvent(CalendarEvent::Id id); + CalendarEventIterator begin(); + CalendarEventIterator end(); + bool empty() const; + unsigned int getCount() const; + }; + } +} \ No newline at end of file diff --git a/src/displayapp/DisplayApp.h b/src/displayapp/DisplayApp.h index c6f8691750..242b4f734b 100644 --- a/src/displayapp/DisplayApp.h +++ b/src/displayapp/DisplayApp.h @@ -8,7 +8,7 @@ #include "displayapp/Apps.h" #include "displayapp/LittleVgl.h" #include "displayapp/TouchEvents.h" -#include "components/ble/CalendarManager.h" +#include "components/calendar/CalendarManager.h" #include "components/brightness/BrightnessController.h" #include "components/motor/MotorController.h" #include "components/firmwarevalidator/FirmwareValidator.h" diff --git a/src/displayapp/screens/Timeline.h b/src/displayapp/screens/Timeline.h index c12482b9cf..cf3152e3dc 100644 --- a/src/displayapp/screens/Timeline.h +++ b/src/displayapp/screens/Timeline.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include "components/calendar/CalendarManager.h" #include "Screen.h" @@ -20,7 +20,7 @@ namespace Pinetime { void displayCurrent(); Controllers::CalendarManager& calendarManager; - Controllers::CalendarManager::CalendarIterator currentEvent; + Controllers::CalendarManager::CalendarEventIterator currentEvent; bool running = true; lv_obj_t * title_label; lv_obj_t * time_label; From 6368017ed84d5792d50a7943fcfab3fd41fd2776 Mon Sep 17 00:00:00 2001 From: Zorvalt Date: Sat, 13 Mar 2021 20:06:35 +0100 Subject: [PATCH 07/12] Use lvgl set_text_fmt instead of sprintf in timeline --- src/displayapp/screens/Timeline.cpp | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/displayapp/screens/Timeline.cpp b/src/displayapp/screens/Timeline.cpp index b5f5f94225..96afd2ea79 100644 --- a/src/displayapp/screens/Timeline.cpp +++ b/src/displayapp/screens/Timeline.cpp @@ -1,15 +1,12 @@ #include "Timeline.h" -#include -#include #include +#include using namespace Pinetime::Applications::Screens; -char big_buffer[100]; - void formatDateTime(char* buffer, time_t timestamp) { - auto time = gmtime(×tamp); + auto *time = gmtime(×tamp); auto year = 1900 + time->tm_year; auto month = 1 + time->tm_mon; auto day = time->tm_mday; @@ -30,12 +27,9 @@ Timeline::Timeline(DisplayApp* app, Controllers::CalendarManager& calendarManage lv_obj_set_auto_realign(time_label, true); lv_obj_align(time_label, nullptr, LV_ALIGN_CENTER, 0, 0); - for (int i = 0; i < 100; ++i) { - big_buffer[i] = 0; - } - currentEvent = calendarManager.begin(); } + Timeline::~Timeline() { lv_obj_clean(lv_scr_act()); } @@ -72,8 +66,7 @@ bool Timeline::OnTouchEvent(TouchEvents event) { case TouchEvents::SwipeLeft: { auto nbEvents = calendarManager.getCount(); lv_label_set_text(title_label, "TIMELINE - Stats"); - sprintf(big_buffer, "Nb events: %d", nbEvents); - lv_label_set_text(time_label, big_buffer); + lv_label_set_text_fmt(time_label, "Nb events: %d", nbEvents); } break; case TouchEvents::SwipeRight: { @@ -85,6 +78,7 @@ bool Timeline::OnTouchEvent(TouchEvents event) { } return true; } + bool Timeline::OnTouchEvent(uint16_t x, uint16_t y) { return Screen::OnTouchEvent(x, y); } @@ -95,14 +89,11 @@ void Timeline::displayCurrent() { lv_label_set_text(time_label, "No event"); } else { auto event = *currentEvent; - sprintf(big_buffer, "%s", event.title.c_str()); - lv_label_set_text(title_label, big_buffer); - + lv_label_set_text_fmt(title_label, "%s", event.title.c_str()); char begin[16], end[16]; formatDateTime(begin, event.timestamp); formatDateTime(end, event.timestamp + event.duration); - sprintf(big_buffer, "%s\n->\n%s", begin, end); - lv_label_set_text(time_label, big_buffer); + lv_label_set_text_fmt(time_label, "%s\n->\n%s", begin, end); } } From 50fde274fef8134089cc5770322d1fd449ae2173 Mon Sep 17 00:00:00 2001 From: Zorvalt Date: Sat, 13 Mar 2021 22:05:31 +0100 Subject: [PATCH 08/12] Fix CalendarManager::addEvent wrong loop condition --- src/components/calendar/CalendarManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/calendar/CalendarManager.cpp b/src/components/calendar/CalendarManager.cpp index a8584b38bb..cc7c3747c0 100644 --- a/src/components/calendar/CalendarManager.cpp +++ b/src/components/calendar/CalendarManager.cpp @@ -22,7 +22,7 @@ bool CalendarManager::isBefore(CalendarEvent& event1, CalendarEvent& event2) { bool CalendarManager::addEvent(CalendarEvent& event) { auto it = calendarEvents.begin(); - while (calendarEvents.begin() != calendarEvents.end() && isBefore(*it, event)) { + while (it != calendarEvents.end() && isBefore(*it, event)) { ++it; } calendarEvents.insert(it, event); From 1d3dd6a79e403c9c7477a70b993c9de2908895e6 Mon Sep 17 00:00:00 2001 From: Zorvalt Date: Tue, 16 Mar 2021 01:45:04 +0100 Subject: [PATCH 09/12] Add timetable (draft) in timeline Screen --- src/displayapp/DisplayApp.cpp | 2 +- src/displayapp/screens/Timeline.cpp | 135 ++++++++++++++++++++-------- src/displayapp/screens/Timeline.h | 22 ++++- src/libs/lv_conf.h | 2 +- 4 files changed, 118 insertions(+), 43 deletions(-) diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index 7ced838935..7abaceda99 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -487,7 +487,7 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) break; case Apps::Timeline: - currentScreen = std::make_unique(this, calendarManager); + currentScreen = std::make_unique(this, dateTimeController, calendarManager); break; } currentApp = app; diff --git a/src/displayapp/screens/Timeline.cpp b/src/displayapp/screens/Timeline.cpp index 96afd2ea79..cedbd0c3fe 100644 --- a/src/displayapp/screens/Timeline.cpp +++ b/src/displayapp/screens/Timeline.cpp @@ -1,32 +1,50 @@ #include "Timeline.h" #include -#include +#include using namespace Pinetime::Applications::Screens; +static const char days[] = "M\nT\nW\nT\nF\nS\nS\nM\nT\nW\nT\nF\nS"; + void formatDateTime(char* buffer, time_t timestamp) { - auto *time = gmtime(×tamp); + auto *time = localtime(×tamp); auto year = 1900 + time->tm_year; auto month = 1 + time->tm_mon; - auto day = time->tm_mday; - auto hour = time->tm_hour; - auto minute = time->tm_min; - sprintf(buffer, "%d-%d-%d %d:%d", year, month, day, hour, minute); + sprintf(buffer, "%d-%d-%d %d:%d", year, month, time->tm_mday, time->tm_hour, time->tm_min); } -Timeline::Timeline(DisplayApp* app, Controllers::CalendarManager& calendarManager) - : Screen(app), calendarManager(calendarManager) { +Timeline::Timeline(DisplayApp* app, Controllers::DateTime& dateTimeController, Controllers::CalendarManager& calendarManager) + : Screen(app), dateTimeController(dateTimeController), calendarManager(calendarManager) { + week_canvas = lv_canvas_create(lv_scr_act(), nullptr); + lv_canvas_set_buffer(week_canvas, &cbuf[0], CANVAS_WIDTH, CANVAS_HEIGHT, LV_IMG_CF_INDEXED_1BIT); + lv_obj_set_pos(week_canvas, 20, CANVAS_Y_POSITION); + lv_canvas_set_palette(week_canvas, 0, LV_COLOR_BLACK); + lv_canvas_set_palette(week_canvas, 1, LV_COLOR_GRAY); + lv_color_t c0; + c0.full = 0; + lv_canvas_fill_bg(week_canvas, c0, LV_OPA_COVER); + title_label = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(title_label, "TIMELINE"); lv_obj_set_auto_realign(title_label, true); lv_obj_align(title_label, nullptr, LV_ALIGN_IN_TOP_MID, 0, 0); time_label = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text(time_label, "Swipe right\nfor help"); + lv_label_set_text_static(time_label, "Swipe to use!\n\nUp/Down: prev/next\nLeft: timetable\nRight: timeline"); lv_obj_set_auto_realign(time_label, true); lv_obj_align(time_label, nullptr, LV_ALIGN_CENTER, 0, 0); + days_label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text(days_label, ""); + lv_obj_set_auto_realign(days_label, true); + lv_obj_align(days_label, week_canvas, LV_ALIGN_OUT_LEFT_TOP, -5, 0); + + hours_label = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text(hours_label, ""); + lv_obj_set_auto_realign(hours_label, true); + lv_obj_align(hours_label, week_canvas, LV_ALIGN_OUT_TOP_LEFT, 0, 0); + currentEvent = calendarManager.begin(); } @@ -41,38 +59,31 @@ bool Timeline::OnButtonPushed() { bool Timeline::OnTouchEvent(TouchEvents event) { switch (event) { - case TouchEvents::SwipeUp: { - auto calBegin = calendarManager.begin(); + case TouchEvents::SwipeUp: + currentEvent++; if (currentEvent == calendarManager.end()) { - currentEvent = calBegin; - } else { currentEvent--; } - displayCurrent(); - } break; + displayCurrentEvent(); + break; - case TouchEvents::SwipeDown: { - auto calBegin = calendarManager.begin(); - if (currentEvent == calendarManager.end()) { - currentEvent = calBegin; - } else { - currentEvent++; + case TouchEvents::SwipeDown: + if (currentEvent != calendarManager.begin()) { + currentEvent--; } - displayCurrent(); - } break; + displayCurrentEvent(); + break; - case TouchEvents::SwipeLeft: { - auto nbEvents = calendarManager.getCount(); - lv_label_set_text(title_label, "TIMELINE - Stats"); - lv_label_set_text_fmt(time_label, "Nb events: %d", nbEvents); - } break; + case TouchEvents::SwipeLeft: + displayWeek(); + break; + + case TouchEvents::SwipeRight: + displayCurrentEvent(); + break; - case TouchEvents::SwipeRight: { - lv_label_set_text(title_label, "TIMELINE - Help"); - lv_label_set_text(time_label, "Swipe to use\nUp/Down: prev/next\nLeft: stats\nRight: help"); - } default: return false; } @@ -83,7 +94,56 @@ bool Timeline::OnTouchEvent(uint16_t x, uint16_t y) { return Screen::OnTouchEvent(x, y); } -void Timeline::displayCurrent() { +void Timeline::displayWeek() { + lv_label_set_text_static(title_label, "Next 7 days"); + lv_label_set_text_static(time_label, ""); + + auto currentDateTime = dateTimeController.CurrentDateTime(); + auto dp = date::floor(currentDateTime); + auto time = date::make_time(currentDateTime-dp); + auto yearMonthDay = date::year_month_day(dp); + today_index = date::weekday(yearMonthDay).iso_encoding() - 1; + + char days_label_content[14]; + strncpy(&days_label_content[0], &days[today_index*2], 13); + days_label_content[13] = '\0'; + lv_label_set_text(days_label, &days_label_content[0]); + lv_label_set_text_static(hours_label, "0 4 8 12 16 20 24"); + + lv_color_t event_color; + event_color.full = 1; + + for(auto & it : calendarManager) { + time_t timestamp = it.timestamp; + auto *it_time = localtime(×tamp); + auto day_index = it_time->tm_wday-1; + auto line_index = day_index - today_index; + if (line_index == 0 && timestamp > currentDateTime.time_since_epoch().count()) { + continue; // Do not show events of the same day as today but next week + } + + if (line_index < 0) { + line_index += 7; + } + + auto y_event = 24*line_index; + auto begin = it_time->tm_hour*60 + it_time->tm_min; + auto end = (begin*60 + it.duration) / 60; + for (uint32_t x = begin/8; x < end/8; ++x) { + for (auto y_offset = 0; y_offset < 10; ++y_offset) { + lv_canvas_set_px(week_canvas, x, 5+y_event+y_offset, event_color); + } + } + } +} + +void Timeline::displayCurrentEvent() { + lv_color_t c0; + c0.full = 0; + lv_canvas_fill_bg(week_canvas, c0, LV_OPA_COVER); + lv_label_set_text_static(hours_label, ""); + lv_label_set_text_static(days_label, ""); + if (currentEvent == calendarManager.end()) { lv_label_set_text(title_label, ""); lv_label_set_text(time_label, "No event"); @@ -91,9 +151,10 @@ void Timeline::displayCurrent() { auto event = *currentEvent; lv_label_set_text_fmt(title_label, "%s", event.title.c_str()); - char begin[16], end[16]; - formatDateTime(begin, event.timestamp); - formatDateTime(end, event.timestamp + event.duration); - lv_label_set_text_fmt(time_label, "%s\n->\n%s", begin, end); + char begin[16]; + formatDateTime(&begin[0], event.timestamp); + char end[16]; + formatDateTime(&end[0], event.timestamp + event.duration); + lv_label_set_text_fmt(time_label, "%s\n->\n%s", &begin[0], &end[0]); } } diff --git a/src/displayapp/screens/Timeline.h b/src/displayapp/screens/Timeline.h index cf3152e3dc..08fead8284 100644 --- a/src/displayapp/screens/Timeline.h +++ b/src/displayapp/screens/Timeline.h @@ -1,29 +1,43 @@ #pragma once -#include +#include #include "components/calendar/CalendarManager.h" #include "Screen.h" +#include "Clock.h" + +#define CANVAS_WIDTH 192 +#define CANVAS_Y_POSITION 60 +#define CANVAS_HEIGHT 180 namespace Pinetime { namespace Applications { namespace Screens { class Timeline : public Screen { public: - explicit Timeline(DisplayApp* app, Controllers::CalendarManager& calendarManager); + explicit Timeline(DisplayApp* app, Controllers::DateTime& dateTimeController, Controllers::CalendarManager& calendarManager); ~Timeline() override; bool OnButtonPushed() override; bool OnTouchEvent(TouchEvents event) override; bool OnTouchEvent(uint16_t x, uint16_t y) override; private: - void displayCurrent(); - + void displayCurrentEvent(); + void displayWeek(); + + Controllers::DateTime& dateTimeController; Controllers::CalendarManager& calendarManager; Controllers::CalendarManager::CalendarEventIterator currentEvent; + bool running = true; + uint8_t today_index = 0; + lv_obj_t * title_label; lv_obj_t * time_label; + lv_obj_t * days_label; + lv_obj_t * hours_label; + lv_obj_t * week_canvas; + uint8_t cbuf[LV_IMG_BUF_SIZE_INDEXED_1BIT(CANVAS_WIDTH, CANVAS_HEIGHT)]{}; }; } } diff --git a/src/libs/lv_conf.h b/src/libs/lv_conf.h index 73109c5af9..01bf389f6d 100644 --- a/src/libs/lv_conf.h +++ b/src/libs/lv_conf.h @@ -592,7 +592,7 @@ typedef void* lv_obj_user_data_t; #endif /*Canvas (dependencies: lv_img)*/ -#define LV_USE_CANVAS 0 +#define LV_USE_CANVAS 1 /*Check box (dependencies: lv_btn, lv_label)*/ #define LV_USE_CHECKBOX 1 From d5a76d804f00d25dbbed613fda6c222a18e26f66 Mon Sep 17 00:00:00 2001 From: Zorvalt Date: Sun, 6 Mar 2022 22:03:12 +0100 Subject: [PATCH 10/12] Replace lvgl canvas with LittleVgl --- src/displayapp/DisplayApp.cpp | 2 +- src/displayapp/screens/Timeline.cpp | 193 +++++++++++++++++++--------- src/displayapp/screens/Timeline.h | 47 ++++--- src/libs/lv_conf.h | 2 +- 4 files changed, 164 insertions(+), 80 deletions(-) diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index 7abaceda99..0166d43f6a 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -487,7 +487,7 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) break; case Apps::Timeline: - currentScreen = std::make_unique(this, dateTimeController, calendarManager); + currentScreen = std::make_unique(this, dateTimeController, calendarManager, lvgl); break; } currentApp = app; diff --git a/src/displayapp/screens/Timeline.cpp b/src/displayapp/screens/Timeline.cpp index cedbd0c3fe..0a7269ad0f 100644 --- a/src/displayapp/screens/Timeline.cpp +++ b/src/displayapp/screens/Timeline.cpp @@ -5,7 +5,8 @@ using namespace Pinetime::Applications::Screens; -static const char days[] = "M\nT\nW\nT\nF\nS\nS\nM\nT\nW\nT\nF\nS"; +static constexpr char days[] = "M\0T\0W\0T\0F\0S\0S\0M\0T\0W\0T\0F\0S"; +static constexpr char hours_texts[] = " 4\0" " 8\0" "12\0" "16\0" "20"; void formatDateTime(char* buffer, time_t timestamp) { auto *time = localtime(×tamp); @@ -14,17 +15,11 @@ void formatDateTime(char* buffer, time_t timestamp) { sprintf(buffer, "%d-%d-%d %d:%d", year, month, time->tm_mday, time->tm_hour, time->tm_min); } -Timeline::Timeline(DisplayApp* app, Controllers::DateTime& dateTimeController, Controllers::CalendarManager& calendarManager) - : Screen(app), dateTimeController(dateTimeController), calendarManager(calendarManager) { - week_canvas = lv_canvas_create(lv_scr_act(), nullptr); - lv_canvas_set_buffer(week_canvas, &cbuf[0], CANVAS_WIDTH, CANVAS_HEIGHT, LV_IMG_CF_INDEXED_1BIT); - lv_obj_set_pos(week_canvas, 20, CANVAS_Y_POSITION); - lv_canvas_set_palette(week_canvas, 0, LV_COLOR_BLACK); - lv_canvas_set_palette(week_canvas, 1, LV_COLOR_GRAY); - lv_color_t c0; - c0.full = 0; - lv_canvas_fill_bg(week_canvas, c0, LV_OPA_COVER); - +Timeline::Timeline(DisplayApp* app, + Controllers::DateTime& dateTimeController, + Controllers::CalendarManager& calendarManager, + Components::LittleVgl& lvgl) + : Screen(app), dateTimeController(dateTimeController), calendarManager(calendarManager), lvgl{lvgl} { title_label = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text(title_label, "TIMELINE"); lv_obj_set_auto_realign(title_label, true); @@ -35,16 +30,46 @@ Timeline::Timeline(DisplayApp* app, Controllers::DateTime& dateTimeController, C lv_obj_set_auto_realign(time_label, true); lv_obj_align(time_label, nullptr, LV_ALIGN_CENTER, 0, 0); - days_label = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text(days_label, ""); - lv_obj_set_auto_realign(days_label, true); - lv_obj_align(days_label, week_canvas, LV_ALIGN_OUT_LEFT_TOP, -5, 0); + days_container = lv_obj_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_bg_opa(days_container, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP); + lv_obj_set_size(days_container, TIMETABLE_WIDTH, lv_obj_get_height(title_label)); + lv_obj_align(days_container, nullptr, LV_ALIGN_IN_TOP_MID, 0, 0); + + timetable_draw_area = lv_obj_create(lv_scr_act(), nullptr); + lv_obj_set_size(timetable_draw_area, TIMETABLE_WIDTH + 1, TIMETABLE_HEIGHT + 1); + lv_obj_set_style_local_radius(timetable_draw_area, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0); + lv_obj_set_style_local_bg_opa(timetable_draw_area, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP); + lv_obj_set_style_local_bg_color(timetable_draw_area, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_TRANSP); + lv_obj_set_style_local_border_color(timetable_draw_area, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_obj_set_style_local_border_width(timetable_draw_area, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 1); + lv_obj_move_background(timetable_draw_area); + lv_obj_align(timetable_draw_area, days_container, LV_ALIGN_OUT_BOTTOM_MID, 0, 0); + + for (int i=0; i < 7; i++) { + days_labels[i] = lv_label_create(days_container, nullptr); + lv_label_set_text_static(days_labels[i], &days[i*2]); + lv_obj_set_width(days_labels[i], TIMESLOT_PX_WIDTH); + lv_obj_align_mid_x(days_labels[i], nullptr, LV_ALIGN_IN_BOTTOM_LEFT, INTER_DAY_SPACE + TIMESLOT_PX_WIDTH/2 + DAY_PX_OFFSET*i); + lv_obj_align_y(days_labels[i], nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0); + } + + hours_container = lv_obj_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_bg_opa(hours_container, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP); + lv_obj_set_size(hours_container, 25, TIMETABLE_HEIGHT); + lv_obj_align(hours_container, timetable_draw_area, LV_ALIGN_OUT_LEFT_MID, 0, 0); + + static constexpr lv_coord_t HOURS_LABELS_SPACING = TIMETABLE_HEIGHT / 6; + for (int i=0; i < 5; i++) { + hours_labels[i] = lv_label_create(hours_container, nullptr); + lv_label_set_text_static(hours_labels[i], &hours_texts[i*3]); + lv_obj_align_mid_x(hours_labels[i], nullptr, LV_ALIGN_IN_TOP_MID, 0); + lv_obj_align_mid_y(hours_labels[i], nullptr, LV_ALIGN_IN_TOP_MID, HOURS_LABELS_SPACING*(i+1)); + } + + lv_obj_set_hidden(timetable_draw_area, true); + lv_obj_set_hidden(days_container, true); + lv_obj_set_hidden(hours_container, true); - hours_label = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text(hours_label, ""); - lv_obj_set_auto_realign(hours_label, true); - lv_obj_align(hours_label, week_canvas, LV_ALIGN_OUT_TOP_LEFT, 0, 0); - currentEvent = calendarManager.begin(); } @@ -52,11 +77,6 @@ Timeline::~Timeline() { lv_obj_clean(lv_scr_act()); } -bool Timeline::OnButtonPushed() { - running = false; - return true; -} - bool Timeline::OnTouchEvent(TouchEvents event) { switch (event) { case TouchEvents::SwipeUp: @@ -94,58 +114,107 @@ bool Timeline::OnTouchEvent(uint16_t x, uint16_t y) { return Screen::OnTouchEvent(x, y); } +void Timeline::set_timeslot_area(lv_area_t* timeslot_area, uint8_t day_index, uint16_t timeslot_index) { + const lv_coord_t TIMETABLE_X0 = lv_obj_get_x(timetable_draw_area) + INTER_DAY_SPACE; + const lv_coord_t TIMETABLE_Y0 = lv_obj_get_y(timetable_draw_area) + INTER_DAY_SPACE; + + const lv_coord_t x_offset = TIMETABLE_X0 + DAY_PX_OFFSET * day_index; + const lv_coord_t y_offset = TIMETABLE_Y0 + timeslot_index * TIMESLOT_PX_HEIGHT; + + timeslot_area->x1 = x_offset; + timeslot_area->y1 = y_offset; + timeslot_area->x2 = x_offset + TIMESLOT_PX_WIDTH - 1; + timeslot_area->y2 = y_offset + TIMESLOT_PX_HEIGHT - 1; +} + +void Timeline::draw_event(uint8_t day_column, uint16_t event_start, uint16_t duration) { + uint16_t event_end = event_start + duration; + + // Handle day overflow recursively + if (event_end > MINUTES_PER_DAY) { + draw_event(day_column+1, 0, event_end - MINUTES_PER_DAY); + event_end = MINUTES_PER_DAY; + } + + // If the event starts before or finishes after the middle of a timeslot, shrink/expand to next timeslot + const lv_coord_t FIRST_TIMESLOT_UNDERFLOW = (event_start % MINUTES_PER_TIMESLOT > MINUTES_PER_TIMESLOT/2) ? 1 : 0; + const lv_coord_t LAST_TIMESLOT_OVERFLOW = (event_end % MINUTES_PER_TIMESLOT < MINUTES_PER_TIMESLOT/2) ? 0 : 1; + + // Compute rounded timeslot event_start and end + const lv_coord_t FIRST_TIMESLOT_START = event_start / MINUTES_PER_TIMESLOT + FIRST_TIMESLOT_UNDERFLOW; + const lv_coord_t LAST_TIMESLOT_END = event_end / MINUTES_PER_TIMESLOT + LAST_TIMESLOT_OVERFLOW; + + lv_area_t timeslot_area; + for(lv_coord_t timeslot=FIRST_TIMESLOT_START; timeslot < LAST_TIMESLOT_END; timeslot++) { + set_timeslot_area(×lot_area, day_column, timeslot); + lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None); + lvgl.FlushDisplay(×lot_area, timeslotBuffer); + } +} + void Timeline::displayWeek() { - lv_label_set_text_static(title_label, "Next 7 days"); - lv_label_set_text_static(time_label, ""); + lv_obj_set_hidden(time_label, true); + lv_obj_set_hidden(title_label, true); + + lv_obj_set_hidden(timetable_draw_area, false); + lv_obj_set_hidden(days_container, false); + lv_obj_set_hidden(hours_container, false); auto currentDateTime = dateTimeController.CurrentDateTime(); auto dp = date::floor(currentDateTime); auto time = date::make_time(currentDateTime-dp); auto yearMonthDay = date::year_month_day(dp); today_index = date::weekday(yearMonthDay).iso_encoding() - 1; - - char days_label_content[14]; - strncpy(&days_label_content[0], &days[today_index*2], 13); - days_label_content[13] = '\0'; - lv_label_set_text(days_label, &days_label_content[0]); - lv_label_set_text_static(hours_label, "0 4 8 12 16 20 24"); - - lv_color_t event_color; - event_color.full = 1; - + + + // Wait to allow lvgl to first draw the UI + lv_refr_now(nullptr); + uint32_t t = lv_tick_get(); + while(lv_tick_elaps(t) < 100); + + std::fill(timeslotBuffer, ×lotBuffer[TIMESLOT_BUFFER_SIZE], LV_COLOR_RED); + + for (int di=0; di<7; di++) { + draw_event(di, 60 * 3, 10*di); + draw_event(di, 60 * 8 + 45 + 60*(di%2), 90); + draw_event(di, 60 * 11 + 15 + 60*(di%2), 90); + draw_event(di, 60 * 14 + 45 + 60*(di%2), 90); + draw_event(di, 60 * 16 + 15 + 60*(di%2), 90); + draw_event(di, 60 * 19 + 45 + 60*(di%2), 90); + } + + lv_refr_now(nullptr); + uint32_t t2 = lv_tick_get(); + while(lv_tick_elaps(t2) < 100); + std::fill(timeslotBuffer, ×lotBuffer[TIMESLOT_BUFFER_SIZE], LV_COLOR_BLUE); + for(auto & it : calendarManager) { time_t timestamp = it.timestamp; - auto *it_time = localtime(×tamp); - auto day_index = it_time->tm_wday-1; - auto line_index = day_index - today_index; - if (line_index == 0 && timestamp > currentDateTime.time_since_epoch().count()) { - continue; // Do not show events of the same day as today but next week - } - - if (line_index < 0) { - line_index += 7; + tm *it_time = localtime(×tamp); + uint8_t day_index = it_time->tm_wday-1; + uint8_t column_index = day_index - today_index; + if (column_index == 0 && timestamp > currentDateTime.time_since_epoch().count()) { + continue; // Do not show events matching the current day of the week but occurring next week } - - auto y_event = 24*line_index; - auto begin = it_time->tm_hour*60 + it_time->tm_min; - auto end = (begin*60 + it.duration) / 60; - for (uint32_t x = begin/8; x < end/8; ++x) { - for (auto y_offset = 0; y_offset < 10; ++y_offset) { - lv_canvas_set_px(week_canvas, x, 5+y_event+y_offset, event_color); - } + + if (column_index < 0) { + column_index += 7; } + + draw_event(column_index, it_time->tm_hour*60 + it_time->tm_min, it.duration/60); } } void Timeline::displayCurrentEvent() { - lv_color_t c0; - c0.full = 0; - lv_canvas_fill_bg(week_canvas, c0, LV_OPA_COVER); - lv_label_set_text_static(hours_label, ""); - lv_label_set_text_static(days_label, ""); - + lv_obj_set_hidden(timetable_draw_area, true); + lv_obj_set_hidden(days_container, true); + lv_obj_set_hidden(hours_container, true); + + lv_obj_set_hidden(title_label, false); + lv_obj_set_hidden(time_label, false); + if (currentEvent == calendarManager.end()) { - lv_label_set_text(title_label, ""); + lv_label_set_text_static(title_label, "TIMELINE"); lv_label_set_text(time_label, "No event"); } else { auto event = *currentEvent; diff --git a/src/displayapp/screens/Timeline.h b/src/displayapp/screens/Timeline.h index 08fead8284..ec1d18a641 100644 --- a/src/displayapp/screens/Timeline.h +++ b/src/displayapp/screens/Timeline.h @@ -1,43 +1,58 @@ #pragma once -#include #include "components/calendar/CalendarManager.h" +#include "displayapp/LittleVgl.h" +#include -#include "Screen.h" #include "Clock.h" +#include "Screen.h" + +// Timetable hours(y-axis) formatting +static constexpr uint16_t MINUTES_PER_DAY = 24 * 60; +static constexpr uint16_t MINUTES_PER_TIMESLOT = 20; +static constexpr uint16_t TIMESLOT_PX_HEIGHT = 3; +static constexpr lv_coord_t TIMETABLE_HEIGHT = MINUTES_PER_DAY / MINUTES_PER_TIMESLOT * TIMESLOT_PX_HEIGHT; -#define CANVAS_WIDTH 192 -#define CANVAS_Y_POSITION 60 -#define CANVAS_HEIGHT 180 +// Timetable days(x-axis) formatting +static constexpr lv_coord_t TIMESLOT_PX_WIDTH = 16; +static constexpr lv_coord_t INTER_DAY_SPACE = 8; +static constexpr lv_coord_t DAY_PX_OFFSET = INTER_DAY_SPACE + TIMESLOT_PX_WIDTH; +static constexpr lv_coord_t TIMETABLE_WIDTH = 7 * DAY_PX_OFFSET + INTER_DAY_SPACE; namespace Pinetime { namespace Applications { namespace Screens { class Timeline : public Screen { public: - explicit Timeline(DisplayApp* app, Controllers::DateTime& dateTimeController, Controllers::CalendarManager& calendarManager); + explicit Timeline(DisplayApp* app, Controllers::DateTime& dateTimeController, Controllers::CalendarManager& calendarManager, Components::LittleVgl& lvgl); ~Timeline() override; - bool OnButtonPushed() override; bool OnTouchEvent(TouchEvents event) override; bool OnTouchEvent(uint16_t x, uint16_t y) override; private: void displayCurrentEvent(); + void set_timeslot_area(lv_area_t* timeslot_area, uint8_t day_index, uint16_t timeslot_index); + void draw_event(uint8_t day_column, uint16_t event_start, uint16_t duration); void displayWeek(); - + Controllers::DateTime& dateTimeController; Controllers::CalendarManager& calendarManager; - Controllers::CalendarManager::CalendarEventIterator currentEvent; - - bool running = true; + Controllers::CalendarManager::CalendarEventIterator currentEvent; + uint8_t today_index = 0; - + lv_obj_t * title_label; lv_obj_t * time_label; - lv_obj_t * days_label; - lv_obj_t * hours_label; - lv_obj_t * week_canvas; - uint8_t cbuf[LV_IMG_BUF_SIZE_INDEXED_1BIT(CANVAS_WIDTH, CANVAS_HEIGHT)]{}; + + lv_obj_t * days_container; + lv_obj_t* hours_labels[5]; // 4 8 12 16 20 + lv_obj_t * hours_container; + lv_obj_t* days_labels[7]; + + Pinetime::Components::LittleVgl& lvgl; + static constexpr uint16_t TIMESLOT_BUFFER_SIZE = TIMESLOT_PX_WIDTH * TIMESLOT_PX_HEIGHT; + lv_color_t timeslotBuffer[TIMESLOT_BUFFER_SIZE]; + lv_obj_t * timetable_draw_area; }; } } diff --git a/src/libs/lv_conf.h b/src/libs/lv_conf.h index 01bf389f6d..73109c5af9 100644 --- a/src/libs/lv_conf.h +++ b/src/libs/lv_conf.h @@ -592,7 +592,7 @@ typedef void* lv_obj_user_data_t; #endif /*Canvas (dependencies: lv_img)*/ -#define LV_USE_CANVAS 1 +#define LV_USE_CANVAS 0 /*Check box (dependencies: lv_btn, lv_label)*/ #define LV_USE_CHECKBOX 1 From b41d19ce202eb68f211db5f0d5eb2260be07b107 Mon Sep 17 00:00:00 2001 From: Zorvalt Date: Tue, 8 Mar 2022 12:10:48 +0100 Subject: [PATCH 11/12] Fix event day index comparison in timeline --- src/displayapp/screens/Timeline.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/displayapp/screens/Timeline.cpp b/src/displayapp/screens/Timeline.cpp index 0a7269ad0f..582da8836b 100644 --- a/src/displayapp/screens/Timeline.cpp +++ b/src/displayapp/screens/Timeline.cpp @@ -192,15 +192,15 @@ void Timeline::displayWeek() { time_t timestamp = it.timestamp; tm *it_time = localtime(×tamp); uint8_t day_index = it_time->tm_wday-1; + if (day_index < today_index) { + day_index += 7; + } + uint8_t column_index = day_index - today_index; if (column_index == 0 && timestamp > currentDateTime.time_since_epoch().count()) { continue; // Do not show events matching the current day of the week but occurring next week } - if (column_index < 0) { - column_index += 7; - } - draw_event(column_index, it_time->tm_hour*60 + it_time->tm_min, it.duration/60); } } From 4a5cce71fa00be2d20b83bf7731ac1128e1db1c9 Mon Sep 17 00:00:00 2001 From: Zorvalt Date: Tue, 8 Mar 2022 13:47:45 +0100 Subject: [PATCH 12/12] Fix recovery build: missing calendarManager --- src/CMakeLists.txt | 2 ++ src/displayapp/DisplayAppRecovery.cpp | 1 + src/displayapp/DisplayAppRecovery.h | 2 ++ 3 files changed, 5 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a20595babb..86f854139a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -571,6 +571,8 @@ list(APPEND RECOVERY_SOURCE_FILES components/ble/NavigationService.cpp components/ble/HeartRateService.cpp components/ble/MotionService.cpp + components/ble/CalendarService.cpp + components/calendar/CalendarManager.cpp components/firmwarevalidator/FirmwareValidator.cpp components/settings/Settings.cpp components/timer/TimerController.cpp diff --git a/src/displayapp/DisplayAppRecovery.cpp b/src/displayapp/DisplayAppRecovery.cpp index 9d6eb22f68..acd5fc343c 100644 --- a/src/displayapp/DisplayAppRecovery.cpp +++ b/src/displayapp/DisplayAppRecovery.cpp @@ -14,6 +14,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd, Drivers::Cst816S& touchPanel, Controllers::Battery& batteryController, Controllers::Ble& bleController, + Controllers::CalendarManager& calendarManager, Controllers::DateTime& dateTimeController, Drivers::WatchdogView& watchdog, Pinetime::Controllers::NotificationManager& notificationManager, diff --git a/src/displayapp/DisplayAppRecovery.h b/src/displayapp/DisplayAppRecovery.h index 61f1c9bf5c..c8defd9e65 100644 --- a/src/displayapp/DisplayAppRecovery.h +++ b/src/displayapp/DisplayAppRecovery.h @@ -26,6 +26,7 @@ namespace Pinetime { class Settings; class Battery; class Ble; + class CalendarManager; class DateTime; class NotificationManager; class HeartRateController; @@ -49,6 +50,7 @@ namespace Pinetime { Drivers::Cst816S&, Controllers::Battery& batteryController, Controllers::Ble& bleController, + Controllers::CalendarManager& calendarManager, Controllers::DateTime& dateTimeController, Drivers::WatchdogView& watchdog, Pinetime::Controllers::NotificationManager& notificationManager,