summaryrefslogtreecommitdiff
path: root/src/displayapp/screens
diff options
context:
space:
mode:
Diffstat (limited to 'src/displayapp/screens')
-rw-r--r--src/displayapp/screens/ApplicationList.cpp82
-rw-r--r--src/displayapp/screens/ApplicationList.h32
-rw-r--r--src/displayapp/screens/BatteryIcon.cpp21
-rw-r--r--src/displayapp/screens/BatteryIcon.h16
-rw-r--r--src/displayapp/screens/BleIcon.cpp8
-rw-r--r--src/displayapp/screens/BleIcon.h12
-rw-r--r--src/displayapp/screens/Brightness.cpp92
-rw-r--r--src/displayapp/screens/Brightness.h33
-rw-r--r--src/displayapp/screens/Clock.cpp227
-rw-r--r--src/displayapp/screens/Clock.h88
-rw-r--r--src/displayapp/screens/DropDownDemo.cpp64
-rw-r--r--src/displayapp/screens/DropDownDemo.h29
-rw-r--r--src/displayapp/screens/FirmwareUpdate.cpp82
-rw-r--r--src/displayapp/screens/FirmwareUpdate.h41
-rw-r--r--src/displayapp/screens/FirmwareValidation.cpp91
-rw-r--r--src/displayapp/screens/FirmwareValidation.h42
-rw-r--r--src/displayapp/screens/Gauge.cpp58
-rw-r--r--src/displayapp/screens/Gauge.h32
-rw-r--r--src/displayapp/screens/InfiniPaint.cpp44
-rw-r--r--src/displayapp/screens/InfiniPaint.h35
-rw-r--r--src/displayapp/screens/Label.cpp15
-rw-r--r--src/displayapp/screens/Label.h23
-rw-r--r--src/displayapp/screens/Meter.cpp47
-rw-r--r--src/displayapp/screens/Meter.h32
-rw-r--r--src/displayapp/screens/Modal.cpp81
-rw-r--r--src/displayapp/screens/Modal.h39
-rw-r--r--src/displayapp/screens/Music.cpp125
-rw-r--r--src/displayapp/screens/Music.h49
-rw-r--r--src/displayapp/screens/Screen.cpp2
-rw-r--r--src/displayapp/screens/Screen.h30
-rw-r--r--src/displayapp/screens/ScreenList.h66
-rw-r--r--src/displayapp/screens/Symbols.h30
-rw-r--r--src/displayapp/screens/SystemInfo.cpp116
-rw-r--r--src/displayapp/screens/SystemInfo.h47
-rw-r--r--src/displayapp/screens/Tab.cpp67
-rw-r--r--src/displayapp/screens/Tab.h23
-rw-r--r--src/displayapp/screens/Tile.cpp61
-rw-r--r--src/displayapp/screens/Tile.h39
38 files changed, 2021 insertions, 0 deletions
diff --git a/src/displayapp/screens/ApplicationList.cpp b/src/displayapp/screens/ApplicationList.cpp
new file mode 100644
index 00000000..eb85be4f
--- /dev/null
+++ b/src/displayapp/screens/ApplicationList.cpp
@@ -0,0 +1,82 @@
+#include <libs/lvgl/lvgl.h>
+#include <DisplayApp/DisplayApp.h>
+#include <functional>
+#include "ApplicationList.h"
+#include "Tile.h"
+#include "Symbols.h"
+
+using namespace Pinetime::Applications::Screens;
+
+ApplicationList::ApplicationList(Pinetime::Applications::DisplayApp *app) :
+ Screen(app),
+ screens{app, {
+ [this]() -> std::unique_ptr<Screen> { return CreateScreen1(); },
+ [this]() -> std::unique_ptr<Screen> { return CreateScreen2(); },
+ //[this]() -> std::unique_ptr<Screen> { return CreateScreen3(); }
+ }
+ } {}
+
+
+ApplicationList::~ApplicationList() {
+ lv_obj_clean(lv_scr_act());
+}
+
+bool ApplicationList::Refresh() {
+ if(running)
+ running = screens.Refresh();
+ return running;
+}
+
+bool ApplicationList::OnButtonPushed() {
+ running = false;
+ app->StartApp(Apps::Clock);
+ return true;
+}
+
+bool ApplicationList::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
+ return screens.OnTouchEvent(event);
+}
+
+std::unique_ptr<Screen> ApplicationList::CreateScreen1() {
+ std::array<Screens::Tile::Applications, 6> applications {
+ {{Symbols::clock, Apps::Clock},
+ {Symbols::music, Apps::Music},
+ {Symbols::sun, Apps::Brightness},
+ {Symbols::list, Apps::SysInfo},
+ {Symbols::check, Apps::FirmwareValidation},
+ {Symbols::none, Apps::None}
+ }
+
+
+ };
+
+ return std::unique_ptr<Screen>(new Screens::Tile(app, applications));
+}
+
+std::unique_ptr<Screen> ApplicationList::CreateScreen2() {
+ std::array<Screens::Tile::Applications, 6> applications {
+ {{Symbols::tachometer, Apps::Gauge},
+ {Symbols::asterisk, Apps::Meter},
+ {Symbols::paintbrush, Apps::Paint},
+ {Symbols::none, Apps::None},
+ {Symbols::none, Apps::None},
+ {Symbols::none, Apps::None}
+ }
+ };
+
+ return std::unique_ptr<Screen>(new Screens::Tile(app, applications));
+}
+
+std::unique_ptr<Screen> ApplicationList::CreateScreen3() {
+ std::array<Screens::Tile::Applications, 6> applications {
+ {{"A", Apps::Meter},
+ {"B", Apps::Gauge},
+ {"C", Apps::Clock},
+ {"D", Apps::Music},
+ {"E", Apps::SysInfo},
+ {"F", Apps::Brightness}
+ }
+ };
+
+ return std::unique_ptr<Screen>(new Screens::Tile(app, applications));
+}
diff --git a/src/displayapp/screens/ApplicationList.h b/src/displayapp/screens/ApplicationList.h
new file mode 100644
index 00000000..a1e6811b
--- /dev/null
+++ b/src/displayapp/screens/ApplicationList.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <vector>
+#include <Components/Ble/NimbleController.h>
+#include "Screen.h"
+#include "Label.h"
+#include "ScreenList.h"
+#include "Gauge.h"
+#include "Meter.h"
+#include <functional>
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+ class ApplicationList : public Screen {
+ public:
+ explicit ApplicationList(DisplayApp* app);
+ ~ApplicationList() override;
+ bool Refresh() override;
+ bool OnButtonPushed() override;
+ bool OnTouchEvent(TouchEvents event) override;
+ private:
+ bool running = true;
+
+ ScreenList<2> screens;
+ std::unique_ptr<Screen> CreateScreen1();
+ std::unique_ptr<Screen> CreateScreen2();
+ std::unique_ptr<Screen> CreateScreen3();
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/src/displayapp/screens/BatteryIcon.cpp b/src/displayapp/screens/BatteryIcon.cpp
new file mode 100644
index 00000000..26939d18
--- /dev/null
+++ b/src/displayapp/screens/BatteryIcon.cpp
@@ -0,0 +1,21 @@
+#include "BatteryIcon.h"
+#include "Symbols.h"
+using namespace Pinetime::Applications::Screens;
+
+const char* BatteryIcon::GetBatteryIcon(float batteryPercent) {
+ if(batteryPercent > 90.0f) return Symbols::batteryFull;
+ if(batteryPercent > 75.0f) return Symbols::batteryThreeQuarter;
+ if(batteryPercent > 50.0f) return Symbols::batteryHalf;
+ if(batteryPercent > 25.0f) return Symbols::batteryOneQuarter;
+ return Symbols::batteryEmpty;
+}
+
+const char* BatteryIcon::GetUnknownIcon() {
+ return Symbols::batteryEmpty;
+}
+
+const char *BatteryIcon::GetPlugIcon(bool isCharging) {
+ if(isCharging)
+ return Symbols::plug;
+ else return "";
+}
diff --git a/src/displayapp/screens/BatteryIcon.h b/src/displayapp/screens/BatteryIcon.h
new file mode 100644
index 00000000..58f04a87
--- /dev/null
+++ b/src/displayapp/screens/BatteryIcon.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <libs/lvgl/src/lv_draw/lv_img_decoder.h>
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+ class BatteryIcon {
+ public:
+ static const char* GetUnknownIcon();
+ static const char* GetBatteryIcon(float batteryPercent);
+ static const char* GetPlugIcon(bool isCharging);
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/src/displayapp/screens/BleIcon.cpp b/src/displayapp/screens/BleIcon.cpp
new file mode 100644
index 00000000..1bbbd053
--- /dev/null
+++ b/src/displayapp/screens/BleIcon.cpp
@@ -0,0 +1,8 @@
+#include "BleIcon.h"
+#include "Symbols.h"
+using namespace Pinetime::Applications::Screens;
+
+const char* BleIcon::GetIcon(bool isConnected) {
+ if(isConnected) return Symbols::bluetooth;
+ else return "";
+} \ No newline at end of file
diff --git a/src/displayapp/screens/BleIcon.h b/src/displayapp/screens/BleIcon.h
new file mode 100644
index 00000000..c1398d2a
--- /dev/null
+++ b/src/displayapp/screens/BleIcon.h
@@ -0,0 +1,12 @@
+#pragma once
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+ class BleIcon {
+ public:
+ static const char* GetIcon(bool isConnected);
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/src/displayapp/screens/Brightness.cpp b/src/displayapp/screens/Brightness.cpp
new file mode 100644
index 00000000..9e3416c0
--- /dev/null
+++ b/src/displayapp/screens/Brightness.cpp
@@ -0,0 +1,92 @@
+#include <libs/lvgl/lvgl.h>
+#include "Brightness.h"
+
+using namespace Pinetime::Applications::Screens;
+
+void slider_event_cb(lv_obj_t * slider, lv_event_t event) {
+ if(event == LV_EVENT_VALUE_CHANGED) {
+ auto* brightnessSlider = static_cast<Brightness*>(slider->user_data);
+ brightnessSlider->OnValueChanged();
+ }
+}
+
+Brightness::Brightness(Pinetime::Applications::DisplayApp *app, Controllers::BrightnessController& brightness) : Screen(app), brightness{brightness} {
+ slider = lv_slider_create(lv_scr_act(), NULL);
+ lv_obj_set_user_data(slider, this);
+ lv_obj_set_width(slider, LV_DPI * 2);
+ lv_obj_align(slider, NULL, LV_ALIGN_CENTER, 0, 0);
+ lv_obj_set_event_cb(slider, slider_event_cb);
+ lv_slider_set_range(slider, 0, 2);
+ lv_slider_set_value(slider, LevelToInt(brightness.Level()), LV_ANIM_OFF);
+
+ slider_label = lv_label_create(lv_scr_act(), NULL);
+ lv_label_set_text(slider_label, LevelToString(brightness.Level()));
+ lv_obj_set_auto_realign(slider_label, true);
+ lv_obj_align(slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
+}
+
+Brightness::~Brightness() {
+ lv_obj_clean(lv_scr_act());
+}
+
+bool Brightness::Refresh() {
+ return running;
+}
+
+bool Brightness::OnButtonPushed() {
+ running = false;
+ return true;
+}
+
+const char *Brightness::LevelToString(Pinetime::Controllers::BrightnessController::Levels level) {
+ switch(level) {
+ case Pinetime::Controllers::BrightnessController::Levels::Off: return "Off";
+ case Pinetime::Controllers::BrightnessController::Levels::Low: return "Low";
+ case Pinetime::Controllers::BrightnessController::Levels::Medium: return "Medium";
+ case Pinetime::Controllers::BrightnessController::Levels::High: return "High";
+ default : return "???";
+ }
+}
+
+void Brightness::OnValueChanged() {
+ SetValue(lv_slider_get_value(slider));
+}
+
+void Brightness::SetValue(uint8_t value) {
+ switch(value) {
+ case 0: brightness.Set(Controllers::BrightnessController::Levels::Low); break;
+ case 1: brightness.Set(Controllers::BrightnessController::Levels::Medium); break;
+ case 2: brightness.Set(Controllers::BrightnessController::Levels::High); break;
+ }
+ lv_label_set_text(slider_label, LevelToString(brightness.Level()));
+}
+
+uint8_t Brightness::LevelToInt(Pinetime::Controllers::BrightnessController::Levels level) {
+ switch(level) {
+ case Pinetime::Controllers::BrightnessController::Levels::Off: return 0;
+ case Pinetime::Controllers::BrightnessController::Levels::Low: return 0;
+ case Pinetime::Controllers::BrightnessController::Levels::Medium: return 1;
+ case Pinetime::Controllers::BrightnessController::Levels::High: return 2;
+ default : return 0;
+ }
+}
+
+bool Brightness::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
+ switch(event) {
+ case TouchEvents::SwipeLeft:
+ brightness.Lower();
+ SetValue();
+ return true;
+ case TouchEvents::SwipeRight:
+ brightness.Higher();
+ SetValue();
+ return true;
+ default:
+ return false;
+ }
+}
+
+void Brightness::SetValue() {
+ lv_slider_set_value(slider, LevelToInt(brightness.Level()), LV_ANIM_OFF);
+ lv_label_set_text(slider_label, LevelToString(brightness.Level()));
+}
diff --git a/src/displayapp/screens/Brightness.h b/src/displayapp/screens/Brightness.h
new file mode 100644
index 00000000..37cbcd7e
--- /dev/null
+++ b/src/displayapp/screens/Brightness.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <libs/lvgl/src/lv_core/lv_obj.h>
+#include <Components/Brightness/BrightnessController.h>
+#include "Screen.h"
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+ class Brightness : public Screen {
+ public:
+ Brightness(DisplayApp* app, Controllers::BrightnessController& brightness);
+ ~Brightness() override;
+ bool Refresh() override;
+ bool OnButtonPushed() override;
+ bool OnTouchEvent(TouchEvents event) override;
+
+ void OnValueChanged();
+ private:
+ bool running = true;
+ Controllers::BrightnessController& brightness;
+
+ lv_obj_t * slider_label;
+ lv_obj_t * slider;
+
+ const char* LevelToString(Controllers::BrightnessController::Levels level);
+ uint8_t LevelToInt(Controllers::BrightnessController::Levels level);
+ void SetValue(uint8_t value);
+ void SetValue();
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/src/displayapp/screens/Clock.cpp b/src/displayapp/screens/Clock.cpp
new file mode 100644
index 00000000..06fab9aa
--- /dev/null
+++ b/src/displayapp/screens/Clock.cpp
@@ -0,0 +1,227 @@
+#include <cstdio>
+#include <libs/date/includes/date/date.h>
+#include <Components/DateTime/DateTimeController.h>
+#include <libs/lvgl/lvgl.h>
+#include "Clock.h"
+#include "../DisplayApp.h"
+#include "BatteryIcon.h"
+#include "BleIcon.h"
+#include "Symbols.h"
+using namespace Pinetime::Applications::Screens;
+extern lv_font_t jetbrains_mono_extrabold_compressed;
+extern lv_font_t jetbrains_mono_bold_20;
+extern lv_style_t* LabelBigStyle;
+
+static void event_handler(lv_obj_t * obj, lv_event_t event) {
+ Clock* screen = static_cast<Clock *>(obj->user_data);
+ screen->OnObjectEvent(obj, event);
+}
+
+Clock::Clock(DisplayApp* app,
+ Controllers::DateTime& dateTimeController,
+ Controllers::Battery& batteryController,
+ Controllers::Ble& bleController) : Screen(app), currentDateTime{{}},
+ dateTimeController{dateTimeController}, batteryController{batteryController}, bleController{bleController} {
+ displayedChar[0] = 0;
+ displayedChar[1] = 0;
+ displayedChar[2] = 0;
+ displayedChar[3] = 0;
+ displayedChar[4] = 0;
+
+ batteryIcon = lv_label_create(lv_scr_act(), NULL);
+ 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(), NULL);
+ 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(), NULL);
+ lv_label_set_text(bleIcon, Symbols::bluetooth);
+ lv_obj_align(bleIcon, batteryPlug, LV_ALIGN_OUT_LEFT_MID, -5, 0);
+
+
+ label_date = lv_label_create(lv_scr_act(), NULL);
+
+ lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 60);
+
+ label_time = lv_label_create(lv_scr_act(), NULL);
+ lv_label_set_style(label_time, LV_LABEL_STYLE_MAIN, LabelBigStyle);
+ lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 0);
+
+ backgroundLabel = lv_label_create(lv_scr_act(), NULL);
+ 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(), NULL);
+ 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(), NULL);
+ 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(), NULL);
+ 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(), NULL);
+ 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(), NULL);
+ lv_label_set_text(stepIcon, Symbols::shoe);
+ lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 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);
+
+ currentDateTime = dateTimeController.CurrentDateTime();
+
+ if(currentDateTime.IsUpdated()) {
+ auto newDateTime = currentDateTime.Get();
+
+ auto dp = date::floor<date::days>(newDateTime);
+ auto time = date::make_time(newDateTime-dp);
+ auto yearMonthDay = date::year_month_day(dp);
+
+ auto year = (int)yearMonthDay.year();
+ auto month = static_cast<Pinetime::Controllers::DateTime::Months>((unsigned)yearMonthDay.month());
+ auto day = (unsigned)yearMonthDay.day();
+ auto dayOfWeek = static_cast<Pinetime::Controllers::DateTime::Days>(date::weekday(yearMonthDay).iso_encoding());
+
+ auto hour = time.hours().count();
+ auto minute = time.minutes().count();
+
+ char minutesChar[3];
+ sprintf(minutesChar, "%02d", static_cast<int>(minute));
+
+ char hoursChar[3];
+ sprintf(hoursChar, "%02d", static_cast<int>(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;
+ }
+ }
+
+ // TODO heartbeat = heartBeatController.GetValue();
+ if(heartbeat.IsUpdated()) {
+ char heartbeatBuffer[4];
+ sprintf(heartbeatBuffer, "%d", heartbeat.Get());
+ 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;
+}
+
+const char *Clock::MonthToString(Pinetime::Controllers::DateTime::Months month) {
+ return Clock::MonthsString[static_cast<uint8_t>(month)];
+}
+
+const char *Clock::DayOfWeekToString(Pinetime::Controllers::DateTime::Days dayOfWeek) {
+ return Clock::DaysString[static_cast<uint8_t>(dayOfWeek)];
+}
+
+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;
+ }
+ }
+}
+
+bool Clock::OnButtonPushed() {
+ running = false;
+ return false;
+}
+
+
diff --git a/src/displayapp/screens/Clock.h b/src/displayapp/screens/Clock.h
new file mode 100644
index 00000000..7363fdaf
--- /dev/null
+++ b/src/displayapp/screens/Clock.h
@@ -0,0 +1,88 @@
+#pragma once
+
+#include <cstdint>
+#include <chrono>
+#include "Screen.h"
+#include <bits/unique_ptr.h>
+#include <libs/lvgl/src/lv_core/lv_style.h>
+#include <libs/lvgl/src/lv_core/lv_obj.h>
+#include <Components/Battery/BatteryController.h>
+#include <Components/Ble/BleController.h>
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+
+ template <class T>
+ class DirtyValue {
+ public:
+ explicit DirtyValue(T v) { value = v; }
+ explicit DirtyValue(T& v) { value = v; }
+ bool IsUpdated() const { return isUpdated; }
+ T& Get() { this->isUpdated = false; return value; }
+
+ DirtyValue& operator=(const T& other) {
+ if (this->value != other) {
+ this->value = other;
+ this->isUpdated = true;
+ }
+ return *this;
+ }
+ private:
+ T value;
+ bool isUpdated = true;
+ };
+ class Clock : public Screen{
+ public:
+ Clock(DisplayApp* app,
+ Controllers::DateTime& dateTimeController,
+ Controllers::Battery& batteryController,
+ Controllers::Ble& bleController);
+ ~Clock() override;
+
+ bool Refresh() override;
+ bool OnButtonPushed() 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<float> batteryPercentRemaining {0};
+ DirtyValue<bool> bleState {false};
+ DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime;
+ DirtyValue<uint32_t> stepCount {0};
+ DirtyValue<uint8_t> heartbeat {0};
+
+
+ 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;
+
+ Controllers::DateTime& dateTimeController;
+ Controllers::Battery& batteryController;
+ Controllers::Ble& bleController;
+
+ bool running = true;
+
+ };
+ }
+ }
+}
diff --git a/src/displayapp/screens/DropDownDemo.cpp b/src/displayapp/screens/DropDownDemo.cpp
new file mode 100644
index 00000000..735a0cce
--- /dev/null
+++ b/src/displayapp/screens/DropDownDemo.cpp
@@ -0,0 +1,64 @@
+#include <libs/lvgl/lvgl.h>
+#include <libraries/log/nrf_log.h>
+#include "DropDownDemo.h"
+#include "../DisplayApp.h"
+
+using namespace Pinetime::Applications::Screens;
+extern lv_font_t jetbrains_mono_extrabold_compressed;
+extern lv_font_t jetbrains_mono_bold_20;
+
+DropDownDemo::DropDownDemo(Pinetime::Applications::DisplayApp *app) : Screen(app) {
+ // Create the dropdown object, with many item, and fix its height
+ ddlist = lv_ddlist_create(lv_scr_act(), NULL);
+ lv_ddlist_set_options(ddlist, "Apple\n"
+ "Banana\n"
+ "Orange\n"
+ "Melon\n"
+ "Grape\n"
+ "Raspberry\n"
+ "A\n"
+ "B\n"
+ "C\n"
+ "D\n"
+ "E");
+ lv_ddlist_set_fix_width(ddlist, 150);
+ lv_ddlist_set_draw_arrow(ddlist, true);
+ lv_ddlist_set_fix_height(ddlist, 150);
+ lv_obj_align(ddlist, NULL, LV_ALIGN_IN_TOP_MID, 0, 20);
+}
+
+DropDownDemo::~DropDownDemo() {
+ // Reset the touchmode
+ app->SetTouchMode(DisplayApp::TouchModes::Gestures);
+ lv_obj_clean(lv_scr_act());
+}
+
+bool DropDownDemo::Refresh() {
+ auto* list = static_cast<lv_ddlist_ext_t *>(ddlist->ext_attr);
+
+ // Switch touchmode to Polling if the dropdown is opened. This will allow to scroll inside the
+ // dropdown while it is opened.
+ // Disable the polling mode when the dropdown is closed to be able to handle the gestures.
+ if(list->opened)
+ app->SetTouchMode(DisplayApp::TouchModes::Polling);
+ else
+ app->SetTouchMode(DisplayApp::TouchModes::Gestures);
+ return running;
+}
+
+bool DropDownDemo::OnButtonPushed() {
+ running = false;
+ return true;
+}
+
+bool DropDownDemo::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
+ // If the dropdown is opened, notify Display app that it doesn't need to handle the event
+ // (this will prevent displayApp from going back to the menu or clock scree).
+ auto* list = static_cast<lv_ddlist_ext_t *>(ddlist->ext_attr);
+ if(list->opened) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
diff --git a/src/displayapp/screens/DropDownDemo.h b/src/displayapp/screens/DropDownDemo.h
new file mode 100644
index 00000000..7c75efc0
--- /dev/null
+++ b/src/displayapp/screens/DropDownDemo.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <cstdint>
+#include "Screen.h"
+#include <bits/unique_ptr.h>
+#include <libs/lvgl/src/lv_core/lv_style.h>
+#include <libs/lvgl/src/lv_core/lv_obj.h>
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+
+ class DropDownDemo : public Screen{
+ public:
+ DropDownDemo(DisplayApp* app);
+ ~DropDownDemo() override;
+
+ bool Refresh() override;
+ bool OnButtonPushed() override;
+ bool OnTouchEvent(TouchEvents event) override;
+
+ private:
+ lv_obj_t * ddlist;
+ bool running = true;
+ bool isDropDownOpened = false;
+ };
+ }
+ }
+}
diff --git a/src/displayapp/screens/FirmwareUpdate.cpp b/src/displayapp/screens/FirmwareUpdate.cpp
new file mode 100644
index 00000000..e831114d
--- /dev/null
+++ b/src/displayapp/screens/FirmwareUpdate.cpp
@@ -0,0 +1,82 @@
+#include <libs/lvgl/lvgl.h>
+#include "FirmwareUpdate.h"
+#include "../DisplayApp.h"
+
+using namespace Pinetime::Applications::Screens;
+extern lv_font_t jetbrains_mono_extrabold_compressed;
+extern lv_font_t jetbrains_mono_bold_20;
+
+
+FirmwareUpdate::FirmwareUpdate(Pinetime::Applications::DisplayApp *app, Pinetime::Controllers::Ble& bleController) :
+ Screen(app), bleController{bleController} {
+
+ titleLabel = lv_label_create(lv_scr_act(), NULL);
+ lv_label_set_text(titleLabel, "Firmware update");
+ lv_obj_set_auto_realign(titleLabel, true);
+ lv_obj_align(titleLabel, NULL, LV_ALIGN_IN_TOP_MID, 0, 50);
+
+ bar1 = lv_bar_create(lv_scr_act(), NULL);
+ lv_obj_set_size(bar1, 200, 30);
+ lv_obj_align(bar1, NULL, LV_ALIGN_CENTER, 0, 0);
+ lv_bar_set_anim_time(bar1, 10);
+ lv_bar_set_range(bar1, 0, 100);
+ lv_bar_set_value(bar1, 0, LV_ANIM_OFF);
+
+ percentLabel = lv_label_create(lv_scr_act(), NULL);
+ lv_label_set_text(percentLabel, "");
+ lv_obj_set_auto_realign(percentLabel, true);
+ lv_obj_align(percentLabel, bar1, LV_ALIGN_OUT_TOP_MID, 0, 60);
+}
+
+FirmwareUpdate::~FirmwareUpdate() {
+ lv_obj_clean(lv_scr_act());
+}
+
+bool FirmwareUpdate::Refresh() {
+ switch(bleController.State()) {
+ default:
+ case Pinetime::Controllers::Ble::FirmwareUpdateStates::Idle:
+ case Pinetime::Controllers::Ble::FirmwareUpdateStates::Running:
+ if(state != States::Running)
+ state = States::Running;
+ return DisplayProgression();
+ case Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated:
+ if(state != States::Validated) {
+ UpdateValidated();
+ state = States::Validated;
+ }
+ return running;
+ case Pinetime::Controllers::Ble::FirmwareUpdateStates::Error:
+ if(state != States::Error) {
+ UpdateError();
+ state = States::Error;
+ }
+ return running;
+ }
+}
+
+bool FirmwareUpdate::DisplayProgression() const {
+ float current = bleController.FirmwareUpdateCurrentBytes() / 1024.0f;
+ float total = bleController.FirmwareUpdateTotalBytes() / 1024.0f;
+ int16_t pc = (current / total) * 100.0f;
+ sprintf(percentStr, "%d %%", pc);
+ lv_label_set_text(percentLabel, percentStr);
+
+ lv_bar_set_value(bar1, pc, LV_ANIM_OFF);
+ return running;
+}
+
+bool FirmwareUpdate::OnButtonPushed() {
+ running = false;
+ return true;
+}
+
+void FirmwareUpdate::UpdateValidated() {
+ lv_label_set_recolor(percentLabel, true);
+ lv_label_set_text(percentLabel, "#00ff00 Image Ok!#");
+}
+
+void FirmwareUpdate::UpdateError() {
+ lv_label_set_recolor(percentLabel, true);
+ lv_label_set_text(percentLabel, "#ff0000 Error!#");
+}
diff --git a/src/displayapp/screens/FirmwareUpdate.h b/src/displayapp/screens/FirmwareUpdate.h
new file mode 100644
index 00000000..faaf3953
--- /dev/null
+++ b/src/displayapp/screens/FirmwareUpdate.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <cstdint>
+#include <chrono>
+#include "Screen.h"
+#include <bits/unique_ptr.h>
+#include <libs/lvgl/src/lv_core/lv_style.h>
+#include <libs/lvgl/src/lv_core/lv_obj.h>
+#include <Components/Ble/BleController.h>
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+
+ class FirmwareUpdate : public Screen{
+ public:
+ FirmwareUpdate(DisplayApp* app, Pinetime::Controllers::Ble& bleController);
+ ~FirmwareUpdate() override;
+
+ bool Refresh() override;
+ bool OnButtonPushed() override;
+
+ private:
+ enum class States { Idle, Running, Validated, Error };
+ Pinetime::Controllers::Ble& bleController;
+ lv_obj_t* bar1;
+ lv_obj_t* percentLabel;
+ lv_obj_t* titleLabel;
+ mutable char percentStr[10];
+ bool running = true;
+ States state;
+
+ bool DisplayProgression() const;
+
+ void UpdateValidated();
+
+ void UpdateError();
+ };
+ }
+ }
+}
diff --git a/src/displayapp/screens/FirmwareValidation.cpp b/src/displayapp/screens/FirmwareValidation.cpp
new file mode 100644
index 00000000..fb2dd956
--- /dev/null
+++ b/src/displayapp/screens/FirmwareValidation.cpp
@@ -0,0 +1,91 @@
+#include <libs/lvgl/lvgl.h>
+#include "FirmwareValidation.h"
+#include "../DisplayApp.h"
+#include "../../Version.h"
+#include "../../Components/FirmwareValidator/FirmwareValidator.h"
+
+using namespace Pinetime::Applications::Screens;
+extern lv_font_t jetbrains_mono_extrabold_compressed;
+extern lv_font_t jetbrains_mono_bold_20;
+
+namespace {
+ static void ButtonEventHandler(lv_obj_t * obj, lv_event_t event)
+ {
+ FirmwareValidation* screen = static_cast<FirmwareValidation *>(obj->user_data);
+ screen->OnButtonEvent(obj, event);
+ }
+
+}
+
+FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp *app,
+ Pinetime::Controllers::FirmwareValidator &validator)
+ : Screen{app}, validator{validator} {
+ labelVersionInfo = lv_label_create(lv_scr_act(), NULL);
+ lv_obj_align(labelVersionInfo, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0);
+ lv_label_set_text(labelVersionInfo, "Version : ");
+ lv_label_set_align(labelVersionInfo, LV_LABEL_ALIGN_LEFT);
+
+
+ labelVersionValue = lv_label_create(lv_scr_act(), NULL);
+ lv_obj_align(labelVersionValue, labelVersionInfo, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
+ lv_label_set_recolor(labelVersionValue, true);
+ sprintf(version, "%ld.%ld.%ld", Version::Major(), Version::Minor(), Version::Patch());
+ lv_label_set_text(labelVersionValue, version);
+
+ labelIsValidated = lv_label_create(lv_scr_act(), NULL);
+ lv_obj_align(labelIsValidated, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 50);
+ lv_label_set_recolor(labelIsValidated, true);
+ lv_label_set_long_mode(labelIsValidated, LV_LABEL_LONG_BREAK);
+ lv_obj_set_width(labelIsValidated, 240);
+
+ if(validator.IsValidated())
+ lv_label_set_text(labelIsValidated, "You have already\n#00ff00 validated# this firmware#");
+ else {
+ lv_label_set_text(labelIsValidated,
+ "Please #00ff00 Validate# this version or\n#ff0000 Reset# to rollback to the previous version.");
+
+ buttonValidate = lv_btn_create(lv_scr_act(), NULL);
+ lv_obj_align(buttonValidate, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
+ buttonValidate->user_data = this;
+ lv_obj_set_event_cb(buttonValidate, ButtonEventHandler);
+
+ labelButtonValidate = lv_label_create(buttonValidate, NULL);
+ lv_label_set_recolor(labelButtonValidate, true);
+ lv_label_set_text(labelButtonValidate, "#00ff00 Validate#");
+
+ buttonReset = lv_btn_create(lv_scr_act(), NULL);
+ buttonReset->user_data = this;
+ lv_obj_align(buttonReset, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
+ lv_obj_set_event_cb(buttonReset, ButtonEventHandler);
+
+ labelButtonReset = lv_label_create(buttonReset, NULL);
+ lv_label_set_recolor(labelButtonReset, true);
+ lv_label_set_text(labelButtonReset, "#ff0000 Reset#");
+ }
+}
+
+
+FirmwareValidation::~FirmwareValidation() {
+ lv_obj_clean(lv_scr_act());
+}
+
+bool FirmwareValidation::Refresh() {
+ return running;
+}
+
+bool FirmwareValidation::OnButtonPushed() {
+ running = false;
+ return true;
+}
+
+void FirmwareValidation::OnButtonEvent(lv_obj_t *object, lv_event_t event) {
+ if(object == buttonValidate && event == LV_EVENT_PRESSED) {
+ validator.Validate();
+ running = false;
+ } else if(object == buttonReset && event == LV_EVENT_PRESSED) {
+ validator.Reset();
+ }
+
+}
+
+
diff --git a/src/displayapp/screens/FirmwareValidation.h b/src/displayapp/screens/FirmwareValidation.h
new file mode 100644
index 00000000..947f5575
--- /dev/null
+++ b/src/displayapp/screens/FirmwareValidation.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <cstdint>
+#include "Screen.h"
+#include <bits/unique_ptr.h>
+#include <libs/lvgl/src/lv_core/lv_style.h>
+#include <libs/lvgl/src/lv_core/lv_obj.h>
+
+namespace Pinetime {
+ namespace Controllers {
+ class FirmwareValidator;
+ }
+
+ namespace Applications {
+ namespace Screens {
+
+ class FirmwareValidation : public Screen{
+ public:
+ FirmwareValidation(DisplayApp* app, Pinetime::Controllers::FirmwareValidator& validator);
+ ~FirmwareValidation() override;
+
+ bool Refresh() override;
+ bool OnButtonPushed() override;
+
+ void OnButtonEvent(lv_obj_t *object, lv_event_t event);
+
+ private:
+ Pinetime::Controllers::FirmwareValidator& validator;
+
+ lv_obj_t* labelVersionInfo;
+ lv_obj_t* labelVersionValue;
+ char version[9];
+ lv_obj_t* labelIsValidated;
+ lv_obj_t* buttonValidate;
+ lv_obj_t* labelButtonValidate;
+ lv_obj_t* buttonReset;
+ lv_obj_t* labelButtonReset;
+ bool running = true;
+ };
+ }
+ }
+}
diff --git a/src/displayapp/screens/Gauge.cpp b/src/displayapp/screens/Gauge.cpp
new file mode 100644
index 00000000..fd905231
--- /dev/null
+++ b/src/displayapp/screens/Gauge.cpp
@@ -0,0 +1,58 @@
+#include <libs/lvgl/lvgl.h>
+#include "Gauge.h"
+#include "../DisplayApp.h"
+
+using namespace Pinetime::Applications::Screens;
+extern lv_font_t jetbrains_mono_extrabold_compressed;
+extern lv_font_t jetbrains_mono_bold_20;
+
+
+Gauge::Gauge(Pinetime::Applications::DisplayApp *app) : Screen(app) {
+ /*Create a style*/
+ lv_style_copy(&style, &lv_style_pretty_color);
+ style.body.main_color = LV_COLOR_CYAN; /*Line color at the beginning*/
+ style.body.grad_color = LV_COLOR_RED; /*Line color at the end*/
+ style.body.padding.left = 10; /*Scale line length*/
+ style.body.padding.inner = 8 ; /*Scale label padding*/
+ style.body.border.color = lv_color_hex3(0x333); /*Needle middle circle color*/
+ style.line.width = 3;
+ style.text.color = LV_COLOR_WHITE;
+ style.line.color = LV_COLOR_RED; /*Line color after the critical value*/
+
+
+ /*Describe the color for the needles*/
+
+ needle_colors[0] = LV_COLOR_ORANGE;
+
+ /*Create a gauge*/
+ gauge1 = lv_gauge_create(lv_scr_act(), NULL);
+ lv_gauge_set_style(gauge1, LV_GAUGE_STYLE_MAIN, &style);
+ lv_gauge_set_needle_count(gauge1, 1, needle_colors);
+ lv_obj_set_size(gauge1, 180, 180);
+ lv_obj_align(gauge1, NULL, LV_ALIGN_CENTER, 0, 0);
+ lv_gauge_set_scale(gauge1, 360, 60, 0);
+ lv_gauge_set_range(gauge1, 0, 59);
+
+ /*Set the values*/
+ lv_gauge_set_value(gauge1, 0, value);
+}
+
+Gauge::~Gauge() {
+
+
+ lv_obj_clean(lv_scr_act());
+}
+
+bool Gauge::Refresh() {
+// lv_lmeter_set_value(lmeter, value++); /*Set the current value*/
+// if(value>=60) value = 0;
+
+ lv_gauge_set_value(gauge1, 0, value++);
+ if(value == 59) value = 0;
+ return running;
+}
+
+bool Gauge::OnButtonPushed() {
+ running = false;
+ return true;
+}
diff --git a/src/displayapp/screens/Gauge.h b/src/displayapp/screens/Gauge.h
new file mode 100644
index 00000000..03c06bed
--- /dev/null
+++ b/src/displayapp/screens/Gauge.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <cstdint>
+#include "Screen.h"
+#include <bits/unique_ptr.h>
+#include <libs/lvgl/src/lv_core/lv_style.h>
+#include <libs/lvgl/src/lv_core/lv_obj.h>
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+
+ class Gauge : public Screen{
+ public:
+ Gauge(DisplayApp* app);
+ ~Gauge() override;
+
+ bool Refresh() override;
+ bool OnButtonPushed() override;
+
+ private:
+ lv_style_t style;
+ lv_color_t needle_colors[3];
+ lv_obj_t * gauge1;
+
+ uint32_t value=30;
+ bool running = true;
+
+ };
+ }
+ }
+}
diff --git a/src/displayapp/screens/InfiniPaint.cpp b/src/displayapp/screens/InfiniPaint.cpp
new file mode 100644
index 00000000..b340f5d8
--- /dev/null
+++ b/src/displayapp/screens/InfiniPaint.cpp
@@ -0,0 +1,44 @@
+#include <libs/lvgl/lvgl.h>
+#include <libraries/log/nrf_log.h>
+#include "InfiniPaint.h"
+#include "../DisplayApp.h"
+
+using namespace Pinetime::Applications::Screens;
+extern lv_font_t jetbrains_mono_extrabold_compressed;
+extern lv_font_t jetbrains_mono_bold_20;
+
+InfiniPaint::InfiniPaint(Pinetime::Applications::DisplayApp *app, Pinetime::Components::LittleVgl& lvgl) : Screen(app), lvgl{lvgl} {
+ app->SetTouchMode(DisplayApp::TouchModes::Polling);
+ std::fill(b, b+bufferSize, LV_COLOR_WHITE);
+}
+
+InfiniPaint::~InfiniPaint() {
+ // Reset the touchmode
+ app->SetTouchMode(DisplayApp::TouchModes::Gestures);
+ lv_obj_clean(lv_scr_act());
+}
+
+bool InfiniPaint::Refresh() {
+ return running;
+}
+
+bool InfiniPaint::OnButtonPushed() {
+ running = false;
+ return true;
+}
+
+bool InfiniPaint::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
+ return true;
+}
+
+bool InfiniPaint::OnTouchEvent(uint16_t x, uint16_t y) {
+ lv_area_t area;
+ area.x1 = x-(width/2);
+ area.y1 = y-(height/2);
+ area.x2 = x+(width/2)-1;
+ area.y2 = y+(height/2)-1;
+ lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::None);
+ lvgl.FlushDisplay(&area, b);
+ return true;
+}
+
diff --git a/src/displayapp/screens/InfiniPaint.h b/src/displayapp/screens/InfiniPaint.h
new file mode 100644
index 00000000..a1592f97
--- /dev/null
+++ b/src/displayapp/screens/InfiniPaint.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <cstdint>
+#include "Screen.h"
+#include <bits/unique_ptr.h>
+#include <libs/lvgl/src/lv_core/lv_style.h>
+#include <libs/lvgl/src/lv_core/lv_obj.h>
+#include <drivers/St7789.h>
+#include <DisplayApp/LittleVgl.h>
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+
+ class InfiniPaint : public Screen{
+ public:
+ InfiniPaint(DisplayApp* app, Pinetime::Components::LittleVgl& lvgl);
+ ~InfiniPaint() override;
+
+ bool Refresh() override;
+ bool OnButtonPushed() override;
+ bool OnTouchEvent(TouchEvents event) override;
+ bool OnTouchEvent(uint16_t x, uint16_t y) override;
+
+ private:
+ Pinetime::Components::LittleVgl& lvgl;
+ static constexpr uint16_t width = 10;
+ static constexpr uint16_t height = 10;
+ static constexpr uint16_t bufferSize = width*height;
+ lv_color_t b[bufferSize];
+ bool running = true;
+ };
+ }
+ }
+}
diff --git a/src/displayapp/screens/Label.cpp b/src/displayapp/screens/Label.cpp
new file mode 100644
index 00000000..780ee88e
--- /dev/null
+++ b/src/displayapp/screens/Label.cpp
@@ -0,0 +1,15 @@
+#include <libs/lvgl/lvgl.h>
+#include "Label.h"
+
+using namespace Pinetime::Applications::Screens;
+
+Label::Label(Pinetime::Applications::DisplayApp *app, const char *text) : Screen(app), text{text} {
+ label = lv_label_create(lv_scr_act(), NULL);
+ lv_label_set_align(label, LV_LABEL_ALIGN_LEFT);
+ lv_obj_set_size(label, 240, 240);
+ lv_label_set_text(label, text);
+}
+
+Label::~Label() {
+ lv_obj_clean(lv_scr_act());
+}
diff --git a/src/displayapp/screens/Label.h b/src/displayapp/screens/Label.h
new file mode 100644
index 00000000..3e7b3797
--- /dev/null
+++ b/src/displayapp/screens/Label.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <vector>
+#include "Screen.h"
+#include <lvgl/lvgl.h>
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+
+ class Label : public Screen {
+ public:
+ Label(DisplayApp* app, const char* text);
+ ~Label() override;
+ bool Refresh() override {return false;}
+
+ private:
+ lv_obj_t * label = nullptr;
+ const char* text = nullptr;
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/src/displayapp/screens/Meter.cpp b/src/displayapp/screens/Meter.cpp
new file mode 100644
index 00000000..c74b8bdf
--- /dev/null
+++ b/src/displayapp/screens/Meter.cpp
@@ -0,0 +1,47 @@
+#include <libs/lvgl/lvgl.h>
+#include "Meter.h"
+#include "../DisplayApp.h"
+
+using namespace Pinetime::Applications::Screens;
+extern lv_font_t jetbrains_mono_extrabold_compressed;
+extern lv_font_t jetbrains_mono_bold_20;
+
+
+Meter::Meter(Pinetime::Applications::DisplayApp *app) : Screen(app) {
+
+ lv_style_copy(&style_lmeter, &lv_style_pretty_color);
+ style_lmeter.line.width = 2;
+ style_lmeter.line.color = LV_COLOR_SILVER;
+ style_lmeter.body.main_color = lv_color_make(255,0,0);
+ style_lmeter.body.grad_color = lv_color_make(160,0,0);
+ style_lmeter.body.padding.left = 16; /*Line length*/
+
+ /*Create a line meter */
+ lmeter = lv_lmeter_create(lv_scr_act(), NULL);
+ lv_lmeter_set_range(lmeter, 0, 60); /*Set the range*/
+ lv_lmeter_set_value(lmeter, value); /*Set the current value*/
+ lv_lmeter_set_angle_offset(lmeter, 180);
+ lv_lmeter_set_scale(lmeter, 360, 60); /*Set the angle and number of lines*/
+ lv_lmeter_set_style(lmeter, LV_LMETER_STYLE_MAIN, &style_lmeter); /*Apply the new style*/
+ lv_obj_set_size(lmeter, 150, 150);
+ lv_obj_align(lmeter, NULL, LV_ALIGN_CENTER, 0, 0);
+
+}
+
+Meter::~Meter() {
+
+
+ lv_obj_clean(lv_scr_act());
+}
+
+bool Meter::Refresh() {
+ lv_lmeter_set_value(lmeter, value++); /*Set the current value*/
+ if(value>=60) value = 0;
+
+ return running;
+}
+
+bool Meter::OnButtonPushed() {
+ running = false;
+ return true;
+}
diff --git a/src/displayapp/screens/Meter.h b/src/displayapp/screens/Meter.h
new file mode 100644
index 00000000..ddf8be8d
--- /dev/null
+++ b/src/displayapp/screens/Meter.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <cstdint>
+#include <chrono>
+#include "Screen.h"
+#include <bits/unique_ptr.h>
+#include <libs/lvgl/src/lv_core/lv_style.h>
+#include <libs/lvgl/src/lv_core/lv_obj.h>
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+
+ class Meter : public Screen{
+ public:
+ Meter(DisplayApp* app);
+ ~Meter() override;
+
+ bool Refresh() override;
+ bool OnButtonPushed() override;
+
+ private:
+ lv_style_t style_lmeter;
+ lv_obj_t * lmeter;
+
+ uint32_t value=0;
+ bool running = true;
+
+ };
+ }
+ }
+}
diff --git a/src/displayapp/screens/Modal.cpp b/src/displayapp/screens/Modal.cpp
new file mode 100644
index 00000000..63ae70c0
--- /dev/null
+++ b/src/displayapp/screens/Modal.cpp
@@ -0,0 +1,81 @@
+#include <libs/lvgl/lvgl.h>
+#include "Modal.h"
+#include "../DisplayApp.h"
+
+using namespace Pinetime::Applications::Screens;
+extern lv_font_t jetbrains_mono_extrabold_compressed;
+extern lv_font_t jetbrains_mono_bold_20;
+
+Modal::Modal(Pinetime::Applications::DisplayApp *app) : Screen(app) {
+
+
+}
+
+Modal::~Modal() {
+ lv_obj_clean(lv_scr_act());
+}
+
+bool Modal::Refresh() {
+
+ return running;
+}
+
+bool Modal::OnButtonPushed() {
+ running = false;
+ return true;
+}
+
+void Modal::Hide() {
+ /* Delete the parent modal background */
+ lv_obj_del_async(lv_obj_get_parent(mbox));
+ mbox = NULL; /* happens before object is actually deleted! */
+ isVisible = false;
+}
+
+void Modal::mbox_event_cb(lv_obj_t *obj, lv_event_t evt) {
+ auto* m = static_cast<Modal *>(obj->user_data);
+ m->OnEvent(obj, evt);
+}
+
+void Modal::OnEvent(lv_obj_t *event_obj, lv_event_t evt) {
+ if(evt == LV_EVENT_DELETE && event_obj == mbox) {
+ Hide();
+ } else if(evt == LV_EVENT_VALUE_CHANGED) {
+ /* A button was clicked */
+ lv_mbox_start_auto_close(mbox, 0);
+// Hide();
+ }
+}
+
+void Modal::Show(const char* msg) {
+ if(isVisible) return;
+ isVisible = true;
+ lv_style_copy(&modal_style, &lv_style_plain_color);
+ modal_style.body.main_color = modal_style.body.grad_color = LV_COLOR_BLACK;
+ modal_style.body.opa = LV_OPA_50;
+
+ obj = lv_obj_create(lv_scr_act(), NULL);
+ lv_obj_set_style(obj, &modal_style);
+ lv_obj_set_pos(obj, 0, 0);
+ lv_obj_set_size(obj, LV_HOR_RES, LV_VER_RES);
+ lv_obj_set_opa_scale_enable(obj, true); /* Enable opacity scaling for the animation */
+
+ static const char * btns2[] = {"Ok", ""};
+
+ /* Create the message box as a child of the modal background */
+ mbox = lv_mbox_create(obj, NULL);
+ lv_mbox_add_btns(mbox, btns2);
+ lv_mbox_set_text(mbox, msg);
+ lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0);
+ lv_obj_set_event_cb(mbox, Modal::mbox_event_cb);
+
+ mbox->user_data = this;
+
+ /* Fade the message box in with an animation */
+ lv_anim_t a;
+ lv_anim_init(&a);
+ lv_anim_set_time(&a, 500, 0);
+ lv_anim_set_values(&a, LV_OPA_TRANSP, LV_OPA_COVER);
+ lv_anim_set_exec_cb(&a, obj, (lv_anim_exec_xcb_t)lv_obj_set_opa_scale);
+ lv_anim_create(&a);
+}
diff --git a/src/displayapp/screens/Modal.h b/src/displayapp/screens/Modal.h
new file mode 100644
index 00000000..c616c294
--- /dev/null
+++ b/src/displayapp/screens/Modal.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <cstdint>
+#include <chrono>
+#include "Screen.h"
+#include <bits/unique_ptr.h>
+#include <libs/lvgl/src/lv_core/lv_style.h>
+#include <libs/lvgl/src/lv_core/lv_obj.h>
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+
+ class Modal : public Screen{
+ public:
+ Modal(DisplayApp* app);
+ ~Modal() override;
+
+ void Show(const char* msg);
+ void Hide();
+
+ bool Refresh() override;
+ bool OnButtonPushed() override;
+
+ static void mbox_event_cb(lv_obj_t *obj, lv_event_t evt);
+ private:
+ void OnEvent(lv_obj_t *event_obj, lv_event_t evt);
+
+ lv_style_t modal_style;
+ lv_obj_t *obj;
+ lv_obj_t *mbox;
+ lv_obj_t *info;
+ bool running = true;
+ bool isVisible = false;
+
+ };
+ }
+ }
+}
diff --git a/src/displayapp/screens/Music.cpp b/src/displayapp/screens/Music.cpp
new file mode 100644
index 00000000..9b7d198b
--- /dev/null
+++ b/src/displayapp/screens/Music.cpp
@@ -0,0 +1,125 @@
+#include <libs/lvgl/lvgl.h>
+#include "Music.h"
+
+using namespace Pinetime::Applications::Screens;
+extern lv_font_t jetbrains_mono_extrabold_compressed;
+extern lv_font_t jetbrains_mono_bold_20;
+
+static void event_handler(lv_obj_t * obj, lv_event_t event)
+{
+ Music* screen = static_cast<Music *>(obj->user_data);
+ screen->OnObjectEvent(obj, event);
+}
+
+Music::Music(Pinetime::Applications::DisplayApp *app, Pinetime::Controllers::MusicService &music) : Screen(app), musicService(music) {
+ lv_obj_t * label;
+
+ btnVolDown = lv_btn_create(lv_scr_act(), NULL);
+ btnVolDown->user_data = this;
+ lv_obj_set_event_cb(btnVolDown, event_handler);
+ lv_obj_align(btnVolDown, NULL, LV_ALIGN_IN_TOP_LEFT, 10, 10);
+ label = lv_label_create(btnVolDown, NULL);
+ lv_label_set_text(label, "v-");
+
+ btnVolUp = lv_btn_create(lv_scr_act(), NULL);
+ btnVolUp->user_data = this;
+ lv_obj_set_event_cb(btnVolUp, event_handler);
+ lv_obj_align(btnVolUp, NULL, LV_ALIGN_IN_TOP_RIGHT, -10, 10);
+ label = lv_label_create(btnVolUp, NULL);
+ lv_label_set_text(label, "v+");
+
+ btnPrev = lv_btn_create(lv_scr_act(), NULL);
+ btnPrev->user_data = this;
+ lv_obj_set_event_cb(btnPrev, event_handler);
+ lv_obj_set_size(btnPrev, LV_HOR_RES / 4, LV_VER_RES / 4);
+ lv_obj_align(btnPrev, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 10,-10);
+ label = lv_label_create(btnPrev, NULL);
+ lv_label_set_text(label, "<<");
+
+ btnPlayPause = lv_btn_create(lv_scr_act(), NULL);
+ btnPlayPause->user_data = this;
+ lv_obj_set_event_cb(btnPlayPause, event_handler);
+ lv_obj_set_size(btnPlayPause, LV_HOR_RES / 4, LV_VER_RES / 4);
+ lv_obj_align(btnPlayPause, NULL, LV_ALIGN_IN_BOTTOM_MID, 0,-10);
+ txtPlayPause = lv_label_create(btnPlayPause, NULL);
+ lv_label_set_text(txtPlayPause, ">");
+
+ btnNext = lv_btn_create(lv_scr_act(), NULL);
+ btnNext->user_data = this;
+ lv_obj_set_event_cb(btnNext, event_handler);
+ lv_obj_set_size(btnNext, LV_HOR_RES / 4, LV_VER_RES / 4);
+ lv_obj_align(btnNext, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, -10,-10);
+ label = lv_label_create(btnNext, NULL);
+ lv_label_set_text(label, ">>");
+
+ txtArtist = lv_label_create(lv_scr_act(), NULL);
+ lv_label_set_long_mode(txtArtist, LV_LABEL_LONG_SROLL);
+ lv_obj_align(txtArtist, NULL, LV_ALIGN_IN_LEFT_MID, 0,-20);
+ lv_label_set_text(txtArtist, "Artist Name");
+ lv_label_set_align(txtArtist, LV_LABEL_ALIGN_CENTER);
+ lv_obj_set_width(txtArtist, LV_HOR_RES);
+
+ txtTrack = lv_label_create(lv_scr_act(), NULL);
+ lv_label_set_long_mode(txtTrack, LV_LABEL_LONG_DOT);
+ lv_obj_align(txtTrack, NULL, LV_ALIGN_IN_LEFT_MID, 0,20);
+ lv_label_set_text(txtTrack, "This is a very long track name");
+ lv_label_set_align(txtTrack, LV_LABEL_ALIGN_CENTER);
+ lv_obj_set_width(txtTrack, LV_HOR_RES);
+
+ musicService.event(Controllers::MusicService::EVENT_MUSIC_OPEN);
+}
+
+Music::~Music() {
+ lv_obj_clean(lv_scr_act());
+}
+
+bool Music::OnButtonPushed() {
+ running = false;
+ return true;
+}
+
+bool Music::Refresh() {
+
+ if (m_artist != musicService.artist()) {
+ m_artist = musicService.artist();
+ lv_label_set_text(txtArtist, m_artist.data());
+ }
+ if (m_track != musicService.track()) {
+ m_track = musicService.track();
+ lv_label_set_text(txtTrack, m_track.data());
+ }
+ if (m_album != musicService.album()) {
+ m_album = musicService.album();
+ }
+ if (m_status != musicService.status()) {
+ m_status = musicService.status();
+ }
+ if (m_status == Pinetime::Controllers::MusicService::STATUS_MUSIC_PLAYING) {
+ lv_label_set_text(txtPlayPause, "||");
+ } else {
+ lv_label_set_text(txtPlayPause, ">");
+ }
+
+ return running;
+}
+
+void Music::OnObjectEvent(lv_obj_t* obj, lv_event_t event)
+{
+ if (event == LV_EVENT_CLICKED) {
+ if (obj == btnVolDown) {
+ musicService.event(Controllers::MusicService::EVENT_MUSIC_VOLDOWN);
+ } else if (obj == btnVolUp) {
+ musicService.event(Controllers::MusicService::EVENT_MUSIC_VOLUP);
+ } else if (obj == btnPrev) {
+ musicService.event(Controllers::MusicService::EVENT_MUSIC_PREV);
+ } else if (obj == btnPlayPause) {
+ if (m_status == Pinetime::Controllers::MusicService::STATUS_MUSIC_PLAYING) {
+ musicService.event(Controllers::MusicService::EVENT_MUSIC_PAUSE);
+ } else {
+ musicService.event(Controllers::MusicService::EVENT_MUSIC_PLAY);
+ }
+ } else if (obj == btnNext) {
+ musicService.event(Controllers::MusicService::EVENT_MUSIC_NEXT);
+ }
+ }
+}
diff --git a/src/displayapp/screens/Music.h b/src/displayapp/screens/Music.h
new file mode 100644
index 00000000..95cac0f0
--- /dev/null
+++ b/src/displayapp/screens/Music.h
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <cstdint>
+#include <chrono>
+#include <Components/Gfx/Gfx.h>
+#include "Screen.h"
+#include <bits/unique_ptr.h>
+#include <libs/lvgl/src/lv_core/lv_style.h>
+#include <libs/lvgl/src/lv_core/lv_obj.h>
+#include <Components/Battery/BatteryController.h>
+#include <Components/Ble/BleController.h>
+#include "../../Version.h"
+#include <Components/Ble/MusicService.h>
+#include <string>
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+
+ class Music : public Screen{
+ public:
+ Music(DisplayApp* app, Pinetime::Controllers::MusicService &music);
+ ~Music() override;
+
+ bool Refresh() override;
+ bool OnButtonPushed() override;
+
+ void OnObjectEvent(lv_obj_t* obj, lv_event_t event);
+
+ private:
+ lv_obj_t * btnPrev;
+ lv_obj_t * btnPlayPause;
+ lv_obj_t * btnNext;
+ lv_obj_t * btnVolDown;
+ lv_obj_t * btnVolUp;
+ lv_obj_t * txtArtist;
+ lv_obj_t * txtTrack;
+ lv_obj_t * txtPlayPause;
+
+ bool running = true;
+ Pinetime::Controllers::MusicService &musicService;
+ std::string m_artist;
+ std::string m_album;
+ std::string m_track;
+ unsigned char m_status;
+ };
+ }
+ }
+}
diff --git a/src/displayapp/screens/Screen.cpp b/src/displayapp/screens/Screen.cpp
new file mode 100644
index 00000000..1467df33
--- /dev/null
+++ b/src/displayapp/screens/Screen.cpp
@@ -0,0 +1,2 @@
+#include "Screen.h"
+using namespace Pinetime::Applications::Screens; \ No newline at end of file
diff --git a/src/displayapp/screens/Screen.h b/src/displayapp/screens/Screen.h
new file mode 100644
index 00000000..dbf81a44
--- /dev/null
+++ b/src/displayapp/screens/Screen.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <cstdint>
+#include "../TouchEvents.h"
+
+namespace Pinetime {
+ namespace Applications {
+ class DisplayApp;
+ namespace Screens {
+ class Screen {
+ public:
+ Screen(DisplayApp* app) : app{app} {}
+ virtual ~Screen() = default;
+
+ // Return false if the app can be closed, true if it must continue to run
+ virtual bool Refresh() = 0;
+
+ // Return false if the button hasn't been handled by the app, true if it has been handled
+ virtual bool OnButtonPushed() { return false; }
+
+ // Return false if the event hasn't been handled by the app, true if it has been handled
+ virtual bool OnTouchEvent(TouchEvents event) { return false; }
+ virtual bool OnTouchEvent(uint16_t x, uint16_t y) { return false; }
+
+ protected:
+ DisplayApp* app;
+ };
+ }
+ }
+}
diff --git a/src/displayapp/screens/ScreenList.h b/src/displayapp/screens/ScreenList.h
new file mode 100644
index 00000000..d873336d
--- /dev/null
+++ b/src/displayapp/screens/ScreenList.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include <vector>
+#include <Components/Ble/NimbleController.h>
+#include <functional>
+#include "Screen.h"
+#include "Label.h"
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+ template <size_t N>
+ class ScreenList : public Screen {
+ public:
+ ScreenList(DisplayApp* app, std::array<std::function<std::unique_ptr<Screen>()>, N>&& screens)
+ : Screen(app), screens{std::move(screens)}, current{this->screens[0]()} {
+
+ }
+
+ ~ScreenList() override {
+
+ }
+
+ bool Refresh() override {
+ running = current->Refresh();
+ return running;
+ }
+
+ bool OnButtonPushed() override {
+ running = false;
+ return true;
+ }
+
+ 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;
+ }
+ return false;
+ }
+
+ private:
+ bool running = true;
+ uint8_t screenIndex = 0;
+ std::array<std::function<std::unique_ptr<Screen>()>, N> screens;
+ std::unique_ptr<Screen> current;
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/src/displayapp/screens/Symbols.h b/src/displayapp/screens/Symbols.h
new file mode 100644
index 00000000..aeea3243
--- /dev/null
+++ b/src/displayapp/screens/Symbols.h
@@ -0,0 +1,30 @@
+#pragma once
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+ namespace Symbols {
+ static constexpr const char* none = "";
+ static constexpr const char* batteryFull = "\xEF\x89\x80";
+ static constexpr const char* batteryEmpty = "\xEF\x89\x84";
+ static constexpr const char* batteryThreeQuarter = "\xEF\x89\x81";
+ static constexpr const char* batteryHalf = "\xEF\x89\x82";
+ static constexpr const char* batteryOneQuarter = "\xEF\x89\x83";
+ static constexpr const char* heartBeat = "\xEF\x88\x9E";
+ static constexpr const char* bluetoothFull = "\xEF\x8A\x93";
+ static constexpr const char* bluetooth = "\xEF\x8A\x94";
+ static constexpr const char* plug = "\xEF\x87\xA6";
+ static constexpr const char* shoe = "\xEF\x95\x8B";
+ static constexpr const char* clock = "\xEF\x80\x97";
+ static constexpr const char* info = "\xEF\x84\xA9";
+ static constexpr const char* list = "\xEF\x80\xBA";
+ static constexpr const char* sun = "\xEF\x86\x85";
+ static constexpr const char* check = "\xEF\x95\xA0";
+ static constexpr const char* music = "\xEF\x80\x81";
+ static constexpr const char* tachometer = "\xEF\x8F\xBD";
+ static constexpr const char* asterisk = "\xEF\x81\xA9";
+ static constexpr const char* paintbrush = "\xEF\x87\xBC";
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/displayapp/screens/SystemInfo.cpp b/src/displayapp/screens/SystemInfo.cpp
new file mode 100644
index 00000000..fcafcf7d
--- /dev/null
+++ b/src/displayapp/screens/SystemInfo.cpp
@@ -0,0 +1,116 @@
+#include <libs/lvgl/lvgl.h>
+#include <DisplayApp/DisplayApp.h>
+#include <functional>
+#include "SystemInfo.h"
+#include "../../Version.h"
+#include "Tile.h"
+
+using namespace Pinetime::Applications::Screens;
+
+SystemInfo::SystemInfo(Pinetime::Applications::DisplayApp *app,
+ Pinetime::Controllers::DateTime &dateTimeController,
+ Pinetime::Controllers::Battery& batteryController,
+ Pinetime::Controllers::BrightnessController& brightnessController,
+ Pinetime::Controllers::Ble& bleController,
+ Pinetime::Drivers::WatchdogView& watchdog) :
+ Screen(app),
+ dateTimeController{dateTimeController}, batteryController{batteryController},
+ brightnessController{brightnessController}, bleController{bleController}, watchdog{watchdog},
+ screens{app, {
+ [this]() -> std::unique_ptr<Screen> { return CreateScreen1(); },
+ [this]() -> std::unique_ptr<Screen> { return CreateScreen2(); },
+ [this]() -> std::unique_ptr<Screen> { return CreateScreen3(); }
+ }
+ } {}
+
+
+SystemInfo::~SystemInfo() {
+ lv_obj_clean(lv_scr_act());
+}
+
+bool SystemInfo::Refresh() {
+ screens.Refresh();
+ return running;
+}
+
+bool SystemInfo::OnButtonPushed() {
+ running = false;
+ return true;
+}
+
+bool SystemInfo::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
+ return screens.OnTouchEvent(event);
+}
+
+std::unique_ptr<Screen> SystemInfo::CreateScreen1() {
+ auto batteryPercentF = batteryController.PercentRemaining();
+ uint16_t batteryPercent = 0;
+ if(batteryPercentF > 100.0f) batteryPercent = 100;
+ else if(batteryPercentF < 0.0f) batteryPercent = 0;
+
+ uint8_t brightness = 0;
+ switch(brightnessController.Level()) {
+ case Controllers::BrightnessController::Levels::Off: brightness = 0; break;
+ case Controllers::BrightnessController::Levels::Low: brightness = 1; break;
+ case Controllers::BrightnessController::Levels::Medium: brightness = 2; break;
+ case Controllers::BrightnessController::Levels::High: brightness = 3; break;
+ }
+ auto resetReason = [this]() {
+ switch (watchdog.ResetReason()) {
+ case Drivers::Watchdog::ResetReasons::Watchdog: return "wtdg";
+ case Drivers::Watchdog::ResetReasons::HardReset: return "hardr";
+ case Drivers::Watchdog::ResetReasons::NFC: return "nfc";
+ case Drivers::Watchdog::ResetReasons::SoftReset: return "softr";
+ case Drivers::Watchdog::ResetReasons::CpuLockup: return "cpulock";
+ case Drivers::Watchdog::ResetReasons::SystemOff: return "off";
+ case Drivers::Watchdog::ResetReasons::LpComp: return "lpcomp";
+ case Drivers::Watchdog::ResetReasons::DebugInterface: return "dbg";
+ case Drivers::Watchdog::ResetReasons::ResetPin: return "rst";
+ default: return "?";
+ }
+ }();
+
+ // uptime
+ static constexpr uint32_t secondsInADay = 60*60*24;
+ static constexpr uint32_t secondsInAnHour = 60*60;
+ static constexpr uint32_t secondsInAMinute = 60;
+ uint32_t uptimeSeconds = dateTimeController.Uptime().count();
+ uint32_t uptimeDays = (uptimeSeconds / secondsInADay);
+ uptimeSeconds = uptimeSeconds % secondsInADay;
+ uint32_t uptimeHours = uptimeSeconds / secondsInAnHour;
+ uptimeSeconds = uptimeSeconds % secondsInAnHour;
+ uint32_t uptimeMinutes = uptimeSeconds / secondsInAMinute;
+ uptimeSeconds = uptimeSeconds % secondsInAMinute;
+ // TODO handle more than 100 days of uptime
+
+ sprintf(t1, "Pinetime\n"
+ "Version:%ld.%ld.%ld\n"
+ "Build: %s\n"
+ " %s\n"
+ "Date: %02d/%02d/%04d\n"
+ "Time: %02d:%02d:%02d\n"
+ "Uptime: %02lud %02lu:%02lu:%02lu\n"
+ "Battery: %d%%\n"
+ "Backlight: %d/3\n"
+ "Last reset: %s\n",
+ Version::Major(), Version::Minor(), Version::Patch(),
+ __DATE__, __TIME__,
+ dateTimeController.Day(), static_cast<uint8_t>(dateTimeController.Month()), dateTimeController.Year(),
+ dateTimeController.Hours(), dateTimeController.Minutes(), dateTimeController.Seconds(),
+ uptimeDays, uptimeHours, uptimeMinutes, uptimeSeconds,
+ batteryPercent, brightness, resetReason);
+
+ return std::unique_ptr<Screen>(new Screens::Label(app, t1));
+}
+
+std::unique_ptr<Screen> SystemInfo::CreateScreen2() {
+ auto& bleAddr = bleController.Address();
+ sprintf(t2, "BLE MAC: \n %2x:%2x:%2x:%2x:%2x:%2x",
+ bleAddr[5], bleAddr[4], bleAddr[3], bleAddr[2], bleAddr[1], bleAddr[0]);
+ return std::unique_ptr<Screen>(new Screens::Label(app, t2));
+}
+
+std::unique_ptr<Screen> SystemInfo::CreateScreen3() {
+ strncpy(t3, "Hello from\nthe developper!", 27);
+ return std::unique_ptr<Screen>(new Screens::Label(app, t3));
+}
diff --git a/src/displayapp/screens/SystemInfo.h b/src/displayapp/screens/SystemInfo.h
new file mode 100644
index 00000000..ac8abae5
--- /dev/null
+++ b/src/displayapp/screens/SystemInfo.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <vector>
+#include <Components/Ble/NimbleController.h>
+#include "Screen.h"
+#include "Label.h"
+#include "ScreenList.h"
+#include "Gauge.h"
+#include "Meter.h"
+#include <functional>
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+ class SystemInfo : public Screen {
+ public:
+ explicit SystemInfo(DisplayApp* app,
+ Pinetime::Controllers::DateTime& dateTimeController,
+ Pinetime::Controllers::Battery& batteryController,
+ Pinetime::Controllers::BrightnessController& brightnessController,
+ Pinetime::Controllers::Ble& bleController,
+ Pinetime::Drivers::WatchdogView& watchdog);
+ ~SystemInfo() override;
+ bool Refresh() override;
+ bool OnButtonPushed() override;
+ bool OnTouchEvent(TouchEvents event) override;
+ private:
+ bool running = true;
+
+ Pinetime::Controllers::DateTime& dateTimeController;
+ Pinetime::Controllers::Battery& batteryController;
+ Pinetime::Controllers::BrightnessController& brightnessController;
+ Pinetime::Controllers::Ble& bleController;
+ Pinetime::Drivers::WatchdogView& watchdog;
+
+ char t1[200];
+ char t2[200];
+ char t3[30];
+
+ ScreenList<3> screens;
+ std::unique_ptr<Screen> CreateScreen1();
+ std::unique_ptr<Screen> CreateScreen2();
+ std::unique_ptr<Screen> CreateScreen3();
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/src/displayapp/screens/Tab.cpp b/src/displayapp/screens/Tab.cpp
new file mode 100644
index 00000000..adc32578
--- /dev/null
+++ b/src/displayapp/screens/Tab.cpp
@@ -0,0 +1,67 @@
+#include <cstdio>
+#include <libs/date/includes/date/date.h>
+#include <Components/DateTime/DateTimeController.h>
+#include <Version.h>
+#include <libs/lvgl/src/lv_core/lv_obj.h>
+#include <libs/lvgl/src/lv_font/lv_font.h>
+#include <libs/lvgl/lvgl.h>
+#include <libraries/log/nrf_log.h>
+#include "Tab.h"
+#include <DisplayApp/DisplayApp.h>
+
+
+using namespace Pinetime::Applications::Screens;
+
+extern lv_font_t jetbrains_mono_bold_20;
+
+//static void event_handler(lv_obj_t * obj, lv_event_t event) {
+// Tile* screen = static_cast<Tile *>(obj->user_data);
+// screen->OnObjectEvent(obj, event);
+//}
+
+Tab::Tab(DisplayApp* app, Pinetime::Components::Gfx &gfx) : Screen(app, gfx) {
+/*Create a Tab view object*/
+ lv_obj_t *tabview;
+ tabview = lv_tabview_create(lv_scr_act(), NULL);
+
+ /*Add 3 tabs (the tabs are page (lv_page) and can be scrolled*/
+ lv_obj_t *tab1 = lv_tabview_add_tab(tabview, "Tab 1");
+ lv_obj_t *tab2 = lv_tabview_add_tab(tabview, "Tab 2");
+ lv_obj_t *tab3 = lv_tabview_add_tab(tabview, "Tab 3");
+
+
+ /*Add content to the tabs*/
+ lv_obj_t * label = lv_label_create(tab1, NULL);
+ lv_label_set_text(label, "This the first tab\n\n"
+ "If the content\n"
+ "of a tab\n"
+ "become too long\n"
+ "the it \n"
+ "automatically\n"
+ "become\n"
+ "scrollable.");
+
+ label = lv_label_create(tab2, NULL);
+ lv_label_set_text(label, "Second tab");
+
+ label = lv_label_create(tab3, NULL);
+ lv_label_set_text(label, "Third tab");
+
+}
+
+Tab::~Tab() {
+ lv_obj_clean(lv_scr_act());
+}
+
+void Tab::Refresh(bool fullRefresh) {
+
+}
+
+void Tab::OnObjectEvent(lv_obj_t *obj, lv_event_t event) {
+ if(event == LV_EVENT_CLICKED) {
+ NRF_LOG_INFO("Clicked");
+ }
+ else if(event == LV_EVENT_VALUE_CHANGED) {
+ NRF_LOG_INFO("Toggled");
+ }
+}
diff --git a/src/displayapp/screens/Tab.h b/src/displayapp/screens/Tab.h
new file mode 100644
index 00000000..e16dbb96
--- /dev/null
+++ b/src/displayapp/screens/Tab.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <cstdint>
+#include "Screen.h"
+#include <bits/unique_ptr.h>
+#include <lvgl/src/lv_core/lv_style.h>
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+ class Tab : public Screen {
+ public:
+ explicit Tab(DisplayApp* app, Components::Gfx& gfx);
+ ~Tab() override;
+ void Refresh(bool fullRefresh) override;
+ void OnObjectEvent(lv_obj_t* obj, lv_event_t event);
+
+ private:
+
+ };
+ }
+ }
+}
diff --git a/src/displayapp/screens/Tile.cpp b/src/displayapp/screens/Tile.cpp
new file mode 100644
index 00000000..1447d789
--- /dev/null
+++ b/src/displayapp/screens/Tile.cpp
@@ -0,0 +1,61 @@
+#include <libs/lvgl/src/lv_core/lv_obj.h>
+#include <libs/lvgl/src/lv_font/lv_font.h>
+#include <libs/lvgl/lvgl.h>
+#include "Tile.h"
+#include <DisplayApp/DisplayApp.h>
+#include "Symbols.h"
+#include "../../Version.h"
+
+using namespace Pinetime::Applications::Screens;
+
+extern lv_font_t jetbrains_mono_bold_20;
+
+static void event_handler(lv_obj_t * obj, lv_event_t event) {
+ Tile* screen = static_cast<Tile *>(obj->user_data);
+ uint32_t* eventDataPtr = (uint32_t*) lv_event_get_data();
+ uint32_t eventData = *eventDataPtr;
+ screen->OnObjectEvent(obj, event, eventData);
+}
+
+Tile::Tile(DisplayApp* app, std::array<Applications, 6>& applications) : Screen(app) {
+ for(int i = 0, appIndex = 0; i < 8; i++) {
+ if(i == 3) btnm_map1[i] = "\n";
+ else if(i == 7) btnm_map1[i] = "";
+ else {
+ btnm_map1[i] = applications[appIndex].icon;
+ apps[appIndex] = applications[appIndex].application;
+ appIndex++;
+ }
+ }
+ modal.reset(new Modal(app));
+
+ btnm1 = lv_btnm_create(lv_scr_act(), NULL);
+ lv_btnm_set_map(btnm1, btnm_map1);
+ lv_obj_set_size(btnm1, LV_HOR_RES, LV_VER_RES);
+
+ btnm1->user_data = this;
+ lv_obj_set_event_cb(btnm1, event_handler);
+}
+
+Tile::~Tile() {
+ lv_obj_clean(lv_scr_act());
+}
+
+bool Tile::Refresh() {
+ return running;
+}
+
+void Tile::OnObjectEvent(lv_obj_t *obj, lv_event_t event, uint32_t buttonId) {
+ if(event == LV_EVENT_VALUE_CHANGED) {
+ app->StartApp(apps[buttonId]);
+ running = false;
+ }
+}
+
+bool Tile::OnButtonPushed() {
+ app->StartApp(Apps::Clock);
+ running = false;
+ return true;
+}
+
+
diff --git a/src/displayapp/screens/Tile.h b/src/displayapp/screens/Tile.h
new file mode 100644
index 00000000..3136d892
--- /dev/null
+++ b/src/displayapp/screens/Tile.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <cstdint>
+#include "Screen.h"
+#include <bits/unique_ptr.h>
+#include "Modal.h"
+#include <lvgl/src/lv_core/lv_style.h>
+#include <DisplayApp/Apps.h>
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+ class Tile : public Screen {
+ public:
+ struct Applications {
+ const char* icon;
+ Pinetime::Applications::Apps application;
+ };
+
+ explicit Tile(DisplayApp* app, std::array<Applications, 6>& applications);
+ ~Tile() override;
+
+ bool Refresh() override;
+ bool OnButtonPushed() override;
+
+ void OnObjectEvent(lv_obj_t* obj, lv_event_t event, uint32_t buttonId);
+
+ private:
+ lv_obj_t * btnm1;
+ bool running = true;
+
+ std::unique_ptr<Modal> modal;
+
+ const char* btnm_map1[8];
+ Pinetime::Applications::Apps apps[6];
+ };
+ }
+ }
+}