summaryrefslogtreecommitdiff
path: root/src/displayapp
diff options
context:
space:
mode:
Diffstat (limited to 'src/displayapp')
-rw-r--r--src/displayapp/Apps.h2
-rw-r--r--src/displayapp/DisplayApp.cpp40
-rw-r--r--src/displayapp/DisplayAppRecovery.cpp6
-rw-r--r--src/displayapp/fonts/Readme.md2
-rw-r--r--src/displayapp/fonts/jetbrains_mono_bold_20.c105
-rw-r--r--src/displayapp/screens/ApplicationList.cpp14
-rw-r--r--src/displayapp/screens/Clock.cpp8
-rw-r--r--src/displayapp/screens/Notifications.cpp16
-rw-r--r--src/displayapp/screens/StopWatch.cpp210
-rw-r--r--src/displayapp/screens/StopWatch.h86
-rw-r--r--src/displayapp/screens/Symbols.h3
-rw-r--r--src/displayapp/screens/SystemInfo.cpp6
-rw-r--r--src/displayapp/screens/WatchFaceAnalog.cpp2
13 files changed, 416 insertions, 84 deletions
diff --git a/src/displayapp/Apps.h b/src/displayapp/Apps.h
index 028fc80c..74b121df 100644
--- a/src/displayapp/Apps.h
+++ b/src/displayapp/Apps.h
@@ -2,6 +2,6 @@
namespace Pinetime {
namespace Applications {
- enum class Apps {None, Launcher, Clock, SysInfo, Meter, Brightness, Music, FirmwareValidation, Paint, Paddle, Notifications, Twos, HeartRate, Navigation};
+ enum class Apps {None, Launcher, Clock, SysInfo, Meter, Brightness, Music, FirmwareValidation, Paint, Paddle, Notifications, Twos, HeartRate, Navigation, StopWatch};
}
}
diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp
index fee973ac..3de26991 100644
--- a/src/displayapp/DisplayApp.cpp
+++ b/src/displayapp/DisplayApp.cpp
@@ -12,6 +12,7 @@
#include "displayapp/screens/FirmwareValidation.h"
#include "displayapp/screens/InfiniPaint.h"
#include "displayapp/screens/Paddle.h"
+#include "displayapp/screens/StopWatch.h"
#include "displayapp/screens/Meter.h"
#include "displayapp/screens/Music.h"
#include "displayapp/screens/Navigation.h"
@@ -122,7 +123,7 @@ void DisplayApp::Refresh() {
currentScreen.reset(nullptr);
lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::Up);
onClockApp = false;
- currentScreen.reset(new Screens::Notifications(this, notificationManager, systemTask.nimble().alertService(), Screens::Notifications::Modes::Preview));
+ currentScreen = std::make_unique<Screens::Notifications>(this, notificationManager, systemTask.nimble().alertService(), Screens::Notifications::Modes::Preview);
}
}
break;
@@ -160,10 +161,10 @@ void DisplayApp::Refresh() {
// lvgl.SetFullRefresh(components::LittleVgl::FullRefreshDirections::Down);
// currentScreen.reset(nullptr);
// if(toggle) {
-// currentScreen.reset(new Screens::Tile(this));
+// currentScreen = std::make_unique<Screens::Tile>(this);
// toggle = false;
// } else {
-// currentScreen.reset(new Screens::Clock(this, dateTimeController, batteryController, bleController));
+// currentScreen = std::make_unique<Screens::Clock>(this, dateTimeController, batteryController, bleController);
// toggle = true;
// }
@@ -171,10 +172,14 @@ void DisplayApp::Refresh() {
case Messages::BleFirmwareUpdateStarted:
lvgl.SetFullRefresh(Components::LittleVgl::FullRefreshDirections::Down);
currentScreen.reset(nullptr);
- currentScreen.reset(new Screens::FirmwareUpdate(this, bleController));
+ currentScreen = std::make_unique<Screens::FirmwareUpdate>(this, bleController);
onClockApp = false;
break;
+ case Messages::UpdateDateTime:
+ // Added to remove warning
+ // What should happen here?
+ break;
}
}
@@ -197,22 +202,23 @@ void DisplayApp::RunningState() {
onClockApp = false;
switch(nextApp) {
case Apps::None:
- case Apps::Launcher: currentScreen.reset(new Screens::ApplicationList(this, settingsController)); break;
+ case Apps::Launcher: currentScreen = std::make_unique<Screens::ApplicationList>(this, settingsController); break;
case Apps::Clock:
- currentScreen.reset(new Screens::Clock(this, dateTimeController, batteryController, bleController, notificationManager, settingsController, heartRateController));
+ currentScreen = std::make_unique<Screens::Clock>(this, dateTimeController, batteryController, bleController, notificationManager, settingsController, heartRateController);
onClockApp = true;
break;
- case Apps::SysInfo: currentScreen.reset(new Screens::SystemInfo(this, dateTimeController, batteryController, brightnessController, bleController, watchdog)); break;
- case Apps::Meter: currentScreen.reset(new Screens::Meter(this)); break;
- case Apps::Twos: currentScreen.reset(new Screens::Twos(this)); break;
- case Apps::Paint: currentScreen.reset(new Screens::InfiniPaint(this, lvgl)); break;
- case Apps::Paddle: currentScreen.reset(new Screens::Paddle(this, lvgl)); break;
- case Apps::Brightness : currentScreen.reset(new Screens::Brightness(this, brightnessController)); break;
- case Apps::Music : currentScreen.reset(new Screens::Music(this, systemTask.nimble().music())); break;
- case Apps::Navigation : currentScreen.reset(new Screens::Navigation(this, systemTask.nimble().navigation())); break;
- case Apps::FirmwareValidation: currentScreen.reset(new Screens::FirmwareValidation(this, validator)); break;
- case Apps::Notifications: currentScreen.reset(new Screens::Notifications(this, notificationManager, systemTask.nimble().alertService(), Screens::Notifications::Modes::Normal)); break;
- case Apps::HeartRate: currentScreen.reset(new Screens::HeartRate(this, heartRateController)); break;
+ case Apps::SysInfo: currentScreen = std::make_unique<Screens::SystemInfo>(this, dateTimeController, batteryController, brightnessController, bleController, watchdog); break;
+ case Apps::Meter: currentScreen = std::make_unique<Screens::Meter>(this);break;
+ case Apps::StopWatch: currentScreen = std::make_unique<Screens::StopWatch>(this); break;
+ case Apps::Twos: currentScreen = std::make_unique<Screens::Twos>(this); break;
+ case Apps::Paint: currentScreen = std::make_unique<Screens::InfiniPaint>(this, lvgl); break;
+ case Apps::Paddle: currentScreen = std::make_unique<Screens::Paddle>(this, lvgl); break;
+ case Apps::Brightness : currentScreen = std::make_unique<Screens::Brightness>(this, brightnessController); break;
+ case Apps::Music : currentScreen = std::make_unique<Screens::Music>(this, systemTask.nimble().music()); break;
+ case Apps::Navigation : currentScreen = std::make_unique<Screens::Navigation>(this, systemTask.nimble().navigation()); break;
+ case Apps::FirmwareValidation: currentScreen = std::make_unique<Screens::FirmwareValidation>(this, validator); break;
+ case Apps::Notifications: currentScreen = std::make_unique<Screens::Notifications>(this, notificationManager, systemTask.nimble().alertService(), Screens::Notifications::Modes::Normal); break;
+ case Apps::HeartRate: currentScreen = std::make_unique<Screens::HeartRate>(this, heartRateController); break;
}
nextApp = Apps::None;
}
diff --git a/src/displayapp/DisplayAppRecovery.cpp b/src/displayapp/DisplayAppRecovery.cpp
index 9cf76953..57b8aedd 100644
--- a/src/displayapp/DisplayAppRecovery.cpp
+++ b/src/displayapp/DisplayAppRecovery.cpp
@@ -83,8 +83,7 @@ void DisplayApp::DisplayLogo(uint16_t color) {
for(int i = 0; i < displayWidth; i++) {
rleDecoder.DecodeNext(displayBuffer, displayWidth * bytesPerPixel);
ulTaskNotifyTake(pdTRUE, 500);
- lcd.BeginDrawBuffer(0, i, displayWidth, 1);
- lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(displayBuffer), displayWidth * bytesPerPixel);
+ lcd.DrawBuffer(0, i, displayWidth, 1, reinterpret_cast<const uint8_t *>(displayBuffer), displayWidth * bytesPerPixel);
}
}
@@ -94,8 +93,7 @@ void DisplayApp::DisplayOtaProgress(uint8_t percent, uint16_t color) {
for(int i = 0; i < barHeight; i++) {
ulTaskNotifyTake(pdTRUE, 500);
uint16_t barWidth = std::min(static_cast<float>(percent) * 2.4f, static_cast<float>(displayWidth));
- lcd.BeginDrawBuffer(0, displayWidth - barHeight + i, barWidth, 1);
- lcd.NextDrawBuffer(reinterpret_cast<const uint8_t *>(displayBuffer), barWidth * bytesPerPixel);
+ lcd.DrawBuffer(0, displayWidth - barHeight + i, barWidth, 1, reinterpret_cast<const uint8_t *>(displayBuffer), barWidth * bytesPerPixel);
}
}
diff --git a/src/displayapp/fonts/Readme.md b/src/displayapp/fonts/Readme.md
index 8e50c297..79b36bca 100644
--- a/src/displayapp/fonts/Readme.md
+++ b/src/displayapp/fonts/Readme.md
@@ -10,7 +10,7 @@
* Bpp : 1 bit-per-pixel
* Do not enable font compression and horizontal subpixel hinting
* Load the file `JetBrainsMono-Bold.tff` and specify the following range : `0x20-0x7f, 0x410-0x44f`
- * Add a 2nd font, load the file `FontAwesome5-Solid+Brands+Regular.woff` and specify the following range : `0xf293, 0xf294, 0xf244, 0xf240, 0xf242, 0xf243, 0xf241, 0xf54b, 0xf21e, 0xf1e6, 0xf54b, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf069, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf029, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd`
+ * Add a 2nd font, load the file `FontAwesome5-Solid+Brands+Regular.woff` and specify the following range : `0xf293, 0xf294, 0xf244, 0xf240, 0xf242, 0xf243, 0xf241, 0xf54b, 0xf21e, 0xf1e6, 0xf54b, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf069, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf029, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024`
* Click on Convert, and download the file `jetbrains_mono_bold_20.c` and copy it in `src/DisplayApp/Fonts`
Add new symbols:
diff --git a/src/displayapp/fonts/jetbrains_mono_bold_20.c b/src/displayapp/fonts/jetbrains_mono_bold_20.c
index dc30104a..f4050db8 100644
--- a/src/displayapp/fonts/jetbrains_mono_bold_20.c
+++ b/src/displayapp/fonts/jetbrains_mono_bold_20.c
@@ -466,8 +466,8 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t gylph_bitmap[] = {
0x66, 0x66, 0x66, 0x6c, 0x63,
/* U+417 "З" */
- 0x1f, 0x8f, 0xfd, 0xc7, 0x80, 0x70, 0x1c, 0x3e,
- 0x7, 0xf0, 0xf, 0x0, 0xe0, 0x1d, 0x83, 0xb8,
+ 0x1f, 0xf, 0xf3, 0xc7, 0x0, 0x60, 0x1c, 0x1e,
+ 0x3, 0xf0, 0xe, 0x0, 0xe0, 0x1f, 0x83, 0xf8,
0xf7, 0xfc, 0x3e, 0x0,
/* U+418 "И" */
@@ -615,7 +615,7 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t gylph_bitmap[] = {
0x70,
/* U+437 "з" */
- 0x3f, 0x1f, 0xfe, 0x1c, 0x7, 0x1f, 0x7, 0xe0,
+ 0x3f, 0x1f, 0xfe, 0x1c, 0x7, 0x1f, 0x87, 0xe0,
0x1c, 0x7, 0xe1, 0xdf, 0xe3, 0xf0,
/* U+438 "и" */
@@ -738,6 +738,15 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t gylph_bitmap[] = {
0xcf, 0x9f, 0xff, 0xf1, 0xff, 0xfc, 0x1f, 0xff,
0x1, 0xff, 0xc0, 0x1f, 0xf0, 0x0, 0x70, 0x0,
+ /* U+F024 "" */
+ 0x70, 0x0, 0xf, 0x80, 0x0, 0xf8, 0x0, 0xf,
+ 0xff, 0xf, 0x7f, 0xff, 0xf7, 0xff, 0xff, 0x7f,
+ 0xff, 0xf7, 0xff, 0xff, 0x7f, 0xff, 0xf7, 0xff,
+ 0xff, 0x7f, 0xff, 0xf7, 0xff, 0xff, 0x7f, 0xff,
+ 0xf7, 0xff, 0xff, 0x7f, 0x7f, 0xe7, 0x0, 0x78,
+ 0x70, 0x0, 0x7, 0x0, 0x0, 0x70, 0x0, 0x7,
+ 0x0, 0x0,
+
/* U+F027 "" */
0x0, 0xc0, 0x3, 0x80, 0xf, 0x0, 0x3e, 0xf,
0xfc, 0x9f, 0xf9, 0xbf, 0xf1, 0xff, 0xe3, 0xff,
@@ -789,6 +798,13 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t gylph_bitmap[] = {
0xff, 0xfc, 0xff, 0xff, 0x3f, 0xff, 0xcf, 0xff,
0xf3, 0xff, 0xfc, 0xff, 0x7e, 0x1f, 0x80,
+ /* U+F04D "" */
+ 0x7f, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0x80,
+
/* U+F051 "" */
0xe0, 0x3f, 0x81, 0xfe, 0xf, 0xf8, 0x7f, 0xe3,
0xff, 0x9f, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff,
@@ -897,6 +913,13 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t gylph_bitmap[] = {
0x81, 0xf8, 0x6d, 0x99, 0x9a, 0x36, 0x7, 0x80,
0xe0, 0x18, 0x2, 0x0, 0x0,
+ /* U+F2F2 "" */
+ 0x7, 0xe0, 0x7, 0xe0, 0x1, 0x80, 0x3, 0xc0,
+ 0xf, 0xf2, 0x1f, 0xff, 0x3e, 0x7e, 0x7e, 0x7e,
+ 0xfe, 0x7e, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f,
+ 0xfe, 0x7f, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xfe,
+ 0x3f, 0xfc, 0x1f, 0xf8, 0x7, 0xe0,
+
/* U+F3DD "" */
0x40, 0x0, 0x40, 0x70, 0x0, 0x7e, 0x3c, 0x0,
0x3f, 0x8f, 0x80, 0x1f, 0x81, 0xe0, 0x1f, 0xc0,
@@ -1080,7 +1103,7 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 1514, .adv_w = 192, .box_w = 11, .box_h = 17, .ofs_x = 1, .ofs_y = -3},
{.bitmap_index = 1538, .adv_w = 192, .box_w = 9, .box_h = 14, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 1554, .adv_w = 192, .box_w = 12, .box_h = 14, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 1575, .adv_w = 192, .box_w = 11, .box_h = 14, .ofs_x = 0, .ofs_y = 0},
+ {.bitmap_index = 1575, .adv_w = 192, .box_w = 11, .box_h = 14, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1595, .adv_w = 192, .box_w = 9, .box_h = 14, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1611, .adv_w = 192, .box_w = 9, .box_h = 19, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1633, .adv_w = 192, .box_w = 10, .box_h = 14, .ofs_x = 2, .ofs_y = 0},
@@ -1139,36 +1162,39 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 2498, .adv_w = 192, .box_w = 9, .box_h = 11, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2511, .adv_w = 320, .box_w = 20, .box_h = 20, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 2561, .adv_w = 320, .box_w = 19, .box_h = 20, .ofs_x = 0, .ofs_y = -3},
- {.bitmap_index = 2609, .adv_w = 240, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
- {.bitmap_index = 2638, .adv_w = 360, .box_w = 23, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
- {.bitmap_index = 2693, .adv_w = 280, .box_w = 18, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 2732, .adv_w = 320, .box_w = 20, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 2775, .adv_w = 280, .box_w = 13, .box_h = 17, .ofs_x = 2, .ofs_y = -1},
- {.bitmap_index = 2803, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
- {.bitmap_index = 2851, .adv_w = 280, .box_w = 18, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 2890, .adv_w = 280, .box_w = 13, .box_h = 17, .ofs_x = 2, .ofs_y = -1},
- {.bitmap_index = 2918, .adv_w = 320, .box_w = 19, .box_h = 20, .ofs_x = 0, .ofs_y = -3},
- {.bitmap_index = 2966, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
- {.bitmap_index = 3019, .adv_w = 120, .box_w = 8, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
- {.bitmap_index = 3038, .adv_w = 320, .box_w = 20, .box_h = 20, .ofs_x = 0, .ofs_y = -3},
- {.bitmap_index = 3088, .adv_w = 240, .box_w = 15, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
- {.bitmap_index = 3124, .adv_w = 320, .box_w = 20, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
- {.bitmap_index = 3172, .adv_w = 320, .box_w = 20, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 3215, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
- {.bitmap_index = 3253, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
- {.bitmap_index = 3291, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
- {.bitmap_index = 3329, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
- {.bitmap_index = 3367, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
- {.bitmap_index = 3405, .adv_w = 280, .box_w = 15, .box_h = 20, .ofs_x = 1, .ofs_y = -3},
- {.bitmap_index = 3443, .adv_w = 200, .box_w = 11, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
- {.bitmap_index = 3472, .adv_w = 400, .box_w = 25, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
- {.bitmap_index = 3538, .adv_w = 360, .box_w = 23, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
- {.bitmap_index = 3587, .adv_w = 320, .box_w = 20, .box_h = 20, .ofs_x = 0, .ofs_y = -2},
- {.bitmap_index = 3637, .adv_w = 400, .box_w = 25, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
- {.bitmap_index = 3697, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
- {.bitmap_index = 3750, .adv_w = 360, .box_w = 22, .box_h = 20, .ofs_x = 0, .ofs_y = -2},
- {.bitmap_index = 3805, .adv_w = 360, .box_w = 22, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
- {.bitmap_index = 3858, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0}
+ {.bitmap_index = 2609, .adv_w = 320, .box_w = 20, .box_h = 20, .ofs_x = 0, .ofs_y = -3},
+ {.bitmap_index = 2659, .adv_w = 240, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
+ {.bitmap_index = 2688, .adv_w = 360, .box_w = 23, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
+ {.bitmap_index = 2743, .adv_w = 280, .box_w = 18, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
+ {.bitmap_index = 2782, .adv_w = 320, .box_w = 20, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
+ {.bitmap_index = 2825, .adv_w = 280, .box_w = 13, .box_h = 17, .ofs_x = 2, .ofs_y = -1},
+ {.bitmap_index = 2853, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
+ {.bitmap_index = 2901, .adv_w = 280, .box_w = 18, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
+ {.bitmap_index = 2940, .adv_w = 280, .box_w = 18, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
+ {.bitmap_index = 2979, .adv_w = 280, .box_w = 13, .box_h = 17, .ofs_x = 2, .ofs_y = -1},
+ {.bitmap_index = 3007, .adv_w = 320, .box_w = 19, .box_h = 20, .ofs_x = 0, .ofs_y = -3},
+ {.bitmap_index = 3055, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
+ {.bitmap_index = 3108, .adv_w = 120, .box_w = 8, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
+ {.bitmap_index = 3127, .adv_w = 320, .box_w = 20, .box_h = 20, .ofs_x = 0, .ofs_y = -3},
+ {.bitmap_index = 3177, .adv_w = 240, .box_w = 15, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
+ {.bitmap_index = 3213, .adv_w = 320, .box_w = 20, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
+ {.bitmap_index = 3261, .adv_w = 320, .box_w = 20, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
+ {.bitmap_index = 3304, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
+ {.bitmap_index = 3342, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
+ {.bitmap_index = 3380, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
+ {.bitmap_index = 3418, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
+ {.bitmap_index = 3456, .adv_w = 400, .box_w = 25, .box_h = 12, .ofs_x = 0, .ofs_y = 1},
+ {.bitmap_index = 3494, .adv_w = 280, .box_w = 15, .box_h = 20, .ofs_x = 1, .ofs_y = -3},
+ {.bitmap_index = 3532, .adv_w = 200, .box_w = 11, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
+ {.bitmap_index = 3561, .adv_w = 280, .box_w = 16, .box_h = 19, .ofs_x = 1, .ofs_y = -2},
+ {.bitmap_index = 3599, .adv_w = 400, .box_w = 25, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
+ {.bitmap_index = 3665, .adv_w = 360, .box_w = 23, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
+ {.bitmap_index = 3714, .adv_w = 320, .box_w = 20, .box_h = 20, .ofs_x = 0, .ofs_y = -2},
+ {.bitmap_index = 3764, .adv_w = 400, .box_w = 25, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
+ {.bitmap_index = 3824, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
+ {.bitmap_index = 3877, .adv_w = 360, .box_w = 22, .box_h = 20, .ofs_x = 0, .ofs_y = -2},
+ {.bitmap_index = 3932, .adv_w = 360, .box_w = 22, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
+ {.bitmap_index = 3985, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0}
};
/*---------------------
@@ -1176,10 +1202,11 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
*--------------------*/
static const uint16_t unicode_list_2[] = {
- 0x0, 0x16, 0x26, 0x27, 0x28, 0x39, 0x47, 0x4a,
- 0x4b, 0x50, 0x68, 0x94, 0x128, 0x184, 0x1e5, 0x1fb,
- 0x21d, 0x23f, 0x240, 0x241, 0x242, 0x243, 0x292, 0x293,
- 0x3dc, 0x3fc, 0x45c, 0x54a, 0x55f, 0x59e, 0x59f, 0x6a8
+ 0x0, 0x16, 0x23, 0x26, 0x27, 0x28, 0x39, 0x47,
+ 0x4a, 0x4b, 0x4c, 0x50, 0x68, 0x94, 0x128, 0x184,
+ 0x1e5, 0x1fb, 0x21d, 0x23f, 0x240, 0x241, 0x242, 0x243,
+ 0x292, 0x293, 0x2f1, 0x3dc, 0x3fc, 0x45c, 0x54a, 0x55f,
+ 0x59e, 0x59f, 0x6a8
};
/*Collect the unicode lists and glyph_id offsets*/
@@ -1195,7 +1222,7 @@ static const lv_font_fmt_txt_cmap_t cmaps[] =
},
{
.range_start = 61441, .range_length = 1705, .glyph_id_start = 160,
- .unicode_list = unicode_list_2, .glyph_id_ofs_list = NULL, .list_length = 32, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
+ .unicode_list = unicode_list_2, .glyph_id_ofs_list = NULL, .list_length = 35, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
}
};
diff --git a/src/displayapp/screens/ApplicationList.cpp b/src/displayapp/screens/ApplicationList.cpp
index 0f3286df..60039045 100644
--- a/src/displayapp/screens/ApplicationList.cpp
+++ b/src/displayapp/screens/ApplicationList.cpp
@@ -56,21 +56,21 @@ std::unique_ptr<Screen> ApplicationList::CreateScreen1() {
};
- return std::unique_ptr<Screen>(new Screens::Tile(0, app, settingsController, applications));
+ return std::make_unique<Screens::Tile>(0, app, settingsController, applications);
}
std::unique_ptr<Screen> ApplicationList::CreateScreen2() {
std::array<Screens::Tile::Applications, 6> applications {
{{Symbols::map, Apps::Navigation},
- {Symbols::asterisk, Apps::Meter},
+ {Symbols::stopWatch, Apps::StopWatch},
{Symbols::paintbrush, Apps::Paint},
- {Symbols::info, Apps::Notifications},
- {Symbols::paddle, Apps::Paddle},
- {"2", Apps::Twos}
+ {Symbols::info, Apps::Notifications},
+ {Symbols::paddle, Apps::Paddle},
+ {"2", Apps::Twos}
}
};
- return std::unique_ptr<Screen>(new Screens::Tile(1, app, settingsController, applications));
+ return std::make_unique<Screens::Tile>(1, app, settingsController, applications);
}
std::unique_ptr<Screen> ApplicationList::CreateScreen3() {
@@ -84,6 +84,6 @@ std::unique_ptr<Screen> ApplicationList::CreateScreen3() {
}
};
- return std::unique_ptr<Screen>(new Screens::Tile(2, app, settingsController, applications));
+ return std::make_unique<Screens::Tile>(2, app, settingsController, applications);
}
diff --git a/src/displayapp/screens/Clock.cpp b/src/displayapp/screens/Clock.cpp
index 342dd222..69180370 100644
--- a/src/displayapp/screens/Clock.cpp
+++ b/src/displayapp/screens/Clock.cpp
@@ -64,20 +64,20 @@ bool Clock::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
}
std::unique_ptr<Screen> Clock::WatchFaceDigitalScreen() {
- return std::unique_ptr<Screen>(new Screens::WatchFaceDigital(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController, heartRateController));
+ return std::make_unique<Screens::WatchFaceDigital>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController, heartRateController);
}
std::unique_ptr<Screen> Clock::WatchFaceAnalogScreen() {
- return std::unique_ptr<Screen>(new Screens::WatchFaceAnalog(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController));
+ return std::make_unique<Screens::WatchFaceAnalog>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController);
}
/*
// Examples for more watch faces
std::unique_ptr<Screen> Clock::WatchFaceMinimalScreen() {
- return std::unique_ptr<Screen>(new Screens::WatchFaceMinimal(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController));
+ return std::make_unique<Screens::WatchFaceMinimal>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController);
}
std::unique_ptr<Screen> Clock::WatchFaceCustomScreen() {
- return std::unique_ptr<Screen>(new Screens::WatchFaceCustom(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController));
+ return std::make_unique<Screens::WatchFaceCustom>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController);
}
*/ \ No newline at end of file
diff --git a/src/displayapp/screens/Notifications.cpp b/src/displayapp/screens/Notifications.cpp
index 4219bac7..c903ed0f 100644
--- a/src/displayapp/screens/Notifications.cpp
+++ b/src/displayapp/screens/Notifications.cpp
@@ -17,22 +17,22 @@ Notifications::Notifications(DisplayApp *app,
auto notification = notificationManager.GetLastNotification();
if(notification.valid) {
currentId = notification.id;
- currentItem.reset(new NotificationItem("\nNotification",
+ currentItem = std::make_unique<NotificationItem>("\nNotification",
notification.message.data(),
notification.index,
notification.category,
notificationManager.NbNotifications(),
mode,
- alertNotificationService));
+ alertNotificationService);
validDisplay = true;
} else {
- currentItem.reset(new NotificationItem("\nNotification",
+ currentItem = std::make_unique<NotificationItem>("\nNotification",
"No notification to display",
0,
notification.category,
notificationManager.NbNotifications(),
Modes::Preview,
- alertNotificationService));
+ alertNotificationService);
}
if(mode == Modes::Preview) {
@@ -87,13 +87,13 @@ bool Notifications::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
currentId = previousNotification.id;
currentItem.reset(nullptr);
app->SetFullRefresh(DisplayApp::FullRefreshDirections::Up);
- currentItem.reset(new NotificationItem("\nNotification",
+ currentItem = std::make_unique<NotificationItem>("\nNotification",
previousNotification.message.data(),
previousNotification.index,
previousNotification.category,
notificationManager.NbNotifications(),
mode,
- alertNotificationService));
+ alertNotificationService);
}
return true;
case Pinetime::Applications::TouchEvents::SwipeDown: {
@@ -109,13 +109,13 @@ bool Notifications::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
currentId = nextNotification.id;
currentItem.reset(nullptr);
app->SetFullRefresh(DisplayApp::FullRefreshDirections::Down);
- currentItem.reset(new NotificationItem("\nNotification",
+ currentItem = std::make_unique<NotificationItem>("\nNotification",
nextNotification.message.data(),
nextNotification.index,
nextNotification.category,
notificationManager.NbNotifications(),
mode,
- alertNotificationService));
+ alertNotificationService);
}
return true;
case Pinetime::Applications::TouchEvents::LongTap: {
diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp
new file mode 100644
index 00000000..63f18d4b
--- /dev/null
+++ b/src/displayapp/screens/StopWatch.cpp
@@ -0,0 +1,210 @@
+#include "StopWatch.h"
+
+#include "Screen.h"
+#include "Symbols.h"
+#include "lvgl/lvgl.h"
+#include "projdefs.h"
+#include "FreeRTOSConfig.h"
+#include "task.h"
+
+#include <tuple>
+
+using namespace Pinetime::Applications::Screens;
+
+// Anonymous namespace for local functions
+namespace {
+ TimeSeparated_t convertTicksToTimeSegments(const TickType_t timeElapsed) {
+ const int timeElapsedMillis = (static_cast<float>(timeElapsed) / static_cast<float>(configTICK_RATE_HZ)) * 1000;
+
+ const int milliSecs = (timeElapsedMillis % 1000) / 10; // Get only the first two digits and ignore the last
+ const int secs = (timeElapsedMillis / 1000) % 60;
+ const int mins = (timeElapsedMillis / 1000) / 60;
+ return TimeSeparated_t {mins, secs, milliSecs};
+ }
+
+ TickType_t calculateDelta(const TickType_t startTime, const TickType_t currentTime) {
+ TickType_t delta = 0;
+ // Take care of overflow
+ if (startTime > currentTime) {
+ delta = 0xffffffff - startTime;
+ delta += (currentTime + 1);
+ } else {
+ delta = currentTime - startTime;
+ }
+ return delta;
+ }
+}
+
+static void play_pause_event_handler(lv_obj_t* obj, lv_event_t event) {
+ auto stopWatch = static_cast<StopWatch*>(obj->user_data);
+ stopWatch->playPauseBtnEventHandler(event);
+}
+
+static void stop_lap_event_handler(lv_obj_t* obj, lv_event_t event) {
+ auto stopWatch = static_cast<StopWatch*>(obj->user_data);
+ stopWatch->stopLapBtnEventHandler(event);
+}
+
+StopWatch::StopWatch(DisplayApp* app)
+ : Screen(app), running {true}, currentState {States::Init}, currentEvent {Events::Stop}, startTime {}, oldTimeElapsed {},
+ currentTimeSeparated {}, lapBuffer {}, lapNr {}, lapPressed {false} {
+
+ time = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_extrabold_compressed);
+ lv_obj_align(time, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, -45);
+ lv_label_set_text(time, "00:00");
+
+ msecTime = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_font(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20);
+ lv_obj_align(msecTime, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 108, 3);
+ lv_label_set_text(msecTime, "00");
+
+ btnPlayPause = lv_btn_create(lv_scr_act(), nullptr);
+ btnPlayPause->user_data = this;
+ lv_obj_set_event_cb(btnPlayPause, play_pause_event_handler);
+ lv_obj_align(btnPlayPause, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, -10);
+ lv_obj_set_height(btnPlayPause, 40);
+ txtPlayPause = lv_label_create(btnPlayPause, nullptr);
+ lv_label_set_text(txtPlayPause, Symbols::play);
+
+ lapOneText = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_font(lapOneText, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20);
+ lv_obj_align(lapOneText, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 50, 30);
+ lv_label_set_text(lapOneText, "");
+
+ lapTwoText = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_font(lapTwoText, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20);
+ lv_obj_align(lapTwoText, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 50, 55);
+ lv_label_set_text(lapTwoText, "");
+
+ // We don't want this button in the init state
+ btnStopLap = nullptr;
+}
+
+StopWatch::~StopWatch() {
+ lv_obj_clean(lv_scr_act());
+}
+
+bool StopWatch::Refresh() {
+ // @startuml CHIP8_state
+ // State "Init" as init
+ // State "Running" as run
+ // State "Halted" as halt
+
+ // [*] --> init
+ // init -> run : press play
+ // run -> run : press lap
+ // run --> halt : press pause
+ // halt --> run : press play
+ // halt --> init : press stop
+ // @enduml
+ // Copy paste the above plantuml text to visualize the state diagram
+ switch (currentState) {
+ // Init state when an user first opens the app
+ // and when a stop/reset button is pressed
+ case States::Init: {
+ if (btnStopLap) {
+ lv_obj_del(btnStopLap);
+ }
+ // The initial default value
+ lv_label_set_text(time, "00:00");
+ lv_label_set_text(msecTime, "00");
+
+ lv_label_set_text(lapOneText, "");
+ lv_label_set_text(lapTwoText, "");
+ lapBuffer.clearBuffer();
+ lapNr = 0;
+
+ if (currentEvent == Events::Play) {
+ btnStopLap = lv_btn_create(lv_scr_act(), nullptr);
+ btnStopLap->user_data = this;
+ lv_obj_set_event_cb(btnStopLap, stop_lap_event_handler);
+ lv_obj_align(btnStopLap, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, 0);
+ lv_obj_set_height(btnStopLap, 40);
+ txtStopLap = lv_label_create(btnStopLap, nullptr);
+ lv_label_set_text(txtStopLap, Symbols::lapsFlag);
+
+ startTime = xTaskGetTickCount();
+ currentState = States::Running;
+ }
+ break;
+ }
+ case States::Running: {
+ lv_label_set_text(txtPlayPause, Symbols::pause);
+ lv_label_set_text(txtStopLap, Symbols::lapsFlag);
+
+ const auto timeElapsed = calculateDelta(startTime, xTaskGetTickCount());
+ currentTimeSeparated = convertTicksToTimeSegments((oldTimeElapsed + timeElapsed));
+
+ lv_label_set_text_fmt(time, "%02d:%02d", currentTimeSeparated.mins, currentTimeSeparated.secs);
+ lv_label_set_text_fmt(msecTime, "%02d", currentTimeSeparated.msecs);
+
+ if (lapPressed == true) {
+ if (lapBuffer[1]) {
+ lv_label_set_text_fmt(lapOneText, "#%d %d:%d:%d", (lapNr - 1), lapBuffer[1]->mins, lapBuffer[1]->secs, lapBuffer[1]->msecs);
+ }
+ if (lapBuffer[0]) {
+ lv_label_set_text_fmt(lapTwoText, "#%d %d:%d:%d", lapNr, lapBuffer[0]->mins, lapBuffer[0]->secs, lapBuffer[0]->msecs);
+ }
+ // Reset the bool to avoid setting the text in each cycle until there is a change
+ lapPressed = false;
+ }
+
+ if (currentEvent == Events::Pause) {
+ // Reset the start time
+ startTime = 0;
+ // Store the current time elapsed in cache
+ oldTimeElapsed += timeElapsed;
+ currentState = States::Halted;
+ }
+ break;
+ }
+ case States::Halted: {
+ lv_label_set_text(txtPlayPause, Symbols::play);
+ lv_label_set_text(txtStopLap, Symbols::stop);
+
+ if (currentEvent == Events::Play) {
+ startTime = xTaskGetTickCount();
+ currentState = States::Running;
+ }
+ if (currentEvent == Events::Stop) {
+ currentState = States::Init;
+ oldTimeElapsed = 0;
+ }
+ break;
+ }
+ }
+ return running;
+}
+
+bool StopWatch::OnButtonPushed() {
+ running = false;
+ return true;
+}
+
+void StopWatch::playPauseBtnEventHandler(lv_event_t event) {
+ if (event == LV_EVENT_CLICKED) {
+ if (currentState == States::Init) {
+ currentEvent = Events::Play;
+ } else {
+ // Simple Toggle for play/pause
+ currentEvent = (currentEvent == Events::Play ? Events::Pause : Events::Play);
+ }
+ }
+}
+
+void StopWatch::stopLapBtnEventHandler(lv_event_t event) {
+ if (event == LV_EVENT_CLICKED) {
+ // If running, then this button is used to save laps
+ if (currentState == States::Running) {
+ lapBuffer.addLaps(currentTimeSeparated);
+ lapNr++;
+ lapPressed = true;
+
+ } else if (currentState == States::Halted) {
+ currentEvent = Events::Stop;
+ } else {
+ // Not possible to reach here. Do nothing.
+ }
+ }
+}
diff --git a/src/displayapp/screens/StopWatch.h b/src/displayapp/screens/StopWatch.h
new file mode 100644
index 00000000..f9dd5c76
--- /dev/null
+++ b/src/displayapp/screens/StopWatch.h
@@ -0,0 +1,86 @@
+#pragma once
+
+#include "Screen.h"
+#include "components/datetime/DateTimeController.h"
+#include "../LittleVgl.h"
+
+#include "FreeRTOS.h"
+#include "portmacro_cmsis.h"
+
+#include <array>
+
+namespace Pinetime::Applications::Screens {
+
+ enum class States { Init, Running, Halted };
+
+ enum class Events { Play, Pause, Stop };
+
+ struct TimeSeparated_t {
+ int mins;
+ int secs;
+ int msecs;
+ };
+
+ // A simple buffer to hold the latest two laps
+ template <int N> struct LapTextBuffer_t {
+ LapTextBuffer_t() : buffer {}, currentSize {}, capacity {N}, head {-1} {
+ }
+
+ void addLaps(const TimeSeparated_t& timeVal) {
+ head++;
+ head %= capacity;
+ buffer[head] = timeVal;
+
+ if (currentSize < capacity) {
+ currentSize++;
+ }
+ }
+
+ void clearBuffer() {
+ buffer = {};
+ currentSize = 0;
+ head = -1;
+ }
+
+ TimeSeparated_t* operator[](std::size_t idx) {
+ // Sanity check for out-of-bounds
+ if (idx >= 0 && idx < capacity) {
+ if (idx < currentSize) {
+ // This transformation is to ensure that head is always pointing to index 0.
+ const auto transformed_idx = (head - idx) % capacity;
+ return (&buffer[transformed_idx]);
+ }
+ }
+ return nullptr;
+ }
+
+ private:
+ std::array<TimeSeparated_t, N> buffer;
+ uint8_t currentSize;
+ uint8_t capacity;
+ int8_t head;
+ };
+
+ class StopWatch : public Screen {
+ public:
+ StopWatch(DisplayApp* app);
+ ~StopWatch() override;
+ bool Refresh() override;
+ bool OnButtonPushed() override;
+ void playPauseBtnEventHandler(lv_event_t event);
+ void stopLapBtnEventHandler(lv_event_t event);
+
+ private:
+ bool running;
+ States currentState;
+ Events currentEvent;
+ TickType_t startTime;
+ TickType_t oldTimeElapsed;
+ TimeSeparated_t currentTimeSeparated; // Holds Mins, Secs, millisecs
+ LapTextBuffer_t<2> lapBuffer;
+ int lapNr;
+ bool lapPressed;
+ lv_obj_t *time, *msecTime, *btnPlayPause, *btnStopLap, *txtPlayPause, *txtStopLap;
+ lv_obj_t *lapOneText, *lapTwoText;
+ };
+}
diff --git a/src/displayapp/screens/Symbols.h b/src/displayapp/screens/Symbols.h
index 1a6bbd7f..9a13a755 100644
--- a/src/displayapp/screens/Symbols.h
+++ b/src/displayapp/screens/Symbols.h
@@ -36,6 +36,9 @@ namespace Pinetime {
static constexpr const char* stepBackward = "\xEF\x81\x88";
static constexpr const char* play = "\xEF\x81\x8B";
static constexpr const char* pause = "\xEF\x81\x8C";
+ static constexpr const char* stop = "\xEF\x81\x8D";
+ static constexpr const char* stopWatch = "\xEF\x8B\xB2";
+ static constexpr const char* lapsFlag = "\xEF\x80\xA4";
}
}
}
diff --git a/src/displayapp/screens/SystemInfo.cpp b/src/displayapp/screens/SystemInfo.cpp
index 0d6f8e5a..949fd345 100644
--- a/src/displayapp/screens/SystemInfo.cpp
+++ b/src/displayapp/screens/SystemInfo.cpp
@@ -104,14 +104,14 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen1() {
uptimeDays, uptimeHours, uptimeMinutes, uptimeSeconds,
batteryPercent, brightness, resetReason);
- return std::unique_ptr<Screen>(new Screens::Label(app, t1));
+ return std::make_unique<Screens::Label>(app, t1);
}
std::unique_ptr<Screen> SystemInfo::CreateScreen2() {
auto& bleAddr = bleController.Address();
sprintf(t2, "BLE MAC: \n %02x:%02x:%02x:%02x:%02x:%02x",
bleAddr[5], bleAddr[4], bleAddr[3], bleAddr[2], bleAddr[1], bleAddr[0]);
- return std::unique_ptr<Screen>(new Screens::Label(app, t2));
+ return std::make_unique<Screens::Label>(app, t2);
}
std::unique_ptr<Screen> SystemInfo::CreateScreen3() {
@@ -123,5 +123,5 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen3() {
"Source code:\n"
"https://github.com/\n"
" JF002/InfiniTime");
- return std::unique_ptr<Screen>(new Screens::Label(app, t3));
+ return std::make_unique<Screens::Label>(app, t3);
}
diff --git a/src/displayapp/screens/WatchFaceAnalog.cpp b/src/displayapp/screens/WatchFaceAnalog.cpp
index b51d48c7..66af584a 100644
--- a/src/displayapp/screens/WatchFaceAnalog.cpp
+++ b/src/displayapp/screens/WatchFaceAnalog.cpp
@@ -5,6 +5,8 @@
#include "Symbols.h"
#include "NotificationIcon.h"
+#include <cmath>
+
LV_IMG_DECLARE(bg_clock);
using namespace Pinetime::Applications::Screens;