From 8c53d0b70baa03c2b07360444a7cd0ad99bb8381 Mon Sep 17 00:00:00 2001 From: Joaquim Date: Wed, 24 Feb 2021 19:40:24 +0000 Subject: Multi face support, analog clock, 12/24 config --- src/displayapp/screens/ApplicationList.cpp | 17 +- src/displayapp/screens/ApplicationList.h | 6 +- src/displayapp/screens/Clock.cpp | 256 +++++----------------------- src/displayapp/screens/Clock.h | 69 ++------ src/displayapp/screens/Screen.h | 20 +++ src/displayapp/screens/ScreenList.h | 111 +++++++++--- src/displayapp/screens/SystemInfo.cpp | 7 +- src/displayapp/screens/Tile.cpp | 5 +- src/displayapp/screens/Tile.h | 3 +- src/displayapp/screens/WatchFaceAnalog.cpp | 214 +++++++++++++++++++++++ src/displayapp/screens/WatchFaceAnalog.h | 90 ++++++++++ src/displayapp/screens/WatchFaceDigital.cpp | 254 +++++++++++++++++++++++++++ src/displayapp/screens/WatchFaceDigital.h | 82 +++++++++ 13 files changed, 832 insertions(+), 302 deletions(-) create mode 100644 src/displayapp/screens/WatchFaceAnalog.cpp create mode 100644 src/displayapp/screens/WatchFaceAnalog.h create mode 100644 src/displayapp/screens/WatchFaceDigital.cpp create mode 100644 src/displayapp/screens/WatchFaceDigital.h (limited to 'src/displayapp/screens') diff --git a/src/displayapp/screens/ApplicationList.cpp b/src/displayapp/screens/ApplicationList.cpp index 531636eb..0f3286df 100644 --- a/src/displayapp/screens/ApplicationList.cpp +++ b/src/displayapp/screens/ApplicationList.cpp @@ -8,13 +8,18 @@ using namespace Pinetime::Applications::Screens; -ApplicationList::ApplicationList(Pinetime::Applications::DisplayApp *app) : +ApplicationList::ApplicationList(Pinetime::Applications::DisplayApp *app, + Pinetime::Controllers::Settings &settingsController) : Screen(app), - screens{app, { + settingsController{settingsController}, + screens{app, + settingsController.GetAppMenu(), + { [this]() -> std::unique_ptr { return CreateScreen1(); }, [this]() -> std::unique_ptr { return CreateScreen2(); }, //[this]() -> std::unique_ptr { return CreateScreen3(); } - } + }, + Screens::ScreenListModes::UpDown } {} @@ -51,7 +56,7 @@ std::unique_ptr ApplicationList::CreateScreen1() { }; - return std::unique_ptr(new Screens::Tile(app, applications)); + return std::unique_ptr(new Screens::Tile(0, app, settingsController, applications)); } std::unique_ptr ApplicationList::CreateScreen2() { @@ -65,7 +70,7 @@ std::unique_ptr ApplicationList::CreateScreen2() { } }; - return std::unique_ptr(new Screens::Tile(app, applications)); + return std::unique_ptr(new Screens::Tile(1, app, settingsController, applications)); } std::unique_ptr ApplicationList::CreateScreen3() { @@ -79,6 +84,6 @@ std::unique_ptr ApplicationList::CreateScreen3() { } }; - return std::unique_ptr(new Screens::Tile(app, applications)); + return std::unique_ptr(new Screens::Tile(2, app, settingsController, applications)); } diff --git a/src/displayapp/screens/ApplicationList.h b/src/displayapp/screens/ApplicationList.h index aefb2385..0a0c6388 100644 --- a/src/displayapp/screens/ApplicationList.h +++ b/src/displayapp/screens/ApplicationList.h @@ -10,12 +10,16 @@ namespace Pinetime { namespace Screens { class ApplicationList : public Screen { public: - explicit ApplicationList(DisplayApp* app); + explicit ApplicationList(DisplayApp* app, + Pinetime::Controllers::Settings &settingsController); ~ApplicationList() override; bool Refresh() override; bool OnButtonPushed() override; bool OnTouchEvent(TouchEvents event) override; private: + + Controllers::Settings& settingsController; + bool running = true; ScreenList<2> screens; diff --git a/src/displayapp/screens/Clock.cpp b/src/displayapp/screens/Clock.cpp index 4b280adb..ea9ddd56 100644 --- a/src/displayapp/screens/Clock.cpp +++ b/src/displayapp/screens/Clock.cpp @@ -10,243 +10,73 @@ #include "components/battery/BatteryController.h" #include "components/ble/BleController.h" #include "components/ble/NotificationManager.h" -#include "components/heartrate/HeartRateController.h" #include "../DisplayApp.h" +#include "WatchFaceDigital.h" +#include "WatchFaceAnalog.h" -using namespace Pinetime::Applications::Screens; -static void event_handler(lv_obj_t * obj, lv_event_t event) { - Clock* screen = static_cast(obj->user_data); - screen->OnObjectEvent(obj, event); -} +using namespace Pinetime::Applications::Screens; Clock::Clock(DisplayApp* app, Controllers::DateTime& dateTimeController, Controllers::Battery& batteryController, Controllers::Ble& bleController, Controllers::NotificationManager& notificatioManager, - Controllers::HeartRateController& heartRateController): Screen(app), currentDateTime{{}}, - dateTimeController{dateTimeController}, batteryController{batteryController}, - bleController{bleController}, notificatioManager{notificatioManager}, - heartRateController{heartRateController} { - displayedChar[0] = 0; - displayedChar[1] = 0; - displayedChar[2] = 0; - displayedChar[3] = 0; - displayedChar[4] = 0; - - batteryIcon = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text(batteryIcon, Symbols::batteryFull); - lv_obj_align(batteryIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, -5, 2); - - batteryPlug = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text(batteryPlug, Symbols::plug); - lv_obj_align(batteryPlug, batteryIcon, LV_ALIGN_OUT_LEFT_MID, -5, 0); - - bleIcon = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text(bleIcon, Symbols::bluetooth); - lv_obj_align(bleIcon, batteryPlug, LV_ALIGN_OUT_LEFT_MID, -5, 0); - - notificationIcon = lv_label_create(lv_scr_act(), NULL); - lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false)); - lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 10, 0); - - label_date = lv_label_create(lv_scr_act(), nullptr); - - lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 60); - - label_time = lv_label_create(lv_scr_act(), nullptr); - - lv_obj_set_style_local_text_font(label_time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_extrabold_compressed); - - lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 0); - - backgroundLabel = lv_label_create(lv_scr_act(), nullptr); - backgroundLabel->user_data = this; - lv_obj_set_click(backgroundLabel, true); - lv_obj_set_event_cb(backgroundLabel, event_handler); - lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP); - lv_obj_set_size(backgroundLabel, 240, 240); - lv_obj_set_pos(backgroundLabel, 0, 0); - lv_label_set_text(backgroundLabel, ""); - - - heartbeatIcon = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text(heartbeatIcon, Symbols::heartBeat); - lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2); - - heartbeatValue = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text(heartbeatValue, "0"); - lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0); - - heartbeatBpm = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text(heartbeatBpm, "BPM"); - lv_obj_align(heartbeatBpm, heartbeatValue, LV_ALIGN_OUT_RIGHT_MID, 5, 0); - - stepValue = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text(stepValue, "0"); - lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2); - - stepIcon = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text(stepIcon, Symbols::shoe); - lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0); -} + Controllers::Settings &settingsController, + Controllers::HeartRateController& heartRateController) : Screen(app), + dateTimeController{dateTimeController}, batteryController{batteryController}, + bleController{bleController}, notificatioManager{notificatioManager}, + settingsController{settingsController}, + heartRateController{heartRateController}, + screens{app, + settingsController.GetClockFace(), + { + [this]() -> std::unique_ptr { return WatchFaceDigitalScreen(); }, + [this]() -> std::unique_ptr { return WatchFaceAnalogScreen(); }, + //[this]() -> std::unique_ptr { return WatchFaceMinimalScreen(); }, + //[this]() -> std::unique_ptr { return WatchFaceCustomScreen(); } + }, + Screens::ScreenListModes::LongPress + } { + + settingsController.SetAppMenu(0); + + } Clock::~Clock() { lv_obj_clean(lv_scr_act()); } -bool Clock::Refresh() { - batteryPercentRemaining = batteryController.PercentRemaining(); - if (batteryPercentRemaining.IsUpdated()) { - auto batteryPercent = batteryPercentRemaining.Get(); - lv_label_set_text(batteryIcon, BatteryIcon::GetBatteryIcon(batteryPercent)); - auto isCharging = batteryController.IsCharging() || batteryController.IsPowerPresent(); - lv_label_set_text(batteryPlug, BatteryIcon::GetPlugIcon(isCharging)); - } - - bleState = bleController.IsConnected(); - if (bleState.IsUpdated()) { - if(bleState.Get() == true) { - lv_label_set_text(bleIcon, BleIcon::GetIcon(true)); - } else { - lv_label_set_text(bleIcon, BleIcon::GetIcon(false)); - } - } - lv_obj_align(batteryIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, -5, 5); - lv_obj_align(batteryPlug, batteryIcon, LV_ALIGN_OUT_LEFT_MID, -5, 0); - lv_obj_align(bleIcon, batteryPlug, LV_ALIGN_OUT_LEFT_MID, -5, 0); - - notificationState = notificatioManager.AreNewNotificationsAvailable(); - if(notificationState.IsUpdated()) { - if(notificationState.Get() == true) - lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(true)); - else - lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false)); - } - - currentDateTime = dateTimeController.CurrentDateTime(); - - if(currentDateTime.IsUpdated()) { - auto newDateTime = currentDateTime.Get(); - - auto dp = date::floor(newDateTime); - auto time = date::make_time(newDateTime-dp); - auto yearMonthDay = date::year_month_day(dp); - - auto year = (int)yearMonthDay.year(); - auto month = static_cast((unsigned)yearMonthDay.month()); - auto day = (unsigned)yearMonthDay.day(); - auto dayOfWeek = static_cast(date::weekday(yearMonthDay).iso_encoding()); - - auto hour = time.hours().count(); - auto minute = time.minutes().count(); - - char minutesChar[3]; - sprintf(minutesChar, "%02d", static_cast(minute)); - - char hoursChar[3]; - sprintf(hoursChar, "%02d", static_cast(hour)); - - char timeStr[6]; - sprintf(timeStr, "%c%c:%c%c", hoursChar[0],hoursChar[1],minutesChar[0], minutesChar[1]); - - if(hoursChar[0] != displayedChar[0] || hoursChar[1] != displayedChar[1] || minutesChar[0] != displayedChar[2] || minutesChar[1] != displayedChar[3]) { - displayedChar[0] = hoursChar[0]; - displayedChar[1] = hoursChar[1]; - displayedChar[2] = minutesChar[0]; - displayedChar[3] = minutesChar[1]; - - lv_label_set_text(label_time, timeStr); - } - - if ((year != currentYear) || (month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) { - char dateStr[22]; - sprintf(dateStr, "%s %d %s %d", DayOfWeekToString(dayOfWeek), day, MonthToString(month), year); - lv_label_set_text(label_date, dateStr); - - - currentYear = year; - currentMonth = month; - currentDayOfWeek = dayOfWeek; - currentDay = day; - } - } - - heartbeat = heartRateController.HeartRate(); - heartbeatRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped; - if(heartbeat.IsUpdated() || heartbeatRunning.IsUpdated()) { - char heartbeatBuffer[4]; - if(heartbeatRunning.Get()) - sprintf(heartbeatBuffer, "%d", heartbeat.Get()); - else - sprintf(heartbeatBuffer, "---"); - - lv_label_set_text(heartbeatValue, heartbeatBuffer); - lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2); - lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0); - lv_obj_align(heartbeatBpm, heartbeatValue, LV_ALIGN_OUT_RIGHT_MID, 5, 0); - } - - // TODO stepCount = stepController.GetValue(); - if(stepCount.IsUpdated()) { - char stepBuffer[5]; - sprintf(stepBuffer, "%lu", stepCount.Get()); - lv_label_set_text(stepValue, stepBuffer); - lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2); - lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0); - } +bool Clock::Refresh() { + screens.Refresh(); return running; } -const char *Clock::MonthToString(Pinetime::Controllers::DateTime::Months month) { - return Clock::MonthsString[static_cast(month)]; +bool Clock::OnButtonPushed() { + running = false; + return false; } -const char *Clock::DayOfWeekToString(Pinetime::Controllers::DateTime::Days dayOfWeek) { - return Clock::DaysString[static_cast(dayOfWeek)]; +bool Clock::OnTouchEvent(Pinetime::Applications::TouchEvents event) { + return screens.OnTouchEvent(event); } -char const *Clock::DaysString[] = { - "", - "MONDAY", - "TUESDAY", - "WEDNESDAY", - "THURSDAY", - "FRIDAY", - "SATURDAY", - "SUNDAY" -}; - -char const *Clock::MonthsString[] = { - "", - "JAN", - "FEB", - "MAR", - "APR", - "MAY", - "JUN", - "JUL", - "AUG", - "SEP", - "OCT", - "NOV", - "DEC" -}; - -void Clock::OnObjectEvent(lv_obj_t *obj, lv_event_t event) { - if(obj == backgroundLabel) { - if (event == LV_EVENT_CLICKED) { - - running = false; - } - } +std::unique_ptr Clock::WatchFaceDigitalScreen() { + return std::unique_ptr(new Screens::WatchFaceDigital(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController, heartRateController)); } -bool Clock::OnButtonPushed() { - running = false; - return false; +std::unique_ptr Clock::WatchFaceAnalogScreen() { + return std::unique_ptr(new Screens::WatchFaceAnalog(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController)); } +/* +// examples +std::unique_ptr Clock::WatchFaceMinimalScreen() { + return std::unique_ptr(new Screens::WatchFaceMinimal(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController)); +} +std::unique_ptr Clock::WatchFaceCustomScreen() { + return std::unique_ptr(new Screens::WatchFaceCustom(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController)); +} +*/ \ No newline at end of file diff --git a/src/displayapp/screens/Clock.h b/src/displayapp/screens/Clock.h index 18d70532..2b1be4ac 100644 --- a/src/displayapp/screens/Clock.h +++ b/src/displayapp/screens/Clock.h @@ -5,38 +5,22 @@ #include #include #include "Screen.h" +#include "ScreenList.h" #include "components/datetime/DateTimeController.h" namespace Pinetime { + namespace Drivers { + class BMA421; + } namespace Controllers { + class Settings; class Battery; class Ble; class NotificationManager; - class HeartRateController; } namespace Applications { namespace Screens { - - template - class DirtyValue { - public: - DirtyValue() = default; // Use NSDMI - explicit DirtyValue(T const& v):value{v}{} // Use MIL and const-lvalue-ref - bool IsUpdated() const { return isUpdated; } - T const& Get() { this->isUpdated = false; return value; } // never expose a non-const lvalue-ref - DirtyValue& operator=(const T& other) { - if (this->value != other) { - this->value = other; - this->isUpdated = true; - } - return *this; - } - private: - T value{}; // NSDMI - default initialise type - bool isUpdated{true}; // NSDMI - use brace initilisation - }; - class Clock : public Screen { public: Clock(DisplayApp* app, @@ -44,53 +28,30 @@ namespace Pinetime { Controllers::Battery& batteryController, Controllers::Ble& bleController, Controllers::NotificationManager& notificatioManager, + Controllers::Settings &settingsController, Controllers::HeartRateController& heartRateController); ~Clock() override; bool Refresh() override; bool OnButtonPushed() override; + bool OnTouchEvent(TouchEvents event) override; - void OnObjectEvent(lv_obj_t *pObj, lv_event_t i); private: - static const char* MonthToString(Pinetime::Controllers::DateTime::Months month); - static const char* DayOfWeekToString(Pinetime::Controllers::DateTime::Days dayOfWeek); - static char const *DaysString[]; - static char const *MonthsString[]; - - char displayedChar[5]; - - uint16_t currentYear = 1970; - Pinetime::Controllers::DateTime::Months currentMonth = Pinetime::Controllers::DateTime::Months::Unknown; - Pinetime::Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown; - uint8_t currentDay = 0; - - DirtyValue batteryPercentRemaining {}; - DirtyValue bleState {}; - DirtyValue> currentDateTime{}; - DirtyValue stepCount {}; - DirtyValue heartbeat {}; - DirtyValue heartbeatRunning {}; - DirtyValue notificationState {}; - - lv_obj_t* label_time; - lv_obj_t* label_date; - lv_obj_t* backgroundLabel; - lv_obj_t* batteryIcon; - lv_obj_t* bleIcon; - lv_obj_t* batteryPlug; - lv_obj_t* heartbeatIcon; - lv_obj_t* heartbeatValue; - lv_obj_t* heartbeatBpm; - lv_obj_t* stepIcon; - lv_obj_t* stepValue; - lv_obj_t* notificationIcon; Controllers::DateTime& dateTimeController; Controllers::Battery& batteryController; Controllers::Ble& bleController; Controllers::NotificationManager& notificatioManager; + Controllers::Settings& settingsController; Controllers::HeartRateController& heartRateController; + + ScreenList<2> screens; + std::unique_ptr WatchFaceDigitalScreen(); + std::unique_ptr WatchFaceAnalogScreen(); + std::unique_ptr WatchFaceMinimalScreen(); + std::unique_ptr WatchFaceCustomScreen(); + bool running = true; }; diff --git a/src/displayapp/screens/Screen.h b/src/displayapp/screens/Screen.h index 6b1d0eec..638dac99 100644 --- a/src/displayapp/screens/Screen.h +++ b/src/displayapp/screens/Screen.h @@ -7,6 +7,26 @@ namespace Pinetime { namespace Applications { class DisplayApp; namespace Screens { + + template + class DirtyValue { + public: + DirtyValue() = default; // Use NSDMI + explicit DirtyValue(T const& v):value{v}{} // Use MIL and const-lvalue-ref + bool IsUpdated() const { return isUpdated; } + T const& Get() { this->isUpdated = false; return value; } // never expose a non-const lvalue-ref + DirtyValue& operator=(const T& other) { + if (this->value != other) { + this->value = other; + this->isUpdated = true; + } + return *this; + } + private: + T value{}; // NSDMI - default initialise type + bool isUpdated{true}; // NSDMI - use brace initilisation + }; + class Screen { public: explicit Screen(DisplayApp* app) : app{app} {} diff --git a/src/displayapp/screens/ScreenList.h b/src/displayapp/screens/ScreenList.h index 736e3634..56d9abe0 100644 --- a/src/displayapp/screens/ScreenList.h +++ b/src/displayapp/screens/ScreenList.h @@ -9,16 +9,33 @@ namespace Pinetime { namespace Applications { namespace Screens { + + enum class ScreenListModes {UpDown, RightLeft, LongPress}; template class ScreenList : public Screen { public: - ScreenList(DisplayApp* app, std::array()>, N>&& screens) - : Screen(app), screens{std::move(screens)}, current{this->screens[0]()} { + ScreenList( + DisplayApp* app, + uint8_t initScreen, + std::array()>, N>&& screens, + ScreenListModes mode + ) + : Screen(app), + initScreen{initScreen}, + screens{std::move(screens)}, + mode{mode}, + current{this->screens[initScreen]()} + { + screenIndex = initScreen; } ~ScreenList() override { - + current.reset(nullptr); + /*for(uint8_t i = 0; i < screens.size(); i++) { + screens[i]().reset(nullptr); + }*/ + lv_obj_clean(lv_scr_act()); } bool Refresh() override { @@ -32,34 +49,80 @@ namespace Pinetime { } bool OnTouchEvent(TouchEvents event) override { - switch (event) { - case TouchEvents::SwipeDown: - if (screenIndex > 0) { - current.reset(nullptr); - app->SetFullRefresh(DisplayApp::FullRefreshDirections::Down); - screenIndex--; - current = screens[screenIndex](); - } - return true; - case TouchEvents::SwipeUp: - if (screenIndex < screens.size() - 1) { - current.reset(nullptr); - app->SetFullRefresh(DisplayApp::FullRefreshDirections::Up); - screenIndex++; - current = screens[screenIndex](); - } - return true; - default: - return false; + + if ( mode == ScreenListModes::UpDown) { + switch (event) { + case TouchEvents::SwipeDown: + if (screenIndex > 0) { + current.reset(nullptr); + app->SetFullRefresh(DisplayApp::FullRefreshDirections::Down); + screenIndex--; + current = screens[screenIndex](); + return true; + } else { + return false; + } + + case TouchEvents::SwipeUp: + if (screenIndex < screens.size() - 1) { + current.reset(nullptr); + app->SetFullRefresh(DisplayApp::FullRefreshDirections::Up); + screenIndex++; + current = screens[screenIndex](); + } + return true; + default: + return false; + } + } else if ( mode == ScreenListModes::RightLeft) { + switch (event) { + case TouchEvents::SwipeRight: + if (screenIndex > 0) { + current.reset(nullptr); + app->SetFullRefresh(DisplayApp::FullRefreshDirections::None); + screenIndex--; + current = screens[screenIndex](); + return true; + } else { + return false; + } + + case TouchEvents::SwipeLeft: + if (screenIndex < screens.size() - 1) { + current.reset(nullptr); + app->SetFullRefresh(DisplayApp::FullRefreshDirections::None); + screenIndex++; + current = screens[screenIndex](); + } + return true; + default: + return false; + } + } else if ( event == TouchEvents::LongTap ) { + if (screenIndex < screens.size() - 1) { + screenIndex++; + } else { + screenIndex = 0; + } + current.reset(nullptr); + app->SetFullRefresh(DisplayApp::FullRefreshDirections::None); + current = screens[screenIndex](); + return true; } + return false; } private: - bool running = true; - uint8_t screenIndex = 0; + + uint8_t initScreen = 0; std::array()>, N> screens; + ScreenListModes mode = ScreenListModes::UpDown; + + uint8_t screenIndex = 0; std::unique_ptr current; + + bool running = true; }; } } diff --git a/src/displayapp/screens/SystemInfo.cpp b/src/displayapp/screens/SystemInfo.cpp index 2de5dada..3c6d1d9d 100644 --- a/src/displayapp/screens/SystemInfo.cpp +++ b/src/displayapp/screens/SystemInfo.cpp @@ -20,11 +20,14 @@ SystemInfo::SystemInfo(Pinetime::Applications::DisplayApp *app, Screen(app), dateTimeController{dateTimeController}, batteryController{batteryController}, brightnessController{brightnessController}, bleController{bleController}, watchdog{watchdog}, - screens{app, { + screens{app, + 0, + { [this]() -> std::unique_ptr { return CreateScreen1(); }, [this]() -> std::unique_ptr { return CreateScreen2(); }, [this]() -> std::unique_ptr { return CreateScreen3(); } - } + }, + Screens::ScreenListModes::UpDown } {} diff --git a/src/displayapp/screens/Tile.cpp b/src/displayapp/screens/Tile.cpp index 3b82b060..ca753db9 100644 --- a/src/displayapp/screens/Tile.cpp +++ b/src/displayapp/screens/Tile.cpp @@ -10,7 +10,10 @@ static void event_handler(lv_obj_t * obj, lv_event_t event) { screen->OnObjectEvent(obj, event, eventData); } -Tile::Tile(DisplayApp* app, std::array& applications) : Screen(app) { +Tile::Tile(uint8_t screenID, DisplayApp* app, Controllers::Settings& settingsController, std::array& applications) : Screen(app) { + + settingsController.SetAppMenu(screenID); + for(int i = 0, appIndex = 0; i < 8; i++) { if(i == 3) btnm_map1[i] = "\n"; else if(i == 7) btnm_map1[i] = ""; diff --git a/src/displayapp/screens/Tile.h b/src/displayapp/screens/Tile.h index 55ed45e3..f717a220 100644 --- a/src/displayapp/screens/Tile.h +++ b/src/displayapp/screens/Tile.h @@ -5,6 +5,7 @@ #include #include "Screen.h" #include "../Apps.h" +#include "components/settings/Settings.h" namespace Pinetime { namespace Applications { @@ -16,7 +17,7 @@ namespace Pinetime { Pinetime::Applications::Apps application; }; - explicit Tile(DisplayApp* app, std::array& applications); + explicit Tile(uint8_t screenID, DisplayApp* app, Controllers::Settings& settingsController, std::array& applications); ~Tile() override; bool Refresh() override; diff --git a/src/displayapp/screens/WatchFaceAnalog.cpp b/src/displayapp/screens/WatchFaceAnalog.cpp new file mode 100644 index 00000000..efca10c2 --- /dev/null +++ b/src/displayapp/screens/WatchFaceAnalog.cpp @@ -0,0 +1,214 @@ +#include +#include "WatchFaceAnalog.h" +#include "BatteryIcon.h" +#include "BleIcon.h" +#include "Symbols.h" +#include "NotificationIcon.h" + +LV_IMG_DECLARE(bg_clock); + +using namespace Pinetime::Applications::Screens; + +#define HOUR_LENGTH 70 +#define MINUTE_LENGTH 90 +#define SECOND_LENGTH 110 +#define PI 3.14159265358979323846 + +// ## +static int16_t coordinate_x_relocate(int16_t x) +{ + return ((x) + LV_HOR_RES / 2); +} + +// ## +static int16_t coordinate_y_relocate(int16_t y) +{ + return (((y) - LV_HOR_RES / 2) < 0) ? (0 - ((y) - LV_HOR_RES / 2)) : ((y) - LV_HOR_RES / 2); +} + +WatchFaceAnalog::WatchFaceAnalog(Pinetime::Applications::DisplayApp *app, + Controllers::DateTime& dateTimeController, + Controllers::Battery& batteryController, + Controllers::Ble& bleController, + Controllers::NotificationManager& notificatioManager, + Controllers::Settings &settingsController) : Screen(app), currentDateTime{{}}, + dateTimeController{dateTimeController}, batteryController{batteryController}, + bleController{bleController}, notificatioManager{notificatioManager}, + settingsController{settingsController} { + settingsController.SetClockFace(1); + + sHour = 99; + sMinute = 99; + sSecond = 99; + + lv_obj_t * bg_clock_img = lv_img_create(lv_scr_act(), NULL); + lv_img_set_src(bg_clock_img, &bg_clock); + lv_obj_align(bg_clock_img, NULL, LV_ALIGN_CENTER, 0, 0); + + batteryIcon = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text(batteryIcon, Symbols::batteryHalf); + lv_obj_align(batteryIcon, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, -8, -4); + + + notificationIcon = lv_label_create(lv_scr_act(), NULL); + lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FF00)); + lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false)); + lv_obj_align(notificationIcon, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 8, -4); + + // Date - Day / Week day + + label_date_day = lv_label_create(lv_scr_act(), NULL); + lv_obj_set_style_local_text_color(label_date_day, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xf0a500)); + lv_label_set_text_fmt(label_date_day, "%s\n%02i", dateTimeController.DayOfWeekShortToString(), dateTimeController.Day()); + lv_label_set_align( label_date_day, LV_LABEL_ALIGN_CENTER ); + lv_obj_align(label_date_day, NULL, LV_ALIGN_CENTER, 50, 0); + + minute_body = lv_line_create(lv_scr_act(), NULL); + minute_body_trace = lv_line_create(lv_scr_act(), NULL); + hour_body = lv_line_create(lv_scr_act(), NULL); + hour_body_trace = lv_line_create(lv_scr_act(), NULL); + second_body = lv_line_create(lv_scr_act(), NULL); + + + lv_style_init(&second_line_style); + lv_style_set_line_width(&second_line_style, LV_STATE_DEFAULT, 3); + lv_style_set_line_color(&second_line_style, LV_STATE_DEFAULT, LV_COLOR_RED); + lv_style_set_line_rounded(&second_line_style, LV_STATE_DEFAULT, true); + lv_obj_add_style(second_body, LV_LINE_PART_MAIN, &second_line_style); + + lv_style_init(&minute_line_style); + lv_style_set_line_width(&minute_line_style, LV_STATE_DEFAULT, 7); + lv_style_set_line_color(&minute_line_style, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_style_set_line_rounded(&minute_line_style, LV_STATE_DEFAULT, true); + lv_obj_add_style(minute_body, LV_LINE_PART_MAIN, &minute_line_style); + + lv_style_init(&minute_line_style_trace); + lv_style_set_line_width(&minute_line_style_trace, LV_STATE_DEFAULT, 3); + lv_style_set_line_color(&minute_line_style_trace, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_style_set_line_rounded(&minute_line_style_trace, LV_STATE_DEFAULT, false); + lv_obj_add_style(minute_body_trace, LV_LINE_PART_MAIN, &minute_line_style_trace); + + + lv_style_init(&hour_line_style); + lv_style_set_line_width(&hour_line_style, LV_STATE_DEFAULT, 7); + lv_style_set_line_color(&hour_line_style, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_style_set_line_rounded(&hour_line_style, LV_STATE_DEFAULT, true); + lv_obj_add_style(hour_body, LV_LINE_PART_MAIN, &hour_line_style); + + lv_style_init(&hour_line_style_trace); + lv_style_set_line_width(&hour_line_style_trace, LV_STATE_DEFAULT, 3); + lv_style_set_line_color(&hour_line_style_trace, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_style_set_line_rounded(&hour_line_style_trace, LV_STATE_DEFAULT, false); + lv_obj_add_style(hour_body_trace, LV_LINE_PART_MAIN, &hour_line_style_trace); + + UpdateClock(); + + /*lv_obj_t* backgroundLabel = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP); + lv_obj_set_size(backgroundLabel, 240, 240); + lv_obj_set_pos(backgroundLabel, 0, 0); + lv_label_set_text_static(backgroundLabel, "");*/ + +} + +WatchFaceAnalog::~WatchFaceAnalog() { + + lv_style_reset(&hour_line_style); + lv_style_reset(&hour_line_style_trace); + lv_style_reset(&minute_line_style); + lv_style_reset(&minute_line_style_trace); + lv_style_reset(&second_line_style); + + lv_obj_clean(lv_scr_act()); +} + +void WatchFaceAnalog::UpdateClock() { + + hour = dateTimeController.Hours(); + minute = dateTimeController.Minutes(); + second = dateTimeController.Seconds(); + + if(sMinute != minute) { + minute_point[0].x = coordinate_x_relocate(30 * sin(minute * 6 * PI / 180)); + minute_point[0].y = coordinate_y_relocate(30 * cos(minute * 6 * PI / 180)); + minute_point[1].x = coordinate_x_relocate(MINUTE_LENGTH * sin(minute * 6 * PI / 180)); + minute_point[1].y = coordinate_y_relocate(MINUTE_LENGTH * cos(minute * 6 * PI / 180)); + + minute_point_trace[0].x = coordinate_x_relocate(5 * sin(minute * 6 * PI / 180)); + minute_point_trace[0].y = coordinate_y_relocate(5 * cos(minute * 6 * PI / 180)); + minute_point_trace[1].x = coordinate_x_relocate(31 * sin(minute * 6 * PI / 180)); + minute_point_trace[1].y = coordinate_y_relocate(31 * cos(minute * 6 * PI / 180)); + + lv_line_set_points(minute_body, minute_point, 2); + lv_line_set_points(minute_body_trace, minute_point_trace, 2); + } + + if(sHour != hour || sMinute != minute) { + sHour = hour; + sMinute = minute; + hour_point[0].x = coordinate_x_relocate(30 * sin((((hour > 12 ? hour - 12 : hour) * 30) + (minute * 0.5)) * PI / 180)); + hour_point[0].y = coordinate_y_relocate(30 * cos((((hour > 12 ? hour - 12 : hour) * 30) + (minute * 0.5)) * PI / 180)); + hour_point[1].x = coordinate_x_relocate(HOUR_LENGTH * sin((((hour > 12 ? hour - 12 : hour) * 30) + (minute * 0.5)) * PI / 180)); + hour_point[1].y = coordinate_y_relocate(HOUR_LENGTH * cos((((hour > 12 ? hour - 12 : hour) * 30) + (minute * 0.5)) * PI / 180)); + + hour_point_trace[0].x = coordinate_x_relocate(5 * sin((((hour > 12 ? hour - 12 : hour) * 30) + (minute * 0.5)) * PI / 180)); + hour_point_trace[0].y = coordinate_y_relocate(5 * cos((((hour > 12 ? hour - 12 : hour) * 30) + (minute * 0.5)) * PI / 180)); + hour_point_trace[1].x = coordinate_x_relocate(31 * sin((((hour > 12 ? hour - 12 : hour) * 30) + (minute * 0.5)) * PI / 180)); + hour_point_trace[1].y = coordinate_y_relocate(31 * cos((((hour > 12 ? hour - 12 : hour) * 30) + (minute * 0.5)) * PI / 180)); + + lv_line_set_points(hour_body, hour_point, 2); + lv_line_set_points(hour_body_trace, hour_point_trace, 2); + } + + if(sSecond != second) { + sSecond = second; + second_point[0].x = coordinate_x_relocate(20 * sin((180 + second * 6) * PI / 180)); + second_point[0].y = coordinate_y_relocate(20 * cos((180 + second * 6) * PI / 180)); + second_point[1].x = coordinate_x_relocate(SECOND_LENGTH * sin(second * 6 * PI / 180)); + second_point[1].y = coordinate_y_relocate(SECOND_LENGTH * cos(second * 6 * PI / 180)); + lv_line_set_points(second_body, second_point, 2); + + } +} + + +bool WatchFaceAnalog::Refresh() { + + batteryPercentRemaining = batteryController.PercentRemaining(); + if (batteryPercentRemaining.IsUpdated()) { + auto batteryPercent = batteryPercentRemaining.Get(); + lv_label_set_text(batteryIcon, BatteryIcon::GetBatteryIcon(batteryPercent)); + } + + + notificationState = notificatioManager.AreNewNotificationsAvailable(); + + if(notificationState.IsUpdated()) { + if(notificationState.Get() == true) + lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(true)); + else + lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false)); + } + + currentDateTime = dateTimeController.CurrentDateTime(); + + if(currentDateTime.IsUpdated()) { + + month = dateTimeController.Month(); + day = dateTimeController.Day(); + dayOfWeek = dateTimeController.DayOfWeek(); + + UpdateClock(); + + if ((month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) { + + lv_label_set_text_fmt(label_date_day, "%s\n%02i", dateTimeController.DayOfWeekShortToString(), day); + + currentMonth = month; + currentDayOfWeek = dayOfWeek; + currentDay = day; + } + } + + return true; +} \ No newline at end of file diff --git a/src/displayapp/screens/WatchFaceAnalog.h b/src/displayapp/screens/WatchFaceAnalog.h new file mode 100644 index 00000000..56b086ab --- /dev/null +++ b/src/displayapp/screens/WatchFaceAnalog.h @@ -0,0 +1,90 @@ +#pragma once + +#include +#include +#include +#include +#include "Screen.h" +#include "ScreenList.h" +#include "components/datetime/DateTimeController.h" +#include "components/battery/BatteryController.h" +#include "components/ble/BleController.h" +#include "components/ble/NotificationManager.h" + +namespace Pinetime { + namespace Controllers { + class Settings; + class Battery; + class Ble; + class NotificationManager; + } + namespace Applications { + namespace Screens { + + class WatchFaceAnalog : public Screen { + public: + WatchFaceAnalog(DisplayApp* app, + Controllers::DateTime& dateTimeController, + Controllers::Battery& batteryController, + Controllers::Ble& bleController, + Controllers::NotificationManager& notificatioManager, + Controllers::Settings &settingsController); + + ~WatchFaceAnalog() override; + + bool Refresh() override; + + private: + uint8_t sHour, sMinute, sSecond; + uint8_t hour; + uint8_t minute; + uint8_t second; + + Pinetime::Controllers::DateTime::Months month; + uint8_t day; + Pinetime::Controllers::DateTime::Days dayOfWeek; + + Pinetime::Controllers::DateTime::Months currentMonth = Pinetime::Controllers::DateTime::Months::Unknown; + Pinetime::Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown; + uint8_t currentDay = 0; + + DirtyValue batteryPercentRemaining {0}; + DirtyValue> currentDateTime; + DirtyValue notificationState {false}; + + lv_obj_t *hour_body; + lv_obj_t *hour_body_trace; + lv_obj_t *minute_body; + lv_obj_t *minute_body_trace; + lv_obj_t *second_body; + + // ## + lv_point_t hour_point[2]; + lv_point_t hour_point_trace[2]; + lv_point_t minute_point[2]; + lv_point_t minute_point_trace[2]; + lv_point_t second_point[2]; + + // ## + lv_style_t hour_line_style; + lv_style_t hour_line_style_trace; + lv_style_t minute_line_style; + lv_style_t minute_line_style_trace; + lv_style_t second_line_style; + + lv_obj_t* label_date_day; + lv_obj_t* batteryIcon; + lv_obj_t* notificationIcon; + + + Controllers::DateTime& dateTimeController; + Controllers::Battery& batteryController; + Controllers::Ble& bleController; + Controllers::NotificationManager& notificatioManager; + Controllers::Settings& settingsController; + + void UpdateClock(); + }; + } + } +} \ No newline at end of file diff --git a/src/displayapp/screens/WatchFaceDigital.cpp b/src/displayapp/screens/WatchFaceDigital.cpp new file mode 100644 index 00000000..99df318a --- /dev/null +++ b/src/displayapp/screens/WatchFaceDigital.cpp @@ -0,0 +1,254 @@ +#include "WatchFaceDigital.h" + +#include +#include +#include +#include "BatteryIcon.h" +#include "BleIcon.h" +#include "NotificationIcon.h" +#include "Symbols.h" +#include "components/battery/BatteryController.h" +#include "components/ble/BleController.h" +#include "components/ble/NotificationManager.h" +#include "components/heartrate/HeartRateController.h" +#include "components/settings/Settings.h" +#include "../DisplayApp.h" + +using namespace Pinetime::Applications::Screens; + + +WatchFaceDigital::WatchFaceDigital(DisplayApp* app, + Controllers::DateTime& dateTimeController, + Controllers::Battery& batteryController, + Controllers::Ble& bleController, + Controllers::NotificationManager& notificatioManager, + Controllers::Settings &settingsController, + Controllers::HeartRateController& heartRateController): Screen(app), currentDateTime{{}}, + dateTimeController{dateTimeController}, batteryController{batteryController}, + bleController{bleController}, notificatioManager{notificatioManager}, + settingsController{settingsController}, + heartRateController{heartRateController} { + settingsController.SetClockFace(0); + + // init + /*currentDateTime = dateTimeController.CurrentDateTime(); + batteryPercentRemaining = batteryController.PercentRemaining(); + bleState = bleController.IsConnected(); + notificationState = notificatioManager.AreNewNotificationsAvailable(); + heartbeat = heartRateController.HeartRate();*/ + + displayedChar[0] = 0; + displayedChar[1] = 0; + displayedChar[2] = 0; + displayedChar[3] = 0; + displayedChar[4] = 0; + + batteryIcon = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text(batteryIcon, Symbols::batteryFull); + lv_obj_align(batteryIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, -5, 2); + + batteryPlug = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(batteryPlug, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFF0000)); + lv_label_set_text(batteryPlug, Symbols::plug); + lv_obj_align(batteryPlug, batteryIcon, LV_ALIGN_OUT_LEFT_MID, -5, 0); + + bleIcon = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(bleIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x0000FF)); + lv_label_set_text(bleIcon, Symbols::bluetooth); + lv_obj_align(bleIcon, batteryPlug, LV_ALIGN_OUT_LEFT_MID, -5, 0); + + notificationIcon = lv_label_create(lv_scr_act(), NULL); + lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FF00)); + lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false)); + lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 10, 0); + + label_date = lv_label_create(lv_scr_act(), nullptr); + lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_CENTER, 0, 60); + lv_obj_set_style_local_text_color(label_date, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999)); + + label_time = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_font(label_time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_extrabold_compressed); + + lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0); + + label_time_ampm = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text_static(label_time_ampm, ""); + lv_obj_align(label_time_ampm, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -30, -55); + + backgroundLabel = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_click(backgroundLabel, true); + lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP); + lv_obj_set_size(backgroundLabel, 240, 240); + lv_obj_set_pos(backgroundLabel, 0, 0); + lv_label_set_text(backgroundLabel, ""); + + + heartbeatIcon = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text(heartbeatIcon, Symbols::heartBeat); + lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xCE1B1B)); + lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2); + + heartbeatValue = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(heartbeatValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xCE1B1B)); + lv_label_set_text(heartbeatValue, "---"); + lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0); + + heartbeatBpm = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(heartbeatBpm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xCE1B1B)); + lv_label_set_text(heartbeatBpm, "BPM"); + lv_obj_align(heartbeatBpm, heartbeatValue, LV_ALIGN_OUT_RIGHT_MID, 5, 0); + + stepValue = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FFE7)); + lv_label_set_text(stepValue, "0"); + lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2); + + stepIcon = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FFE7)); + lv_label_set_text(stepIcon, Symbols::shoe); + lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0); +} + +WatchFaceDigital::~WatchFaceDigital() { + lv_obj_clean(lv_scr_act()); +} + +bool WatchFaceDigital::Refresh() { + batteryPercentRemaining = batteryController.PercentRemaining(); + if (batteryPercentRemaining.IsUpdated()) { + auto batteryPercent = batteryPercentRemaining.Get(); + lv_label_set_text(batteryIcon, BatteryIcon::GetBatteryIcon(batteryPercent)); + auto isCharging = batteryController.IsCharging() || batteryController.IsPowerPresent(); + lv_label_set_text(batteryPlug, BatteryIcon::GetPlugIcon(isCharging)); + } + + bleState = bleController.IsConnected(); + if (bleState.IsUpdated()) { + if(bleState.Get() == true) { + lv_label_set_text(bleIcon, BleIcon::GetIcon(true)); + } else { + lv_label_set_text(bleIcon, BleIcon::GetIcon(false)); + } + } + lv_obj_align(batteryIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, -5, 5); + lv_obj_align(batteryPlug, batteryIcon, LV_ALIGN_OUT_LEFT_MID, -5, 0); + lv_obj_align(bleIcon, batteryPlug, LV_ALIGN_OUT_LEFT_MID, -5, 0); + + notificationState = notificatioManager.AreNewNotificationsAvailable(); + if(notificationState.IsUpdated()) { + if(notificationState.Get() == true) + lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(true)); + else + lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false)); + } + + currentDateTime = dateTimeController.CurrentDateTime(); + + if(currentDateTime.IsUpdated()) { + auto newDateTime = currentDateTime.Get(); + + auto dp = date::floor(newDateTime); + auto time = date::make_time(newDateTime-dp); + auto yearMonthDay = date::year_month_day(dp); + + auto year = (int)yearMonthDay.year(); + auto month = static_cast((unsigned)yearMonthDay.month()); + auto day = (unsigned)yearMonthDay.day(); + auto dayOfWeek = static_cast(date::weekday(yearMonthDay).iso_encoding()); + + int hour = time.hours().count(); + auto minute = time.minutes().count(); + + char minutesChar[3]; + sprintf(minutesChar, "%02d", static_cast(minute)); + + char hoursChar[3]; + char ampmChar[3]; + if ( settingsController.GetClockType() == Controllers::Settings::ClockType::H24 ) { + sprintf(hoursChar, "%02d", hour); + } else { + if (hour > 12) { + hour -= 12; + sprintf(ampmChar, "PM"); + } else { + sprintf(ampmChar, "AM"); + } + sprintf(hoursChar, "%02d", hour); + } + + if(hoursChar[0] != displayedChar[0] || hoursChar[1] != displayedChar[1] || minutesChar[0] != displayedChar[2] || minutesChar[1] != displayedChar[3]) { + displayedChar[0] = hoursChar[0]; + displayedChar[1] = hoursChar[1]; + displayedChar[2] = minutesChar[0]; + displayedChar[3] = minutesChar[1]; + + char timeStr[6]; + + if ( settingsController.GetClockType() == Controllers::Settings::ClockType::H12 ) { + lv_label_set_text(label_time_ampm, ampmChar); + if ( hoursChar[0] == '0' ) { hoursChar[0] = ' '; } + } + + sprintf(timeStr, "%c%c:%c%c", hoursChar[0],hoursChar[1],minutesChar[0], minutesChar[1]); + lv_label_set_text(label_time, timeStr); + + if ( settingsController.GetClockType() == Controllers::Settings::ClockType::H12 ) { + lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0); + } else { + lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + } + + } + + if ((year != currentYear) || (month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) { + char dateStr[22]; + if ( settingsController.GetClockType() == Controllers::Settings::ClockType::H24 ) { + sprintf(dateStr, "%s %d %s %d", dateTimeController.DayOfWeekShortToString(), day, dateTimeController.MonthShortToString(), year); + } else { + sprintf(dateStr, "%s %s %d %d", dateTimeController.DayOfWeekShortToString(), dateTimeController.MonthShortToString(), day, year); + } + lv_label_set_text(label_date, dateStr); + lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_CENTER, 0, 60); + + + currentYear = year; + currentMonth = month; + currentDayOfWeek = dayOfWeek; + currentDay = day; + } + } + + heartbeat = heartRateController.HeartRate(); + heartbeatRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped; + if(heartbeat.IsUpdated() || heartbeatRunning.IsUpdated()) { + char heartbeatBuffer[4]; + if(heartbeatRunning.Get()) + sprintf(heartbeatBuffer, "%d", heartbeat.Get()); + else + sprintf(heartbeatBuffer, "---"); + + lv_label_set_text(heartbeatValue, heartbeatBuffer); + lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2); + lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0); + lv_obj_align(heartbeatBpm, heartbeatValue, LV_ALIGN_OUT_RIGHT_MID, 5, 0); + } + + // TODO stepCount = stepController.GetValue(); + if(stepCount.IsUpdated()) { + char stepBuffer[5]; + sprintf(stepBuffer, "%lu", stepCount.Get()); + lv_label_set_text(stepValue, stepBuffer); + lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2); + lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0); + } + + return running; +} + + +bool WatchFaceDigital::OnButtonPushed() { + running = false; + return false; +} + + diff --git a/src/displayapp/screens/WatchFaceDigital.h b/src/displayapp/screens/WatchFaceDigital.h new file mode 100644 index 00000000..70a9ce5d --- /dev/null +++ b/src/displayapp/screens/WatchFaceDigital.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include +#include +#include "Screen.h" +#include "ScreenList.h" +#include "components/datetime/DateTimeController.h" + +namespace Pinetime { + namespace Controllers { + class Settings; + class Battery; + class Ble; + class NotificationManager; + class HeartRateController; + } + + namespace Applications { + namespace Screens { + + class WatchFaceDigital : public Screen { + public: + WatchFaceDigital(DisplayApp* app, + Controllers::DateTime& dateTimeController, + Controllers::Battery& batteryController, + Controllers::Ble& bleController, + Controllers::NotificationManager& notificatioManager, + Controllers::Settings &settingsController, + Controllers::HeartRateController& heartRateController); + ~WatchFaceDigital() override; + + bool Refresh() override; + bool OnButtonPushed() override; + + void OnObjectEvent(lv_obj_t *pObj, lv_event_t i); + private: + + + char displayedChar[5]; + + uint16_t currentYear = 1970; + Pinetime::Controllers::DateTime::Months currentMonth = Pinetime::Controllers::DateTime::Months::Unknown; + Pinetime::Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown; + uint8_t currentDay = 0; + + DirtyValue batteryPercentRemaining {}; + DirtyValue bleState {}; + DirtyValue> currentDateTime{}; + DirtyValue stepCount {}; + DirtyValue heartbeat {}; + DirtyValue heartbeatRunning {}; + DirtyValue notificationState {}; + + lv_obj_t* label_time; + lv_obj_t* label_time_ampm; + lv_obj_t* label_date; + lv_obj_t* backgroundLabel; + lv_obj_t* batteryIcon; + lv_obj_t* bleIcon; + lv_obj_t* batteryPlug; + lv_obj_t* heartbeatIcon; + lv_obj_t* heartbeatValue; + lv_obj_t* heartbeatBpm; + lv_obj_t* stepIcon; + lv_obj_t* stepValue; + lv_obj_t* notificationIcon; + + Controllers::DateTime& dateTimeController; + Controllers::Battery& batteryController; + Controllers::Ble& bleController; + Controllers::NotificationManager& notificatioManager; + Controllers::Settings& settingsController; + Controllers::HeartRateController& heartRateController; + + bool running = true; + + }; + } + } +} -- cgit v1.2.3 From 3d6e8c3bebbdaafa764b06ebc2769f2eaac05298 Mon Sep 17 00:00:00 2001 From: Joaquim Date: Sat, 6 Mar 2021 19:55:36 +0000 Subject: Merge from upstream --- README.md | 4 +++- images/0.14.0/collage1.png | Bin 0 -> 621336 bytes images/0.14.0/collage2.png | Bin 0 -> 639600 bytes images/infinitime-logo-github.jpg | Bin 0 -> 37430 bytes src/CMakeLists.txt | 29 ++++++++++++++++++++--------- src/displayapp/screens/Clock.cpp | 3 ++- 6 files changed, 25 insertions(+), 11 deletions(-) create mode 100644 images/0.14.0/collage1.png create mode 100644 images/0.14.0/collage2.png create mode 100644 images/infinitime-logo-github.jpg (limited to 'src/displayapp/screens') diff --git a/README.md b/README.md index 929d3404..3f89ec7d 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,8 @@ The goal of this project is to design an open-source firmware for the Pinetime s ## Overview -![Pinetime screens](images/0.7.0/montage.jpg "PinetimeScreens") +![Pinetime screens](images/0.14.0/collage1.png "PinetimeScreens") +![Pinetime screens](images/0.14.0/collage2.png "PinetimeScreens") As of now, here is the list of achievements of this project: @@ -46,6 +47,7 @@ As of now, here is the list of achievements of this project: - Supported by 2 companion apps (development is in progress): * [Gadgetbridge](https://codeberg.org/Freeyourgadget/Gadgetbridge/) (on Android) * [Amazfish](https://openrepos.net/content/piggz/amazfish) (on SailfishOS and Linux) + * **[Experimental]** [WebBLEWatch](https://hubmartin.github.io/WebBLEWatch/) Synchronize time directly from your web browser. [video](https://youtu.be/IakiuhVDdrY) - **[Experimental]** OTA (Over-the-air) update via BLE - **[Experimental]** Bootloader based on [MCUBoot](https://juullabs-oss.github.io/mcuboot/) diff --git a/images/0.14.0/collage1.png b/images/0.14.0/collage1.png new file mode 100644 index 00000000..fd5f27ba Binary files /dev/null and b/images/0.14.0/collage1.png differ diff --git a/images/0.14.0/collage2.png b/images/0.14.0/collage2.png new file mode 100644 index 00000000..293147dd Binary files /dev/null and b/images/0.14.0/collage2.png differ diff --git a/images/infinitime-logo-github.jpg b/images/infinitime-logo-github.jpg new file mode 100644 index 00000000..cf19e35c Binary files /dev/null and b/images/infinitime-logo-github.jpg differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6029f142..e26b52f7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,17 @@ cmake_minimum_required(VERSION 3.10) project(pinetime-app C CXX ASM) + +set(CMAKE_C_STANDARD 99) +set(CMAKE_CXX_STANDARD 14) + +# set(CMAKE_GENERATOR "Unix Makefiles") +set(CMAKE_C_EXTENSIONS OFF) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # define some variables just for this example to determine file locations set(NRF_PROJECT_NAME pinetime-app) set(NRF_BOARD pca10040) @@ -760,7 +771,7 @@ target_compile_options(nrf-sdk PRIVATE $<$,$>: ${COMMON_FLAGS} -O3> $<$,$>: ${COMMON_FLAGS} -O0> $<$,$>: ${COMMON_FLAGS} -O3> - $<$: -MP -MD -std=c99 -x assembler-with-cpp> + $<$: -MP -MD -x assembler-with-cpp> ) # NimBLE @@ -772,7 +783,7 @@ target_compile_options(nimble PRIVATE $<$,$>: ${COMMON_FLAGS} -O3 -Wno-unused-but-set-variable -Wno-maybe-uninitialized> $<$,$>: ${COMMON_FLAGS} -O0 -g3 -Wno-unused-but-set-variable -Wno-maybe-uninitialized> $<$,$>: ${COMMON_FLAGS} -O3 -Wno-unused-but-set-variable -Wno-maybe-uninitialized> - $<$: -MP -MD -std=c99 -x assembler-with-cpp> + $<$: -MP -MD -x assembler-with-cpp> ) # lvgl @@ -784,7 +795,7 @@ target_compile_options(lvgl PRIVATE $<$,$>: ${COMMON_FLAGS} -O3> $<$,$>: ${COMMON_FLAGS} -O0 -g3> $<$,$>: ${COMMON_FLAGS} -O3> - $<$: -MP -MD -std=c99 -x assembler-with-cpp> + $<$: -MP -MD -x assembler-with-cpp> ) # Build autonomous binary (without support for bootloader) @@ -799,12 +810,12 @@ target_compile_options(${EXECUTABLE_NAME} PUBLIC $<$,$>: ${COMMON_FLAGS} -O3> $<$,$>: ${COMMON_FLAGS} -O0 -g3> $<$,$>: ${COMMON_FLAGS} -O3> - $<$: -MP -MD -std=c99 -x assembler-with-cpp> + $<$: -MP -MD -x assembler-with-cpp> ) set_target_properties(${EXECUTABLE_NAME} PROPERTIES SUFFIX ".out" - LINK_FLAGS "-mthumb -mabi=aapcs -std=gnu++98 -std=c99 -L ${NRF5_SDK_PATH}/modules/nrfx/mdk -T${NRF5_LINKER_SCRIPT} -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm -Wl,-Map=${EXECUTABLE_FILE_NAME}.map" + LINK_FLAGS "-mthumb -mabi=aapcs -L ${NRF5_SDK_PATH}/modules/nrfx/mdk -T${NRF5_LINKER_SCRIPT} -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm -Wl,-Map=${EXECUTABLE_FILE_NAME}.map" CXX_STANDARD 11 C_STANDARD 99 ) @@ -831,12 +842,12 @@ target_compile_options(${EXECUTABLE_MCUBOOT_NAME} PUBLIC $<$,$>: ${COMMON_FLAGS} -O3> $<$,$>: ${COMMON_FLAGS} -O0 -g3> $<$,$>: ${COMMON_FLAGS} -O3> - $<$: -MP -MD -std=c99 -x assembler-with-cpp> + $<$: -MP -MD -x assembler-with-cpp> ) set_target_properties(${EXECUTABLE_MCUBOOT_NAME} PROPERTIES SUFFIX ".out" - LINK_FLAGS "-mthumb -mabi=aapcs -std=gnu++98 -std=c99 -L ${NRF5_SDK_PATH}/modules/nrfx/mdk -T${NRF5_LINKER_SCRIPT_MCUBOOT} -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm -Wl,-Map=${EXECUTABLE_MCUBOOT_FILE_NAME}.map" + LINK_FLAGS "-mthumb -mabi=aapcs -L ${NRF5_SDK_PATH}/modules/nrfx/mdk -T${NRF5_LINKER_SCRIPT_MCUBOOT} -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm -Wl,-Map=${EXECUTABLE_MCUBOOT_FILE_NAME}.map" CXX_STANDARD 11 C_STANDARD 99 ) @@ -860,12 +871,12 @@ target_compile_options(${EXECUTABLE_GRAPHICS_NAME} PUBLIC $<$,$>: ${COMMON_FLAGS} -O3> $<$,$>: ${COMMON_FLAGS} -O0 -g3> $<$,$>: ${COMMON_FLAGS} -O3> - $<$: -MP -MD -std=c99 -x assembler-with-cpp> + $<$: -MP -MD -x assembler-with-cpp> ) set_target_properties(${EXECUTABLE_GRAPHICS_NAME} PROPERTIES SUFFIX ".out" - LINK_FLAGS "-mthumb -mabi=aapcs -std=gnu++98 -std=c99 -L ${NRF5_SDK_PATH}/modules/nrfx/mdk -T${NRF5_LINKER_SCRIPT} -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm -Wl,-Map=${EXECUTABLE_GRAPHICS_FILE_NAME}.map" + LINK_FLAGS "-mthumb -mabi=aapcs -L ${NRF5_SDK_PATH}/modules/nrfx/mdk -T${NRF5_LINKER_SCRIPT} -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm -Wl,-Map=${EXECUTABLE_GRAPHICS_FILE_NAME}.map" CXX_STANDARD 11 C_STANDARD 99 ) diff --git a/src/displayapp/screens/Clock.cpp b/src/displayapp/screens/Clock.cpp index ea9ddd56..342dd222 100644 --- a/src/displayapp/screens/Clock.cpp +++ b/src/displayapp/screens/Clock.cpp @@ -33,6 +33,7 @@ Clock::Clock(DisplayApp* app, { [this]() -> std::unique_ptr { return WatchFaceDigitalScreen(); }, [this]() -> std::unique_ptr { return WatchFaceAnalogScreen(); }, + // Examples for more watch faces //[this]() -> std::unique_ptr { return WatchFaceMinimalScreen(); }, //[this]() -> std::unique_ptr { return WatchFaceCustomScreen(); } }, @@ -71,7 +72,7 @@ std::unique_ptr Clock::WatchFaceAnalogScreen() { } /* -// examples +// Examples for more watch faces std::unique_ptr Clock::WatchFaceMinimalScreen() { return std::unique_ptr(new Screens::WatchFaceMinimal(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController)); } -- cgit v1.2.3 From 97deb43fb78b581535887938a0c3c0813896fcbe Mon Sep 17 00:00:00 2001 From: Joaquim Date: Sat, 6 Mar 2021 19:55:53 +0000 Subject: Update from JF comments to PR --- src/components/settings/Settings.cpp | 2 -- src/displayapp/screens/Clock.h | 6 ++++-- src/displayapp/screens/ScreenList.h | 18 ++---------------- src/displayapp/screens/WatchFaceAnalog.cpp | 6 ------ src/displayapp/screens/WatchFaceDigital.cpp | 7 ------- 5 files changed, 6 insertions(+), 33 deletions(-) (limited to 'src/displayapp/screens') diff --git a/src/components/settings/Settings.cpp b/src/components/settings/Settings.cpp index 87cfe885..0c6cf610 100644 --- a/src/components/settings/Settings.cpp +++ b/src/components/settings/Settings.cpp @@ -6,9 +6,7 @@ using namespace Pinetime::Controllers; // TODO (team): // Read and write the settings to Flash // - void Settings::Init() { - // default Clock face clockFace = 0; diff --git a/src/displayapp/screens/Clock.h b/src/displayapp/screens/Clock.h index 2b1be4ac..964ccbf6 100644 --- a/src/displayapp/screens/Clock.h +++ b/src/displayapp/screens/Clock.h @@ -49,8 +49,10 @@ namespace Pinetime { ScreenList<2> screens; std::unique_ptr WatchFaceDigitalScreen(); std::unique_ptr WatchFaceAnalogScreen(); - std::unique_ptr WatchFaceMinimalScreen(); - std::unique_ptr WatchFaceCustomScreen(); + + // Examples for more watch faces + //std::unique_ptr WatchFaceMinimalScreen(); + //std::unique_ptr WatchFaceCustomScreen(); bool running = true; diff --git a/src/displayapp/screens/ScreenList.h b/src/displayapp/screens/ScreenList.h index 56d9abe0..43b33f40 100644 --- a/src/displayapp/screens/ScreenList.h +++ b/src/displayapp/screens/ScreenList.h @@ -15,26 +15,12 @@ namespace Pinetime { class ScreenList : public Screen { public: - ScreenList( - DisplayApp* app, - uint8_t initScreen, - std::array()>, N>&& screens, - ScreenListModes mode - ) - : Screen(app), - initScreen{initScreen}, - screens{std::move(screens)}, - mode{mode}, - current{this->screens[initScreen]()} - { + ScreenList(DisplayApp* app, uint8_t initScreen, std::array()>, N>&& screens, ScreenListModes mode) + : Screen(app), initScreen{initScreen}, screens{std::move(screens)}, mode{mode}, current{this->screens[initScreen]()} { screenIndex = initScreen; } ~ScreenList() override { - current.reset(nullptr); - /*for(uint8_t i = 0; i < screens.size(); i++) { - screens[i]().reset(nullptr); - }*/ lv_obj_clean(lv_scr_act()); } diff --git a/src/displayapp/screens/WatchFaceAnalog.cpp b/src/displayapp/screens/WatchFaceAnalog.cpp index efca10c2..b51d48c7 100644 --- a/src/displayapp/screens/WatchFaceAnalog.cpp +++ b/src/displayapp/screens/WatchFaceAnalog.cpp @@ -103,12 +103,6 @@ WatchFaceAnalog::WatchFaceAnalog(Pinetime::Applications::DisplayApp *app, UpdateClock(); - /*lv_obj_t* backgroundLabel = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP); - lv_obj_set_size(backgroundLabel, 240, 240); - lv_obj_set_pos(backgroundLabel, 0, 0); - lv_label_set_text_static(backgroundLabel, "");*/ - } WatchFaceAnalog::~WatchFaceAnalog() { diff --git a/src/displayapp/screens/WatchFaceDigital.cpp b/src/displayapp/screens/WatchFaceDigital.cpp index 99df318a..4560ccdb 100644 --- a/src/displayapp/screens/WatchFaceDigital.cpp +++ b/src/displayapp/screens/WatchFaceDigital.cpp @@ -30,13 +30,6 @@ WatchFaceDigital::WatchFaceDigital(DisplayApp* app, heartRateController{heartRateController} { settingsController.SetClockFace(0); - // init - /*currentDateTime = dateTimeController.CurrentDateTime(); - batteryPercentRemaining = batteryController.PercentRemaining(); - bleState = bleController.IsConnected(); - notificationState = notificatioManager.AreNewNotificationsAvailable(); - heartbeat = heartRateController.HeartRate();*/ - displayedChar[0] = 0; displayedChar[1] = 0; displayedChar[2] = 0; -- cgit v1.2.3 From 282e34dca14ddc799b9511643e50a4f9023003ed Mon Sep 17 00:00:00 2001 From: Joaquim José Almeida Pereira Date: Mon, 8 Mar 2021 12:23:32 +0000 Subject: fix AM/PM --- src/displayapp/screens/WatchFaceDigital.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'src/displayapp/screens') diff --git a/src/displayapp/screens/WatchFaceDigital.cpp b/src/displayapp/screens/WatchFaceDigital.cpp index 4560ccdb..c39fe496 100644 --- a/src/displayapp/screens/WatchFaceDigital.cpp +++ b/src/displayapp/screens/WatchFaceDigital.cpp @@ -160,11 +160,21 @@ bool WatchFaceDigital::Refresh() { if ( settingsController.GetClockType() == Controllers::Settings::ClockType::H24 ) { sprintf(hoursChar, "%02d", hour); } else { - if (hour > 12) { - hour -= 12; - sprintf(ampmChar, "PM"); - } else { - sprintf(ampmChar, "AM"); + if (hour == 0 && hour != 12) { + hour = 12; + sprintf(ampmChar, "AM"); + } + else if (hour == 12 && hour != 0) { + hour = 12; + sprintf(ampmChar, "PM"); + } + else if (hour < 12 && hour != 0) { + sprintf(ampmChar, "AM"); + } + else if (hour > 12 && hour != 0) + { + hour = hour - 12; + sprintf(ampmChar, "PM"); } sprintf(hoursChar, "%02d", hour); } -- cgit v1.2.3