summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.devcontainer/build.sh1
-rw-r--r--.github/workflows/main.yml3
-rw-r--r--CMakeLists.txt42
-rw-r--r--README.md1
-rwxr-xr-xcmake-nRF5x/CMake_nRF5x.cmake51
-rw-r--r--doc/ExternalResources.md70
-rw-r--r--doc/buildAndProgram.md41
-rw-r--r--doc/code/Apps.md2
-rw-r--r--doc/gettingStarted/itd-external-resources.pngbin0 -> 11869 bytes
-rw-r--r--doc/gettingStarted/updating-software.md32
-rw-r--r--docker/Dockerfile10
-rwxr-xr-xdocker/build.sh8
-rwxr-xr-xdocker/post_build.sh.in2
-rw-r--r--src/CMakeLists.txt79
-rw-r--r--src/components/alarm/AlarmController.cpp2
-rw-r--r--src/components/alarm/AlarmController.h2
-rw-r--r--src/components/datetime/DateTimeController.cpp5
-rw-r--r--src/components/datetime/DateTimeController.h1
-rw-r--r--src/components/motor/MotorController.cpp2
-rw-r--r--src/components/settings/Settings.h38
-rw-r--r--src/displayapp/Colors.cpp2
-rw-r--r--src/displayapp/DisplayApp.cpp26
-rw-r--r--src/displayapp/DisplayApp.h5
-rw-r--r--src/displayapp/DisplayAppRecovery.cpp4
-rw-r--r--src/displayapp/DisplayAppRecovery.h4
-rw-r--r--src/displayapp/screens/CheckboxList.cpp116
-rw-r--r--src/displayapp/screens/CheckboxList.h48
-rw-r--r--src/displayapp/screens/Clock.cpp47
-rw-r--r--src/displayapp/screens/Clock.h10
-rw-r--r--src/displayapp/screens/Error.cpp3
-rw-r--r--src/displayapp/screens/Metronome.cpp7
-rw-r--r--src/displayapp/screens/Metronome.h1
-rw-r--r--src/displayapp/screens/Twos.cpp71
-rw-r--r--src/displayapp/screens/Twos.h7
-rw-r--r--src/displayapp/screens/WatchFaceAnalog.cpp1
-rw-r--r--src/displayapp/screens/WatchFaceCasioStyleG7710.cpp335
-rw-r--r--src/displayapp/screens/WatchFaceCasioStyleG7710.h109
-rw-r--r--src/displayapp/screens/WatchFaceDigital.cpp6
-rw-r--r--src/displayapp/screens/WatchFaceDigital.h4
-rw-r--r--src/displayapp/screens/WatchFaceInfineat.cpp607
-rw-r--r--src/displayapp/screens/WatchFaceInfineat.h144
-rw-r--r--src/displayapp/screens/WatchFacePineTimeStyle.cpp37
-rw-r--r--src/displayapp/screens/WatchFacePineTimeStyle.h4
-rw-r--r--src/displayapp/screens/WatchFaceTerminal.cpp6
-rw-r--r--src/displayapp/screens/WatchFaceTerminal.h4
-rw-r--r--src/displayapp/screens/settings/SettingSetDate.cpp6
-rw-r--r--src/displayapp/screens/settings/SettingSetDate.h1
-rw-r--r--src/displayapp/screens/settings/SettingSetTime.cpp8
-rw-r--r--src/displayapp/screens/settings/SettingSetTime.h1
-rw-r--r--src/displayapp/screens/settings/SettingSteps.cpp6
-rw-r--r--src/displayapp/screens/settings/SettingWatchFace.cpp98
-rw-r--r--src/displayapp/screens/settings/SettingWatchFace.h11
-rw-r--r--src/displayapp/widgets/StatusIcons.cpp2
-rw-r--r--src/libs/lv_conf.h2
-rw-r--r--src/main.cpp3
-rw-r--r--src/resources/CMakeLists.txt29
-rw-r--r--src/resources/fonts.json62
-rw-r--r--src/resources/fonts/7segment.woffbin0 -> 2124 bytes
-rw-r--r--src/resources/fonts/BebasNeue-Regular.ttfbin0 -> 60576 bytes
-rw-r--r--src/resources/fonts/Teko-Light.ttfbin0 -> 279608 bytes
-rw-r--r--src/resources/fonts/repetitionscrolling.ttfbin0 -> 42872 bytes
-rwxr-xr-xsrc/resources/generate-fonts.py80
-rwxr-xr-xsrc/resources/generate-img.py56
-rwxr-xr-xsrc/resources/generate-package.py72
-rw-r--r--src/resources/images.json9
-rw-r--r--src/resources/images/pine_logo.pngbin0 -> 1808 bytes
-rw-r--r--src/resources/images/pine_logo.svg253
-rw-r--r--src/resources/obsolete_files.json6
68 files changed, 2342 insertions, 363 deletions
diff --git a/.devcontainer/build.sh b/.devcontainer/build.sh
index 5dcdf91d..3d8aecbf 100644
--- a/.devcontainer/build.sh
+++ b/.devcontainer/build.sh
@@ -60,7 +60,6 @@ CmakeGenerate() {
cmake -G "Unix Makefiles" \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
- -DUSE_OPENOCD=1 \
-DARM_NONE_EABI_TOOLCHAIN_PATH="$TOOLS_DIR/$GCC_ARM_VER" \
-DNRF5_SDK_PATH="$TOOLS_DIR/$NRF_SDK_VER" \
"$SOURCES_DIR"
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index b8a70b6e..f7af14c5 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -6,10 +6,13 @@ on:
branches: [ master, develop ]
paths-ignore:
- 'doc/**'
+ - '**.md'
pull_request:
branches: [ develop ]
paths-ignore:
- 'doc/**'
+ - '**.md'
+
jobs:
build-firmware:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index be8ef41f..75a6baa5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,28 +24,6 @@ if (NOT NRF5_SDK_PATH)
message(FATAL_ERROR "The path to the NRF52 SDK must be specified on the command line (add -DNRF5_SDK_PATH=<path>")
endif ()
-if(USE_JLINK)
- if (NOT NRFJPROG)
- message(FATAL_ERROR "the path to the tool nrfjprog must be specified on the command line (add -DNRFJPROG=<path>")
- endif ()
-endif()
-
-if(USE_GDB_CLIENT)
- if(NOT GDB_CLIENT_BIN_PATH)
- set(GDB_CLIENT_BIN_PATH "arm-none-eabi-gdb")
- endif()
-
- if(NOT GDB_CLIENT_TARGET_REMOTE)
- message(FATAL_ERROR "The GDB target must be specified (add -DGDB_CLIENT_TARGET_REMOTE=<target>")
- endif()
-endif()
-
-if(USE_OPENOCD)
- if(NOT OPENOCD_BIN_PATH)
- set(OPENOCD_BIN_PATH "openocd")
- endif()
-endif()
-
if(DEFINED USE_DEBUG_PINS AND USE_DEBUG_PINS)
add_definitions(-DUSE_DEBUG_PINS)
endif()
@@ -54,6 +32,10 @@ if(BUILD_DFU)
set(BUILD_DFU true)
endif()
+if(BUILD_RESOURCES)
+ set(BUILD_RESOURCES true)
+endif()
+
set(TARGET_DEVICE "PINETIME" CACHE STRING "Target device")
set_property(CACHE TARGET_DEVICE PROPERTY STRINGS PINETIME MOY-TFK5 MOY-TIN5 MOY-TON5 MOY-UNK)
@@ -77,17 +59,6 @@ message(" * Toolchain : " ${ARM_NONE_EABI_TOOLCHAIN_PATH})
message(" * GitRef(S) : " ${PROJECT_GIT_COMMIT_HASH})
message(" * NRF52 SDK : " ${NRF5_SDK_PATH})
message(" * Target device : " ${TARGET_DEVICE})
-set(PROGRAMMER "???")
-if(USE_JLINK)
- message(" * Programmer/debugger : JLINK")
- message(" * NrfJprog : " ${NRFJPROG})
-elseif(USE_GDB_CLIENT)
- message(" * Programmer/debugger : GDB Client")
- message(" * GDB Client path : " ${GDB_CLIENT_BIN_PATH})
- message(" * GDB Target : " ${GDB_CLIENT_TARGET_REMOTE})
-elseif(USE_OPENOCD)
- message(" * Programmer/debugger : OpenOCD Client")
-endif()
if(USE_DEBUG_PINS)
message(" * Debug pins : Enabled")
else()
@@ -98,6 +69,11 @@ if(BUILD_DFU)
else()
message(" * Build DFU (using adafruit-nrfutil) : Disabled")
endif()
+if(BUILD_RESOURCES)
+ message(" * Build resources : Enabled")
+else()
+ message(" * Build resources : Disabled")
+endif()
set(VERSION_EDIT_WARNING "// Do not edit this file, it is automatically generated by CMAKE!")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/Version.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/Version.h)
diff --git a/README.md b/README.md
index a3d2229b..6fd41586 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,7 @@ Fast open-source firmware for the [PineTime smartwatch](https://www.pine64.org/p
- [Generate the fonts and symbols](src/displayapp/fonts/README.md)
- [Tips on designing an app UI](doc/ui_guidelines.md)
- [Bootloader, OTA and DFU](bootloader/README.md)
+- [External resources](doc/ExternalResources.md)
- [Versioning](doc/versioning.md)
- [Project branches](doc/branches.md)
- [Files included in the release notes](doc/filesInReleaseNotes.md)
diff --git a/cmake-nRF5x/CMake_nRF5x.cmake b/cmake-nRF5x/CMake_nRF5x.cmake
index 9bf63f2a..95f1fdb6 100755
--- a/cmake-nRF5x/CMake_nRF5x.cmake
+++ b/cmake-nRF5x/CMake_nRF5x.cmake
@@ -229,24 +229,6 @@ macro(nRF5x_setup)
"${NRF5_SDK_PATH}/modules/nrfx/drivers/src/nrfx_twi.c"
)
- # adds target for erasing
- if(USE_JLINK)
- add_custom_target(FLASH_ERASE
- COMMAND ${NRFJPROG} --eraseall -f ${NRF_TARGET}
- COMMENT "erasing flashing"
- )
- elseif(USE_GDB_CLIENT)
- add_custom_target(FLASH_ERASE
- COMMAND ${GDB_CLIENT_BIN_PATH} -nx --batch -ex 'target extended-remote ${GDB_CLIENT_TARGET_REMOTE}' -ex 'monitor swdp_scan' -ex 'attach 1' -ex 'mon erase_mass'
- COMMENT "erasing flashing"
- )
- elseif(USE_OPENOCD)
- add_custom_target(FLASH_ERASE
- COMMAND ${OPENOCD_BIN_PATH} -f interface/stlink.cfg -c 'transport select hla_swd' -f target/nrf52.cfg -c init -c halt -c 'nrf5 mass_erase' -c reset -c shutdown
- COMMENT "erasing flashing"
- )
- endif()
-
if(${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin")
set(TERMINAL "open")
elseif(${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows")
@@ -255,16 +237,6 @@ macro(nRF5x_setup)
set(TERMINAL "gnome-terminal")
endif()
- if(USE_JLINK)
- add_custom_target(START_JLINK
- COMMAND ${TERMINAL} "${DIR_OF_nRF5x_CMAKE}/runJLinkGDBServer-${NRF_TARGET}"
- COMMAND ${TERMINAL} "${DIR_OF_nRF5x_CMAKE}/runJLinkExe-${NRF_TARGET}"
- COMMAND sleep 2s
- COMMAND ${TERMINAL} "${DIR_OF_nRF5x_CMAKE}/runJLinkRTTClient"
- COMMENT "started JLink commands"
- )
- endif()
-
endmacro(nRF5x_setup)
# adds a target for comiling and flashing an executable
@@ -282,29 +254,6 @@ macro(nRF5x_addExecutable EXECUTABLE_NAME SOURCE_FILES)
COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_NAME}.out "${EXECUTABLE_NAME}.hex"
COMMENT "post build steps for ${EXECUTABLE_NAME}")
- # custom target for flashing the board
- if(USE_JLINK)
- add_custom_target("FLASH_${EXECUTABLE_NAME}"
- DEPENDS ${EXECUTABLE_NAME}
- COMMAND ${NRFJPROG} --program ${EXECUTABLE_NAME}.hex -f ${NRF_TARGET} --sectorerase
- COMMAND sleep 0.5s
- COMMAND ${NRFJPROG} --reset -f ${NRF_TARGET}
- COMMENT "flashing ${EXECUTABLE_NAME}.hex"
- )
- elseif(USE_GDB_CLIENT)
- add_custom_target("FLASH_${EXECUTABLE_NAME}"
- DEPENDS ${EXECUTABLE_NAME}
- COMMAND ${GDB_CLIENT_BIN_PATH} -nx --batch -ex 'target extended-remote ${GDB_CLIENT_TARGET_REMOTE}' -ex 'monitor swdp_scan' -ex 'attach 1' -ex 'load' -ex 'kill' ${EXECUTABLE_NAME}.hex
- COMMENT "flashing ${EXECUTABLE_NAME}.hex"
- )
- elseif(USE_OPENOCD)
- add_custom_target("FLASH_${EXECUTABLE_NAME}"
- DEPENDS ${EXECUTABLE_NAME}
- COMMAND ${OPENOCD_BIN_PATH} -c "tcl_port disabled" -c "gdb_port 3333" -c "telnet_port 4444" -f interface/stlink.cfg -c 'transport select hla_swd' -f target/nrf52.cfg -c "program \"${EXECUTABLE_NAME}.hex\"" -c reset -c shutdown
- COMMENT "flashing ${EXECUTABLE_NAME}.hex"
- )
- endif()
-
endmacro()
# adds app-level scheduler library
diff --git a/doc/ExternalResources.md b/doc/ExternalResources.md
new file mode 100644
index 00000000..85319f6f
--- /dev/null
+++ b/doc/ExternalResources.md
@@ -0,0 +1,70 @@
+# External resources
+Since InfiniTime 1.11 apps and watchfaces can benefit from the external flash memory to store images and fonts.
+This external memory is a lot bigger (4MB) than the internal memory that contains the firmware (512KB).
+
+This page describes how the resources are integrated in InfiniTime from a developer perspective. [This page](gettingStarted/updating-software.md) explains how to install and update the external resources using companion apps.
+
+## Resources generation
+
+Resources are generated at build time via the [CMake target `Generate Resources`](https://github.com/InfiniTimeOrg/InfiniTime/blob/develop/src/resources/CMakeLists.txt#L19).
+It runs 3 Python scripts that respectively convert the fonts to binary format, convert the images to binary format and package everything in a .zip file.
+
+The resulting file `infinitime-resources-x.y.z.zip` contains the images and fonts converted in binary `.bin` files and a JSON file `resources.json`.
+
+Companion apps use this file to upload the files to the watch.
+
+```
+{
+ "resources": [
+ {
+ "filename": "lv_font_dots_40.bin",
+ "path": "/fonts/lv_font_dots_40.bin"
+ }
+ ],
+ "obsolete_files": [
+ {
+ "path": "/example-of-obsolete-file.bin",
+ "since": "1.11.0"
+ }
+ ]
+}
+```
+
+The resource JSON file describes an array of resources and an array of obsolete files :
+
+- `resources` : a resource is a file that must be flashed to the watch
+ - `filename`: name of the resources in the zip file.
+ - `path` : file path and name where the file must be flashed in the watch FS.
+
+- `obsolete_files` : files that are not needed anymore in the memory of the watch that can be deleted during the update procedure.
+ - `path` : path of the file in the watch FS
+ - `since` : version of InfiniTime that made this file obsolete.
+
+## Resources update procedure
+
+The update procedure is based on the [BLE FS API](BLEFS.md). The companion app simply write the binary files to the watch FS using information from the file `resources.json`.
+
+## Working with external resources in the code
+
+Load a picture from the external resources:
+
+```
+lv_obj_t* logo = lv_img_create(lv_scr_act(), nullptr);
+lv_img_set_src(logo, "F:/images/logo.bin");
+```
+
+Load a font from the external resources: you first need to check that the file actually exists. LVGL will crash when trying to open a font that doesn't exist.
+
+```
+lv_font_t* font_teko = nullptr;
+if (filesystem.FileOpen(&f, "/fonts/font.bin", LFS_O_RDONLY) >= 0) {
+ filesystem.FileClose(&f);
+ font_teko = lv_font_load("F:/fonts/font.bin");
+}
+
+if(font != nullptr) {
+ lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font);
+}
+
+```
+
diff --git a/doc/buildAndProgram.md b/doc/buildAndProgram.md
index 58d0f72e..78bea1b4 100644
--- a/doc/buildAndProgram.md
+++ b/doc/buildAndProgram.md
@@ -40,12 +40,9 @@ CMake configures the project according to variables you specify the command line
----------|-------------|--------|
**ARM_NONE_EABI_TOOLCHAIN_PATH**|path to the toolchain directory|`-DARM_NONE_EABI_TOOLCHAIN_PATH=/home/jf/nrf52/gcc-arm-none-eabi-10.3-2021.10/`|
**NRF5_SDK_PATH**|path to the NRF52 SDK|`-DNRF5_SDK_PATH=/home/jf/nrf52/Pinetime/sdk`|
-**USE_JLINK, USE_GDB_CLIENT and USE_OPENOCD**|Enable *JLink* mode, *GDB Client* (Black Magic Probe) mode or *OpenOCD* mode (set the one you want to use to `1`)|`-DUSE_JLINK=1`
**CMAKE_BUILD_TYPE (\*)**| Build type (Release or Debug). Release is applied by default if this variable is not specified.|`-DCMAKE_BUILD_TYPE=Debug`
-**NRFJPROG**|Path to the NRFJProg executable. Used only if `USE_JLINK` is 1.|`-DNRFJPROG=/opt/nrfjprog/nrfjprog`
-**GDB_CLIENT_BIN_PATH**|Path to arm-none-eabi-gdb executable. Used only if `USE_GDB_CLIENT` is 1.|`-DGDB_CLIENT_BIN_PATH=/home/jf/nrf52/gcc-arm-none-eabi-9-2019-q4-major/bin/arm-none-eabi-gdb`
-**GDB_CLIENT_TARGET_REMOTE**|Target remote connection string. Used only if `USE_GDB_CLIENT` is 1.|`-DGDB_CLIENT_TARGET_REMOTE=/dev/ttyACM0`
**BUILD_DFU (\*\*)**|Build DFU files while building (needs [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil)).|`-DBUILD_DFU=1`
+**BUILD_RESOURCES (\*\*)**| Generate external resource while building (needs [lv_font_conv](https://github.com/lvgl/lv_font_conv) and [lv_img_conv](https://github.com/lvgl/lv_img_conv). |`-DBUILD_RESOURCES=1`
**TARGET_DEVICE**|Target device, used for hardware configuration. Allowed: `PINETIME, MOY-TFK5, MOY-TIN5, MOY-TON5, MOY-UNK`|`-DTARGET_DEVICE=PINETIME` (Default)
#### (\*) Note about **CMAKE_BUILD_TYPE**
@@ -56,30 +53,16 @@ The *Debug* mode disables all optimizations, which makes the code easier to debu
#### (\*\*) Note about **BUILD_DFU**
DFU files are the files you'll need to install your build of InfiniTime using OTA (over-the-air) mechanism. To generate the DFU file, the Python tool [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil) is needed on your system. Check that this tool is properly installed before enabling this option.
-#### CMake command line for JLink
+#### CMake command
```
-cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_JLINK=1 -DNRFJPROG=... ../
-```
-
-#### CMake command line for GDB Client (Black Magic Probe)
-
-```
-cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_GDB_CLIENT=1 -DGDB_CLIENT_BIN_PATH=... -DGDB_CLIENT_TARGET_REMOTE=... ../
-```
-
-#### CMake command line for OpenOCD
-
-```
-cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=... -DUSE_OPENOCD=1 -DGDB_CLIENT_BIN_PATH=[optional] ../
+cmake -DARM_NONE_EABI_TOOLCHAIN_PATH=... -DNRF5_SDK_PATH=...
```
### Build the project
During the project generation, CMake created the following targets:
-- **FLASH_ERASE** : mass erase the flash memory of the NRF52.
-- **FLASH_pinetime-app** : flash the firmware into the NRF52.
- **pinetime-app** : build the standalone (without bootloader support) version of the firmware.
- **pinetime-recovery** : build the standalone recovery version of infinitime (light firmware that only supports OTA and basic UI)
- **pinetime-recovery-loader** : build the standalone tool that flashes the recovery firmware into the external SPI flash
@@ -107,24 +90,6 @@ Binary files are generated into the folder `src`:
The same files are generated for **pinetime-recovery** and **pinetime-recoveryloader**
-### Program and run
-
-#### Using CMake targets
-
-These target have been configured during the project generation by CMake according to the parameters you provided to the command line.
-
-Mass erase:
-
-```
-make FLASH_ERASE
-```
-
-Flash the application:
-
-```
-make FLASH_pinetime-app
-```
-
### How to generate files needed by the factory
These files are needed by the Pine64 factory to flash InfiniTime as the default firmware on the PineTimes.
diff --git a/doc/code/Apps.md b/doc/code/Apps.md
index c756a8b4..ad0f0403 100644
--- a/doc/code/Apps.md
+++ b/doc/code/Apps.md
@@ -99,7 +99,7 @@ Now, go to the function `DisplayApp::LoadApp` and add another case to the switch
The case will be the id you gave your app earlier.
If your app needs any additional arguments, this is the place to pass them.
-If you want to add your app in the app launcher, add your app in [displayapp/screens/ApplicationList.cpp](/src/displayapp/screens/ApplicationList.cpp) to one of the `CreateScreen` functions, or add another `CreateScreen` function if there are no empty spaces for your app. If your app is a setting, do the same procedure in [displayapp/screens/settings/Settings.cpp](/src/displayapp/screens/settings/Settings.cpp).
+If you want to add your app in the app launcher, add your app in [displayapp/screens/ApplicationList.h](/src/displayapp/screens/ApplicationList.h) to the array containing the applications and their corresponding symbol. If your app is a setting, do the same procedure in [displayapp/screens/settings/Settings.h](/src/displayapp/screens/settings/Settings.h).
You should now be able to [build](../buildAndProgram.md) the firmware
and flash it to your PineTime. Yay!
diff --git a/doc/gettingStarted/itd-external-resources.png b/doc/gettingStarted/itd-external-resources.png
new file mode 100644
index 00000000..b3ff99be
--- /dev/null
+++ b/doc/gettingStarted/itd-external-resources.png
Binary files differ
diff --git a/doc/gettingStarted/updating-software.md b/doc/gettingStarted/updating-software.md
index d302607e..5c867023 100644
--- a/doc/gettingStarted/updating-software.md
+++ b/doc/gettingStarted/updating-software.md
@@ -39,3 +39,35 @@ You can validate your updated firmware on InfiniTime >= 1.0 by following this si
- Open settings by tapping the cogwheel on the bottom right
- Swipe up until you find an entry named **Firmware** and tap on it
- If the firmware is not validated yet, you can either validate the running firmware, or reset and revert to the previous firmware version
+
+# Updating resources
+
+Since InfiniTime 1.11 apps and watchfaces can take benefit of the external flash memory to store their pictures and fonts.
+This external memory is a lot bigger (4MB) than the internal memory where the firmware is flashed (512KB).
+Since those resources are not part of the firmware, they need to be flashed and updated separately.
+
+Resources are packaged into a single .zip file named `infinitime-resources-x.y.z.zip` (where `x`, `y` and `z` are the version numbers of InfiniTime).
+You can use the companion app of your choice to flash the resources.
+
+**Note : at the time of writing this page, [Amazfish](https://github.com/piggz/harbour-amazfish) and [ITD](https://gitea.arsenm.dev/Arsen6331/itd) have already integrated this functionality. Other companion apps will hopefully implement it soon!*
+
+## Amazfish
+Use the `Download file` functionality of Amazfish.
+
+![Update resources with Amazfish - Download file](amazfish-external-resources-1.png)
+
+Amazfish automatically detects the file type (firmware or resources) and apply the corresponding flash procedure when you hit the button **Send file**.
+
+![Update resources with Amazfish](amazfish-external-resources-2.png)
+
+## ITD
+
+Run `itctl` with the `res` command:
+
+```
+itctl res load infinitime-resources-1.10.0.zip
+```
+
+Example:
+
+![Update resources using itctl](itd-external-resources.png)
diff --git a/docker/Dockerfile b/docker/Dockerfile
index f2d187d0..927160db 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -22,6 +22,12 @@ RUN apt-get update -qq \
python3-dev \
git \
apt-utils \
+ pkg-config \
+ libpixman-1-dev \
+ libcairo2-dev \
+ libpango-1.0-0 \
+ ibpango1.0-dev \
+ libpangocairo-1.0-0 \
&& curl -sL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y nodejs \
&& rm -rf /var/cache/apt/* /var/lib/apt/lists/*;
@@ -33,6 +39,10 @@ RUN pip3 install -Iv cryptography==3.3
RUN pip3 install cbor
RUN npm i lv_font_conv@1.5.2 -g
+RUN npm i ts-node@10.9.1 -g
+RUN npm i @swc/core -g
+RUN npm i lv_img_conv@0.3.0 -g
+
# build.sh knows how to compile
COPY build.sh /opt/
diff --git a/docker/build.sh b/docker/build.sh
index 663390c4..07e0d17e 100755
--- a/docker/build.sh
+++ b/docker/build.sh
@@ -11,6 +11,10 @@ export SOURCES_DIR="${SOURCES_DIR:=/sources}"
export BUILD_DIR="${BUILD_DIR:=$SOURCES_DIR/build}"
export OUTPUT_DIR="${OUTPUT_DIR:=$SOURCES_DIR/build/output}"
+# Specify a folder with read/write access to NPM
+export NPM_DIR="$BUILD_DIR/npm"
+export npm_config_cache="${NPM_DIR}"
+
export BUILD_TYPE=${BUILD_TYPE:=Release}
export GCC_ARM_VER=${GCC_ARM_VER:="10.3-2021.10"}
export NRF_SDK_VER=${NRF_SDK_VER:="nRF5_SDK_15.3.0_59ac345"}
@@ -59,10 +63,10 @@ CmakeGenerate() {
-S "$SOURCES_DIR" \
-B "$BUILD_DIR" \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
- -DUSE_OPENOCD=1 \
-DARM_NONE_EABI_TOOLCHAIN_PATH="$TOOLS_DIR/$GCC_ARM_PATH" \
-DNRF5_SDK_PATH="$TOOLS_DIR/$NRF_SDK_VER" \
- -DBUILD_DFU=1
+ -DBUILD_DFU=1 \
+ -DBUILD_RESOURCES=1
}
CmakeBuild() {
diff --git a/docker/post_build.sh.in b/docker/post_build.sh.in
index 8c94471a..5d82f3be 100755
--- a/docker/post_build.sh.in
+++ b/docker/post_build.sh.in
@@ -15,6 +15,8 @@ cp "$BUILD_DIR/src/pinetime-mcuboot-app-dfu-$PROJECT_VERSION.zip" "$OUTPUT_DIR/p
cp "$BUILD_DIR/src/pinetime-mcuboot-recovery-loader-image-$PROJECT_VERSION.bin" "$OUTPUT_DIR/pinetime-mcuboot-recovery-loader-image-$PROJECT_VERSION.bin"
cp "$BUILD_DIR/src/pinetime-mcuboot-recovery-loader-dfu-$PROJECT_VERSION.zip" "$OUTPUT_DIR/pinetime-mcuboot-recovery-loader-dfu-$PROJECT_VERSION.zip"
+cp "$BUILD_DIR/src/resources/infinitime-resources-$PROJECT_VERSION.zip" "$OUTPUT_DIR/infinitime-resources-$PROJECT_VERSION.zip"
+
mkdir -p "$OUTPUT_DIR/src"
cp $BUILD_DIR/src/*.bin "$OUTPUT_DIR/src/"
cp $BUILD_DIR/src/*.hex "$OUTPUT_DIR/src/"
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index db4a8e2a..e59c0d81 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -396,6 +396,7 @@ list(APPEND SOURCE_FILES
displayapp/screens/Motion.cpp
displayapp/screens/FlashLight.cpp
displayapp/screens/List.cpp
+ displayapp/screens/CheckboxList.cpp
displayapp/screens/BatteryInfo.cpp
displayapp/screens/Steps.cpp
displayapp/screens/Timer.cpp
@@ -426,8 +427,10 @@ list(APPEND SOURCE_FILES
displayapp/icons/bg_clock.c
displayapp/screens/WatchFaceAnalog.cpp
displayapp/screens/WatchFaceDigital.cpp
+ displayapp/screens/WatchFaceInfineat.cpp
displayapp/screens/WatchFaceTerminal.cpp
displayapp/screens/WatchFacePineTimeStyle.cpp
+ displayapp/screens/WatchFaceCasioStyleG7710.cpp
##
@@ -602,6 +605,7 @@ set(INCLUDE_FILES
displayapp/screens/FirmwareUpdate.h
displayapp/screens/FirmwareValidation.h
displayapp/screens/ApplicationList.h
+ displayapp/screens/CheckboxList.h
displayapp/Apps.h
displayapp/screens/Notifications.h
displayapp/screens/HeartRate.h
@@ -940,6 +944,10 @@ add_custom_command(TARGET ${EXECUTABLE_NAME}
COMMAND ${CMAKE_OBJCOPY} -O ihex ${EXECUTABLE_FILE_NAME}.out "${EXECUTABLE_FILE_NAME}.hex"
COMMENT "post build steps for ${EXECUTABLE_FILE_NAME}")
+if(BUILD_RESOURCES)
+ add_dependencies(${EXECUTABLE_NAME} GenerateResources)
+endif()
+
# Build binary intended to be used by bootloader
set(EXECUTABLE_MCUBOOT_NAME "pinetime-mcuboot-app")
set(EXECUTABLE_MCUBOOT_FILE_NAME ${EXECUTABLE_MCUBOOT_NAME}-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH})
@@ -973,6 +981,10 @@ add_custom_command(TARGET ${EXECUTABLE_MCUBOOT_NAME}
COMMENT "post build steps for ${EXECUTABLE_MCUBOOT_FILE_NAME}"
)
+if(BUILD_RESOURCES)
+ add_dependencies(${EXECUTABLE_MCUBOOT_NAME} GenerateResources)
+endif()
+
if(BUILD_DFU)
add_custom_command(TARGET ${EXECUTABLE_MCUBOOT_NAME}
POST_BUILD
@@ -1127,68 +1139,7 @@ if(BUILD_DFU)
)
endif()
+if(BUILD_RESOURCES)
+ add_subdirectory(resources)
+endif()
-# FLASH
-if (USE_JLINK)
- add_custom_target(FLASH_ERASE
- COMMAND ${NRFJPROG} --eraseall -f ${NRF_TARGET}
- COMMENT "erasing flashing"
- )
- add_custom_target("FLASH_${EXECUTABLE_NAME}"
- DEPENDS ${EXECUTABLE_NAME}
- COMMAND ${NRFJPROG} --program ${EXECUTABLE_FILE_NAME}.hex -f ${NRF_TARGET} --sectorerase
- COMMAND sleep 0.5s
- COMMAND ${NRFJPROG} --reset -f ${NRF_TARGET}
- COMMENT "flashing ${EXECUTABLE_FILE_NAME}.hex"
- )
-
-elseif (USE_GDB_CLIENT)
- add_custom_target(FLASH_ERASE
- COMMAND ${GDB_CLIENT_BIN_PATH} -nx --batch -ex 'target extended-remote ${GDB_CLIENT_TARGET_REMOTE}' -ex 'monitor swdp_scan' -ex 'attach 1' -ex 'mon erase_mass'
- COMMENT "erasing flashing"
- )
- add_custom_target("FLASH_${EXECUTABLE_NAME}"
- DEPENDS ${EXECUTABLE_NAME}
- COMMAND ${GDB_CLIENT_BIN_PATH} -nx --batch -ex 'target extended-remote ${GDB_CLIENT_TARGET_REMOTE}' -ex 'monitor swdp_scan' -ex 'attach 1' -ex 'load' -ex 'kill' ${EXECUTABLE_FILE_NAME}.hex
- COMMENT "flashing ${EXECUTABLE_FILE_NAME}.hex"
- )
-elseif (USE_OPENOCD)
- if (USE_CMSIS_DAP)
- add_custom_target(FLASH_ERASE
- COMMAND ${OPENOCD_BIN_PATH} -c 'source [find interface/cmsis-dap.cfg]' -c 'transport select swd'
- -c 'source [find target/nrf52.cfg]'
- -c 'init'
- -c 'halt'
- -c 'nrf5 mass_erase'
- -c 'halt'
- -c 'reset'
- -c 'exit'
- COMMENT "erasing flashing"
- )
- add_custom_target("FLASH_${EXECUTABLE_NAME}"
- DEPENDS ${EXECUTABLE_NAME}
- COMMAND ${OPENOCD_BIN_PATH}
- -c 'tcl_port disabled'
- -c 'gdb_port 3333'
- -c 'telnet_port 4444'
- -c 'source [find interface/cmsis-dap.cfg]'
- -c 'transport select swd'
- -c 'source [find target/nrf52.cfg]'
- -c 'halt'
- -c "program \"${EXECUTABLE_FILE_NAME}.hex\""
- -c 'reset'
- -c 'shutdown'
- COMMENT "flashing ${EXECUTABLE_BIN_NAME}.hex"
- )
- else ()
- add_custom_target(FLASH_ERASE
- COMMAND ${OPENOCD_BIN_PATH} -f interface/stlink.cfg -c 'transport select hla_swd' -f target/nrf52.cfg -c init -c halt -c 'nrf5 mass_erase' -c reset -c shutdown
- COMMENT "erasing flashing"
- )
- add_custom_target("FLASH_${EXECUTABLE_NAME}"
- DEPENDS ${EXECUTABLE_NAME}
- COMMAND ${OPENOCD_BIN_PATH} -c "tcl_port disabled" -c "gdb_port 3333" -c "telnet_port 4444" -f interface/stlink.cfg -c 'transport select hla_swd' -f target/nrf52.cfg -c "program \"${EXECUTABLE_FILE_NAME}.hex\"" -c reset -c shutdown
- COMMENT "flashing ${EXECUTABLE_FILE_NAME}.hex"
- )
- endif ()
-endif ()
diff --git a/src/components/alarm/AlarmController.cpp b/src/components/alarm/AlarmController.cpp
index 9f4e9105..d97e1cff 100644
--- a/src/components/alarm/AlarmController.cpp
+++ b/src/components/alarm/AlarmController.cpp
@@ -82,7 +82,7 @@ void AlarmController::ScheduleAlarm() {
state = AlarmState::Set;
}
-uint32_t AlarmController::SecondsToAlarm() {
+uint32_t AlarmController::SecondsToAlarm() const {
return std::chrono::duration_cast<std::chrono::seconds>(alarmTime - dateTimeController.CurrentDateTime()).count();
}
diff --git a/src/components/alarm/AlarmController.h b/src/components/alarm/AlarmController.h
index d630a128..91f60f5a 100644
--- a/src/components/alarm/AlarmController.h
+++ b/src/components/alarm/AlarmController.h
@@ -36,7 +36,7 @@ namespace Pinetime {
void ScheduleAlarm();
void DisableAlarm();
void SetOffAlarmNow();
- uint32_t SecondsToAlarm();
+ uint32_t SecondsToAlarm() const;
void StopAlerting();
enum class AlarmState { Not_Set, Set, Alerting };
enum class RecurType { None, Daily, Weekdays };
diff --git a/src/components/datetime/DateTimeController.cpp b/src/components/datetime/DateTimeController.cpp
index ba04705f..4dc16329 100644
--- a/src/components/datetime/DateTimeController.cpp
+++ b/src/components/datetime/DateTimeController.cpp
@@ -7,6 +7,7 @@ using namespace Pinetime::Controllers;
namespace {
char const* DaysStringShort[] = {"--", "MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
+ char const* DaysStringShortLow[] = {"--", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
char const* MonthsString[] = {"--", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
char const* MonthsStringLow[] = {"--", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
}
@@ -126,6 +127,10 @@ const char* DateTime::MonthShortToStringLow(Months month) {
return MonthsStringLow[static_cast<uint8_t>(month)];
}
+const char* DateTime::DayOfWeekShortToStringLow() const {
+ return DaysStringShortLow[static_cast<uint8_t>(dayOfWeek)];
+}
+
void DateTime::Register(Pinetime::System::SystemTask* systemTask) {
this->systemTask = systemTask;
}
diff --git a/src/components/datetime/DateTimeController.h b/src/components/datetime/DateTimeController.h
index 00bbc2ee..81319d15 100644
--- a/src/components/datetime/DateTimeController.h
+++ b/src/components/datetime/DateTimeController.h
@@ -64,6 +64,7 @@ namespace Pinetime {
const char* MonthShortToString() const;
const char* DayOfWeekShortToString() const;
static const char* MonthShortToStringLow(Months month);
+ const char* DayOfWeekShortToStringLow() const;
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> CurrentDateTime() const {
return currentDateTime;
diff --git a/src/components/motor/MotorController.cpp b/src/components/motor/MotorController.cpp
index 90e41d20..db6103f4 100644
--- a/src/components/motor/MotorController.cpp
+++ b/src/components/motor/MotorController.cpp
@@ -19,7 +19,7 @@ void MotorController::Ring(TimerHandle_t xTimer) {
}
void MotorController::RunForDuration(uint8_t motorDuration) {
- if (xTimerChangePeriod(shortVib, pdMS_TO_TICKS(motorDuration), 0) == pdPASS && xTimerStart(shortVib, 0) == pdPASS) {
+ if (motorDuration > 0 && xTimerChangePeriod(shortVib, pdMS_TO_TICKS(motorDuration), 0) == pdPASS && xTimerStart(shortVib, 0) == pdPASS) {
nrf_gpio_pin_clear(PinMap::Motor);
}
}
diff --git a/src/components/settings/Settings.h b/src/components/settings/Settings.h
index 62586567..9661a199 100644
--- a/src/components/settings/Settings.h
+++ b/src/components/settings/Settings.h
@@ -34,7 +34,8 @@ namespace Pinetime {
Navy,
Magenta,
Purple,
- Orange
+ Orange,
+ Pink
};
enum class PTSGaugeStyle : uint8_t { Full, Half, Numeric };
@@ -44,6 +45,10 @@ namespace Pinetime {
Colors ColorBG = Colors::Black;
PTSGaugeStyle gaugeStyle = PTSGaugeStyle::Full;
};
+ struct WatchFaceInfineat {
+ bool showSideCover = true;
+ int colorIndex = 0;
+ };
Settings(Pinetime::Controllers::FS& fs);
@@ -97,6 +102,26 @@ namespace Pinetime {
return settings.PTS.ColorBG;
};
+ void SetInfineatShowSideCover(bool show) {
+ if (show != settings.watchFaceInfineat.showSideCover) {
+ settings.watchFaceInfineat.showSideCover = show;
+ settingsChanged = true;
+ }
+ };
+ bool GetInfineatShowSideCover() const {
+ return settings.watchFaceInfineat.showSideCover;
+ };
+
+ void SetInfineatColorIndex(int index) {
+ if (index != settings.watchFaceInfineat.colorIndex) {
+ settings.watchFaceInfineat.colorIndex = index;
+ settingsChanged = true;
+ }
+ };
+ int GetInfineatColorIndex() const {
+ return settings.watchFaceInfineat.colorIndex;
+ };
+
void SetPTSGaugeStyle(PTSGaugeStyle gaugeStyle) {
if (gaugeStyle != settings.PTS.gaugeStyle)
settingsChanged = true;
@@ -110,6 +135,14 @@ namespace Pinetime {
appMenu = menu;
};
+ void SetWatchfacesMenu(uint8_t menu) {
+ watchFacesMenu = menu;
+ };
+
+ uint8_t GetWatchfacesMenu() const {
+ return watchFacesMenu;
+ };
+
uint8_t GetAppMenu() const {
return appMenu;
};
@@ -238,6 +271,8 @@ namespace Pinetime {
PineTimeStyle PTS;
+ WatchFaceInfineat watchFaceInfineat;
+
std::bitset<4> wakeUpMode {0};
uint16_t shakeWakeThreshold = 150;
Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium;
@@ -248,6 +283,7 @@ namespace Pinetime {
uint8_t appMenu = 0;
uint8_t settingsMenu = 0;
+ uint8_t watchFacesMenu = 0;
/* ble state is intentionally not saved with the other watch settings and initialized
* to off (false) on every boot because we always want ble to be enabled on startup
*/
diff --git a/src/displayapp/Colors.cpp b/src/displayapp/Colors.cpp
index 106c5163..2e9790eb 100644
--- a/src/displayapp/Colors.cpp
+++ b/src/displayapp/Colors.cpp
@@ -39,6 +39,8 @@ lv_color_t Pinetime::Applications::Convert(Pinetime::Controllers::Settings::Colo
return LV_COLOR_MAKE(0xb0, 0x0, 0xb0);
case Pinetime::Controllers::Settings::Colors::Orange:
return LV_COLOR_ORANGE;
+ case Pinetime::Controllers::Settings::Colors::Pink:
+ return LV_COLOR_MAKE(0xFF, 0xAE, 0xC9);
default:
return LV_COLOR_WHITE;
}
diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp
index 29684466..85c6da3e 100644
--- a/src/displayapp/DisplayApp.cpp
+++ b/src/displayapp/DisplayApp.cpp
@@ -75,7 +75,8 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
Pinetime::Controllers::TimerController& timerController,
Pinetime::Controllers::AlarmController& alarmController,
Pinetime::Controllers::BrightnessController& brightnessController,
- Pinetime::Controllers::TouchHandler& touchHandler)
+ Pinetime::Controllers::TouchHandler& touchHandler,
+ Pinetime::Controllers::FS& filesystem)
: lcd {lcd},
lvgl {lvgl},
touchPanel {touchPanel},
@@ -91,7 +92,8 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
timerController {timerController},
alarmController {alarmController},
brightnessController {brightnessController},
- touchHandler {touchHandler} {
+ touchHandler {touchHandler},
+ filesystem {filesystem} {
}
void DisplayApp::Start(System::BootErrors error) {
@@ -125,7 +127,7 @@ void DisplayApp::Process(void* instance) {
void DisplayApp::InitHw() {
brightnessController.Init();
- brightnessController.Set(settingsController.GetBrightness());
+ ApplyBrightness();
}
void DisplayApp::Refresh() {
@@ -156,7 +158,7 @@ void DisplayApp::Refresh() {
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
break;
case Messages::RestoreBrightness:
- brightnessController.Set(settingsController.GetBrightness());
+ ApplyBrightness();
break;
case Messages::GoToSleep:
while (brightnessController.Level() != Controllers::BrightnessController::Levels::Off) {
@@ -167,7 +169,7 @@ void DisplayApp::Refresh() {
state = States::Idle;
break;
case Messages::GoToRunning:
- brightnessController.Set(settingsController.GetBrightness());
+ ApplyBrightness();
state = States::Running;
break;
case Messages::UpdateTimeOut:
@@ -301,7 +303,7 @@ void DisplayApp::ReturnApp(Apps app, DisplayApp::FullRefreshDirections direction
void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) {
touchHandler.CancelTap();
- brightnessController.Set(settingsController.GetBrightness());
+ ApplyBrightness();
currentScreen.reset(nullptr);
SetFullRefresh(direction);
@@ -324,7 +326,8 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
notificationManager,
settingsController,
heartRateController,
- motionController);
+ motionController,
+ filesystem);
break;
case Apps::Error:
@@ -527,3 +530,12 @@ void DisplayApp::PushMessageToSystemTask(Pinetime::System::Messages message) {
void DisplayApp::Register(Pinetime::System::SystemTask* systemTask) {
this->systemTask = systemTask;
}
+void DisplayApp::ApplyBrightness() {
+ auto brightness = settingsController.GetBrightness();
+ if(brightness != Controllers::BrightnessController::Levels::Low &&
+ brightness != Controllers::BrightnessController::Levels::Medium &&
+ brightness != Controllers::BrightnessController::Levels::High) {
+ brightness = Controllers::BrightnessController::Levels::High;
+ }
+ brightnessController.Set(brightness);
+}
diff --git a/src/displayapp/DisplayApp.h b/src/displayapp/DisplayApp.h
index 43972232..4c54e227 100644
--- a/src/displayapp/DisplayApp.h
+++ b/src/displayapp/DisplayApp.h
@@ -62,7 +62,8 @@ namespace Pinetime {
Pinetime::Controllers::TimerController& timerController,
Pinetime::Controllers::AlarmController& alarmController,
Pinetime::Controllers::BrightnessController& brightnessController,
- Pinetime::Controllers::TouchHandler& touchHandler);
+ Pinetime::Controllers::TouchHandler& touchHandler,
+ Pinetime::Controllers::FS& filesystem);
void Start(System::BootErrors error);
void PushMessage(Display::Messages msg);
@@ -90,6 +91,7 @@ namespace Pinetime {
Pinetime::Controllers::AlarmController& alarmController;
Pinetime::Controllers::BrightnessController& brightnessController;
Pinetime::Controllers::TouchHandler& touchHandler;
+ Pinetime::Controllers::FS& filesystem;
Pinetime::Controllers::FirmwareValidator validator;
@@ -119,6 +121,7 @@ namespace Pinetime {
Apps nextApp = Apps::None;
DisplayApp::FullRefreshDirections nextDirection;
System::BootErrors bootError;
+ void ApplyBrightness();
};
}
}
diff --git a/src/displayapp/DisplayAppRecovery.cpp b/src/displayapp/DisplayAppRecovery.cpp
index ca15dbaf..e553aa87 100644
--- a/src/displayapp/DisplayAppRecovery.cpp
+++ b/src/displayapp/DisplayAppRecovery.cpp
@@ -2,6 +2,7 @@
#include <FreeRTOS.h>
#include <task.h>
#include <libraries/log/nrf_log.h>
+#include "components/fs/FS.h"
#include "components/rle/RleDecoder.h"
#include "touchhandler/TouchHandler.h"
#include "displayapp/icons/infinitime/infinitime-nb.c"
@@ -24,7 +25,8 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
Pinetime::Controllers::TimerController& timerController,
Pinetime::Controllers::AlarmController& alarmController,
Pinetime::Controllers::BrightnessController& brightnessController,
- Pinetime::Controllers::TouchHandler& touchHandler)
+ Pinetime::Controllers::TouchHandler& touchHandler,
+ Pinetime::Controllers::FS& filesystem)
: lcd {lcd}, bleController {bleController} {
}
diff --git a/src/displayapp/DisplayAppRecovery.h b/src/displayapp/DisplayAppRecovery.h
index 0e801221..7d4f0fd0 100644
--- a/src/displayapp/DisplayAppRecovery.h
+++ b/src/displayapp/DisplayAppRecovery.h
@@ -35,6 +35,7 @@ namespace Pinetime {
class TimerController;
class AlarmController;
class BrightnessController;
+ class FS;
}
namespace System {
@@ -59,7 +60,8 @@ namespace Pinetime {
Pinetime::Controllers::TimerController& timerController,
Pinetime::Controllers::AlarmController& alarmController,
Pinetime::Controllers::BrightnessController& brightnessController,
- Pinetime::Controllers::TouchHandler& touchHandler);
+ Pinetime::Controllers::TouchHandler& touchHandler,
+ Pinetime::Controllers::FS& filesystem);
void Start();
void Start(Pinetime::System::BootErrors) {
Start();
diff --git a/src/displayapp/screens/CheckboxList.cpp b/src/displayapp/screens/CheckboxList.cpp
new file mode 100644
index 00000000..952d86da
--- /dev/null
+++ b/src/displayapp/screens/CheckboxList.cpp
@@ -0,0 +1,116 @@
+#include "displayapp/screens/CheckboxList.h"
+#include "displayapp/DisplayApp.h"
+#include "displayapp/screens/Styles.h"
+
+using namespace Pinetime::Applications::Screens;
+
+namespace {
+ static void event_handler(lv_obj_t* obj, lv_event_t event) {
+ CheckboxList* screen = static_cast<CheckboxList*>(obj->user_data);
+ screen->UpdateSelected(obj, event);
+ }
+
+}
+
+CheckboxList::CheckboxList(const uint8_t screenID,
+ const uint8_t numScreens,
+ DisplayApp* app,
+ Controllers::Settings& settingsController,
+ const char* optionsTitle,
+ const char* optionsSymbol,
+ void (Controllers::Settings::*SetOptionIndex)(uint8_t),
+ uint8_t (Controllers::Settings::*GetOptionIndex)() const,
+ std::array<const char*, MaxItems> options)
+ : Screen(app),
+ screenID {screenID},
+ settingsController {settingsController},
+ SetOptionIndex {SetOptionIndex},
+ GetOptionIndex {GetOptionIndex},
+ options {options} {
+
+ settingsController.SetWatchfacesMenu(screenID);
+
+ // Set the background to Black
+ lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
+
+ if (numScreens > 1) {
+ pageIndicatorBasePoints[0].x = LV_HOR_RES - 1;
+ pageIndicatorBasePoints[0].y = 0;
+ pageIndicatorBasePoints[1].x = LV_HOR_RES - 1;
+ pageIndicatorBasePoints[1].y = LV_VER_RES;
+
+ pageIndicatorBase = lv_line_create(lv_scr_act(), NULL);
+ lv_obj_set_style_local_line_width(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3);
+ lv_obj_set_style_local_line_color(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111));
+ lv_line_set_points(pageIndicatorBase, pageIndicatorBasePoints.data(), 2);
+
+ const uint16_t indicatorSize = LV_VER_RES / numScreens;
+ const uint16_t indicatorPos = indicatorSize * screenID;
+
+ pageIndicatorPoints[0].x = LV_HOR_RES - 1;
+ pageIndicatorPoints[0].y = indicatorPos;
+ pageIndicatorPoints[1].x = LV_HOR_RES - 1;
+ pageIndicatorPoints[1].y = indicatorPos + indicatorSize;
+
+ pageIndicator = lv_line_create(lv_scr_act(), NULL);
+ lv_obj_set_style_local_line_width(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3);
+ lv_obj_set_style_local_line_color(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
+ lv_line_set_points(pageIndicator, pageIndicatorPoints.data(), 2);
+ }
+
+ lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr);
+
+ lv_obj_set_style_local_bg_opa(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
+ lv_obj_set_style_local_pad_all(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 10);
+ 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_width(container1, LV_HOR_RES - 20);
+ lv_obj_set_height(container1, LV_VER_RES - 50);
+ lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT);
+
+ lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr);
+ lv_label_set_text_static(title, optionsTitle);
+ lv_label_set_align(title, LV_LABEL_ALIGN_CENTER);
+ lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 10, 15);
+
+ lv_obj_t* 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_ORANGE);
+ lv_label_set_text_static(icon, optionsSymbol);
+ lv_label_set_align(icon, LV_LABEL_ALIGN_CENTER);
+ lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0);
+
+ for (unsigned int i = 0; i < options.size(); i++) {
+ if (strcmp(options[i], "")) {
+ cbOption[i] = lv_checkbox_create(container1, nullptr);
+ lv_checkbox_set_text(cbOption[i], options[i]);
+ cbOption[i]->user_data = this;
+ lv_obj_set_event_cb(cbOption[i], event_handler);
+ SetRadioButtonStyle(cbOption[i]);
+
+ if (static_cast<unsigned int>((settingsController.*GetOptionIndex)() - MaxItems * screenID) == i) {
+ lv_checkbox_set_checked(cbOption[i], true);
+ }
+ }
+ }
+}
+
+CheckboxList::~CheckboxList() {
+ lv_obj_clean(lv_scr_act());
+}
+
+void CheckboxList::UpdateSelected(lv_obj_t* object, lv_event_t event) {
+ if (event == LV_EVENT_VALUE_CHANGED) {
+ for (unsigned int i = 0; i < options.size(); i++) {
+ if (strcmp(options[i], "")) {
+ if (object == cbOption[i]) {
+ lv_checkbox_set_checked(cbOption[i], true);
+ (settingsController.*SetOptionIndex)(MaxItems * screenID + i);
+ } else {
+ lv_checkbox_set_checked(cbOption[i], false);
+ }
+ }
+ }
+ }
+}
diff --git a/src/displayapp/screens/CheckboxList.h b/src/displayapp/screens/CheckboxList.h
new file mode 100644
index 00000000..5bdd143e
--- /dev/null
+++ b/src/displayapp/screens/CheckboxList.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <lvgl/lvgl.h>
+#include <cstdint>
+#include <memory>
+#include <array>
+#include "displayapp/screens/Screen.h"
+#include "displayapp/Apps.h"
+#include "components/settings/Settings.h"
+
+namespace Pinetime {
+ namespace Applications {
+ namespace Screens {
+ class CheckboxList : public Screen {
+ public:
+ static constexpr size_t MaxItems = 4;
+
+ CheckboxList(const uint8_t screenID,
+ const uint8_t numScreens,
+ DisplayApp* app,
+ Controllers::Settings& settingsController,
+ const char* optionsTitle,
+ const char* optionsSymbol,
+ void (Controllers::Settings::*SetOptionIndex)(uint8_t),
+ uint8_t (Controllers::Settings::*GetOptionIndex)() const,
+ std::array<const char*, MaxItems> options);
+
+ ~CheckboxList() override;
+
+ void UpdateSelected(lv_obj_t* object, lv_event_t event);
+
+ private:
+ const uint8_t screenID;
+ Controllers::Settings& settingsController;
+ const char* optionsTitle;
+ const char* optionsSymbol;
+ void (Controllers::Settings::*SetOptionIndex)(uint8_t);
+ uint8_t (Controllers::Settings::*GetOptionIndex)() const;
+ std::array<const char*, MaxItems> options;
+ std::array<lv_obj_t*, MaxItems> cbOption;
+ std::array<lv_point_t, 2> pageIndicatorBasePoints;
+ std::array<lv_point_t, 2> pageIndicatorPoints;
+ lv_obj_t* pageIndicatorBase;
+ lv_obj_t* pageIndicator;
+ };
+ }
+ }
+}
diff --git a/src/displayapp/screens/Clock.cpp b/src/displayapp/screens/Clock.cpp
index 1687dccf..07d307e0 100644
--- a/src/displayapp/screens/Clock.cpp
+++ b/src/displayapp/screens/Clock.cpp
@@ -10,8 +10,10 @@
#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;
@@ -19,18 +21,20 @@ Clock::Clock(DisplayApp* app,
Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController,
Controllers::Ble& bleController,
- Controllers::NotificationManager& notificatioManager,
+ Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController,
Controllers::HeartRateController& heartRateController,
- Controllers::MotionController& motionController)
+ Controllers::MotionController& motionController,
+ Controllers::FS& filesystem)
: Screen(app),
dateTimeController {dateTimeController},
batteryController {batteryController},
bleController {bleController},
- notificatioManager {notificatioManager},
+ notificationManager {notificationManager},
settingsController {settingsController},
heartRateController {heartRateController},
motionController {motionController},
+ filesystem {filesystem},
screen {[this, &settingsController]() {
switch (settingsController.GetClockFace()) {
case 0:
@@ -45,6 +49,12 @@ Clock::Clock(DisplayApp* app,
case 3:
return WatchFaceTerminalScreen();
break;
+ case 4:
+ return WatchFaceInfineatScreen();
+ break;
+ case 5:
+ return WatchFaceCasioStyleG7710();
+ break;
}
return WatchFaceDigitalScreen();
}()} {
@@ -68,7 +78,7 @@ std::unique_ptr<Screen> Clock::WatchFaceDigitalScreen() {
dateTimeController,
batteryController,
bleController,
- notificatioManager,
+ notificationManager,
settingsController,
heartRateController,
motionController);
@@ -79,7 +89,7 @@ std::unique_ptr<Screen> Clock::WatchFaceAnalogScreen() {
dateTimeController,
batteryController,
bleController,
- notificatioManager,
+ notificationManager,
settingsController);
}
@@ -88,7 +98,7 @@ std::unique_ptr<Screen> Clock::WatchFacePineTimeStyleScreen() {
dateTimeController,
batteryController,
bleController,
- notificatioManager,
+ notificationManager,
settingsController,
motionController);
}
@@ -98,8 +108,31 @@ std::unique_ptr<Screen> Clock::WatchFaceTerminalScreen() {
dateTimeController,
batteryController,
bleController,
- notificatioManager,
+ notificationManager,
settingsController,
heartRateController,
motionController);
}
+
+std::unique_ptr<Screen> Clock::WatchFaceInfineatScreen() {
+ return std::make_unique<Screens::WatchFaceInfineat>(app,
+ dateTimeController,
+ batteryController,
+ bleController,
+ notificationManager,
+ settingsController,
+ motionController,
+ filesystem);
+}
+
+std::unique_ptr<Screen> Clock::WatchFaceCasioStyleG7710() {
+ return std::make_unique<Screens::WatchFaceCasioStyleG7710>(app,
+ dateTimeController,
+ batteryController,
+ bleController,
+ notificationManager,
+ settingsController,
+ heartRateController,
+ motionController,
+ filesystem);
+}
diff --git a/src/displayapp/screens/Clock.h b/src/displayapp/screens/Clock.h
index 1ba752c7..613fef57 100644
--- a/src/displayapp/screens/Clock.h
+++ b/src/displayapp/screens/Clock.h
@@ -25,10 +25,11 @@ namespace Pinetime {
Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController,
Controllers::Ble& bleController,
- Controllers::NotificationManager& notificatioManager,
+ Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController,
Controllers::HeartRateController& heartRateController,
- Controllers::MotionController& motionController);
+ Controllers::MotionController& motionController,
+ Controllers::FS& filesystem);
~Clock() override;
bool OnTouchEvent(TouchEvents event) override;
@@ -38,16 +39,19 @@ namespace Pinetime {
Controllers::DateTime& dateTimeController;
Controllers::Battery& batteryController;
Controllers::Ble& bleController;
- Controllers::NotificationManager& notificatioManager;
+ 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/Error.cpp b/src/displayapp/screens/Error.cpp
index 1f2c61d6..74f222a9 100644
--- a/src/displayapp/screens/Error.cpp
+++ b/src/displayapp/screens/Error.cpp
@@ -36,7 +36,8 @@ Error::Error(Pinetime::Applications::DisplayApp* app, System::BootErrors error)
lv_obj_set_event_cb(btnOk, ButtonEventCallback);
lv_obj_set_size(btnOk, LV_HOR_RES, 50);
lv_obj_align(btnOk, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0);
- lv_obj_set_style_local_value_str(btnOk, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "Proceed");
+ lv_obj_t* lblOk = lv_label_create(btnOk, nullptr);
+ lv_label_set_text_static(lblOk, "Proceed");
lv_obj_set_style_local_bg_color(btnOk, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE);
}
diff --git a/src/displayapp/screens/Metronome.cpp b/src/displayapp/screens/Metronome.cpp
index 174ac1b6..df87092b 100644
--- a/src/displayapp/screens/Metronome.cpp
+++ b/src/displayapp/screens/Metronome.cpp
@@ -64,7 +64,8 @@ Metronome::Metronome(DisplayApp* app, Controllers::MotorController& motorControl
lv_obj_set_event_cb(playPause, eventHandler);
lv_obj_set_size(playPause, 115, 50);
lv_obj_align(playPause, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
- lv_obj_set_style_local_value_str(playPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Symbols::play);
+ lblPlayPause = lv_label_create(playPause, nullptr);
+ lv_label_set_text_static(lblPlayPause, Symbols::play);
taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
}
@@ -126,12 +127,12 @@ void Metronome::OnEvent(lv_obj_t* obj, lv_event_t event) {
if (obj == playPause) {
metronomeStarted = !metronomeStarted;
if (metronomeStarted) {
- lv_obj_set_style_local_value_str(playPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Symbols::pause);
+ lv_label_set_text_static(lblPlayPause, Symbols::pause);
systemTask.PushMessage(System::Messages::DisableSleeping);
startTime = xTaskGetTickCount();
counter = 1;
} else {
- lv_obj_set_style_local_value_str(playPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Symbols::play);
+ lv_label_set_text_static(lblPlayPause, Symbols::play);
systemTask.PushMessage(System::Messages::EnableSleeping);
}
}
diff --git a/src/displayapp/screens/Metronome.h b/src/displayapp/screens/Metronome.h
index 8933b17e..c062959c 100644
--- a/src/displayapp/screens/Metronome.h
+++ b/src/displayapp/screens/Metronome.h
@@ -31,6 +31,7 @@ namespace Pinetime {
lv_obj_t *bpmArc, *bpmTap, *bpmValue;
lv_obj_t *bpbDropdown, *currentBpbText;
lv_obj_t* playPause;
+ lv_obj_t* lblPlayPause;
lv_task_t* taskRefresh;
};
diff --git a/src/displayapp/screens/Twos.cpp b/src/displayapp/screens/Twos.cpp
index 5d1f4980..9e7418c8 100644
--- a/src/displayapp/screens/Twos.cpp
+++ b/src/displayapp/screens/Twos.cpp
@@ -7,53 +7,34 @@ using namespace Pinetime::Applications::Screens;
Twos::Twos(Pinetime::Applications::DisplayApp* app) : Screen(app) {
- // create styles to apply to different valued tiles
- lv_style_init(&style_cell1);
- lv_style_init(&style_cell2);
- lv_style_init(&style_cell3);
- lv_style_init(&style_cell4);
- lv_style_init(&style_cell5);
+ struct colorPair {
+ lv_color_t bg;
+ lv_color_t fg;
+ };
- lv_style_set_border_color(&style_cell1, LV_STATE_DEFAULT, lv_color_hex(0xbbada0));
- lv_style_set_border_width(&style_cell1, LV_STATE_DEFAULT, 3);
- lv_style_set_bg_opa(&style_cell1, LV_STATE_DEFAULT, LV_OPA_COVER);
- lv_style_set_bg_color(&style_cell1, LV_STATE_DEFAULT, lv_color_hex(0xcdc0b4));
- lv_style_set_pad_top(&style_cell1, LV_STATE_DEFAULT, 29);
- lv_style_set_text_color(&style_cell1, LV_STATE_DEFAULT, LV_COLOR_BLACK);
+ static constexpr colorPair colors[nColors] = {
+ {LV_COLOR_MAKE(0xcd, 0xc0, 0xb4), LV_COLOR_BLACK},
+ {LV_COLOR_MAKE(0xef, 0xdf, 0xc6), LV_COLOR_BLACK},
+ {LV_COLOR_MAKE(0xef, 0x92, 0x63), LV_COLOR_WHITE},
+ {LV_COLOR_MAKE(0xf7, 0x61, 0x42), LV_COLOR_WHITE},
+ {LV_COLOR_MAKE(0x00, 0x7d, 0xc5), LV_COLOR_WHITE},
+ };
- lv_style_set_border_color(&style_cell2, LV_STATE_DEFAULT, lv_color_hex(0xbbada0));
- lv_style_set_border_width(&style_cell2, LV_STATE_DEFAULT, 3);
- lv_style_set_bg_opa(&style_cell2, LV_STATE_DEFAULT, LV_OPA_COVER);
- lv_style_set_bg_color(&style_cell2, LV_STATE_DEFAULT, lv_color_hex(0xefdfc6));
- lv_style_set_pad_top(&style_cell2, LV_STATE_DEFAULT, 29);
- lv_style_set_text_color(&style_cell2, LV_STATE_DEFAULT, LV_COLOR_BLACK);
-
- lv_style_set_border_color(&style_cell3, LV_STATE_DEFAULT, lv_color_hex(0xbbada0));
- lv_style_set_border_width(&style_cell3, LV_STATE_DEFAULT, 3);
- lv_style_set_bg_opa(&style_cell3, LV_STATE_DEFAULT, LV_OPA_COVER);
- lv_style_set_bg_color(&style_cell3, LV_STATE_DEFAULT, lv_color_hex(0xef9263));
- lv_style_set_pad_top(&style_cell3, LV_STATE_DEFAULT, 29);
+ gridDisplay = lv_table_create(lv_scr_act(), nullptr);
- lv_style_set_border_color(&style_cell4, LV_STATE_DEFAULT, lv_color_hex(0xbbada0));
- lv_style_set_border_width(&style_cell4, LV_STATE_DEFAULT, 3);
- lv_style_set_bg_opa(&style_cell4, LV_STATE_DEFAULT, LV_OPA_COVER);
- lv_style_set_bg_color(&style_cell4, LV_STATE_DEFAULT, lv_color_hex(0xf76142));
- lv_style_set_pad_top(&style_cell4, LV_STATE_DEFAULT, 29);
+ for (size_t i = 0; i < nColors; i++) {
+ lv_style_init(&cellStyles[i]);
- lv_style_set_border_color(&style_cell5, LV_STATE_DEFAULT, lv_color_hex(0xbbada0));
- lv_style_set_border_width(&style_cell5, LV_STATE_DEFAULT, 3);
- lv_style_set_bg_opa(&style_cell5, LV_STATE_DEFAULT, LV_OPA_COVER);
- lv_style_set_bg_color(&style_cell5, LV_STATE_DEFAULT, lv_color_hex(0x007dc5));
- lv_style_set_pad_top(&style_cell5, LV_STATE_DEFAULT, 29);
+ lv_style_set_border_color(&cellStyles[i], LV_STATE_DEFAULT, lv_color_hex(0xbbada0));
+ lv_style_set_border_width(&cellStyles[i], LV_STATE_DEFAULT, 3);
+ lv_style_set_bg_opa(&cellStyles[i], LV_STATE_DEFAULT, LV_OPA_COVER);
+ lv_style_set_bg_color(&cellStyles[i], LV_STATE_DEFAULT, colors[i].bg);
+ lv_style_set_pad_top(&cellStyles[i], LV_STATE_DEFAULT, 29);
+ lv_style_set_text_color(&cellStyles[i], LV_STATE_DEFAULT, colors[i].fg);
- // format grid display
+ lv_obj_add_style(gridDisplay, LV_TABLE_PART_CELL1 + i, &cellStyles[i]);
+ }
- gridDisplay = lv_table_create(lv_scr_act(), nullptr);
- lv_obj_add_style(gridDisplay, LV_TABLE_PART_CELL1, &style_cell1);
- lv_obj_add_style(gridDisplay, LV_TABLE_PART_CELL2, &style_cell2);
- lv_obj_add_style(gridDisplay, LV_TABLE_PART_CELL3, &style_cell3);
- lv_obj_add_style(gridDisplay, LV_TABLE_PART_CELL4, &style_cell4);
- lv_obj_add_style(gridDisplay, LV_TABLE_PART_CELL4 + 1, &style_cell5);
lv_table_set_col_cnt(gridDisplay, nCols);
lv_table_set_row_cnt(gridDisplay, nRows);
for (int col = 0; col < nCols; col++) {
@@ -83,11 +64,9 @@ Twos::Twos(Pinetime::Applications::DisplayApp* app) : Screen(app) {
}
Twos::~Twos() {
- lv_style_reset(&style_cell1);
- lv_style_reset(&style_cell2);
- lv_style_reset(&style_cell3);
- lv_style_reset(&style_cell4);
- lv_style_reset(&style_cell5);
+ for (lv_style_t cellStyle : cellStyles) {
+ lv_style_reset(&cellStyle);
+ }
lv_obj_clean(lv_scr_act());
}
diff --git a/src/displayapp/screens/Twos.h b/src/displayapp/screens/Twos.h
index 4a6ada0b..da935724 100644
--- a/src/displayapp/screens/Twos.h
+++ b/src/displayapp/screens/Twos.h
@@ -18,11 +18,8 @@ namespace Pinetime {
bool OnTouchEvent(TouchEvents event) override;
private:
- lv_style_t style_cell1;
- lv_style_t style_cell2;
- lv_style_t style_cell3;
- lv_style_t style_cell4;
- lv_style_t style_cell5;
+ static constexpr int nColors = 5;
+ lv_style_t cellStyles[nColors];
lv_obj_t* scoreText;
lv_obj_t* gridDisplay;
diff --git a/src/displayapp/screens/WatchFaceAnalog.cpp b/src/displayapp/screens/WatchFaceAnalog.cpp
index 5ebb3304..5e5317ee 100644
--- a/src/displayapp/screens/WatchFaceAnalog.cpp
+++ b/src/displayapp/screens/WatchFaceAnalog.cpp
@@ -70,7 +70,6 @@ WatchFaceAnalog::WatchFaceAnalog(Pinetime::Applications::DisplayApp* app,
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_RED);
lv_obj_align(plugIcon, nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 0);
notificationIcon = lv_label_create(lv_scr_act(), NULL);
diff --git a/src/displayapp/screens/WatchFaceCasioStyleG7710.cpp b/src/displayapp/screens/WatchFaceCasioStyleG7710.cpp
new file mode 100644
index 00000000..f9a01abd
--- /dev/null
+++ b/src/displayapp/screens/WatchFaceCasioStyleG7710.cpp
@@ -0,0 +1,335 @@
+#include "displayapp/screens/WatchFaceCasioStyleG7710.h"
+
+#include <date/date.h>
+#include <lvgl/lvgl.h>
+#include <cstdio>
+#include "displayapp/screens/BatteryIcon.h"
+#include "displayapp/screens/BleIcon.h"
+#include "displayapp/screens/NotificationIcon.h"
+#include "displayapp/screens/Symbols.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/settings/Settings.h"
+using namespace Pinetime::Applications::Screens;
+
+WatchFaceCasioStyleG7710::WatchFaceCasioStyleG7710(DisplayApp* app,
+ Controllers::DateTime& dateTimeController,
+ Controllers::Battery& batteryController,
+ Controllers::Ble& bleController,
+ Controllers::NotificationManager& notificatioManager,
+ Controllers::Settings& settingsController,
+ Controllers::HeartRateController& heartRateController,
+ Controllers::MotionController& motionController,
+ Controllers::FS& filesystem)
+ : Screen(app),
+ currentDateTime {{}},
+ dateTimeController {dateTimeController},
+ batteryController {batteryController},
+ bleController {bleController},
+ notificatioManager {notificatioManager},
+ settingsController {settingsController},
+ heartRateController {heartRateController},
+ motionController {motionController} {
+
+ lfs_file f = {};
+ if (filesystem.FileOpen(&f, "/fonts/lv_font_dots_40.bin", LFS_O_RDONLY) >= 0) {
+ filesystem.FileClose(&f);
+ font_dot40 = lv_font_load("F:/fonts/lv_font_dots_40.bin");
+ }
+
+ if (filesystem.FileOpen(&f, "/fonts/7segments_40.bin", LFS_O_RDONLY) >= 0) {
+ filesystem.FileClose(&f);
+ font_segment40 = lv_font_load("F:/fonts/7segments_40.bin");
+ }
+
+ if (filesystem.FileOpen(&f, "/fonts/7segments_115.bin", LFS_O_RDONLY) >= 0) {
+ filesystem.FileClose(&f);
+ 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%");
+
+ batteryIcon.Create(lv_scr_act());
+ batteryIcon.SetColor(color_text);
+ lv_obj_align(batteryIcon.GetObject(), label_battery_vallue, 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);
+ lv_label_set_text_static(batteryPlug, Symbols::plug);
+ lv_obj_align(batteryPlug, batteryIcon.GetObject(), LV_ALIGN_OUT_LEFT_MID, -5, 0);
+
+ bleIcon = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(bleIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
+ lv_label_set_text_static(bleIcon, Symbols::bluetooth);
+ lv_obj_align(bleIcon, batteryPlug, LV_ALIGN_OUT_LEFT_MID, -5, 0);
+
+ notificationIcon = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
+ lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(false));
+ lv_obj_align(notificationIcon, bleIcon, LV_ALIGN_OUT_LEFT_MID, -5, 0);
+
+ label_day_of_week = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_align(label_day_of_week, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 10, 64);
+ lv_obj_set_style_local_text_color(label_day_of_week, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
+ lv_obj_set_style_local_text_font(label_day_of_week, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_dot40);
+ lv_label_set_text_static(label_day_of_week, "SUN");
+
+ label_week_number = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_align(label_week_number, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 5, 22);
+ lv_obj_set_style_local_text_color(label_week_number, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
+ lv_obj_set_style_local_text_font(label_week_number, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_dot40);
+ lv_label_set_text_static(label_week_number, "WK26");
+
+ label_day_of_year = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_align(label_day_of_year, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 100, 30);
+ lv_obj_set_style_local_text_color(label_day_of_year, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
+ lv_obj_set_style_local_text_font(label_day_of_year, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_segment40);
+ lv_label_set_text_static(label_day_of_year, "181-184");
+
+ lv_style_init(&style_line);
+ lv_style_set_line_width(&style_line, LV_STATE_DEFAULT, 2);
+ lv_style_set_line_color(&style_line, LV_STATE_DEFAULT, color_text);
+ lv_style_set_line_rounded(&style_line, LV_STATE_DEFAULT, true);
+
+ lv_style_init(&style_border);
+ lv_style_set_line_width(&style_border, LV_STATE_DEFAULT, 6);
+ lv_style_set_line_color(&style_border, LV_STATE_DEFAULT, color_text);
+ lv_style_set_line_rounded(&style_border, LV_STATE_DEFAULT, true);
+
+ line_icons = lv_line_create(lv_scr_act(), nullptr);
+ lv_line_set_points(line_icons, line_icons_points, 3);
+ lv_obj_add_style(line_icons, LV_LINE_PART_MAIN, &style_line);
+ lv_obj_align(line_icons, nullptr, LV_ALIGN_IN_TOP_RIGHT, -10, 18);
+
+ line_day_of_week_number = lv_line_create(lv_scr_act(), nullptr);
+ lv_line_set_points(line_day_of_week_number, line_day_of_week_number_points, 4);
+ lv_obj_add_style(line_day_of_week_number, LV_LINE_PART_MAIN, &style_border);
+ lv_obj_align(line_day_of_week_number, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 8);
+
+ line_day_of_year = lv_line_create(lv_scr_act(), nullptr);
+ lv_line_set_points(line_day_of_year, line_day_of_year_points, 3);
+ lv_obj_add_style(line_day_of_year, LV_LINE_PART_MAIN, &style_line);
+ lv_obj_align(line_day_of_year, nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 60);
+
+ label_date = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 100, 70);
+ lv_obj_set_style_local_text_color(label_date, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
+ lv_obj_set_style_local_text_font(label_date, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_segment40);
+ lv_label_set_text_static(label_date, "6-30");
+
+ line_date = lv_line_create(lv_scr_act(), nullptr);
+ lv_line_set_points(line_date, line_date_points, 3);
+ lv_obj_add_style(line_date, LV_LINE_PART_MAIN, &style_line);
+ lv_obj_align(line_date, nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 100);
+
+ label_time = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(label_time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
+ lv_obj_set_style_local_text_font(label_time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_segment115);
+ lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_CENTER, 0, 40);
+
+ line_time = lv_line_create(lv_scr_act(), nullptr);
+ lv_line_set_points(line_time, line_time_points, 3);
+ lv_obj_add_style(line_time, LV_LINE_PART_MAIN, &style_line);
+ lv_obj_align(line_time, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, -25);
+
+ label_time_ampm = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(label_time_ampm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
+ lv_label_set_text_static(label_time_ampm, "");
+ lv_obj_align(label_time_ampm, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 5, -5);
+
+ backgroundLabel = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_click(backgroundLabel, true);
+ lv_label_set_long_mode(backgroundLabel, LV_LABEL_LONG_CROP);
+ lv_obj_set_size(backgroundLabel, 240, 240);
+ lv_obj_set_pos(backgroundLabel, 0, 0);
+ lv_label_set_text_static(backgroundLabel, "");
+
+ heartbeatIcon = lv_label_create(lv_scr_act(), nullptr);
+ lv_label_set_text_static(heartbeatIcon, Symbols::heartBeat);
+ lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
+ lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2);
+
+ heartbeatValue = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(heartbeatValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
+ lv_label_set_text_static(heartbeatValue, "");
+ lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
+
+ stepValue = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
+ lv_label_set_text_static(stepValue, "0");
+ lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2);
+
+ stepIcon = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
+ lv_label_set_text_static(stepIcon, Symbols::shoe);
+ lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0);
+
+ taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
+ Refresh();
+}
+
+WatchFaceCasioStyleG7710::~WatchFaceCasioStyleG7710() {
+ lv_task_del(taskRefresh);
+
+ lv_style_reset(&style_line);
+ lv_style_reset(&style_border);
+
+ if (font_dot40 != nullptr) {
+ lv_font_free(font_dot40);
+ }
+
+ if (font_segment40 != nullptr) {
+ lv_font_free(font_segment40);
+ }
+
+ if (font_segment115 != nullptr) {
+ lv_font_free(font_segment115);
+ }
+
+ lv_obj_clean(lv_scr_act());
+}
+
+void WatchFaceCasioStyleG7710::Refresh() {
+ powerPresent = batteryController.IsPowerPresent();
+ if (powerPresent.IsUpdated()) {
+ lv_label_set_text_static(batteryPlug, BatteryIcon::GetPlugIcon(powerPresent.Get()));
+ }
+
+ batteryPercentRemaining = batteryController.PercentRemaining();
+ if (batteryPercentRemaining.IsUpdated()) {
+ auto batteryPercent = batteryPercentRemaining.Get();
+ batteryIcon.SetBatteryPercentage(batteryPercent);
+ lv_label_set_text_fmt(label_battery_vallue, "%d%%", batteryPercent);
+ }
+
+ bleState = bleController.IsConnected();
+ bleRadioEnabled = bleController.IsRadioEnabled();
+ if (bleState.IsUpdated() || bleRadioEnabled.IsUpdated()) {
+ lv_label_set_text_static(bleIcon, BleIcon::GetIcon(bleState.Get()));
+ }
+ lv_obj_realign(label_battery_vallue);
+ lv_obj_realign(batteryIcon.GetObject());
+ lv_obj_realign(batteryPlug);
+ lv_obj_realign(bleIcon);
+ lv_obj_realign(notificationIcon);
+
+ notificationState = notificatioManager.AreNewNotificationsAvailable();
+ if (notificationState.IsUpdated()) {
+ lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
+ }
+
+ currentDateTime = dateTimeController.CurrentDateTime();
+
+ if (currentDateTime.IsUpdated()) {
+ auto newDateTime = currentDateTime.Get();
+
+ auto dp = date::floor<date::days>(newDateTime);
+ auto time = date::make_time(newDateTime - dp);
+ auto yearMonthDay = date::year_month_day(dp);
+
+ auto year = static_cast<int>(yearMonthDay.year());
+ auto month = static_cast<Pinetime::Controllers::DateTime::Months>(static_cast<unsigned>(yearMonthDay.month()));
+ auto day = static_cast<unsigned>(yearMonthDay.day());
+ auto dayOfWeek = static_cast<Pinetime::Controllers::DateTime::Days>(date::weekday(yearMonthDay).iso_encoding());
+
+ uint8_t hour = time.hours().count();
+ uint8_t minute = time.minutes().count();
+ auto weekNumberFormat = "%V";
+
+ 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 ((year != currentYear) || (month != currentMonth) || (dayOfWeek != currentDayOfWeek) || (day != currentDay)) {
+ if (settingsController.GetClockType() == Controllers::Settings::ClockType::H24) {
+ // 24h mode: ddmmyyyy, first DOW=Monday;
+ lv_label_set_text_fmt(label_date, "%3d-%2d", day, month);
+ weekNumberFormat = "%V"; // Replaced by the week number of the year (Monday as the first day of the week) as a decimal number
+ // [01,53]. If the week containing 1 January has four or more days in the new year, then it is considered
+ // week 1. Otherwise, it is the last week of the previous year, and the next week is week 1. Both January
+ // 4th and the first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday]
+ } else {
+ // 12h mode: mmddyyyy, first DOW=Sunday;
+ lv_label_set_text_fmt(label_date, "%3d-%2d", month, day);
+ weekNumberFormat = "%U"; // Replaced by the week number of the year as a decimal number [00,53]. The first Sunday of January is the
+ // first day of week 1; days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday]
+ }
+
+ uint8_t weekNumber;
+ uint16_t dayOfYearNumber, daysTillEndOfYearNumber;
+
+ time_t ttTime =
+ std::chrono::system_clock::to_time_t(std::chrono::time_point_cast<std::chrono::system_clock::duration>(currentDateTime.Get()));
+ tm* tmTime = std::localtime(&ttTime);
+
+ dayOfYearNumber = tmTime->tm_yday + 1; // tm_yday day of year [0,365] => yday+1
+ daysTillEndOfYearNumber = (yearMonthDay.year().is_leap() ? 366 : 365) - dayOfYearNumber;
+
+ char buffer[8];
+ strftime(buffer, 8, weekNumberFormat, tmTime);
+ weekNumber = atoi(buffer);
+
+ lv_label_set_text_fmt(label_day_of_week, "%s", dateTimeController.DayOfWeekShortToString());
+ lv_label_set_text_fmt(label_day_of_year, "%3d-%3d", dayOfYearNumber, daysTillEndOfYearNumber);
+ lv_label_set_text_fmt(label_week_number, "WK%02d", weekNumber);
+
+ lv_obj_realign(label_day_of_week);
+ 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;
+ }
+ }
+
+ heartbeat = heartRateController.HeartRate();
+ heartbeatRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped;
+ if (heartbeat.IsUpdated() || heartbeatRunning.IsUpdated()) {
+ if (heartbeatRunning.Get()) {
+ lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
+ lv_label_set_text_fmt(heartbeatValue, "%d", heartbeat.Get());
+ } else {
+ lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x1B1B1B));
+ lv_label_set_text_static(heartbeatValue, "");
+ }
+
+ lv_obj_realign(heartbeatIcon);
+ lv_obj_realign(heartbeatValue);
+ }
+
+ stepCount = motionController.NbSteps();
+ motionSensorOk = motionController.IsSensorOk();
+ if (stepCount.IsUpdated() || motionSensorOk.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
new file mode 100644
index 00000000..c3821205
--- /dev/null
+++ b/src/displayapp/screens/WatchFaceCasioStyleG7710.h
@@ -0,0 +1,109 @@
+#pragma once
+
+#include <displayapp/screens/BatteryIcon.h>
+#include <lvgl/src/lv_core/lv_obj.h>
+#include <chrono>
+#include <cstdint>
+#include <memory>
+#include "displayapp/screens/Screen.h"
+#include "components/datetime/DateTimeController.h"
+#include "components/ble/BleController.h"
+
+namespace Pinetime {
+ namespace Controllers {
+ class Settings;
+ class Battery;
+ class Ble;
+ class NotificationManager;
+ class HeartRateController;
+ class MotionController;
+ }
+
+ namespace Applications {
+ namespace Screens {
+
+ class WatchFaceCasioStyleG7710 : public Screen {
+ public:
+ WatchFaceCasioStyleG7710(DisplayApp* app,
+ Controllers::DateTime& dateTimeController,
+ Controllers::Battery& batteryController,
+ Controllers::Ble& bleController,
+ Controllers::NotificationManager& notificatioManager,
+ Controllers::Settings& settingsController,
+ Controllers::HeartRateController& heartRateController,
+ Controllers::MotionController& motionController,
+ Controllers::FS& filesystem);
+ ~WatchFaceCasioStyleG7710() override;
+
+ void Refresh() override;
+
+ 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 {};
+
+ 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}};
+ lv_point_t line_day_of_year_points[3] {{0, 5}, {130, 5}, {135, 0}};
+ lv_point_t line_date_points[3] {{0, 5}, {135, 5}, {140, 0}};
+ lv_point_t line_time_points[3] {{0, 0}, {230, 0}, {235, 5}};
+
+ lv_color_t color_text = lv_color_hex(0x98B69A);
+
+ lv_style_t style_line;
+ lv_style_t style_border;
+
+ lv_obj_t* label_time;
+ lv_obj_t* line_time;
+ lv_obj_t* label_time_ampm;
+ lv_obj_t* label_date;
+ lv_obj_t* line_date;
+ lv_obj_t* label_day_of_week;
+ lv_obj_t* label_week_number;
+ lv_obj_t* line_day_of_week_number;
+ lv_obj_t* label_day_of_year;
+ lv_obj_t* line_day_of_year;
+ lv_obj_t* backgroundLabel;
+ lv_obj_t* bleIcon;
+ lv_obj_t* batteryPlug;
+ lv_obj_t* label_battery_vallue;
+ lv_obj_t* heartbeatIcon;
+ lv_obj_t* heartbeatValue;
+ lv_obj_t* stepIcon;
+ lv_obj_t* stepValue;
+ lv_obj_t* notificationIcon;
+ lv_obj_t* line_icons;
+
+ BatteryIcon batteryIcon;
+
+ Controllers::DateTime& dateTimeController;
+ Controllers::Battery& batteryController;
+ Controllers::Ble& bleController;
+ Controllers::NotificationManager& notificatioManager;
+ Controllers::Settings& settingsController;
+ Controllers::HeartRateController& heartRateController;
+ Controllers::MotionController& motionController;
+
+ lv_task_t* taskRefresh;
+ lv_font_t* font_dot40 = nullptr;
+ lv_font_t* font_segment40 = nullptr;
+ lv_font_t* font_segment115 = nullptr;
+ };
+ }
+ }
+}
diff --git a/src/displayapp/screens/WatchFaceDigital.cpp b/src/displayapp/screens/WatchFaceDigital.cpp
index 705272f7..47f40dab 100644
--- a/src/displayapp/screens/WatchFaceDigital.cpp
+++ b/src/displayapp/screens/WatchFaceDigital.cpp
@@ -18,14 +18,14 @@ WatchFaceDigital::WatchFaceDigital(DisplayApp* app,
Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController,
Controllers::Ble& bleController,
- Controllers::NotificationManager& notificatioManager,
+ Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController,
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController)
: Screen(app),
currentDateTime {{}},
dateTimeController {dateTimeController},
- notificatioManager {notificatioManager},
+ notificationManager {notificationManager},
settingsController {settingsController},
heartRateController {heartRateController},
motionController {motionController},
@@ -83,7 +83,7 @@ WatchFaceDigital::~WatchFaceDigital() {
void WatchFaceDigital::Refresh() {
statusIcons.Update();
- notificationState = notificatioManager.AreNewNotificationsAvailable();
+ notificationState = notificationManager.AreNewNotificationsAvailable();
if (notificationState.IsUpdated()) {
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
}
diff --git a/src/displayapp/screens/WatchFaceDigital.h b/src/displayapp/screens/WatchFaceDigital.h
index 49935792..60446afa 100644
--- a/src/displayapp/screens/WatchFaceDigital.h
+++ b/src/displayapp/screens/WatchFaceDigital.h
@@ -28,7 +28,7 @@ namespace Pinetime {
Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController,
Controllers::Ble& bleController,
- Controllers::NotificationManager& notificatioManager,
+ Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController,
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController);
@@ -66,7 +66,7 @@ namespace Pinetime {
lv_obj_t* notificationIcon;
Controllers::DateTime& dateTimeController;
- Controllers::NotificationManager& notificatioManager;
+ Controllers::NotificationManager& notificationManager;
Controllers::Settings& settingsController;
Controllers::HeartRateController& heartRateController;
Controllers::MotionController& motionController;
diff --git a/src/displayapp/screens/WatchFaceInfineat.cpp b/src/displayapp/screens/WatchFaceInfineat.cpp
new file mode 100644
index 00000000..ae3f3dbb
--- /dev/null
+++ b/src/displayapp/screens/WatchFaceInfineat.cpp
@@ -0,0 +1,607 @@
+#include "displayapp/screens/WatchFaceInfineat.h"
+
+#include <date/date.h>
+#include <lvgl/lvgl.h>
+#include <cstdio>
+#include "displayapp/screens/Symbols.h"
+#include "displayapp/screens/BleIcon.h"
+#include "components/settings/Settings.h"
+#include "components/battery/BatteryController.h"
+#include "components/ble/BleController.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<WatchFaceInfineat*>(obj->user_data);
+ screen->UpdateSelected(obj, event);
+ }
+}
+
+WatchFaceInfineat::WatchFaceInfineat(DisplayApp* app,
+ Controllers::DateTime& dateTimeController,
+ Controllers::Battery& batteryController,
+ Controllers::Ble& bleController,
+ Controllers::NotificationManager& notificationManager,
+ Controllers::Settings& settingsController,
+ Controllers::MotionController& motionController,
+ Controllers::FS& filesystem)
+ : Screen(app),
+ currentDateTime {{}},
+ dateTimeController {dateTimeController},
+ batteryController {batteryController},
+ bleController {bleController},
+ 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");
+ }
+
+ // Black background covering the whole screen
+ background = lv_obj_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_bg_color(background, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
+ lv_obj_set_size(background, 240, 240);
+ lv_obj_align(background, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 0, 0);
+
+ // Side Cover
+ line0 = lv_line_create(lv_scr_act(), nullptr);
+ line1 = lv_line_create(lv_scr_act(), nullptr);
+ line2 = lv_line_create(lv_scr_act(), nullptr);
+ line3 = lv_line_create(lv_scr_act(), nullptr);
+ line4 = lv_line_create(lv_scr_act(), nullptr);
+ line5 = lv_line_create(lv_scr_act(), nullptr);
+ line6 = lv_line_create(lv_scr_act(), nullptr);
+ line7 = lv_line_create(lv_scr_act(), nullptr);
+ line8 = lv_line_create(lv_scr_act(), nullptr);
+ lineBattery = lv_line_create(lv_scr_act(), nullptr);
+
+ lv_style_init(&line0Style);
+ lv_style_set_line_width(&line0Style, LV_STATE_DEFAULT, 18);
+ lv_style_set_line_color(&line0Style,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[settingsController.GetInfineatColorIndex() * nLines]));
+ lv_obj_add_style(line0, LV_LINE_PART_MAIN, &line0Style);
+ line0Points[0] = {30, 25};
+ line0Points[1] = {68, -8};
+ lv_line_set_points(line0, line0Points, 2);
+
+ lv_style_init(&line1Style);
+ lv_style_set_line_width(&line1Style, LV_STATE_DEFAULT, 15);
+ lv_style_set_line_color(&line1Style,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[settingsController.GetInfineatColorIndex() * nLines + 1]));
+ lv_obj_add_style(line1, LV_LINE_PART_MAIN, &line1Style);
+ line1Points[0] = {26, 167};
+ line1Points[1] = {43, 216};
+ lv_line_set_points(line1, line1Points, 2);
+
+ lv_style_init(&line2Style);
+ lv_style_set_line_width(&line2Style, LV_STATE_DEFAULT, 14);
+ lv_style_set_line_color(&line2Style,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[settingsController.GetInfineatColorIndex() * nLines + 2]));
+ lv_obj_add_style(line2, LV_LINE_PART_MAIN, &line2Style);
+ line2Points[0] = {27, 40};
+ line2Points[1] = {27, 196};
+ lv_line_set_points(line2, line2Points, 2);
+
+ lv_style_init(&line3Style);
+ lv_style_set_line_width(&line3Style, LV_STATE_DEFAULT, 22);
+ lv_style_set_line_color(&line3Style,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[settingsController.GetInfineatColorIndex() * nLines + 3]));
+ lv_obj_add_style(line3, LV_LINE_PART_MAIN, &line3Style);
+ line3Points[0] = {12, 182};
+ line3Points[1] = {65, 249};
+ lv_line_set_points(line3, line3Points, 2);
+
+ lv_style_init(&line4Style);
+ lv_style_set_line_width(&line4Style, LV_STATE_DEFAULT, 20);
+ lv_style_set_line_color(&line4Style,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[settingsController.GetInfineatColorIndex() * nLines + 4]));
+ lv_obj_add_style(line4, LV_LINE_PART_MAIN, &line4Style);
+ line4Points[0] = {17, 99};
+ line4Points[1] = {17, 144};
+ lv_line_set_points(line4, line4Points, 2);
+
+ lv_style_init(&line5Style);
+ lv_style_set_line_width(&line5Style, LV_STATE_DEFAULT, 18);
+ lv_style_set_line_color(&line5Style,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[settingsController.GetInfineatColorIndex() * nLines + 5]));
+ lv_obj_add_style(line5, LV_LINE_PART_MAIN, &line5Style);
+ line5Points[0] = {14, 81};
+ line5Points[1] = {40, 127};
+ lv_line_set_points(line5, line5Points, 2);
+
+ lv_style_init(&line6Style);
+ lv_style_set_line_width(&line6Style, LV_STATE_DEFAULT, 18);
+ lv_style_set_line_color(&line6Style,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[settingsController.GetInfineatColorIndex() * nLines + 6]));
+ lv_obj_add_style(line6, LV_LINE_PART_MAIN, &line6Style);
+ line6Points[0] = {14, 163};
+ line6Points[1] = {40, 118};
+ lv_line_set_points(line6, line6Points, 2);
+
+ lv_style_init(&line7Style);
+ lv_style_set_line_width(&line7Style, LV_STATE_DEFAULT, 52);
+ lv_style_set_line_color(&line7Style,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[settingsController.GetInfineatColorIndex() * nLines + 7]));
+ lv_obj_add_style(line7, LV_LINE_PART_MAIN, &line7Style);
+ line7Points[0] = {-20, 124};
+ line7Points[1] = {25, -11};
+ lv_line_set_points(line7, line7Points, 2);
+
+ lv_style_init(&line8Style);
+ lv_style_set_line_width(&line8Style, LV_STATE_DEFAULT, 48);
+ lv_style_set_line_color(&line8Style,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[settingsController.GetInfineatColorIndex() * nLines + 8]));
+ lv_obj_add_style(line8, LV_LINE_PART_MAIN, &line8Style);
+ line8Points[0] = {-29, 89};
+ line8Points[1] = {27, 254};
+ lv_line_set_points(line8, line8Points, 2);
+
+ logoPine = lv_img_create(lv_scr_act(), nullptr);
+ lv_img_set_src(logoPine, "F:/images/pine_small.bin");
+ lv_obj_set_pos(logoPine, 15, 106);
+
+ lv_style_init(&lineBatteryStyle);
+ lv_style_set_line_width(&lineBatteryStyle, LV_STATE_DEFAULT, 24);
+ lv_style_set_line_color(&lineBatteryStyle,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[settingsController.GetInfineatColorIndex() * nLines + 4]));
+ lv_style_set_line_opa(&lineBatteryStyle, LV_STATE_DEFAULT, 190);
+ lv_obj_add_style(lineBattery, LV_LINE_PART_MAIN, &lineBatteryStyle);
+ lineBatteryPoints[0] = {27, 105};
+ lineBatteryPoints[1] = {27, 106};
+ 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,
+ lv_color_hex(infineatColors.orange[settingsController.GetInfineatColorIndex() * nLines + 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);
+ lv_obj_set_hidden(line0, true);
+ lv_obj_set_hidden(line1, true);
+ lv_obj_set_hidden(line2, true);
+ lv_obj_set_hidden(line3, true);
+ lv_obj_set_hidden(line4, true);
+ lv_obj_set_hidden(line5, true);
+ lv_obj_set_hidden(line6, true);
+ lv_obj_set_hidden(line7, true);
+ lv_obj_set_hidden(line8, 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);
+ if (font_bebas != nullptr) {
+ lv_obj_set_size(timeContainer, 185, 185);
+ lv_obj_align(timeContainer, lv_scr_act(), LV_ALIGN_CENTER, 0, -10);
+ } else {
+ lv_obj_set_size(timeContainer, 110, 145);
+ lv_obj_align(timeContainer, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
+ }
+
+ labelHour = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_font(labelHour, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_extrabold_compressed);
+ lv_label_set_text(labelHour, "01");
+ if (font_bebas != nullptr) {
+ lv_obj_set_style_local_text_font(labelHour, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_bebas);
+ lv_obj_align(labelHour, timeContainer, LV_ALIGN_IN_TOP_MID, 0, 0);
+ } else {
+ lv_obj_set_style_local_text_font(labelHour, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_extrabold_compressed);
+ lv_obj_align(labelHour, timeContainer, LV_ALIGN_IN_TOP_MID, 0, 5);
+ }
+
+ labelMinutes = lv_label_create(lv_scr_act(), nullptr);
+ if (font_bebas != nullptr) {
+ lv_obj_set_style_local_text_font(labelMinutes, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_bebas);
+ } else {
+ lv_obj_set_style_local_text_font(labelMinutes, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_extrabold_compressed);
+ }
+ lv_label_set_text(labelMinutes, "00");
+ lv_obj_align(labelMinutes, timeContainer, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
+
+ labelTimeAmPm = lv_label_create(lv_scr_act(), nullptr);
+ if (font_teko != nullptr) {
+ lv_obj_set_style_local_text_font(labelTimeAmPm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_teko);
+ } else {
+ lv_obj_set_style_local_text_font(labelTimeAmPm, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20);
+ }
+
+ lv_label_set_text(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, lv_color_hex(0x999999));
+ if (font_teko != nullptr) {
+ lv_obj_set_style_local_text_font(labelDate, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_teko);
+ } else {
+ lv_obj_set_style_local_text_font(labelDate, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20);
+ }
+ lv_obj_align(labelDate, dateContainer, LV_ALIGN_IN_TOP_MID, 0, 0);
+ lv_label_set_text(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, lv_color_hex(0x999999));
+ lv_label_set_text(bleIcon, Symbols::bluetooth);
+ lv_obj_align(bleIcon, dateContainer, LV_ALIGN_OUT_BOTTOM_MID, 0, 0);
+
+ stepValue = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999));
+ if (font_teko != nullptr) {
+ lv_obj_set_style_local_text_font(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_teko);
+ } else {
+ lv_obj_set_style_local_text_font(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20);
+ }
+ lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 10, 0);
+ lv_label_set_text(stepValue, "0");
+
+ stepIcon = lv_label_create(lv_scr_act(), nullptr);
+ lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999));
+ lv_label_set_text(stepIcon, Symbols::shoe);
+ lv_obj_align(stepIcon, 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_set_style_local_value_str(btnClose, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "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_set_style_local_value_str(btnNextColor, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, ">");
+ 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_set_style_local_value_str(btnPrevColor, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "<");
+ 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, 80);
+ 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";
+ lv_obj_set_style_local_value_str(btnToggleCover, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, labelToggle);
+ lv_obj_set_event_cb(btnToggleCover, event_handler);
+ lv_obj_set_hidden(btnToggleCover, 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();
+}
+
+WatchFaceInfineat::~WatchFaceInfineat() {
+ lv_task_del(taskRefresh);
+
+ lv_style_reset(&line0Style);
+ lv_style_reset(&line1Style);
+ lv_style_reset(&line2Style);
+ lv_style_reset(&line3Style);
+ lv_style_reset(&line4Style);
+ lv_style_reset(&line5Style);
+ lv_style_reset(&line6Style);
+ lv_style_reset(&line7Style);
+ lv_style_reset(&line8Style);
+ lv_style_reset(&lineBatteryStyle);
+
+ 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 WatchFaceInfineat::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 WatchFaceInfineat::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);
+}
+
+bool WatchFaceInfineat::OnButtonPushed() {
+ if (!lv_obj_get_hidden(btnClose)) {
+ CloseMenu();
+ return true;
+ }
+ return false;
+}
+
+void WatchFaceInfineat::UpdateSelected(lv_obj_t* object, lv_event_t event) {
+ if (event == LV_EVENT_CLICKED) {
+ bool showSideCover = settingsController.GetInfineatShowSideCover();
+ int colorIndex = settingsController.GetInfineatColorIndex();
+
+ 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);
+ }
+ if (object == btnClose) {
+ CloseMenu();
+ }
+ if (object == btnToggleCover) {
+ settingsController.SetInfineatShowSideCover(!showSideCover);
+ ToggleBatteryIndicatorColor(!showSideCover);
+ lv_obj_set_hidden(line0, showSideCover);
+ lv_obj_set_hidden(line1, showSideCover);
+ lv_obj_set_hidden(line2, showSideCover);
+ lv_obj_set_hidden(line3, showSideCover);
+ lv_obj_set_hidden(line4, showSideCover);
+ lv_obj_set_hidden(line5, showSideCover);
+ lv_obj_set_hidden(line6, showSideCover);
+ lv_obj_set_hidden(line7, showSideCover);
+ lv_obj_set_hidden(line8, showSideCover);
+ lv_obj_set_hidden(btnNextColor, showSideCover);
+ lv_obj_set_hidden(btnPrevColor, showSideCover);
+ const char* labelToggle = showSideCover ? "OFF" : "ON";
+ lv_obj_set_style_local_value_str(btnToggleCover, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, labelToggle);
+ }
+ 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) {
+ lv_obj_set_style_local_line_color(line0,
+ LV_LINE_PART_MAIN,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[colorIndex * nLines + 0]));
+ lv_obj_set_style_local_line_color(line1,
+ LV_LINE_PART_MAIN,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[colorIndex * nLines + 1]));
+ lv_obj_set_style_local_line_color(line2,
+ LV_LINE_PART_MAIN,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[colorIndex * nLines + 2]));
+ lv_obj_set_style_local_line_color(line3,
+ LV_LINE_PART_MAIN,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[colorIndex * nLines + 3]));
+ lv_obj_set_style_local_line_color(line4,
+ LV_LINE_PART_MAIN,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[colorIndex * nLines + 4]));
+ lv_obj_set_style_local_line_color(line5,
+ LV_LINE_PART_MAIN,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[colorIndex * nLines + 5]));
+ lv_obj_set_style_local_line_color(line6,
+ LV_LINE_PART_MAIN,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[colorIndex * nLines + 6]));
+ lv_obj_set_style_local_line_color(line7,
+ LV_LINE_PART_MAIN,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[colorIndex * nLines + 7]));
+ lv_obj_set_style_local_line_color(line8,
+ LV_LINE_PART_MAIN,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[colorIndex * nLines + 8]));
+ lv_obj_set_style_local_line_color(lineBattery,
+ LV_LINE_PART_MAIN,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[colorIndex * nLines + 4]));
+ lv_obj_set_style_local_bg_color(notificationIcon,
+ LV_BTN_PART_MAIN,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[colorIndex * nLines + 7]));
+ }
+ }
+}
+
+void WatchFaceInfineat::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 = dateTimeController.CurrentDateTime();
+
+ if (currentDateTime.IsUpdated()) {
+ auto newDateTime = currentDateTime.Get();
+
+ auto dp = date::floor<date::days>(newDateTime);
+ auto time = date::make_time(newDateTime - dp);
+ auto yearMonthDay = date::year_month_day(dp);
+
+ auto year = static_cast<int>(yearMonthDay.year());
+ auto month = static_cast<Pinetime::Controllers::DateTime::Months>(static_cast<unsigned>(yearMonthDay.month()));
+ auto day = static_cast<unsigned>(yearMonthDay.day());
+ auto dayOfWeek = static_cast<Pinetime::Controllers::DateTime::Days>(date::weekday(yearMonthDay).iso_encoding());
+
+ int hour = time.hours().count();
+ auto minute = time.minutes().count();
+
+ char minutesChar[3];
+ sprintf(minutesChar, "%02d", static_cast<int>(minute));
+
+ char hoursChar[3];
+ char ampmChar[3];
+
+ 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");
+ }
+ }
+ 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);
+ }
+
+ 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);
+ lv_obj_realign(labelDate);
+
+ currentYear = year;
+ currentMonth = month;
+ currentDayOfWeek = dayOfWeek;
+ currentDay = day;
+ }
+ }
+
+ batteryPercentRemaining = batteryController.PercentRemaining();
+ isCharging = batteryController.IsCharging();
+ // We store if battery and charging are updated before calling Get(),
+ // since Get() sets isUpdated to false.
+ bool isBatteryUpdated = batteryPercentRemaining.IsUpdated();
+ bool isChargingUpdated = isCharging.IsUpdated();
+ if (isCharging.Get()) { // Charging battery animation
+ chargingBatteryPercent += 1;
+ if (chargingBatteryPercent > 100) {
+ chargingBatteryPercent = batteryPercentRemaining.Get();
+ }
+ SetBatteryLevel(chargingBatteryPercent);
+ } else if (isChargingUpdated || isBatteryUpdated) {
+ chargingBatteryPercent = batteryPercentRemaining.Get();
+ SetBatteryLevel(chargingBatteryPercent);
+ }
+
+ bleState = bleController.IsConnected();
+ bleRadioEnabled = bleController.IsRadioEnabled();
+ if (bleState.IsUpdated()) {
+ lv_label_set_text(bleIcon, BleIcon::GetIcon(bleState.Get()));
+ lv_obj_align(bleIcon, dateContainer, LV_ALIGN_OUT_BOTTOM_MID, 0, 3);
+ }
+
+ stepCount = motionController.NbSteps();
+ motionSensorOk = motionController.IsSensorOk();
+ if (stepCount.IsUpdated() || motionSensorOk.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);
+ }
+
+ if (!lv_obj_get_hidden(btnSettings)) {
+ if ((savedTick > 0) && (lv_tick_get() - savedTick > 3000)) {
+ lv_obj_set_hidden(btnSettings, true);
+ savedTick = 0;
+ }
+ }
+}
+
+void WatchFaceInfineat::SetBatteryLevel(uint8_t batteryPercent) {
+ // starting point (y) + Pine64 logo height * (100 - batteryPercent) / 100
+ lineBatteryPoints[1] = {27, static_cast<lv_coord_t>(105 + 32 * (100 - batteryPercent) / 100)};
+ lv_line_set_points(lineBattery, lineBatteryPoints, 2);
+}
+
+void WatchFaceInfineat::ToggleBatteryIndicatorColor(bool showSideCover) {
+ if (!showSideCover) { // make indicator and notification icon color white
+ lv_obj_set_style_local_image_recolor_opa(logoPine, LV_IMG_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_100);
+ lv_obj_set_style_local_image_recolor(logoPine, 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(logoPine, LV_IMG_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0);
+ lv_obj_set_style_local_line_color(lineBattery,
+ LV_LINE_PART_MAIN,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[settingsController.GetInfineatColorIndex() * nLines + 4]));
+ lv_obj_set_style_local_bg_color(notificationIcon,
+ LV_BTN_PART_MAIN,
+ LV_STATE_DEFAULT,
+ lv_color_hex(infineatColors.orange[settingsController.GetInfineatColorIndex() * nLines + 7]));
+ }
+}
diff --git a/src/displayapp/screens/WatchFaceInfineat.h b/src/displayapp/screens/WatchFaceInfineat.h
new file mode 100644
index 00000000..4a7dbebd
--- /dev/null
+++ b/src/displayapp/screens/WatchFaceInfineat.h
@@ -0,0 +1,144 @@
+#pragma once
+
+#include <lvgl/lvgl.h>
+#include <chrono>
+#include <cstdint>
+#include <memory>
+#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 WatchFaceInfineat : public Screen {
+ public:
+ WatchFaceInfineat(DisplayApp* app,
+ Controllers::DateTime& dateTimeController,
+ Controllers::Battery& batteryController,
+ Controllers::Ble& bleController,
+ Controllers::NotificationManager& notificationManager,
+ Controllers::Settings& settingsController,
+ Controllers::MotionController& motionController,
+ Controllers::FS& fs);
+
+ ~WatchFaceInfineat() override;
+
+ bool OnTouchEvent(TouchEvents event) override;
+ bool OnButtonPushed() override;
+ void UpdateSelected(lv_obj_t* object, lv_event_t event);
+ void CloseMenu();
+
+ void Refresh() override;
+
+ 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 ;)
+
+ 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 {};
+
+ lv_obj_t* background;
+
+ // Lines making up the side cover
+ lv_obj_t* line0;
+ lv_obj_t* line1;
+ lv_obj_t* line2;
+ lv_obj_t* line3;
+ lv_obj_t* line4;
+ lv_obj_t* line5;
+ lv_obj_t* line6;
+ lv_obj_t* line7;
+ lv_obj_t* line8;
+ lv_obj_t* lineBattery;
+
+ lv_style_t line0Style;
+ lv_style_t line1Style;
+ lv_style_t line2Style;
+ lv_style_t line3Style;
+ lv_style_t line4Style;
+ lv_style_t line5Style;
+ lv_style_t line6Style;
+ lv_style_t line7Style;
+ lv_style_t line8Style;
+ lv_style_t lineBatteryStyle;
+
+ lv_point_t line0Points[2];
+ lv_point_t line1Points[2];
+ lv_point_t line2Points[2];
+ lv_point_t line3Points[2];
+ lv_point_t line4Points[2];
+ lv_point_t line5Points[2];
+ lv_point_t line6Points[2];
+ lv_point_t line7Points[2];
+ lv_point_t line8Points[2];
+ lv_point_t lineBatteryPoints[2];
+
+ lv_obj_t* logoPine;
+
+ 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* stepIcon;
+ lv_obj_t* stepValue;
+ lv_obj_t* notificationIcon;
+ lv_obj_t* btnClose;
+ lv_obj_t* btnNextColor;
+ lv_obj_t* btnToggleCover;
+ lv_obj_t* btnPrevColor;
+ lv_obj_t* btnSettings;
+ lv_obj_t* labelBtnSettings;
+
+ static constexpr int nLines = 9;
+ static constexpr int nColors = 7; // must match number of colors in InfineatColors
+ struct InfineatColors {
+ int orange[nLines] = {0xfd872b, 0xdb3316, 0x6f1000, 0xfd7a0a, 0xffffff, 0xffffff, 0xffffff, 0xe85102, 0xea1c00};
+ int blue[nLines] = {0xe7f8ff, 0x2232d0, 0x182a8b, 0xe7f8ff, 0xffffff, 0xffffff, 0xffffff, 0x5991ff, 0x1636ff};
+ int green[nLines] = {0xb8ff9b, 0x088608, 0x004a00, 0xb8ff9b, 0xffffff, 0xffffff, 0xffffff, 0x62d515, 0x007400};
+ int rainbow[nLines] = {0x2da400, 0xac09c4, 0xfe0303, 0x0d57ff, 0xffffff, 0xffffff, 0xffffff, 0xe0b900, 0xe85102};
+ int gray[nLines] = {0xeeeeee, 0x98959b, 0x191919, 0xeeeeee, 0xffffff, 0xffffff, 0xffffff, 0x919191, 0x3a3a3a};
+ int nordBlue[nLines] = {0xc3daf2, 0x4d78ce, 0x153451, 0xc3daf2, 0xffffff, 0xffffff, 0xffffff, 0x5d8ad2, 0x21518a};
+ int nordGreen[nLines] = {0xd5f0e9, 0x238373, 0x1d413f, 0xd5f0e9, 0xffffff, 0xffffff, 0xffffff, 0x2fb8a2, 0x11705a};
+ } infineatColors;
+
+ Controllers::DateTime& dateTimeController;
+ Controllers::Battery& batteryController;
+ Controllers::Ble& bleController;
+ Controllers::NotificationManager& notificationManager;
+ Controllers::Settings& settingsController;
+ Controllers::MotionController& motionController;
+
+ void SetBatteryLevel(uint8_t batteryPercent);
+ void ToggleBatteryIndicatorColor(bool showSideCover);
+
+ lv_task_t* taskRefresh;
+ lv_font_t* font_teko = nullptr;
+ lv_font_t* font_bebas = nullptr;
+ };
+ }
+ }
+}
diff --git a/src/displayapp/screens/WatchFacePineTimeStyle.cpp b/src/displayapp/screens/WatchFacePineTimeStyle.cpp
index 14030920..2acd3f7e 100644
--- a/src/displayapp/screens/WatchFacePineTimeStyle.cpp
+++ b/src/displayapp/screens/WatchFacePineTimeStyle.cpp
@@ -48,7 +48,7 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(DisplayApp* app,
Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController,
Controllers::Ble& bleController,
- Controllers::NotificationManager& notificatioManager,
+ Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController,
Controllers::MotionController& motionController)
: Screen(app),
@@ -56,7 +56,7 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(DisplayApp* app,
dateTimeController {dateTimeController},
batteryController {batteryController},
bleController {bleController},
- notificatioManager {notificatioManager},
+ notificationManager {notificationManager},
settingsController {settingsController},
motionController {motionController} {
@@ -237,7 +237,8 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(DisplayApp* app,
lv_obj_set_size(btnNextTime, 60, 60);
lv_obj_align(btnNextTime, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -15, -80);
lv_obj_set_style_local_bg_opa(btnNextTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50);
- lv_obj_set_style_local_value_str(btnNextTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, ">");
+ lv_obj_t* lblNextTime = lv_label_create(btnNextTime, nullptr);
+ lv_label_set_text_static(lblNextTime, ">");
lv_obj_set_event_cb(btnNextTime, event_handler);
lv_obj_set_hidden(btnNextTime, true);
@@ -246,7 +247,8 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(DisplayApp* app,
lv_obj_set_size(btnPrevTime, 60, 60);
lv_obj_align(btnPrevTime, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 15, -80);
lv_obj_set_style_local_bg_opa(btnPrevTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50);
- lv_obj_set_style_local_value_str(btnPrevTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "<");
+ lv_obj_t* lblPrevTime = lv_label_create(btnPrevTime, nullptr);
+ lv_label_set_text_static(lblPrevTime, "<");
lv_obj_set_event_cb(btnPrevTime, event_handler);
lv_obj_set_hidden(btnPrevTime, true);
@@ -255,7 +257,8 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(DisplayApp* app,
lv_obj_set_size(btnNextBar, 60, 60);
lv_obj_align(btnNextBar, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -15, 0);
lv_obj_set_style_local_bg_opa(btnNextBar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50);
- lv_obj_set_style_local_value_str(btnNextBar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, ">");
+ lv_obj_t* lblNextBar = lv_label_create(btnNextBar, nullptr);
+ lv_label_set_text_static(lblNextBar, ">");
lv_obj_set_event_cb(btnNextBar, event_handler);
lv_obj_set_hidden(btnNextBar, true);
@@ -264,7 +267,8 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(DisplayApp* app,
lv_obj_set_size(btnPrevBar, 60, 60);
lv_obj_align(btnPrevBar, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 15, 0);
lv_obj_set_style_local_bg_opa(btnPrevBar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50);
- lv_obj_set_style_local_value_str(btnPrevBar, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "<");
+ lv_obj_t* lblPrevBar = lv_label_create(btnPrevBar, nullptr);
+ lv_label_set_text_static(lblPrevBar, "<");
lv_obj_set_event_cb(btnPrevBar, event_handler);
lv_obj_set_hidden(btnPrevBar, true);
@@ -273,7 +277,8 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(DisplayApp* app,
lv_obj_set_size(btnNextBG, 60, 60);
lv_obj_align(btnNextBG, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -15, 80);
lv_obj_set_style_local_bg_opa(btnNextBG, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50);
- lv_obj_set_style_local_value_str(btnNextBG, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, ">");
+ lv_obj_t* lblNextBG = lv_label_create(btnNextBG, nullptr);
+ lv_label_set_text_static(lblNextBG, ">");
lv_obj_set_event_cb(btnNextBG, event_handler);
lv_obj_set_hidden(btnNextBG, true);
@@ -282,7 +287,8 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(DisplayApp* app,
lv_obj_set_size(btnPrevBG, 60, 60);
lv_obj_align(btnPrevBG, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 15, 80);
lv_obj_set_style_local_bg_opa(btnPrevBG, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50);
- lv_obj_set_style_local_value_str(btnPrevBG, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "<");
+ lv_obj_t* lblPrevBG = lv_label_create(btnPrevBG, nullptr);
+ lv_label_set_text_static(lblPrevBG, "<");
lv_obj_set_event_cb(btnPrevBG, event_handler);
lv_obj_set_hidden(btnPrevBG, true);
@@ -291,7 +297,8 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(DisplayApp* app,
lv_obj_set_size(btnReset, 60, 60);
lv_obj_align(btnReset, lv_scr_act(), LV_ALIGN_CENTER, 0, 80);
lv_obj_set_style_local_bg_opa(btnReset, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50);
- lv_obj_set_style_local_value_str(btnReset, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "Rst");
+ lv_obj_t* lblReset = lv_label_create(btnReset, nullptr);
+ lv_label_set_text_static(lblReset, "Rst");
lv_obj_set_event_cb(btnReset, event_handler);
lv_obj_set_hidden(btnReset, true);
@@ -300,7 +307,8 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(DisplayApp* app,
lv_obj_set_size(btnRandom, 60, 60);
lv_obj_align(btnRandom, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_local_bg_opa(btnRandom, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50);
- lv_obj_set_style_local_value_str(btnRandom, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "Rnd");
+ lv_obj_t* lblRandom = lv_label_create(btnRandom, nullptr);
+ lv_label_set_text_static(lblRandom, "Rnd");
lv_obj_set_event_cb(btnRandom, event_handler);
lv_obj_set_hidden(btnRandom, true);
@@ -309,7 +317,8 @@ WatchFacePineTimeStyle::WatchFacePineTimeStyle(DisplayApp* app,
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_50);
- lv_obj_set_style_local_value_str(btnClose, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "X");
+ 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);
@@ -432,7 +441,7 @@ void WatchFacePineTimeStyle::Refresh() {
AlignIcons();
}
- notificationState = notificatioManager.AreNewNotificationsAvailable();
+ notificationState = notificationManager.AreNewNotificationsAvailable();
if (notificationState.IsUpdated()) {
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
AlignIcons();
@@ -679,7 +688,7 @@ void WatchFacePineTimeStyle::UpdateSelected(lv_obj_t* object, lv_event_t event)
Pinetime::Controllers::Settings::Colors WatchFacePineTimeStyle::GetNext(Pinetime::Controllers::Settings::Colors color) {
auto colorAsInt = static_cast<uint8_t>(color);
Pinetime::Controllers::Settings::Colors nextColor;
- if (colorAsInt < 16) {
+ if (colorAsInt < 17) {
nextColor = static_cast<Controllers::Settings::Colors>(colorAsInt + 1);
} else {
nextColor = static_cast<Controllers::Settings::Colors>(0);
@@ -694,7 +703,7 @@ Pinetime::Controllers::Settings::Colors WatchFacePineTimeStyle::GetPrevious(Pine
if (colorAsInt > 0) {
prevColor = static_cast<Controllers::Settings::Colors>(colorAsInt - 1);
} else {
- prevColor = static_cast<Controllers::Settings::Colors>(16);
+ prevColor = static_cast<Controllers::Settings::Colors>(17);
}
return prevColor;
}
diff --git a/src/displayapp/screens/WatchFacePineTimeStyle.h b/src/displayapp/screens/WatchFacePineTimeStyle.h
index 0b6acd95..3085a1ae 100644
--- a/src/displayapp/screens/WatchFacePineTimeStyle.h
+++ b/src/displayapp/screens/WatchFacePineTimeStyle.h
@@ -28,7 +28,7 @@ namespace Pinetime {
Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController,
Controllers::Ble& bleController,
- Controllers::NotificationManager& notificatioManager,
+ Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController,
Controllers::MotionController& motionController);
~WatchFacePineTimeStyle() override;
@@ -105,7 +105,7 @@ namespace Pinetime {
Controllers::DateTime& dateTimeController;
Controllers::Battery& batteryController;
Controllers::Ble& bleController;
- Controllers::NotificationManager& notificatioManager;
+ Controllers::NotificationManager& notificationManager;
Controllers::Settings& settingsController;
Controllers::MotionController& motionController;
diff --git a/src/displayapp/screens/WatchFaceTerminal.cpp b/src/displayapp/screens/WatchFaceTerminal.cpp
index f5490b44..92189737 100644
--- a/src/displayapp/screens/WatchFaceTerminal.cpp
+++ b/src/displayapp/screens/WatchFaceTerminal.cpp
@@ -17,7 +17,7 @@ WatchFaceTerminal::WatchFaceTerminal(DisplayApp* app,
Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController,
Controllers::Ble& bleController,
- Controllers::NotificationManager& notificatioManager,
+ Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController,
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController)
@@ -26,7 +26,7 @@ WatchFaceTerminal::WatchFaceTerminal(DisplayApp* app,
dateTimeController {dateTimeController},
batteryController {batteryController},
bleController {bleController},
- notificatioManager {notificatioManager},
+ notificationManager {notificationManager},
settingsController {settingsController},
heartRateController {heartRateController},
motionController {motionController} {
@@ -100,7 +100,7 @@ void WatchFaceTerminal::Refresh() {
}
}
- notificationState = notificatioManager.AreNewNotificationsAvailable();
+ notificationState = notificationManager.AreNewNotificationsAvailable();
if (notificationState.IsUpdated()) {
if (notificationState.Get()) {
lv_label_set_text_static(notificationIcon, "You have mail.");
diff --git a/src/displayapp/screens/WatchFaceTerminal.h b/src/displayapp/screens/WatchFaceTerminal.h
index 828dbdb1..a81289a7 100644
--- a/src/displayapp/screens/WatchFaceTerminal.h
+++ b/src/displayapp/screens/WatchFaceTerminal.h
@@ -26,7 +26,7 @@ namespace Pinetime {
Controllers::DateTime& dateTimeController,
Controllers::Battery& batteryController,
Controllers::Ble& bleController,
- Controllers::NotificationManager& notificatioManager,
+ Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController,
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController);
@@ -68,7 +68,7 @@ namespace Pinetime {
Controllers::DateTime& dateTimeController;
Controllers::Battery& batteryController;
Controllers::Ble& bleController;
- Controllers::NotificationManager& notificatioManager;
+ Controllers::NotificationManager& notificationManager;
Controllers::Settings& settingsController;
Controllers::HeartRateController& heartRateController;
Controllers::MotionController& motionController;
diff --git a/src/displayapp/screens/settings/SettingSetDate.cpp b/src/displayapp/screens/settings/SettingSetDate.cpp
index 1407a98f..421aef02 100644
--- a/src/displayapp/screens/settings/SettingSetDate.cpp
+++ b/src/displayapp/screens/settings/SettingSetDate.cpp
@@ -79,9 +79,11 @@ SettingSetDate::SettingSetDate(Pinetime::Applications::DisplayApp* app, Pinetime
lv_obj_set_size(btnSetTime, 120, 48);
lv_obj_align(btnSetTime, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0);
lv_obj_set_style_local_bg_color(btnSetTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x38, 0x38, 0x38));
- lv_obj_set_style_local_value_str(btnSetTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "Set");
+ lblSetTime = lv_label_create(btnSetTime, nullptr);
+ lv_label_set_text_static(lblSetTime, "Set");
lv_obj_set_event_cb(btnSetTime, event_handler);
lv_btn_set_state(btnSetTime, LV_BTN_STATE_DISABLED);
+ lv_obj_set_state(lblSetTime, LV_STATE_DISABLED);
}
SettingSetDate::~SettingSetDate() {
@@ -102,10 +104,12 @@ void SettingSetDate::HandleButtonPress() {
dateTimeController.Seconds(),
nrf_rtc_counter_get(portNRF_RTC_REG));
lv_btn_set_state(btnSetTime, LV_BTN_STATE_DISABLED);
+ lv_obj_set_state(lblSetTime, LV_STATE_DISABLED);
}
void SettingSetDate::CheckDay() {
const int maxDay = MaximumDayOfMonth(monthCounter.GetValue(), yearCounter.GetValue());
dayCounter.SetMax(maxDay);
lv_btn_set_state(btnSetTime, LV_BTN_STATE_RELEASED);
+ lv_obj_set_state(lblSetTime, LV_STATE_DEFAULT);
}
diff --git a/src/displayapp/screens/settings/SettingSetDate.h b/src/displayapp/screens/settings/SettingSetDate.h
index af0d654e..a0ffc683 100644
--- a/src/displayapp/screens/settings/SettingSetDate.h
+++ b/src/displayapp/screens/settings/SettingSetDate.h
@@ -21,6 +21,7 @@ namespace Pinetime {
Controllers::DateTime& dateTimeController;
lv_obj_t* btnSetTime;
+ lv_obj_t* lblSetTime;
Widgets::Counter dayCounter = Widgets::Counter(1, 31, jetbrains_mono_bold_20);
Widgets::Counter monthCounter = Widgets::Counter(1, 12, jetbrains_mono_bold_20);
diff --git a/src/displayapp/screens/settings/SettingSetTime.cpp b/src/displayapp/screens/settings/SettingSetTime.cpp
index 47b786e4..e7d824fd 100644
--- a/src/displayapp/screens/settings/SettingSetTime.cpp
+++ b/src/displayapp/screens/settings/SettingSetTime.cpp
@@ -67,13 +67,15 @@ SettingSetTime::SettingSetTime(Pinetime::Applications::DisplayApp* app,
btnSetTime->user_data = this;
lv_obj_set_size(btnSetTime, 120, 50);
lv_obj_align(btnSetTime, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0);
- lv_obj_set_style_local_value_str(btnSetTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "Set");
+ lblSetTime = lv_label_create(btnSetTime, nullptr);
+ lv_label_set_text_static(lblSetTime, "Set");
lv_obj_set_style_local_bg_color(btnSetTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt);
- lv_obj_set_style_local_value_color(btnSetTime, LV_BTN_PART_MAIN, LV_STATE_DISABLED, LV_COLOR_GRAY);
+ lv_obj_set_style_local_text_color(lblSetTime, LV_LABEL_PART_MAIN, LV_STATE_DISABLED, LV_COLOR_GRAY);
lv_obj_set_event_cb(btnSetTime, SetTimeEventHandler);
UpdateScreen();
lv_obj_set_state(btnSetTime, LV_STATE_DISABLED);
+ lv_obj_set_state(lblSetTime, LV_STATE_DISABLED);
}
SettingSetTime::~SettingSetTime() {
@@ -89,6 +91,7 @@ void SettingSetTime::UpdateScreen() {
}
}
lv_obj_set_state(btnSetTime, LV_STATE_DEFAULT);
+ lv_obj_set_state(lblSetTime, LV_STATE_DEFAULT);
}
void SettingSetTime::SetTime() {
@@ -104,4 +107,5 @@ void SettingSetTime::SetTime() {
0,
nrf_rtc_counter_get(portNRF_RTC_REG));
lv_obj_set_state(btnSetTime, LV_STATE_DISABLED);
+ lv_obj_set_state(lblSetTime, LV_STATE_DISABLED);
}
diff --git a/src/displayapp/screens/settings/SettingSetTime.h b/src/displayapp/screens/settings/SettingSetTime.h
index e0b42bdd..b61962c1 100644
--- a/src/displayapp/screens/settings/SettingSetTime.h
+++ b/src/displayapp/screens/settings/SettingSetTime.h
@@ -26,6 +26,7 @@ namespace Pinetime {
lv_obj_t* lblampm;
lv_obj_t* btnSetTime;
+ lv_obj_t* lblSetTime;
Widgets::Counter hourCounter = Widgets::Counter(0, 23, jetbrains_mono_42);
Widgets::Counter minuteCounter = Widgets::Counter(0, 59, jetbrains_mono_42);
};
diff --git a/src/displayapp/screens/settings/SettingSteps.cpp b/src/displayapp/screens/settings/SettingSteps.cpp
index af5bd6e9..a6b6f4a8 100644
--- a/src/displayapp/screens/settings/SettingSteps.cpp
+++ b/src/displayapp/screens/settings/SettingSteps.cpp
@@ -48,7 +48,8 @@ SettingSteps::SettingSteps(Pinetime::Applications::DisplayApp* app, Pinetime::Co
btnPlus->user_data = this;
lv_obj_set_size(btnPlus, 80, 50);
lv_obj_align(btnPlus, lv_scr_act(), LV_ALIGN_CENTER, 55, 80);
- lv_obj_set_style_local_value_str(btnPlus, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "+");
+ lv_obj_t* lblPlus = lv_label_create(btnPlus, nullptr);
+ lv_label_set_text_static(lblPlus, "+");
lv_obj_set_event_cb(btnPlus, event_handler);
btnMinus = lv_btn_create(lv_scr_act(), nullptr);
@@ -56,7 +57,8 @@ SettingSteps::SettingSteps(Pinetime::Applications::DisplayApp* app, Pinetime::Co
lv_obj_set_size(btnMinus, 80, 50);
lv_obj_set_event_cb(btnMinus, event_handler);
lv_obj_align(btnMinus, lv_scr_act(), LV_ALIGN_CENTER, -55, 80);
- lv_obj_set_style_local_value_str(btnMinus, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "-");
+ lv_obj_t* lblMinus = lv_label_create(btnMinus, nullptr);
+ lv_label_set_text_static(lblMinus, "-");
}
SettingSteps::~SettingSteps() {
diff --git a/src/displayapp/screens/settings/SettingWatchFace.cpp b/src/displayapp/screens/settings/SettingWatchFace.cpp
index be595a74..411cc898 100644
--- a/src/displayapp/screens/settings/SettingWatchFace.cpp
+++ b/src/displayapp/screens/settings/SettingWatchFace.cpp
@@ -1,58 +1,29 @@
#include "displayapp/screens/settings/SettingWatchFace.h"
#include <lvgl/lvgl.h>
#include "displayapp/DisplayApp.h"
+#include "displayapp/screens/CheckboxList.h"
#include "displayapp/screens/Screen.h"
#include "displayapp/screens/Styles.h"
#include "displayapp/screens/Symbols.h"
+#include "components/settings/Settings.h"
using namespace Pinetime::Applications::Screens;
-namespace {
- void event_handler(lv_obj_t* obj, lv_event_t event) {
- auto* screen = static_cast<SettingWatchFace*>(obj->user_data);
- screen->UpdateSelected(obj, event);
- }
-}
-
-constexpr std::array<const char*, 4> SettingWatchFace::options;
+constexpr const char* SettingWatchFace::title;
+constexpr const char* SettingWatchFace::symbol;
SettingWatchFace::SettingWatchFace(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Settings& settingsController)
- : Screen(app), settingsController {settingsController} {
-
- lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr);
-
- lv_obj_set_style_local_bg_opa(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
- lv_obj_set_style_local_pad_all(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 10);
- 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_width(container1, LV_HOR_RES - 20);
- lv_obj_set_height(container1, LV_VER_RES - 50);
- lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT);
-
- lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr);
- lv_label_set_text_static(title, "Watch face");
- lv_label_set_align(title, LV_LABEL_ALIGN_CENTER);
- lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 10, 15);
-
- lv_obj_t* 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_ORANGE);
- lv_label_set_text_static(icon, Symbols::home);
- lv_label_set_align(icon, LV_LABEL_ALIGN_CENTER);
- lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0);
-
- for (unsigned int i = 0; i < options.size(); i++) {
- cbOption[i] = lv_checkbox_create(container1, nullptr);
- lv_checkbox_set_text(cbOption[i], options[i]);
- cbOption[i]->user_data = this;
- lv_obj_set_event_cb(cbOption[i], event_handler);
- SetRadioButtonStyle(cbOption[i]);
-
- if (settingsController.GetClockFace() == i) {
- lv_checkbox_set_checked(cbOption[i], true);
- }
- }
+ : Screen(app),
+ settingsController {settingsController},
+ screens {app,
+ settingsController.GetWatchfacesMenu(),
+ {[this]() -> std::unique_ptr<Screen> {
+ return CreateScreen1();
+ },
+ [this]() -> std::unique_ptr<Screen> {
+ return CreateScreen2();
+ }},
+ Screens::ScreenListModes::UpDown} {
}
SettingWatchFace::~SettingWatchFace() {
@@ -60,15 +31,32 @@ SettingWatchFace::~SettingWatchFace() {
settingsController.SaveSettings();
}
-void SettingWatchFace::UpdateSelected(lv_obj_t* object, lv_event_t event) {
- if (event == LV_EVENT_VALUE_CHANGED) {
- for (unsigned int i = 0; i < options.size(); i++) {
- if (object == cbOption[i]) {
- lv_checkbox_set_checked(cbOption[i], true);
- settingsController.SetClockFace(i);
- } else {
- lv_checkbox_set_checked(cbOption[i], false);
- }
- }
- }
+bool SettingWatchFace::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
+ return screens.OnTouchEvent(event);
+}
+
+std::unique_ptr<Screen> SettingWatchFace::CreateScreen1() {
+ std::array<const char*, 4> watchfaces {"Digital face", "Analog face", "PineTimeStyle", "Terminal"};
+ return std::make_unique<Screens::CheckboxList>(0,
+ 2,
+ app,
+ settingsController,
+ title,
+ symbol,
+ &Controllers::Settings::SetClockFace,
+ &Controllers::Settings::GetClockFace,
+ watchfaces);
+}
+
+std::unique_ptr<Screen> SettingWatchFace::CreateScreen2() {
+ std::array<const char*, 4> watchfaces {"Infineat face", "Casio G7710", "", ""};
+ return std::make_unique<Screens::CheckboxList>(1,
+ 2,
+ app,
+ settingsController,
+ title,
+ symbol,
+ &Controllers::Settings::SetClockFace,
+ &Controllers::Settings::GetClockFace,
+ watchfaces);
}
diff --git a/src/displayapp/screens/settings/SettingWatchFace.h b/src/displayapp/screens/settings/SettingWatchFace.h
index d65f4a22..7d14554e 100644
--- a/src/displayapp/screens/settings/SettingWatchFace.h
+++ b/src/displayapp/screens/settings/SettingWatchFace.h
@@ -4,8 +4,10 @@
#include <cstdint>
#include <lvgl/lvgl.h>
+#include "displayapp/screens/ScreenList.h"
#include "components/settings/Settings.h"
#include "displayapp/screens/Screen.h"
+#include "displayapp/screens/Symbols.h"
namespace Pinetime {
@@ -17,13 +19,16 @@ namespace Pinetime {
SettingWatchFace(DisplayApp* app, Pinetime::Controllers::Settings& settingsController);
~SettingWatchFace() override;
- void UpdateSelected(lv_obj_t* object, lv_event_t event);
+ bool OnTouchEvent(TouchEvents event) override;
private:
- static constexpr std::array<const char*, 4> options = {"Digital face", "Analog face", "PineTimeStyle", "Terminal"};
Controllers::Settings& settingsController;
+ ScreenList<2> screens;
- lv_obj_t* cbOption[options.size()];
+ static constexpr const char* title = "Watch face";
+ static constexpr const char* symbol = Symbols::home;
+ std::unique_ptr<Screen> CreateScreen1();
+ std::unique_ptr<Screen> CreateScreen2();
};
}
}
diff --git a/src/displayapp/widgets/StatusIcons.cpp b/src/displayapp/widgets/StatusIcons.cpp
index 607f3745..aacf13ae 100644
--- a/src/displayapp/widgets/StatusIcons.cpp
+++ b/src/displayapp/widgets/StatusIcons.cpp
@@ -15,11 +15,9 @@ void StatusIcons::Create() {
lv_obj_set_style_local_bg_opa(container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
bleIcon = lv_label_create(container, nullptr);
- lv_obj_set_style_local_text_color(bleIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x0082FC));
lv_label_set_text_static(bleIcon, Screens::Symbols::bluetooth);
batteryPlug = lv_label_create(container, nullptr);
- lv_obj_set_style_local_text_color(batteryPlug, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED);
lv_label_set_text_static(batteryPlug, Screens::Symbols::plug);
batteryIcon.Create(container);
diff --git a/src/libs/lv_conf.h b/src/libs/lv_conf.h
index 00f6a1df..063f1d34 100644
--- a/src/libs/lv_conf.h
+++ b/src/libs/lv_conf.h
@@ -164,7 +164,7 @@ typedef void* lv_anim_user_data_t;
#define LV_USE_PATTERN 1
/*1: enable value string drawing on rectangles*/
-#define LV_USE_VALUE_STR 1
+#define LV_USE_VALUE_STR 0
/* 1: Use other blend modes than normal (`LV_BLEND_MODE_...`)*/
#define LV_USE_BLEND_MODES 0
diff --git a/src/main.cpp b/src/main.cpp
index 109971bc..ad7a07dc 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -131,7 +131,8 @@ Pinetime::Applications::DisplayApp displayApp(lcd,
timerController,
alarmController,
brightnessController,
- touchHandler);
+ touchHandler,
+ fs);
Pinetime::System::SystemTask systemTask(spi,
lcd,
diff --git a/src/resources/CMakeLists.txt b/src/resources/CMakeLists.txt
new file mode 100644
index 00000000..0983aaff
--- /dev/null
+++ b/src/resources/CMakeLists.txt
@@ -0,0 +1,29 @@
+
+find_program(LV_FONT_CONV "lv_font_conv" NO_CACHE REQUIRED
+ HINTS "${CMAKE_SOURCE_DIR}/node_modules/.bin")
+message(STATUS "Using ${LV_FONT_CONV} to generate font files")
+
+find_program(LV_IMG_CONV "lv_img_conv" NO_CACHE REQUIRED
+ HINTS "${CMAKE_SOURCE_DIR}/node_modules/.bin")
+message(STATUS "Using ${LV_IMG_CONV} to generate font files")
+
+if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
+ # FindPython3 module introduces with CMake 3.12
+ # https://cmake.org/cmake/help/latest/module/FindPython3.html
+ find_package(Python3 REQUIRED)
+else()
+ set(Python3_EXECUTABLE "python")
+endif()
+
+# generate fonts
+add_custom_target(GenerateResources
+ COMMAND "${Python3_EXECUTABLE}" ${CMAKE_CURRENT_SOURCE_DIR}/generate-fonts.py --lv-font-conv "${LV_FONT_CONV}" ${CMAKE_CURRENT_SOURCE_DIR}/fonts.json
+ COMMAND "${Python3_EXECUTABLE}" ${CMAKE_CURRENT_SOURCE_DIR}/generate-img.py --lv-img-conv "${LV_IMG_CONV}" ${CMAKE_CURRENT_SOURCE_DIR}/images.json
+ COMMAND "${Python3_EXECUTABLE}" ${CMAKE_CURRENT_SOURCE_DIR}/generate-package.py --config ${CMAKE_CURRENT_SOURCE_DIR}/fonts.json --config ${CMAKE_CURRENT_SOURCE_DIR}/images.json --obsolete obsolete_files.json --output infinitime-resources-${pinetime_VERSION_MAJOR}.${pinetime_VERSION_MINOR}.${pinetime_VERSION_PATCH}.zip
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/fonts.json
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/images.json
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+
+ COMMENT "Generate fonts and images for resource package"
+)
+
diff --git a/src/resources/fonts.json b/src/resources/fonts.json
new file mode 100644
index 00000000..a270e6a2
--- /dev/null
+++ b/src/resources/fonts.json
@@ -0,0 +1,62 @@
+{
+ "teko" : {
+ "sources": [
+ {
+ "file": "fonts/Teko-Light.ttf",
+ "symbols": "0123456789:/ampMonTueWdhFriSt "
+ }
+ ],
+ "bpp": 1,
+ "size": 28,
+ "format": "bin",
+ "target_path": "/fonts/"
+ },
+ "bebas" : {
+ "sources": [
+ {
+ "file": "fonts/BebasNeue-Regular.ttf",
+ "symbols": "0123456789:"
+ }
+ ],
+ "bpp": 1,
+ "size": 120,
+ "format": "bin",
+ "target_path": "/fonts/"
+ },
+ "lv_font_dots_40": {
+ "sources": [
+ {
+ "file": "fonts/repetitionscrolling.ttf",
+ "symbols": "0123456789-MONTUEWEDTHUFRISATSUN WK"
+ }
+ ],
+ "bpp": 1,
+ "size": 40,
+ "format": "bin",
+ "target_path": "/fonts/"
+ },
+ "7segments_40" : {
+ "sources": [
+ {
+ "file": "fonts/7segment.woff",
+ "symbols": "0123456789: -"
+ }
+ ],
+ "bpp": 1,
+ "size": 40,
+ "format": "bin",
+ "target_path": "/fonts/"
+ },
+ "7segments_115" : {
+ "sources": [
+ {
+ "file": "fonts/7segment.woff",
+ "symbols": "0123456789: -"
+ }
+ ],
+ "bpp": 1,
+ "size": 115,
+ "format": "bin",
+ "target_path": "/fonts/"
+ }
+}
diff --git a/src/resources/fonts/7segment.woff b/src/resources/fonts/7segment.woff
new file mode 100644
index 00000000..79ed9249
--- /dev/null
+++ b/src/resources/fonts/7segment.woff
Binary files differ
diff --git a/src/resources/fonts/BebasNeue-Regular.ttf b/src/resources/fonts/BebasNeue-Regular.ttf
new file mode 100644
index 00000000..76e22b8b
--- /dev/null
+++ b/src/resources/fonts/BebasNeue-Regular.ttf
Binary files differ
diff --git a/src/resources/fonts/Teko-Light.ttf b/src/resources/fonts/Teko-Light.ttf
new file mode 100644
index 00000000..679f0137
--- /dev/null
+++ b/src/resources/fonts/Teko-Light.ttf
Binary files differ
diff --git a/src/resources/fonts/repetitionscrolling.ttf b/src/resources/fonts/repetitionscrolling.ttf
new file mode 100644
index 00000000..dc124164
--- /dev/null
+++ b/src/resources/fonts/repetitionscrolling.ttf
Binary files differ
diff --git a/src/resources/generate-fonts.py b/src/resources/generate-fonts.py
new file mode 100755
index 00000000..20408166
--- /dev/null
+++ b/src/resources/generate-fonts.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+
+import io
+import sys
+import json
+import shutil
+import typing
+import os.path
+import argparse
+import subprocess
+
+class Source(object):
+ def __init__(self, d):
+ self.file = d['file']
+ if not os.path.exists(self.file):
+ self.file = os.path.join(os.path.dirname(sys.argv[0]), self.file)
+ self.range = d.get('range')
+ self.symbols = d.get('symbols')
+
+
+def gen_lvconv_line(lv_font_conv: str, dest: str, size: int, bpp: int, format: str, sources: typing.List[Source], compress:bool=False):
+ if format != "lvgl" and format != "bin":
+ format = "bin" if dest.lower().endswith(".bin") else "lvgl"
+
+ args = [lv_font_conv, '--size', str(size), '--output', dest, '--bpp', str(bpp), '--format', format]
+ if not compress:
+ args.append('--no-compress')
+ for source in sources:
+ args.extend(['--font', source.file])
+ if source.range:
+ args.extend(['--range', source.range])
+ if source.symbols:
+ args.extend(['--symbols', source.symbols])
+
+ return args
+
+def main():
+ ap = argparse.ArgumentParser(description='auto generate LVGL font files from fonts')
+ ap.add_argument('config', type=str, help='config file to use')
+ ap.add_argument('-f', '--font', type=str, action='append', help='Choose specific fonts to generate (default: all)', default=[])
+ ap.add_argument('--lv-font-conv', type=str, help='Path to "lv_font_conf" executable', default="lv_font_conv")
+ args = ap.parse_args()
+
+ if not shutil.which(args.lv_font_conv):
+ sys.exit(f"Missing lv_font_conv. Make sure it's findable (in PATH) or specify it manually")
+ if not os.path.exists(args.config):
+ sys.exit(f'Error: the config file {args.config} does not exist.')
+ if not os.access(args.config, os.R_OK):
+ sys.exit(f'Error: the config file {args.config} is not accessible (permissions?).')
+ with open(args.config, 'r') as fd:
+ data = json.load(fd)
+
+ fonts_to_run = set(data.keys())
+
+ if args.font:
+ enabled_fonts = set()
+ for font in args.font:
+ enabled_fonts.add(font[:-2] if font.endswith('.c') else font)
+ d = enabled_fonts.difference(fonts_to_run)
+ if d:
+ print(f'Warning: requested font{"s" if len(d)>1 else ""} missing: {" ".join(d)}')
+ fonts_to_run = fonts_to_run.intersection(enabled_fonts)
+
+ for name in fonts_to_run:
+ font = data[name]
+ sources = font.pop('sources')
+ patches = font.pop('patches') if 'patches' in font else []
+ font['sources'] = [Source(thing) for thing in sources]
+ extension = 'c' if font['format'] != 'bin' else 'bin'
+ font.pop('target_path')
+ line = gen_lvconv_line(args.lv_font_conv, f'{name}.{extension}', **font)
+ subprocess.check_call(line)
+ if patches:
+ for patch in patches:
+ subprocess.check_call(['/usr/bin/env', 'patch', name+'.'+extension, patch])
+
+
+
+if __name__ == '__main__':
+ main()
diff --git a/src/resources/generate-img.py b/src/resources/generate-img.py
new file mode 100755
index 00000000..cdbfc030
--- /dev/null
+++ b/src/resources/generate-img.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+
+import io
+import sys
+import json
+import shutil
+import typing
+import os.path
+import argparse
+import subprocess
+
+def gen_lvconv_line(lv_img_conv: str, dest: str, color_format: str, output_format: str, binary_format: str, sources: str):
+ args = [lv_img_conv, sources, '--force', '--output-file', dest, '--color-format', color_format, '--output-format', output_format, '--binary-format', binary_format]
+
+ return args
+
+def main():
+ ap = argparse.ArgumentParser(description='auto generate LVGL font files from fonts')
+ ap.add_argument('config', type=str, help='config file to use')
+ ap.add_argument('-i', '--image', type=str, action='append', help='Choose specific images to generate (default: all)', default=[])
+ ap.add_argument('--lv-img-conv', type=str, help='Path to "lv_img_conf" executable', default="lv_img_conv")
+ args = ap.parse_args()
+
+ if not shutil.which(args.lv_img_conv):
+ sys.exit(f"Missing lv_img_conv. Make sure it's findable (in PATH) or specify it manually")
+ if not os.path.exists(args.config):
+ sys.exit(f'Error: the config file {args.config} does not exist.')
+ if not os.access(args.config, os.R_OK):
+ sys.exit(f'Error: the config file {args.config} is not accessible (permissions?).')
+ with open(args.config, 'r') as fd:
+ data = json.load(fd)
+
+ images_to_run = set(data.keys())
+
+ if args.image:
+ enabled_images = set()
+ for image in args.image:
+ enabled_images.add(image[:-2] if image.endswith('.c') else image)
+ d = enabled_images.difference(images_to_run)
+ if d:
+ print(f'Warning: requested image{"s" if len(d)>1 else ""} missing: {" ".join(d)}')
+ images_to_run = images_to_run.intersection(enabled_images)
+
+ for name in images_to_run:
+ image = data[name]
+ if not os.path.exists(image['sources']):
+ image['sources'] = os.path.join(os.path.dirname(sys.argv[0]), image['sources'])
+ extension = 'bin'
+ image.pop('target_path')
+ line = gen_lvconv_line(args.lv_img_conv, f'{name}.{extension}', **image)
+ subprocess.check_call(line)
+
+
+
+if __name__ == '__main__':
+ main()
diff --git a/src/resources/generate-package.py b/src/resources/generate-package.py
new file mode 100755
index 00000000..ff02d4fe
--- /dev/null
+++ b/src/resources/generate-package.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+
+import io
+import sys
+import json
+import shutil
+import typing
+import os.path
+import argparse
+import subprocess
+from zipfile import ZipFile
+
+def main():
+ ap = argparse.ArgumentParser(description='auto generate LVGL font files from fonts')
+ ap.add_argument('--config', '-c', type=str, action='append', help='config file to use')
+ ap.add_argument('--obsolete', type=str, help='List of obsolete files')
+ ap.add_argument('--output', type=str, help='output file name')
+ args = ap.parse_args()
+
+ for config_file in args.config:
+ if not os.path.exists(config_file):
+ sys.exit(f'Error: the config file {config_file} does not exist.')
+ if not os.access(config_file, os.R_OK):
+ sys.exit(f'Error: the config file {config_file} is not accessible (permissions?).')
+
+ if args.obsolete:
+ obsolete_file_path = os.path.join(os.path.dirname(sys.argv[0]), args.obsolete)
+ if not os.path.exists(obsolete_file_path):
+ sys.exit(f'Error: the "obsolete" file {args.obsolete} does not exist.')
+ if not os.access(obsolete_file_path, os.R_OK):
+ sys.exit(f'Error: the "obsolete" file {args.obsolete} is not accessible (permissions?).')
+
+ zf = ZipFile(args.output, mode='w')
+ resource_files = []
+
+ for config_file in args.config:
+ with open(config_file, 'r') as fd:
+ data = json.load(fd)
+
+ resource_names = set(data.keys())
+ for name in resource_names:
+ resource = data[name]
+ resource_files.append({
+ "filename": name+'.bin',
+ "path": resource['target_path'] + name+'.bin'
+ })
+
+ path = name + '.bin'
+ if not os.path.exists(path):
+ path = os.path.join(os.path.dirname(sys.argv[0]), path)
+ zf.write(path)
+
+ if args.obsolete:
+ obsolete_file_path = os.path.join(os.path.dirname(sys.argv[0]), args.obsolete)
+ with open(obsolete_file_path, 'r') as fd:
+ obsolete_data = json.load(fd)
+ else:
+ obsolete_data = {}
+ output = {
+ 'resources': resource_files,
+ 'obsolete_files': obsolete_data
+ }
+
+
+ with open("resources.json", 'w') as fd:
+ json.dump(output, fd, indent=4)
+
+ zf.write('resources.json')
+ zf.close()
+
+if __name__ == '__main__':
+ main()
diff --git a/src/resources/images.json b/src/resources/images.json
new file mode 100644
index 00000000..db2ccab0
--- /dev/null
+++ b/src/resources/images.json
@@ -0,0 +1,9 @@
+{
+ "pine_small" : {
+ "sources": "images/pine_logo.png",
+ "color_format": "CF_TRUE_COLOR_ALPHA",
+ "output_format": "bin",
+ "binary_format": "ARGB8565_RBSWAP",
+ "target_path": "/images/"
+ }
+}
diff --git a/src/resources/images/pine_logo.png b/src/resources/images/pine_logo.png
new file mode 100644
index 00000000..aa96be4b
--- /dev/null
+++ b/src/resources/images/pine_logo.png
Binary files differ
diff --git a/src/resources/images/pine_logo.svg b/src/resources/images/pine_logo.svg
new file mode 100644
index 00000000..55f21169
--- /dev/null
+++ b/src/resources/images/pine_logo.svg
@@ -0,0 +1,253 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="110.49872"
+ height="150.24246"
+ viewBox="0 0 29.236118 39.751652"
+ version="1.1"
+ id="svg2418"
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
+ sodipodi:docname="pine_logo.svg"
+ inkscape:export-filename="/home/diegomiguel/Syncthing/Watchface/pine_logo_new_2_transparent.png"
+ inkscape:export-xdpi="19.807983"
+ inkscape:export-ydpi="19.807983"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview2420"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:document-units="mm"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ units="px"
+ inkscape:zoom="4.1424077"
+ inkscape:cx="69.886892"
+ inkscape:cy="73.387272"
+ inkscape:window-width="1920"
+ inkscape:window-height="1026"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g972"
+ inkscape:snap-page="true" />
+ <defs
+ id="defs2415" />
+ <g
+ inkscape:label="Layer 1"
+ id="layer1"
+ transform="translate(-91.35232,-110.1768)"
+ inkscape:groupmode="layer">
+ <rect
+ style="display:none;opacity:1;fill:#ffffff;fill-opacity:1;stroke:#4d4d4d;stroke-width:0"
+ id="rect2129"
+ width="29.236118"
+ height="39.751652"
+ x="91.352318"
+ y="110.1768"
+ inkscape:label="bg" />
+ <g
+ id="g32004"
+ style="display:none;stroke:none"
+ inkscape:export-filename="/Users/diegomiguel/Syncthing/Watchface/logo_pine.png"
+ inkscape:export-xdpi="19.965168"
+ inkscape:export-ydpi="19.965168"
+ inkscape:label="pine_logo"
+ transform="translate(75.060638,-5.5438717)">
+ <g
+ id="g13016"
+ inkscape:label="pine"
+ style="display:inline;fill:#6f2d00;fill-opacity:1;stroke:none"
+ transform="matrix(1.1631294,0,0,1.1631294,-5.0422885,-22.11978)"
+ inkscape:export-filename="/Users/diegomiguel/Syncthing/Watchface/logo_pine.png"
+ inkscape:export-xdpi="31.276381"
+ inkscape:export-ydpi="31.276381"
+ sodipodi:insensitive="true">
+ <path
+ id="path5716"
+ style="fill:#6f2d00;fill-opacity:1;stroke:none;stroke-width:2.34917;stroke-linecap:round"
+ inkscape:transform-center-x="1.2687941"
+ d="M 116.82422,535.70898 102.4375,542.5 l -14.386719,6.78906 14.386719,6.78906 11,5.19141 v 15.80664 h 3.38672 v -14.20703 -3.93555 -9.64453 z"
+ transform="scale(0.26458333)" />
+ <path
+ style="fill:#6f2d00;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 18.341872,136.77692 3.331558,7.57837 7.029221,-3.22172 z"
+ id="path5936"
+ sodipodi:nodetypes="cccc" />
+ <path
+ sodipodi:type="star"
+ style="fill:#6f2d00;fill-opacity:1;stroke:none;stroke-width:1.51181;stroke-linecap:round"
+ id="path7773"
+ inkscape:flatsided="false"
+ sodipodi:sides="3"
+ sodipodi:cx="116.64632"
+ sodipodi:cy="501.86975"
+ sodipodi:r1="14.699218"
+ sodipodi:r2="7.3496094"
+ sodipodi:arg1="1.0471976"
+ sodipodi:arg2="2.0943951"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ transform="matrix(0.39243637,0,0,0.31059853,-17.750778,-19.228227)"
+ inkscape:transform-center-x="1.4421265"
+ d="m 123.99592,514.59965 -11.02441,-6.36495 -11.02441,-6.36495 11.02441,-6.36495 11.02442,-6.36494 0,12.72989 z" />
+ <path
+ sodipodi:type="star"
+ style="fill:#6f2d00;fill-opacity:1;stroke:none;stroke-width:1.51181;stroke-linecap:round"
+ id="path7877"
+ inkscape:flatsided="false"
+ sodipodi:sides="3"
+ sodipodi:cx="116.64632"
+ sodipodi:cy="501.86975"
+ sodipodi:r1="14.699218"
+ sodipodi:r2="7.3496094"
+ sodipodi:arg1="1.0471976"
+ sodipodi:arg2="2.0943951"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ transform="matrix(-0.3940968,0,0,-0.29190487,69.062729,278.57074)"
+ inkscape:transform-center-x="-1.4482278"
+ inkscape:transform-center-y="3.6892669e-06"
+ d="m 123.99592,514.59965 -11.02441,-6.36495 -11.02441,-6.36495 11.02441,-6.36495 11.02442,-6.36494 0,12.72989 z" />
+ <path
+ sodipodi:type="star"
+ style="fill:#6f2d00;fill-opacity:1;stroke:none;stroke-width:1.51181;stroke-linecap:round"
+ id="path7929"
+ inkscape:flatsided="false"
+ sodipodi:sides="3"
+ sodipodi:cx="116.64632"
+ sodipodi:cy="501.86975"
+ sodipodi:r1="14.699218"
+ sodipodi:r2="7.3496094"
+ sodipodi:arg1="1.0471976"
+ sodipodi:arg2="2.0943951"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ transform="matrix(0.34926521,0,0,0.27033526,-12.397729,-7.5515591)"
+ inkscape:transform-center-x="1.28348"
+ inkscape:transform-center-y="1.7340579e-06"
+ d="m 123.99592,514.59965 -11.02441,-6.36495 -11.02441,-6.36495 11.02441,-6.36495 11.02442,-6.36494 0,12.72989 z" />
+ <path
+ style="fill:#6f2d00;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 24.903849,122.34368 -1.378447,3.99721 5.0395,-2.31516 z"
+ id="path7964"
+ sodipodi:nodetypes="cccc" />
+ <path
+ style="fill:#6f2d00;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 30.909733,118.50827 v 4.63122 l -3.122345,-1.29967 z"
+ id="path11445" />
+ </g>
+ <g
+ id="g13032"
+ transform="matrix(-1.1631294,0,0,1.1631294,66.861771,-22.11978)"
+ style="fill:#de5a00;fill-opacity:1;stroke:none"
+ inkscape:label="pine"
+ inkscape:export-filename="/Users/diegomiguel/Syncthing/Watchface/logo_pine.png"
+ inkscape:export-xdpi="31.276381"
+ inkscape:export-ydpi="31.276381">
+ <path
+ id="path13018"
+ style="fill:#de5a00;fill-opacity:1;stroke:none;stroke-width:2.34917;stroke-linecap:round"
+ inkscape:transform-center-x="1.2687941"
+ d="M 116.82422,535.70898 102.4375,542.5 l -14.386719,6.78906 14.386719,6.78906 11,5.19141 v 15.80664 h 3.38672 v -14.20703 -3.93555 -9.64453 z"
+ transform="scale(0.26458333)" />
+ <path
+ style="fill:#de5a00;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 18.341872,136.77692 3.331558,7.57837 7.029221,-3.22172 z"
+ id="path13020"
+ sodipodi:nodetypes="cccc" />
+ <path
+ sodipodi:type="star"
+ style="fill:#de5a00;fill-opacity:1;stroke:none;stroke-width:1.51181;stroke-linecap:round"
+ id="path13022"
+ inkscape:flatsided="false"
+ sodipodi:sides="3"
+ sodipodi:cx="116.64632"
+ sodipodi:cy="501.86975"
+ sodipodi:r1="14.699218"
+ sodipodi:r2="7.3496094"
+ sodipodi:arg1="1.0471976"
+ sodipodi:arg2="2.0943951"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ transform="matrix(0.39243637,0,0,0.31059853,-17.750778,-19.228227)"
+ inkscape:transform-center-x="1.4421265"
+ d="m 123.99592,514.59965 -11.02441,-6.36495 -11.02441,-6.36495 11.02441,-6.36495 11.02442,-6.36494 0,12.72989 z" />
+ <path
+ sodipodi:type="star"
+ style="fill:#de5a00;fill-opacity:1;stroke:none;stroke-width:1.51181;stroke-linecap:round"
+ id="path13024"
+ inkscape:flatsided="false"
+ sodipodi:sides="3"
+ sodipodi:cx="116.64632"
+ sodipodi:cy="501.86975"
+ sodipodi:r1="14.699218"
+ sodipodi:r2="7.3496094"
+ sodipodi:arg1="1.0471976"
+ sodipodi:arg2="2.0943951"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ transform="matrix(-0.3940968,0,0,-0.29190487,69.062729,278.57074)"
+ inkscape:transform-center-x="-1.4482278"
+ inkscape:transform-center-y="3.6892669e-06"
+ d="m 123.99592,514.59965 -11.02441,-6.36495 -11.02441,-6.36495 11.02441,-6.36495 11.02442,-6.36494 0,12.72989 z" />
+ <path
+ sodipodi:type="star"
+ style="fill:#de5a00;fill-opacity:1;stroke:none;stroke-width:1.51181;stroke-linecap:round"
+ id="path13026"
+ inkscape:flatsided="false"
+ sodipodi:sides="3"
+ sodipodi:cx="116.64632"
+ sodipodi:cy="501.86975"
+ sodipodi:r1="14.699218"
+ sodipodi:r2="7.3496094"
+ sodipodi:arg1="1.0471976"
+ sodipodi:arg2="2.0943951"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ transform="matrix(0.34926521,0,0,0.27033526,-12.397729,-7.5515591)"
+ inkscape:transform-center-x="1.28348"
+ inkscape:transform-center-y="1.7340579e-06"
+ d="m 123.99592,514.59965 -11.02441,-6.36495 -11.02441,-6.36495 11.02441,-6.36495 11.02442,-6.36494 0,12.72989 z" />
+ <path
+ style="fill:#de5a00;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 24.903849,122.34368 -1.378447,3.99721 5.0395,-2.31516 z"
+ id="path13028"
+ sodipodi:nodetypes="cccc" />
+ <path
+ style="fill:#de5a00;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 30.909733,118.50827 v 4.63122 l -3.122345,-1.29967 z"
+ id="path13030" />
+ </g>
+ </g>
+ <g
+ id="g972"
+ style="display:inline;stroke:none"
+ inkscape:export-filename="/Users/diegomiguel/Syncthing/Watchface/logo_pine.png"
+ inkscape:export-xdpi="19.965168"
+ inkscape:export-ydpi="19.965168"
+ inkscape:label="pine_logo"
+ transform="translate(75.060638,-5.5438717)">
+ <path
+ id="path952"
+ style="fill:#0f0f0f;fill-opacity:1;stroke:none;stroke-width:0.307744px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 30.909731,115.72067 v 5.38671 l -3.631692,-1.51168 z m -6.985621,4.46108 -1.603312,4.64927 5.861591,-2.69283 z m 6.98562,10.72311 -4.478564,-2.00136 -4.478563,-2.00136 4.478563,-2.00136 4.478568,-2.00137 v 4.00273 z m -12.461069,-3.7293 5.05343,2.16104 5.053431,2.16105 -5.053431,2.16105 -5.053434,2.16104 v -4.32209 z m 12.461067,14.24725 -5.032139,-2.29945 -5.032139,-2.29944 5.032139,-2.29943 5.032144,-2.29945 v 4.59888 z m -14.618046,-4.45333 3.875033,8.81462 8.175894,-3.74728 z m 14.618058,5.77232 -4.427436,2.0899 -4.427436,2.08929 4.427436,2.0893 3.385191,1.59762 v 4.86441 h 1.042245 v -4.37214 -1.21114 -2.96805 z"
+ inkscape:label="left" />
+ <path
+ id="path968"
+ style="fill:#575757;fill-opacity:1;stroke:none;stroke-width:0.307744px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 30.909752,115.72067 v 5.38671 l 3.631691,-1.51168 z m 6.98562,4.46108 1.603312,4.64927 -5.86159,-2.69283 z m -6.985619,10.72311 4.478564,-2.00136 4.478563,-2.00136 -4.478563,-2.00136 -4.478568,-2.00136 v 4.00272 z m 12.461067,-3.72931 -5.05343,2.16105 -5.05343,2.16105 5.05343,2.16104 5.053435,2.16105 v -4.32209 z m -12.461065,14.24726 5.032139,-2.29945 5.032139,-2.29944 -5.032139,-2.29944 -5.032144,-2.29944 v 4.59888 z m 14.618045,-4.45333 -3.875033,8.81462 -8.175893,-3.74728 z m -14.618058,5.77231 4.427436,2.0899 4.427436,2.0893 -4.427436,2.0893 -3.385191,1.59763 v 4.8644 h -1.042245 v -4.37213 -1.21115 -2.96805 z"
+ inkscape:label="right" />
+ </g>
+ </g>
+</svg>
diff --git a/src/resources/obsolete_files.json b/src/resources/obsolete_files.json
new file mode 100644
index 00000000..6109ace7
--- /dev/null
+++ b/src/resources/obsolete_files.json
@@ -0,0 +1,6 @@
+[
+ {
+ "path": "/example-of-obsolete-file.bin",
+ "since": "1.11.0"
+ }
+] \ No newline at end of file