aboutsummaryrefslogtreecommitdiffstats
path: root/src/displayapp/screens
diff options
context:
space:
mode:
Diffstat (limited to 'src/displayapp/screens')
-rw-r--r--src/displayapp/screens/Alarm.cpp48
-rw-r--r--src/displayapp/screens/Alarm.h33
-rw-r--r--src/displayapp/screens/ApplicationList.cpp29
-rw-r--r--src/displayapp/screens/ApplicationList.h42
-rw-r--r--src/displayapp/screens/BatteryInfo.cpp43
-rw-r--r--src/displayapp/screens/BatteryInfo.h2
-rw-r--r--src/displayapp/screens/Calculator.cpp375
-rw-r--r--src/displayapp/screens/Calculator.h83
-rw-r--r--src/displayapp/screens/CheckboxList.h2
-rw-r--r--src/displayapp/screens/Clock.cpp129
-rw-r--r--src/displayapp/screens/Clock.h57
-rw-r--r--src/displayapp/screens/Dice.cpp199
-rw-r--r--src/displayapp/screens/Dice.h61
-rw-r--r--src/displayapp/screens/FirmwareUpdate.cpp6
-rw-r--r--src/displayapp/screens/FirmwareValidation.cpp4
-rw-r--r--src/displayapp/screens/FlashLight.cpp7
-rw-r--r--src/displayapp/screens/FlashLight.h4
-rw-r--r--src/displayapp/screens/HeartRate.cpp19
-rw-r--r--src/displayapp/screens/HeartRate.h14
-rw-r--r--src/displayapp/screens/InfiniPaint.h13
-rw-r--r--src/displayapp/screens/List.h2
-rw-r--r--src/displayapp/screens/Metronome.cpp7
-rw-r--r--src/displayapp/screens/Metronome.h14
-rw-r--r--src/displayapp/screens/Motion.cpp8
-rw-r--r--src/displayapp/screens/Motion.h12
-rw-r--r--src/displayapp/screens/Music.h13
-rw-r--r--src/displayapp/screens/Navigation.cpp289
-rw-r--r--src/displayapp/screens/Navigation.h19
-rw-r--r--src/displayapp/screens/Notifications.cpp20
-rw-r--r--src/displayapp/screens/Notifications.h3
-rw-r--r--src/displayapp/screens/Paddle.h13
-rw-r--r--src/displayapp/screens/Screen.h35
-rw-r--r--src/displayapp/screens/Steps.h13
-rw-r--r--src/displayapp/screens/StopWatch.cpp37
-rw-r--r--src/displayapp/screens/StopWatch.h90
-rw-r--r--src/displayapp/screens/Symbols.h16
-rw-r--r--src/displayapp/screens/SystemInfo.cpp70
-rw-r--r--src/displayapp/screens/SystemInfo.h5
-rw-r--r--src/displayapp/screens/Tile.cpp9
-rw-r--r--src/displayapp/screens/Tile.h4
-rw-r--r--src/displayapp/screens/Timer.cpp37
-rw-r--r--src/displayapp/screens/Timer.h73
-rw-r--r--src/displayapp/screens/Twos.cpp2
-rw-r--r--src/displayapp/screens/Twos.h13
-rw-r--r--src/displayapp/screens/WatchFaceAnalog.cpp42
-rw-r--r--src/displayapp/screens/WatchFaceAnalog.h41
-rw-r--r--src/displayapp/screens/WatchFaceCasioStyleG7710.cpp79
-rw-r--r--src/displayapp/screens/WatchFaceCasioStyleG7710.h54
-rw-r--r--src/displayapp/screens/WatchFaceDigital.cpp100
-rw-r--r--src/displayapp/screens/WatchFaceDigital.h54
-rw-r--r--src/displayapp/screens/WatchFaceInfineat.cpp73
-rw-r--r--src/displayapp/screens/WatchFaceInfineat.h46
-rw-r--r--src/displayapp/screens/WatchFacePineTimeStyle.cpp147
-rw-r--r--src/displayapp/screens/WatchFacePineTimeStyle.h49
-rw-r--r--src/displayapp/screens/WatchFaceTerminal.cpp57
-rw-r--r--src/displayapp/screens/WatchFaceTerminal.h51
-rw-r--r--src/displayapp/screens/Weather.cpp376
-rw-r--r--src/displayapp/screens/Weather.h59
-rw-r--r--src/displayapp/screens/WeatherSymbols.cpp61
-rw-r--r--src/displayapp/screens/WeatherSymbols.h14
-rw-r--r--src/displayapp/screens/settings/QuickSettings.cpp5
-rw-r--r--src/displayapp/screens/settings/QuickSettings.h3
-rw-r--r--src/displayapp/screens/settings/SettingBluetooth.cpp6
-rw-r--r--src/displayapp/screens/settings/SettingBluetooth.h1
-rw-r--r--src/displayapp/screens/settings/SettingDisplay.cpp31
-rw-r--r--src/displayapp/screens/settings/SettingDisplay.h5
-rw-r--r--src/displayapp/screens/settings/SettingSetDateTime.cpp3
-rw-r--r--src/displayapp/screens/settings/SettingSetDateTime.h1
-rw-r--r--src/displayapp/screens/settings/SettingWakeUp.cpp6
-rw-r--r--src/displayapp/screens/settings/SettingWakeUp.h3
-rw-r--r--src/displayapp/screens/settings/SettingWatchFace.cpp47
-rw-r--r--src/displayapp/screens/settings/SettingWatchFace.h30
-rw-r--r--src/displayapp/screens/settings/SettingWeatherFormat.cpp63
-rw-r--r--src/displayapp/screens/settings/SettingWeatherFormat.h26
-rw-r--r--src/displayapp/screens/settings/Settings.cpp2
-rw-r--r--src/displayapp/screens/settings/Settings.h8
76 files changed, 2377 insertions, 1140 deletions
diff --git a/src/displayapp/screens/Alarm.cpp b/src/displayapp/screens/Alarm.cpp
index 4e6ce797..4cf43921 100644
--- a/src/displayapp/screens/Alarm.cpp
+++ b/src/displayapp/screens/Alarm.cpp
@@ -19,6 +19,10 @@
#include "displayapp/screens/Screen.h"
#include "displayapp/screens/Symbols.h"
#include "displayapp/InfiniTimeTheme.h"
+#include "components/settings/Settings.h"
+#include "components/alarm/AlarmController.h"
+#include "components/motor/MotorController.h"
+#include "systemtask/SystemTask.h"
using namespace Pinetime::Applications::Screens;
using Pinetime::Controllers::AlarmController;
@@ -44,7 +48,7 @@ Alarm::Alarm(Controllers::AlarmController& alarmController,
Controllers::Settings::ClockType clockType,
System::SystemTask& systemTask,
Controllers::MotorController& motorController)
- : alarmController {alarmController}, systemTask {systemTask}, motorController {motorController} {
+ : alarmController {alarmController}, wakeLock(systemTask), motorController {motorController} {
hourCounter.Create();
lv_obj_align(hourCounter.GetObject(), nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
@@ -73,7 +77,7 @@ Alarm::Alarm(Controllers::AlarmController& alarmController,
btnStop = lv_btn_create(lv_scr_act(), nullptr);
btnStop->user_data = this;
lv_obj_set_event_cb(btnStop, btnEventHandler);
- lv_obj_set_size(btnStop, 115, 50);
+ lv_obj_set_size(btnStop, 240, 70);
lv_obj_align(btnStop, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
lv_obj_set_style_local_bg_color(btnStop, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED);
txtStop = lv_label_create(btnStop, nullptr);
@@ -113,7 +117,7 @@ Alarm::Alarm(Controllers::AlarmController& alarmController,
UpdateAlarmTime();
- if (alarmController.State() == Controllers::AlarmController::AlarmState::Alerting) {
+ if (alarmController.IsAlerting()) {
SetAlerting();
} else {
SetSwitchState(LV_ANIM_OFF);
@@ -121,14 +125,15 @@ Alarm::Alarm(Controllers::AlarmController& alarmController,
}
Alarm::~Alarm() {
- if (alarmController.State() == AlarmController::AlarmState::Alerting) {
+ if (alarmController.IsAlerting()) {
StopAlerting();
}
lv_obj_clean(lv_scr_act());
+ alarmController.SaveAlarm();
}
void Alarm::DisableAlarm() {
- if (alarmController.State() == AlarmController::AlarmState::Set) {
+ if (alarmController.IsEnabled()) {
alarmController.DisableAlarm();
lv_switch_off(enableSwitch, LV_ANIM_ON);
}
@@ -168,7 +173,7 @@ bool Alarm::OnButtonPushed() {
HideInfo();
return true;
}
- if (alarmController.State() == AlarmController::AlarmState::Alerting) {
+ if (alarmController.IsAlerting()) {
StopAlerting();
return true;
}
@@ -177,7 +182,7 @@ bool Alarm::OnButtonPushed() {
bool Alarm::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
// Don't allow closing the screen by swiping while the alarm is alerting
- return alarmController.State() == AlarmController::AlarmState::Alerting && event == TouchEvents::SwipeDown;
+ return alarmController.IsAlerting() && event == TouchEvents::SwipeDown;
}
void Alarm::OnValueChanged() {
@@ -198,10 +203,14 @@ void Alarm::UpdateAlarmTime() {
void Alarm::SetAlerting() {
lv_obj_set_hidden(enableSwitch, true);
+ lv_obj_set_hidden(btnRecur, true);
+ lv_obj_set_hidden(btnInfo, true);
+ hourCounter.HideControls();
+ minuteCounter.HideControls();
lv_obj_set_hidden(btnStop, false);
taskStopAlarm = lv_task_create(StopAlarmTaskCallback, pdMS_TO_TICKS(60 * 1000), LV_TASK_PRIO_MID, this);
motorController.StartRinging();
- systemTask.PushMessage(System::Messages::DisableSleeping);
+ wakeLock.Lock();
}
void Alarm::StopAlerting() {
@@ -212,21 +221,20 @@ void Alarm::StopAlerting() {
lv_task_del(taskStopAlarm);
taskStopAlarm = nullptr;
}
- systemTask.PushMessage(System::Messages::EnableSleeping);
- lv_obj_set_hidden(enableSwitch, false);
+ wakeLock.Release();
lv_obj_set_hidden(btnStop, true);
+ hourCounter.ShowControls();
+ minuteCounter.ShowControls();
+ lv_obj_set_hidden(btnInfo, false);
+ lv_obj_set_hidden(btnRecur, false);
+ lv_obj_set_hidden(enableSwitch, false);
}
void Alarm::SetSwitchState(lv_anim_enable_t anim) {
- switch (alarmController.State()) {
- case AlarmController::AlarmState::Set:
- lv_switch_on(enableSwitch, anim);
- break;
- case AlarmController::AlarmState::Not_Set:
- lv_switch_off(enableSwitch, anim);
- break;
- default:
- break;
+ if (alarmController.IsEnabled()) {
+ lv_switch_on(enableSwitch, anim);
+ } else {
+ lv_switch_off(enableSwitch, anim);
}
}
@@ -243,7 +251,7 @@ void Alarm::ShowInfo() {
txtMessage = lv_label_create(btnMessage, nullptr);
lv_obj_set_style_local_bg_color(btnMessage, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_NAVY);
- if (alarmController.State() == AlarmController::AlarmState::Set) {
+ if (alarmController.IsEnabled()) {
auto timeToAlarm = alarmController.SecondsToAlarm();
auto daysToAlarm = timeToAlarm / 86400;
diff --git a/src/displayapp/screens/Alarm.h b/src/displayapp/screens/Alarm.h
index 91177366..a875b275 100644
--- a/src/displayapp/screens/Alarm.h
+++ b/src/displayapp/screens/Alarm.h
@@ -17,21 +17,23 @@
*/
#pragma once
+#include "displayapp/apps/Apps.h"
+#include "components/settings/Settings.h"
#include "displayapp/screens/Screen.h"
-#include "systemtask/SystemTask.h"
-#include "displayapp/LittleVgl.h"
-#include "components/alarm/AlarmController.h"
#include "displayapp/widgets/Counter.h"
+#include "displayapp/Controllers.h"
+#include "systemtask/WakeLock.h"
+#include "Symbols.h"
namespace Pinetime {
namespace Applications {
namespace Screens {
class Alarm : public Screen {
public:
- Alarm(Controllers::AlarmController& alarmController,
- Controllers::Settings::ClockType clockType,
- System::SystemTask& systemTask,
- Controllers::MotorController& motorController);
+ explicit Alarm(Controllers::AlarmController& alarmController,
+ Controllers::Settings::ClockType clockType,
+ System::SystemTask& systemTask,
+ Controllers::MotorController& motorController);
~Alarm() override;
void SetAlerting();
void OnButtonEvent(lv_obj_t* obj, lv_event_t event);
@@ -42,7 +44,7 @@ namespace Pinetime {
private:
Controllers::AlarmController& alarmController;
- System::SystemTask& systemTask;
+ System::WakeLock wakeLock;
Controllers::MotorController& motorController;
lv_obj_t *btnStop, *txtStop, *btnRecur, *txtRecur, *btnInfo, *enableSwitch;
@@ -63,6 +65,19 @@ namespace Pinetime {
Widgets::Counter hourCounter = Widgets::Counter(0, 23, jetbrains_mono_76);
Widgets::Counter minuteCounter = Widgets::Counter(0, 59, jetbrains_mono_76);
};
+ }
+
+ template <>
+ struct AppTraits<Apps::Alarm> {
+ static constexpr Apps app = Apps::Alarm;
+ static constexpr const char* icon = Screens::Symbols::bell;
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::Alarm(controllers.alarmController,
+ controllers.settingsController.GetClockType(),
+ *controllers.systemTask,
+ controllers.motorController);
+ };
};
- };
+ }
}
diff --git a/src/displayapp/screens/ApplicationList.cpp b/src/displayapp/screens/ApplicationList.cpp
index 0a65a5d4..fb46b413 100644
--- a/src/displayapp/screens/ApplicationList.cpp
+++ b/src/displayapp/screens/ApplicationList.cpp
@@ -1,13 +1,12 @@
#include "displayapp/screens/ApplicationList.h"
+#include "displayapp/screens/Tile.h"
#include <lvgl/lvgl.h>
#include <functional>
-#include "displayapp/Apps.h"
-#include "displayapp/DisplayApp.h"
+#include <algorithm>
+#include "components/settings/Settings.h"
using namespace Pinetime::Applications::Screens;
-constexpr std::array<Tile::Applications, ApplicationList::applications.size()> ApplicationList::applications;
-
auto ApplicationList::CreateScreenList() const {
std::array<std::function<std::unique_ptr<Screen>()>, nScreens> screens;
for (size_t i = 0; i < screens.size(); i++) {
@@ -18,16 +17,22 @@ auto ApplicationList::CreateScreenList() const {
return screens;
}
-ApplicationList::ApplicationList(Pinetime::Applications::DisplayApp* app,
+ApplicationList::ApplicationList(DisplayApp* app,
Pinetime::Controllers::Settings& settingsController,
const Pinetime::Controllers::Battery& batteryController,
const Pinetime::Controllers::Ble& bleController,
- Controllers::DateTime& dateTimeController)
+ const Pinetime::Controllers::AlarmController& alarmController,
+ Controllers::DateTime& dateTimeController,
+ Pinetime::Controllers::FS& filesystem,
+ std::array<Tile::Applications, UserAppTypes::Count>&& apps)
: app {app},
settingsController {settingsController},
batteryController {batteryController},
bleController {bleController},
+ alarmController {alarmController},
dateTimeController {dateTimeController},
+ filesystem {filesystem},
+ apps {std::move(apps)},
screens {app, settingsController.GetAppMenu(), CreateScreenList(), Screens::ScreenListModes::UpDown} {
}
@@ -40,9 +45,14 @@ bool ApplicationList::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
}
std::unique_ptr<Screen> ApplicationList::CreateScreen(unsigned int screenNum) const {
- std::array<Tile::Applications, appsPerScreen> apps;
+ std::array<Tile::Applications, appsPerScreen> pageApps;
+
for (int i = 0; i < appsPerScreen; i++) {
- apps[i] = applications[screenNum * appsPerScreen + i];
+ if (i + (screenNum * appsPerScreen) >= apps.size()) {
+ pageApps[i] = {"", Pinetime::Applications::Apps::None, false};
+ } else {
+ pageApps[i] = apps[i + (screenNum * appsPerScreen)];
+ }
}
return std::make_unique<Screens::Tile>(screenNum,
@@ -51,6 +61,7 @@ std::unique_ptr<Screen> ApplicationList::CreateScreen(unsigned int screenNum) co
settingsController,
batteryController,
bleController,
+ alarmController,
dateTimeController,
- apps);
+ pageApps);
}
diff --git a/src/displayapp/screens/ApplicationList.h b/src/displayapp/screens/ApplicationList.h
index 7bdd1154..4a57d7c0 100644
--- a/src/displayapp/screens/ApplicationList.h
+++ b/src/displayapp/screens/ApplicationList.h
@@ -2,14 +2,12 @@
#include <array>
#include <memory>
-
-#include "displayapp/screens/Screen.h"
-#include "displayapp/screens/ScreenList.h"
-#include "components/datetime/DateTimeController.h"
-#include "components/settings/Settings.h"
-#include "components/battery/BatteryController.h"
-#include "displayapp/screens/Symbols.h"
-#include "displayapp/screens/Tile.h"
+#include "displayapp/apps/Apps.h"
+#include "Screen.h"
+#include "ScreenList.h"
+#include "displayapp/Controllers.h"
+#include "Symbols.h"
+#include "Tile.h"
namespace Pinetime {
namespace Applications {
@@ -20,7 +18,10 @@ namespace Pinetime {
Pinetime::Controllers::Settings& settingsController,
const Pinetime::Controllers::Battery& batteryController,
const Pinetime::Controllers::Ble& bleController,
- Controllers::DateTime& dateTimeController);
+ const Pinetime::Controllers::AlarmController& alarmController,
+ Controllers::DateTime& dateTimeController,
+ Pinetime::Controllers::FS& filesystem,
+ std::array<Tile::Applications, UserAppTypes::Count>&& apps);
~ApplicationList() override;
bool OnTouchEvent(TouchEvents event) override;
@@ -32,30 +33,15 @@ namespace Pinetime {
Controllers::Settings& settingsController;
const Pinetime::Controllers::Battery& batteryController;
const Pinetime::Controllers::Ble& bleController;
+ const Pinetime::Controllers::AlarmController& alarmController;
Controllers::DateTime& dateTimeController;
+ Pinetime::Controllers::FS& filesystem;
+ std::array<Tile::Applications, UserAppTypes::Count> apps;
static constexpr int appsPerScreen = 6;
- // Increment this when more space is needed
- static constexpr int nScreens = 2;
-
- static constexpr std::array<Tile::Applications, appsPerScreen * nScreens> applications {{
- {Symbols::stopWatch, Apps::StopWatch},
- {Symbols::clock, Apps::Alarm},
- {Symbols::hourGlass, Apps::Timer},
- {Symbols::shoe, Apps::Steps},
- {Symbols::heartBeat, Apps::HeartRate},
- {Symbols::music, Apps::Music},
-
- {Symbols::paintbrush, Apps::Paint},
- {Symbols::paddle, Apps::Paddle},
- {"2", Apps::Twos},
- {Symbols::drum, Apps::Metronome},
- {Symbols::map, Apps::Navigation},
- {Symbols::none, Apps::None},
+ static constexpr int nScreens = UserAppTypes::Count > 0 ? (UserAppTypes::Count - 1) / appsPerScreen + 1 : 1;
- // {"M", Apps::Motion},
- }};
ScreenList<nScreens> screens;
};
}
diff --git a/src/displayapp/screens/BatteryInfo.cpp b/src/displayapp/screens/BatteryInfo.cpp
index ab0a2bd4..20401988 100644
--- a/src/displayapp/screens/BatteryInfo.cpp
+++ b/src/displayapp/screens/BatteryInfo.cpp
@@ -10,33 +10,35 @@ BatteryInfo::BatteryInfo(const Pinetime::Controllers::Battery& batteryController
batteryPercent = batteryController.PercentRemaining();
batteryVoltage = batteryController.Voltage();
- charging_bar = lv_bar_create(lv_scr_act(), nullptr);
- lv_obj_set_size(charging_bar, 200, 15);
- lv_bar_set_range(charging_bar, 0, 100);
- lv_obj_align(charging_bar, nullptr, LV_ALIGN_CENTER, 0, 10);
- lv_bar_set_anim_time(charging_bar, 1000);
- lv_obj_set_style_local_radius(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
- lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, Colors::bgAlt);
- lv_obj_set_style_local_bg_opa(charging_bar, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_OPA_100);
- lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_RED);
- lv_bar_set_value(charging_bar, batteryPercent, LV_ANIM_ON);
+ chargingArc = lv_arc_create(lv_scr_act(), nullptr);
+ lv_arc_set_rotation(chargingArc, 270);
+ lv_arc_set_bg_angles(chargingArc, 0, 360);
+ lv_arc_set_adjustable(chargingArc, false);
+ lv_obj_set_size(chargingArc, 180, 180);
+ lv_obj_align(chargingArc, nullptr, LV_ALIGN_CENTER, 0, -30);
+ lv_arc_set_value(chargingArc, batteryPercent);
+ lv_obj_set_style_local_bg_opa(chargingArc, LV_ARC_PART_BG, LV_STATE_DEFAULT, LV_OPA_0);
+ lv_obj_set_style_local_line_color(chargingArc, LV_ARC_PART_BG, LV_STATE_DEFAULT, Colors::bgAlt);
+ lv_obj_set_style_local_border_width(chargingArc, LV_ARC_PART_BG, LV_STATE_DEFAULT, 2);
+ lv_obj_set_style_local_radius(chargingArc, LV_ARC_PART_BG, LV_STATE_DEFAULT, 0);
+ lv_obj_set_style_local_line_color(chargingArc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_LIME);
status = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(status, "Reading Battery status");
lv_label_set_align(status, LV_LABEL_ALIGN_CENTER);
- lv_obj_align(status, charging_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);
+ lv_obj_align(status, nullptr, LV_ALIGN_IN_BOTTOM_MID, 0, -17);
percent = lv_label_create(lv_scr_act(), nullptr);
- lv_obj_set_style_local_text_font(percent, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76);
+ lv_obj_set_style_local_text_font(percent, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42);
lv_label_set_text_fmt(percent, "%02i%%", batteryPercent);
lv_label_set_align(percent, LV_LABEL_ALIGN_LEFT);
- lv_obj_align(percent, nullptr, LV_ALIGN_CENTER, 0, -60);
+ lv_obj_align(percent, chargingArc, LV_ALIGN_CENTER, 0, 0);
voltage = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(voltage, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::orange);
lv_label_set_text_fmt(voltage, "%1i.%02i volts", batteryVoltage / 1000, batteryVoltage % 1000 / 10);
lv_label_set_align(voltage, LV_LABEL_ALIGN_CENTER);
- lv_obj_align(voltage, nullptr, LV_ALIGN_CENTER, 0, 95);
+ lv_obj_align(voltage, nullptr, LV_ALIGN_IN_BOTTOM_MID, 0, -7);
taskRefresh = lv_task_create(RefreshTaskCallback, 5000, LV_TASK_PRIO_MID, this);
Refresh();
@@ -53,22 +55,23 @@ void BatteryInfo::Refresh() {
batteryVoltage = batteryController.Voltage();
if (batteryController.IsCharging()) {
- lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_RED);
+ lv_obj_set_style_local_line_color(chargingArc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_LIME);
lv_label_set_text_static(status, "Charging");
} else if (batteryPercent == 100) {
- lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_BLUE);
+ lv_obj_set_style_local_line_color(chargingArc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_BLUE);
lv_label_set_text_static(status, "Fully charged");
} else if (batteryPercent < 10) {
- lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
+ lv_obj_set_style_local_line_color(chargingArc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_RED);
lv_label_set_text_static(status, "Battery low");
} else {
- lv_obj_set_style_local_bg_color(charging_bar, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, Colors::highlight);
+ lv_obj_set_style_local_line_color(chargingArc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_GREEN);
lv_label_set_text_static(status, "Discharging");
}
lv_label_set_text_fmt(percent, "%02i%%", batteryPercent);
+ lv_obj_align(percent, chargingArc, LV_ALIGN_CENTER, 0, 0);
- lv_obj_align(status, charging_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);
+ lv_obj_align(status, voltage, LV_ALIGN_IN_BOTTOM_MID, 0, -27);
lv_label_set_text_fmt(voltage, "%1i.%02i volts", batteryVoltage / 1000, batteryVoltage % 1000 / 10);
- lv_bar_set_value(charging_bar, batteryPercent, LV_ANIM_ON);
+ lv_arc_set_value(chargingArc, batteryPercent);
}
diff --git a/src/displayapp/screens/BatteryInfo.h b/src/displayapp/screens/BatteryInfo.h
index aa01d464..27bbaa00 100644
--- a/src/displayapp/screens/BatteryInfo.h
+++ b/src/displayapp/screens/BatteryInfo.h
@@ -24,7 +24,7 @@ namespace Pinetime {
lv_obj_t* voltage;
lv_obj_t* percent;
- lv_obj_t* charging_bar;
+ lv_obj_t* chargingArc;
lv_obj_t* status;
lv_task_t* taskRefresh;
diff --git a/src/displayapp/screens/Calculator.cpp b/src/displayapp/screens/Calculator.cpp
new file mode 100644
index 00000000..a1f09383
--- /dev/null
+++ b/src/displayapp/screens/Calculator.cpp
@@ -0,0 +1,375 @@
+#include <cmath>
+#include <cinttypes>
+#include "Calculator.h"
+#include "displayapp/InfiniTimeTheme.h"
+#include "Symbols.h"
+
+using namespace Pinetime::Applications::Screens;
+
+static void eventHandler(lv_obj_t* obj, lv_event_t event) {
+ auto app = static_cast<Calculator*>(obj->user_data);
+ app->OnButtonEvent(obj, event);
+}
+
+Calculator::~Calculator() {
+ lv_obj_clean(lv_scr_act());
+}
+
+constexpr const char* const buttonMap[] = {
+ "7", "8", "9", Symbols::backspace, "\n", "4", "5", "6", "+ -", "\n", "1", "2", "3", "* /", "\n", "0", ".", "(-)", "=", ""};
+
+Calculator::Calculator() {
+ resultLabel = lv_label_create(lv_scr_act(), nullptr);
+ lv_label_set_long_mode(resultLabel, LV_LABEL_LONG_CROP);
+ lv_label_set_align(resultLabel, LV_LABEL_ALIGN_RIGHT);
+ lv_label_set_text_fmt(resultLabel, "%" PRId64, result);
+ lv_obj_set_size(resultLabel, 200, 20);
+ lv_obj_set_pos(resultLabel, 10, 5);
+
+ valueLabel = lv_label_create(lv_scr_act(), nullptr);
+ lv_label_set_long_mode(valueLabel, LV_LABEL_LONG_CROP);
+ lv_label_set_align(valueLabel, LV_LABEL_ALIGN_RIGHT);
+ lv_label_set_text_fmt(valueLabel, "%" PRId64, value);
+ lv_obj_set_size(valueLabel, 200, 20);
+ lv_obj_set_pos(valueLabel, 10, 35);
+
+ buttonMatrix = lv_btnmatrix_create(lv_scr_act(), nullptr);
+ buttonMatrix->user_data = this;
+ lv_obj_set_event_cb(buttonMatrix, eventHandler);
+ lv_btnmatrix_set_map(buttonMatrix, const_cast<const char**>(buttonMap));
+ lv_btnmatrix_set_one_check(buttonMatrix, true);
+ lv_obj_set_size(buttonMatrix, 238, 180);
+ lv_obj_set_style_local_bg_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_DEFAULT, Colors::bgAlt);
+ lv_obj_set_style_local_pad_inner(buttonMatrix, LV_BTNMATRIX_PART_BG, LV_STATE_DEFAULT, 1);
+ lv_obj_set_style_local_pad_top(buttonMatrix, LV_BTNMATRIX_PART_BG, LV_STATE_DEFAULT, 1);
+ lv_obj_set_style_local_pad_bottom(buttonMatrix, LV_BTNMATRIX_PART_BG, LV_STATE_DEFAULT, 1);
+ lv_obj_set_style_local_pad_left(buttonMatrix, LV_BTNMATRIX_PART_BG, LV_STATE_DEFAULT, 1);
+ lv_obj_set_style_local_pad_right(buttonMatrix, LV_BTNMATRIX_PART_BG, LV_STATE_DEFAULT, 1);
+ lv_obj_align(buttonMatrix, nullptr, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
+
+ lv_obj_set_style_local_bg_opa(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, LV_OPA_COVER);
+ lv_obj_set_style_local_bg_grad_stop(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, 128);
+ lv_obj_set_style_local_bg_main_stop(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, 128);
+}
+
+void Calculator::OnButtonEvent(lv_obj_t* obj, lv_event_t event) {
+ if ((obj == buttonMatrix) && (event == LV_EVENT_PRESSED)) {
+ HandleInput();
+ }
+}
+
+void Calculator::HandleInput() {
+ const char* buttonText = lv_btnmatrix_get_active_btn_text(buttonMatrix);
+
+ if (buttonText == nullptr) {
+ return;
+ }
+
+ if ((equalSignPressedBefore && (*buttonText != '=')) || (error != Error::None)) {
+ ResetInput();
+ UpdateOperation();
+ }
+
+ // we only compare the first char because it is enough
+ switch (*buttonText) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': {
+ // *buttonText is the first char in buttonText
+ // "- '0'" results in the int value of the char
+ uint8_t digit = (*buttonText) - '0';
+ int8_t sign = (value < 0) ? -1 : 1;
+
+ // if this is true, we already pressed the . button
+ if (offset < FIXED_POINT_OFFSET) {
+ value += sign * offset * digit;
+ offset /= 10;
+ } else if (value <= MAX_VALUE / 10) {
+ value *= 10;
+ value += sign * offset * digit;
+ }
+ } break;
+
+ // unary minus
+ case '(':
+ value = -value;
+ break;
+
+ case '.':
+ if (offset == FIXED_POINT_OFFSET) {
+ offset /= 10;
+ }
+ break;
+
+ // for every operator we:
+ // - eval the current operator if value > FIXED_POINT_OFFSET
+ // - then set the new operator
+ // - + and - as well as * and / cycle on the same button
+ case '+':
+ if (value != 0) {
+ Eval();
+ ResetInput();
+ }
+
+ switch (operation) {
+ case '+':
+ operation = '-';
+ break;
+ case '-':
+ operation = ' ';
+ break;
+ default:
+ operation = '+';
+ break;
+ }
+ UpdateOperation();
+ break;
+
+ case '*':
+ if (value != 0) {
+ Eval();
+ ResetInput();
+ }
+
+ switch (operation) {
+ case '*':
+ operation = '/';
+ break;
+ case '/':
+ operation = ' ';
+ break;
+ default:
+ operation = '*';
+ break;
+ }
+ UpdateOperation();
+ break;
+
+ // this is a little hacky because it matches only the first char
+ case Symbols::backspace[0]:
+ if (value != 0) {
+ // delete one value digit
+ if (offset < FIXED_POINT_OFFSET) {
+ if (offset == 0) {
+ offset = 1;
+ } else {
+ offset *= 10;
+ }
+ } else {
+ value /= 10;
+ }
+ if (offset < FIXED_POINT_OFFSET) {
+ value -= value % (10 * offset);
+ } else {
+ value -= value % offset;
+ }
+ } else if (offset < FIXED_POINT_OFFSET) {
+ if (offset == 0) {
+ offset = 1;
+ } else {
+ offset *= 10;
+ }
+ } else {
+ // reset the result
+ result = 0;
+ }
+
+ if (value == 0) {
+ operation = ' ';
+ UpdateOperation();
+ }
+ break;
+
+ case '=':
+ equalSignPressedBefore = true;
+ Eval();
+ // If the operation is ' ' then we move the value to the result.
+ // We reset the input after this.
+ // This seems more convenient.
+ if (operation == ' ') {
+ ResetInput();
+ }
+ break;
+ }
+
+ UpdateValueLabel();
+ UpdateResultLabel();
+}
+
+void Calculator::UpdateOperation() const {
+ switch (operation) {
+ case '+':
+ lv_obj_set_style_local_bg_grad_dir(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, LV_GRAD_DIR_HOR);
+ lv_obj_set_style_local_bg_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::deepOrange);
+ lv_obj_set_style_local_bg_grad_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::bgAlt);
+ lv_btnmatrix_set_btn_ctrl(buttonMatrix, 7, LV_BTNMATRIX_CTRL_CHECK_STATE);
+ break;
+ case '-':
+ lv_obj_set_style_local_bg_grad_dir(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, LV_GRAD_DIR_HOR);
+ lv_obj_set_style_local_bg_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::bgAlt);
+ lv_obj_set_style_local_bg_grad_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::deepOrange);
+ lv_btnmatrix_set_btn_ctrl(buttonMatrix, 7, LV_BTNMATRIX_CTRL_CHECK_STATE);
+ break;
+ case '*':
+ lv_obj_set_style_local_bg_grad_dir(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, LV_GRAD_DIR_HOR);
+ lv_obj_set_style_local_bg_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::deepOrange);
+ lv_obj_set_style_local_bg_grad_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::bgAlt);
+ lv_btnmatrix_set_btn_ctrl(buttonMatrix, 11, LV_BTNMATRIX_CTRL_CHECK_STATE);
+ break;
+ case '/':
+ lv_obj_set_style_local_bg_grad_dir(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, LV_GRAD_DIR_HOR);
+ lv_obj_set_style_local_bg_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::bgAlt);
+ lv_obj_set_style_local_bg_grad_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::deepOrange);
+ lv_btnmatrix_set_btn_ctrl(buttonMatrix, 11, LV_BTNMATRIX_CTRL_CHECK_STATE);
+ break;
+ default:
+ lv_btnmatrix_clear_btn_ctrl_all(buttonMatrix, LV_BTNMATRIX_CTRL_CHECK_STATE);
+ break;
+ }
+}
+
+void Calculator::ResetInput() {
+ value = 0;
+ offset = FIXED_POINT_OFFSET;
+ operation = ' ';
+ equalSignPressedBefore = false;
+ error = Error::None;
+}
+
+void Calculator::UpdateResultLabel() const {
+ int64_t integer = result / FIXED_POINT_OFFSET;
+ int64_t remainder = result % FIXED_POINT_OFFSET;
+ bool negative = (remainder < 0);
+
+ if (remainder == 0) {
+ lv_label_set_text_fmt(resultLabel, "%" PRId64, integer);
+ return;
+ }
+
+ if (remainder < 0) {
+ remainder = -remainder;
+ }
+
+ uint8_t minWidth = N_DECIMALS;
+
+ // cut "0"-digits on the right
+ while ((remainder > 0) && (remainder % 10 == 0)) {
+ remainder /= 10;
+ minWidth--;
+ }
+
+ if ((integer == 0) && negative) {
+ lv_label_set_text_fmt(resultLabel, "-0.%0*" PRId64, minWidth, remainder);
+ } else {
+ lv_label_set_text_fmt(resultLabel, "%" PRId64 ".%0*" PRId64, integer, minWidth, remainder);
+ }
+}
+
+void Calculator::UpdateValueLabel() {
+ switch (error) {
+ case Error::TooLarge:
+ lv_label_set_text_static(valueLabel, "too large");
+ break;
+ case Error::ZeroDivision:
+ lv_label_set_text_static(valueLabel, "zero division");
+ break;
+ case Error::None:
+ default: {
+ int64_t integer = value / FIXED_POINT_OFFSET;
+ int64_t remainder = value % FIXED_POINT_OFFSET;
+ bool negative = (remainder < 0);
+
+ int64_t printRemainder = remainder < 0 ? -remainder : remainder;
+
+ uint8_t minWidth = 0;
+ int64_t tmpOffset = offset;
+
+ if (tmpOffset == 0) {
+ tmpOffset = 1;
+ minWidth = 1;
+ }
+ while (tmpOffset < FIXED_POINT_OFFSET) {
+ tmpOffset *= 10;
+ minWidth++;
+ }
+ minWidth--;
+
+ for (uint8_t i = minWidth; i < N_DECIMALS; i++) {
+ printRemainder /= 10;
+ }
+
+ if ((integer == 0) && negative) {
+ lv_label_set_text_fmt(valueLabel, "-0.%0*" PRId64, minWidth, printRemainder);
+ } else if (offset == FIXED_POINT_OFFSET) {
+ lv_label_set_text_fmt(valueLabel, "%" PRId64, integer);
+ } else if ((offset == (FIXED_POINT_OFFSET / 10)) && (remainder == 0)) {
+ lv_label_set_text_fmt(valueLabel, "%" PRId64 ".", integer);
+ } else {
+ lv_label_set_text_fmt(valueLabel, "%" PRId64 ".%0*" PRId64, integer, minWidth, printRemainder);
+ }
+ } break;
+ }
+}
+
+// update the result based on value and operation
+void Calculator::Eval() {
+ switch (operation) {
+ case ' ':
+ result = value;
+ break;
+
+ case '+':
+ // check for overflow
+ if (((result > 0) && (value > (MAX_VALUE - result))) || ((result < 0) && (value < (MIN_VALUE - result)))) {
+ error = Error::TooLarge;
+ break;
+ }
+
+ result += value;
+ break;
+ case '-':
+ // check for overflow
+ if (((result < 0) && (value > (MAX_VALUE + result))) || ((result > 0) && (value < (MIN_VALUE + result)))) {
+ error = Error::TooLarge;
+ break;
+ }
+
+ result -= value;
+ break;
+ case '*':
+ // check for overflow
+ // while dividing we eliminate the fixed point offset
+ // therefore we have to multiply it again for the comparison with value
+ // we also assume here that MAX_VALUE == -MIN_VALUE
+ if ((result != 0) && (std::abs(value) > (FIXED_POINT_OFFSET * (MAX_VALUE / std::abs(result))))) {
+ error = Error::TooLarge;
+ break;
+ }
+
+ result *= value;
+ // fixed point offset was multiplied too
+ result /= FIXED_POINT_OFFSET;
+ break;
+ case '/':
+ // check for zero division
+ if (value == 0) {
+ error = Error::ZeroDivision;
+ break;
+ }
+
+ // fixed point offset will be divided too
+ result *= FIXED_POINT_OFFSET;
+ result /= value;
+ break;
+
+ default:
+ break;
+ }
+}
diff --git a/src/displayapp/screens/Calculator.h b/src/displayapp/screens/Calculator.h
new file mode 100644
index 00000000..9971f275
--- /dev/null
+++ b/src/displayapp/screens/Calculator.h
@@ -0,0 +1,83 @@
+#pragma once
+
+#include "displayapp/screens/Screen.h"
+#include "displayapp/apps/Apps.h"
+#include "displayapp/Controllers.h"
+#include "Symbols.h"
+
+namespace {
+ constexpr int64_t powi(int64_t base, uint8_t exponent) {
+ int64_t value = 1;
+ while (exponent) {
+ value *= base;
+ exponent--;
+ }
+ return value;
+ }
+}
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+ class Calculator : public Screen {
+ public:
+ ~Calculator() override;
+
+ Calculator();
+
+ void OnButtonEvent(lv_obj_t* obj, lv_event_t event);
+
+ private:
+ lv_obj_t* buttonMatrix {};
+ lv_obj_t* valueLabel {};
+ lv_obj_t* resultLabel {};
+
+ void Eval();
+ void ResetInput();
+ void HandleInput();
+ void UpdateValueLabel();
+ void UpdateResultLabel() const;
+ void UpdateOperation() const;
+
+ // change this if you want to change the number of decimals
+ // keep in mind, that we only have 12 digits in total (see MAX_DIGITS)
+ // due to the fixed point implementation
+ static constexpr uint8_t N_DECIMALS = 3;
+ // this is the constant default offset
+ static constexpr int64_t FIXED_POINT_OFFSET = powi(10, N_DECIMALS);
+ // this is the current offset, may vary after pressing '.'
+ int64_t offset = FIXED_POINT_OFFSET;
+
+ // the screen can show 12 chars
+ // but two are needed for '.' and '-'
+ static constexpr uint8_t MAX_DIGITS = 12;
+ static constexpr int64_t MAX_VALUE = powi(10, MAX_DIGITS) - 1;
+ // this is assumed in the multiplication overflow!
+ static constexpr int64_t MIN_VALUE = -MAX_VALUE;
+
+ int64_t value = 0;
+ int64_t result = 0;
+ char operation = ' ';
+ bool equalSignPressedBefore = false;
+
+ enum Error {
+ TooLarge,
+ ZeroDivision,
+ None,
+ };
+
+ Error error = Error::None;
+ };
+ }
+
+ template <>
+ struct AppTraits<Apps::Calculator> {
+ static constexpr Apps app = Apps::Calculator;
+ static constexpr const char* icon = Screens::Symbols::calculator;
+
+ static Screens::Screen* Create(AppControllers& /* controllers */) {
+ return new Screens::Calculator();
+ };
+ };
+ }
+}
diff --git a/src/displayapp/screens/CheckboxList.h b/src/displayapp/screens/CheckboxList.h
index c208bc48..c6119970 100644
--- a/src/displayapp/screens/CheckboxList.h
+++ b/src/displayapp/screens/CheckboxList.h
@@ -1,6 +1,6 @@
#pragma once
-#include "displayapp/Apps.h"
+#include "displayapp/apps/Apps.h"
#include "displayapp/screens/Screen.h"
#include <array>
#include <cstdint>
diff --git a/src/displayapp/screens/Clock.cpp b/src/displayapp/screens/Clock.cpp
deleted file mode 100644
index a03dc68b..00000000
--- a/src/displayapp/screens/Clock.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-#include "displayapp/screens/Clock.h"
-
-#include <lvgl/lvgl.h>
-#include "components/battery/BatteryController.h"
-#include "components/motion/MotionController.h"
-#include "components/ble/BleController.h"
-#include "components/ble/NotificationManager.h"
-#include "components/settings/Settings.h"
-#include "displayapp/DisplayApp.h"
-#include "displayapp/screens/WatchFaceDigital.h"
-#include "displayapp/screens/WatchFaceTerminal.h"
-#include "displayapp/screens/WatchFaceInfineat.h"
-#include "displayapp/screens/WatchFaceAnalog.h"
-#include "displayapp/screens/WatchFacePineTimeStyle.h"
-#include "displayapp/screens/WatchFaceCasioStyleG7710.h"
-
-using namespace Pinetime::Applications::Screens;
-
-Clock::Clock(Controllers::DateTime& dateTimeController,
- const Controllers::Battery& batteryController,
- const Controllers::Ble& bleController,
- Controllers::NotificationManager& notificationManager,
- Controllers::Settings& settingsController,
- Controllers::HeartRateController& heartRateController,
- Controllers::MotionController& motionController,
- Controllers::FS& filesystem)
- : dateTimeController {dateTimeController},
- batteryController {batteryController},
- bleController {bleController},
- notificationManager {notificationManager},
- settingsController {settingsController},
- heartRateController {heartRateController},
- motionController {motionController},
- filesystem {filesystem},
- screen {[this, &settingsController]() {
- switch (settingsController.GetClockFace()) {
- case 0:
- return WatchFaceDigitalScreen();
- break;
- case 1:
- return WatchFaceAnalogScreen();
- break;
- case 2:
- return WatchFacePineTimeStyleScreen();
- break;
- case 3:
- return WatchFaceTerminalScreen();
- break;
- case 4:
- return WatchFaceInfineatScreen();
- break;
- case 5:
- return WatchFaceCasioStyleG7710();
- break;
- }
- return WatchFaceDigitalScreen();
- }()} {
- settingsController.SetAppMenu(0);
-}
-
-Clock::~Clock() {
- lv_obj_clean(lv_scr_act());
-}
-
-bool Clock::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
- return screen->OnTouchEvent(event);
-}
-
-bool Clock::OnButtonPushed() {
- return screen->OnButtonPushed();
-}
-
-std::unique_ptr<Screen> Clock::WatchFaceDigitalScreen() {
- return std::make_unique<Screens::WatchFaceDigital>(dateTimeController,
- batteryController,
- bleController,
- notificationManager,
- settingsController,
- heartRateController,
- motionController);
-}
-
-std::unique_ptr<Screen> Clock::WatchFaceAnalogScreen() {
- return std::make_unique<Screens::WatchFaceAnalog>(dateTimeController,
- batteryController,
- bleController,
- notificationManager,
- settingsController);
-}
-
-std::unique_ptr<Screen> Clock::WatchFacePineTimeStyleScreen() {
- return std::make_unique<Screens::WatchFacePineTimeStyle>(dateTimeController,
- batteryController,
- bleController,
- notificationManager,
- settingsController,
- motionController);
-}
-
-std::unique_ptr<Screen> Clock::WatchFaceTerminalScreen() {
- return std::make_unique<Screens::WatchFaceTerminal>(dateTimeController,
- batteryController,
- bleController,
- notificationManager,
- settingsController,
- heartRateController,
- motionController);
-}
-
-std::unique_ptr<Screen> Clock::WatchFaceInfineatScreen() {
- return std::make_unique<Screens::WatchFaceInfineat>(dateTimeController,
- batteryController,
- bleController,
- notificationManager,
- settingsController,
- motionController,
- filesystem);
-}
-
-std::unique_ptr<Screen> Clock::WatchFaceCasioStyleG7710() {
- return std::make_unique<Screens::WatchFaceCasioStyleG7710>(dateTimeController,
- batteryController,
- bleController,
- notificationManager,
- settingsController,
- heartRateController,
- motionController,
- filesystem);
-}
diff --git a/src/displayapp/screens/Clock.h b/src/displayapp/screens/Clock.h
deleted file mode 100644
index 8c987fbb..00000000
--- a/src/displayapp/screens/Clock.h
+++ /dev/null
@@ -1,57 +0,0 @@
-#pragma once
-
-#include <lvgl/src/lv_core/lv_obj.h>
-#include <chrono>
-#include <cstdint>
-#include <memory>
-#include <components/heartrate/HeartRateController.h>
-#include "displayapp/screens/Screen.h"
-#include "components/datetime/DateTimeController.h"
-
-namespace Pinetime {
- namespace Controllers {
- class Settings;
- class Battery;
- class Ble;
- class NotificationManager;
- class MotionController;
- }
-
- namespace Applications {
- namespace Screens {
- class Clock : public Screen {
- public:
- Clock(Controllers::DateTime& dateTimeController,
- const Controllers::Battery& batteryController,
- const Controllers::Ble& bleController,
- Controllers::NotificationManager& notificationManager,
- Controllers::Settings& settingsController,
- Controllers::HeartRateController& heartRateController,
- Controllers::MotionController& motionController,
- Controllers::FS& filesystem);
- ~Clock() override;
-
- bool OnTouchEvent(TouchEvents event) override;
- bool OnButtonPushed() override;
-
- private:
- Controllers::DateTime& dateTimeController;
- const Controllers::Battery& batteryController;
- const Controllers::Ble& bleController;
- Controllers::NotificationManager& notificationManager;
- Controllers::Settings& settingsController;
- Controllers::HeartRateController& heartRateController;
- Controllers::MotionController& motionController;
- Controllers::FS& filesystem;
-
- std::unique_ptr<Screen> screen;
- std::unique_ptr<Screen> WatchFaceDigitalScreen();
- std::unique_ptr<Screen> WatchFaceAnalogScreen();
- std::unique_ptr<Screen> WatchFacePineTimeStyleScreen();
- std::unique_ptr<Screen> WatchFaceTerminalScreen();
- std::unique_ptr<Screen> WatchFaceInfineatScreen();
- std::unique_ptr<Screen> WatchFaceCasioStyleG7710();
- };
- }
- }
-}
diff --git a/src/displayapp/screens/Dice.cpp b/src/displayapp/screens/Dice.cpp
new file mode 100644
index 00000000..302c5f3f
--- /dev/null
+++ b/src/displayapp/screens/Dice.cpp
@@ -0,0 +1,199 @@
+#include "displayapp/screens/Dice.h"
+#include "displayapp/screens/Screen.h"
+#include "displayapp/screens/Symbols.h"
+#include "components/settings/Settings.h"
+#include "components/motor/MotorController.h"
+#include "components/motion/MotionController.h"
+
+using namespace Pinetime::Applications::Screens;
+
+namespace {
+ lv_obj_t* MakeLabel(lv_font_t* font,
+ lv_color_t color,
+ lv_label_long_mode_t longMode,
+ uint8_t width,
+ lv_label_align_t labelAlignment,
+ const char* text,
+ lv_obj_t* reference,
+ lv_align_t alignment,
+ int8_t x,
+ int8_t y) {
+ lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font);
+ lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color);
+ lv_label_set_long_mode(label, longMode);
+ if (width != 0) {
+ lv_obj_set_width(label, width);
+ }
+ lv_label_set_align(label, labelAlignment);
+ lv_label_set_text(label, text);
+ lv_obj_align(label, reference, alignment, x, y);
+ return label;
+ }
+
+ void btnRollEventHandler(lv_obj_t* obj, lv_event_t event) {
+ auto* screen = static_cast<Dice*>(obj->user_data);
+ if (event == LV_EVENT_CLICKED) {
+ screen->Roll();
+ }
+ }
+}
+
+Dice::Dice(Controllers::MotionController& motionController,
+ Controllers::MotorController& motorController,
+ Controllers::Settings& settingsController)
+ : motorController {motorController}, motionController {motionController}, settingsController {settingsController} {
+ std::seed_seq sseq {static_cast<uint32_t>(xTaskGetTickCount()),
+ static_cast<uint32_t>(motionController.X()),
+ static_cast<uint32_t>(motionController.Y()),
+ static_cast<uint32_t>(motionController.Z())};
+ gen.seed(sseq);
+
+ lv_obj_t* nCounterLabel = MakeLabel(&jetbrains_mono_bold_20,
+ LV_COLOR_WHITE,
+ LV_LABEL_LONG_EXPAND,
+ 0,
+ LV_LABEL_ALIGN_CENTER,
+ "count",
+ lv_scr_act(),
+ LV_ALIGN_IN_TOP_LEFT,
+ 0,
+ 0);
+
+ lv_obj_t* dCounterLabel = MakeLabel(&jetbrains_mono_bold_20,
+ LV_COLOR_WHITE,
+ LV_LABEL_LONG_EXPAND,
+ 0,
+ LV_LABEL_ALIGN_CENTER,
+ "sides",
+ nCounterLabel,
+ LV_ALIGN_OUT_RIGHT_MID,
+ 20,
+ 0);
+
+ nCounter.Create();
+ lv_obj_align(nCounter.GetObject(), nCounterLabel, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
+ nCounter.SetValue(1);
+
+ dCounter.Create();
+ lv_obj_align(dCounter.GetObject(), dCounterLabel, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
+ dCounter.SetValue(6);
+
+ std::uniform_int_distribution<> distrib(0, resultColors.size() - 1);
+ currentColorIndex = distrib(gen);
+
+ resultTotalLabel = MakeLabel(&jetbrains_mono_42,
+ resultColors[currentColorIndex],
+ LV_LABEL_LONG_BREAK,
+ 120,
+ LV_LABEL_ALIGN_CENTER,
+ "",
+ lv_scr_act(),
+ LV_ALIGN_IN_TOP_RIGHT,
+ 11,
+ 38);
+ resultIndividualLabel = MakeLabel(&jetbrains_mono_bold_20,
+ resultColors[currentColorIndex],
+ LV_LABEL_LONG_BREAK,
+ 90,
+ LV_LABEL_ALIGN_CENTER,
+ "",
+ resultTotalLabel,
+ LV_ALIGN_OUT_BOTTOM_MID,
+ 0,
+ 10);
+
+ Roll();
+ openingRoll = false;
+
+ btnRoll = lv_btn_create(lv_scr_act(), nullptr);
+ btnRoll->user_data = this;
+ lv_obj_set_event_cb(btnRoll, btnRollEventHandler);
+ lv_obj_set_size(btnRoll, 240, 50);
+ lv_obj_align(btnRoll, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0);
+
+ btnRollLabel = MakeLabel(&jetbrains_mono_bold_20,
+ LV_COLOR_WHITE,
+ LV_LABEL_LONG_EXPAND,
+ 0,
+ LV_LABEL_ALIGN_CENTER,
+ Symbols::dice,
+ btnRoll,
+ LV_ALIGN_CENTER,
+ 0,
+ 0);
+
+ // Spagetti code in motion controller: it only updates the shake speed when shake to wake is on...
+ enableShakeForDice = !settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::Shake);
+ if (enableShakeForDice) {
+ settingsController.setWakeUpMode(Pinetime::Controllers::Settings::WakeUpMode::Shake, true);
+ }
+ refreshTask = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
+}
+
+Dice::~Dice() {
+ // reset the shake to wake mode.
+ if (enableShakeForDice) {
+ settingsController.setWakeUpMode(Pinetime::Controllers::Settings::WakeUpMode::Shake, false);
+ enableShakeForDice = false;
+ }
+ lv_task_del(refreshTask);
+ lv_obj_clean(lv_scr_act());
+}
+
+void Dice::Refresh() {
+ // we only reset the hysteresis when at rest
+ if (motionController.CurrentShakeSpeed() >= settingsController.GetShakeThreshold()) {
+ if (currentRollHysteresis <= 0) {
+ // this timestamp is used for the screen timeout
+ lv_disp_get_next(NULL)->last_activity_time = lv_tick_get();
+
+ Roll();
+ }
+ } else if (currentRollHysteresis > 0)
+ --currentRollHysteresis;
+}
+
+void Dice::Roll() {
+ uint8_t resultIndividual;
+ uint16_t resultTotal = 0;
+ std::uniform_int_distribution<> distrib(1, dCounter.GetValue());
+
+ lv_label_set_text(resultIndividualLabel, "");
+
+ if (nCounter.GetValue() == 1) {
+ resultTotal = distrib(gen);
+ if (dCounter.GetValue() == 2) {
+ switch (resultTotal) {
+ case 1:
+ lv_label_set_text(resultIndividualLabel, "HEADS");
+ break;
+ case 2:
+ lv_label_set_text(resultIndividualLabel, "TAILS");
+ break;
+ }
+ }
+ } else {
+ for (uint8_t i = 0; i < nCounter.GetValue(); i++) {
+ resultIndividual = distrib(gen);
+ resultTotal += resultIndividual;
+ lv_label_ins_text(resultIndividualLabel, LV_LABEL_POS_LAST, std::to_string(resultIndividual).c_str());
+ if (i < (nCounter.GetValue() - 1)) {
+ lv_label_ins_text(resultIndividualLabel, LV_LABEL_POS_LAST, "+");
+ }
+ }
+ }
+
+ lv_label_set_text_fmt(resultTotalLabel, "%d", resultTotal);
+ if (openingRoll == false) {
+ motorController.RunForDuration(30);
+ NextColor();
+ currentRollHysteresis = rollHysteresis;
+ }
+}
+
+void Dice::NextColor() {
+ currentColorIndex = (currentColorIndex + 1) % resultColors.size();
+ lv_obj_set_style_local_text_color(resultTotalLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, resultColors[currentColorIndex]);
+ lv_obj_set_style_local_text_color(resultIndividualLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, resultColors[currentColorIndex]);
+}
diff --git a/src/displayapp/screens/Dice.h b/src/displayapp/screens/Dice.h
new file mode 100644
index 00000000..da91657d
--- /dev/null
+++ b/src/displayapp/screens/Dice.h
@@ -0,0 +1,61 @@
+#pragma once
+
+#include "displayapp/apps/Apps.h"
+#include "displayapp/screens/Screen.h"
+#include "displayapp/widgets/Counter.h"
+#include "displayapp/Controllers.h"
+#include "Symbols.h"
+
+#include <array>
+#include <random>
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+ class Dice : public Screen {
+ public:
+ Dice(Controllers::MotionController& motionController,
+ Controllers::MotorController& motorController,
+ Controllers::Settings& settingsController);
+ ~Dice() override;
+ void Roll();
+ void Refresh() override;
+
+ private:
+ lv_obj_t* btnRoll;
+ lv_obj_t* btnRollLabel;
+ lv_obj_t* resultTotalLabel;
+ lv_obj_t* resultIndividualLabel;
+ lv_task_t* refreshTask;
+ bool enableShakeForDice = false;
+
+ std::mt19937 gen;
+
+ std::array<lv_color_t, 3> resultColors = {LV_COLOR_YELLOW, LV_COLOR_MAGENTA, LV_COLOR_AQUA};
+ uint8_t currentColorIndex;
+ void NextColor();
+
+ Widgets::Counter nCounter = Widgets::Counter(1, 9, jetbrains_mono_42);
+ Widgets::Counter dCounter = Widgets::Counter(2, 99, jetbrains_mono_42);
+
+ bool openingRoll = true;
+ uint8_t currentRollHysteresis = 0;
+ static constexpr uint8_t rollHysteresis = 10;
+
+ Controllers::MotorController& motorController;
+ Controllers::MotionController& motionController;
+ Controllers::Settings& settingsController;
+ };
+ }
+
+ template <>
+ struct AppTraits<Apps::Dice> {
+ static constexpr Apps app = Apps::Dice;
+ static constexpr const char* icon = Screens::Symbols::dice;
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::Dice(controllers.motionController, controllers.motorController, controllers.settingsController);
+ };
+ };
+ }
+}
diff --git a/src/displayapp/screens/FirmwareUpdate.cpp b/src/displayapp/screens/FirmwareUpdate.cpp
index c40240c9..7d00ef39 100644
--- a/src/displayapp/screens/FirmwareUpdate.cpp
+++ b/src/displayapp/screens/FirmwareUpdate.cpp
@@ -2,6 +2,7 @@
#include <lvgl/lvgl.h>
#include "components/ble/BleController.h"
#include "displayapp/DisplayApp.h"
+#include "displayapp/InfiniTimeTheme.h"
using namespace Pinetime::Applications::Screens;
@@ -12,6 +13,9 @@ FirmwareUpdate::FirmwareUpdate(const Pinetime::Controllers::Ble& bleController)
lv_obj_align(titleLabel, nullptr, LV_ALIGN_IN_TOP_MID, 0, 50);
bar1 = lv_bar_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_bg_color(bar1, LV_BAR_PART_BG, LV_STATE_DEFAULT, Colors::bgAlt);
+ lv_obj_set_style_local_bg_opa(bar1, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_OPA_100);
+ lv_obj_set_style_local_radius(bar1, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE);
lv_obj_set_size(bar1, 200, 30);
lv_obj_align(bar1, nullptr, LV_ALIGN_CENTER, 0, 0);
lv_bar_set_range(bar1, 0, 1000);
@@ -75,7 +79,7 @@ void FirmwareUpdate::DisplayProgression() const {
const uint32_t total = bleController.FirmwareUpdateTotalBytes();
const int16_t permille = current / (total / 1000);
- lv_label_set_text_fmt(percentLabel, "%d %%", permille / 10);
+ lv_label_set_text_fmt(percentLabel, "%d%%", permille / 10);
lv_bar_set_value(bar1, permille, LV_ANIM_OFF);
}
diff --git a/src/displayapp/screens/FirmwareValidation.cpp b/src/displayapp/screens/FirmwareValidation.cpp
index 4a1b3d9f..2a9919d5 100644
--- a/src/displayapp/screens/FirmwareValidation.cpp
+++ b/src/displayapp/screens/FirmwareValidation.cpp
@@ -17,8 +17,8 @@ namespace {
FirmwareValidation::FirmwareValidation(Pinetime::Controllers::FirmwareValidator& validator) : validator {validator} {
labelVersion = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_fmt(labelVersion,
- "Version : %lu.%lu.%lu\n"
- "ShortRef : %s",
+ "Version: %lu.%lu.%lu\n"
+ "ShortRef: %s",
Version::Major(),
Version::Minor(),
Version::Patch(),
diff --git a/src/displayapp/screens/FlashLight.cpp b/src/displayapp/screens/FlashLight.cpp
index 1b7cf39c..7e0caff1 100644
--- a/src/displayapp/screens/FlashLight.cpp
+++ b/src/displayapp/screens/FlashLight.cpp
@@ -15,8 +15,9 @@ namespace {
}
FlashLight::FlashLight(System::SystemTask& systemTask, Controllers::BrightnessController& brightnessController)
- : systemTask {systemTask}, brightnessController {brightnessController} {
+ : wakeLock(systemTask), brightnessController {brightnessController} {
+ previousBrightnessLevel = brightnessController.Level();
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
flashLight = lv_label_create(lv_scr_act(), nullptr);
@@ -46,13 +47,13 @@ FlashLight::FlashLight(System::SystemTask& systemTask, Controllers::BrightnessCo
backgroundAction->user_data = this;
lv_obj_set_event_cb(backgroundAction, EventHandler);
- systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
+ wakeLock.Lock();
}
FlashLight::~FlashLight() {
lv_obj_clean(lv_scr_act());
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
- systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
+ brightnessController.Set(previousBrightnessLevel);
}
void FlashLight::SetColors() {
diff --git a/src/displayapp/screens/FlashLight.h b/src/displayapp/screens/FlashLight.h
index 2b710ed5..00ef4a7e 100644
--- a/src/displayapp/screens/FlashLight.h
+++ b/src/displayapp/screens/FlashLight.h
@@ -3,6 +3,7 @@
#include "displayapp/screens/Screen.h"
#include "components/brightness/BrightnessController.h"
#include "systemtask/SystemTask.h"
+#include "systemtask/WakeLock.h"
#include <cstdint>
#include <lvgl/lvgl.h>
@@ -23,10 +24,11 @@ namespace Pinetime {
void SetIndicators();
void SetColors();
- Pinetime::System::SystemTask& systemTask;
+ Pinetime::System::WakeLock wakeLock;
Controllers::BrightnessController& brightnessController;
Controllers::BrightnessController::Levels brightnessLevel = Controllers::BrightnessController::Levels::High;
+ Controllers::BrightnessController::Levels previousBrightnessLevel;
lv_obj_t* flashLight;
lv_obj_t* backgroundAction;
diff --git a/src/displayapp/screens/HeartRate.cpp b/src/displayapp/screens/HeartRate.cpp
index f611fa26..1a84d349 100644
--- a/src/displayapp/screens/HeartRate.cpp
+++ b/src/displayapp/screens/HeartRate.cpp
@@ -29,7 +29,7 @@ namespace {
}
HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, System::SystemTask& systemTask)
- : heartRateController {heartRateController}, systemTask {systemTask} {
+ : heartRateController {heartRateController}, wakeLock(systemTask) {
bool isHrRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped;
label_hr = lv_label_create(lv_scr_act(), nullptr);
@@ -41,7 +41,7 @@ HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, Syst
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray);
}
- lv_label_set_text_static(label_hr, "000");
+ lv_label_set_text_static(label_hr, "---");
lv_obj_align(label_hr, nullptr, LV_ALIGN_CENTER, 0, -40);
label_bpm = lv_label_create(lv_scr_act(), nullptr);
@@ -63,7 +63,7 @@ HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, Syst
label_startStop = lv_label_create(btn_startStop, nullptr);
UpdateStartStopButton(isHrRunning);
if (isHrRunning) {
- systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
+ wakeLock.Lock();
}
taskRefresh = lv_task_create(RefreshTaskCallback, 100, LV_TASK_PRIO_MID, this);
@@ -72,7 +72,6 @@ HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, Syst
HeartRate::~HeartRate() {
lv_task_del(taskRefresh);
lv_obj_clean(lv_scr_act());
- systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
}
void HeartRate::Refresh() {
@@ -82,10 +81,14 @@ void HeartRate::Refresh() {
case Controllers::HeartRateController::States::NoTouch:
case Controllers::HeartRateController::States::NotEnoughData:
// case Controllers::HeartRateController::States::Stopped:
- lv_label_set_text_static(label_hr, "000");
+ lv_label_set_text_static(label_hr, "---");
break;
default:
- lv_label_set_text_fmt(label_hr, "%03d", heartRateController.HeartRate());
+ if (heartRateController.HeartRate() == 0) {
+ lv_label_set_text_static(label_hr, "---");
+ } else {
+ lv_label_set_text_fmt(label_hr, "%03d", heartRateController.HeartRate());
+ }
}
lv_label_set_text_static(label_status, ToString(state));
@@ -97,12 +100,12 @@ void HeartRate::OnStartStopEvent(lv_event_t event) {
if (heartRateController.State() == Controllers::HeartRateController::States::Stopped) {
heartRateController.Start();
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
- systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
+ wakeLock.Lock();
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight);
} else {
heartRateController.Stop();
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
- systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
+ wakeLock.Release();
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray);
}
}
diff --git a/src/displayapp/screens/HeartRate.h b/src/displayapp/screens/HeartRate.h
index 78ae63db..88b4918c 100644
--- a/src/displayapp/screens/HeartRate.h
+++ b/src/displayapp/screens/HeartRate.h
@@ -4,6 +4,8 @@
#include <chrono>
#include "displayapp/screens/Screen.h"
#include "systemtask/SystemTask.h"
+#include "systemtask/WakeLock.h"
+#include "Symbols.h"
#include <lvgl/src/lv_core/lv_style.h>
#include <lvgl/src/lv_core/lv_obj.h>
@@ -26,7 +28,7 @@ namespace Pinetime {
private:
Controllers::HeartRateController& heartRateController;
- Pinetime::System::SystemTask& systemTask;
+ Pinetime::System::WakeLock wakeLock;
void UpdateStartStopButton(bool isRunning);
lv_obj_t* label_hr;
lv_obj_t* label_bpm;
@@ -37,5 +39,15 @@ namespace Pinetime {
lv_task_t* taskRefresh;
};
}
+
+ template <>
+ struct AppTraits<Apps::HeartRate> {
+ static constexpr Apps app = Apps::HeartRate;
+ static constexpr const char* icon = Screens::Symbols::heartBeat;
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::HeartRate(controllers.heartRateController, *controllers.systemTask);
+ };
+ };
}
}
diff --git a/src/displayapp/screens/InfiniPaint.h b/src/displayapp/screens/InfiniPaint.h
index ec184c44..b1f9741a 100644
--- a/src/displayapp/screens/InfiniPaint.h
+++ b/src/displayapp/screens/InfiniPaint.h
@@ -5,6 +5,9 @@
#include <algorithm> // std::fill
#include "displayapp/screens/Screen.h"
#include "components/motor/MotorController.h"
+#include "Symbols.h"
+#include "displayapp/apps/Apps.h"
+#include <displayapp/Controllers.h>
namespace Pinetime {
namespace Components {
@@ -35,5 +38,15 @@ namespace Pinetime {
uint8_t color = 2;
};
}
+
+ template <>
+ struct AppTraits<Apps::Paint> {
+ static constexpr Apps app = Apps::Paint;
+ static constexpr const char* icon = Screens::Symbols::paintbrush;
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::InfiniPaint(controllers.lvgl, controllers.motorController);
+ };
+ };
}
}
diff --git a/src/displayapp/screens/List.h b/src/displayapp/screens/List.h
index 564229e6..17a25f82 100644
--- a/src/displayapp/screens/List.h
+++ b/src/displayapp/screens/List.h
@@ -5,7 +5,7 @@
#include <array>
#include "displayapp/screens/Screen.h"
#include "displayapp/widgets/PageIndicator.h"
-#include "displayapp/Apps.h"
+#include "displayapp/apps/Apps.h"
#include "components/settings/Settings.h"
#define MAXLISTITEMS 4
diff --git a/src/displayapp/screens/Metronome.cpp b/src/displayapp/screens/Metronome.cpp
index 314fde73..6b758470 100644
--- a/src/displayapp/screens/Metronome.cpp
+++ b/src/displayapp/screens/Metronome.cpp
@@ -22,7 +22,7 @@ namespace {
}
Metronome::Metronome(Controllers::MotorController& motorController, System::SystemTask& systemTask)
- : motorController {motorController}, systemTask {systemTask} {
+ : motorController {motorController}, wakeLock(systemTask) {
bpmArc = lv_arc_create(lv_scr_act(), nullptr);
bpmArc->user_data = this;
@@ -72,7 +72,6 @@ Metronome::Metronome(Controllers::MotorController& motorController, System::Syst
Metronome::~Metronome() {
lv_task_del(taskRefresh);
- systemTask.PushMessage(System::Messages::EnableSleeping);
lv_obj_clean(lv_scr_act());
}
@@ -128,12 +127,12 @@ void Metronome::OnEvent(lv_obj_t* obj, lv_event_t event) {
metronomeStarted = !metronomeStarted;
if (metronomeStarted) {
lv_label_set_text_static(lblPlayPause, Symbols::pause);
- systemTask.PushMessage(System::Messages::DisableSleeping);
+ wakeLock.Lock();
startTime = xTaskGetTickCount();
counter = 1;
} else {
lv_label_set_text_static(lblPlayPause, Symbols::play);
- systemTask.PushMessage(System::Messages::EnableSleeping);
+ wakeLock.Release();
}
}
break;
diff --git a/src/displayapp/screens/Metronome.h b/src/displayapp/screens/Metronome.h
index 13b0d664..fab7ff87 100644
--- a/src/displayapp/screens/Metronome.h
+++ b/src/displayapp/screens/Metronome.h
@@ -1,8 +1,10 @@
#pragma once
#include "systemtask/SystemTask.h"
+#include "systemtask/WakeLock.h"
#include "components/motor/MotorController.h"
#include "displayapp/screens/Screen.h"
+#include "Symbols.h"
namespace Pinetime {
namespace Applications {
@@ -20,7 +22,7 @@ namespace Pinetime {
TickType_t startTime = 0;
TickType_t tappedTime = 0;
Controllers::MotorController& motorController;
- System::SystemTask& systemTask;
+ System::WakeLock wakeLock;
int16_t bpm = 120;
uint8_t bpb = 4;
uint8_t counter = 1;
@@ -36,5 +38,15 @@ namespace Pinetime {
lv_task_t* taskRefresh;
};
}
+
+ template <>
+ struct AppTraits<Apps::Metronome> {
+ static constexpr Apps app = Apps::Metronome;
+ static constexpr const char* icon = Screens::Symbols::drum;
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::Metronome(controllers.motorController, *controllers.systemTask);
+ };
+ };
}
}
diff --git a/src/displayapp/screens/Motion.cpp b/src/displayapp/screens/Motion.cpp
index 87c55eea..ecbed317 100644
--- a/src/displayapp/screens/Motion.cpp
+++ b/src/displayapp/screens/Motion.cpp
@@ -53,9 +53,9 @@ void Motion::Refresh() {
lv_label_set_text_fmt(labelStep, "Steps %lu", motionController.NbSteps());
lv_label_set_text_fmt(label,
- "X #FF0000 %d# Y #00B000 %d# Z #FFFF00 %d#",
- motionController.X() / 0x10,
- motionController.Y() / 0x10,
- motionController.Z() / 0x10);
+ "X #FF0000 %d# Y #00B000 %d# Z #FFFF00 %d# mg",
+ motionController.X(),
+ motionController.Y(),
+ motionController.Z());
lv_obj_align(label, nullptr, LV_ALIGN_IN_TOP_MID, 0, 10);
}
diff --git a/src/displayapp/screens/Motion.h b/src/displayapp/screens/Motion.h
index e4cbe483..e13e068c 100644
--- a/src/displayapp/screens/Motion.h
+++ b/src/displayapp/screens/Motion.h
@@ -6,6 +6,8 @@
#include <lvgl/src/lv_core/lv_style.h>
#include <lvgl/src/lv_core/lv_obj.h>
#include <components/motion/MotionController.h>
+#include "displayapp/Controllers.h"
+#include "displayapp/apps/Apps.h"
namespace Pinetime {
namespace Applications {
@@ -30,5 +32,15 @@ namespace Pinetime {
lv_task_t* taskRefresh;
};
}
+
+ template <>
+ struct AppTraits<Apps::Motion> {
+ static constexpr Apps app = Apps::Motion;
+ static constexpr const char* icon = "M";
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::Motion(controllers.motionController);
+ };
+ };
}
}
diff --git a/src/displayapp/screens/Music.h b/src/displayapp/screens/Music.h
index 847c6e74..52253321 100644
--- a/src/displayapp/screens/Music.h
+++ b/src/displayapp/screens/Music.h
@@ -21,6 +21,9 @@
#include <lvgl/src/lv_core/lv_obj.h>
#include <string>
#include "displayapp/screens/Screen.h"
+#include "displayapp/apps/Apps.h"
+#include "displayapp/Controllers.h"
+#include "Symbols.h"
namespace Pinetime {
namespace Controllers {
@@ -82,5 +85,15 @@ namespace Pinetime {
/** Watchapp */
};
}
+
+ template <>
+ struct AppTraits<Apps::Music> {
+ static constexpr Apps app = Apps::Music;
+ static constexpr const char* icon = Screens::Symbols::music;
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::Music(*controllers.musicService);
+ };
+ };
}
}
diff --git a/src/displayapp/screens/Navigation.cpp b/src/displayapp/screens/Navigation.cpp
index 7baea09d..ee9f2a00 100644
--- a/src/displayapp/screens/Navigation.cpp
+++ b/src/displayapp/screens/Navigation.cpp
@@ -23,105 +23,166 @@
using namespace Pinetime::Applications::Screens;
-LV_FONT_DECLARE(lv_font_navi_80)
+/* Notes about the navigation icons :
+ * - Icons are generated from a TTF font converted in PNG images. Those images are all appended
+ * vertically into a single PNG images. Since LVGL support images width and height up to
+ * 2048 px, the icons needs to be split into 2 separate PNG pictures. More info in
+ * src/displayapp/fonts/README.md
+ * - To make the handling of those icons easier, they must all have the same width and height
+ * - Those PNG are then converted into BINARY format using the classical image generator
+ * (in src/resources/generate-img.py)
+ * - The array `iconMap` maps each icon with an index. This index corresponds to the position of
+ * the icon in the file. All index lower than 25 (`maxIconsPerFile`) represent icons located
+ * in the first file (navigation0.bin). All the other icons are located in the second file
+ * (navigation1.bin). Since all icons have the same height, this index must be multiplied by
+ * 80px (`iconHeight`) to get the actual position (in pixels) of the icon in the image.
+ * - This is how the images are laid out in the PNG files :
+ * *---------------*
+ * | ICON 0 |
+ * | FILE 0 |
+ * | INDEX = 0 |
+ * | PIXEL# = 0 |
+ * *---------------*
+ * | ICON 1 |
+ * | FILE 0 |
+ * | INDEX = 1 |
+ * | PIXEL# = -80 |
+ * *---------------*
+ * | ICON 2 |
+ * | FILE 0 |
+ * | INDEX = 2 |
+ * | PIXEL# = -160 |
+ * *---------------*
+ * | ... |
+ * *---------------*
+ * | ICON 25 |
+ * | FILE 1 |
+ * | INDEX = 25 |
+ * | PIXEL# = 0 |
+ * *---------------*
+ * | ICON 26 |
+ * | FILE 1 |
+ * | INDEX = 26 |
+ * | PIXEL# = -80 |
+ * *---------------*
+ * - The source images are located in `src/resources/navigation0.png` and `src/resources/navigation1.png`
+ */
namespace {
- constexpr std::array<std::pair<const char*, const char*>, 86> m_iconMap = {{
- {"arrive-left", "\xEE\xA4\x81"},
- {"arrive-right", "\xEE\xA4\x82"},
- {"arrive-straight", "\xEE\xA4\x80"},
- {"arrive", "\xEE\xA4\x80"},
- {"close", "\xEE\xA4\x83"},
- {"continue-left", "\xEE\xA4\x85"},
- {"continue-right", "\xEE\xA4\x86"},
- {"continue-slight-left", "\xEE\xA4\x87"},
- {"continue-slight-right", "\xEE\xA4\x88"},
- {"continue-straight", "\xEE\xA4\x84"},
- {"continue-uturn", "\xEE\xA4\x89"},
- {"continue", "\xEE\xA4\x84"},
- {"depart-left", "\xEE\xA4\x8B"},
- {"depart-right", "\xEE\xA4\x8C"},
- {"depart-straight", "\xEE\xA4\x8A"},
- {"end-of-road-left", "\xEE\xA4\x8D"},
- {"end-of-road-right", "\xEE\xA4\x8E"},
- {"ferry", "\xEE\xA4\x8F"},
- {"flag", "\xEE\xA4\x90"},
- {"fork-left", "\xEE\xA4\x92"},
- {"fork-right", "\xEE\xA4\x93"},
- {"fork-slight-left", "\xEE\xA4\x94"},
- {"fork-slight-right", "\xEE\xA4\x95"},
- {"fork-straight", "\xEE\xA4\x96"},
- {"invalid", "\xEE\xA4\x84"},
- {"invalid-left", "\xEE\xA4\x85"},
- {"invalid-right", "\xEE\xA4\x86"},
- {"invalid-slight-left", "\xEE\xA4\x87"},
- {"invalid-slight-right", "\xEE\xA4\x88"},
- {"invalid-straight", "\xEE\xA4\x84"},
- {"invalid-uturn", "\xEE\xA4\x89"},
- {"merge-left", "\xEE\xA4\x97"},
- {"merge-right", "\xEE\xA4\x98"},
- {"merge-slight-left", "\xEE\xA4\x99"},
- {"merge-slight-right", "\xEE\xA4\x9A"},
- {"merge-straight", "\xEE\xA4\x84"},
- {"new-name-left", "\xEE\xA4\x85"},
- {"new-name-right", "\xEE\xA4\x86"},
- {"new-name-sharp-left", "\xEE\xA4\x9B"},
- {"new-name-sharp-right", "\xEE\xA4\x9C"},
- {"new-name-slight-left", "\xEE\xA4\x87"},
- {"new-name-slight-right", "\xEE\xA4\x88"},
- {"new-name-straight", "\xEE\xA4\x84"},
- {"notification-left", "\xEE\xA4\x85"},
- {"notification-right", "\xEE\xA4\x86"},
- {"notification-sharp-left", "\xEE\xA4\x9B"},
- {"notification-sharp-right", "\xEE\xA4\xA5"},
- {"notification-slight-left", "\xEE\xA4\x87"},
- {"notification-slight-right", "\xEE\xA4\x88"},
- {"notification-straight", "\xEE\xA4\x84"},
- {"off-ramp-left", "\xEE\xA4\x9D"},
- {"off-ramp-right", "\xEE\xA4\x9E"},
- {"off-ramp-slight-left", "\xEE\xA4\x9F"},
- {"off-ramp-slight-right", "\xEE\xA4\xA0"},
- {"on-ramp-left", "\xEE\xA4\x85"},
- {"on-ramp-right", "\xEE\xA4\x86"},
- {"on-ramp-sharp-left", "\xEE\xA4\x9B"},
- {"on-ramp-sharp-right", "\xEE\xA4\xA5"},
- {"on-ramp-slight-left", "\xEE\xA4\x87"},
- {"on-ramp-slight-right", "\xEE\xA4\x88"},
- {"on-ramp-straight", "\xEE\xA4\x84"},
- {"rotary", "\xEE\xA4\xA1"},
- {"rotary-left", "\xEE\xA4\xA2"},
- {"rotary-right", "\xEE\xA4\xA3"},
- {"rotary-sharp-left", "\xEE\xA4\xA4"},
- {"rotary-sharp-right", "\xEE\xA4\xA5"},
- {"rotary-slight-left", "\xEE\xA4\xA6"},
- {"rotary-slight-right", "\xEE\xA4\xA7"},
- {"rotary-straight", "\xEE\xA4\xA8"},
- {"roundabout", "\xEE\xA4\xA1"},
- {"roundabout-left", "\xEE\xA4\xA2"},
- {"roundabout-right", "\xEE\xA4\xA3"},
- {"roundabout-sharp-left", "\xEE\xA4\xA4"},
- {"roundabout-sharp-right", "\xEE\xA4\xA5"},
- {"roundabout-slight-left", "\xEE\xA4\xA6"},
- {"roundabout-slight-right", "\xEE\xA4\xA7"},
- {"roundabout-straight", "\xEE\xA4\xA8"},
- {"turn-left", "\xEE\xA4\x85"},
- {"turn-right", "\xEE\xA4\x86"},
- {"turn-sharp-left", "\xEE\xA4\x9B"},
- {"turn-sharp-right", "\xEE\xA4\xA5"},
- {"turn-slight-left", "\xEE\xA4\x87"},
- {"turn-slight-right", "\xEE\xA4\x88"},
- {"turn-straight", "\xEE\xA4\x84"},
- {"updown", "\xEE\xA4\xA9"},
- {"uturn", "\xEE\xA4\x89"},
+ struct Icon {
+ const char* fileName;
+ int16_t offset;
+ };
+
+ constexpr uint16_t iconHeight = -80;
+ constexpr uint8_t flagIndex = 18;
+ constexpr uint8_t maxIconsPerFile = 25;
+ const char* iconsFile0 = "F:/images/navigation0.bin";
+ const char* iconsFile1 = "F:/images/navigation1.bin";
+
+ constexpr std::array<std::pair<const char*, uint8_t>, 86> iconMap = {{
+ {"arrive-left", 1},
+ {"arrive-right", 2},
+ {"arrive-straight", 0},
+ {"arrive", 0},
+ {"close", 3},
+ {"continue-left", 5},
+ {"continue-right", 6},
+ {"continue-slight-left", 7},
+ {"continue-slight-right", 8},
+ {"continue-straight", 4},
+ {"continue-uturn", 9},
+ {"continue", 4},
+ {"depart-left", 11},
+ {"depart-right", 12},
+ {"depart-straight", 10},
+ {"end-of-road-left", 13},
+ {"end-of-road-right", 14},
+ {"ferry", 15},
+ {"flag", 16},
+ {"fork-left", 18},
+ {"fork-right", 19},
+ {"fork-slight-left", 20},
+ {"fork-slight-right", 21},
+ {"fork-straight", 22},
+ {"invalid", 4},
+ {"invalid-left", 5},
+ {"invalid-right", 6},
+ {"invalid-slight-left", 7},
+ {"invalid-slight-right", 8},
+ {"invalid-straight", 4},
+ {"invalid-uturn", 9},
+ {"merge-left", 23},
+ {"merge-right", 24},
+ {"merge-slight-left", 25},
+ {"merge-slight-right", 26},
+ {"merge-straight", 4},
+ {"new-name-left", 5},
+ {"new-name-right", 6},
+ {"new-name-sharp-left", 27},
+ {"new-name-sharp-right", 28},
+ {"new-name-slight-left", 7},
+ {"new-name-slight-right", 8},
+ {"new-name-straight", 4},
+ {"notification-left", 5},
+ {"notification-right", 6},
+ {"notification-sharp-left", 27},
+ {"notification-sharp-right", 37},
+ {"notification-slight-left", 7},
+ {"notification-slight-right", 8},
+ {"notification-straight", 4},
+ {"off-ramp-left", 29},
+ {"off-ramp-right", 30},
+ {"off-ramp-slight-left", 31},
+ {"off-ramp-slight-right", 32},
+ {"on-ramp-left", 5},
+ {"on-ramp-right", 6},
+ {"on-ramp-sharp-left", 27},
+ {"on-ramp-sharp-right", 37},
+ {"on-ramp-slight-left", 7},
+ {"on-ramp-slight-right", 8},
+ {"on-ramp-straight", 4},
+ {"rotary", 33},
+ {"rotary-left", 34},
+ {"rotary-right", 35},
+ {"rotary-sharp-left", 36},
+ {"rotary-sharp-right", 37},
+ {"rotary-slight-left", 38},
+ {"rotary-slight-right", 39},
+ {"rotary-straight", 40},
+ {"roundabout", 33},
+ {"roundabout-left", 34},
+ {"roundabout-right", 35},
+ {"roundabout-sharp-left", 36},
+ {"roundabout-sharp-right", 37},
+ {"roundabout-slight-left", 38},
+ {"roundabout-slight-right", 39},
+ {"roundabout-straight", 40},
+ {"turn-left", 5},
+ {"turn-right", 6},
+ {"turn-sharp-left", 27},
+ {"turn-sharp-right", 37},
+ {"turn-slight-left", 7},
+ {"turn-slight-right", 8},
+ {"turn-straight", 4},
+ {"updown", 41},
+ {"uturn", 9},
}};
- const char* iconForName(const std::string& icon) {
- for (auto iter : m_iconMap) {
+ Icon GetIcon(uint8_t index) {
+ if (index < maxIconsPerFile) {
+ return {iconsFile0, static_cast<int16_t>(iconHeight * index)};
+ }
+ return {iconsFile1, static_cast<int16_t>(iconHeight * (index - maxIconsPerFile))};
+ }
+
+ Icon GetIcon(const std::string& icon) {
+ for (const auto& iter : iconMap) {
if (iter.first == icon) {
- return iter.second;
+ return GetIcon(iter.second);
}
}
- return "\xEE\xA4\x90";
+ return GetIcon(flagIndex);
}
}
@@ -130,27 +191,33 @@ namespace {
*
*/
Navigation::Navigation(Pinetime::Controllers::NavigationService& nav) : navService(nav) {
-
- imgFlag = lv_label_create(lv_scr_act(), nullptr);
- lv_obj_set_style_local_text_font(imgFlag, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_navi_80);
- lv_obj_set_style_local_text_color(imgFlag, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_CYAN);
- lv_label_set_text_static(imgFlag, iconForName("flag"));
+ const auto& image = GetIcon("flag");
+ imgFlag = lv_img_create(lv_scr_act(), nullptr);
+ lv_img_set_auto_size(imgFlag, false);
+ lv_obj_set_size(imgFlag, 80, 80);
+ lv_img_set_src(imgFlag, image.fileName);
+ lv_img_set_offset_x(imgFlag, 0);
+ lv_img_set_offset_y(imgFlag, image.offset);
+ lv_obj_set_style_local_image_recolor_opa(imgFlag, LV_IMG_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_COVER);
+ lv_obj_set_style_local_image_recolor(imgFlag, LV_IMG_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_CYAN);
lv_obj_align(imgFlag, nullptr, LV_ALIGN_CENTER, 0, -60);
txtNarrative = lv_label_create(lv_scr_act(), nullptr);
- lv_label_set_long_mode(txtNarrative, LV_LABEL_LONG_BREAK);
+ lv_label_set_long_mode(txtNarrative, LV_LABEL_LONG_DOT);
lv_obj_set_width(txtNarrative, LV_HOR_RES);
+ lv_obj_set_height(txtNarrative, 80);
lv_label_set_text_static(txtNarrative, "Navigation");
lv_label_set_align(txtNarrative, LV_LABEL_ALIGN_CENTER);
- lv_obj_align(txtNarrative, nullptr, LV_ALIGN_CENTER, 0, 10);
+ lv_obj_align(txtNarrative, nullptr, LV_ALIGN_CENTER, 0, 30);
txtManDist = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_long_mode(txtManDist, LV_LABEL_LONG_BREAK);
lv_obj_set_style_local_text_color(txtManDist, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
+ lv_obj_set_style_local_text_font(txtManDist, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42);
lv_obj_set_width(txtManDist, LV_HOR_RES);
lv_label_set_text_static(txtManDist, "--M");
lv_label_set_align(txtManDist, LV_LABEL_ALIGN_CENTER);
- lv_obj_align(txtManDist, nullptr, LV_ALIGN_CENTER, 0, 60);
+ lv_obj_align(txtManDist, nullptr, LV_ALIGN_CENTER, 0, 90);
// Route Progress
barProgress = lv_bar_create(lv_scr_act(), nullptr);
@@ -173,7 +240,11 @@ Navigation::~Navigation() {
void Navigation::Refresh() {
if (flag != navService.getFlag()) {
flag = navService.getFlag();
- lv_label_set_text_static(imgFlag, iconForName(flag));
+ const auto& image = GetIcon(flag);
+ lv_img_set_src(imgFlag, image.fileName);
+ lv_obj_set_style_local_image_recolor_opa(imgFlag, LV_IMG_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_COVER);
+ lv_obj_set_style_local_image_recolor(imgFlag, LV_IMG_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_CYAN);
+ lv_img_set_offset_y(imgFlag, image.offset);
}
if (narrative != navService.getNarrative()) {
@@ -196,3 +267,19 @@ void Navigation::Refresh() {
}
}
}
+
+bool Navigation::IsAvailable(Pinetime::Controllers::FS& filesystem) {
+ lfs_file file = {};
+
+ if (filesystem.FileOpen(&file, "/images/navigation0.bin", LFS_O_RDONLY) < 0) {
+ return false;
+ }
+ filesystem.FileClose(&file);
+
+ if (filesystem.FileOpen(&file, "/images/navigation1.bin", LFS_O_RDONLY) < 0) {
+ return false;
+ }
+ filesystem.FileClose(&file);
+
+ return true;
+}
diff --git a/src/displayapp/screens/Navigation.h b/src/displayapp/screens/Navigation.h
index 6495edb2..5c7a0429 100644
--- a/src/displayapp/screens/Navigation.h
+++ b/src/displayapp/screens/Navigation.h
@@ -22,20 +22,25 @@
#include <string>
#include "displayapp/screens/Screen.h"
#include <array>
+#include "displayapp/apps/Apps.h"
+#include "displayapp/Controllers.h"
+#include "Symbols.h"
namespace Pinetime {
namespace Controllers {
class NavigationService;
+ class FS;
}
namespace Applications {
namespace Screens {
class Navigation : public Screen {
public:
- Navigation(Pinetime::Controllers::NavigationService& nav);
+ explicit Navigation(Pinetime::Controllers::NavigationService& nav);
~Navigation() override;
void Refresh() override;
+ static bool IsAvailable(Pinetime::Controllers::FS& filesystem);
private:
lv_obj_t* imgFlag;
@@ -48,10 +53,20 @@ namespace Pinetime {
std::string flag;
std::string narrative;
std::string manDist;
- int progress;
+ int progress = 0;
lv_task_t* taskRefresh;
};
}
+
+ template <>
+ struct AppTraits<Apps::Navigation> {
+ static constexpr Apps app = Apps::Navigation;
+ static constexpr const char* icon = Screens::Symbols::map;
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::Navigation(*controllers.navigationService);
+ };
+ };
}
}
diff --git a/src/displayapp/screens/Notifications.cpp b/src/displayapp/screens/Notifications.cpp
index 037c43a7..837c4683 100644
--- a/src/displayapp/screens/Notifications.cpp
+++ b/src/displayapp/screens/Notifications.cpp
@@ -20,7 +20,7 @@ Notifications::Notifications(DisplayApp* app,
notificationManager {notificationManager},
alertNotificationService {alertNotificationService},
motorController {motorController},
- systemTask {systemTask},
+ wakeLock(systemTask),
mode {mode} {
notificationManager.ClearNewNotificationFlag();
@@ -40,7 +40,7 @@ Notifications::Notifications(DisplayApp* app,
validDisplay = false;
}
if (mode == Modes::Preview) {
- systemTask.PushMessage(System::Messages::DisableSleeping);
+ wakeLock.Lock();
if (notification.category == Controllers::NotificationManager::Categories::IncomingCall) {
motorController.StartRinging();
} else {
@@ -65,7 +65,6 @@ Notifications::~Notifications() {
lv_task_del(taskRefresh);
// make sure we stop any vibrations before exiting
motorController.StopRinging();
- systemTask.PushMessage(System::Messages::EnableSleeping);
lv_obj_clean(lv_scr_act());
}
@@ -82,7 +81,6 @@ void Notifications::Refresh() {
} else if (mode == Modes::Preview && dismissingNotification) {
running = false;
- currentItem = std::make_unique<NotificationItem>(alertNotificationService, motorController);
} else if (dismissingNotification) {
dismissingNotification = false;
@@ -113,15 +111,15 @@ void Notifications::Refresh() {
alertNotificationService,
motorController);
} else {
- currentItem = std::make_unique<NotificationItem>(alertNotificationService, motorController);
+ running = false;
}
}
- running = currentItem->IsRunning() && running;
+ running = running && currentItem->IsRunning();
}
void Notifications::OnPreviewInteraction() {
- systemTask.PushMessage(System::Messages::EnableSleeping);
+ wakeLock.Release();
motorController.StopRinging();
if (timeoutLine != nullptr) {
lv_obj_del(timeoutLine);
@@ -173,7 +171,9 @@ bool Notifications::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
} else if (nextMessage.valid) {
currentId = nextMessage.id;
} else {
- // don't update id, won't be found be refresh and try to load latest message or no message box
+ // don't update id, notification manager will try to fetch
+ // but not find it. Refresh will try to load latest message
+ // or dismiss to watchface
}
DismissToBlack();
return true;
@@ -246,8 +246,8 @@ namespace {
Notifications::NotificationItem::NotificationItem(Pinetime::Controllers::AlertNotificationService& alertNotificationService,
Pinetime::Controllers::MotorController& motorController)
- : NotificationItem("Notification",
- "No notification to display",
+ : NotificationItem("Notifications",
+ "No notifications to display",
0,
Controllers::NotificationManager::Categories::Unknown,
0,
diff --git a/src/displayapp/screens/Notifications.h b/src/displayapp/screens/Notifications.h
index 114316b3..8488dc5b 100644
--- a/src/displayapp/screens/Notifications.h
+++ b/src/displayapp/screens/Notifications.h
@@ -8,6 +8,7 @@
#include "components/ble/NotificationManager.h"
#include "components/motor/MotorController.h"
#include "systemtask/SystemTask.h"
+#include "systemtask/WakeLock.h"
namespace Pinetime {
namespace Controllers {
@@ -73,7 +74,7 @@ namespace Pinetime {
Pinetime::Controllers::NotificationManager& notificationManager;
Pinetime::Controllers::AlertNotificationService& alertNotificationService;
Pinetime::Controllers::MotorController& motorController;
- System::SystemTask& systemTask;
+ System::WakeLock wakeLock;
Modes mode = Modes::Normal;
std::unique_ptr<NotificationItem> currentItem;
Pinetime::Controllers::NotificationManager::Notification::Id currentId;
diff --git a/src/displayapp/screens/Paddle.h b/src/displayapp/screens/Paddle.h
index 33dac191..586cccf4 100644
--- a/src/displayapp/screens/Paddle.h
+++ b/src/displayapp/screens/Paddle.h
@@ -3,6 +3,9 @@
#include <lvgl/lvgl.h>
#include <cstdint>
#include "displayapp/screens/Screen.h"
+#include "displayapp/apps/Apps.h"
+#include "displayapp/Controllers.h"
+#include "Symbols.h"
namespace Pinetime {
namespace Components {
@@ -45,5 +48,15 @@ namespace Pinetime {
lv_task_t* taskRefresh;
};
}
+
+ template <>
+ struct AppTraits<Apps::Paddle> {
+ static constexpr Apps app = Apps::Paddle;
+ static constexpr const char* icon = Screens::Symbols::paddle;
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::Paddle(controllers.lvgl);
+ };
+ };
}
}
diff --git a/src/displayapp/screens/Screen.h b/src/displayapp/screens/Screen.h
index 09bd6131..9f6e0ede 100644
--- a/src/displayapp/screens/Screen.h
+++ b/src/displayapp/screens/Screen.h
@@ -9,41 +9,6 @@ namespace Pinetime {
class DisplayApp;
namespace Screens {
-
- template <class T>
- class DirtyValue {
- public:
- DirtyValue() = default; // Use NSDMI
-
- explicit DirtyValue(T const& v) : value {v} {
- } // Use MIL and const-lvalue-ref
-
- bool IsUpdated() {
- if (this->isUpdated) {
- this->isUpdated = false;
- return true;
- }
- return false;
- }
-
- T const& Get() {
- this->isUpdated = false;
- return value;
- } // never expose a non-const lvalue-ref
-
- DirtyValue& operator=(const T& other) {
- if (this->value != other) {
- this->value = other;
- this->isUpdated = true;
- }
- return *this;
- }
-
- private:
- T value {}; // NSDMI - default initialise type
- bool isUpdated {true}; // NSDMI - use brace initialisation
- };
-
class Screen {
private:
virtual void Refresh() {
diff --git a/src/displayapp/screens/Steps.h b/src/displayapp/screens/Steps.h
index 5dc07eff..6443582f 100644
--- a/src/displayapp/screens/Steps.h
+++ b/src/displayapp/screens/Steps.h
@@ -4,6 +4,9 @@
#include <lvgl/lvgl.h>
#include "displayapp/screens/Screen.h"
#include <components/motion/MotionController.h>
+#include "displayapp/apps/Apps.h"
+#include "displayapp/Controllers.h"
+#include "Symbols.h"
namespace Pinetime {
@@ -39,5 +42,15 @@ namespace Pinetime {
lv_task_t* taskRefresh;
};
}
+
+ template <>
+ struct AppTraits<Apps::Steps> {
+ static constexpr Apps app = Apps::Steps;
+ static constexpr const char* icon = Screens::Symbols::shoe;
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::Steps(controllers.motionController, controllers.settingsController);
+ };
+ };
}
}
diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp
index 72904c88..ff852beb 100644
--- a/src/displayapp/screens/StopWatch.cpp
+++ b/src/displayapp/screens/StopWatch.cpp
@@ -12,8 +12,9 @@ namespace {
const int hundredths = (timeElapsedCentis % 100);
const int secs = (timeElapsedCentis / 100) % 60;
- const int mins = (timeElapsedCentis / 100) / 60;
- return TimeSeparated_t {mins, secs, hundredths};
+ const int mins = ((timeElapsedCentis / 100) / 60) % 60;
+ const int hours = ((timeElapsedCentis / 100) / 60) / 60;
+ return TimeSeparated_t {hours, mins, secs, hundredths};
}
void play_pause_event_handler(lv_obj_t* obj, lv_event_t event) {
@@ -33,7 +34,7 @@ namespace {
constexpr TickType_t blinkInterval = pdMS_TO_TICKS(1000);
}
-StopWatch::StopWatch(System::SystemTask& systemTask) : systemTask {systemTask} {
+StopWatch::StopWatch(System::SystemTask& systemTask) : wakeLock(systemTask) {
static constexpr uint8_t btnWidth = 115;
static constexpr uint8_t btnHeight = 80;
btnPlayPause = lv_btn_create(lv_scr_act(), nullptr);
@@ -78,7 +79,6 @@ StopWatch::StopWatch(System::SystemTask& systemTask) : systemTask {systemTask} {
StopWatch::~StopWatch() {
lv_task_del(taskRefresh);
- systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
lv_obj_clean(lv_scr_act());
}
@@ -110,6 +110,12 @@ void StopWatch::SetInterfaceStopped() {
lv_label_set_text_static(time, "00:00");
lv_label_set_text_static(msecTime, "00");
+ if (isHoursLabelUpdated) {
+ lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76);
+ lv_obj_realign(time);
+ isHoursLabelUpdated = false;
+ }
+
lv_label_set_text_static(lapText, "");
lv_label_set_text_static(txtPlayPause, Symbols::play);
lv_label_set_text_static(txtStopLap, Symbols::lapsFlag);
@@ -128,7 +134,7 @@ void StopWatch::Start() {
SetInterfaceRunning();
startTime = xTaskGetTickCount();
currentState = States::Running;
- systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
+ wakeLock.Lock();
}
void StopWatch::Pause() {
@@ -138,7 +144,7 @@ void StopWatch::Pause() {
oldTimeElapsed = laps[lapsDone];
blinkTime = xTaskGetTickCount() + blinkInterval;
currentState = States::Halted;
- systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
+ wakeLock.Release();
}
void StopWatch::Refresh() {
@@ -146,7 +152,16 @@ void StopWatch::Refresh() {
laps[lapsDone] = oldTimeElapsed + xTaskGetTickCount() - startTime;
TimeSeparated_t currentTimeSeparated = convertTicksToTimeSegments(laps[lapsDone]);
- lv_label_set_text_fmt(time, "%02d:%02d", currentTimeSeparated.mins, currentTimeSeparated.secs);
+ if (currentTimeSeparated.hours == 0) {
+ lv_label_set_text_fmt(time, "%02d:%02d", currentTimeSeparated.mins, currentTimeSeparated.secs);
+ } else {
+ lv_label_set_text_fmt(time, "%02d:%02d:%02d", currentTimeSeparated.hours, currentTimeSeparated.mins, currentTimeSeparated.secs);
+ if (!isHoursLabelUpdated) {
+ lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42);
+ lv_obj_realign(time);
+ isHoursLabelUpdated = true;
+ }
+ }
lv_label_set_text_fmt(msecTime, "%02d", currentTimeSeparated.hundredths);
} else if (currentState == States::Halted) {
const TickType_t currentTime = xTaskGetTickCount();
@@ -182,8 +197,12 @@ void StopWatch::stopLapBtnEventHandler() {
continue;
}
TimeSeparated_t times = convertTicksToTimeSegments(laps[i]);
- char buffer[16];
- sprintf(buffer, "#%2d %2d:%02d.%02d\n", i + 1, times.mins, times.secs, times.hundredths);
+ char buffer[17];
+ if (times.hours == 0) {
+ snprintf(buffer, sizeof(buffer), "#%2d %2d:%02d.%02d\n", i + 1, times.mins, times.secs, times.hundredths);
+ } else {
+ snprintf(buffer, sizeof(buffer), "#%2d %2d:%02d:%02d.%02d\n", i + 1, times.hours, times.mins, times.secs, times.hundredths);
+ }
lv_label_ins_text(lapText, LV_LABEL_POS_LAST, buffer);
}
} else if (currentState == States::Halted) {
diff --git a/src/displayapp/screens/StopWatch.h b/src/displayapp/screens/StopWatch.h
index 409e3a19..55a178dc 100644
--- a/src/displayapp/screens/StopWatch.h
+++ b/src/displayapp/screens/StopWatch.h
@@ -7,48 +7,68 @@
#include "portmacro_cmsis.h"
#include "systemtask/SystemTask.h"
+#include "systemtask/WakeLock.h"
+#include "displayapp/apps/Apps.h"
+#include "displayapp/Controllers.h"
+#include "Symbols.h"
-namespace Pinetime::Applications::Screens {
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
- enum class States { Init, Running, Halted };
+ enum class States { Init, Running, Halted };
- struct TimeSeparated_t {
- int mins;
- int secs;
- int hundredths;
- };
+ struct TimeSeparated_t {
+ int hours;
+ int mins;
+ int secs;
+ int hundredths;
+ };
- class StopWatch : public Screen {
- public:
- explicit StopWatch(System::SystemTask& systemTask);
- ~StopWatch() override;
- void Refresh() override;
+ class StopWatch : public Screen {
+ public:
+ explicit StopWatch(System::SystemTask& systemTask);
+ ~StopWatch() override;
+ void Refresh() override;
- void playPauseBtnEventHandler();
- void stopLapBtnEventHandler();
- bool OnButtonPushed() override;
+ void playPauseBtnEventHandler();
+ void stopLapBtnEventHandler();
+ bool OnButtonPushed() override;
- private:
- void SetInterfacePaused();
- void SetInterfaceRunning();
- void SetInterfaceStopped();
+ private:
+ void SetInterfacePaused();
+ void SetInterfaceRunning();
+ void SetInterfaceStopped();
- void Reset();
- void Start();
- void Pause();
+ void Reset();
+ void Start();
+ void Pause();
- Pinetime::System::SystemTask& systemTask;
- States currentState = States::Init;
- TickType_t startTime;
- TickType_t oldTimeElapsed = 0;
- TickType_t blinkTime = 0;
- static constexpr int maxLapCount = 20;
- TickType_t laps[maxLapCount + 1];
- static constexpr int displayedLaps = 2;
- int lapsDone = 0;
- lv_obj_t *time, *msecTime, *btnPlayPause, *btnStopLap, *txtPlayPause, *txtStopLap;
- lv_obj_t* lapText;
+ Pinetime::System::WakeLock wakeLock;
+ States currentState = States::Init;
+ TickType_t startTime;
+ TickType_t oldTimeElapsed = 0;
+ TickType_t blinkTime = 0;
+ static constexpr int maxLapCount = 20;
+ TickType_t laps[maxLapCount + 1];
+ static constexpr int displayedLaps = 2;
+ int lapsDone = 0;
+ lv_obj_t *time, *msecTime, *btnPlayPause, *btnStopLap, *txtPlayPause, *txtStopLap;
+ lv_obj_t* lapText;
+ bool isHoursLabelUpdated = false;
- lv_task_t* taskRefresh;
- };
+ lv_task_t* taskRefresh;
+ };
+ }
+
+ template <>
+ struct AppTraits<Apps::StopWatch> {
+ static constexpr Apps app = Apps::StopWatch;
+ static constexpr const char* icon = Screens::Symbols::stopWatch;
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::StopWatch(*controllers.systemTask);
+ };
+ };
+ }
}
diff --git a/src/displayapp/screens/Symbols.h b/src/displayapp/screens/Symbols.h
index 934cdc3f..40699b3d 100644
--- a/src/displayapp/screens/Symbols.h
+++ b/src/displayapp/screens/Symbols.h
@@ -11,6 +11,7 @@ namespace Pinetime {
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* bell = "\xEF\x83\xB3";
static constexpr const char* info = "\xEF\x84\xA9";
static constexpr const char* list = "\xEF\x80\xBA";
static constexpr const char* sun = "\xEF\x86\x85";
@@ -34,9 +35,24 @@ namespace Pinetime {
static constexpr const char* hourGlass = "\xEF\x89\x92";
static constexpr const char* lapsFlag = "\xEF\x80\xA4";
static constexpr const char* drum = "\xEF\x95\xA9";
+ static constexpr const char* dice = "\xEF\x94\xA2";
static constexpr const char* eye = "\xEF\x81\xAE";
static constexpr const char* home = "\xEF\x80\x95";
static constexpr const char* sleep = "\xEE\xBD\x84";
+ static constexpr const char* calculator = "\xEF\x87\xAC";
+ static constexpr const char* backspace = "\xEF\x95\x9A";
+
+ // fontawesome_weathericons.c
+ // static constexpr const char* sun = "\xEF\x86\x85";
+ static constexpr const char* cloudSun = "\xEF\x9B\x84";
+ static constexpr const char* cloudSunRain = "\xEF\x9D\x83";
+ static constexpr const char* cloudShowersHeavy = "\xEF\x9D\x80";
+ static constexpr const char* smog = "\xEF\x9D\x9F";
+ static constexpr const char* cloud = "\xEF\x83\x82";
+ static constexpr const char* cloudMeatball = "\xEF\x9C\xBB";
+ static constexpr const char* bolt = "\xEF\x83\xA7";
+ static constexpr const char* snowflake = "\xEF\x8B\x9C";
+ static constexpr const char* ban = "\xEF\x81\x9E";
// lv_font_sys_48.c
static constexpr const char* settings = "\xEE\xA2\xB8";
diff --git a/src/displayapp/screens/SystemInfo.cpp b/src/displayapp/screens/SystemInfo.cpp
index 511ecf50..2392f3be 100644
--- a/src/displayapp/screens/SystemInfo.cpp
+++ b/src/displayapp/screens/SystemInfo.cpp
@@ -38,15 +38,16 @@ SystemInfo::SystemInfo(Pinetime::Applications::DisplayApp* app,
const Pinetime::Controllers::Ble& bleController,
const Pinetime::Drivers::Watchdog& watchdog,
Pinetime::Controllers::MotionController& motionController,
- const Pinetime::Drivers::Cst816S& touchPanel)
- : app {app},
- dateTimeController {dateTimeController},
+ const Pinetime::Drivers::Cst816S& touchPanel,
+ const Pinetime::Drivers::SpiNorFlash& spiNorFlash)
+ : dateTimeController {dateTimeController},
batteryController {batteryController},
brightnessController {brightnessController},
bleController {bleController},
watchdog {watchdog},
motionController {motionController},
touchPanel {touchPanel},
+ spiNorFlash {spiNorFlash},
screens {app,
0,
{[this]() -> std::unique_ptr<Screen> {
@@ -101,24 +102,24 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen1() {
std::unique_ptr<Screen> SystemInfo::CreateScreen2() {
auto batteryPercent = batteryController.PercentRemaining();
const auto* resetReason = [this]() {
- switch (watchdog.ResetReason()) {
- case Drivers::Watchdog::ResetReasons::Watchdog:
+ switch (watchdog.GetResetReason()) {
+ case Drivers::Watchdog::ResetReason::Watchdog:
return "wtdg";
- case Drivers::Watchdog::ResetReasons::HardReset:
+ case Drivers::Watchdog::ResetReason::HardReset:
return "hardr";
- case Drivers::Watchdog::ResetReasons::NFC:
+ case Drivers::Watchdog::ResetReason::NFC:
return "nfc";
- case Drivers::Watchdog::ResetReasons::SoftReset:
+ case Drivers::Watchdog::ResetReason::SoftReset:
return "softr";
- case Drivers::Watchdog::ResetReasons::CpuLockup:
+ case Drivers::Watchdog::ResetReason::CpuLockup:
return "cpulock";
- case Drivers::Watchdog::ResetReasons::SystemOff:
+ case Drivers::Watchdog::ResetReason::SystemOff:
return "off";
- case Drivers::Watchdog::ResetReasons::LpComp:
+ case Drivers::Watchdog::ResetReason::LpComp:
return "lpcomp";
- case Drivers::Watchdog::ResetReasons::DebugInterface:
+ case Drivers::Watchdog::ResetReason::DebugInterface:
return "dbg";
- case Drivers::Watchdog::ResetReasons::ResetPin:
+ case Drivers::Watchdog::ResetReason::ResetPin:
return "rst";
default:
return "?";
@@ -177,6 +178,8 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen2() {
return std::make_unique<Screens::Label>(1, 5, label);
}
+extern int mallocFailedCount;
+extern int stackOverflowCount;
std::unique_ptr<Screen> SystemInfo::CreateScreen3() {
lv_mem_monitor_t mon;
lv_mem_monitor(&mon);
@@ -184,26 +187,32 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen3() {
lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_recolor(label, true);
const auto& bleAddr = bleController.Address();
+ auto spiFlashId = spiNorFlash.GetIdentification();
lv_label_set_text_fmt(label,
"#808080 BLE MAC#\n"
- " %02x:%02x:%02x:%02x:%02x:%02x"
+ " %02x:%02x:%02x:%02x:%02x:%02x\n"
"\n"
- "#808080 LVGL Memory#\n"
- " #808080 used# %d (%d%%)\n"
- " #808080 max used# %lu\n"
- " #808080 frag# %d%%\n"
- " #808080 free# %d",
+ "#808080 SPI Flash# %02x-%02x-%02x\n"
+ "\n"
+ "#808080 Memory heap#\n"
+ " #808080 Free# %d/%d\n"
+ " #808080 Min free# %d\n"
+ " #808080 Alloc err# %d\n"
+ " #808080 Ovrfl err# %d",
bleAddr[5],
bleAddr[4],
bleAddr[3],
bleAddr[2],
bleAddr[1],
bleAddr[0],
- static_cast<int>(mon.total_size - mon.free_size),
- mon.used_pct,
- mon.max_used,
- mon.frag_pct,
- static_cast<int>(mon.free_biggest_size));
+ spiFlashId.manufacturer,
+ spiFlashId.type,
+ spiFlashId.density,
+ xPortGetFreeHeapSize(),
+ xPortGetHeapSize(),
+ xPortGetMinimumEverFreeHeapSize(),
+ mallocFailedCount,
+ stackOverflowCount);
lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
return std::make_unique<Screens::Label>(2, 5, label);
}
@@ -232,11 +241,16 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen4() {
lv_table_set_col_width(infoTask, 3, 90);
auto nb = uxTaskGetSystemState(tasksStatus, maxTaskCount, nullptr);
+// g++ emits a spurious warning (and thus error because we compile with -Werror)
+// due to the way std::sort is implemented
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
std::sort(tasksStatus, tasksStatus + nb, sortById);
+#pragma GCC diagnostic pop
for (uint8_t i = 0; i < nb && i < maxTaskCount; i++) {
- char buffer[7] = {0};
+ char buffer[11] = {0};
- sprintf(buffer, "%lu", tasksStatus[i].xTaskNumber);
+ snprintf(buffer, sizeof(buffer), "%lu", tasksStatus[i].xTaskNumber);
lv_table_set_cell_value(infoTask, i + 1, 0, buffer);
switch (tasksStatus[i].eCurrentState) {
case eReady:
@@ -260,9 +274,9 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen4() {
lv_table_set_cell_value(infoTask, i + 1, 1, buffer);
lv_table_set_cell_value(infoTask, i + 1, 2, tasksStatus[i].pcTaskName);
if (tasksStatus[i].usStackHighWaterMark < 20) {
- sprintf(buffer, "%d low", tasksStatus[i].usStackHighWaterMark);
+ snprintf(buffer, sizeof(buffer), "%" PRIu16 " low", tasksStatus[i].usStackHighWaterMark);
} else {
- sprintf(buffer, "%d", tasksStatus[i].usStackHighWaterMark);
+ snprintf(buffer, sizeof(buffer), "%" PRIu16, tasksStatus[i].usStackHighWaterMark);
}
lv_table_set_cell_value(infoTask, i + 1, 3, buffer);
}
diff --git a/src/displayapp/screens/SystemInfo.h b/src/displayapp/screens/SystemInfo.h
index 199af51e..301662dc 100644
--- a/src/displayapp/screens/SystemInfo.h
+++ b/src/displayapp/screens/SystemInfo.h
@@ -29,12 +29,12 @@ namespace Pinetime {
const Pinetime::Controllers::Ble& bleController,
const Pinetime::Drivers::Watchdog& watchdog,
Pinetime::Controllers::MotionController& motionController,
- const Pinetime::Drivers::Cst816S& touchPanel);
+ const Pinetime::Drivers::Cst816S& touchPanel,
+ const Pinetime::Drivers::SpiNorFlash& spiNorFlash);
~SystemInfo() override;
bool OnTouchEvent(TouchEvents event) override;
private:
- DisplayApp* app;
Pinetime::Controllers::DateTime& dateTimeController;
const Pinetime::Controllers::Battery& batteryController;
Pinetime::Controllers::BrightnessController& brightnessController;
@@ -42,6 +42,7 @@ namespace Pinetime {
const Pinetime::Drivers::Watchdog& watchdog;
Pinetime::Controllers::MotionController& motionController;
const Pinetime::Drivers::Cst816S& touchPanel;
+ const Pinetime::Drivers::SpiNorFlash& spiNorFlash;
ScreenList<5> screens;
diff --git a/src/displayapp/screens/Tile.cpp b/src/displayapp/screens/Tile.cpp
index 1266f379..7c585a4b 100644
--- a/src/displayapp/screens/Tile.cpp
+++ b/src/displayapp/screens/Tile.cpp
@@ -1,5 +1,4 @@
#include "displayapp/screens/Tile.h"
-#include "displayapp/DisplayApp.h"
#include "displayapp/screens/BatteryIcon.h"
#include "components/ble/BleController.h"
#include "displayapp/InfiniTimeTheme.h"
@@ -30,9 +29,13 @@ Tile::Tile(uint8_t screenID,
Controllers::Settings& settingsController,
const Controllers::Battery& batteryController,
const Controllers::Ble& bleController,
+ const Controllers::AlarmController& alarmController,
Controllers::DateTime& dateTimeController,
std::array<Applications, 6>& applications)
- : app {app}, dateTimeController {dateTimeController}, pageIndicator(screenID, numScreens), statusIcons(batteryController, bleController) {
+ : app {app},
+ dateTimeController {dateTimeController},
+ pageIndicator(screenID, numScreens),
+ statusIcons(batteryController, bleController, alarmController) {
settingsController.SetAppMenu(screenID);
@@ -76,7 +79,7 @@ Tile::Tile(uint8_t screenID,
for (uint8_t i = 0; i < 6; i++) {
lv_btnmatrix_set_btn_ctrl(btnm1, i, LV_BTNMATRIX_CTRL_CLICK_TRIG);
- if (applications[i].application == Apps::None) {
+ if (applications[i].application == Apps::None || !applications[i].enabled) {
lv_btnmatrix_set_btn_ctrl(btnm1, i, LV_BTNMATRIX_CTRL_DISABLED);
}
}
diff --git a/src/displayapp/screens/Tile.h b/src/displayapp/screens/Tile.h
index 91acb26c..c16151d0 100644
--- a/src/displayapp/screens/Tile.h
+++ b/src/displayapp/screens/Tile.h
@@ -4,7 +4,7 @@
#include <cstdint>
#include <memory>
#include "displayapp/screens/Screen.h"
-#include "displayapp/Apps.h"
+#include "displayapp/apps/Apps.h"
#include "components/datetime/DateTimeController.h"
#include "components/settings/Settings.h"
#include "components/battery/BatteryController.h"
@@ -19,6 +19,7 @@ namespace Pinetime {
struct Applications {
const char* icon;
Pinetime::Applications::Apps application;
+ bool enabled;
};
explicit Tile(uint8_t screenID,
@@ -27,6 +28,7 @@ namespace Pinetime {
Controllers::Settings& settingsController,
const Controllers::Battery& batteryController,
const Controllers::Ble& bleController,
+ const Controllers::AlarmController& alarmController,
Controllers::DateTime& dateTimeController,
std::array<Applications, 6>& applications);
diff --git a/src/displayapp/screens/Timer.cpp b/src/displayapp/screens/Timer.cpp
index df78a5a0..31cde733 100644
--- a/src/displayapp/screens/Timer.cpp
+++ b/src/displayapp/screens/Timer.cpp
@@ -17,7 +17,7 @@ static void btnEventHandler(lv_obj_t* obj, lv_event_t event) {
}
}
-Timer::Timer(Controllers::TimerController& timerController) : timerController {timerController} {
+Timer::Timer(Controllers::Timer& timerController) : timer {timerController} {
lv_obj_t* colonLabel = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(colonLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76);
@@ -59,10 +59,10 @@ Timer::Timer(Controllers::TimerController& timerController) : timerController {t
lv_obj_set_event_cb(btnPlayPause, btnEventHandler);
lv_obj_set_size(btnPlayPause, LV_HOR_RES, 50);
- txtPlayPause = lv_label_create(lv_scr_act(), nullptr);
- lv_obj_align(txtPlayPause, btnPlayPause, LV_ALIGN_CENTER, 0, 0);
+ // Create the label as a child of the button so it stays centered by default
+ txtPlayPause = lv_label_create(btnPlayPause, nullptr);
- if (timerController.IsRunning()) {
+ if (timer.IsRunning()) {
SetTimerRunning();
} else {
SetTimerStopped();
@@ -85,7 +85,7 @@ void Timer::MaskReset() {
buttonPressing = false;
// A click event is processed before a release event,
// so the release event would override the "Pause" text without this check
- if (!timerController.IsRunning()) {
+ if (!timer.IsRunning()) {
lv_label_set_text_static(txtPlayPause, "Start");
}
maskPosition = 0;
@@ -103,10 +103,8 @@ void Timer::UpdateMask() {
}
void Timer::Refresh() {
- if (timerController.IsRunning()) {
- auto secondsRemaining = std::chrono::duration_cast<std::chrono::seconds>(timerController.GetTimeRemaining());
- minuteCounter.SetValue(secondsRemaining.count() / 60);
- secondCounter.SetValue(secondsRemaining.count() % 60);
+ if (timer.IsRunning()) {
+ DisplayTime();
} else if (buttonPressing && xTaskGetTickCount() > pressTime + pdMS_TO_TICKS(150)) {
lv_label_set_text_static(txtPlayPause, "Reset");
maskPosition += 15;
@@ -119,6 +117,14 @@ void Timer::Refresh() {
}
}
+void Timer::DisplayTime() {
+ displaySeconds = std::chrono::duration_cast<std::chrono::seconds>(timer.GetTimeRemaining());
+ if (displaySeconds.IsUpdated()) {
+ minuteCounter.SetValue(displaySeconds.Get().count() / 60);
+ secondCounter.SetValue(displaySeconds.Get().count() % 60);
+ }
+}
+
void Timer::SetTimerRunning() {
minuteCounter.HideControls();
secondCounter.HideControls();
@@ -132,22 +138,19 @@ void Timer::SetTimerStopped() {
}
void Timer::ToggleRunning() {
- if (timerController.IsRunning()) {
- auto secondsRemaining = std::chrono::duration_cast<std::chrono::seconds>(timerController.GetTimeRemaining());
- minuteCounter.SetValue(secondsRemaining.count() / 60);
- secondCounter.SetValue(secondsRemaining.count() % 60);
- timerController.StopTimer();
+ if (timer.IsRunning()) {
+ DisplayTime();
+ timer.StopTimer();
SetTimerStopped();
} else if (secondCounter.GetValue() + minuteCounter.GetValue() > 0) {
auto timerDuration = std::chrono::minutes(minuteCounter.GetValue()) + std::chrono::seconds(secondCounter.GetValue());
- timerController.StartTimer(timerDuration);
+ timer.StartTimer(timerDuration);
Refresh();
SetTimerRunning();
}
}
void Timer::Reset() {
- minuteCounter.SetValue(0);
- secondCounter.SetValue(0);
+ DisplayTime();
SetTimerStopped();
}
diff --git a/src/displayapp/screens/Timer.h b/src/displayapp/screens/Timer.h
index a6e26063..a07c729b 100644
--- a/src/displayapp/screens/Timer.h
+++ b/src/displayapp/screens/Timer.h
@@ -1,45 +1,60 @@
#pragma once
#include "displayapp/screens/Screen.h"
-#include "components/datetime/DateTimeController.h"
#include "systemtask/SystemTask.h"
#include "displayapp/LittleVgl.h"
#include "displayapp/widgets/Counter.h"
+#include "utility/DirtyValue.h"
#include <lvgl/lvgl.h>
-#include "components/timer/TimerController.h"
+#include "components/timer/Timer.h"
+#include "Symbols.h"
-namespace Pinetime::Applications::Screens {
- class Timer : public Screen {
- public:
- Timer(Controllers::TimerController& timerController);
- ~Timer() override;
- void Refresh() override;
- void Reset();
- void ToggleRunning();
- void ButtonPressed();
- void MaskReset();
+namespace Pinetime::Applications {
+ namespace Screens {
+ class Timer : public Screen {
+ public:
+ Timer(Controllers::Timer& timerController);
+ ~Timer() override;
+ void Refresh() override;
+ void Reset();
+ void ToggleRunning();
+ void ButtonPressed();
+ void MaskReset();
- private:
- void SetTimerRunning();
- void SetTimerStopped();
- void UpdateMask();
- Controllers::TimerController& timerController;
+ private:
+ void SetTimerRunning();
+ void SetTimerStopped();
+ void UpdateMask();
+ void DisplayTime();
+ Pinetime::Controllers::Timer& timer;
- lv_obj_t* btnPlayPause;
- lv_obj_t* txtPlayPause;
+ lv_obj_t* btnPlayPause;
+ lv_obj_t* txtPlayPause;
- lv_obj_t* btnObjectMask;
- lv_obj_t* highlightObjectMask;
- lv_objmask_mask_t* btnMask;
- lv_objmask_mask_t* highlightMask;
+ lv_obj_t* btnObjectMask;
+ lv_obj_t* highlightObjectMask;
+ lv_objmask_mask_t* btnMask;
+ lv_objmask_mask_t* highlightMask;
- lv_task_t* taskRefresh;
- Widgets::Counter minuteCounter = Widgets::Counter(0, 59, jetbrains_mono_76);
- Widgets::Counter secondCounter = Widgets::Counter(0, 59, jetbrains_mono_76);
+ lv_task_t* taskRefresh;
+ Widgets::Counter minuteCounter = Widgets::Counter(0, 59, jetbrains_mono_76);
+ Widgets::Counter secondCounter = Widgets::Counter(0, 59, jetbrains_mono_76);
- bool buttonPressing = false;
- lv_coord_t maskPosition = 0;
- TickType_t pressTime = 0;
+ bool buttonPressing = false;
+ lv_coord_t maskPosition = 0;
+ TickType_t pressTime = 0;
+ Utility::DirtyValue<std::chrono::seconds> displaySeconds;
+ };
+ }
+
+ template <>
+ struct AppTraits<Apps::Timer> {
+ static constexpr Apps app = Apps::Timer;
+ static constexpr const char* icon = Screens::Symbols::hourGlass;
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::Timer(controllers.timer);
+ };
};
}
diff --git a/src/displayapp/screens/Twos.cpp b/src/displayapp/screens/Twos.cpp
index 8157a160..6f2eff40 100644
--- a/src/displayapp/screens/Twos.cpp
+++ b/src/displayapp/screens/Twos.cpp
@@ -242,7 +242,7 @@ void Twos::updateGridDisplay() {
const unsigned int col = i % nCols;
if (grid[row][col].value > 0) {
char buffer[7];
- sprintf(buffer, "%d", grid[row][col].value);
+ snprintf(buffer, sizeof(buffer), "%u", grid[row][col].value);
lv_table_set_cell_value(gridDisplay, row, col, buffer);
} else {
lv_table_set_cell_value(gridDisplay, row, col, "");
diff --git a/src/displayapp/screens/Twos.h b/src/displayapp/screens/Twos.h
index e731eae6..52449fd3 100644
--- a/src/displayapp/screens/Twos.h
+++ b/src/displayapp/screens/Twos.h
@@ -1,7 +1,8 @@
#pragma once
-#include <lvgl/src/lv_core/lv_obj.h>
+#include "displayapp/apps/Apps.h"
#include "displayapp/screens/Screen.h"
+#include "displayapp/Controllers.h"
namespace Pinetime {
namespace Applications {
@@ -35,5 +36,15 @@ namespace Pinetime {
bool placeNewTile();
};
}
+
+ template <>
+ struct AppTraits<Apps::Twos> {
+ static constexpr Apps app = Apps::Twos;
+ static constexpr const char* icon = "2";
+
+ static Screens::Screen* Create(AppControllers& /*controllers*/) {
+ return new Screens::Twos();
+ };
+ };
}
}
diff --git a/src/displayapp/screens/WatchFaceAnalog.cpp b/src/displayapp/screens/WatchFaceAnalog.cpp
index 76d01cf1..80a1c8b9 100644
--- a/src/displayapp/screens/WatchFaceAnalog.cpp
+++ b/src/displayapp/screens/WatchFaceAnalog.cpp
@@ -8,8 +8,6 @@
#include "components/settings/Settings.h"
#include "displayapp/InfiniTimeTheme.h"
-LV_IMG_DECLARE(bg_clock);
-
using namespace Pinetime::Applications::Screens;
namespace {
@@ -60,9 +58,41 @@ WatchFaceAnalog::WatchFaceAnalog(Controllers::DateTime& dateTimeController,
sMinute = 99;
sSecond = 99;
- lv_obj_t* bg_clock_img = lv_img_create(lv_scr_act(), nullptr);
- lv_img_set_src(bg_clock_img, &bg_clock);
- lv_obj_align(bg_clock_img, nullptr, LV_ALIGN_CENTER, 0, 0);
+ minor_scales = lv_linemeter_create(lv_scr_act(), nullptr);
+ lv_linemeter_set_scale(minor_scales, 300, 51);
+ lv_linemeter_set_angle_offset(minor_scales, 180);
+ lv_obj_set_size(minor_scales, 240, 240);
+ lv_obj_align(minor_scales, nullptr, LV_ALIGN_CENTER, 0, 0);
+ lv_obj_set_style_local_bg_opa(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
+ lv_obj_set_style_local_scale_width(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 4);
+ lv_obj_set_style_local_scale_end_line_width(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 1);
+ lv_obj_set_style_local_scale_end_color(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
+
+ major_scales = lv_linemeter_create(lv_scr_act(), nullptr);
+ lv_linemeter_set_scale(major_scales, 300, 11);
+ lv_linemeter_set_angle_offset(major_scales, 180);
+ lv_obj_set_size(major_scales, 240, 240);
+ lv_obj_align(major_scales, nullptr, LV_ALIGN_CENTER, 0, 0);
+ lv_obj_set_style_local_bg_opa(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
+ lv_obj_set_style_local_scale_width(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 6);
+ lv_obj_set_style_local_scale_end_line_width(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 4);
+ lv_obj_set_style_local_scale_end_color(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
+
+ large_scales = lv_linemeter_create(lv_scr_act(), nullptr);
+ lv_linemeter_set_scale(large_scales, 180, 3);
+ lv_linemeter_set_angle_offset(large_scales, 180);
+ lv_obj_set_size(large_scales, 240, 240);
+ lv_obj_align(large_scales, nullptr, LV_ALIGN_CENTER, 0, 0);
+ lv_obj_set_style_local_bg_opa(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
+ lv_obj_set_style_local_scale_width(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 20);
+ lv_obj_set_style_local_scale_end_line_width(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 4);
+ lv_obj_set_style_local_scale_end_color(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_AQUA);
+
+ twelve = lv_label_create(lv_scr_act(), nullptr);
+ lv_label_set_align(twelve, LV_LABEL_ALIGN_CENTER);
+ lv_label_set_text_static(twelve, "12");
+ lv_obj_set_pos(twelve, 110, 10);
+ lv_obj_set_style_local_text_color(twelve, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_AQUA);
batteryIcon.Create(lv_scr_act());
lv_obj_align(batteryIcon.GetObject(), nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 0);
@@ -226,7 +256,7 @@ void WatchFaceAnalog::Refresh() {
if (currentDateTime.IsUpdated()) {
UpdateClock();
- currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get());
+ currentDate = std::chrono::time_point_cast<std::chrono::days>(currentDateTime.Get());
if (currentDate.IsUpdated()) {
lv_label_set_text_fmt(label_date_day, "%s\n%02i", dateTimeController.DayOfWeekShortToString(), dateTimeController.Day());
}
diff --git a/src/displayapp/screens/WatchFaceAnalog.h b/src/displayapp/screens/WatchFaceAnalog.h
index b32293da..958ff64d 100644
--- a/src/displayapp/screens/WatchFaceAnalog.h
+++ b/src/displayapp/screens/WatchFaceAnalog.h
@@ -9,7 +9,8 @@
#include "components/battery/BatteryController.h"
#include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h"
-#include <displayapp/screens/BatteryIcon.h>
+#include "displayapp/screens/BatteryIcon.h"
+#include "utility/DirtyValue.h"
namespace Pinetime {
namespace Controllers {
@@ -37,13 +38,17 @@ namespace Pinetime {
private:
uint8_t sHour, sMinute, sSecond;
- DirtyValue<uint8_t> batteryPercentRemaining {0};
- DirtyValue<bool> isCharging {};
- DirtyValue<bool> bleState {};
- DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime;
- DirtyValue<bool> notificationState {false};
- using days = std::chrono::duration<int32_t, std::ratio<86400>>; // TODO: days is standard in c++20
- DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate;
+ Utility::DirtyValue<uint8_t> batteryPercentRemaining {0};
+ Utility::DirtyValue<bool> isCharging {};
+ Utility::DirtyValue<bool> bleState {};
+ Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime;
+ Utility::DirtyValue<bool> notificationState {false};
+ Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::days>> currentDate;
+
+ lv_obj_t* minor_scales;
+ lv_obj_t* major_scales;
+ lv_obj_t* large_scales;
+ lv_obj_t* twelve;
lv_obj_t* hour_body;
lv_obj_t* hour_body_trace;
@@ -70,7 +75,7 @@ namespace Pinetime {
BatteryIcon batteryIcon;
- const Controllers::DateTime& dateTimeController;
+ Controllers::DateTime& dateTimeController;
const Controllers::Battery& batteryController;
const Controllers::Ble& bleController;
Controllers::NotificationManager& notificationManager;
@@ -82,5 +87,23 @@ namespace Pinetime {
lv_task_t* taskRefresh;
};
}
+
+ template <>
+ struct WatchFaceTraits<WatchFace::Analog> {
+ static constexpr WatchFace watchFace = WatchFace::Analog;
+ static constexpr const char* name = "Analog face";
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::WatchFaceAnalog(controllers.dateTimeController,
+ controllers.batteryController,
+ controllers.bleController,
+ controllers.notificationManager,
+ controllers.settingsController);
+ };
+
+ static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) {
+ return true;
+ }
+ };
}
}
diff --git a/src/displayapp/screens/WatchFaceCasioStyleG7710.cpp b/src/displayapp/screens/WatchFaceCasioStyleG7710.cpp
index ca37c8fc..c695f852 100644
--- a/src/displayapp/screens/WatchFaceCasioStyleG7710.cpp
+++ b/src/displayapp/screens/WatchFaceCasioStyleG7710.cpp
@@ -48,14 +48,14 @@ WatchFaceCasioStyleG7710::WatchFaceCasioStyleG7710(Controllers::DateTime& dateTi
font_segment115 = lv_font_load("F:/fonts/7segments_115.bin");
}
- label_battery_vallue = lv_label_create(lv_scr_act(), nullptr);
- lv_obj_align(label_battery_vallue, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, 0, 0);
- lv_obj_set_style_local_text_color(label_battery_vallue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
- lv_label_set_text_static(label_battery_vallue, "00%");
+ label_battery_value = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_align(label_battery_value, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, 0, 0);
+ lv_obj_set_style_local_text_color(label_battery_value, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
+ lv_label_set_text_static(label_battery_value, "00%");
batteryIcon.Create(lv_scr_act());
batteryIcon.SetColor(color_text);
- lv_obj_align(batteryIcon.GetObject(), label_battery_vallue, LV_ALIGN_OUT_LEFT_MID, -5, 0);
+ lv_obj_align(batteryIcon.GetObject(), label_battery_value, LV_ALIGN_OUT_LEFT_MID, -5, 0);
batteryPlug = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(batteryPlug, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
@@ -203,7 +203,7 @@ void WatchFaceCasioStyleG7710::Refresh() {
if (batteryPercentRemaining.IsUpdated()) {
auto batteryPercent = batteryPercentRemaining.Get();
batteryIcon.SetBatteryPercentage(batteryPercent);
- lv_label_set_text_fmt(label_battery_vallue, "%d%%", batteryPercent);
+ lv_label_set_text_fmt(label_battery_value, "%d%%", batteryPercent);
}
bleState = bleController.IsConnected();
@@ -211,7 +211,7 @@ void WatchFaceCasioStyleG7710::Refresh() {
if (bleState.IsUpdated() || bleRadioEnabled.IsUpdated()) {
lv_label_set_text_static(bleIcon, BleIcon::GetIcon(bleState.Get()));
}
- lv_obj_realign(label_battery_vallue);
+ lv_obj_realign(label_battery_value);
lv_obj_realign(batteryIcon.GetObject());
lv_obj_realign(batteryPlug);
lv_obj_realign(bleIcon);
@@ -222,43 +222,36 @@ void WatchFaceCasioStyleG7710::Refresh() {
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
}
- currentDateTime = dateTimeController.CurrentDateTime();
-
+ currentDateTime = std::chrono::time_point_cast<std::chrono::minutes>(dateTimeController.CurrentDateTime());
if (currentDateTime.IsUpdated()) {
- auto hour = dateTimeController.Hours();
- auto minute = dateTimeController.Minutes();
- auto year = dateTimeController.Year();
- auto month = dateTimeController.Month();
- auto dayOfWeek = dateTimeController.DayOfWeek();
- auto day = dateTimeController.Day();
- auto dayOfYear = dateTimeController.DayOfYear();
-
- auto weekNumberFormat = "%V";
+ uint8_t hour = dateTimeController.Hours();
+ uint8_t minute = dateTimeController.Minutes();
- if (displayedHour != hour || displayedMinute != minute) {
- displayedHour = hour;
- displayedMinute = minute;
-
- if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
- char ampmChar[2] = "A";
- if (hour == 0) {
- hour = 12;
- } else if (hour == 12) {
- ampmChar[0] = 'P';
- } else if (hour > 12) {
- hour = hour - 12;
- ampmChar[0] = 'P';
- }
- lv_label_set_text(label_time_ampm, ampmChar);
- lv_label_set_text_fmt(label_time, "%2d:%02d", hour, minute);
- lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_CENTER, 0, 40);
- } else {
- lv_label_set_text_fmt(label_time, "%02d:%02d", hour, minute);
- lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_CENTER, 0, 40);
+ if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
+ char ampmChar[2] = "A";
+ if (hour == 0) {
+ hour = 12;
+ } else if (hour == 12) {
+ ampmChar[0] = 'P';
+ } else if (hour > 12) {
+ hour = hour - 12;
+ ampmChar[0] = 'P';
}
+ lv_label_set_text(label_time_ampm, ampmChar);
+ lv_label_set_text_fmt(label_time, "%2d:%02d", hour, minute);
+ } else {
+ lv_label_set_text_fmt(label_time, "%02d:%02d", hour, minute);
}
+ lv_obj_realign(label_time);
+
+ currentDate = std::chrono::time_point_cast<std::chrono::days>(currentDateTime.Get());
+ if (currentDate.IsUpdated()) {
+ const char* weekNumberFormat = "%V";
- if ((year != currentYear) || (month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) {
+ uint16_t year = dateTimeController.Year();
+ Controllers::DateTime::Months month = dateTimeController.Month();
+ uint8_t day = dateTimeController.Day();
+ int dayOfYear = dateTimeController.DayOfYear();
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H24) {
// 24h mode: ddmmyyyy, first DOW=Monday;
lv_label_set_text_fmt(label_date, "%3d-%2d", day, month);
@@ -293,11 +286,6 @@ void WatchFaceCasioStyleG7710::Refresh() {
lv_obj_realign(label_day_of_year);
lv_obj_realign(label_week_number);
lv_obj_realign(label_date);
-
- currentYear = year;
- currentMonth = month;
- currentDayOfWeek = dayOfWeek;
- currentDay = day;
}
}
@@ -317,8 +305,7 @@ void WatchFaceCasioStyleG7710::Refresh() {
}
stepCount = motionController.NbSteps();
- motionSensorOk = motionController.IsSensorOk();
- if (stepCount.IsUpdated() || motionSensorOk.IsUpdated()) {
+ if (stepCount.IsUpdated()) {
lv_label_set_text_fmt(stepValue, "%lu", stepCount.Get());
lv_obj_realign(stepValue);
lv_obj_realign(stepIcon);
diff --git a/src/displayapp/screens/WatchFaceCasioStyleG7710.h b/src/displayapp/screens/WatchFaceCasioStyleG7710.h
index 0445c9f2..0f46a692 100644
--- a/src/displayapp/screens/WatchFaceCasioStyleG7710.h
+++ b/src/displayapp/screens/WatchFaceCasioStyleG7710.h
@@ -5,9 +5,12 @@
#include <chrono>
#include <cstdint>
#include <memory>
+#include <displayapp/Controllers.h>
#include "displayapp/screens/Screen.h"
#include "components/datetime/DateTimeController.h"
#include "components/ble/BleController.h"
+#include "utility/DirtyValue.h"
+#include "displayapp/apps/Apps.h"
namespace Pinetime {
namespace Controllers {
@@ -39,24 +42,16 @@ namespace Pinetime {
static bool IsAvailable(Pinetime::Controllers::FS& filesystem);
private:
- uint8_t displayedHour = -1;
- uint8_t displayedMinute = -1;
-
- uint16_t currentYear = 1970;
- Controllers::DateTime::Months currentMonth = Pinetime::Controllers::DateTime::Months::Unknown;
- Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown;
- uint8_t currentDay = 0;
-
- DirtyValue<uint8_t> batteryPercentRemaining {};
- DirtyValue<bool> powerPresent {};
- DirtyValue<bool> bleState {};
- DirtyValue<bool> bleRadioEnabled {};
- DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime {};
- DirtyValue<bool> motionSensorOk {};
- DirtyValue<uint32_t> stepCount {};
- DirtyValue<uint8_t> heartbeat {};
- DirtyValue<bool> heartbeatRunning {};
- DirtyValue<bool> notificationState {};
+ Utility::DirtyValue<uint8_t> batteryPercentRemaining {};
+ Utility::DirtyValue<bool> powerPresent {};
+ Utility::DirtyValue<bool> bleState {};
+ Utility::DirtyValue<bool> bleRadioEnabled {};
+ Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>> currentDateTime {};
+ Utility::DirtyValue<uint32_t> stepCount {};
+ Utility::DirtyValue<uint8_t> heartbeat {};
+ Utility::DirtyValue<bool> heartbeatRunning {};
+ Utility::DirtyValue<bool> notificationState {};
+ Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::days>> currentDate;
lv_point_t line_icons_points[3] {{0, 5}, {117, 5}, {122, 0}};
lv_point_t line_day_of_week_number_points[4] {{0, 0}, {100, 0}, {95, 95}, {0, 95}};
@@ -82,7 +77,7 @@ namespace Pinetime {
lv_obj_t* backgroundLabel;
lv_obj_t* bleIcon;
lv_obj_t* batteryPlug;
- lv_obj_t* label_battery_vallue;
+ lv_obj_t* label_battery_value;
lv_obj_t* heartbeatIcon;
lv_obj_t* heartbeatValue;
lv_obj_t* stepIcon;
@@ -106,5 +101,26 @@ namespace Pinetime {
lv_font_t* font_segment115 = nullptr;
};
}
+
+ template <>
+ struct WatchFaceTraits<WatchFace::CasioStyleG7710> {
+ static constexpr WatchFace watchFace = WatchFace::CasioStyleG7710;
+ static constexpr const char* name = "Casio G7710";
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::WatchFaceCasioStyleG7710(controllers.dateTimeController,
+ controllers.batteryController,
+ controllers.bleController,
+ controllers.notificationManager,
+ controllers.settingsController,
+ controllers.heartRateController,
+ controllers.motionController,
+ controllers.filesystem);
+ };
+
+ static bool IsAvailable(Pinetime::Controllers::FS& filesystem) {
+ return Screens::WatchFaceCasioStyleG7710::IsAvailable(filesystem);
+ }
+ };
}
}
diff --git a/src/displayapp/screens/WatchFaceDigital.cpp b/src/displayapp/screens/WatchFaceDigital.cpp
index ad35b5c9..3163c6e7 100644
--- a/src/displayapp/screens/WatchFaceDigital.cpp
+++ b/src/displayapp/screens/WatchFaceDigital.cpp
@@ -2,13 +2,16 @@
#include <lvgl/lvgl.h>
#include <cstdio>
+
#include "displayapp/screens/NotificationIcon.h"
#include "displayapp/screens/Symbols.h"
+#include "displayapp/screens/WeatherSymbols.h"
#include "components/battery/BatteryController.h"
#include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h"
#include "components/heartrate/HeartRateController.h"
#include "components/motion/MotionController.h"
+#include "components/ble/SimpleWeatherService.h"
#include "components/settings/Settings.h"
using namespace Pinetime::Applications::Screens;
@@ -16,17 +19,20 @@ using namespace Pinetime::Applications::Screens;
WatchFaceDigital::WatchFaceDigital(Controllers::DateTime& dateTimeController,
const Controllers::Battery& batteryController,
const Controllers::Ble& bleController,
+ const Controllers::AlarmController& alarmController,
Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController,
Controllers::HeartRateController& heartRateController,
- Controllers::MotionController& motionController)
+ Controllers::MotionController& motionController,
+ Controllers::SimpleWeatherService& weatherService)
: currentDateTime {{}},
dateTimeController {dateTimeController},
notificationManager {notificationManager},
settingsController {settingsController},
heartRateController {heartRateController},
motionController {motionController},
- statusIcons(batteryController, bleController) {
+ weatherService {weatherService},
+ statusIcons(batteryController, bleController, alarmController) {
statusIcons.Create();
@@ -35,6 +41,18 @@ WatchFaceDigital::WatchFaceDigital(Controllers::DateTime& dateTimeController,
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(false));
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
+ weatherIcon = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(weatherIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999));
+ lv_obj_set_style_local_text_font(weatherIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &fontawesome_weathericons);
+ lv_label_set_text(weatherIcon, "");
+ lv_obj_align(weatherIcon, nullptr, LV_ALIGN_IN_TOP_MID, -20, 50);
+ lv_obj_set_auto_realign(weatherIcon, true);
+
+ temperature = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999));
+ lv_label_set_text(temperature, "");
+ lv_obj_align(temperature, nullptr, LV_ALIGN_IN_TOP_MID, 20, 50);
+
label_date = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_CENTER, 0, 60);
lv_obj_set_style_local_text_color(label_date, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999));
@@ -85,40 +103,34 @@ void WatchFaceDigital::Refresh() {
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
}
- currentDateTime = dateTimeController.CurrentDateTime();
+ currentDateTime = std::chrono::time_point_cast<std::chrono::minutes>(dateTimeController.CurrentDateTime());
if (currentDateTime.IsUpdated()) {
- auto hour = dateTimeController.Hours();
- auto minute = dateTimeController.Minutes();
- auto year = dateTimeController.Year();
- auto month = dateTimeController.Month();
- auto dayOfWeek = dateTimeController.DayOfWeek();
- auto day = dateTimeController.Day();
+ uint8_t hour = dateTimeController.Hours();
+ uint8_t minute = dateTimeController.Minutes();
- if (displayedHour != hour || displayedMinute != minute) {
- displayedHour = hour;
- displayedMinute = minute;
-
- if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
- char ampmChar[3] = "AM";
- if (hour == 0) {
- hour = 12;
- } else if (hour == 12) {
- ampmChar[0] = 'P';
- } else if (hour > 12) {
- hour = hour - 12;
- ampmChar[0] = 'P';
- }
- lv_label_set_text(label_time_ampm, ampmChar);
- lv_label_set_text_fmt(label_time, "%2d:%02d", hour, minute);
- lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0);
- } else {
- lv_label_set_text_fmt(label_time, "%02d:%02d", hour, minute);
- lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
+ if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
+ char ampmChar[3] = "AM";
+ if (hour == 0) {
+ hour = 12;
+ } else if (hour == 12) {
+ ampmChar[0] = 'P';
+ } else if (hour > 12) {
+ hour = hour - 12;
+ ampmChar[0] = 'P';
}
+ lv_label_set_text(label_time_ampm, ampmChar);
+ lv_label_set_text_fmt(label_time, "%2d:%02d", hour, minute);
+ lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0);
+ } else {
+ lv_label_set_text_fmt(label_time, "%02d:%02d", hour, minute);
+ lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
}
- if ((year != currentYear) || (month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) {
+ currentDate = std::chrono::time_point_cast<std::chrono::days>(currentDateTime.Get());
+ if (currentDate.IsUpdated()) {
+ uint16_t year = dateTimeController.Year();
+ uint8_t day = dateTimeController.Day();
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H24) {
lv_label_set_text_fmt(label_date,
"%s %d %s %d",
@@ -135,11 +147,6 @@ void WatchFaceDigital::Refresh() {
year);
}
lv_obj_realign(label_date);
-
- currentYear = year;
- currentMonth = month;
- currentDayOfWeek = dayOfWeek;
- currentDay = day;
}
}
@@ -159,10 +166,29 @@ void WatchFaceDigital::Refresh() {
}
stepCount = motionController.NbSteps();
- motionSensorOk = motionController.IsSensorOk();
- if (stepCount.IsUpdated() || motionSensorOk.IsUpdated()) {
+ if (stepCount.IsUpdated()) {
lv_label_set_text_fmt(stepValue, "%lu", stepCount.Get());
lv_obj_realign(stepValue);
lv_obj_realign(stepIcon);
}
+
+ currentWeather = weatherService.Current();
+ if (currentWeather.IsUpdated()) {
+ auto optCurrentWeather = currentWeather.Get();
+ if (optCurrentWeather) {
+ int16_t temp = optCurrentWeather->temperature.Celsius();
+ char tempUnit = 'C';
+ if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
+ temp = optCurrentWeather->temperature.Fahrenheit();
+ tempUnit = 'F';
+ }
+ lv_label_set_text_fmt(temperature, "%d°%c", temp, tempUnit);
+ lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId));
+ } else {
+ lv_label_set_text_static(temperature, "");
+ lv_label_set_text(weatherIcon, "");
+ }
+ lv_obj_realign(temperature);
+ lv_obj_realign(weatherIcon);
+ }
}
diff --git a/src/displayapp/screens/WatchFaceDigital.h b/src/displayapp/screens/WatchFaceDigital.h
index 0931f007..3005cea5 100644
--- a/src/displayapp/screens/WatchFaceDigital.h
+++ b/src/displayapp/screens/WatchFaceDigital.h
@@ -6,14 +6,18 @@
#include <memory>
#include "displayapp/screens/Screen.h"
#include "components/datetime/DateTimeController.h"
+#include "components/ble/SimpleWeatherService.h"
#include "components/ble/BleController.h"
#include "displayapp/widgets/StatusIcons.h"
+#include "utility/DirtyValue.h"
+#include "displayapp/apps/Apps.h"
namespace Pinetime {
namespace Controllers {
class Settings;
class Battery;
class Ble;
+ class AlarmController;
class NotificationManager;
class HeartRateController;
class MotionController;
@@ -27,10 +31,12 @@ namespace Pinetime {
WatchFaceDigital(Controllers::DateTime& dateTimeController,
const Controllers::Battery& batteryController,
const Controllers::Ble& bleController,
+ const Controllers::AlarmController& alarmController,
Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController,
Controllers::HeartRateController& heartRateController,
- Controllers::MotionController& motionController);
+ Controllers::MotionController& motionController,
+ Controllers::SimpleWeatherService& weather);
~WatchFaceDigital() override;
void Refresh() override;
@@ -39,21 +45,14 @@ namespace Pinetime {
uint8_t displayedHour = -1;
uint8_t displayedMinute = -1;
- uint16_t currentYear = 1970;
- Controllers::DateTime::Months currentMonth = Pinetime::Controllers::DateTime::Months::Unknown;
- Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown;
- uint8_t currentDay = 0;
+ Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>> currentDateTime {};
+ Utility::DirtyValue<uint32_t> stepCount {};
+ Utility::DirtyValue<uint8_t> heartbeat {};
+ Utility::DirtyValue<bool> heartbeatRunning {};
+ Utility::DirtyValue<bool> notificationState {};
+ Utility::DirtyValue<std::optional<Pinetime::Controllers::SimpleWeatherService::CurrentWeather>> currentWeather {};
- DirtyValue<uint8_t> batteryPercentRemaining {};
- DirtyValue<bool> powerPresent {};
- DirtyValue<bool> bleState {};
- DirtyValue<bool> bleRadioEnabled {};
- DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime {};
- DirtyValue<bool> motionSensorOk {};
- DirtyValue<uint32_t> stepCount {};
- DirtyValue<uint8_t> heartbeat {};
- DirtyValue<bool> heartbeatRunning {};
- DirtyValue<bool> notificationState {};
+ Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::days>> currentDate;
lv_obj_t* label_time;
lv_obj_t* label_time_ampm;
@@ -63,16 +62,41 @@ namespace Pinetime {
lv_obj_t* stepIcon;
lv_obj_t* stepValue;
lv_obj_t* notificationIcon;
+ lv_obj_t* weatherIcon;
+ lv_obj_t* temperature;
Controllers::DateTime& dateTimeController;
Controllers::NotificationManager& notificationManager;
Controllers::Settings& settingsController;
Controllers::HeartRateController& heartRateController;
Controllers::MotionController& motionController;
+ Controllers::SimpleWeatherService& weatherService;
lv_task_t* taskRefresh;
Widgets::StatusIcons statusIcons;
};
}
+
+ template <>
+ struct WatchFaceTraits<WatchFace::Digital> {
+ static constexpr WatchFace watchFace = WatchFace::Digital;
+ static constexpr const char* name = "Digital face";
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::WatchFaceDigital(controllers.dateTimeController,
+ controllers.batteryController,
+ controllers.bleController,
+ controllers.alarmController,
+ controllers.notificationManager,
+ controllers.settingsController,
+ controllers.heartRateController,
+ controllers.motionController,
+ *controllers.weatherController);
+ };
+
+ static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) {
+ return true;
+ }
+ };
}
}
diff --git a/src/displayapp/screens/WatchFaceInfineat.cpp b/src/displayapp/screens/WatchFaceInfineat.cpp
index ab0898e0..40f2abbb 100644
--- a/src/displayapp/screens/WatchFaceInfineat.cpp
+++ b/src/displayapp/screens/WatchFaceInfineat.cpp
@@ -397,74 +397,52 @@ void WatchFaceInfineat::Refresh() {
lv_obj_align(notificationIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, 0, 0);
}
- currentDateTime = dateTimeController.CurrentDateTime();
-
+ currentDateTime = std::chrono::time_point_cast<std::chrono::minutes>(dateTimeController.CurrentDateTime());
if (currentDateTime.IsUpdated()) {
- auto hour = dateTimeController.Hours();
- auto minute = dateTimeController.Minutes();
- auto year = dateTimeController.Year();
- auto month = dateTimeController.Month();
- auto dayOfWeek = dateTimeController.DayOfWeek();
- auto day = dateTimeController.Day();
-
- char minutesChar[3];
- sprintf(minutesChar, "%02d", static_cast<int>(minute));
-
- char hoursChar[3];
- char ampmChar[3];
+ uint8_t hour = dateTimeController.Hours();
+ uint8_t minute = dateTimeController.Minutes();
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
- if (hour < 12) {
- if (hour == 0) {
- hour = 12;
- }
- sprintf(ampmChar, "AM");
- } else { // hour >= 12
- if (hour != 12) {
- hour = hour - 12;
- }
- sprintf(ampmChar, "PM");
+ char ampmChar[3] = "AM";
+ if (hour == 0) {
+ hour = 12;
+ } else if (hour == 12) {
+ ampmChar[0] = 'P';
+ } else if (hour > 12) {
+ hour = hour - 12;
+ ampmChar[0] = 'P';
}
+ lv_label_set_text(labelTimeAmPm, ampmChar);
}
- sprintf(hoursChar, "%02d", hour);
-
- if ((hoursChar[0] != displayedChar[0]) || (hoursChar[1] != displayedChar[1]) || (minutesChar[0] != displayedChar[2]) ||
- (minutesChar[1] != displayedChar[3])) {
- displayedChar[0] = hoursChar[0];
- displayedChar[1] = hoursChar[1];
- displayedChar[2] = minutesChar[0];
- displayedChar[3] = minutesChar[1];
-
- lv_label_set_text_fmt(labelHour, "%s", hoursChar);
- lv_label_set_text_fmt(labelMinutes, "%s", minutesChar);
- }
+ lv_label_set_text_fmt(labelHour, "%02d", hour);
+ lv_label_set_text_fmt(labelMinutes, "%02d", minute);
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
- lv_label_set_text(labelTimeAmPm, ampmChar);
lv_obj_align(labelTimeAmPm, timeContainer, LV_ALIGN_OUT_RIGHT_TOP, 0, 10);
lv_obj_align(labelHour, timeContainer, LV_ALIGN_IN_TOP_MID, 0, 5);
lv_obj_align(labelMinutes, timeContainer, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
}
- if ((year != currentYear) || (month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) {
- lv_label_set_text_fmt(labelDate, "%s %02d", dateTimeController.DayOfWeekShortToStringLow(), day);
+ currentDate = std::chrono::time_point_cast<std::chrono::days>(currentDateTime.Get());
+ if (currentDate.IsUpdated()) {
+ uint8_t day = dateTimeController.Day();
+ Controllers::DateTime::Days dayOfWeek = dateTimeController.DayOfWeek();
+ lv_label_set_text_fmt(labelDate, "%s %02d", dateTimeController.DayOfWeekShortToStringLow(dayOfWeek), day);
lv_obj_realign(labelDate);
-
- currentYear = year;
- currentMonth = month;
- currentDayOfWeek = dayOfWeek;
- currentDay = day;
}
}
batteryPercentRemaining = batteryController.PercentRemaining();
isCharging = batteryController.IsCharging();
- if (batteryController.IsCharging()) { // Charging battery animation
- chargingBatteryPercent += 1;
+ // Charging battery animation
+ if (batteryController.IsCharging() && (xTaskGetTickCount() - chargingAnimationTick > pdMS_TO_TICKS(150))) {
+ // Dividing 100 by the height gives the battery percentage required to shift the animation by 1 pixel
+ chargingBatteryPercent += 100 / lv_obj_get_height(logoPine);
if (chargingBatteryPercent > 100) {
chargingBatteryPercent = batteryPercentRemaining.Get();
}
SetBatteryLevel(chargingBatteryPercent);
+ chargingAnimationTick = xTaskGetTickCount();
} else if (isCharging.IsUpdated() || batteryPercentRemaining.IsUpdated()) {
chargingBatteryPercent = batteryPercentRemaining.Get();
SetBatteryLevel(chargingBatteryPercent);
@@ -478,8 +456,7 @@ void WatchFaceInfineat::Refresh() {
}
stepCount = motionController.NbSteps();
- motionSensorOk = motionController.IsSensorOk();
- if (stepCount.IsUpdated() || motionSensorOk.IsUpdated()) {
+ if (stepCount.IsUpdated()) {
lv_label_set_text_fmt(stepValue, "%lu", stepCount.Get());
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 10, 0);
lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0);
diff --git a/src/displayapp/screens/WatchFaceInfineat.h b/src/displayapp/screens/WatchFaceInfineat.h
index 26973efe..78d020f1 100644
--- a/src/displayapp/screens/WatchFaceInfineat.h
+++ b/src/displayapp/screens/WatchFaceInfineat.h
@@ -4,8 +4,11 @@
#include <chrono>
#include <cstdint>
#include <memory>
+#include <displayapp/Controllers.h>
#include "displayapp/screens/Screen.h"
#include "components/datetime/DateTimeController.h"
+#include "utility/DirtyValue.h"
+#include "displayapp/apps/Apps.h"
namespace Pinetime {
namespace Controllers {
@@ -42,23 +45,18 @@ namespace Pinetime {
static bool IsAvailable(Pinetime::Controllers::FS& filesystem);
private:
- char displayedChar[5] {};
-
- uint16_t currentYear = 1970;
- Pinetime::Controllers::DateTime::Months currentMonth = Pinetime::Controllers::DateTime::Months::Unknown;
- Pinetime::Controllers::DateTime::Days currentDayOfWeek = Pinetime::Controllers::DateTime::Days::Unknown;
- uint8_t currentDay = 0;
uint32_t savedTick = 0;
uint8_t chargingBatteryPercent = 101; // not a mistake ;)
+ TickType_t chargingAnimationTick = 0;
- DirtyValue<uint8_t> batteryPercentRemaining {};
- DirtyValue<bool> isCharging {};
- DirtyValue<bool> bleState {};
- DirtyValue<bool> bleRadioEnabled {};
- DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime {};
- DirtyValue<bool> motionSensorOk {};
- DirtyValue<uint32_t> stepCount {};
- DirtyValue<bool> notificationState {};
+ Utility::DirtyValue<uint8_t> batteryPercentRemaining {};
+ Utility::DirtyValue<bool> isCharging {};
+ Utility::DirtyValue<bool> bleState {};
+ Utility::DirtyValue<bool> bleRadioEnabled {};
+ Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>> currentDateTime {};
+ Utility::DirtyValue<uint32_t> stepCount {};
+ Utility::DirtyValue<bool> notificationState {};
+ Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::days>> currentDate;
// Lines making up the side cover
lv_obj_t* lineBattery;
@@ -102,5 +100,25 @@ namespace Pinetime {
lv_font_t* font_bebas = nullptr;
};
}
+
+ template <>
+ struct WatchFaceTraits<WatchFace::Infineat> {
+ static constexpr WatchFace watchFace = WatchFace::Infineat;
+ static constexpr const char* name = "Infineat face";
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::WatchFaceInfineat(controllers.dateTimeController,
+ controllers.batteryController,
+ controllers.bleController,
+ controllers.notificationManager,
+ controllers.settingsController,
+ controllers.motionController,
+ controllers.filesystem);
+ };
+
+ static bool IsAvailable(Pinetime::Controllers::FS& filesystem) {
+ return Screens::WatchFaceInfineat::IsAvailable(filesystem);
+ }
+ };
}
}
diff --git a/src/displayapp/screens/WatchFacePineTimeStyle.cpp b/src/displayapp/screens/WatchFacePineTimeStyle.cpp
index 85505a63..22ccefc7 100644
--- a/src/displayapp/screens/WatchFacePineTimeStyle.cpp
+++ b/src/displayapp/screens/WatchFacePineTimeStyle.cpp
@@ -22,17 +22,19 @@
#include "displayapp/screens/WatchFacePineTimeStyle.h"
#include <lvgl/lvgl.h>
#include <cstdio>
-#include <displayapp/Colors.h>
+#include "displayapp/Colors.h"
#include "displayapp/screens/BatteryIcon.h"
#include "displayapp/screens/BleIcon.h"
#include "displayapp/screens/NotificationIcon.h"
#include "displayapp/screens/Symbols.h"
+#include "displayapp/screens/WeatherSymbols.h"
#include "components/battery/BatteryController.h"
#include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h"
#include "components/motion/MotionController.h"
#include "components/settings/Settings.h"
#include "displayapp/DisplayApp.h"
+#include "components/ble/SimpleWeatherService.h"
using namespace Pinetime::Applications::Screens;
@@ -48,7 +50,8 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(Controllers::DateTime& dateTimeCo
const Controllers::Ble& bleController,
Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController,
- Controllers::MotionController& motionController)
+ Controllers::MotionController& motionController,
+ Controllers::SimpleWeatherService& weatherService)
: currentDateTime {{}},
batteryIcon(false),
dateTimeController {dateTimeController},
@@ -56,7 +59,8 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(Controllers::DateTime& dateTimeCo
bleController {bleController},
notificationManager {notificationManager},
settingsController {settingsController},
- motionController {motionController} {
+ motionController {motionController},
+ weatherService {weatherService} {
// Create a 200px wide background rectangle
timebar = lv_obj_create(lv_scr_act(), nullptr);
@@ -94,27 +98,53 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(Controllers::DateTime& dateTimeCo
// Display icons
batteryIcon.Create(sidebar);
batteryIcon.SetColor(LV_COLOR_BLACK);
- lv_obj_align(batteryIcon.GetObject(), nullptr, LV_ALIGN_IN_TOP_MID, 0, 2);
+ lv_obj_align(batteryIcon.GetObject(), nullptr, LV_ALIGN_IN_TOP_MID, 10, 2);
plugIcon = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(plugIcon, Symbols::plug);
lv_obj_set_style_local_text_color(plugIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
- lv_obj_align(plugIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 2);
+ lv_obj_align(plugIcon, sidebar, LV_ALIGN_IN_TOP_MID, 10, 2);
bleIcon = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(bleIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
- lv_label_set_text_static(bleIcon, "");
+ lv_obj_align(bleIcon, sidebar, LV_ALIGN_IN_TOP_MID, -10, 2);
notificationIcon = lv_label_create(lv_scr_act(), nullptr);
- lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
- lv_label_set_text_static(notificationIcon, "");
+ lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Convert(settingsController.GetPTSColorTime()));
+ lv_obj_align(notificationIcon, timebar, LV_ALIGN_IN_TOP_LEFT, 5, 5);
+
+ weatherIcon = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(weatherIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
+ lv_obj_set_style_local_text_font(weatherIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &fontawesome_weathericons);
+ lv_label_set_text(weatherIcon, Symbols::ban);
+ lv_obj_align(weatherIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 35);
+ lv_obj_set_auto_realign(weatherIcon, true);
+ if (settingsController.GetPTSWeather() == Pinetime::Controllers::Settings::PTSWeather::On) {
+ lv_obj_set_hidden(weatherIcon, false);
+ } else {
+ lv_obj_set_hidden(weatherIcon, true);
+ }
+
+ temperature = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
+ lv_label_set_text(temperature, "--");
+ lv_obj_align(temperature, sidebar, LV_ALIGN_IN_TOP_MID, 0, 65);
+ if (settingsController.GetPTSWeather() == Pinetime::Controllers::Settings::PTSWeather::On) {
+ lv_obj_set_hidden(temperature, false);
+ } else {
+ lv_obj_set_hidden(temperature, true);
+ }
// Calendar icon
calendarOuter = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(calendarOuter, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_obj_set_style_local_radius(calendarOuter, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 0);
lv_obj_set_size(calendarOuter, 34, 34);
- lv_obj_align(calendarOuter, sidebar, LV_ALIGN_CENTER, 0, 0);
+ if (settingsController.GetPTSWeather() == Pinetime::Controllers::Settings::PTSWeather::On) {
+ lv_obj_align(calendarOuter, sidebar, LV_ALIGN_CENTER, 0, 20);
+ } else {
+ lv_obj_align(calendarOuter, sidebar, LV_ALIGN_CENTER, 0, 0);
+ }
calendarInner = lv_obj_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_bg_color(calendarInner, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
@@ -150,17 +180,17 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(Controllers::DateTime& dateTimeCo
dateDayOfWeek = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(dateDayOfWeek, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_label_set_text_static(dateDayOfWeek, "THU");
- lv_obj_align(dateDayOfWeek, sidebar, LV_ALIGN_CENTER, 0, -34);
+ lv_obj_align(dateDayOfWeek, calendarOuter, LV_ALIGN_CENTER, 0, -32);
dateDay = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(dateDay, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_label_set_text_static(dateDay, "25");
- lv_obj_align(dateDay, sidebar, LV_ALIGN_CENTER, 0, 3);
+ lv_obj_align(dateDay, calendarOuter, LV_ALIGN_CENTER, 0, 3);
dateMonth = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(dateMonth, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_label_set_text_static(dateMonth, "MAR");
- lv_obj_align(dateMonth, sidebar, LV_ALIGN_CENTER, 0, 32);
+ lv_obj_align(dateMonth, calendarOuter, LV_ALIGN_CENTER, 0, 32);
// Step count gauge
if (settingsController.GetPTSColorBar() == Pinetime::Controllers::Settings::Colors::White) {
@@ -323,13 +353,23 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(Controllers::DateTime& dateTimeCo
btnSteps = lv_btn_create(lv_scr_act(), nullptr);
btnSteps->user_data = this;
lv_obj_set_size(btnSteps, 160, 60);
- lv_obj_align(btnSteps, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
+ lv_obj_align(btnSteps, lv_scr_act(), LV_ALIGN_CENTER, 0, -10);
lv_obj_set_style_local_bg_opa(btnSteps, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50);
lv_obj_t* lblSteps = lv_label_create(btnSteps, nullptr);
lv_label_set_text_static(lblSteps, "Steps style");
lv_obj_set_event_cb(btnSteps, event_handler);
lv_obj_set_hidden(btnSteps, true);
+ btnWeather = lv_btn_create(lv_scr_act(), nullptr);
+ btnWeather->user_data = this;
+ lv_obj_set_size(btnWeather, 160, 60);
+ lv_obj_align(btnWeather, lv_scr_act(), LV_ALIGN_CENTER, 0, 60);
+ lv_obj_set_style_local_bg_opa(btnWeather, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50);
+ lv_obj_t* lblWeather = lv_label_create(btnWeather, nullptr);
+ lv_label_set_text_static(lblWeather, "Weather");
+ lv_obj_set_event_cb(btnWeather, event_handler);
+ lv_obj_set_hidden(btnWeather, true);
+
btnSetColor = lv_btn_create(lv_scr_act(), nullptr);
btnSetColor->user_data = this;
lv_obj_set_size(btnSetColor, 150, 60);
@@ -337,9 +377,9 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(Controllers::DateTime& dateTimeCo
lv_obj_set_style_local_radius(btnSetColor, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 20);
lv_obj_set_style_local_bg_opa(btnSetColor, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50);
lv_obj_set_event_cb(btnSetColor, event_handler);
- lbl_btnSetColor = lv_label_create(btnSetColor, nullptr);
- lv_obj_set_style_local_text_font(lbl_btnSetColor, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48);
- lv_label_set_text_static(lbl_btnSetColor, Symbols::paintbrushLg);
+ lv_obj_t* lblSetColor = lv_label_create(btnSetColor, nullptr);
+ lv_obj_set_style_local_text_font(lblSetColor, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48);
+ lv_label_set_text_static(lblSetColor, Symbols::paintbrushLg);
lv_obj_set_hidden(btnSetColor, true);
btnSetOpts = lv_btn_create(lv_scr_act(), nullptr);
@@ -349,9 +389,9 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(Controllers::DateTime& dateTimeCo
lv_obj_set_style_local_radius(btnSetOpts, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 20);
lv_obj_set_style_local_bg_opa(btnSetOpts, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50);
lv_obj_set_event_cb(btnSetOpts, event_handler);
- lbl_btnSetOpts = lv_label_create(btnSetOpts, nullptr);
- lv_obj_set_style_local_text_font(lbl_btnSetOpts, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48);
- lv_label_set_text_static(lbl_btnSetOpts, Symbols::settings);
+ lv_obj_t* lblSetOpts = lv_label_create(btnSetOpts, nullptr);
+ lv_obj_set_style_local_text_font(lblSetOpts, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48);
+ lv_label_set_text_static(lblSetOpts, Symbols::settings);
lv_obj_set_hidden(btnSetOpts, true);
taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
@@ -388,6 +428,7 @@ void WatchFacePineTimeStyle::CloseMenu() {
lv_obj_set_hidden(btnRandom, true);
lv_obj_set_hidden(btnClose, true);
lv_obj_set_hidden(btnSteps, true);
+ lv_obj_set_hidden(btnWeather, true);
}
bool WatchFacePineTimeStyle::OnButtonPushed() {
@@ -403,17 +444,6 @@ void WatchFacePineTimeStyle::SetBatteryIcon() {
batteryIcon.SetBatteryPercentage(batteryPercent);
}
-void WatchFacePineTimeStyle::AlignIcons() {
- if (notificationState.Get() && bleState.Get()) {
- lv_obj_align(bleIcon, sidebar, LV_ALIGN_IN_TOP_MID, 8, 25);
- lv_obj_align(notificationIcon, sidebar, LV_ALIGN_IN_TOP_MID, -8, 25);
- } else if (notificationState.Get() && !bleState.Get()) {
- lv_obj_align(notificationIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 25);
- } else {
- lv_obj_align(bleIcon, sidebar, LV_ALIGN_IN_TOP_MID, 0, 25);
- }
-}
-
void WatchFacePineTimeStyle::Refresh() {
isCharging = batteryController.IsCharging();
if (isCharging.IsUpdated()) {
@@ -437,13 +467,12 @@ void WatchFacePineTimeStyle::Refresh() {
bleRadioEnabled = bleController.IsRadioEnabled();
if (bleState.IsUpdated() || bleRadioEnabled.IsUpdated()) {
lv_label_set_text_static(bleIcon, BleIcon::GetIcon(bleState.Get()));
- AlignIcons();
+ lv_obj_realign(bleIcon);
}
notificationState = notificationManager.AreNewNotificationsAvailable();
if (notificationState.IsUpdated()) {
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
- AlignIcons();
}
currentDateTime = dateTimeController.CurrentDateTime();
@@ -499,8 +528,7 @@ void WatchFacePineTimeStyle::Refresh() {
}
stepCount = motionController.NbSteps();
- motionSensorOk = motionController.IsSensorOk();
- if (stepCount.IsUpdated() || motionSensorOk.IsUpdated()) {
+ if (stepCount.IsUpdated()) {
lv_gauge_set_value(stepGauge, 0, (stepCount.Get() / (settingsController.GetStepsGoal() / 100)) % 100);
lv_obj_realign(stepGauge);
lv_label_set_text_fmt(stepValue, "%luK", (stepCount.Get() / 1000));
@@ -510,6 +538,25 @@ void WatchFacePineTimeStyle::Refresh() {
lv_obj_set_style_local_scale_grad_color(stepGauge, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
}
}
+
+ currentWeather = weatherService.Current();
+ if (currentWeather.IsUpdated()) {
+ auto optCurrentWeather = currentWeather.Get();
+ if (optCurrentWeather) {
+ int16_t temp = optCurrentWeather->temperature.Celsius();
+ if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
+ temp = optCurrentWeather->temperature.Fahrenheit();
+ }
+ lv_label_set_text_fmt(temperature, "%d°", temp);
+ lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId));
+ } else {
+ lv_label_set_text(temperature, "--");
+ lv_label_set_text(weatherIcon, Symbols::ban);
+ }
+ lv_obj_realign(temperature);
+ lv_obj_realign(weatherIcon);
+ }
+
if (!lv_obj_get_hidden(btnSetColor)) {
if ((savedTick > 0) && (lv_tick_get() - savedTick > 3000)) {
lv_obj_set_hidden(btnSetColor, true);
@@ -655,6 +702,37 @@ void WatchFacePineTimeStyle::UpdateSelected(lv_obj_t* object, lv_event_t event)
settingsController.SetPTSGaugeStyle(Controllers::Settings::PTSGaugeStyle::Full);
}
}
+ if (object == btnWeather) {
+ if (lv_obj_get_hidden(weatherIcon)) {
+ // show weather icon and temperature
+ lv_obj_set_hidden(weatherIcon, false);
+ lv_obj_set_hidden(temperature, false);
+ lv_obj_align(calendarOuter, sidebar, LV_ALIGN_CENTER, 0, 20);
+ lv_obj_realign(calendarInner);
+ lv_obj_realign(calendarBar1);
+ lv_obj_realign(calendarBar2);
+ lv_obj_realign(calendarCrossBar1);
+ lv_obj_realign(calendarCrossBar2);
+ lv_obj_realign(dateDayOfWeek);
+ lv_obj_realign(dateDay);
+ lv_obj_realign(dateMonth);
+ settingsController.SetPTSWeather(Controllers::Settings::PTSWeather::On);
+ } else {
+ // hide weather
+ lv_obj_set_hidden(weatherIcon, true);
+ lv_obj_set_hidden(temperature, true);
+ lv_obj_align(calendarOuter, sidebar, LV_ALIGN_CENTER, 0, 0);
+ lv_obj_realign(calendarInner);
+ lv_obj_realign(calendarBar1);
+ lv_obj_realign(calendarBar2);
+ lv_obj_realign(calendarCrossBar1);
+ lv_obj_realign(calendarCrossBar2);
+ lv_obj_realign(dateDayOfWeek);
+ lv_obj_realign(dateDay);
+ lv_obj_realign(dateMonth);
+ settingsController.SetPTSWeather(Controllers::Settings::PTSWeather::Off);
+ }
+ }
if (object == btnSetColor) {
lv_obj_set_hidden(btnSetColor, true);
lv_obj_set_hidden(btnSetOpts, true);
@@ -672,6 +750,7 @@ void WatchFacePineTimeStyle::UpdateSelected(lv_obj_t* object, lv_event_t event)
lv_obj_set_hidden(btnSetColor, true);
lv_obj_set_hidden(btnSetOpts, true);
lv_obj_set_hidden(btnSteps, false);
+ lv_obj_set_hidden(btnWeather, false);
lv_obj_set_hidden(btnClose, false);
}
}
diff --git a/src/displayapp/screens/WatchFacePineTimeStyle.h b/src/displayapp/screens/WatchFacePineTimeStyle.h
index bccb224a..72537095 100644
--- a/src/displayapp/screens/WatchFacePineTimeStyle.h
+++ b/src/displayapp/screens/WatchFacePineTimeStyle.h
@@ -4,11 +4,14 @@
#include <chrono>
#include <cstdint>
#include <memory>
+#include <displayapp/Controllers.h>
#include "displayapp/screens/Screen.h"
#include "displayapp/screens/BatteryIcon.h"
#include "displayapp/Colors.h"
#include "components/datetime/DateTimeController.h"
+#include "components/ble/SimpleWeatherService.h"
#include "components/ble/BleController.h"
+#include "utility/DirtyValue.h"
namespace Pinetime {
namespace Controllers {
@@ -29,7 +32,8 @@ namespace Pinetime {
const Controllers::Ble& bleController,
Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController,
- Controllers::MotionController& motionController);
+ Controllers::MotionController& motionController,
+ Controllers::SimpleWeatherService& weather);
~WatchFacePineTimeStyle() override;
bool OnTouchEvent(TouchEvents event) override;
@@ -50,14 +54,14 @@ namespace Pinetime {
uint8_t currentDay = 0;
uint32_t savedTick = 0;
- DirtyValue<uint8_t> batteryPercentRemaining {};
- DirtyValue<bool> isCharging {};
- DirtyValue<bool> bleState {};
- DirtyValue<bool> bleRadioEnabled {};
- DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime {};
- DirtyValue<bool> motionSensorOk {};
- DirtyValue<uint32_t> stepCount {};
- DirtyValue<bool> notificationState {};
+ Utility::DirtyValue<uint8_t> batteryPercentRemaining {};
+ Utility::DirtyValue<bool> isCharging {};
+ Utility::DirtyValue<bool> bleState {};
+ Utility::DirtyValue<bool> bleRadioEnabled {};
+ Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime {};
+ Utility::DirtyValue<uint32_t> stepCount {};
+ Utility::DirtyValue<bool> notificationState {};
+ Utility::DirtyValue<std::optional<Pinetime::Controllers::SimpleWeatherService::CurrentWeather>> currentWeather {};
static Pinetime::Controllers::Settings::Colors GetNext(Controllers::Settings::Colors color);
static Pinetime::Controllers::Settings::Colors GetPrevious(Controllers::Settings::Colors color);
@@ -72,6 +76,7 @@ namespace Pinetime {
lv_obj_t* btnRandom;
lv_obj_t* btnClose;
lv_obj_t* btnSteps;
+ lv_obj_t* btnWeather;
lv_obj_t* timebar;
lv_obj_t* sidebar;
lv_obj_t* timeDD1;
@@ -81,6 +86,8 @@ namespace Pinetime {
lv_obj_t* dateDayOfWeek;
lv_obj_t* dateDay;
lv_obj_t* dateMonth;
+ lv_obj_t* weatherIcon;
+ lv_obj_t* temperature;
lv_obj_t* plugIcon;
lv_obj_t* bleIcon;
lv_obj_t* calendarOuter;
@@ -93,8 +100,6 @@ namespace Pinetime {
lv_obj_t* stepGauge;
lv_obj_t* btnSetColor;
lv_obj_t* btnSetOpts;
- lv_obj_t* lbl_btnSetColor;
- lv_obj_t* lbl_btnSetOpts;
lv_obj_t* stepIcon;
lv_obj_t* stepValue;
lv_color_t needle_colors[1];
@@ -107,13 +112,33 @@ namespace Pinetime {
Controllers::NotificationManager& notificationManager;
Controllers::Settings& settingsController;
Controllers::MotionController& motionController;
+ Controllers::SimpleWeatherService& weatherService;
void SetBatteryIcon();
void CloseMenu();
- void AlignIcons();
lv_task_t* taskRefresh;
};
}
+
+ template <>
+ struct WatchFaceTraits<WatchFace::PineTimeStyle> {
+ static constexpr WatchFace watchFace = WatchFace::PineTimeStyle;
+ static constexpr const char* name = "PineTimeStyle";
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::WatchFacePineTimeStyle(controllers.dateTimeController,
+ controllers.batteryController,
+ controllers.bleController,
+ controllers.notificationManager,
+ controllers.settingsController,
+ controllers.motionController,
+ *controllers.weatherController);
+ };
+
+ static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) {
+ return true;
+ }
+ };
}
}
diff --git a/src/displayapp/screens/WatchFaceTerminal.cpp b/src/displayapp/screens/WatchFaceTerminal.cpp
index e5ff195e..96d77741 100644
--- a/src/displayapp/screens/WatchFaceTerminal.cpp
+++ b/src/displayapp/screens/WatchFaceTerminal.cpp
@@ -104,45 +104,33 @@ void WatchFaceTerminal::Refresh() {
}
}
- currentDateTime = dateTimeController.CurrentDateTime();
-
+ currentDateTime = std::chrono::time_point_cast<std::chrono::seconds>(dateTimeController.CurrentDateTime());
if (currentDateTime.IsUpdated()) {
- auto hour = dateTimeController.Hours();
- auto minute = dateTimeController.Minutes();
- auto second = dateTimeController.Seconds();
- auto year = dateTimeController.Year();
- auto month = dateTimeController.Month();
- auto dayOfWeek = dateTimeController.DayOfWeek();
- auto day = dateTimeController.Day();
-
- if (displayedHour != hour || displayedMinute != minute || displayedSecond != second) {
- displayedHour = hour;
- displayedMinute = minute;
- displayedSecond = second;
+ uint8_t hour = dateTimeController.Hours();
+ uint8_t minute = dateTimeController.Minutes();
+ uint8_t second = dateTimeController.Seconds();
- if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
- char ampmChar[3] = "AM";
- if (hour == 0) {
- hour = 12;
- } else if (hour == 12) {
- ampmChar[0] = 'P';
- } else if (hour > 12) {
- hour = hour - 12;
- ampmChar[0] = 'P';
- }
- lv_label_set_text_fmt(label_time, "[TIME]#11cc55 %02d:%02d:%02d %s#", hour, minute, second, ampmChar);
- } else {
- lv_label_set_text_fmt(label_time, "[TIME]#11cc55 %02d:%02d:%02d", hour, minute, second);
+ if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
+ char ampmChar[3] = "AM";
+ if (hour == 0) {
+ hour = 12;
+ } else if (hour == 12) {
+ ampmChar[0] = 'P';
+ } else if (hour > 12) {
+ hour = hour - 12;
+ ampmChar[0] = 'P';
}
+ lv_label_set_text_fmt(label_time, "[TIME]#11cc55 %02d:%02d:%02d %s#", hour, minute, second, ampmChar);
+ } else {
+ lv_label_set_text_fmt(label_time, "[TIME]#11cc55 %02d:%02d:%02d", hour, minute, second);
}
- if ((year != currentYear) || (month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) {
+ currentDate = std::chrono::time_point_cast<std::chrono::days>(currentDateTime.Get());
+ if (currentDate.IsUpdated()) {
+ uint16_t year = dateTimeController.Year();
+ Controllers::DateTime::Months month = dateTimeController.Month();
+ uint8_t day = dateTimeController.Day();
lv_label_set_text_fmt(label_date, "[DATE]#007fff %04d-%02d-%02d#", short(year), char(month), char(day));
-
- currentYear = year;
- currentMonth = month;
- currentDayOfWeek = dayOfWeek;
- currentDay = day;
}
}
@@ -157,8 +145,7 @@ void WatchFaceTerminal::Refresh() {
}
stepCount = motionController.NbSteps();
- motionSensorOk = motionController.IsSensorOk();
- if (stepCount.IsUpdated() || motionSensorOk.IsUpdated()) {
+ if (stepCount.IsUpdated()) {
lv_label_set_text_fmt(stepValue, "[STEP]#ee3377 %lu steps#", stepCount.Get());
}
}
diff --git a/src/displayapp/screens/WatchFaceTerminal.h b/src/displayapp/screens/WatchFaceTerminal.h
index 67156a50..bf460866 100644
--- a/src/displayapp/screens/WatchFaceTerminal.h
+++ b/src/displayapp/screens/WatchFaceTerminal.h
@@ -4,8 +4,10 @@
#include <chrono>
#include <cstdint>
#include <memory>
+#include <displayapp/Controllers.h>
#include "displayapp/screens/Screen.h"
#include "components/datetime/DateTimeController.h"
+#include "utility/DirtyValue.h"
namespace Pinetime {
namespace Controllers {
@@ -34,25 +36,16 @@ namespace Pinetime {
void Refresh() override;
private:
- uint8_t displayedHour = -1;
- uint8_t displayedMinute = -1;
- uint8_t displayedSecond = -1;
-
- 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<int> batteryPercentRemaining {};
- DirtyValue<bool> powerPresent {};
- DirtyValue<bool> bleState {};
- DirtyValue<bool> bleRadioEnabled {};
- DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime {};
- DirtyValue<bool> motionSensorOk {};
- DirtyValue<uint32_t> stepCount {};
- DirtyValue<uint8_t> heartbeat {};
- DirtyValue<bool> heartbeatRunning {};
- DirtyValue<bool> notificationState {};
+ Utility::DirtyValue<int> batteryPercentRemaining {};
+ Utility::DirtyValue<bool> powerPresent {};
+ Utility::DirtyValue<bool> bleState {};
+ Utility::DirtyValue<bool> bleRadioEnabled {};
+ Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> currentDateTime {};
+ Utility::DirtyValue<uint32_t> stepCount {};
+ Utility::DirtyValue<uint8_t> heartbeat {};
+ Utility::DirtyValue<bool> heartbeatRunning {};
+ Utility::DirtyValue<bool> notificationState {};
+ Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::days>> currentDate;
lv_obj_t* label_time;
lv_obj_t* label_date;
@@ -75,5 +68,25 @@ namespace Pinetime {
lv_task_t* taskRefresh;
};
}
+
+ template <>
+ struct WatchFaceTraits<WatchFace::Terminal> {
+ static constexpr WatchFace watchFace = WatchFace::Terminal;
+ static constexpr const char* name = "Terminal";
+
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::WatchFaceTerminal(controllers.dateTimeController,
+ controllers.batteryController,
+ controllers.bleController,
+ controllers.notificationManager,
+ controllers.settingsController,
+ controllers.heartRateController,
+ controllers.motionController);
+ };
+
+ static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) {
+ return true;
+ }
+ };
}
}
diff --git a/src/displayapp/screens/Weather.cpp b/src/displayapp/screens/Weather.cpp
index 4921174c..25464c70 100644
--- a/src/displayapp/screens/Weather.cpp
+++ b/src/displayapp/screens/Weather.cpp
@@ -1,221 +1,197 @@
-/* Copyright (C) 2021 Avamander
+#include "displayapp/screens/Weather.h"
- This file is part of InfiniTime.
-
- InfiniTime is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published
- by the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- InfiniTime is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#include "Weather.h"
#include <lvgl/lvgl.h>
-#include <components/ble/weather/WeatherService.h>
-#include "Label.h"
-#include "components/battery/BatteryController.h"
-#include "components/ble/BleController.h"
-#include "components/ble/weather/WeatherData.h"
-using namespace Pinetime::Applications::Screens;
+#include "components/ble/SimpleWeatherService.h"
+#include "components/datetime/DateTimeController.h"
+#include "components/settings/Settings.h"
+#include "displayapp/DisplayApp.h"
+#include "displayapp/screens/WeatherSymbols.h"
+#include "displayapp/InfiniTimeTheme.h"
-Weather::Weather(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::WeatherService& weather)
- : app {app},
- weatherService(weather),
- screens {app,
- 0,
- {[this]() -> std::unique_ptr<Screen> {
- return CreateScreenTemperature();
- },
- [this]() -> std::unique_ptr<Screen> {
- return CreateScreenAir();
- },
- [this]() -> std::unique_ptr<Screen> {
- return CreateScreenClouds();
- },
- [this]() -> std::unique_ptr<Screen> {
- return CreateScreenPrecipitation();
- },
- [this]() -> std::unique_ptr<Screen> {
- return CreateScreenHumidity();
- }},
- Screens::ScreenListModes::UpDown} {
-}
+using namespace Pinetime::Applications::Screens;
-Weather::~Weather() {
- lv_obj_clean(lv_scr_act());
-}
+namespace {
+ lv_color_t TemperatureColor(Pinetime::Controllers::SimpleWeatherService::Temperature temp) {
+ if (temp.Celsius() <= 0) { // freezing
+ return Colors::blue;
+ } else if (temp.Celsius() <= 4) { // ice
+ return LV_COLOR_CYAN;
+ } else if (temp.Celsius() >= 27) { // hot
+ return Colors::deepOrange;
+ }
+ return Colors::orange; // normal
+ }
-void Weather::Refresh() {
- if (running) {
- // screens.Refresh();
+ uint8_t TemperatureStyle(Pinetime::Controllers::SimpleWeatherService::Temperature temp) {
+ if (temp.Celsius() <= 0) { // freezing
+ return LV_TABLE_PART_CELL3;
+ } else if (temp.Celsius() <= 4) { // ice
+ return LV_TABLE_PART_CELL4;
+ } else if (temp.Celsius() >= 27) { // hot
+ return LV_TABLE_PART_CELL6;
+ }
+ return LV_TABLE_PART_CELL5; // normal
}
}
-bool Weather::OnButtonPushed() {
- running = false;
- return true;
-}
+Weather::Weather(Controllers::Settings& settingsController, Controllers::SimpleWeatherService& weatherService)
+ : settingsController {settingsController}, weatherService {weatherService} {
-bool Weather::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
- return screens.OnTouchEvent(event);
-}
+ temperature = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
+ lv_obj_set_style_local_text_font(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42);
+ lv_label_set_text(temperature, "---");
+ lv_obj_align(temperature, nullptr, LV_ALIGN_CENTER, 0, -30);
+ lv_obj_set_auto_realign(temperature, true);
-std::unique_ptr<Screen> Weather::CreateScreenTemperature() {
- lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
- lv_label_set_recolor(label, true);
- std::unique_ptr<Controllers::WeatherData::Temperature>& current = weatherService.GetCurrentTemperature();
- if (current->timestamp == 0) {
- // Do not use the data, it's invalid
- lv_label_set_text_fmt(label,
- "#FFFF00 Temperature#\n\n"
- "#444444 %d#°C \n\n"
- "#444444 %d#\n\n"
- "%d\n"
- "%d\n",
- 0,
- 0,
- 0,
- 0);
- } else {
- lv_label_set_text_fmt(label,
- "#FFFF00 Temperature#\n\n"
- "#444444 %d#°C \n\n"
- "#444444 %hd#\n\n"
- "%llu\n"
- "%lu\n",
- current->temperature / 100,
- current->dewPoint,
- current->timestamp,
- current->expires);
- }
- lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
- lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
- return std::unique_ptr<Screen>(new Screens::Label(0, 5, label));
-}
+ minTemperature = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(minTemperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::bg);
+ lv_label_set_text(minTemperature, "");
+ lv_obj_align(minTemperature, temperature, LV_ALIGN_OUT_LEFT_MID, -10, 0);
+ lv_obj_set_auto_realign(minTemperature, true);
+
+ maxTemperature = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(maxTemperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::bg);
+ lv_label_set_text(maxTemperature, "");
+ lv_obj_align(maxTemperature, temperature, LV_ALIGN_OUT_RIGHT_MID, 10, 0);
+ lv_obj_set_auto_realign(maxTemperature, true);
+
+ condition = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(condition, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray);
+ lv_label_set_text(condition, "");
+ lv_obj_align(condition, temperature, LV_ALIGN_OUT_TOP_MID, 0, -10);
+ lv_obj_set_auto_realign(condition, true);
+
+ icon = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(icon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
+ lv_obj_set_style_local_text_font(icon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &fontawesome_weathericons);
+ lv_label_set_text(icon, "");
+ lv_obj_align(icon, condition, LV_ALIGN_OUT_TOP_MID, 0, 0);
+ lv_obj_set_auto_realign(icon, true);
-std::unique_ptr<Screen> Weather::CreateScreenAir() {
- lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
- lv_label_set_recolor(label, true);
- std::unique_ptr<Controllers::WeatherData::AirQuality>& current = weatherService.GetCurrentQuality();
- if (current->timestamp == 0) {
- // Do not use the data, it's invalid
- lv_label_set_text_fmt(label,
- "#FFFF00 Air quality#\n\n"
- "#444444 %s#\n"
- "#444444 %d#\n\n"
- "%d\n"
- "%d\n",
- "",
- 0,
- 0,
- 0);
- } else {
- lv_label_set_text_fmt(label,
- "#FFFF00 Air quality#\n\n"
- "#444444 %s#\n"
- "#444444 %lu#\n\n"
- "%llu\n"
- "%lu\n",
- current->polluter.c_str(),
- (current->amount / 100),
- current->timestamp,
- current->expires);
+ forecast = lv_table_create(lv_scr_act(), nullptr);
+ lv_table_set_col_cnt(forecast, Controllers::SimpleWeatherService::MaxNbForecastDays);
+ lv_table_set_row_cnt(forecast, 4);
+ // LV_TABLE_PART_CELL1: Default table style
+ lv_obj_set_style_local_border_color(forecast, LV_TABLE_PART_CELL1, LV_STATE_DEFAULT, LV_COLOR_BLACK);
+ lv_obj_set_style_local_text_color(forecast, LV_TABLE_PART_CELL1, LV_STATE_DEFAULT, Colors::lightGray);
+ // LV_TABLE_PART_CELL2: Condition icon
+ lv_obj_set_style_local_border_color(forecast, LV_TABLE_PART_CELL2, LV_STATE_DEFAULT, LV_COLOR_BLACK);
+ lv_obj_set_style_local_text_color(forecast, LV_TABLE_PART_CELL2, LV_STATE_DEFAULT, LV_COLOR_WHITE);
+ lv_obj_set_style_local_text_font(forecast, LV_TABLE_PART_CELL2, LV_STATE_DEFAULT, &fontawesome_weathericons);
+ // LV_TABLE_PART_CELL3: Freezing
+ lv_obj_set_style_local_border_color(forecast, LV_TABLE_PART_CELL3, LV_STATE_DEFAULT, LV_COLOR_BLACK);
+ lv_obj_set_style_local_text_color(forecast, LV_TABLE_PART_CELL3, LV_STATE_DEFAULT, Colors::blue);
+ // LV_TABLE_PART_CELL4: Ice
+ lv_obj_set_style_local_border_color(forecast, LV_TABLE_PART_CELL4, LV_STATE_DEFAULT, LV_COLOR_BLACK);
+ lv_obj_set_style_local_text_color(forecast, LV_TABLE_PART_CELL4, LV_STATE_DEFAULT, LV_COLOR_CYAN);
+ // LV_TABLE_PART_CELL5: Normal
+ lv_obj_set_style_local_border_color(forecast, LV_TABLE_PART_CELL5, LV_STATE_DEFAULT, LV_COLOR_BLACK);
+ lv_obj_set_style_local_text_color(forecast, LV_TABLE_PART_CELL5, LV_STATE_DEFAULT, Colors::orange);
+ // LV_TABLE_PART_CELL6: Hot
+ lv_obj_set_style_local_border_color(forecast, LV_TABLE_PART_CELL6, LV_STATE_DEFAULT, LV_COLOR_BLACK);
+ lv_obj_set_style_local_text_color(forecast, LV_TABLE_PART_CELL6, LV_STATE_DEFAULT, Colors::deepOrange);
+
+ lv_obj_align(forecast, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
+
+ for (int i = 0; i < Controllers::SimpleWeatherService::MaxNbForecastDays; i++) {
+ lv_table_set_col_width(forecast, i, 48);
+ lv_table_set_cell_type(forecast, 1, i, LV_TABLE_PART_CELL2);
+ lv_table_set_cell_align(forecast, 0, i, LV_LABEL_ALIGN_CENTER);
+ lv_table_set_cell_align(forecast, 1, i, LV_LABEL_ALIGN_CENTER);
+ lv_table_set_cell_align(forecast, 2, i, LV_LABEL_ALIGN_CENTER);
+ lv_table_set_cell_align(forecast, 3, i, LV_LABEL_ALIGN_CENTER);
}
- lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
- lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
- return std::unique_ptr<Screen>(new Screens::Label(0, 5, label));
+
+ taskRefresh = lv_task_create(RefreshTaskCallback, 1000, LV_TASK_PRIO_MID, this);
+ Refresh();
}
-std::unique_ptr<Screen> Weather::CreateScreenClouds() {
- lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
- lv_label_set_recolor(label, true);
- std::unique_ptr<Controllers::WeatherData::Clouds>& current = weatherService.GetCurrentClouds();
- if (current->timestamp == 0) {
- // Do not use the data, it's invalid
- lv_label_set_text_fmt(label,
- "#FFFF00 Clouds#\n\n"
- "#444444 %d%%#\n\n"
- "%d\n"
- "%d\n",
- 0,
- 0,
- 0);
- } else {
- lv_label_set_text_fmt(label,
- "#FFFF00 Clouds#\n\n"
- "#444444 %hhu%%#\n\n"
- "%llu\n"
- "%lu\n",
- current->amount,
- current->timestamp,
- current->expires);
- }
- lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
- lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
- return std::unique_ptr<Screen>(new Screens::Label(0, 5, label));
+Weather::~Weather() {
+ lv_task_del(taskRefresh);
+ lv_obj_clean(lv_scr_act());
}
-std::unique_ptr<Screen> Weather::CreateScreenPrecipitation() {
- lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
- lv_label_set_recolor(label, true);
- std::unique_ptr<Controllers::WeatherData::Precipitation>& current = weatherService.GetCurrentPrecipitation();
- if (current->timestamp == 0) {
- // Do not use the data, it's invalid
- lv_label_set_text_fmt(label,
- "#FFFF00 Precipitation#\n\n"
- "#444444 %d%%#\n\n"
- "%d\n"
- "%d\n",
- 0,
- 0,
- 0);
- } else {
- lv_label_set_text_fmt(label,
- "#FFFF00 Precipitation#\n\n"
- "#444444 %hhu%%#\n\n"
- "%llu\n"
- "%lu\n",
- current->amount,
- current->timestamp,
- current->expires);
+void Weather::Refresh() {
+ currentWeather = weatherService.Current();
+ if (currentWeather.IsUpdated()) {
+ auto optCurrentWeather = currentWeather.Get();
+ if (optCurrentWeather) {
+ int16_t temp = optCurrentWeather->temperature.Celsius();
+ int16_t minTemp = optCurrentWeather->minTemperature.Celsius();
+ int16_t maxTemp = optCurrentWeather->maxTemperature.Celsius();
+ char tempUnit = 'C';
+ if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
+ temp = optCurrentWeather->temperature.Fahrenheit();
+ minTemp = optCurrentWeather->minTemperature.Fahrenheit();
+ maxTemp = optCurrentWeather->maxTemperature.Fahrenheit();
+ tempUnit = 'F';
+ }
+ lv_obj_set_style_local_text_color(temperature,
+ LV_LABEL_PART_MAIN,
+ LV_STATE_DEFAULT,
+ TemperatureColor(optCurrentWeather->temperature));
+ lv_label_set_text(icon, Symbols::GetSymbol(optCurrentWeather->iconId));
+ lv_label_set_text(condition, Symbols::GetCondition(optCurrentWeather->iconId));
+ lv_label_set_text_fmt(temperature, "%d°%c", temp, tempUnit);
+ lv_label_set_text_fmt(minTemperature, "%d°", minTemp);
+ lv_label_set_text_fmt(maxTemperature, "%d°", maxTemp);
+ } else {
+ lv_label_set_text(icon, "");
+ lv_label_set_text(condition, "");
+ lv_label_set_text(temperature, "---");
+ lv_obj_set_style_local_text_color(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
+ lv_label_set_text(minTemperature, "");
+ lv_label_set_text(maxTemperature, "");
+ }
}
- lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
- lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
- return std::unique_ptr<Screen>(new Screens::Label(0, 5, label));
-}
-std::unique_ptr<Screen> Weather::CreateScreenHumidity() {
- lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
- lv_label_set_recolor(label, true);
- std::unique_ptr<Controllers::WeatherData::Humidity>& current = weatherService.GetCurrentHumidity();
- if (current->timestamp == 0) {
- // Do not use the data, it's invalid
- lv_label_set_text_fmt(label,
- "#FFFF00 Humidity#\n\n"
- "#444444 %d%%#\n\n"
- "%d\n"
- "%d\n",
- 0,
- 0,
- 0);
- } else {
- lv_label_set_text_fmt(label,
- "#FFFF00 Humidity#\n\n"
- "#444444 %hhu%%#\n\n"
- "%llu\n"
- "%lu\n",
- current->humidity,
- current->timestamp,
- current->expires);
+ currentForecast = weatherService.GetForecast();
+ if (currentForecast.IsUpdated()) {
+ auto optCurrentForecast = currentForecast.Get();
+ if (optCurrentForecast) {
+ std::tm localTime = *std::localtime(reinterpret_cast<const time_t*>(&optCurrentForecast->timestamp));
+
+ for (int i = 0; i < optCurrentForecast->nbDays; i++) {
+ int16_t maxTemp = optCurrentForecast->days[i]->maxTemperature.Celsius();
+ int16_t minTemp = optCurrentForecast->days[i]->minTemperature.Celsius();
+ if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
+ maxTemp = optCurrentForecast->days[i]->maxTemperature.Fahrenheit();
+ minTemp = optCurrentForecast->days[i]->minTemperature.Fahrenheit();
+ }
+ lv_table_set_cell_type(forecast, 2, i, TemperatureStyle(optCurrentForecast->days[i]->maxTemperature));
+ lv_table_set_cell_type(forecast, 3, i, TemperatureStyle(optCurrentForecast->days[i]->minTemperature));
+ uint8_t wday = localTime.tm_wday + i + 1;
+ if (wday > 7) {
+ wday -= 7;
+ }
+ const char* dayOfWeek = Controllers::DateTime::DayOfWeekShortToStringLow(static_cast<Controllers::DateTime::Days>(wday));
+ lv_table_set_cell_value(forecast, 0, i, dayOfWeek);
+ lv_table_set_cell_value(forecast, 1, i, Symbols::GetSymbol(optCurrentForecast->days[i]->iconId));
+ // Pad cells based on the largest number of digits on each column
+ char maxPadding[3] = " ";
+ char minPadding[3] = " ";
+ int diff = snprintf(nullptr, 0, "%d", maxTemp) - snprintf(nullptr, 0, "%d", minTemp);
+ if (diff <= 0) {
+ maxPadding[-diff] = '\0';
+ minPadding[0] = '\0';
+ } else {
+ maxPadding[0] = '\0';
+ minPadding[diff] = '\0';
+ }
+ lv_table_set_cell_value_fmt(forecast, 2, i, "%s%d", maxPadding, maxTemp);
+ lv_table_set_cell_value_fmt(forecast, 3, i, "%s%d", minPadding, minTemp);
+ }
+ } else {
+ for (int i = 0; i < Controllers::SimpleWeatherService::MaxNbForecastDays; i++) {
+ lv_table_set_cell_value(forecast, 0, i, "");
+ lv_table_set_cell_value(forecast, 1, i, "");
+ lv_table_set_cell_value(forecast, 2, i, "");
+ lv_table_set_cell_value(forecast, 3, i, "");
+ lv_table_set_cell_type(forecast, 2, i, LV_TABLE_PART_CELL1);
+ lv_table_set_cell_type(forecast, 3, i, LV_TABLE_PART_CELL1);
+ }
+ }
}
- lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
- lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
- return std::unique_ptr<Screen>(new Screens::Label(0, 5, label));
}
diff --git a/src/displayapp/screens/Weather.h b/src/displayapp/screens/Weather.h
index 459534aa..6975311e 100644
--- a/src/displayapp/screens/Weather.h
+++ b/src/displayapp/screens/Weather.h
@@ -1,45 +1,56 @@
#pragma once
-#include <memory>
-#include <components/ble/weather/WeatherService.h>
-#include "Screen.h"
-#include "ScreenList.h"
+#include <cstdint>
+#include <lvgl/lvgl.h>
+#include "displayapp/screens/Screen.h"
+#include "components/ble/SimpleWeatherService.h"
+#include "displayapp/apps/Apps.h"
+#include "displayapp/Controllers.h"
+#include "Symbols.h"
+#include "utility/DirtyValue.h"
namespace Pinetime {
- namespace Applications {
- class DisplayApp;
+ namespace Controllers {
+ class Settings;
+ }
+
+ namespace Applications {
namespace Screens {
+
class Weather : public Screen {
public:
- explicit Weather(DisplayApp* app, Pinetime::Controllers::WeatherService& weather);
-
+ Weather(Controllers::Settings& settingsController, Controllers::SimpleWeatherService& weatherService);
~Weather() override;
void Refresh() override;
- bool OnButtonPushed() override;
-
- bool OnTouchEvent(TouchEvents event) override;
-
private:
- DisplayApp* app;
- bool running = true;
-
- Controllers::WeatherService& weatherService;
-
- ScreenList<5> screens;
+ Controllers::Settings& settingsController;
+ Controllers::SimpleWeatherService& weatherService;
- std::unique_ptr<Screen> CreateScreenTemperature();
+ Utility::DirtyValue<std::optional<Controllers::SimpleWeatherService::CurrentWeather>> currentWeather {};
+ Utility::DirtyValue<std::optional<Controllers::SimpleWeatherService::Forecast>> currentForecast {};
- std::unique_ptr<Screen> CreateScreenAir();
+ lv_obj_t* icon;
+ lv_obj_t* condition;
+ lv_obj_t* temperature;
+ lv_obj_t* minTemperature;
+ lv_obj_t* maxTemperature;
+ lv_obj_t* forecast;
- std::unique_ptr<Screen> CreateScreenClouds();
+ lv_task_t* taskRefresh;
+ };
+ }
- std::unique_ptr<Screen> CreateScreenPrecipitation();
+ template <>
+ struct AppTraits<Apps::Weather> {
+ static constexpr Apps app = Apps::Weather;
+ static constexpr const char* icon = Screens::Symbols::cloudSunRain;
- std::unique_ptr<Screen> CreateScreenHumidity();
+ static Screens::Screen* Create(AppControllers& controllers) {
+ return new Screens::Weather(controllers.settingsController, *controllers.weatherController);
};
- }
+ };
}
}
diff --git a/src/displayapp/screens/WeatherSymbols.cpp b/src/displayapp/screens/WeatherSymbols.cpp
new file mode 100644
index 00000000..de66312f
--- /dev/null
+++ b/src/displayapp/screens/WeatherSymbols.cpp
@@ -0,0 +1,61 @@
+#include "displayapp/screens/WeatherSymbols.h"
+
+const char* Pinetime::Applications::Screens::Symbols::GetSymbol(const Pinetime::Controllers::SimpleWeatherService::Icons icon) {
+ switch (icon) {
+ case Pinetime::Controllers::SimpleWeatherService::Icons::Sun:
+ return Symbols::sun;
+ break;
+ case Pinetime::Controllers::SimpleWeatherService::Icons::CloudsSun:
+ return Symbols::cloudSun;
+ break;
+ case Pinetime::Controllers::SimpleWeatherService::Icons::Clouds:
+ return Symbols::cloud;
+ break;
+ case Pinetime::Controllers::SimpleWeatherService::Icons::BrokenClouds:
+ return Symbols::cloudMeatball;
+ break;
+ case Pinetime::Controllers::SimpleWeatherService::Icons::Thunderstorm:
+ return Symbols::bolt;
+ break;
+ case Pinetime::Controllers::SimpleWeatherService::Icons::Snow:
+ return Symbols::snowflake;
+ break;
+ case Pinetime::Controllers::SimpleWeatherService::Icons::CloudShowerHeavy:
+ return Symbols::cloudShowersHeavy;
+ break;
+ case Pinetime::Controllers::SimpleWeatherService::Icons::CloudSunRain:
+ return Symbols::cloudSunRain;
+ break;
+ case Pinetime::Controllers::SimpleWeatherService::Icons::Smog:
+ return Symbols::smog;
+ break;
+ default:
+ return Symbols::ban;
+ break;
+ }
+}
+
+const char* Pinetime::Applications::Screens::Symbols::GetCondition(const Pinetime::Controllers::SimpleWeatherService::Icons icon) {
+ switch (icon) {
+ case Pinetime::Controllers::SimpleWeatherService::Icons::Sun:
+ return "Clear sky";
+ case Pinetime::Controllers::SimpleWeatherService::Icons::CloudsSun:
+ return "Few clouds";
+ case Pinetime::Controllers::SimpleWeatherService::Icons::Clouds:
+ return "Scattered clouds";
+ case Pinetime::Controllers::SimpleWeatherService::Icons::BrokenClouds:
+ return "Broken clouds";
+ case Pinetime::Controllers::SimpleWeatherService::Icons::CloudShowerHeavy:
+ return "Shower rain";
+ case Pinetime::Controllers::SimpleWeatherService::Icons::CloudSunRain:
+ return "Rain";
+ case Pinetime::Controllers::SimpleWeatherService::Icons::Thunderstorm:
+ return "Thunderstorm";
+ case Pinetime::Controllers::SimpleWeatherService::Icons::Snow:
+ return "Snow";
+ case Pinetime::Controllers::SimpleWeatherService::Icons::Smog:
+ return "Mist";
+ default:
+ return "";
+ }
+}
diff --git a/src/displayapp/screens/WeatherSymbols.h b/src/displayapp/screens/WeatherSymbols.h
new file mode 100644
index 00000000..f3eeed55
--- /dev/null
+++ b/src/displayapp/screens/WeatherSymbols.h
@@ -0,0 +1,14 @@
+#pragma once
+#include "components/ble/SimpleWeatherService.h"
+#include "displayapp/screens/Symbols.h"
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+ namespace Symbols {
+ const char* GetSymbol(const Pinetime::Controllers::SimpleWeatherService::Icons icon);
+ const char* GetCondition(const Pinetime::Controllers::SimpleWeatherService::Icons icon);
+ }
+ }
+ }
+}
diff --git a/src/displayapp/screens/settings/QuickSettings.cpp b/src/displayapp/screens/settings/QuickSettings.cpp
index 05484888..c5c3071a 100644
--- a/src/displayapp/screens/settings/QuickSettings.cpp
+++ b/src/displayapp/screens/settings/QuickSettings.cpp
@@ -33,13 +33,14 @@ QuickSettings::QuickSettings(Pinetime::Applications::DisplayApp* app,
Controllers::BrightnessController& brightness,
Controllers::MotorController& motorController,
Pinetime::Controllers::Settings& settingsController,
- const Controllers::Ble& bleController)
+ const Controllers::Ble& bleController,
+ const Controllers::AlarmController& alarmController)
: app {app},
dateTimeController {dateTimeController},
brightness {brightness},
motorController {motorController},
settingsController {settingsController},
- statusIcons(batteryController, bleController) {
+ statusIcons(batteryController, bleController, alarmController) {
statusIcons.Create();
diff --git a/src/displayapp/screens/settings/QuickSettings.h b/src/displayapp/screens/settings/QuickSettings.h
index 55da6176..87c126b7 100644
--- a/src/displayapp/screens/settings/QuickSettings.h
+++ b/src/displayapp/screens/settings/QuickSettings.h
@@ -23,7 +23,8 @@ namespace Pinetime {
Controllers::BrightnessController& brightness,
Controllers::MotorController& motorController,
Pinetime::Controllers::Settings& settingsController,
- const Controllers::Ble& bleController);
+ const Controllers::Ble& bleController,
+ const Controllers::AlarmController& alarmController);
~QuickSettings() override;
diff --git a/src/displayapp/screens/settings/SettingBluetooth.cpp b/src/displayapp/screens/settings/SettingBluetooth.cpp
index 82c3dee1..e4dc695c 100644
--- a/src/displayapp/screens/settings/SettingBluetooth.cpp
+++ b/src/displayapp/screens/settings/SettingBluetooth.cpp
@@ -36,17 +36,19 @@ namespace {
SettingBluetooth::SettingBluetooth(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Settings& settingsController)
: app {app},
+ settings {settingsController},
checkboxList(
0,
1,
"Bluetooth",
Symbols::bluetooth,
settingsController.GetBleRadioEnabled() ? 0 : 1,
- [&settings = settingsController](uint32_t index) {
+ [this](uint32_t index) {
const bool priorMode = settings.GetBleRadioEnabled();
const bool newMode = options[index].radioEnabled;
if (newMode != priorMode) {
settings.SetBleRadioEnabled(newMode);
+ this->app->PushMessage(Pinetime::Applications::Display::Messages::BleRadioEnableToggle);
}
},
CreateOptionArray()) {
@@ -54,6 +56,4 @@ SettingBluetooth::SettingBluetooth(Pinetime::Applications::DisplayApp* app, Pine
SettingBluetooth::~SettingBluetooth() {
lv_obj_clean(lv_scr_act());
- // Pushing the message in the OnValueChanged function causes a freeze?
- app->PushMessage(Pinetime::Applications::Display::Messages::BleRadioEnableToggle);
}
diff --git a/src/displayapp/screens/settings/SettingBluetooth.h b/src/displayapp/screens/settings/SettingBluetooth.h
index 1e3f9b81..0cf014f5 100644
--- a/src/displayapp/screens/settings/SettingBluetooth.h
+++ b/src/displayapp/screens/settings/SettingBluetooth.h
@@ -20,6 +20,7 @@ namespace Pinetime {
private:
DisplayApp* app;
+ Pinetime::Controllers::Settings& settings;
CheckboxList checkboxList;
};
}
diff --git a/src/displayapp/screens/settings/SettingDisplay.cpp b/src/displayapp/screens/settings/SettingDisplay.cpp
index a9476432..bbc188a9 100644
--- a/src/displayapp/screens/settings/SettingDisplay.cpp
+++ b/src/displayapp/screens/settings/SettingDisplay.cpp
@@ -9,16 +9,22 @@
using namespace Pinetime::Applications::Screens;
namespace {
- void event_handler(lv_obj_t* obj, lv_event_t event) {
+ void TimeoutEventHandler(lv_obj_t* obj, lv_event_t event) {
auto* screen = static_cast<SettingDisplay*>(obj->user_data);
screen->UpdateSelected(obj, event);
}
+
+ void AlwaysOnEventHandler(lv_obj_t* obj, lv_event_t event) {
+ if (event == LV_EVENT_VALUE_CHANGED) {
+ auto* screen = static_cast<SettingDisplay*>(obj->user_data);
+ screen->ToggleAlwaysOn();
+ }
+ }
}
constexpr std::array<uint16_t, 6> SettingDisplay::options;
-SettingDisplay::SettingDisplay(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Settings& settingsController)
- : app {app}, settingsController {settingsController} {
+SettingDisplay::SettingDisplay(Pinetime::Controllers::Settings& settingsController) : settingsController {settingsController} {
lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr);
@@ -43,19 +49,26 @@ SettingDisplay::SettingDisplay(Pinetime::Applications::DisplayApp* app, Pinetime
lv_label_set_align(icon, LV_LABEL_ALIGN_CENTER);
lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0);
- char buffer[12];
+ char buffer[4];
for (unsigned int i = 0; i < options.size(); i++) {
cbOption[i] = lv_checkbox_create(container1, nullptr);
- sprintf(buffer, "%2ds", options[i] / 1000);
+ snprintf(buffer, sizeof(buffer), "%2" PRIu16 "s", options[i] / 1000);
lv_checkbox_set_text(cbOption[i], buffer);
cbOption[i]->user_data = this;
- lv_obj_set_event_cb(cbOption[i], event_handler);
+ lv_obj_set_event_cb(cbOption[i], TimeoutEventHandler);
SetRadioButtonStyle(cbOption[i]);
if (settingsController.GetScreenTimeOut() == options[i]) {
lv_checkbox_set_checked(cbOption[i], true);
}
}
+
+ alwaysOnCheckbox = lv_checkbox_create(container1, nullptr);
+ lv_checkbox_set_text(alwaysOnCheckbox, "Always On");
+ lv_checkbox_set_checked(alwaysOnCheckbox, settingsController.GetAlwaysOnDisplaySetting());
+ lv_obj_add_state(alwaysOnCheckbox, LV_STATE_DEFAULT);
+ alwaysOnCheckbox->user_data = this;
+ lv_obj_set_event_cb(alwaysOnCheckbox, AlwaysOnEventHandler);
}
SettingDisplay::~SettingDisplay() {
@@ -63,13 +76,17 @@ SettingDisplay::~SettingDisplay() {
settingsController.SaveSettings();
}
+void SettingDisplay::ToggleAlwaysOn() {
+ settingsController.SetAlwaysOnDisplaySetting(!settingsController.GetAlwaysOnDisplaySetting());
+ lv_checkbox_set_checked(alwaysOnCheckbox, settingsController.GetAlwaysOnDisplaySetting());
+}
+
void SettingDisplay::UpdateSelected(lv_obj_t* object, lv_event_t event) {
if (event == LV_EVENT_CLICKED) {
for (unsigned int i = 0; i < options.size(); i++) {
if (object == cbOption[i]) {
lv_checkbox_set_checked(cbOption[i], true);
settingsController.SetScreenTimeOut(options[i]);
- app->PushMessage(Applications::Display::Messages::UpdateTimeOut);
} else {
lv_checkbox_set_checked(cbOption[i], false);
}
diff --git a/src/displayapp/screens/settings/SettingDisplay.h b/src/displayapp/screens/settings/SettingDisplay.h
index 64212c02..3bd10a62 100644
--- a/src/displayapp/screens/settings/SettingDisplay.h
+++ b/src/displayapp/screens/settings/SettingDisplay.h
@@ -14,17 +14,18 @@ namespace Pinetime {
class SettingDisplay : public Screen {
public:
- SettingDisplay(DisplayApp* app, Pinetime::Controllers::Settings& settingsController);
+ SettingDisplay(Pinetime::Controllers::Settings& settingsController);
~SettingDisplay() override;
void UpdateSelected(lv_obj_t* object, lv_event_t event);
+ void ToggleAlwaysOn();
private:
- DisplayApp* app;
static constexpr std::array<uint16_t, 6> options = {5000, 7000, 10000, 15000, 20000, 30000};
Controllers::Settings& settingsController;
lv_obj_t* cbOption[options.size()];
+ lv_obj_t* alwaysOnCheckbox;
};
}
}
diff --git a/src/displayapp/screens/settings/SettingSetDateTime.cpp b/src/displayapp/screens/settings/SettingSetDateTime.cpp
index cf9b0638..8926ff31 100644
--- a/src/displayapp/screens/settings/SettingSetDateTime.cpp
+++ b/src/displayapp/screens/settings/SettingSetDateTime.cpp
@@ -15,8 +15,7 @@ bool SettingSetDateTime::OnTouchEvent(Pinetime::Applications::TouchEvents event)
SettingSetDateTime::SettingSetDateTime(Pinetime::Applications::DisplayApp* app,
Pinetime::Controllers::DateTime& dateTimeController,
Pinetime::Controllers::Settings& settingsController)
- : app {app},
- dateTimeController {dateTimeController},
+ : dateTimeController {dateTimeController},
settingsController {settingsController},
screens {app,
0,
diff --git a/src/displayapp/screens/settings/SettingSetDateTime.h b/src/displayapp/screens/settings/SettingSetDateTime.h
index 051b1abe..dea283f8 100644
--- a/src/displayapp/screens/settings/SettingSetDateTime.h
+++ b/src/displayapp/screens/settings/SettingSetDateTime.h
@@ -20,7 +20,6 @@ namespace Pinetime {
void Quit();
private:
- DisplayApp* app;
Controllers::DateTime& dateTimeController;
Controllers::Settings& settingsController;
diff --git a/src/displayapp/screens/settings/SettingWakeUp.cpp b/src/displayapp/screens/settings/SettingWakeUp.cpp
index 8df34c20..4649dc82 100644
--- a/src/displayapp/screens/settings/SettingWakeUp.cpp
+++ b/src/displayapp/screens/settings/SettingWakeUp.cpp
@@ -8,7 +8,7 @@
using namespace Pinetime::Applications::Screens;
-constexpr std::array<SettingWakeUp::Option, 4> SettingWakeUp::options;
+constexpr std::array<SettingWakeUp::Option, 5> SettingWakeUp::options;
namespace {
void event_handler(lv_obj_t* obj, lv_event_t event) {
@@ -27,9 +27,9 @@ SettingWakeUp::SettingWakeUp(Pinetime::Controllers::Settings& settingsController
lv_obj_set_style_local_pad_inner(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5);
lv_obj_set_style_local_border_width(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
- lv_obj_set_pos(container1, 10, 60);
+ lv_obj_set_pos(container1, 10, 35);
lv_obj_set_width(container1, LV_HOR_RES - 20);
- lv_obj_set_height(container1, LV_VER_RES - 50);
+ lv_obj_set_height(container1, LV_VER_RES - 20);
lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT);
lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr);
diff --git a/src/displayapp/screens/settings/SettingWakeUp.h b/src/displayapp/screens/settings/SettingWakeUp.h
index 28219ca1..61edabce 100644
--- a/src/displayapp/screens/settings/SettingWakeUp.h
+++ b/src/displayapp/screens/settings/SettingWakeUp.h
@@ -25,11 +25,12 @@ namespace Pinetime {
};
Controllers::Settings& settingsController;
- static constexpr std::array<Option, 4> options = {{
+ static constexpr std::array<Option, 5> options = {{
{Controllers::Settings::WakeUpMode::SingleTap, "Single Tap"},
{Controllers::Settings::WakeUpMode::DoubleTap, "Double Tap"},
{Controllers::Settings::WakeUpMode::RaiseWrist, "Raise Wrist"},
{Controllers::Settings::WakeUpMode::Shake, "Shake Wake"},
+ {Controllers::Settings::WakeUpMode::LowerWrist, "Lower Wrist"},
}};
lv_obj_t* cbOption[options.size()];
diff --git a/src/displayapp/screens/settings/SettingWatchFace.cpp b/src/displayapp/screens/settings/SettingWatchFace.cpp
index 285efa72..0d5168d2 100644
--- a/src/displayapp/screens/settings/SettingWatchFace.cpp
+++ b/src/displayapp/screens/settings/SettingWatchFace.cpp
@@ -9,6 +9,37 @@ using namespace Pinetime::Applications::Screens;
constexpr const char* SettingWatchFace::title;
constexpr const char* SettingWatchFace::symbol;
+namespace {
+ uint32_t IndexOf(const std::array<Pinetime::Applications::Screens::SettingWatchFace::Item,
+ Pinetime::Applications::UserWatchFaceTypes::Count>& watchfaces,
+ Pinetime::Applications::WatchFace watchface) {
+ size_t index = 0;
+ auto found = std::find_if(watchfaces.begin(),
+ watchfaces.end(),
+ [&index, &watchface](const Pinetime::Applications::Screens::SettingWatchFace::Item& item) {
+ const bool result = item.watchface == watchface;
+ if (!result) {
+ index++;
+ }
+ return result;
+ });
+ if (found == watchfaces.end()) {
+ index = 0;
+ }
+
+ return index;
+ }
+
+ Pinetime::Applications::WatchFace IndexToWatchFace(const std::array<Pinetime::Applications::Screens::SettingWatchFace::Item,
+ Pinetime::Applications::UserWatchFaceTypes::Count>& watchfaces,
+ size_t index) {
+ if (index >= watchfaces.size()) {
+ return watchfaces[0].watchface;
+ }
+ return watchfaces[index].watchface;
+ }
+}
+
auto SettingWatchFace::CreateScreenList() const {
std::array<std::function<std::unique_ptr<Screen>()>, nScreens> screens;
for (size_t i = 0; i < screens.size(); i++) {
@@ -20,9 +51,10 @@ auto SettingWatchFace::CreateScreenList() const {
}
SettingWatchFace::SettingWatchFace(Pinetime::Applications::DisplayApp* app,
+ std::array<Screens::SettingWatchFace::Item, UserWatchFaceTypes::Count>&& watchfaceItems,
Pinetime::Controllers::Settings& settingsController,
Pinetime::Controllers::FS& filesystem)
- : app {app},
+ : watchfaceItems {std::move(watchfaceItems)},
settingsController {settingsController},
filesystem {filesystem},
screens {app, 0, CreateScreenList(), Screens::ScreenListModes::UpDown} {
@@ -39,7 +71,12 @@ bool SettingWatchFace::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
std::unique_ptr<Screen> SettingWatchFace::CreateScreen(unsigned int screenNum) const {
std::array<Screens::CheckboxList::Item, settingsPerScreen> watchfacesOnThisScreen;
for (int i = 0; i < settingsPerScreen; i++) {
- watchfacesOnThisScreen[i] = watchfaces[screenNum * settingsPerScreen + i];
+ if (i + (screenNum * settingsPerScreen) >= watchfaceItems.size()) {
+ watchfacesOnThisScreen[i] = {"", false};
+ } else {
+ auto& item = watchfaceItems[i + (screenNum * settingsPerScreen)];
+ watchfacesOnThisScreen[i] = Screens::CheckboxList::Item {item.name, item.enabled};
+ }
}
return std::make_unique<Screens::CheckboxList>(
@@ -47,9 +84,9 @@ std::unique_ptr<Screen> SettingWatchFace::CreateScreen(unsigned int screenNum) c
nScreens,
title,
symbol,
- settingsController.GetClockFace(),
- [&settings = settingsController](uint32_t clockFace) {
- settings.SetClockFace(clockFace);
+ static_cast<uint32_t>(IndexOf(watchfaceItems, settingsController.GetWatchFace())),
+ [this, &settings = settingsController](uint32_t index) {
+ settings.SetWatchFace(IndexToWatchFace(watchfaceItems, index));
settings.SaveSettings();
},
watchfacesOnThisScreen);
diff --git a/src/displayapp/screens/settings/SettingWatchFace.h b/src/displayapp/screens/settings/SettingWatchFace.h
index 45a50e3d..9edc1f7a 100644
--- a/src/displayapp/screens/settings/SettingWatchFace.h
+++ b/src/displayapp/screens/settings/SettingWatchFace.h
@@ -19,36 +19,34 @@ namespace Pinetime {
class SettingWatchFace : public Screen {
public:
- SettingWatchFace(DisplayApp* app, Pinetime::Controllers::Settings& settingsController, Pinetime::Controllers::FS& filesystem);
+ struct Item {
+ const char* name;
+ WatchFace watchface;
+ bool enabled;
+ };
+
+ SettingWatchFace(DisplayApp* app,
+ std::array<Item, UserWatchFaceTypes::Count>&& watchfaceItems,
+ Pinetime::Controllers::Settings& settingsController,
+ Pinetime::Controllers::FS& filesystem);
~SettingWatchFace() override;
bool OnTouchEvent(TouchEvents event) override;
private:
- DisplayApp* app;
auto CreateScreenList() const;
std::unique_ptr<Screen> CreateScreen(unsigned int screenNum) const;
+ static constexpr int settingsPerScreen = 4;
+ std::array<Item, UserWatchFaceTypes::Count> watchfaceItems;
+ static constexpr int nScreens = UserWatchFaceTypes::Count > 0 ? (UserWatchFaceTypes ::Count - 1) / settingsPerScreen + 1 : 1;
+
Controllers::Settings& settingsController;
Pinetime::Controllers::FS& filesystem;
static constexpr const char* title = "Watch face";
static constexpr const char* symbol = Symbols::home;
- static constexpr int settingsPerScreen = 4;
-
- // Increment this when more space is needed
- static constexpr int nScreens = 2;
-
- std::array<Screens::CheckboxList::Item, settingsPerScreen * nScreens> watchfaces {
- {{"Digital face", true},
- {"Analog face", true},
- {"PineTimeStyle", true},
- {"Terminal", true},
- {"Infineat face", Applications::Screens::WatchFaceInfineat::IsAvailable(filesystem)},
- {"Casio G7710", Applications::Screens::WatchFaceCasioStyleG7710::IsAvailable(filesystem)},
- {"", false},
- {"", false}}};
ScreenList<nScreens> screens;
};
}
diff --git a/src/displayapp/screens/settings/SettingWeatherFormat.cpp b/src/displayapp/screens/settings/SettingWeatherFormat.cpp
new file mode 100644
index 00000000..22d281b2
--- /dev/null
+++ b/src/displayapp/screens/settings/SettingWeatherFormat.cpp
@@ -0,0 +1,63 @@
+#include "displayapp/screens/settings/SettingWeatherFormat.h"
+
+#include <lvgl/lvgl.h>
+
+#include "displayapp/DisplayApp.h"
+#include "displayapp/screens/Styles.h"
+#include "displayapp/screens/Screen.h"
+#include "displayapp/screens/Symbols.h"
+
+using namespace Pinetime::Applications::Screens;
+
+namespace {
+ struct Option {
+ Pinetime::Controllers::Settings::WeatherFormat weatherFormat;
+ const char* name;
+ };
+
+ constexpr std::array<Option, 2> options = {{
+ {Pinetime::Controllers::Settings::WeatherFormat::Metric, "Metric"},
+ {Pinetime::Controllers::Settings::WeatherFormat::Imperial, "Imperial"},
+ }};
+
+ std::array<CheckboxList::Item, CheckboxList::MaxItems> CreateOptionArray() {
+ std::array<Pinetime::Applications::Screens::CheckboxList::Item, CheckboxList::MaxItems> optionArray;
+ for (size_t i = 0; i < CheckboxList::MaxItems; i++) {
+ if (i >= options.size()) {
+ optionArray[i].name = "";
+ optionArray[i].enabled = false;
+ } else {
+ optionArray[i].name = options[i].name;
+ optionArray[i].enabled = true;
+ }
+ }
+ return optionArray;
+ }
+
+ uint32_t GetDefaultOption(Pinetime::Controllers::Settings::WeatherFormat currentOption) {
+ for (size_t i = 0; i < options.size(); i++) {
+ if (options[i].weatherFormat == currentOption) {
+ return i;
+ }
+ }
+ return 0;
+ }
+}
+
+SettingWeatherFormat::SettingWeatherFormat(Pinetime::Controllers::Settings& settingsController)
+ : checkboxList(
+ 0,
+ 1,
+ "Weather format",
+ Symbols::cloudSunRain,
+ GetDefaultOption(settingsController.GetWeatherFormat()),
+ [&settings = settingsController](uint32_t index) {
+ settings.SetWeatherFormat(options[index].weatherFormat);
+ settings.SaveSettings();
+ },
+ CreateOptionArray()) {
+}
+
+SettingWeatherFormat::~SettingWeatherFormat() {
+ lv_obj_clean(lv_scr_act());
+}
diff --git a/src/displayapp/screens/settings/SettingWeatherFormat.h b/src/displayapp/screens/settings/SettingWeatherFormat.h
new file mode 100644
index 00000000..a3d2bf4b
--- /dev/null
+++ b/src/displayapp/screens/settings/SettingWeatherFormat.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <array>
+#include <cstdint>
+#include <lvgl/lvgl.h>
+
+#include "components/settings/Settings.h"
+#include "displayapp/screens/Screen.h"
+#include "displayapp/screens/CheckboxList.h"
+
+namespace Pinetime {
+
+ namespace Applications {
+ namespace Screens {
+
+ class SettingWeatherFormat : public Screen {
+ public:
+ explicit SettingWeatherFormat(Pinetime::Controllers::Settings& settingsController);
+ ~SettingWeatherFormat() override;
+
+ private:
+ CheckboxList checkboxList;
+ };
+ }
+ }
+}
diff --git a/src/displayapp/screens/settings/Settings.cpp b/src/displayapp/screens/settings/Settings.cpp
index 065417fa..cb5ba413 100644
--- a/src/displayapp/screens/settings/Settings.cpp
+++ b/src/displayapp/screens/settings/Settings.cpp
@@ -1,7 +1,7 @@
#include "displayapp/screens/settings/Settings.h"
#include <lvgl/lvgl.h>
#include <functional>
-#include "displayapp/Apps.h"
+#include "displayapp/apps/Apps.h"
#include "displayapp/DisplayApp.h"
using namespace Pinetime::Applications::Screens;
diff --git a/src/displayapp/screens/settings/Settings.h b/src/displayapp/screens/settings/Settings.h
index 3f809753..3722c2be 100644
--- a/src/displayapp/screens/settings/Settings.h
+++ b/src/displayapp/screens/settings/Settings.h
@@ -29,7 +29,7 @@ namespace Pinetime {
static constexpr int entriesPerScreen = 4;
// Increment this when more space is needed
- static constexpr int nScreens = 3;
+ static constexpr int nScreens = 4;
static constexpr std::array<List::Applications, entriesPerScreen * nScreens> entries {{
{Symbols::sun, "Display", Apps::SettingDisplay},
@@ -38,13 +38,15 @@ namespace Pinetime {
{Symbols::home, "Watch face", Apps::SettingWatchFace},
{Symbols::shoe, "Steps", Apps::SettingSteps},
- {Symbols::clock, "Date&Time", Apps::SettingSetDateTime},
+ {Symbols::clock, "Date & Time", Apps::SettingSetDateTime},
+ {Symbols::cloudSunRain, "Weather", Apps::SettingWeatherFormat},
{Symbols::batteryHalf, "Battery", Apps::BatteryInfo},
- {Symbols::clock, "Chimes", Apps::SettingChimes},
+ {Symbols::clock, "Chimes", Apps::SettingChimes},
{Symbols::tachometer, "Shake Calib.", Apps::SettingShakeThreshold},
{Symbols::check, "Firmware", Apps::FirmwareValidation},
{Symbols::bluetooth, "Bluetooth", Apps::SettingBluetooth},
+
{Symbols::list, "About", Apps::SysInfo},
// {Symbols::none, "None", Apps::None},