diff --git a/.gitconfig b/.gitconfig deleted file mode 100644 index b2274475..00000000 --- a/.gitconfig +++ /dev/null @@ -1,5 +0,0 @@ -[core] - whitespace = blank-at-eol,blank-at-eof,space-before-tab - autocrlf = input -[apply] - whitespace = fix diff --git a/.gitignore b/.gitignore index 32ae7f6f..edb1a2cb 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,10 @@ build tools node_modules +# My stuff +#draft_pictures +node_modules + # Resulting binary files *.a *.so diff --git a/README.md b/README.md index e4f6707f..4ee9cfb6 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,48 @@ -# [InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime) +# [InfiniTime](https://github.com/Eve1374/InfiniTime) ![InfiniTime logo](doc/logo/infinitime-logo-small.jpg "InfiniTime Logo") Fast open-source firmware for the [PineTime smartwatch](https://pine64.org/devices/pinetime/) with many features, written in modern C++. +## Quick notes on this InfiniTime version + +- I copied the source code from this git repo : [InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime) +- I added a watch face "WatchFaceMeow" whose main features are to be pink and have info about the alarm status +- I stored the compile commands in scripts compile.sh to run from InfiniTime/ folder, and make_pine_mcu.sh to build the image must be run from InfiniTime/build/ (compile.sh copies make_pine_mcu.sh to build/ +- The file to flash to the pinetime is InfiniTime/build/pinetime-mcuboot-app-dfu-1.14.0.zip : I didn't change the version compared to the one I downloaded from [InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime) so make sure not to keep keep a copy of it + +Here are pictures with and without alarm set : + +![Meow alarm set](doc/ui/meow_alarmset.png "Meow WatchFace, alarm set") ![Meow alarm not set](doc/ui/meow_alarmnotset.png "Meow WatchFace, alarm not set") + +Original repo : [InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime) + +- [Getting started with InfiniTime](doc/gettingStarted/gettingStarted-1.0.md) +- [Updating the software](doc/gettingStarted/updating-software.md) +- [About the firmware and bootloader](doc/gettingStarted/about-software.md) +- [PineTimeStyle Watch face](https://pine64.org/documentation/PineTime/Watchfaces/PineTimeStyle) + - [Weather integration](https://pine64.org/documentation/PineTime/Software/InfiniTime_weather/) + +## Welcome to my InfiniTime fork ?! + +Branches : + +- main : shows this doc +- alarm-status-on-infineat : shows the alarm status on infineat, can be enabled or disabled from the settigns menu that is updated accordingly : + +![Infineat settings](doc/ui/infineat_settings.png "Infineat settings") + +- my-custom-infinitime : branch were I put things that I want for myself, like a watchface with paw instead of shoe icon for steps counter : + +![Meow watchface](doc/ui/meow_alarmset.png "Meow watchface") + ## New to InfiniTime? - [Getting started with InfiniTime](doc/gettingStarted/gettingStarted-1.0.md) - [Updating the software](doc/gettingStarted/updating-software.md) - [About the firmware and bootloader](doc/gettingStarted/about-software.md) -- [PineTimeStyle Watch face](https://wiki.pine64.org/wiki/PineTimeStyle) - - [Weather integration](https://wiki.pine64.org/wiki/Infinitime-Weather) +- [PineTimeStyle Watch face](https://pine64.org/documentation/PineTime/Watchfaces/PineTimeStyle) + - [Weather integration](https://pine64.org/documentation/PineTime/Software/InfiniTime_weather/) ### Companion apps diff --git a/compile.sh b/compile.sh new file mode 100755 index 00000000..fc3a6836 --- /dev/null +++ b/compile.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +rm -r build + +cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=/home/eve/Work/gcc-arm-none-eabi-10.3-2021.10 -DNRF5_SDK_PATH=/home/eve/Work/nRF5_SDK_17.1.0_ddde560 -DTARGET_DEVICE=PINETIME -DBUILD_DFU=1 -DBUILD_RESOURCES=1 -B build -DCMAKE_BUILD_TYPE=Release + +cp make_pine_mcu.sh build/ + diff --git a/doc/lvgl_align.png b/doc/lvgl_align.png new file mode 100644 index 00000000..189c573e Binary files /dev/null and b/doc/lvgl_align.png differ diff --git a/doc/palettes.xcf b/doc/palettes.xcf new file mode 100644 index 00000000..bfad74e0 Binary files /dev/null and b/doc/palettes.xcf differ diff --git a/doc/ui/infineat_settings.png b/doc/ui/infineat_settings.png new file mode 100644 index 00000000..0dffd5e7 Binary files /dev/null and b/doc/ui/infineat_settings.png differ diff --git a/doc/ui/meow_alarmnotset.png b/doc/ui/meow_alarmnotset.png new file mode 100644 index 00000000..ac11e47e Binary files /dev/null and b/doc/ui/meow_alarmnotset.png differ diff --git a/doc/ui/meow_alarmset.png b/doc/ui/meow_alarmset.png new file mode 100644 index 00000000..e0b4b3f7 Binary files /dev/null and b/doc/ui/meow_alarmset.png differ diff --git a/draft_pictures/cat.png b/draft_pictures/cat.png new file mode 100644 index 00000000..da3e6f72 Binary files /dev/null and b/draft_pictures/cat.png differ diff --git a/draft_pictures/cat.xcf b/draft_pictures/cat.xcf new file mode 100644 index 00000000..6bd6671c Binary files /dev/null and b/draft_pictures/cat.xcf differ diff --git a/draft_pictures/cat_clean.xcf b/draft_pictures/cat_clean.xcf new file mode 100644 index 00000000..b554ef17 Binary files /dev/null and b/draft_pictures/cat_clean.xcf differ diff --git a/draft_pictures/cat_small.xcf b/draft_pictures/cat_small.xcf new file mode 100644 index 00000000..254bdd31 Binary files /dev/null and b/draft_pictures/cat_small.xcf differ diff --git a/draft_pictures/coordinates.xcf b/draft_pictures/coordinates.xcf new file mode 100644 index 00000000..e9e6d040 Binary files /dev/null and b/draft_pictures/coordinates.xcf differ diff --git a/make_pine.sh b/make_pine.sh new file mode 100755 index 00000000..006c2421 --- /dev/null +++ b/make_pine.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +#cp ./displayapp/apps/Apps.h ../src/displayapp/apps/Apps.h + +make -j4 pinetime-app + diff --git a/make_pine_mcu.sh b/make_pine_mcu.sh new file mode 100755 index 00000000..87c00ff2 --- /dev/null +++ b/make_pine_mcu.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +#cp ./displayapp/apps/Apps.h ../src/displayapp/apps/Apps.h + +make clean -j4 pinetime-mcuboot-app + diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index b8320c49..00000000 --- a/package-lock.json +++ /dev/null @@ -1,183 +0,0 @@ -{ - "name": "InfiniTime", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "dependencies": { - "lv_font_conv": "^1.5.2" - } - }, - "node_modules/lv_font_conv": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/lv_font_conv/-/lv_font_conv-1.5.2.tgz", - "integrity": "sha512-0UapRSTkVP/pnB8Z4r2HDHx5p2dJx/xUG1+14u/WXo59mwuC7BahR+Bnx/66jKoDrG1wFQwn9ZzoyMxRHOD9bg==", - "bundleDependencies": [ - "argparse", - "bit-buffer", - "debug", - "make-error", - "mkdirp", - "opentype.js", - "pngjs" - ], - "dependencies": { - "argparse": "^2.0.0", - "bit-buffer": "^0.2.5", - "debug": "^4.1.1", - "make-error": "^1.3.5", - "mkdirp": "^1.0.4", - "opentype.js": "^1.1.0", - "pngjs": "^6.0.0" - }, - "bin": { - "lv_font_conv": "lv_font_conv.js" - } - }, - "node_modules/lv_font_conv/node_modules/argparse": { - "version": "2.0.1", - "inBundle": true, - "license": "Python-2.0" - }, - "node_modules/lv_font_conv/node_modules/bit-buffer": { - "version": "0.2.5", - "inBundle": true, - "license": "MIT" - }, - "node_modules/lv_font_conv/node_modules/debug": { - "version": "4.3.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/lv_font_conv/node_modules/make-error": { - "version": "1.3.6", - "inBundle": true, - "license": "ISC" - }, - "node_modules/lv_font_conv/node_modules/mkdirp": { - "version": "1.0.4", - "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/lv_font_conv/node_modules/ms": { - "version": "2.1.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/lv_font_conv/node_modules/opentype.js": { - "version": "1.3.3", - "inBundle": true, - "license": "MIT", - "dependencies": { - "string.prototype.codepointat": "^0.2.1", - "tiny-inflate": "^1.0.3" - }, - "bin": { - "ot": "bin/ot" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/lv_font_conv/node_modules/pngjs": { - "version": "6.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=12.13.0" - } - }, - "node_modules/lv_font_conv/node_modules/string.prototype.codepointat": { - "version": "0.2.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/lv_font_conv/node_modules/tiny-inflate": { - "version": "1.0.3", - "inBundle": true, - "license": "MIT" - } - }, - "dependencies": { - "lv_font_conv": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/lv_font_conv/-/lv_font_conv-1.5.2.tgz", - "integrity": "sha512-0UapRSTkVP/pnB8Z4r2HDHx5p2dJx/xUG1+14u/WXo59mwuC7BahR+Bnx/66jKoDrG1wFQwn9ZzoyMxRHOD9bg==", - "requires": { - "argparse": "^2.0.0", - "bit-buffer": "^0.2.5", - "debug": "^4.1.1", - "make-error": "^1.3.5", - "mkdirp": "^1.0.4", - "opentype.js": "^1.1.0", - "pngjs": "^6.0.0" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "bundled": true - }, - "bit-buffer": { - "version": "0.2.5", - "bundled": true - }, - "debug": { - "version": "4.3.1", - "bundled": true, - "requires": { - "ms": "2.1.2" - } - }, - "make-error": { - "version": "1.3.6", - "bundled": true - }, - "mkdirp": { - "version": "1.0.4", - "bundled": true - }, - "ms": { - "version": "2.1.2", - "bundled": true - }, - "opentype.js": { - "version": "1.3.3", - "bundled": true, - "requires": { - "string.prototype.codepointat": "^0.2.1", - "tiny-inflate": "^1.0.3" - } - }, - "pngjs": { - "version": "6.0.0", - "bundled": true - }, - "string.prototype.codepointat": { - "version": "0.2.1", - "bundled": true - }, - "tiny-inflate": { - "version": "1.0.3", - "bundled": true - } - } - } - } -} diff --git a/package.json b/package.json index d339766c..6577fac5 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "lv_font_conv": "^1.5.2" + "lv_font_conv": "^1.5.3" } } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0229ce17..587e43d5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -371,7 +371,7 @@ list(APPEND SOURCE_FILES displayapp/screens/StopWatch.cpp displayapp/screens/BatteryIcon.cpp displayapp/screens/BleIcon.cpp - displayapp/screens/AlarmIcon.cpp + displayapp/screens/AlarmIcon.cpp displayapp/screens/NotificationIcon.cpp displayapp/screens/SystemInfo.cpp displayapp/screens/Label.cpp diff --git a/src/Version.h.in b/src/Version.h.in index 0d6219c0..dfeccdbc 100644 --- a/src/Version.h.in +++ b/src/Version.h.in @@ -19,4 +19,4 @@ namespace Pinetime { static constexpr const char* commitHash = "@PROJECT_GIT_COMMIT_HASH@"; static constexpr const char* versionString = "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@"; }; -} \ No newline at end of file +} diff --git a/src/components/ble/SimpleWeatherService.cpp b/src/components/ble/SimpleWeatherService.cpp index 146152f8..504cad14 100644 --- a/src/components/ble/SimpleWeatherService.cpp +++ b/src/components/ble/SimpleWeatherService.cpp @@ -80,7 +80,7 @@ int WeatherCallback(uint16_t /*connHandle*/, uint16_t /*attrHandle*/, struct ble return static_cast(arg)->OnCommand(ctxt); } -SimpleWeatherService::SimpleWeatherService(const DateTime& dateTimeController) : dateTimeController(dateTimeController) { +SimpleWeatherService::SimpleWeatherService(DateTime& dateTimeController) : dateTimeController(dateTimeController) { } void SimpleWeatherService::Init() { diff --git a/src/components/ble/SimpleWeatherService.h b/src/components/ble/SimpleWeatherService.h index 4bbefcfc..03d2f6ff 100644 --- a/src/components/ble/SimpleWeatherService.h +++ b/src/components/ble/SimpleWeatherService.h @@ -40,7 +40,7 @@ namespace Pinetime { class SimpleWeatherService { public: - explicit SimpleWeatherService(const DateTime& dateTimeController); + explicit SimpleWeatherService(DateTime& dateTimeController); void Init(); @@ -140,7 +140,7 @@ namespace Pinetime { uint16_t eventHandle {}; - const Pinetime::Controllers::DateTime& dateTimeController; + Pinetime::Controllers::DateTime& dateTimeController; std::optional currentWeather; std::optional forecast; diff --git a/src/components/datetime/DateTimeController.cpp b/src/components/datetime/DateTimeController.cpp index f0ccb5e5..7f58c9b3 100644 --- a/src/components/datetime/DateTimeController.cpp +++ b/src/components/datetime/DateTimeController.cpp @@ -1,6 +1,8 @@ #include "components/datetime/DateTimeController.h" #include #include +#include +#include "nrf_assert.h" using namespace Pinetime::Controllers; @@ -12,11 +14,16 @@ namespace { } DateTime::DateTime(Controllers::Settings& settingsController) : settingsController {settingsController} { + mutex = xSemaphoreCreateMutex(); + ASSERT(mutex != nullptr); + xSemaphoreGive(mutex); } void DateTime::SetCurrentTime(std::chrono::time_point t) { + xSemaphoreTake(mutex, portMAX_DELAY); this->currentDateTime = t; - UpdateTime(previousSystickCounter); // Update internal state without updating the time + UpdateTime(previousSystickCounter, true); // Update internal state without updating the time + xSemaphoreGive(mutex); } void DateTime::SetTime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { @@ -29,13 +36,15 @@ void DateTime::SetTime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, /* .tm_year = */ year - 1900, }; - tm.tm_isdst = -1; // Use DST value from local time zone - currentDateTime = std::chrono::system_clock::from_time_t(std::mktime(&tm)); - NRF_LOG_INFO("%d %d %d ", day, month, year); NRF_LOG_INFO("%d %d %d ", hour, minute, second); - UpdateTime(previousSystickCounter); + tm.tm_isdst = -1; // Use DST value from local time zone + + xSemaphoreTake(mutex, portMAX_DELAY); + currentDateTime = std::chrono::system_clock::from_time_t(std::mktime(&tm)); + UpdateTime(previousSystickCounter, true); + xSemaphoreGive(mutex); systemTask->PushMessage(System::Messages::OnNewTime); } @@ -45,25 +54,34 @@ void DateTime::SetTimeZone(int8_t timezone, int8_t dst) { dstOffset = dst; } -void DateTime::UpdateTime(uint32_t systickCounter) { +std::chrono::time_point DateTime::CurrentDateTime() { + xSemaphoreTake(mutex, portMAX_DELAY); + UpdateTime(nrf_rtc_counter_get(portNRF_RTC_REG), false); + xSemaphoreGive(mutex); + return currentDateTime; +} + +void DateTime::UpdateTime(uint32_t systickCounter, bool forceUpdate) { // Handle systick counter overflow uint32_t systickDelta = 0; if (systickCounter < previousSystickCounter) { - systickDelta = 0xffffff - previousSystickCounter; + systickDelta = static_cast(portNRF_RTC_MAXTICKS) - previousSystickCounter; systickDelta += systickCounter + 1; } else { systickDelta = systickCounter - previousSystickCounter; } - /* - * 1000 ms = 1024 ticks - */ - auto correctedDelta = systickDelta / 1024; - auto rest = systickDelta % 1024; + auto correctedDelta = systickDelta / configTICK_RATE_HZ; + // If a second hasn't passed, there is nothing to do + // If the time has been changed, set forceUpdate to trigger internal state updates + if (correctedDelta == 0 && !forceUpdate) { + return; + } + auto rest = systickDelta % configTICK_RATE_HZ; if (systickCounter >= rest) { previousSystickCounter = systickCounter - rest; } else { - previousSystickCounter = 0xffffff - (rest - systickCounter); + previousSystickCounter = static_cast(portNRF_RTC_MAXTICKS) - (rest - systickCounter - 1); } currentDateTime += std::chrono::seconds(correctedDelta); diff --git a/src/components/datetime/DateTimeController.h b/src/components/datetime/DateTimeController.h index f719df7d..5a453f20 100644 --- a/src/components/datetime/DateTimeController.h +++ b/src/components/datetime/DateTimeController.h @@ -5,6 +5,8 @@ #include #include #include "components/settings/Settings.h" +#include +#include namespace Pinetime { namespace System { @@ -45,8 +47,6 @@ namespace Pinetime { */ void SetTimeZone(int8_t timezone, int8_t dst); - void UpdateTime(uint32_t systickCounter); - uint16_t Year() const { return 1900 + localTime.tm_year; } @@ -124,12 +124,10 @@ namespace Pinetime { static const char* MonthShortToStringLow(Months month); static const char* DayOfWeekShortToStringLow(Days day); - std::chrono::time_point CurrentDateTime() const { - return currentDateTime; - } + std::chrono::time_point CurrentDateTime(); - std::chrono::time_point UTCDateTime() const { - return currentDateTime - std::chrono::seconds((tzOffset + dstOffset) * 15 * 60); + std::chrono::time_point UTCDateTime() { + return CurrentDateTime() - std::chrono::seconds((tzOffset + dstOffset) * 15 * 60); } std::chrono::seconds Uptime() const { @@ -141,10 +139,14 @@ namespace Pinetime { std::string FormattedTime(); private: + void UpdateTime(uint32_t systickCounter, bool forceUpdate); + std::tm localTime; int8_t tzOffset = 0; int8_t dstOffset = 0; + SemaphoreHandle_t mutex = nullptr; + uint32_t previousSystickCounter = 0; std::chrono::time_point currentDateTime; std::chrono::seconds uptime {0}; diff --git a/src/components/datetime/TODO.md b/src/components/datetime/TODO.md new file mode 100644 index 00000000..e9590898 --- /dev/null +++ b/src/components/datetime/TODO.md @@ -0,0 +1,41 @@ +# Refactoring needed + +## Context + +The [PR #2041 - Continuous time updates](https://github.com/InfiniTimeOrg/InfiniTime/pull/2041) highlighted some +limitations in the design of DateTimeController: the granularity of the time returned by `DateTime::CurrentDateTime()` +is limited by the frequency at which SystemTask calls `DateTime::UpdateTime()`, which is currently set to 100ms. + +@mark9064 provided more details +in [this comment](https://github.com/InfiniTimeOrg/InfiniTime/pull/2041#issuecomment-2048528967). + +The [PR #2041 - Continuous time updates](https://github.com/InfiniTimeOrg/InfiniTime/pull/2041) provided some changes +to `DateTime` controller that improves the granularity of the time returned by `DateTime::CurrentDateTime()`. + +However, the review showed that `DateTime` cannot be `const` anymore, even when it's only used to get the current time, +since `DateTime::CurrentDateTime()` changes the internal state of the instance. + +We tried to identify alternative implementation that would have maintained the "const correctness" but we eventually +figured that this would lead to a re-design of `DateTime` which was out of scope of the initial PR (Continuous time +updates and always on display). + +So we decided to merge this PR #2041 and agree to fix/improve `DateTime` later on. + +## What needs to be done? + +Improve/redesign `DateTime` so that it + +* provides a very granular (ideally down to the millisecond) date and time via `CurrentDateTime()`. +* can be declared/passed as `const` when it's only used to **get** the time. +* limits the use of mutex as much as possible (an ideal implementation would not use any mutex, but this might not be + possible). +* improves the design of `DateTime::Seconds()`, `DateTime::Minutes()`, `DateTime::Hours()`, etc as + explained [in this comment](https://github.com/InfiniTimeOrg/InfiniTime/pull/2054#pullrequestreview-2037033105). + +Once this redesign is implemented, all instances/references to `DateTime` should be reviewed and updated to use `const` +where appropriate. + +Please check the following PR to get more context about this redesign: + +* [#2041 - Continuous time updates by @mark9064](https://github.com/InfiniTimeOrg/InfiniTime/pull/2041) +* [#2054 - Continuous time update - Alternative implementation to #2041 by @JF002](https://github.com/InfiniTimeOrg/InfiniTime/pull/2054) \ No newline at end of file diff --git a/src/components/settings/Settings.h b/src/components/settings/Settings.h index 73225ac1..40fd2915 100644 --- a/src/components/settings/Settings.h +++ b/src/components/settings/Settings.h @@ -124,17 +124,6 @@ namespace Pinetime { return settings.watchFaceInfineat.showSideCover; }; - void SetInfineatShowAlarmStatus(bool show) { - if (show != settings.watchFaceInfineat.showAlarmStatus) { - settings.watchFaceInfineat.showAlarmStatus = show; - settingsChanged = true; - } - }; - - bool GetInfineatShowAlarmStatus() const { - return settings.watchFaceInfineat.showAlarmStatus; - }; - void SetInfineatColorIndex(int index) { if (index != settings.watchFaceInfineat.colorIndex) { settings.watchFaceInfineat.colorIndex = index; diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index d9b2e9b3..17f268ff 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -19,6 +19,7 @@ #include "displayapp/screens/Metronome.h" #include "displayapp/screens/Music.h" #include "displayapp/screens/Navigation.h" +#include "displayapp/screens/Calendar.h" #include "displayapp/screens/Notifications.h" #include "displayapp/screens/SystemInfo.h" #include "displayapp/screens/Tile.h" @@ -125,6 +126,7 @@ void DisplayApp::Start(System::BootErrors error) { bootError = error; lvgl.Init(); + motorController.Init(); if (error == System::BootErrors::TouchController) { LoadNewScreen(Apps::Error, DisplayApp::FullRefreshDirections::None); @@ -150,7 +152,6 @@ void DisplayApp::Process(void* instance) { void DisplayApp::InitHw() { brightnessController.Init(); ApplyBrightness(); - motorController.Init(); lcd.Init(); } diff --git a/src/displayapp/InfiniTimeTheme.h b/src/displayapp/InfiniTimeTheme.h index 0690b099..57680e87 100644 --- a/src/displayapp/InfiniTimeTheme.h +++ b/src/displayapp/InfiniTimeTheme.h @@ -8,6 +8,7 @@ namespace Colors { static constexpr lv_color_t green = LV_COLOR_MAKE(0x0, 0xb0, 0x0); static constexpr lv_color_t blue = LV_COLOR_MAKE(0x0, 0x50, 0xff); static constexpr lv_color_t lightGray = LV_COLOR_MAKE(0xb0, 0xb0, 0xb0); + static constexpr lv_color_t gray = LV_COLOR_MAKE(0x50, 0x50, 0x50); static constexpr lv_color_t bg = LV_COLOR_MAKE(0x5d, 0x69, 0x7e); static constexpr lv_color_t bgAlt = LV_COLOR_MAKE(0x38, 0x38, 0x38); diff --git a/src/displayapp/UserApps.h b/src/displayapp/UserApps.h index 67bbfa7d..a0e41f72 100644 --- a/src/displayapp/UserApps.h +++ b/src/displayapp/UserApps.h @@ -12,6 +12,7 @@ #include "displayapp/screens/WatchFaceAnalog.h" #include "displayapp/screens/WatchFaceCasioStyleG7710.h" #include "displayapp/screens/WatchFaceInfineat.h" +#include "displayapp/screens/WatchFaceMeow.h" #include "displayapp/screens/WatchFacePineTimeStyle.h" #include "displayapp/screens/WatchFaceTerminal.h" diff --git a/src/displayapp/apps/Apps.h.in b/src/displayapp/apps/Apps.h.in index 2104a267..7f4d03fc 100644 --- a/src/displayapp/apps/Apps.h.in +++ b/src/displayapp/apps/Apps.h.in @@ -23,6 +23,7 @@ namespace Pinetime { Twos, HeartRate, Navigation, + Calendar, StopWatch, Metronome, Motion, @@ -51,6 +52,7 @@ namespace Pinetime { PineTimeStyle, Terminal, Infineat, + Meow, CasioStyleG7710, }; diff --git a/src/displayapp/apps/CMakeLists.txt b/src/displayapp/apps/CMakeLists.txt index d7858760..c7a188f2 100644 --- a/src/displayapp/apps/CMakeLists.txt +++ b/src/displayapp/apps/CMakeLists.txt @@ -8,12 +8,13 @@ else () set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::HeartRate") set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Music") set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Paint") - set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Paddle") + #set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Paddle") set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Twos") set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Dice") set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Metronome") - set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Navigation") - set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Weather") + #set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Navigation") + #set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Weather") + set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Calendar") #set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Motion") set(USERAPP_TYPES "${DEFAULT_USER_APP_TYPES}" CACHE STRING "List of user apps to build into the firmware") endif () @@ -21,10 +22,11 @@ endif () if(DEFINED ENABLE_WATCHFACES) set(WATCHFACE_TYPES ${ENABLE_WATCHFACES} CACHE STRING "List of watch faces to build into the firmware") else() - set(DEFAULT_WATCHFACE_TYPES "WatchFace::Digital") - set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::Analog") - set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::PineTimeStyle") - set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::Terminal") + set(DEFAULT_WATCHFACE_TYPES "WatchFace::Meow") + set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::Digital") + #set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::Analog") + #set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::PineTimeStyle") + #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(WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}" CACHE STRING "List of watch faces to build into the firmware") diff --git a/src/displayapp/screens/Calendar.cpp b/src/displayapp/screens/Calendar.cpp new file mode 100644 index 00000000..d51126c7 --- /dev/null +++ b/src/displayapp/screens/Calendar.cpp @@ -0,0 +1,96 @@ +/* Copyright (C) 2024 thnikk, Boteium, JustScott + + 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 . +*/ + +#include "displayapp/screens/Calendar.h" +#include "components/datetime/DateTimeController.h" +#include "displayapp/InfiniTimeTheme.h" + +using namespace Pinetime::Applications::Screens; + +Calendar::Calendar(Controllers::DateTime& dateTimeController) : dateTimeController {dateTimeController} { + + // Create calendar object + calendar = lv_calendar_create(lv_scr_act(), NULL); + // Set size + lv_obj_set_size(calendar, LV_HOR_RES, LV_VER_RES); + // Set alignment + lv_obj_align(calendar, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, -5); + // Disable clicks + lv_obj_set_click(calendar, false); + + // Set style of today's date + lv_obj_set_style_local_text_color(calendar, LV_CALENDAR_PART_DATE, LV_STATE_FOCUSED, Colors::deepOrange); + + // Set style of inactive month's days + lv_obj_set_style_local_text_color(calendar, LV_CALENDAR_PART_DATE, LV_STATE_DISABLED, Colors::gray); + + // Get today's date + current.year = static_cast(dateTimeController.Year()); + current.month = static_cast(dateTimeController.Month()); + current.day = static_cast(dateTimeController.Day()); + + // Set today's date + lv_calendar_set_today_date(calendar, ¤t); + lv_calendar_set_showed_date(calendar, ¤t); +} + +bool Calendar::OnTouchEvent(Pinetime::Applications::TouchEvents event) { + switch (event) { + case TouchEvents::SwipeLeft: { + if (current.month == 12) { + current.month = 1; + current.year++; + } else { + current.month++; + } + + lv_calendar_set_showed_date(calendar, ¤t); + return true; + } + case TouchEvents::SwipeRight: { + if (current.month == 1) { + current.month = 12; + current.year--; + } else { + current.month--; + } + + lv_calendar_set_showed_date(calendar, ¤t); + return true; + } + /* + case TouchEvents::SwipeUp: { + current.year++; + lv_calendar_set_showed_date(calendar, ¤t); + return true; + } + case TouchEvents::SwipeDown: { + current.year--; + lv_calendar_set_showed_date(calendar, ¤t); + return true; + } + */ + default: { + return false; + } + } +} + +Calendar::~Calendar() { + lv_obj_clean(lv_scr_act()); +} diff --git a/src/displayapp/screens/Calendar.h b/src/displayapp/screens/Calendar.h new file mode 100644 index 00000000..bbdc8322 --- /dev/null +++ b/src/displayapp/screens/Calendar.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2024 thnikk, Boteium, JustScott + + 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 . +*/ + +#pragma once + +#include "displayapp/apps/Apps.h" +#include "displayapp/Controllers.h" +#include "displayapp/screens/Screen.h" +#include "components/datetime/DateTimeController.h" +#include + +#include "Symbols.h" + +namespace Pinetime { + namespace Controllers { + class Settings; + } + + namespace Applications { + namespace Screens { + class Calendar : public Screen { + public: + Calendar(Controllers::DateTime& dateTimeController); + ~Calendar() override; + + private: + bool OnTouchEvent(TouchEvents event); + Controllers::DateTime& dateTimeController; + lv_obj_t* calendar; + lv_calendar_date_t current; + }; + } + + template <> + struct AppTraits { + static constexpr Apps app = Apps::Calendar; + static constexpr const char* icon = Screens::Symbols::calendar; + + static Screens::Screen* Create(AppControllers& controllers) { + return new Screens::Calendar(controllers.dateTimeController); + }; + }; + } +} diff --git a/src/displayapp/screens/README.md b/src/displayapp/screens/README.md new file mode 100644 index 00000000..f84a5fc6 --- /dev/null +++ b/src/displayapp/screens/README.md @@ -0,0 +1,10 @@ +# Add a new watchface : +## Modify the following files with the names of your source files : + +- /src/displayapp/apps/Apps.h.in +- /src/components/settings/Settings.h +- /src/displayapp/UserApps.h +- /src/displayapp/apps/CMakeLists.txt +- CMakelists.txt + + diff --git a/src/displayapp/screens/WatchFaceAnalog.h b/src/displayapp/screens/WatchFaceAnalog.h index 2eee657e..958ff64d 100644 --- a/src/displayapp/screens/WatchFaceAnalog.h +++ b/src/displayapp/screens/WatchFaceAnalog.h @@ -75,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; diff --git a/src/displayapp/screens/WatchFaceCasioStyleG7710.cpp b/src/displayapp/screens/WatchFaceCasioStyleG7710.cpp index c695f852..ea0bc887 100644 --- a/src/displayapp/screens/WatchFaceCasioStyleG7710.cpp +++ b/src/displayapp/screens/WatchFaceCasioStyleG7710.cpp @@ -4,10 +4,12 @@ #include #include "displayapp/screens/BatteryIcon.h" #include "displayapp/screens/BleIcon.h" +#include "displayapp/screens/AlarmIcon.h" #include "displayapp/screens/NotificationIcon.h" #include "displayapp/screens/Symbols.h" #include "components/battery/BatteryController.h" #include "components/ble/BleController.h" +#include "components/alarm/AlarmController.h" #include "components/ble/NotificationManager.h" #include "components/heartrate/HeartRateController.h" #include "components/motion/MotionController.h" @@ -17,6 +19,7 @@ using namespace Pinetime::Applications::Screens; WatchFaceCasioStyleG7710::WatchFaceCasioStyleG7710(Controllers::DateTime& dateTimeController, const Controllers::Battery& batteryController, const Controllers::Ble& bleController, + Controllers::AlarmController& alarmController, Controllers::NotificationManager& notificatioManager, Controllers::Settings& settingsController, Controllers::HeartRateController& heartRateController, @@ -27,6 +30,7 @@ WatchFaceCasioStyleG7710::WatchFaceCasioStyleG7710(Controllers::DateTime& dateTi dateTimeController {dateTimeController}, batteryController {batteryController}, bleController {bleController}, + alarmController {alarmController}, notificatioManager {notificatioManager}, settingsController {settingsController}, heartRateController {heartRateController}, @@ -168,6 +172,23 @@ WatchFaceCasioStyleG7710::WatchFaceCasioStyleG7710(Controllers::DateTime& dateTi lv_label_set_text_static(stepIcon, Symbols::shoe); lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0); + alarmIcon = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(alarmIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text); + lv_label_set_text_static(alarmIcon, Symbols::notbell); + lv_obj_align(alarmIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2); + + labelAlarm = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(labelAlarm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text); +// lv_obj_set_style_local_text_font(labelAlarm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_teko); + lv_obj_align(labelAlarm, alarmIcon, LV_ALIGN_OUT_RIGHT_MID, 3, 0); + lv_label_set_text_static(labelAlarm, "00:00"); + + labelTimeAmPmAlarm = lv_label_create(lv_scr_act(), nullptr); +// lv_obj_set_style_local_text_font(labelTimeAmPmAlarm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_teko); + lv_label_set_text_static(labelTimeAmPmAlarm, ""); + lv_obj_set_style_local_text_color(labelTimeAmPmAlarm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text); + lv_obj_align(labelTimeAmPmAlarm, labelAlarm, LV_ALIGN_OUT_RIGHT_MID, 3, 0); + taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); Refresh(); } @@ -310,6 +331,41 @@ void WatchFaceCasioStyleG7710::Refresh() { lv_obj_realign(stepValue); lv_obj_realign(stepIcon); } + alarmState = alarmController.State()==Pinetime::Controllers::AlarmController::AlarmState::Set; + // sets the icon as bell or barred bell + lv_label_set_text_static(alarmIcon, AlarmIcon::GetIcon(alarmState)); + //displays the time of the alarm or nothing if the alarm is not set + if (alarmState) { + uint8_t alarmHours = alarmController.Hours(); + uint8_t alarmMinutes = alarmController.Minutes(); + //handles the am pm format. + if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) { + char ampmChar[3] = "AM"; + if (alarmHours == 0) { + alarmHours = 12; + } else if (alarmHours == 12) { + ampmChar[0]='P'; + } else if (alarmHours > 12) { + alarmHours = alarmHours - 12; + ampmChar[0]='P'; + } + lv_label_set_text(labelTimeAmPmAlarm, ampmChar); +// lv_obj_set_style_local_text_font(labelTimeAmPmAlarm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_teko); + lv_obj_align(labelTimeAmPmAlarm, labelAlarm, LV_ALIGN_OUT_RIGHT_MID, 3, 0); + } + + lv_label_set_text_fmt(labelAlarm, "%02d:%02d", alarmHours, alarmMinutes); + + lv_obj_align(alarmIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2); + lv_obj_align(labelAlarm, alarmIcon, LV_ALIGN_OUT_RIGHT_MID, 3, 0); + + } + else { + lv_label_set_text_static(labelAlarm, Symbols::none); + lv_obj_align(alarmIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2); + lv_obj_align(labelAlarm, alarmIcon, LV_ALIGN_OUT_RIGHT_MID, 3, 0); + } + } bool WatchFaceCasioStyleG7710::IsAvailable(Pinetime::Controllers::FS& filesystem) { diff --git a/src/displayapp/screens/WatchFaceCasioStyleG7710.h b/src/displayapp/screens/WatchFaceCasioStyleG7710.h index 0f46a692..7d429451 100644 --- a/src/displayapp/screens/WatchFaceCasioStyleG7710.h +++ b/src/displayapp/screens/WatchFaceCasioStyleG7710.h @@ -30,6 +30,7 @@ namespace Pinetime { WatchFaceCasioStyleG7710(Controllers::DateTime& dateTimeController, const Controllers::Battery& batteryController, const Controllers::Ble& bleController, + Controllers::AlarmController& alarmController, Controllers::NotificationManager& notificatioManager, Controllers::Settings& settingsController, Controllers::HeartRateController& heartRateController, @@ -46,6 +47,7 @@ namespace Pinetime { Utility::DirtyValue powerPresent {}; Utility::DirtyValue bleState {}; Utility::DirtyValue bleRadioEnabled {}; + bool alarmState {}; Utility::DirtyValue> currentDateTime {}; Utility::DirtyValue stepCount {}; Utility::DirtyValue heartbeat {}; @@ -76,6 +78,9 @@ namespace Pinetime { lv_obj_t* line_day_of_year; lv_obj_t* backgroundLabel; lv_obj_t* bleIcon; + lv_obj_t* labelAlarm; + lv_obj_t* labelTimeAmPmAlarm; + lv_obj_t* alarmIcon; lv_obj_t* batteryPlug; lv_obj_t* label_battery_value; lv_obj_t* heartbeatIcon; @@ -90,6 +95,7 @@ namespace Pinetime { Controllers::DateTime& dateTimeController; const Controllers::Battery& batteryController; const Controllers::Ble& bleController; + Controllers::AlarmController& alarmController; Controllers::NotificationManager& notificatioManager; Controllers::Settings& settingsController; Controllers::HeartRateController& heartRateController; @@ -111,6 +117,7 @@ namespace Pinetime { return new Screens::WatchFaceCasioStyleG7710(controllers.dateTimeController, controllers.batteryController, controllers.bleController, + controllers.alarmController, controllers.notificationManager, controllers.settingsController, controllers.heartRateController, diff --git a/src/displayapp/screens/WatchFaceMeow.cpp b/src/displayapp/screens/WatchFaceMeow.cpp new file mode 100644 index 00000000..21d079c7 --- /dev/null +++ b/src/displayapp/screens/WatchFaceMeow.cpp @@ -0,0 +1,683 @@ +/*********************************************************/ +/* + * I modified the watchface Infineat : + * - added alarm info on the screen + * - modified the colors + * - modified step count icon + * Except colors, modifications are at line 254 and 500 + */ +/*********************************************************/ + + + +#include "displayapp/screens/WatchFaceMeow.h" + +#include +#include +#include "displayapp/screens/Symbols.h" +#include "displayapp/screens/BleIcon.h" +//#include "displayapp/screens/AlarmIcon.h" +#include "components/settings/Settings.h" +#include "components/battery/BatteryController.h" +#include "components/ble/BleController.h" +#include "components/alarm/AlarmController.h" +#include "components/ble/NotificationManager.h" +#include "components/motion/MotionController.h" + +using namespace Pinetime::Applications::Screens; + +namespace { + void event_handler(lv_obj_t* obj, lv_event_t event) { + auto* screen = static_cast(obj->user_data); + screen->UpdateSelected(obj, event); + } + + enum class colors { + orange, + blue, + green, + rainbow, + vivid, + pink, + nordGreen, + }; + + constexpr int nColors = 7; // must match number of colors in InfineatColorsColors + + constexpr int nLines = WatchFaceMeow::nLines; + + constexpr std::array orangeColors = {LV_COLOR_MAKE(0xfd, 0x87, 0x2b), + LV_COLOR_MAKE(0xdb, 0x33, 0x16), + LV_COLOR_MAKE(0x6f, 0x10, 0x00), + LV_COLOR_MAKE(0xfd, 0x7a, 0x0a), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xe8, 0x51, 0x02), + LV_COLOR_MAKE(0xea, 0x1c, 0x00)}; + constexpr std::array blueColors = {LV_COLOR_MAKE(0xe7, 0xf8, 0xff), + LV_COLOR_MAKE(0x22, 0x32, 0xd0), + LV_COLOR_MAKE(0x18, 0x2a, 0x8b), + LV_COLOR_MAKE(0xe7, 0xf8, 0xff), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0x59, 0x91, 0xff), + LV_COLOR_MAKE(0x16, 0x36, 0xff)}; + constexpr std::array greenColors = {LV_COLOR_MAKE(0xb8, 0xff, 0x9b), + LV_COLOR_MAKE(0x08, 0x86, 0x08), + LV_COLOR_MAKE(0x00, 0x4a, 0x00), + LV_COLOR_MAKE(0xb8, 0xff, 0x9b), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0x62, 0xd5, 0x15), + LV_COLOR_MAKE(0x00, 0x74, 0x00)}; + //added comments to say which color is which triangle + constexpr std::array rainbowColors = {LV_COLOR_MAKE(0x2d, 0xa4, 0x00), //green, small triangle on top + LV_COLOR_MAKE(0xac, 0x09, 0xc4), //purple, smalltriangle in the bottom half part on the clock display side + LV_COLOR_MAKE(0xfe, 0x03, 0x03), //red, the two small triangles above and below the battery + LV_COLOR_MAKE(0x0d, 0x57, 0xff), //blue, the small triangle at the bottom + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xe0, 0xb9, 0x00), //Yellow, large triangle on top left + LV_COLOR_MAKE(0xe8, 0x51, 0x02)}; //orange, large triangle on bottom left + +// Add new colors, still rainbow but more pastel like + constexpr std::array rainbowVividColors ={LV_COLOR_MAKE(0xa5, 0xeb, 0x64), + LV_COLOR_MAKE(0xfc, 0x42, 0xb5), + LV_COLOR_MAKE(0xe7, 0xc1, 0xff), + LV_COLOR_MAKE(0x11, 0xdf, 0xfa), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xff, 0xec, 0x5d), + LV_COLOR_MAKE(0xff, 0x93, 0xaf)}; + + constexpr std::array pinkColors = {LV_COLOR_MAKE(0xff, 0xe5, 0xec), + LV_COLOR_MAKE(0xff, 0xb3, 0xc6), + LV_COLOR_MAKE(0xfb, 0x6f, 0x92), + LV_COLOR_MAKE(0xff, 0xe5, 0xec), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xff, 0xc2, 0xd1), + LV_COLOR_MAKE(0xff, 0x8f, 0xab)}; + constexpr std::array nordGreenColors = {LV_COLOR_MAKE(0xd5, 0xf0, 0xe9), + LV_COLOR_MAKE(0x23, 0x83, 0x73), + LV_COLOR_MAKE(0x1d, 0x41, 0x3f), + LV_COLOR_MAKE(0xd5, 0xf0, 0xe9), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0xff, 0xff, 0xff), + LV_COLOR_MAKE(0x2f, 0xb8, 0xa2), + LV_COLOR_MAKE(0x11, 0x70, 0x5a)}; + + //define colors for texts and symbols + // gray is used for text symbols and time. I changed it to pink, because I can. + //static constexpr lv_color_t grayColor = LV_COLOR_MAKE(0x99, 0x99, 0x99); + static constexpr lv_color_t pinkColor = LV_COLOR_MAKE(0xfc, 0x42, 0xb5); + + constexpr const std::array* returnColor(colors color) { + if (color == colors::orange) { + return &orangeColors; + } + if (color == colors::blue) { + return &blueColors; + } + if (color == colors::green) { + return &greenColors; + } + if (color == colors::rainbow) { + return &rainbowColors; + } + if (color == colors::vivid) { + return &rainbowVividColors; + } + if (color == colors::pink) { + return &pinkColors; + } + return &nordGreenColors; + } +} + +WatchFaceMeow::WatchFaceMeow(Controllers::DateTime& dateTimeController, + const Controllers::Battery& batteryController, + const Controllers::Ble& bleController, + Controllers::AlarmController& alarmController, + Controllers::Timer& timerController, + Controllers::NotificationManager& notificationManager, + Controllers::Settings& settingsController, + Controllers::MotionController& motionController, + Controllers::FS& filesystem) + : currentDateTime {{}}, + dateTimeController {dateTimeController}, + batteryController {batteryController}, + bleController {bleController}, + alarmController {alarmController}, + timerController {timerController}, + notificationManager {notificationManager}, + settingsController {settingsController}, + motionController {motionController} { + lfs_file f = {}; + if (filesystem.FileOpen(&f, "/fonts/teko.bin", LFS_O_RDONLY) >= 0) { + filesystem.FileClose(&f); + font_teko = lv_font_load("F:/fonts/teko.bin"); + } + + if (filesystem.FileOpen(&f, "/fonts/bebas.bin", LFS_O_RDONLY) >= 0) { + filesystem.FileClose(&f); + font_bebas = lv_font_load("F:/fonts/bebas.bin"); + } + + // Side Cover + //paires de points pour chaque ligne + // les lignes sont pas les contours des triangles, elles sont des grosses bandes superposées + + static constexpr lv_point_t linePoints[nLines][2] = {{{30, 25}, {68, -8}}, //1 small triangle on top + {{26, 167}, {43, 216}}, //2 purple, smalltriangle in the bottom half part on the clock display side + {{27, 40}, {27, 196}},// 3 small triangles up above and below battery + {{12, 182}, {65, 249}}, //4 most bottom right triangle + {{17, 97}, {17, 147}}, // 5 left part of battery zone, overlapped after by the large triangles + {{16, 81}, {42, 127}}, //6 upper part of battery zone + {{16, 163}, {42, 118}}, //7 lower part of battery zone + {{-20, 124}, {25, -11}}, //8 large upper triangle + {{-29, 89}, {27, 254}}}; //9 large lower triangle + //largeur des bandes + static constexpr lv_style_int_t lineWidths[nLines] = {18, 15, 14, 22, 20, 18, 18, 52, 48}; + + const std::array* colors = returnColor(static_cast(settingsController.GetInfineatColorIndex())); + for (int i = 0; i < nLines; i++) { + lines[i] = lv_line_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_line_width(lines[i], LV_LINE_PART_MAIN, LV_STATE_DEFAULT, lineWidths[i]); + lv_color_t color = (*colors)[i]; + lv_obj_set_style_local_line_color(lines[i], LV_LINE_PART_MAIN, LV_STATE_DEFAULT, color); + lv_line_set_points(lines[i], linePoints[i], 2); + } + + //Battery indicator + logoCat = lv_img_create(lv_scr_act(), nullptr); + lv_img_set_src(logoCat, "F:/images/cat_small.bin"); + lv_obj_set_pos(logoCat, 12, 108); + //adjust position for cat + lineBattery = lv_line_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_line_width(lineBattery, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 30); + lv_obj_set_style_local_line_color(lineBattery, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, (*colors)[4]); + lv_obj_set_style_local_line_opa(lineBattery, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 190); + lineBatteryPoints[0] = {27, 107};//27 = image x offset + image width / 2 + lineBatteryPoints[1] = {27, 108};// the line covering the image is initialized as 1 px high + lv_line_set_points(lineBattery, lineBatteryPoints, 2); + lv_obj_move_foreground(lineBattery); + + notificationIcon = lv_obj_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_bg_color(notificationIcon, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, (*colors)[7]); + lv_obj_set_style_local_radius(notificationIcon, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); + lv_obj_set_size(notificationIcon, 13, 13); + lv_obj_set_hidden(notificationIcon, true); + + if (!settingsController.GetInfineatShowSideCover()) { + ToggleBatteryIndicatorColor(false); + for (auto& line : lines) { + lv_obj_set_hidden(line, true); + } + } + + timeContainer = lv_obj_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_bg_opa(timeContainer, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP); + lv_obj_set_size(timeContainer, 185, 185); + lv_obj_align(timeContainer, lv_scr_act(), LV_ALIGN_CENTER, 0, -10); + + labelHour = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text_static(labelHour, "01"); + lv_obj_set_style_local_text_font(labelHour, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_bebas); + lv_obj_set_style_local_text_color(labelHour, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, pinkColor); + lv_obj_align(labelHour, timeContainer, LV_ALIGN_IN_TOP_MID, 0, 0); + + labelMinutes = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_font(labelMinutes, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_bebas); + lv_obj_set_style_local_text_color(labelMinutes, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, pinkColor); + lv_label_set_text_static(labelMinutes, "00"); + lv_obj_align(labelMinutes, timeContainer, LV_ALIGN_IN_BOTTOM_MID, 0, 0); + + labelTimeAmPm = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_font(labelTimeAmPm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_teko); + lv_obj_set_style_local_text_color(labelTimeAmPm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, pinkColor); + + lv_label_set_text_static(labelTimeAmPm, ""); + lv_obj_align(labelTimeAmPm, timeContainer, LV_ALIGN_OUT_RIGHT_TOP, 0, 15); + + dateContainer = lv_obj_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_bg_opa(dateContainer, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP); + lv_obj_set_size(dateContainer, 60, 30); + lv_obj_align(dateContainer, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 5); + + labelDate = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(labelDate, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, pinkColor); + lv_obj_set_style_local_text_font(labelDate, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_teko); + lv_obj_align(labelDate, dateContainer, LV_ALIGN_IN_TOP_MID, 0, 0); + lv_label_set_text_static(labelDate, "Mon 01"); + + bleIcon = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(bleIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, pinkColor); + lv_label_set_text_static(bleIcon, Symbols::bluetooth); + lv_obj_align(bleIcon, dateContainer, LV_ALIGN_OUT_BOTTOM_MID, 0, 0); + + + // Based on existing code, I understand that items on the screen (date, bluteooth status..) + // are declared here with default states, and later below the state (date, ...) is assigned + // So I do the same to add the alarm status : I put a symbol that has a default value + // and a text that has a default value + + // text + labelAlarm = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(labelAlarm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, pinkColor); + lv_obj_set_style_local_text_font(labelAlarm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_teko); + //lv_obj_align(labelAlarm, dateContainer, LV_ALIGN_OUT_BOTTOM_MID, -10, 0); + lv_obj_align(labelAlarm, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -3, 0); + lv_label_set_text_static(labelAlarm, "00:00"); + + labelTimeAmPmAlarm = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_font(labelTimeAmPmAlarm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_teko); + lv_label_set_text_static(labelTimeAmPmAlarm, ""); + lv_obj_set_style_local_text_color(labelTimeAmPmAlarm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, pinkColor); + lv_obj_align(labelTimeAmPmAlarm, labelAlarm, LV_ALIGN_OUT_TOP_RIGHT, 0, 0); + + // symbol + alarmIcon = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(alarmIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, pinkColor); + lv_label_set_text_static(alarmIcon, Symbols::zzz); + lv_obj_align(alarmIcon, labelAlarm, LV_ALIGN_OUT_LEFT_MID, -3, 0); + + // don't show the icons jsut set if we don't show alarm status + if (!settingsController.GetInfineatShowAlarmStatus()) { + lv_obj_set_hidden(labelAlarm, true); + lv_obj_set_hidden(alarmIcon, true); + lv_obj_set_hidden(labelTimeAmPmAlarm, true); + } + + // text + labelTimer = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(labelTimer, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, pinkColor); + lv_obj_set_style_local_text_font(labelTimer, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_teko); + //lv_obj_align(labelAlarm, dateContainer, LV_ALIGN_OUT_BOTTOM_MID, -10, 0); + lv_obj_align(labelAlarm, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -3, 0); + lv_label_set_text_static(labelTimer, "00:00"); + + // symbol + timerIcon = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(timerIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, pinkColor); + lv_label_set_text_static(timerIcon, Symbols::hourGlass); + lv_obj_align(timerIcon, labelTimer, LV_ALIGN_OUT_LEFT_MID, -3, 0); + + // don't show the icons jsut set if we don't show alarm status + if (!settingsController.GetInfineatShowAlarmStatus()) { + lv_obj_set_hidden(labelTimer, true); + lv_obj_set_hidden(timerIcon, true); + } + + stepValue = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, pinkColor); + lv_obj_set_style_local_text_font(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_teko); + lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 10, 0); + lv_label_set_text_static(stepValue, "0"); + + pawIcon = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(pawIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, pinkColor); + lv_label_set_text_static(pawIcon, Symbols::paw); + lv_obj_align(pawIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0); + + + // Setting buttons + btnClose = lv_btn_create(lv_scr_act(), nullptr); + btnClose->user_data = this; + lv_obj_set_size(btnClose, 60, 60); + lv_obj_align(btnClose, lv_scr_act(), LV_ALIGN_CENTER, 0, -80); + lv_obj_set_style_local_bg_opa(btnClose, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_70); + lv_obj_t* lblClose = lv_label_create(btnClose, nullptr); + lv_label_set_text_static(lblClose, "X"); + lv_obj_set_event_cb(btnClose, event_handler); + lv_obj_set_hidden(btnClose, true); + + btnNextColor = lv_btn_create(lv_scr_act(), nullptr); + btnNextColor->user_data = this; + lv_obj_set_size(btnNextColor, 60, 60); + lv_obj_align(btnNextColor, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -15, 0); + lv_obj_set_style_local_bg_opa(btnNextColor, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_70); + lv_obj_t* lblNextColor = lv_label_create(btnNextColor, nullptr); + lv_label_set_text_static(lblNextColor, ">"); + lv_obj_set_event_cb(btnNextColor, event_handler); + lv_obj_set_hidden(btnNextColor, true); + + btnPrevColor = lv_btn_create(lv_scr_act(), nullptr); + btnPrevColor->user_data = this; + lv_obj_set_size(btnPrevColor, 60, 60); + lv_obj_align(btnPrevColor, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 15, 0); + lv_obj_set_style_local_bg_opa(btnPrevColor, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_70); + lv_obj_t* lblPrevColor = lv_label_create(btnPrevColor, nullptr); + lv_label_set_text_static(lblPrevColor, "<"); + lv_obj_set_event_cb(btnPrevColor, event_handler); + lv_obj_set_hidden(btnPrevColor, true); + + btnToggleCover = lv_btn_create(lv_scr_act(), nullptr); + btnToggleCover->user_data = this; + lv_obj_set_size(btnToggleCover, 60, 60); + lv_obj_align(btnToggleCover, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + lv_obj_set_style_local_bg_opa(btnToggleCover, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_70); + const char* labelToggle = settingsController.GetInfineatShowSideCover() ? "ON" : "OFF"; + lblToggle = lv_label_create(btnToggleCover, nullptr); + lv_label_set_text_static(lblToggle, labelToggle); + lv_obj_set_event_cb(btnToggleCover, event_handler); + lv_obj_set_hidden(btnToggleCover, true); + + btnToggleAlarm = lv_btn_create(lv_scr_act(), nullptr); + btnToggleAlarm->user_data = this; + lv_obj_set_size(btnToggleAlarm, 60, 60); + lv_obj_align(btnToggleAlarm, lv_scr_act(), LV_ALIGN_CENTER, 0, 80); + lv_obj_set_style_local_bg_opa(btnToggleAlarm, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_70); + const char* labelToggleAlarm = settingsController.GetInfineatShowAlarmStatus() ? Symbols::bell : Symbols::notbell; + lblAlarm = lv_label_create(btnToggleAlarm, nullptr); + lv_label_set_text_static(lblAlarm, labelToggleAlarm); + lv_obj_set_event_cb(btnToggleAlarm, event_handler); + lv_obj_set_hidden(btnToggleAlarm, true); + + // Button to access the settings + btnSettings = lv_btn_create(lv_scr_act(), nullptr); + btnSettings->user_data = this; + lv_obj_set_size(btnSettings, 150, 150); + lv_obj_align(btnSettings, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); + lv_obj_set_style_local_radius(btnSettings, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 30); + lv_obj_set_style_local_bg_opa(btnSettings, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_70); + lv_obj_set_event_cb(btnSettings, event_handler); + labelBtnSettings = lv_label_create(btnSettings, nullptr); + lv_obj_set_style_local_text_font(labelBtnSettings, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48); + lv_label_set_text_static(labelBtnSettings, Symbols::settings); + lv_obj_set_hidden(btnSettings, true); + + taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); + Refresh(); +} + +WatchFaceMeow::~WatchFaceMeow() { + lv_task_del(taskRefresh); + + if (font_bebas != nullptr) { + lv_font_free(font_bebas); + } + if (font_teko != nullptr) { + lv_font_free(font_teko); + } + + lv_obj_clean(lv_scr_act()); +} + +bool WatchFaceMeow::OnTouchEvent(Pinetime::Applications::TouchEvents event) { + if ((event == Pinetime::Applications::TouchEvents::LongTap) && lv_obj_get_hidden(btnSettings)) { + lv_obj_set_hidden(btnSettings, false); + savedTick = lv_tick_get(); + return true; + } + // Prevent screen from sleeping when double tapping with settings on + if ((event == Pinetime::Applications::TouchEvents::DoubleTap) && !lv_obj_get_hidden(btnClose)) { + return true; + } + return false; +} + +void WatchFaceMeow::CloseMenu() { + settingsController.SaveSettings(); + lv_obj_set_hidden(btnClose, true); + lv_obj_set_hidden(btnNextColor, true); + lv_obj_set_hidden(btnPrevColor, true); + lv_obj_set_hidden(btnToggleCover, true); + lv_obj_set_hidden(btnToggleAlarm, true); +} + +bool WatchFaceMeow::OnButtonPushed() { + if (!lv_obj_get_hidden(btnClose)) { + CloseMenu(); + return true; + } + return false; +} + +void WatchFaceMeow::UpdateSelected(lv_obj_t* object, lv_event_t event) { + if (event == LV_EVENT_CLICKED) { + bool showSideCover = settingsController.GetInfineatShowSideCover(); + int colorIndex = settingsController.GetInfineatColorIndex(); + bool showAlarmStatus = settingsController.GetInfineatShowAlarmStatus(); + + if (object == btnSettings) { + lv_obj_set_hidden(btnSettings, true); + lv_obj_set_hidden(btnClose, false); + lv_obj_set_hidden(btnNextColor, !showSideCover); + lv_obj_set_hidden(btnPrevColor, !showSideCover); + lv_obj_set_hidden(btnToggleCover, false); + lv_obj_set_hidden(btnToggleAlarm, false); + } + if (object == btnClose) { + CloseMenu(); + } + if (object == btnToggleCover) { + settingsController.SetInfineatShowSideCover(!showSideCover); + ToggleBatteryIndicatorColor(!showSideCover); + for (auto& line : lines) { + lv_obj_set_hidden(line, showSideCover); + } + lv_obj_set_hidden(btnNextColor, showSideCover); + lv_obj_set_hidden(btnPrevColor, showSideCover); + const char* labelToggle = showSideCover ? "OFF" : "ON"; + lv_label_set_text_static(lblToggle, labelToggle); + } + + if (object == btnToggleAlarm) { + settingsController.SetInfineatShowAlarmStatus(!showAlarmStatus); + bool newShowAlarmStatus = settingsController.GetInfineatShowAlarmStatus(); + lv_obj_set_hidden(labelAlarm, !newShowAlarmStatus); + lv_obj_set_hidden(alarmIcon, !newShowAlarmStatus); + lv_obj_set_hidden(labelTimeAmPmAlarm, !newShowAlarmStatus); + const char* labelToggleAlarm = newShowAlarmStatus ? Symbols::bell : Symbols::notbell; + lv_label_set_text_static(lblAlarm, labelToggleAlarm); + } + + + if (object == btnNextColor) { + colorIndex = (colorIndex + 1) % nColors; + settingsController.SetInfineatColorIndex(colorIndex); + } + if (object == btnPrevColor) { + colorIndex -= 1; + if (colorIndex < 0) + colorIndex = nColors - 1; + settingsController.SetInfineatColorIndex(colorIndex); + } + if (object == btnNextColor || object == btnPrevColor) { + const std::array* colors = returnColor(static_cast(settingsController.GetInfineatColorIndex())); + for (int i = 0; i < nLines; i++) { + lv_color_t color = (*colors)[i]; + lv_obj_set_style_local_line_color(lines[i], LV_LINE_PART_MAIN, LV_STATE_DEFAULT, color); + } + lv_obj_set_style_local_line_color(lineBattery, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, (*colors)[4]); + lv_obj_set_style_local_bg_color(notificationIcon, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, (*colors)[7]); + } + } +} + +void WatchFaceMeow::Refresh() { + notificationState = notificationManager.AreNewNotificationsAvailable(); + if (notificationState.IsUpdated()) { + lv_obj_set_hidden(notificationIcon, !notificationState.Get()); + lv_obj_align(notificationIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, 0, 0); + } + + currentDateTime = std::chrono::time_point_cast(dateTimeController.CurrentDateTime()); + if (currentDateTime.IsUpdated()) { + uint8_t hour = dateTimeController.Hours(); + uint8_t minute = dateTimeController.Minutes(); + + 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(labelTimeAmPm, ampmChar); + } + lv_label_set_text_fmt(labelHour, "%02d", hour); + lv_label_set_text_fmt(labelMinutes, "%02d", minute); + + if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) { + 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); + } + + currentDate = std::chrono::time_point_cast(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); + } + } + + batteryPercentRemaining = batteryController.PercentRemaining(); + isCharging = batteryController.IsCharging(); + if (batteryController.IsCharging()) { // Charging battery animation + chargingBatteryPercent += 1; + if (chargingBatteryPercent > 100) { + chargingBatteryPercent = batteryPercentRemaining.Get(); + } + SetBatteryLevel(chargingBatteryPercent); + } else if (isCharging.IsUpdated() || batteryPercentRemaining.IsUpdated()) { + chargingBatteryPercent = batteryPercentRemaining.Get(); + SetBatteryLevel(chargingBatteryPercent); + } + + bleState = bleController.IsConnected(); + bleRadioEnabled = bleController.IsRadioEnabled(); + if (bleState.IsUpdated()) { + //bleState.Get : in displayapp/widgets/StatusIcons.cpp: bleState = bleController.IsConnected(); + //dynamic icons have their definitions in displayApp/screens/BleIcon.h / cpp + lv_label_set_text_static(bleIcon, BleIcon::GetIcon(bleState.Get())); + lv_obj_align(bleIcon, dateContainer, LV_ALIGN_OUT_BOTTOM_MID, 0, 3); + } + + // Add alarm state and time + // AlarmState is an enum type in class AlarmController that is in namespace controllers + if (settingsController.GetInfineatShowAlarmStatus()) { + alarmState = alarmController.State()==Pinetime::Controllers::AlarmController::AlarmState::Set; + timerRunning = Pinetime::Controllers::Timer::timerController.IsRunning() + if(!timerRunning) + // sets the icon as bird or bed + const char* alarmSymbol = Symbols::zzz; + if(alarmState) { + alarmSymbol = Symbols::bird; + } + lv_label_set_text_static(alarmIcon, alarmSymbol); + //displays the time of the alarm or nothing if the alarm is not set + if (alarmState) { + uint8_t alarmHours = alarmController.Hours(); + uint8_t alarmMinutes = alarmController.Minutes(); + //handles the am pm format. + if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) { + char ampmChar[3] = "AM"; + if (alarmHours == 0) { + alarmHours = 12; + } else if (alarmHours == 12) { + ampmChar[0]='P'; + } else if (alarmHours > 12) { + alarmHours = alarmHours - 12; + ampmChar[0]='P'; + } + lv_label_set_text(labelTimeAmPmAlarm, ampmChar); + lv_obj_set_style_local_text_font(labelTimeAmPmAlarm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_teko); + lv_obj_align(labelTimeAmPmAlarm, labelAlarm, LV_ALIGN_OUT_TOP_RIGHT, 0, 0); + } + + lv_label_set_text_fmt(labelAlarm, "%02d:%02d", alarmHours, alarmMinutes); + + lv_obj_align(alarmIcon, labelAlarm, LV_ALIGN_OUT_LEFT_MID, -3, 0); + lv_obj_align(labelAlarm, dateContainer, LV_ALIGN_OUT_BOTTOM_MID, -10, 0); + lv_obj_align(labelAlarm, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -3, 0); + else{ //timer is running, display timer instead of alarm + const char* timerSymbol = Symbols::hourGlass; + lv_label_set_text_static(timerIcon, timerSymbol); + auto secondsRemaining = std::chrono::duration_cast(timer.GetTimeRemaining())/1000; + timerMinutes = secondsRemaining.count() / 60; + timerSeconds = secondsRemaining.count() % 60; + lv_label_set_text_fmt(labelTimer, "%02d:%02d", timerMinutes, timerSeconds); + + lv_obj_align(timerIcon, labelTimer, LV_ALIGN_OUT_LEFT_MID, -3, 0); + lv_obj_align(labelTimer, dateContainer, LV_ALIGN_OUT_BOTTOM_MID, -10, 0); + lv_obj_align(labelTimer, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -3, 0); + } + } + else { + lv_label_set_text_static(labelAlarm, Symbols::none); + lv_obj_align(alarmIcon, dateContainer, LV_ALIGN_OUT_BOTTOM_MID, 0, 0); + lv_obj_align(alarmIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -3, 0); + } + } + + + stepCount = motionController.NbSteps(); + 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(pawIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0); + } + + if (!lv_obj_get_hidden(btnSettings)) { + if ((savedTick > 0) && (lv_tick_get() - savedTick > 3000)) { + lv_obj_set_hidden(btnSettings, true); + savedTick = 0; + } + } +} + +void WatchFaceMeow::SetBatteryLevel(uint8_t batteryPercent) { + // starting point (y) + Pine64 logo height * (100 - batteryPercent) / 100^ + //the ligne grows, it covers the icon starting from the top + lineBatteryPoints[1] = {27, static_cast(107 + 31 * (100 - batteryPercent) / 100)}; + lv_line_set_points(lineBattery, lineBatteryPoints, 2); +} + +void WatchFaceMeow::ToggleBatteryIndicatorColor(bool showSideCover) { + if (!showSideCover) { // make indicator and notification icon color white + lv_obj_set_style_local_image_recolor_opa(logoCat, LV_IMG_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_100); + lv_obj_set_style_local_image_recolor(logoCat, LV_IMG_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + lv_obj_set_style_local_line_color(lineBattery, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); + lv_obj_set_style_local_bg_color(notificationIcon, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE); + } else { + lv_obj_set_style_local_image_recolor_opa(logoCat, LV_IMG_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0); + const std::array* colors = returnColor(static_cast(settingsController.GetInfineatColorIndex())); + lv_obj_set_style_local_line_color(lineBattery, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, (*colors)[4]); + lv_obj_set_style_local_bg_color(notificationIcon, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, (*colors)[7]); + } +} + +bool WatchFaceMeow::IsAvailable(Pinetime::Controllers::FS& filesystem) { + lfs_file file = {}; + + if (filesystem.FileOpen(&file, "/fonts/teko.bin", LFS_O_RDONLY) < 0) { + return false; + } + + filesystem.FileClose(&file); + if (filesystem.FileOpen(&file, "/fonts/bebas.bin", LFS_O_RDONLY) < 0) { + return false; + } + + filesystem.FileClose(&file); + if (filesystem.FileOpen(&file, "/images/cat_small.bin", LFS_O_RDONLY) < 0) { + return false; + } + + filesystem.FileClose(&file); + return true; +} diff --git a/src/displayapp/screens/WatchFaceMeow.h b/src/displayapp/screens/WatchFaceMeow.h new file mode 100644 index 00000000..91d446c8 --- /dev/null +++ b/src/displayapp/screens/WatchFaceMeow.h @@ -0,0 +1,141 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "displayapp/screens/Screen.h" +#include "components/datetime/DateTimeController.h" +#include "displayapp/screens/Timer.h" +#include "utility/DirtyValue.h" +#include "displayapp/apps/Apps.h" + +namespace Pinetime { + namespace Controllers { + class Settings; + class Battery; + class Ble; + class NotificationManager; + class MotionController; + } + + namespace Applications { + namespace Screens { + + class WatchFaceMeow : public Screen { + public: + static constexpr int nLines = 9; + WatchFaceMeow(Controllers::DateTime& dateTimeController, + const Controllers::Battery& batteryController, + const Controllers::Ble& bleController, + Controllers::AlarmController& alarmController, + Controllers::Timer& timerController, + Controllers::NotificationManager& notificationManager, + Controllers::Settings& settingsController, + Controllers::MotionController& motionController, + Controllers::FS& fs); + + ~WatchFaceMeow() override; + + bool OnTouchEvent(TouchEvents event) override; + bool OnButtonPushed() override; + void UpdateSelected(lv_obj_t* object, lv_event_t event); + void CloseMenu(); + + void Refresh() override; + + static bool IsAvailable(Pinetime::Controllers::FS& filesystem); + + private: + uint32_t savedTick = 0; + uint8_t chargingBatteryPercent = 101; // not a mistake ;) + + Utility::DirtyValue batteryPercentRemaining {}; + Utility::DirtyValue isCharging {}; + Utility::DirtyValue bleState {}; + Utility::DirtyValue bleRadioEnabled {}; + bool alarmState {}; + Utility::DirtyValue> currentDateTime {}; + bool timerRunning {}; + Utility::DirtyValue stepCount {}; + Utility::DirtyValue notificationState {}; + Utility::DirtyValue> currentDate; + + // Lines making up the side cover + lv_obj_t* lineBattery; + + lv_point_t lineBatteryPoints[2]; + + lv_obj_t* logoCat; + + lv_obj_t* timeContainer; + lv_obj_t* labelHour; + lv_obj_t* labelMinutes; + lv_obj_t* labelTimeAmPm; + lv_obj_t* dateContainer; + lv_obj_t* labelDate; + lv_obj_t* bleIcon; + lv_obj_t* labelAlarm; + lv_obj_t* labelTimeAmPmAlarm; + lv_obj_t* alarmIcon; + lv_obj_t* labelTimer; + lv_obj_t* timerIcon; + lv_obj_t* pawIcon; + lv_obj_t* stepValue; + lv_obj_t* notificationIcon; + lv_obj_t* btnClose; + lv_obj_t* btnNextColor; + lv_obj_t* btnToggleCover; + lv_obj_t* btnToggleAlarm; + lv_obj_t* btnPrevColor; + lv_obj_t* btnSettings; + lv_obj_t* labelBtnSettings; + lv_obj_t* lblToggle; + lv_obj_t* lblAlarm; + + lv_obj_t* lines[nLines]; + + Controllers::DateTime& dateTimeController; + const Controllers::Battery& batteryController; + const Controllers::Ble& bleController; + Controllers::AlarmController& alarmController; + Controllers::Timer& timerController; + Controllers::NotificationManager& notificationManager; + Controllers::Settings& settingsController; + Controllers::MotionController& motionController; + + void SetBatteryLevel(uint8_t batteryPercent); + void ToggleBatteryIndicatorColor(bool showSideCover); + + void ToggleShowAlarmStatus(bool showAlarmStatus); + + lv_task_t* taskRefresh; + lv_font_t* font_teko = nullptr; + lv_font_t* font_bebas = nullptr; + }; + } + + template <> + struct WatchFaceTraits { + static constexpr WatchFace watchFace = WatchFace::Meow; + static constexpr const char* name = "Meow face"; + + static Screens::Screen* Create(AppControllers& controllers) { + return new Screens::WatchFaceMeow(controllers.dateTimeController, + controllers.batteryController, + controllers.bleController, + controllers.alarmController, + controllers.timer, + controllers.notificationManager, + controllers.settingsController, + controllers.motionController, + controllers.filesystem); + }; + + static bool IsAvailable(Pinetime::Controllers::FS& filesystem) { + return Screens::WatchFaceMeow::IsAvailable(filesystem); + } + }; + } +} diff --git a/src/displayapp/screens/addWatchface.md b/src/displayapp/screens/addWatchface.md new file mode 100644 index 00000000..aa8459e8 --- /dev/null +++ b/src/displayapp/screens/addWatchface.md @@ -0,0 +1,11 @@ +# Add a new watchface : +## Modify the following files with the names of your source files : + +- /src/displayapp/apps/Apps.h.in +- /src/components/settings/Settings.h +- /src/displayapp/screens/settings/SettingWatchFace.h +- /src/displayapp/UserApps.h +- /src/displayapp/apps/CMakeLists.txt +- CMakelists.txt + + diff --git a/src/displayapp/screens/settings/SettingWatchFace.h b/src/displayapp/screens/settings/SettingWatchFace.h index 4c75b0ab..0e74e2f9 100644 --- a/src/displayapp/screens/settings/SettingWatchFace.h +++ b/src/displayapp/screens/settings/SettingWatchFace.h @@ -10,8 +10,11 @@ #include "displayapp/screens/Symbols.h" #include "displayapp/screens/CheckboxList.h" #include "displayapp/screens/WatchFaceInfineat.h" +#include "displayapp/screens/WatchFaceMeow.h" #include "displayapp/screens/WatchFaceCasioStyleG7710.h" + + namespace Pinetime { namespace Applications { diff --git a/src/displayapp/screens/todo.txt b/src/displayapp/screens/todo.txt new file mode 100644 index 00000000..4696108b --- /dev/null +++ b/src/displayapp/screens/todo.txt @@ -0,0 +1,4 @@ +compare infineat and meow .h files to make declaration correct +faire en sorte to keep bell and not bell for inifneat wf +check the orig files and the files in lib if they need to be in git + diff --git a/src/drivers/Bma421.cpp b/src/drivers/Bma421.cpp index aff62b8d..74d47d06 100644 --- a/src/drivers/Bma421.cpp +++ b/src/drivers/Bma421.cpp @@ -126,13 +126,6 @@ Bma421::Values Bma421::Process() { uint32_t steps = 0; bma423_step_counter_output(&steps, &bma); - int32_t temperature; - bma4_get_temperature(&temperature, BMA4_DEG, &bma); - temperature = temperature / 1000; - - uint8_t activity = 0; - bma423_activity_output(&activity, &bma); - // X and Y axis are swapped because of the way the sensor is mounted in the PineTime return {steps, data.y, data.x, data.z}; } diff --git a/src/drivers/St7789.cpp b/src/drivers/St7789.cpp index 12e95a41..c22f2199 100644 --- a/src/drivers/St7789.cpp +++ b/src/drivers/St7789.cpp @@ -1,3 +1,4 @@ +#include #include "drivers/St7789.h" #include #include @@ -16,10 +17,9 @@ void St7789::Init() { HardwareReset(); SoftwareReset(); SleepOut(); - ColMod(); + PixelFormat(); MemoryDataAccessControl(); - ColumnAddressSet(); - RowAddressSet(); + SetAddrWindow(0, 0, Width, Height); // P8B Mirrored version does not need display inversion. #ifndef DRIVER_DISPLAY_MIRROR DisplayInversionOn(); @@ -97,8 +97,9 @@ void St7789::SleepIn() { sleepIn = true; } -void St7789::ColMod() { - WriteCommand(static_cast(Commands::ColMod)); +void St7789::PixelFormat() { + WriteCommand(static_cast(Commands::PixelFormat)); + // 65K colours, 16-bit per pixel WriteData(0x55); } @@ -118,22 +119,6 @@ void St7789::MemoryDataAccessControl() { #endif } -void St7789::ColumnAddressSet() { - WriteCommand(static_cast(Commands::ColumnAddressSet)); - WriteData(0x00); - WriteData(0x00); - WriteData(Width >> 8u); - WriteData(Width & 0xffu); -} - -void St7789::RowAddressSet() { - WriteCommand(static_cast(Commands::RowAddressSet)); - WriteData(0x00); - WriteData(0x00); - WriteData(320u >> 8u); - WriteData(320u & 0xffu); -} - void St7789::DisplayInversionOn() { WriteCommand(static_cast(Commands::DisplayInversionOn)); } @@ -148,16 +133,23 @@ void St7789::DisplayOn() { void St7789::SetAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { WriteCommand(static_cast(Commands::ColumnAddressSet)); - WriteData(x0 >> 8); - WriteData(x0 & 0xff); - WriteData(x1 >> 8); - WriteData(x1 & 0xff); + uint8_t colArgs[] = { + static_cast(x0 >> 8), // x start MSB + static_cast(x0), // x start LSB + static_cast(x1 >> 8), // x end MSB + static_cast(x1) // x end LSB + }; + WriteData(colArgs, sizeof(colArgs)); WriteCommand(static_cast(Commands::RowAddressSet)); - WriteData(y0 >> 8); - WriteData(y0 & 0xff); - WriteData(y1 >> 8); - WriteData(y1 & 0xff); + uint8_t rowArgs[] = { + static_cast(y0 >> 8), // y start MSB + static_cast(y0), // y start LSB + static_cast(y1 >> 8), // y end MSB + static_cast(y1) // y end LSB + }; + memcpy(addrWindowArgs, rowArgs, sizeof(rowArgs)); + WriteData(addrWindowArgs, sizeof(addrWindowArgs)); } void St7789::WriteToRam(const uint8_t* data, size_t size) { @@ -179,8 +171,12 @@ void St7789::DisplayOff() { void St7789::VerticalScrollStartAddress(uint16_t line) { verticalScrollingStartAddress = line; WriteCommand(static_cast(Commands::VerticalScrollStartAddress)); - WriteData(line >> 8u); - WriteData(line & 0x00ffu); + uint8_t args[] = { + static_cast(line >> 8), // Frame memory line pointer MSB + static_cast(line) // Frame memory line pointer LSB + }; + memcpy(verticalScrollArgs, args, sizeof(args)); + WriteData(verticalScrollArgs, sizeof(verticalScrollArgs)); } void St7789::Uninit() { diff --git a/src/drivers/St7789.h b/src/drivers/St7789.h index 45d4b56d..844e0180 100644 --- a/src/drivers/St7789.h +++ b/src/drivers/St7789.h @@ -40,7 +40,7 @@ namespace Pinetime { void SleepOut(); void EnsureSleepOutPostDelay(); void SleepIn(); - void ColMod(); + void PixelFormat(); void MemoryDataAccessControl(); void DisplayInversionOn(); void NormalModeOn(); @@ -68,16 +68,17 @@ namespace Pinetime { MemoryDataAccessControl = 0x36, VerticalScrollDefinition = 0x33, VerticalScrollStartAddress = 0x37, - ColMod = 0x3a, + PixelFormat = 0x3a, VdvSet = 0xc4, }; void WriteData(uint8_t data); void WriteData(const uint8_t* data, size_t size); - void ColumnAddressSet(); static constexpr uint16_t Width = 240; static constexpr uint16_t Height = 320; - void RowAddressSet(); + + uint8_t addrWindowArgs[4]; + uint8_t verticalScrollArgs[2]; }; } } diff --git a/src/libs/lv_conf.h b/src/libs/lv_conf.h index c23647f2..c884482b 100644 --- a/src/libs/lv_conf.h +++ b/src/libs/lv_conf.h @@ -589,7 +589,7 @@ typedef void* lv_obj_user_data_t; /*Calendar (dependencies: -)*/ #define LV_USE_CALENDAR 1 #if LV_USE_CALENDAR -#define LV_CALENDAR_WEEK_STARTS_MONDAY 0 +#define LV_CALENDAR_WEEK_STARTS_MONDAY 1 #endif /*Canvas (dependencies: lv_img)*/ diff --git a/src/resources/images.json b/src/resources/images.json index e4247188..2591bcbb 100644 --- a/src/resources/images.json +++ b/src/resources/images.json @@ -1,11 +1,18 @@ { - "pine_small" : { + "pine_small" : { "sources": "images/pine_logo.png", "color_format": "CF_TRUE_COLOR_ALPHA", "output_format": "bin", "binary_format": "ARGB8565_RBSWAP", "target_path": "/images/" }, + "cat_small" : { + "sources": "images/cat_clean.png", + "color_format": "CF_TRUE_COLOR_ALPHA", + "output_format": "bin", + "binary_format": "ARGB8565_RBSWAP", + "target_path": "/images/" + }, "navigation0" : { "sources": "images/navigation0.png", "color_format": "CF_INDEXED_1_BIT", diff --git a/src/resources/images/cat_clean.png b/src/resources/images/cat_clean.png new file mode 100644 index 00000000..e4d98e96 Binary files /dev/null and b/src/resources/images/cat_clean.png differ diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index e3d40d35..a56c2591 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -410,8 +410,6 @@ void SystemTask::Work() { } monitor.Process(); - uint32_t systick_counter = nrf_rtc_counter_get(portNRF_RTC_REG); - dateTimeController.UpdateTime(systick_counter); NoInit_BackUpTime = dateTimeController.CurrentDateTime(); if (nrf_gpio_pin_read(PinMap::Button) == 0) { watchdog.Reload(); diff --git a/todo.log b/todo.log new file mode 100644 index 00000000..e7d50d9f --- /dev/null +++ b/todo.log @@ -0,0 +1,3 @@ +replace alarm info by timer info when timer is ongoing +add a shortcut for a 1min timer on the right +