diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e4a354df64..ae705c4958 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -429,6 +429,7 @@ list(APPEND SOURCE_FILES displayapp/screens/WatchFaceTerminal.cpp displayapp/screens/WatchFacePineTimeStyle.cpp displayapp/screens/WatchFaceCasioStyleG7710.cpp + displayapp/screens/WatchFaceNumerals.cpp displayapp/screens/WatchFacePrideFlag.cpp ## diff --git a/src/displayapp/UserApps.h b/src/displayapp/UserApps.h index 25926edc40..731a4e0369 100644 --- a/src/displayapp/UserApps.h +++ b/src/displayapp/UserApps.h @@ -14,6 +14,7 @@ #include "displayapp/screens/WatchFaceInfineat.h" #include "displayapp/screens/WatchFacePineTimeStyle.h" #include "displayapp/screens/WatchFaceTerminal.h" +#include "displayapp/screens/WatchFaceNumerals.h" #include "displayapp/screens/WatchFacePrideFlag.h" namespace Pinetime { diff --git a/src/displayapp/apps/Apps.h.in b/src/displayapp/apps/Apps.h.in index d440b598d1..b8f90b2753 100644 --- a/src/displayapp/apps/Apps.h.in +++ b/src/displayapp/apps/Apps.h.in @@ -55,6 +55,7 @@ namespace Pinetime { Terminal, Infineat, CasioStyleG7710, + Numerals, PrideFlag, }; diff --git a/src/displayapp/apps/CMakeLists.txt b/src/displayapp/apps/CMakeLists.txt index 93196ed6a0..c00203128b 100644 --- a/src/displayapp/apps/CMakeLists.txt +++ b/src/displayapp/apps/CMakeLists.txt @@ -28,6 +28,7 @@ else() set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::Terminal") set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::Infineat") set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::CasioStyleG7710") + set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::Numerals") set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::PrideFlag") set(WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}" CACHE STRING "List of watch faces to build into the firmware") endif() diff --git a/src/displayapp/screens/WatchFaceNumerals.cpp b/src/displayapp/screens/WatchFaceNumerals.cpp new file mode 100644 index 0000000000..542b6aea9c --- /dev/null +++ b/src/displayapp/screens/WatchFaceNumerals.cpp @@ -0,0 +1,155 @@ +#include "displayapp/screens/WatchFaceNumerals.h" + +#include +#include +#include "displayapp/screens/Symbols.h" +#include "components/ble/NotificationManager.h" +#include "components/settings/Settings.h" + +using namespace Pinetime::Applications::Screens; + +WatchFaceNumerals::WatchFaceNumerals(Controllers::DateTime& dateTimeController, + Controllers::NotificationManager& notificationManager, + Controllers::Settings& settingsController, + Controllers::FS& filesystem) + : currentDateTime {{}}, + dateTimeController {dateTimeController}, + notificationManager {notificationManager}, + settingsController {settingsController} { + + lfs_file f = {}; + if (filesystem.FileOpen(&f, "/fonts/rounded_large.bin", LFS_O_RDONLY) >= 0) { + filesystem.FileClose(&f); + font_large = lv_font_load("F:/fonts/rounded_large.bin"); + } + + if (filesystem.FileOpen(&f, "/fonts/rounded_small.bin", LFS_O_RDONLY) >= 0) { + filesystem.FileClose(&f); + font_small = lv_font_load("F:/fonts/rounded_small.bin"); + } + + notificationIcon = lv_obj_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_bg_color(notificationIcon, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_AQUA); + lv_obj_set_style_local_radius(notificationIcon, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); + lv_obj_set_size(notificationIcon, 16, 16); + lv_obj_align(notificationIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 4, -76); + lv_obj_set_hidden(notificationIcon, true); + + labelTimeHour = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_font(labelTimeHour, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_large); + lv_obj_set_style_local_text_color(labelTimeHour, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); + lv_obj_align(labelTimeHour, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -160, -125); + + labelTimeMinute = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_font(labelTimeMinute, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_large); + lv_obj_set_style_local_text_color(labelTimeMinute, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_AQUA); + lv_obj_align(labelTimeMinute, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -160, 0); + + labelTimeAMPM1 = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text_static(labelTimeAMPM1, ""); + lv_obj_set_style_local_text_font(labelTimeAMPM1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_small); + lv_obj_set_style_local_text_color(labelTimeAMPM1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999)); + lv_obj_align(labelTimeAMPM1, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 2, -34); + // We use two labels for more flexibility with alignment + labelTimeAMPM2 = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text_static(labelTimeAMPM2, "M"); + lv_obj_set_style_local_text_font(labelTimeAMPM2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_small); + lv_obj_set_style_local_text_color(labelTimeAMPM2, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999)); + lv_obj_align(labelTimeAMPM2, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); + + 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_WHITE); + lv_label_set_text(dateDayOfWeek, "---"); + lv_obj_set_style_local_text_font(dateDayOfWeek, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_small); + lv_obj_align(dateDayOfWeek, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 0, 0); + + 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_WHITE); + lv_label_set_text(dateDay, "--"); + lv_obj_set_style_local_text_font(dateDay, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_small); + lv_obj_align(dateDay, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 0, 36); + + taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); + Refresh(); +} + +WatchFaceNumerals::~WatchFaceNumerals() { + lv_task_del(taskRefresh); + + if (font_large != nullptr) { + lv_font_free(font_large); + } + if (font_small != nullptr) { + lv_font_free(font_small); + } + + lv_obj_clean(lv_scr_act()); +} + +void WatchFaceNumerals::Refresh() { + notificationState = notificationManager.AreNewNotificationsAvailable(); + if (notificationState.IsUpdated()) { + lv_obj_set_hidden(notificationIcon, !notificationState.Get()); + } + + currentDateTime = 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(); + + 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(labelTimeAMPM1, ampmChar); + lv_label_set_text_fmt(labelTimeHour, "%02d", hour); + lv_label_set_text_fmt(labelTimeMinute, "%02d", minute); + } else { + lv_obj_set_hidden(labelTimeAMPM2, true); + lv_label_set_text_fmt(labelTimeHour, "%02d", hour); + lv_label_set_text_fmt(labelTimeMinute, "%02d", minute); + } + } + + if ((year != currentYear) || (month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) { + lv_label_set_text_static(dateDayOfWeek, dateTimeController.DayOfWeekShortToString()); + lv_label_set_text_fmt(dateDay, "%d", day); + lv_obj_realign(dateDay); + + currentYear = year; + currentMonth = month; + currentDayOfWeek = dayOfWeek; + currentDay = day; + } + } +} + +bool WatchFaceNumerals::IsAvailable(Pinetime::Controllers::FS& filesystem) { + lfs_file file = {}; + + if (filesystem.FileOpen(&file, "/fonts/rounded_small.bin", LFS_O_RDONLY) < 0) { + return false; + } + + filesystem.FileClose(&file); + if (filesystem.FileOpen(&file, "/fonts/rounded_large.bin", LFS_O_RDONLY) < 0) { + return false; + } + + filesystem.FileClose(&file); + return true; +} diff --git a/src/displayapp/screens/WatchFaceNumerals.h b/src/displayapp/screens/WatchFaceNumerals.h new file mode 100644 index 0000000000..c5711e0a52 --- /dev/null +++ b/src/displayapp/screens/WatchFaceNumerals.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "displayapp/screens/Screen.h" +#include "components/datetime/DateTimeController.h" +#include "utility/DirtyValue.h" +#include "displayapp/apps/Apps.h" + +namespace Pinetime { + namespace Controllers { + class Settings; + class NotificationManager; + } + + namespace Applications { + namespace Screens { + + class WatchFaceNumerals : public Screen { + public: + WatchFaceNumerals(Controllers::DateTime& dateTimeController, + Controllers::NotificationManager& notificationManager, + Controllers::Settings& settingsController, + Controllers::FS& fs); + ~WatchFaceNumerals() override; + + void Refresh() override; + + 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; + Utility::DirtyValue notificationState {}; + Utility::DirtyValue> currentDateTime {}; + + lv_obj_t* labelTimeHour; + lv_obj_t* labelTimeMinute; + lv_obj_t* labelTimeAMPM1; + lv_obj_t* labelTimeAMPM2; + lv_obj_t* label_date; + lv_obj_t* notificationIcon; + lv_obj_t* dateDay; + lv_obj_t* dateDayOfWeek; + + Controllers::DateTime& dateTimeController; + Controllers::NotificationManager& notificationManager; + Controllers::Settings& settingsController; + + lv_font_t* font_large = nullptr; + lv_font_t* font_small = nullptr; + + lv_task_t* taskRefresh; + }; + } + + template <> + struct WatchFaceTraits { + static constexpr WatchFace watchFace = WatchFace::Numerals; + static constexpr const char* name = "Numerals"; + + static Screens::Screen* Create(AppControllers& controllers) { + return new Screens::WatchFaceNumerals(controllers.dateTimeController, + controllers.notificationManager, + controllers.settingsController, + controllers.filesystem); + }; + + static bool IsAvailable(Pinetime::Controllers::FS& filesystem) { + return Screens::WatchFaceNumerals::IsAvailable(filesystem); + } + }; + } +} diff --git a/src/resources/fonts.json b/src/resources/fonts.json index c4a63349a1..2f8a9f6807 100644 --- a/src/resources/fonts.json +++ b/src/resources/fonts.json @@ -58,5 +58,29 @@ "size": 115, "format": "bin", "target_path": "/fonts/" + }, + "rounded_large": { + "sources": [ + { + "file": "fonts/BloggerSans-Bold.ttf", + "symbols": "0123456789" + } + ], + "bpp": 1, + "size": 150, + "format": "bin", + "target_path": "/fonts/" + }, + "rounded_small": { + "sources": [ + { + "file": "fonts/BloggerSans-Bold.ttf", + "symbols": "0123456789APMONTUEWDHRFIS" + } + ], + "bpp": 1, + "size": 30, + "format": "bin", + "target_path": "/fonts/" } } diff --git a/src/resources/fonts/BloggerSans-Bold.ttf b/src/resources/fonts/BloggerSans-Bold.ttf new file mode 100644 index 0000000000..d287255d91 Binary files /dev/null and b/src/resources/fonts/BloggerSans-Bold.ttf differ