Compare commits

...

69 Commits

Author SHA1 Message Date
tofasthacker
11acaeadd3 Merge remote-tracking branch 'origin/faceface' into faceface 2023-11-13 21:38:42 -05:00
4fa8e02289 Incriment version 2023-11-13 21:37:00 -05:00
0cc8017cbd Small rename 2023-11-13 21:37:00 -05:00
d758394ce9 Working face watchface 2023-11-13 21:37:00 -05:00
dd5d65d315 Added duplicate of analog face 2023-11-13 21:37:00 -05:00
2bffcf99f0 Merge branch 'main' of https://github.com/InfiniTimeOrg/InfiniTime 2023-11-13 16:57:53 -05:00
e24323b454 Incriment version 2023-10-25 22:33:30 -04:00
79820669b6 Small rename 2023-10-25 22:32:28 -04:00
1ff782cb0e Update version to 1.13.1
(cherry picked from commit 5d7e1ae1f3)
2023-10-22 23:42:06 -04:00
5d7e1ae1f3 Update version to 1.13.1 2023-10-22 23:41:43 -04:00
415ff6c05c Update reademe todo
(cherry picked from commit cbfd82959e)
2023-10-22 23:34:53 -04:00
2cb36d6e80 Working face watchface 2023-10-22 23:34:22 -04:00
cbfd82959e Update reademe todo 2023-10-22 23:31:15 -04:00
ce136ea148 Added duplicate of analog face 2023-10-22 22:37:53 -04:00
ab03504d0c Fix bug causing quickring to be missing in settings and some settings not remembered correctly 2023-10-22 21:38:52 -04:00
e4b4fba5a1 cleanup code and fix 12hour time on digital face 2023-10-21 23:45:41 -04:00
2ffc63b1d8 cleanup code and fix 12h time not working on digital face 2023-10-21 23:44:29 -04:00
31250fd579 Merge branch 'music_player' 2023-10-21 23:29:35 -04:00
94044d2fdc Removed battery percent from digital watch face 2023-10-21 23:14:41 -04:00
tofasthacker
858bf4f96e fix arrangement of alarm and timer 2023-10-21 22:01:12 -04:00
tofasthacker
1487a16f4a compiles on watch 2023-10-21 21:55:18 -04:00
tofasthacker
13af2bb96c Fixed battary order in digital watch face 2023-10-21 21:24:58 -04:00
e0e2126c29 Merge branch 'digital-face-mods' 2023-10-21 20:49:45 -04:00
6a772e7e8e Finish adding seconds 2023-10-21 20:16:20 -04:00
191f99904c Heartrate removed, steps moved, and seconds label added 2023-10-21 19:15:42 -04:00
8ada9410a1 continue swapping variables 2023-10-21 14:17:25 -04:00
bbfef9ee5c continue swapping variables 2023-10-19 22:39:49 -04:00
886e425461 Start swapping steps with heart rate variables 2023-10-15 23:18:20 -04:00
tofasthacker
6bbdb581cc Merge branch 'motor_pattern' 2023-10-14 23:13:37 -04:00
tofasthacker
16998a8ca8 Update README.md 2023-10-15 03:01:24 +00:00
tofasthacker
270d15a8ea switched red flash light off night default
Some checks failed
Code formatting / test-format (pull_request) Failing after 20s
Code formatting / test-clang-tidy (pull_request) Successful in 7m31s
CI / build-firmware (pull_request) Successful in 5m39s
CI / build-simulator (pull_request) Failing after 5s
CI / get-base-ref-size (pull_request) Successful in 15m27s
CI / Compare build size (pull_request) Successful in 5s
PR comment / comment (pull_request) Failing after 17s
2023-10-14 22:55:41 -04:00
tofasthacker
bf8c4dfef8 change motor patern on half hour 2023-10-14 22:50:41 -04:00
tofasthacker
ecbcce41d7 Merge branch 'chime' into motor_pattern 2023-10-09 16:49:59 -04:00
Riku Isokoski
3d75a7dc9f added motor pattern 2023-10-09 16:49:35 -04:00
tofasthacker
9d749434b5 added red flashlight 2023-10-07 11:41:36 -04:00
tofasthacker
8ff6bd9cae changed chime 2023-10-06 21:04:42 -04:00
tofasthacker
b5f500e96d fixed readme confict 2023-10-04 17:55:55 -04:00
tofasthacker
b488811f7f working sim 2023-10-03 19:02:33 -04:00
tofasthacker
e19e870290 created a working quick ring setting gui 2023-10-01 17:47:08 -04:00
cdd97b3871 Added battery percent to digital watch face 2023-09-29 23:15:07 -04:00
tofasthacker
1fd2496ee2 Update README.md 2023-09-29 01:46:02 +00:00
tofasthacker
8a40eaa9c5 fixed calculator comments 2023-09-28 20:38:01 -04:00
tofasthacker
c87a297a2f moved backspace and changed around the math for exponent 2023-09-28 20:35:36 -04:00
15e525a5ba Update todo and fix docker command in enviroment_setup.md 2023-09-27 22:49:58 -04:00
65976a2b23 Added enviroment_setup.md 2023-09-27 22:29:08 -04:00
tofasthacker
86695db883 working exponent math 2023-09-27 21:27:44 -04:00
tofasthacker
c8de75ccd9 Updated Readme and adjusted quicksettings 2023-09-27 16:38:10 -04:00
tofasthacker
163eaf5ab4 Change gestures in Music app and added quick ring 2023-09-26 21:39:20 -04:00
tofasthacker
21e86c4220 laid down main frame work. Need to add app class 2023-09-24 22:17:11 -04:00
09f7306e31 update todo 2023-09-23 23:19:10 -04:00
a2da74d93a update todo 2023-09-23 23:01:54 -04:00
e3c7295729 Merge branch 'flashlight_on_default' 2023-09-23 22:58:57 -04:00
tofasthacker
990ad0c87e Merge branch 'working_calculator' 2023-09-23 23:04:13 -04:00
tofasthacker
c74ae787ae Update README.md 2023-09-24 02:52:48 +00:00
9258331e9c update TODO 2023-09-23 22:39:49 -04:00
62e89c1bd3 Update todo 2023-09-23 22:28:03 -04:00
1fbd00b43d Turn on flashlight on flashlight app launch 2023-09-23 22:25:36 -04:00
4449a48de7 Update todo 2023-09-23 22:15:44 -04:00
4e6b1c421f Fix todo formatting 2023-09-23 22:11:45 -04:00
c04bd3bd7d Added todo list 2023-09-23 22:09:00 -04:00
tofasthacker
cbabd8914c fixed colored $ in terminal watch face 2023-09-23 19:42:18 -04:00
tofasthacker
5fc831542f fixed terminal $ 2023-09-23 19:39:53 -04:00
b95823e745 Fixed second $ in terminal watch face 2023-09-23 19:32:01 -04:00
tofasthacker
b0019da9dd renamed calculator class and added calculator and backspace icon 2023-09-23 19:03:16 -04:00
tofasthacker
3bb0ac29e2 Switched TestApp for calculator app 2023-09-23 14:17:56 -04:00
tofasthacker
329e42c5d4 added TestApp 2023-09-23 13:33:35 -04:00
tofasthacker
ecb91712fd Fix errors in documentation
Signed-off-by: tofasthacker <tofasthacker@noreply.localhost>
2023-09-22 13:02:40 +00:00
292b7e3fb1 Replaced am/pm with seconds on digital watchface 2023-09-21 21:01:37 -04:00
e334735697 Added swipe left to show music controls and a green $ to the prompt in terminal watch face 2023-09-20 16:12:14 -04:00
35 changed files with 1534 additions and 178 deletions

View File

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose Debug or Release") set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose Debug or Release")
project(pinetime VERSION 1.13.0 LANGUAGES C CXX ASM) project(pinetime VERSION 1.13.1.1 LANGUAGES C CXX ASM)
set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD 14)

108
README.md
View File

@ -1,88 +1,38 @@
# [InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime) # TODO
## Completed
### Pending version
![InfiniTime logo](doc/logo/infinitime-logo-small.jpg "InfiniTime Logo") ### v1.13.1
- [x] Make $s in terminal watch face green
- [x] Swipe left to access music control
- [x] Flashlight starts on
- [x] Calculator
- [x] Redo music control gestures
- [x] Swipe up toggles control mode and swipe down exits
- [x] Swipe left/right goes to watchface/another app
- [x] Rearrange quick acces and apps
- [x] Exponent button on calculator
- [x] Seconds on digital watchface
- [x] Add quick ring settings
Fast open-source firmware for the [PineTime smartwatch](https://www.pine64.org/pinetime/) with many features, written in modern C++. ## Pending merge
## New to InfiniTime? ## In progress
### Elijah
- [ ] Countdown timer presistant buzz
- [Getting started with InfiniTime](doc/gettingStarted/gettingStarted-1.0.md) ### Josh
- [Updating the software](doc/gettingStarted/updating-software.md) - [ ] Smiley face watchface :)
- [About the firmware and bootloader](doc/gettingStarted/about-software.md) - [ ] Remap button double click
- [PineTimeStyle Watch face](https://wiki.pine64.org/wiki/PineTimeStyle)
- [Weather integration](https://wiki.pine64.org/wiki/Infinitime-Weather)
### Companion apps ### Moses
- [Gadgetbridge](https://gadgetbridge.org/) (Android)
- [AmazFish](https://openrepos.net/content/piggz/amazfish/) (SailfishOS)
- [Siglo](https://github.com/alexr4535/siglo) (Linux)
- [InfiniLink](https://github.com/InfiniTimeOrg/InfiniLink) (iOS) **[Looking for a new maintainer]**
- [ITD](https://gitea.elara.ws/Elara6331/itd) (Linux)
- [WatchMate](https://github.com/azymohliad/watchmate) (Linux)
***Note** : We removed mentions to NRFConnect as this app is closed source and recent versions do not work anymore with InfiniTime (the last version known to work is 4.24.3). If you used NRFConnect in the past, we recommend you switch to [Gadgetbridge](https://gadgetbridge.org/).*
## Development ## Medium priority
- [ ] Battery on digital watchface
- [ ] More watchfaces!
- [InfiniTime Vision](doc/InfiniTimeVision.md) ## Lower priority
- [Rough structure of the code](doc/code/Intro.md) - [ ] Temp sensor w/ arduino
- [How to implement an application](doc/code/Apps.md) - [ ] Homeassistant control
- [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)
### Contributing
- [How to contribute?](CONTRIBUTING.md)
- [Coding conventions](doc/coding-convention.md)
### Build, flash and debug
- [InfiniTime simulator](https://github.com/InfiniTimeOrg/InfiniSim)
- [Build the project](doc/buildAndProgram.md)
- [Build the project with Docker](doc/buildWithDocker.md)
- [Build the project with VSCode](doc/buildWithVScode.md)
- [Flash the firmware using OpenOCD and STLinkV2](doc/openOCD.md)
- [Flash the firmware using SWD interface](doc/SWD.md)
- [Flash the firmware using JLink](doc/jlink.md)
- [Flash the firmware using GDB](doc/gdb.md)
- [Stub using NRF52-DK](doc/PinetimeStubWithNrf52DK.md)
### API
- [BLE implementation and API](doc/ble.md)
### Architecture and technical topics
- [Memory analysis](doc/MemoryAnalysis.md)
### Project management
- [Maintainer's guide](doc/maintainer-guide.md)
- [Versioning](doc/versioning.md)
- [Project branches](doc/branches.md)
- [Files included in the release notes](doc/filesInReleaseNotes.md)
- [Files needed by the factory](doc/files-needed-by-factory.md)
## Licenses
This project is released under the GNU General Public License version 3 or, at your option, any later version.
It integrates the following projects:
- RTOS : **[FreeRTOS](https://freertos.org)** under the MIT license
- UI : **[LittleVGL/LVGL](https://lvgl.io/)** under the MIT license
- BLE stack : **[NimBLE](https://github.com/apache/mynewt-nimble)** under the Apache 2.0 license
- Font : **[Jetbrains Mono](https://www.jetbrains.com/fr-fr/lp/mono/)** under the Apache 2.0 license
## Credits
Im not working alone on this project. First, many people create PR for this project. Then, there is the whole #pinetime community : a lot of people all around the world who are hacking, searching, experimenting and programming the Pinetime. We exchange our ideas, experiments and code in the chat rooms and forums.
Here are some people I would like to highlight:
- [Atc1441](https://github.com/atc1441/) : He works on an Arduino based firmware for the Pinetime and many other smartwatches based on similar hardware. He was of great help when I was implementing support for the BMA421 motion sensor and I²C driver.
- [Koen](https://github.com/bosmoment) : Hes working on a firmware based on RiotOS. He integrated similar libs as me : NimBLE, LittleVGL,… His help was invaluable too!
- [Lup Yuen Lee](https://github.com/lupyuen) : He is everywhere: he works on a Rust firmware, builds a MCUBoot based bootloader for the Pinetime, designs a Flutter based companion app for smartphones and writes a lot of articles about the Pinetime!

88
README_orig.md Normal file
View File

@ -0,0 +1,88 @@
# [InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime)
![InfiniTime logo](doc/logo/infinitime-logo-small.jpg "InfiniTime Logo")
Fast open-source firmware for the [PineTime smartwatch](https://www.pine64.org/pinetime/) with many features, written in modern C++.
## New to InfiniTime?
- [Getting started with InfiniTime](doc/gettingStarted/gettingStarted-1.0.md)
- [Updating the software](doc/gettingStarted/updating-software.md)
- [About the firmware and bootloader](doc/gettingStarted/about-software.md)
- [PineTimeStyle Watch face](https://wiki.pine64.org/wiki/PineTimeStyle)
- [Weather integration](https://wiki.pine64.org/wiki/Infinitime-Weather)
### Companion apps
- [Gadgetbridge](https://gadgetbridge.org/) (Android)
- [AmazFish](https://openrepos.net/content/piggz/amazfish/) (SailfishOS)
- [Siglo](https://github.com/alexr4535/siglo) (Linux)
- [InfiniLink](https://github.com/InfiniTimeOrg/InfiniLink) (iOS) **[Looking for a new maintainer]**
- [ITD](https://gitea.elara.ws/Elara6331/itd) (Linux)
- [WatchMate](https://github.com/azymohliad/watchmate) (Linux)
***Note** : We removed mentions to NRFConnect as this app is closed source and recent versions do not work anymore with InfiniTime (the last version known to work is 4.24.3). If you used NRFConnect in the past, we recommend you switch to [Gadgetbridge](https://gadgetbridge.org/).*
## Development
- [InfiniTime Vision](doc/InfiniTimeVision.md)
- [Rough structure of the code](doc/code/Intro.md)
- [How to implement an application](doc/code/Apps.md)
- [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)
### Contributing
- [How to contribute?](CONTRIBUTING.md)
- [Coding conventions](doc/coding-convention.md)
### Build, flash and debug
- [InfiniTime simulator](https://github.com/InfiniTimeOrg/InfiniSim)
- [Build the project](doc/buildAndProgram.md)
- [Build the project with Docker](doc/buildWithDocker.md)
- [Build the project with VSCode](doc/buildWithVScode.md)
- [Flash the firmware using OpenOCD and STLinkV2](doc/openOCD.md)
- [Flash the firmware using SWD interface](doc/SWD.md)
- [Flash the firmware using JLink](doc/jlink.md)
- [Flash the firmware using GDB](doc/gdb.md)
- [Stub using NRF52-DK](doc/PinetimeStubWithNrf52DK.md)
### API
- [BLE implementation and API](doc/ble.md)
### Architecture and technical topics
- [Memory analysis](doc/MemoryAnalysis.md)
### Project management
- [Maintainer's guide](doc/maintainer-guide.md)
- [Versioning](doc/versioning.md)
- [Project branches](doc/branches.md)
- [Files included in the release notes](doc/filesInReleaseNotes.md)
- [Files needed by the factory](doc/files-needed-by-factory.md)
## Licenses
This project is released under the GNU General Public License version 3 or, at your option, any later version.
It integrates the following projects:
- RTOS : **[FreeRTOS](https://freertos.org)** under the MIT license
- UI : **[LittleVGL/LVGL](https://lvgl.io/)** under the MIT license
- BLE stack : **[NimBLE](https://github.com/apache/mynewt-nimble)** under the Apache 2.0 license
- Font : **[Jetbrains Mono](https://www.jetbrains.com/fr-fr/lp/mono/)** under the Apache 2.0 license
## Credits
Im not working alone on this project. First, many people create PR for this project. Then, there is the whole #pinetime community : a lot of people all around the world who are hacking, searching, experimenting and programming the Pinetime. We exchange our ideas, experiments and code in the chat rooms and forums.
Here are some people I would like to highlight:
- [Atc1441](https://github.com/atc1441/) : He works on an Arduino based firmware for the Pinetime and many other smartwatches based on similar hardware. He was of great help when I was implementing support for the BMA421 motion sensor and I²C driver.
- [Koen](https://github.com/bosmoment) : Hes working on a firmware based on RiotOS. He integrated similar libs as me : NimBLE, LittleVGL,… His help was invaluable too!
- [Lup Yuen Lee](https://github.com/lupyuen) : He is everywhere: he works on a Rust firmware, builds a MCUBoot based bootloader for the Pinetime, designs a Flutter based companion app for smartphones and writes a lot of articles about the Pinetime!

View File

@ -33,7 +33,7 @@ For cloning the repo, see [these instructions](../doc/buildAndProgram.md#clone-t
```bash ```bash
cd <project_root> # e.g. cd ./work/Pinetime cd <project_root> # e.g. cd ./work/Pinetime
docker run --rm -it -v ${PWD}:/sources --user $(id -u):$(id -g) infinitime-build docker run --rm -it -v ${PWD}:/sources --user $(id -u):$(id -g) infinitime/infinitime-build
``` ```
By default, the container runs as `root`, which is not convenient as all the files generated by the build will also belong to `root`. By default, the container runs as `root`, which is not convenient as all the files generated by the build will also belong to `root`.
@ -45,7 +45,7 @@ This means calling the script explicitly as it will override the `CMD`.
Here's an example for `pinetime-app`: Here's an example for `pinetime-app`:
```bash ```bash
docker run --rm -it -v ${PWD}:/sources --user $(id -u):$(id -g) infinitime-build /opt/build.sh pinetime-app docker run --rm -it -v ${PWD}:/sources --user $(id -u):$(id -g) infinitime/infinitime-build /opt/build.sh pinetime-app
``` ```
## Using the image from Docker Hub ## Using the image from Docker Hub

94
enviroment_setup.md Executable file
View File

@ -0,0 +1,94 @@
## Clone repositories
1. Clone InfiniTime:
```bash
git clone https://github.com/InfiniTimeOrg/InfiniTime.git
cd InfiniTime
git submodule update --init
```
2. Clone InfiniSim:
```bash
cd .. #Ignore this line if you already cd'd into the diretory you want it cloned into
git clone --recursive https://github.com/InfiniTimeOrg/InfiniSim.git
```
## Install dependencies
### For building InfiniTime
1. Install Docker
2. Pull docker container (optional, container will be pulled when running the docker container if not pulled now).
```bash
sudo docker pull infinitime/infinitime-build
```
### For building InfiniSim
This was the most difficult part for me to get right since the steps on the github page are not entirely complete for my setup. If you encounter errors while building, it's probably because one of the dependencies are missing or have the wrong version installed. If you do have an error while building, check out the issues on github and see if you can find some help there: https://github.com/InfiniTimeOrg/InfiniSim/issues
1. The apt install part. This is almost exactly the same as what is on the github page except for npm. When I added npm in to here, I got an older version that didn't work. We'll install the correct version in step 2.
```bash
sudo apt install -y cmake libsdl2-dev g++ libpng-dev
```
2. Install the correct node version. (At least v14.0.0 is required). Commands from https://github.com/nodesource/distributions First, choose the node version by running one of these lines:
```bash
NODE_MAJOR=16
NODE_MAJOR=18
NODE_MAJOR=20
```
I'm not sure which is the best to use, I went with 16 since that is the closest to the version required by InfiniSim, but I doubt it makes much of a difference. Once you've done that, then install it with these commands:
```bash
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
NODE_MAJOR=20
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
sudo apt-get update
sudo apt-get install nodejs -y
```
3. Install npm packages
```bash
npm install lv_font_conv@1.5.2 -g
npm install ts-node@10.9.1 @swc/core@1.3.82 lv_img_conv@0.3.0 -g
```
## Build InfiniSim
### Building with stock InfiniTime
Now to see if we did everything the way our computer wants it. First cd into the directory where InfinSim was cloned (`cd InfiniSim` if you are still in the directory you started in). Now you should be able to build InfiniSim with the InfiniTime version that was included with InfiniSim:
```bash
cmake -S . -B build
cmake --build build -j4
```
If that didn't produce any error, now run it!
```bash
./build/infinisim
```
### Building with your own version of InfiniTime
To test your own modifications of InfiniTime:
```bash
cmake -S . -B build -DInfiniTime_DIR=../InfiniTime
cmake --build build -j4
```
Assuming that the git clone command was run in the same directory for both InfiniSim and InfiniTime. If not, replace `../InfiniTime` with the path to the directory where InfiniTime was cloned to.
And now run it if there were no errors!
```bash
./build/infinisim
```
## Build InfiniTime
Now is the fun part, building your own version of InfiniTime and flashing it to your watch!
First, cd into the directory into which InfiniTime cloned (`cd ~/InfiniTime` if you cloned from your home directory). Now run the docker container (for more info on this command, see https://github.com/InfiniTimeOrg/InfiniTime/blob/main/doc/buildWithDocker.md):
```bash
sudo docker run --rm -it -v ${PWD}:/sources --user $(id -u):$(id -g) infinitime/infinitime-build
```
If that doesn't have any errors, it will save the files to build/output/. The file you will want to flash to your watch is `pinetime-mcuboot-app-dfu-x.x.x.zip`. I found a pretty nice program called WatchMate that you can use to flash it from linux, but I think you can also use Gadgetbridge to flash the file. In WatchMate, just scroll down to the "Firmware Version" dropdown and click on "Upload from file" >
"Firmware" and select the zip file mentioned above. Once that's done, you may also want to send it the `infinitime-resources-x.x.x.zip` if some features are greyed out on the watch (for me it was a couple watch faces and the navigation app that were greyed out). In WatchMate, you'll want to use the "Resources" option instead of the "Firmware" option to upload this file.
## Celebrate
There you go, you've just flashed your own custom os to your watch! If you got all this far without running into any error, you have something to celebrate about as it took me a couple of days to get to this point. If you did have some errors and figured them out, then great, you've got it all figured out now!
One final thing, I would recommend making use of git commits and branches so that you can revert if you totally mess something up and don't have to clone the entire repository and dependencies again like I had to do several times before I decided to start doing that.

View File

@ -393,6 +393,7 @@ list(APPEND SOURCE_FILES
displayapp/screens/ApplicationList.cpp displayapp/screens/ApplicationList.cpp
displayapp/screens/Notifications.cpp displayapp/screens/Notifications.cpp
displayapp/screens/Twos.cpp displayapp/screens/Twos.cpp
displayapp/screens/Calculator.cpp
displayapp/screens/HeartRate.cpp displayapp/screens/HeartRate.cpp
displayapp/screens/Motion.cpp displayapp/screens/Motion.cpp
displayapp/screens/FlashLight.cpp displayapp/screens/FlashLight.cpp
@ -425,6 +426,7 @@ list(APPEND SOURCE_FILES
displayapp/screens/settings/SettingChimes.cpp displayapp/screens/settings/SettingChimes.cpp
displayapp/screens/settings/SettingShakeThreshold.cpp displayapp/screens/settings/SettingShakeThreshold.cpp
displayapp/screens/settings/SettingBluetooth.cpp displayapp/screens/settings/SettingBluetooth.cpp
displayapp/screens/settings/SettingQuickR.cpp
## Watch faces ## Watch faces
displayapp/screens/WatchFaceAnalog.cpp displayapp/screens/WatchFaceAnalog.cpp
@ -433,6 +435,7 @@ list(APPEND SOURCE_FILES
displayapp/screens/WatchFaceTerminal.cpp displayapp/screens/WatchFaceTerminal.cpp
displayapp/screens/WatchFacePineTimeStyle.cpp displayapp/screens/WatchFacePineTimeStyle.cpp
displayapp/screens/WatchFaceCasioStyleG7710.cpp displayapp/screens/WatchFaceCasioStyleG7710.cpp
displayapp/screens/WatchFaceFace.cpp
## ##

View File

@ -5,14 +5,58 @@
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
namespace {
TimerHandle_t vibTimer;
void PatternStep(TimerHandle_t xTimer) {
/* Vibration pattern format:
* {
* durationOfVibration,
* durationOfPause,
* durationOfVibration,
* durationOfPause,
* ...,
* durationOfVibration,
* zeroTerminator
* }
*
* Patterns can be any length
* The pattern must end with a duration of vibration and a terminator.
*/
static constexpr uint8_t vibrationPattern[] = {10, 100, 50, 200, 10, 0};
static size_t patternPosition = 0;
if (vibrationPattern[patternPosition] != 0 && xTimerChangePeriod(vibTimer, vibrationPattern[patternPosition] << 1, 0) == pdPASS &&
xTimerStart(vibTimer, 0) == pdPASS) {
if (patternPosition % 2 == 0) {
nrf_gpio_pin_clear(Pinetime::PinMap::Motor);
} else {
nrf_gpio_pin_set(Pinetime::PinMap::Motor);
}
patternPosition++;
} else {
patternPosition = 0;
nrf_gpio_pin_set(Pinetime::PinMap::Motor);
auto* motorController = static_cast<MotorController*>(pvTimerGetTimerID(xTimer));
motorController->PatternFinished();
}
}
}
void MotorController::Init() { void MotorController::Init() {
nrf_gpio_cfg_output(PinMap::Motor); nrf_gpio_cfg_output(PinMap::Motor);
nrf_gpio_pin_set(PinMap::Motor); nrf_gpio_pin_set(PinMap::Motor);
vibTimer = xTimerCreate("vibration", 1, pdFALSE, this, PatternStep);
shortVib = xTimerCreate("shortVib", 1, pdFALSE, nullptr, StopMotor); shortVib = xTimerCreate("shortVib", 1, pdFALSE, nullptr, StopMotor);
longVib = xTimerCreate("longVib", pdMS_TO_TICKS(1000), pdTRUE, this, Ring); longVib = xTimerCreate("longVib", pdMS_TO_TICKS(1000), pdTRUE, this, Ring);
} }
void MotorController::PatternFinished() {
patternPlaying = false;
}
void MotorController::Ring(TimerHandle_t xTimer) { void MotorController::Ring(TimerHandle_t xTimer) {
auto* motorController = static_cast<MotorController*>(pvTimerGetTimerID(xTimer)); auto* motorController = static_cast<MotorController*>(pvTimerGetTimerID(xTimer));
motorController->RunForDuration(50); motorController->RunForDuration(50);
@ -24,6 +68,15 @@ void MotorController::RunForDuration(uint8_t motorDuration) {
} }
} }
bool MotorController::StartPattern() {
if (!patternPlaying) {
patternPlaying = true;
PatternStep(vibTimer);
return true;
}
return false;
}
void MotorController::StartRinging() { void MotorController::StartRinging() {
RunForDuration(50); RunForDuration(50);
xTimerStart(longVib, 0); xTimerStart(longVib, 0);

View File

@ -15,10 +15,13 @@ namespace Pinetime {
void RunForDuration(uint8_t motorDuration); void RunForDuration(uint8_t motorDuration);
void StartRinging(); void StartRinging();
void StopRinging(); void StopRinging();
void PatternFinished();
bool StartPattern();
private: private:
static void Ring(TimerHandle_t xTimer); static void Ring(TimerHandle_t xTimer);
static void StopMotor(TimerHandle_t xTimer); static void StopMotor(TimerHandle_t xTimer);
bool patternPlaying;
TimerHandle_t shortVib; TimerHandle_t shortVib;
TimerHandle_t longVib; TimerHandle_t longVib;
}; };

View File

@ -13,6 +13,7 @@ namespace Pinetime {
enum class Notification : uint8_t { On, Off, Sleep }; enum class Notification : uint8_t { On, Off, Sleep };
enum class ChimesOption : uint8_t { None, Hours, HalfHours }; enum class ChimesOption : uint8_t { None, Hours, HalfHours };
enum class WakeUpMode : uint8_t { SingleTap = 0, DoubleTap = 1, RaiseWrist = 2, Shake = 3, LowerWrist = 4 }; enum class WakeUpMode : uint8_t { SingleTap = 0, DoubleTap = 1, RaiseWrist = 2, Shake = 3, LowerWrist = 4 };
enum class QuickApp : uint8_t { MusicPlayer = 0, Calculator = 1, Alarm = 2, Timer = 3, HeartRate = 4 }; //, Alarm = 5, Timer = 6, Stopwatch = 7 };
enum class Colors : uint8_t { enum class Colors : uint8_t {
White, White,
Silver, Silver,
@ -241,6 +242,14 @@ namespace Pinetime {
return getWakeUpModes()[static_cast<size_t>(mode)]; return getWakeUpModes()[static_cast<size_t>(mode)];
} }
void SetBrightness(Controllers::BrightnessController::Levels level) { void SetBrightness(Controllers::BrightnessController::Levels level) {
if (level != settings.brightLevel) { if (level != settings.brightLevel) {
settingsChanged = true; settingsChanged = true;
@ -271,6 +280,26 @@ namespace Pinetime {
return bleRadioEnabled; return bleRadioEnabled;
}; };
// New Settings
void SetQuickRModes(QuickApp App_now, bool enabled) {
if (enabled != isQuickROn(App_now)) {
settingsChanged = true;
}
settings.quickApp.set(static_cast<size_t>(App_now), enabled);
};
std::bitset<5> getQuickRModes() const {
return settings.quickApp;
}
bool isQuickROn(const QuickApp app_holder) const {
return getQuickRModes()[static_cast<size_t>(app_holder)];
}
private: private:
Pinetime::Controllers::FS& fs; Pinetime::Controllers::FS& fs;
@ -279,6 +308,7 @@ namespace Pinetime {
struct SettingsData { struct SettingsData {
uint32_t version = settingsVersion; uint32_t version = settingsVersion;
uint32_t stepsGoal = 10000; uint32_t stepsGoal = 10000;
uint32_t setquickr = 10000;
uint32_t screenTimeOut = 15000; uint32_t screenTimeOut = 15000;
ClockType clockType = ClockType::H24; ClockType clockType = ClockType::H24;
@ -292,6 +322,7 @@ namespace Pinetime {
WatchFaceInfineat watchFaceInfineat; WatchFaceInfineat watchFaceInfineat;
std::bitset<5> wakeUpMode {0}; std::bitset<5> wakeUpMode {0};
std::bitset<5> quickApp {0};
uint16_t shakeWakeThreshold = 150; uint16_t shakeWakeThreshold = 150;
Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium; Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium;

View File

@ -37,7 +37,9 @@ namespace Pinetime {
SettingChimes, SettingChimes,
SettingShakeThreshold, SettingShakeThreshold,
SettingBluetooth, SettingBluetooth,
Error Error,
Calculator,
SettingQuickR
}; };
} }
} }

View File

@ -31,6 +31,9 @@
#include "displayapp/screens/Error.h" #include "displayapp/screens/Error.h"
#include "displayapp/screens/Weather.h" #include "displayapp/screens/Weather.h"
// My New Apps
#include "displayapp/screens/Calculator.h"
#include "drivers/Cst816s.h" #include "drivers/Cst816s.h"
#include "drivers/St7789.h" #include "drivers/St7789.h"
#include "drivers/Watchdog.h" #include "drivers/Watchdog.h"
@ -48,6 +51,7 @@
#include "displayapp/screens/settings/SettingChimes.h" #include "displayapp/screens/settings/SettingChimes.h"
#include "displayapp/screens/settings/SettingShakeThreshold.h" #include "displayapp/screens/settings/SettingShakeThreshold.h"
#include "displayapp/screens/settings/SettingBluetooth.h" #include "displayapp/screens/settings/SettingBluetooth.h"
#include "displayapp/screens/settings/SettingQuickR.h"
#include "libs/lv_conf.h" #include "libs/lv_conf.h"
@ -289,17 +293,29 @@ void DisplayApp::Refresh() {
return TouchEvents::SwipeLeft; return TouchEvents::SwipeLeft;
} }
}; };
if (!currentScreen->OnTouchEvent(gesture)) { if (!currentScreen->OnTouchEvent(gesture)) {
if (currentApp == Apps::Clock) { if (currentApp == Apps::Clock || currentApp == Apps::QuickSettings || (currentApp == Apps::Music && quick_app[0]) || (currentApp == Apps::Calculator && quick_app[1]) || (currentApp == Apps::Alarm && quick_app[2]) || (currentApp == Apps::Timer && quick_app[3]) || (currentApp == Apps::HeartRate && quick_app[4])) {
switch (gesture) { switch (gesture) {
case TouchEvents::SwipeUp: case TouchEvents::SwipeUp:
if (currentApp == Apps::Clock) {
LoadNewScreen(Apps::Launcher, DisplayApp::FullRefreshDirections::Up); LoadNewScreen(Apps::Launcher, DisplayApp::FullRefreshDirections::Up);
} else if (currentApp == Apps::QuickSettings) {
LoadNewScreen(Apps::Settings, DisplayApp::FullRefreshDirections::Up);
}
break; break;
case TouchEvents::SwipeDown: case TouchEvents::SwipeDown:
if (currentApp == Apps::Clock) {
LoadNewScreen(Apps::Notifications, DisplayApp::FullRefreshDirections::Down); LoadNewScreen(Apps::Notifications, DisplayApp::FullRefreshDirections::Down);
} else if (currentApp == Apps::QuickSettings) {
LoadNewScreen(Apps::FlashLight, DisplayApp::FullRefreshDirections::Down);
}
break; break;
case TouchEvents::SwipeRight: case TouchEvents::SwipeRight:
LoadNewScreen(Apps::QuickSettings, DisplayApp::FullRefreshDirections::RightAnim); gotoquickapp(-1);
break;
case TouchEvents::SwipeLeft:
gotoquickapp(1);
break; break;
case TouchEvents::DoubleTap: case TouchEvents::DoubleTap:
PushMessageToSystemTask(System::Messages::GoToSleep); PushMessageToSystemTask(System::Messages::GoToSleep);
@ -318,6 +334,7 @@ void DisplayApp::Refresh() {
if (!currentScreen->OnButtonPushed()) { if (!currentScreen->OnButtonPushed()) {
if (currentApp == Apps::Clock) { if (currentApp == Apps::Clock) {
PushMessageToSystemTask(System::Messages::GoToSleep); PushMessageToSystemTask(System::Messages::GoToSleep);
} else { } else {
LoadPreviousScreen(); LoadPreviousScreen();
} }
@ -358,7 +375,18 @@ void DisplayApp::Refresh() {
break; break;
case Messages::Chime: case Messages::Chime:
LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None); LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None);
motorController.RunForDuration(35); time_var = dateTimeController.Minutes();
if (time_var == 30){
NRF_LOG_INFO("Short: %d", time_var);
motorController.StartPattern();
}
else
{
NRF_LOG_INFO("Long: %d", time_var);
motorController.RunForDuration(200);
}
break; break;
case Messages::OnChargingEvent: case Messages::OnChargingEvent:
RestoreBrightness(); RestoreBrightness();
@ -411,6 +439,7 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
// break; // break;
case Apps::None: case Apps::None:
case Apps::Clock: case Apps::Clock:
currentQ_app = 2;
currentScreen = std::make_unique<Screens::Clock>(dateTimeController, currentScreen = std::make_unique<Screens::Clock>(dateTimeController,
batteryController, batteryController,
bleController, bleController,
@ -548,6 +577,13 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
case Apps::Steps: case Apps::Steps:
currentScreen = std::make_unique<Screens::Steps>(motionController, settingsController); currentScreen = std::make_unique<Screens::Steps>(motionController, settingsController);
break; break;
// My Apps
case Apps::Calculator:
currentScreen = std::make_unique<Screens::Calculator>();
break;
case Apps::SettingQuickR:
currentScreen = std::make_unique<Screens::SettingQuickR>(settingsController);
break;
} }
currentApp = app; currentApp = app;
} }
@ -607,3 +643,84 @@ void DisplayApp::ApplyBrightness() {
} }
brightnessController.Set(brightness); brightnessController.Set(brightness);
} }
void DisplayApp::gotoquickapp(int app_step){
NRF_LOG_INFO("QuickRing Swiped");
currentQ_app += app_step;
quick_app = settingsController.getQuickRModes();
while(true)
{
if(currentQ_app > 7)
{
currentQ_app = 1;
} else if(currentQ_app < 1)
{
currentQ_app = 7;
}
if(currentQ_app != 1 && currentQ_app != 2)
{
if(quick_app[currentQ_app-3])
{
break;
}
else
{
currentQ_app += (app_step);
}
}
else
{
break;
}
}
int quickringtotal = 2;
for(int i = 0; i < 5; i++){
quickringtotal += (quick_app[i] ? 1 : 0 );
}
NRF_LOG_INFO("case number current Q: %i", currentQ_app);
Apps app = Apps::Clock;
switch (currentQ_app) {
case 1:
app = Apps::QuickSettings;
break;
case 2:
app = Apps::Clock;
break;
case 3:
app = Apps::Music;
break;
case 4:
app = Apps::Calculator;
break;
case 5:
app = Apps::Alarm;
break;
case 6:
app = Apps::Timer;
break;
case 7:
app = Apps::HeartRate;
break;
default:
break;
}
if(app_step < 0){
LoadNewScreen(app, DisplayApp::FullRefreshDirections::RightAnim);
}
else
{
LoadNewScreen(app, DisplayApp::FullRefreshDirections::LeftAnim);
}
//appStackDirections.Pop();
//returnAppStack.Pop();
}

View File

@ -118,6 +118,8 @@ namespace Pinetime {
void LoadNewScreen(Apps app, DisplayApp::FullRefreshDirections direction); void LoadNewScreen(Apps app, DisplayApp::FullRefreshDirections direction);
void LoadScreen(Apps app, DisplayApp::FullRefreshDirections direction); void LoadScreen(Apps app, DisplayApp::FullRefreshDirections direction);
void PushMessageToSystemTask(Pinetime::System::Messages message); void PushMessageToSystemTask(Pinetime::System::Messages message);
void gotoquickapp(int app_step);
Apps nextApp = Apps::None; Apps nextApp = Apps::None;
DisplayApp::FullRefreshDirections nextDirection; DisplayApp::FullRefreshDirections nextDirection;
@ -128,7 +130,10 @@ namespace Pinetime {
Utility::StaticStack<Apps, returnAppStackSize> returnAppStack; Utility::StaticStack<Apps, returnAppStackSize> returnAppStack;
Utility::StaticStack<FullRefreshDirections, returnAppStackSize> appStackDirections; Utility::StaticStack<FullRefreshDirections, returnAppStackSize> appStackDirections;
int time_var;
bool isDimmed = false; bool isDimmed = false;
int currentQ_app = 2;
std::bitset<5> quick_app;
}; };
} }
} }

View File

@ -9,6 +9,7 @@ namespace Pinetime {
Terminal = 3, Terminal = 3,
Infineat = 4, Infineat = 4,
CasioStyleG7710 = 5, CasioStyleG7710 = 5,
FaceFace = 6,
}; };
} }
} }

View File

@ -7,7 +7,7 @@
}, },
{ {
"file": "FontAwesome5-Solid+Brands+Regular.woff", "file": "FontAwesome5-Solid+Brands+Regular.woff",
"range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c" "range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf1ec, 0xf55a"
} }
], ],
"bpp": 1, "bpp": 1,

View File

@ -55,7 +55,8 @@ namespace Pinetime {
{"2", Apps::Twos, true}, {"2", Apps::Twos, true},
{Symbols::drum, Apps::Metronome, true}, {Symbols::drum, Apps::Metronome, true},
{Symbols::map, Apps::Navigation, Applications::Screens::Navigation::IsAvailable(filesystem)}, {Symbols::map, Apps::Navigation, Applications::Screens::Navigation::IsAvailable(filesystem)},
{Symbols::none, Apps::None, false}, {Symbols::calculator, Apps::Calculator, true},
//{Symbols::none, Apps::None, false},
// {"M", Apps::Motion}, // {"M", Apps::Motion},
}}; }};

View File

@ -0,0 +1,442 @@
#include <cmath>
#include <cinttypes>
#include <libraries/log/nrf_log.h>
#include "Calculator.h"
#include "displayapp/InfiniTimeTheme.h"
#include "Symbols.h"
using namespace Pinetime::Applications::Screens;
static void eventHandler(lv_obj_t* obj, lv_event_t event) {
auto app = static_cast<Calculator*>(obj->user_data);
app->OnButtonEvent(obj, event);
}
Calculator::~Calculator() {
lv_obj_clean(lv_scr_act());
}
static const char* buttonMap[] = {
"7", "8", "9", "^", "\n", "4", "5", "6", "+ -", "\n", "1", "2", "3", "* /", "\n", "0", ".", "(-)", "=", ""};
Calculator::Calculator() {
resultLabel = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_long_mode(resultLabel, LV_LABEL_LONG_CROP);
lv_label_set_align(resultLabel, LV_LABEL_ALIGN_RIGHT);
lv_label_set_text_fmt(resultLabel, "%" PRId64, result);
lv_obj_set_size(resultLabel, 200, 20);
lv_obj_set_pos(resultLabel, -30, 5);
valueLabel = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_long_mode(valueLabel, LV_LABEL_LONG_CROP);
lv_label_set_align(valueLabel, LV_LABEL_ALIGN_RIGHT);
lv_label_set_text_fmt(valueLabel, "%" PRId64, value);
lv_obj_set_size(valueLabel, 200, 20);
lv_obj_set_pos(valueLabel, -30, 35);
buttonMatrix = lv_btnmatrix_create(lv_scr_act(), nullptr);
buttonMatrix->user_data = this;
lv_obj_set_event_cb(buttonMatrix, eventHandler);
lv_btnmatrix_set_map(buttonMatrix, buttonMap);
lv_btnmatrix_set_one_check(buttonMatrix, true);
lv_obj_set_size(buttonMatrix, 238, 180);
lv_obj_set_style_local_bg_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_DEFAULT, Colors::bgAlt);
lv_obj_set_style_local_pad_inner(buttonMatrix, LV_BTNMATRIX_PART_BG, LV_STATE_DEFAULT, 1);
lv_obj_set_style_local_pad_top(buttonMatrix, LV_BTNMATRIX_PART_BG, LV_STATE_DEFAULT, 1);
lv_obj_set_style_local_pad_bottom(buttonMatrix, LV_BTNMATRIX_PART_BG, LV_STATE_DEFAULT, 1);
lv_obj_set_style_local_pad_left(buttonMatrix, LV_BTNMATRIX_PART_BG, LV_STATE_DEFAULT, 1);
lv_obj_set_style_local_pad_right(buttonMatrix, LV_BTNMATRIX_PART_BG, LV_STATE_DEFAULT, 1);
lv_obj_align(buttonMatrix, nullptr, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
lv_obj_set_style_local_bg_opa(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, LV_OPA_COVER);
lv_obj_set_style_local_bg_grad_stop(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, 128);
lv_obj_set_style_local_bg_main_stop(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, 128);
// BackSpace Button
back = lv_btn_create(lv_scr_act(), nullptr);
back->user_data = this;
lv_obj_set_event_cb(back, eventHandler);
lv_obj_set_size(back, 59, 44);
lv_obj_align(back, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 179, 16);
txtback = lv_label_create(back, nullptr);
lv_obj_set_style_local_bg_color(back, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt);
lv_label_set_text_static(txtback, Symbols::backspace);
}
void Calculator::OnButtonEvent(lv_obj_t* obj, lv_event_t event) {
if ((obj == buttonMatrix) && (event == LV_EVENT_PRESSED)) {
HandleInput();
}
if (obj == back && event == LV_EVENT_PRESSED) {
if (value != 0) {
// delete one value digit
if (offset < FIXED_POINT_OFFSET) {
if (offset == 0) {
offset = 1;
} else {
offset *= 10;
}
} else {
value /= 10;
}
if (offset < FIXED_POINT_OFFSET) {
value -= value % (10 * offset);
} else {
value -= value % offset;
}
} else if (offset < FIXED_POINT_OFFSET) {
if (offset == 0) {
offset = 1;
} else {
offset *= 10;
}
} else {
// reset the result
result = 0;
}
operation = ' ';
UpdateOperation();
UpdateValueLabel();
UpdateResultLabel();
}
}
void Calculator::HandleInput() {
const char* buttonText = lv_btnmatrix_get_active_btn_text(buttonMatrix);
if (buttonText == nullptr) {
return;
}
if ((equalSignPressed && (*buttonText != '=')) || (error != Error::None)) {
ResetInput();
UpdateOperation();
}
// we only compare the first char because it is enough
switch (*buttonText) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
// *buttonText is the first char in buttonText
// "- '0'" results in the int value of the char
auto digit = (*buttonText) - '0';
auto sign = (value < 0) ? -1 : 1;
// if this is true, we already pressed the . button
if (offset < FIXED_POINT_OFFSET) {
value += sign * offset * digit;
offset /= 10;
} else if (value <= MAX_VALUE / 10) {
value *= 10;
value += sign * offset * digit;
}
NRF_LOG_INFO(". offset: %" PRId64, offset);
NRF_LOG_INFO(". value: %" PRId64, value);
NRF_LOG_INFO(". result: %" PRId64, result);
} break;
// unary minus
case '(':
value = -value;
NRF_LOG_INFO(". offset: %" PRId64, offset);
NRF_LOG_INFO(". value: %" PRId64, value);
NRF_LOG_INFO(". result: %" PRId64, result);
break;
// Decimal
case '.':
if (offset == FIXED_POINT_OFFSET) {
offset /= 10;
}
NRF_LOG_INFO(". offset: %" PRId64, offset);
NRF_LOG_INFO(". value: %" PRId64, value);
NRF_LOG_INFO(". result: %" PRId64, result);
break;
// for every operator we:
// - eval the current operator if value > FIXED_POINT_OFFSET
// - then set the new operator
// - + and - as well as * and / cycle on the same button
case '^':
if (value != 0) {
Eval();
ResetInput();
}
switch (operation) {
case ' ':
operation = '^';
break;
default:
operation = ' ';
break;
}
UpdateOperation();
break;
case '+':
if (value != 0) {
Eval();
ResetInput();
}
switch (operation) {
case '+':
operation = '-';
break;
case '-':
operation = ' ';
break;
default:
operation = '+';
break;
}
UpdateOperation();
break;
case '*':
if (value != 0) {
Eval();
ResetInput();
}
switch (operation) {
case '*':
operation = '/';
break;
case '/':
operation = ' ';
break;
default:
operation = '*';
break;
}
UpdateOperation();
break;
case '=':
equalSignPressed = true;
Eval();
// If the operation is ' ' then we move the value to the result.
// We reset the input after this.
// This seems more convenient.
if (operation == ' ') {
ResetInput();
}
NRF_LOG_INFO(". offset: %" PRId64, offset);
NRF_LOG_INFO(". value: %" PRId64, value);
NRF_LOG_INFO(". result: %" PRId64, result);
break;
}
NRF_LOG_INFO("Operation End: %c", operation);
UpdateValueLabel();
UpdateResultLabel();
}
void Calculator::UpdateOperation() const {
switch (operation) {
case '+':
lv_obj_set_style_local_bg_grad_dir(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, LV_GRAD_DIR_HOR);
lv_obj_set_style_local_bg_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::deepOrange);
lv_obj_set_style_local_bg_grad_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::bgAlt);
lv_btnmatrix_set_btn_ctrl(buttonMatrix, 7, LV_BTNMATRIX_CTRL_CHECK_STATE);
break;
case '-':
lv_obj_set_style_local_bg_grad_dir(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, LV_GRAD_DIR_HOR);
lv_obj_set_style_local_bg_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::bgAlt);
lv_obj_set_style_local_bg_grad_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::deepOrange);
lv_btnmatrix_set_btn_ctrl(buttonMatrix, 7, LV_BTNMATRIX_CTRL_CHECK_STATE);
break;
case '*':
lv_obj_set_style_local_bg_grad_dir(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, LV_GRAD_DIR_HOR);
lv_obj_set_style_local_bg_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::deepOrange);
lv_obj_set_style_local_bg_grad_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::bgAlt);
lv_btnmatrix_set_btn_ctrl(buttonMatrix, 11, LV_BTNMATRIX_CTRL_CHECK_STATE);
break;
case '/':
lv_obj_set_style_local_bg_grad_dir(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, LV_GRAD_DIR_HOR);
lv_obj_set_style_local_bg_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::bgAlt);
lv_obj_set_style_local_bg_grad_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::deepOrange);
lv_btnmatrix_set_btn_ctrl(buttonMatrix, 11, LV_BTNMATRIX_CTRL_CHECK_STATE);
break;
case '^':
lv_obj_set_style_local_bg_grad_dir(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, LV_GRAD_DIR_HOR);
lv_obj_set_style_local_bg_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::deepOrange);
lv_obj_set_style_local_bg_grad_color(buttonMatrix, LV_BTNMATRIX_PART_BTN, LV_STATE_CHECKED, Colors::deepOrange);
lv_btnmatrix_set_btn_ctrl(buttonMatrix, 3, LV_BTNMATRIX_CTRL_CHECK_STATE);
break;
default:
lv_btnmatrix_clear_btn_ctrl_all(buttonMatrix, LV_BTNMATRIX_CTRL_CHECK_STATE);
break;
}
}
void Calculator::ResetInput() {
value = 0;
offset = FIXED_POINT_OFFSET;
operation = ' ';
equalSignPressed = false;
error = Error::None;
}
void Calculator::UpdateResultLabel() const {
int64_t integer = result / FIXED_POINT_OFFSET;
int64_t remainder = result % FIXED_POINT_OFFSET;
bool negative = (remainder < 0);
if (remainder == 0) {
lv_label_set_text_fmt(resultLabel, "%" PRId64, integer);
return;
}
if (remainder < 0) {
remainder = -remainder;
}
uint8_t min_width = N_DECIMALS;
// cut "0"-digits on the right
while ((remainder > 0) && (remainder % 10 == 0)) {
remainder /= 10;
min_width--;
}
if ((integer == 0) && negative) {
lv_label_set_text_fmt(resultLabel, "-0.%0*" PRId64, min_width, remainder);
} else {
lv_label_set_text_fmt(resultLabel, "%" PRId64 ".%0*" PRId64, integer, min_width, remainder);
}
}
void Calculator::UpdateValueLabel() {
switch (error) {
case Error::TooLarge:
lv_label_set_text_static(valueLabel, "too large");
break;
case Error::ZeroDivision:
lv_label_set_text_static(valueLabel, "zero division");
break;
case Error::None:
default: {
int64_t integer = value / FIXED_POINT_OFFSET;
int64_t remainder = value % FIXED_POINT_OFFSET;
bool negative = (remainder < 0);
int64_t printRemainder = remainder < 0 ? -remainder : remainder;
uint8_t min_width = 0;
int64_t tmp_offset = offset;
// TODO there has to be a simpler way to do this
if (tmp_offset == 0) {
tmp_offset = 1;
min_width = 1;
}
while (tmp_offset < FIXED_POINT_OFFSET) {
tmp_offset *= 10;
min_width++;
}
min_width--;
for (uint8_t i = min_width; i < N_DECIMALS; i++) {
printRemainder /= 10;
}
if ((integer == 0) && negative) {
lv_label_set_text_fmt(valueLabel, "-0.%0*" PRId64, min_width, printRemainder);
} else if (offset == FIXED_POINT_OFFSET) {
lv_label_set_text_fmt(valueLabel, "%" PRId64, integer);
} else if ((offset == (FIXED_POINT_OFFSET / 10)) && (remainder == 0)) {
lv_label_set_text_fmt(valueLabel, "%" PRId64 ".", integer);
} else {
lv_label_set_text_fmt(valueLabel, "%" PRId64 ".%0*" PRId64, integer, min_width, printRemainder);
}
} break;
}
}
// update the result based on value and operation
void Calculator::Eval() {
switch (operation) {
case ' ':
result = value;
break;
case '+':
// check for overflow
if (((result > 0) && (value > (MAX_VALUE - result))) || ((result < 0) && (value < (MIN_VALUE - result)))) {
error = Error::TooLarge;
break;
}
result += value;
break;
case '-':
// check for overflow
if (((result < 0) && (value > (MAX_VALUE + result))) || ((result > 0) && (value < (MIN_VALUE + result)))) {
error = Error::TooLarge;
break;
}
result -= value;
break;
case '*':
// check for overflow
// while dividing we eliminate the fixed point offset
// therefore we have to multiply it again for the comparison with value
// we also assume here that MAX_VALUE == -MIN_VALUE
if ((result != 0) && (std::abs(value) > (FIXED_POINT_OFFSET * (MAX_VALUE / std::abs(result))))) {
error = Error::TooLarge;
break;
}
result *= value;
// fixed point offset was multiplied too
result /= FIXED_POINT_OFFSET;
break;
case '/':
// check for zero division
if (value == 0) {
error = Error::ZeroDivision;
break;
}
// fixed point offset will be divided too
result *= FIXED_POINT_OFFSET;
result /= value;
break;
case '^':
// check for zero division
if (value == 0) {
error = Error::ZeroDivision;
break;
}
place_holder = result;
place_holder /= FIXED_POINT_OFFSET;
result = pow(place_holder, (value*0.0001))*10000;
break;
default:
break;
}
}

View File

@ -0,0 +1,71 @@
#pragma once
#include "Screen.h"
namespace {
int64_t constexpr powi(int64_t base, uint8_t exponent) {
int64_t value = 1;
while (exponent) {
value *= base;
exponent--;
}
return value;
}
}
namespace Pinetime {
namespace Applications {
namespace Screens {
class Calculator : public Screen {
public:
~Calculator() override;
Calculator();
void OnButtonEvent(lv_obj_t* obj, lv_event_t event);
private:
lv_obj_t* buttonMatrix {};
lv_obj_t* valueLabel {};
lv_obj_t* resultLabel {};
lv_obj_t* back;
lv_obj_t* txtback;
double place_holder;
void Eval();
void ResetInput();
void HandleInput();
void UpdateValueLabel();
void UpdateResultLabel() const;
void UpdateOperation() const;
// change this if you want to change the number of decimals
static constexpr uint8_t N_DECIMALS = 4;
// this is the constant default offset
static constexpr int64_t FIXED_POINT_OFFSET = powi(10, N_DECIMALS);
// this is the current offset, may wary after pressing '.'
int64_t offset = FIXED_POINT_OFFSET;
// the screen can show 12 chars
// but two are needed for '.' and '-'
static constexpr uint8_t MAX_DIGITS = 15;
static constexpr int64_t MAX_VALUE = powi(10, MAX_DIGITS) - 1;
// this is assumed in the multiplication overflow!
static constexpr int64_t MIN_VALUE = -MAX_VALUE;
int64_t value = 0;
int64_t result = 0;
char operation = ' ';
bool equalSignPressed = false;
enum Error {
TooLarge,
ZeroDivision,
None,
};
Error error = Error::None;
};
}
}
}

View File

@ -13,6 +13,7 @@
#include "displayapp/screens/WatchFaceAnalog.h" #include "displayapp/screens/WatchFaceAnalog.h"
#include "displayapp/screens/WatchFacePineTimeStyle.h" #include "displayapp/screens/WatchFacePineTimeStyle.h"
#include "displayapp/screens/WatchFaceCasioStyleG7710.h" #include "displayapp/screens/WatchFaceCasioStyleG7710.h"
#include "displayapp/screens/WatchFaceFace.h"
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
using namespace Pinetime::Applications; using namespace Pinetime::Applications;
@ -55,6 +56,9 @@ Clock::Clock(Controllers::DateTime& dateTimeController,
case WatchFace::CasioStyleG7710: case WatchFace::CasioStyleG7710:
return WatchFaceCasioStyleG7710(); return WatchFaceCasioStyleG7710();
break; break;
case WatchFace::FaceFace:
return WatchFaceFaceScreen();
break;
} }
return WatchFaceDigitalScreen(); return WatchFaceDigitalScreen();
}()} { }()} {
@ -127,7 +131,15 @@ std::unique_ptr<Screen> Clock::WatchFaceCasioStyleG7710() {
bleController, bleController,
notificationManager, notificationManager,
settingsController, settingsController,
heartRateController,
motionController, motionController,
filesystem); filesystem);
} }
std::unique_ptr<Screen> Clock::WatchFaceFaceScreen() {
return std::make_unique<Screens::WatchFaceFace>(dateTimeController,
batteryController,
bleController,
notificationManager,
settingsController);
}

View File

@ -54,6 +54,7 @@ namespace Pinetime {
std::unique_ptr<Screen> WatchFaceTerminalScreen(); std::unique_ptr<Screen> WatchFaceTerminalScreen();
std::unique_ptr<Screen> WatchFaceInfineatScreen(); std::unique_ptr<Screen> WatchFaceInfineatScreen();
std::unique_ptr<Screen> WatchFaceCasioStyleG7710(); std::unique_ptr<Screen> WatchFaceCasioStyleG7710();
std::unique_ptr<Screen> WatchFaceFaceScreen();
}; };
} }
} }

View File

@ -2,6 +2,7 @@
#include "displayapp/DisplayApp.h" #include "displayapp/DisplayApp.h"
#include "displayapp/screens/Symbols.h" #include "displayapp/screens/Symbols.h"
#include "displayapp/InfiniTimeTheme.h" #include "displayapp/InfiniTimeTheme.h"
#include <libraries/log/nrf_log.h>
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
@ -47,6 +48,8 @@ FlashLight::FlashLight(System::SystemTask& systemTask, Controllers::BrightnessCo
lv_obj_set_event_cb(backgroundAction, EventHandler); lv_obj_set_event_cb(backgroundAction, EventHandler);
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping); systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
Toggle();
} }
FlashLight::~FlashLight() { FlashLight::~FlashLight() {
@ -56,8 +59,17 @@ FlashLight::~FlashLight() {
} }
void FlashLight::SetColors() { void FlashLight::SetColors() {
lv_color_t bgColor = isOn ? LV_COLOR_WHITE : LV_COLOR_BLACK; lv_color_t bgColor = LV_COLOR_BLACK;
lv_color_t fgColor = isOn ? Colors::lightGray : LV_COLOR_WHITE; if(State_l == 0){
bgColor = LV_COLOR_BLACK;
} else if (State_l == 1){
bgColor = LV_COLOR_WHITE;
}
else {
bgColor = lv_color_hex(0xff0000);
}
lv_color_t fgColor = State_l ? Colors::lightGray : LV_COLOR_WHITE;
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, bgColor); lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, bgColor);
lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, fgColor); lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, fgColor);
@ -84,9 +96,11 @@ void FlashLight::SetIndicators() {
} }
void FlashLight::Toggle() { void FlashLight::Toggle() {
isOn = !isOn; State_l++;
if(State_l > 2)
State_l = 0;
SetColors(); SetColors();
if (isOn) { if (State_l) {
brightnessController.Set(brightnessLevel); brightnessController.Set(brightnessLevel);
} else { } else {
brightnessController.Set(Controllers::BrightnessController::Levels::Low); brightnessController.Set(Controllers::BrightnessController::Levels::Low);
@ -97,7 +111,7 @@ bool FlashLight::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
auto SetState = [this]() { auto SetState = [this]() {
if (isOn) { if (State_l) {
brightnessController.Set(brightnessLevel); brightnessController.Set(brightnessLevel);
} }
SetIndicators(); SetIndicators();

View File

@ -26,12 +26,13 @@ namespace Pinetime {
Pinetime::System::SystemTask& systemTask; Pinetime::System::SystemTask& systemTask;
Controllers::BrightnessController& brightnessController; Controllers::BrightnessController& brightnessController;
Controllers::BrightnessController::Levels brightnessLevel = Controllers::BrightnessController::Levels::High; Controllers::BrightnessController::Levels brightnessLevel = Controllers::BrightnessController::Levels::High;
lv_obj_t* flashLight; lv_obj_t* flashLight;
lv_obj_t* backgroundAction; lv_obj_t* backgroundAction;
lv_obj_t* indicators[3]; lv_obj_t* indicators[3];
bool isOn = false; int State_l = 0;
}; };
} }
} }

View File

@ -47,6 +47,8 @@ inline void lv_img_set_src_arr(lv_obj_t* img, const lv_img_dsc_t* src_img) {
* *
* TODO: Investigate Apple Media Service and AVRCPv1.6 support for seamless integration * TODO: Investigate Apple Media Service and AVRCPv1.6 support for seamless integration
*/ */
Music::Music(Pinetime::Controllers::MusicService& music) : musicService(music) { Music::Music(Pinetime::Controllers::MusicService& music) : musicService(music) {
lv_obj_t* label; lv_obj_t* label;
@ -250,6 +252,13 @@ void Music::OnObjectEvent(lv_obj_t* obj, lv_event_t event) {
bool Music::OnTouchEvent(Pinetime::Applications::TouchEvents event) { bool Music::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
switch (event) { switch (event) {
case TouchEvents::SwipeDown: {
lv_obj_set_hidden(btnNext, false);
lv_obj_set_hidden(btnPrev, false);
lv_obj_set_hidden(btnVolDown, true);
lv_obj_set_hidden(btnVolUp, true);
return true;
}
case TouchEvents::SwipeUp: { case TouchEvents::SwipeUp: {
lv_obj_set_hidden(btnVolDown, false); lv_obj_set_hidden(btnVolDown, false);
lv_obj_set_hidden(btnVolUp, false); lv_obj_set_hidden(btnVolUp, false);
@ -258,24 +267,6 @@ bool Music::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
lv_obj_set_hidden(btnPrev, true); lv_obj_set_hidden(btnPrev, true);
return true; return true;
} }
case TouchEvents::SwipeDown: {
if (lv_obj_get_hidden(btnNext)) {
lv_obj_set_hidden(btnNext, false);
lv_obj_set_hidden(btnPrev, false);
lv_obj_set_hidden(btnVolDown, true);
lv_obj_set_hidden(btnVolUp, true);
return true;
}
return false;
}
case TouchEvents::SwipeLeft: {
musicService.event(Controllers::MusicService::EVENT_MUSIC_NEXT);
return true;
}
case TouchEvents::SwipeRight: {
musicService.event(Controllers::MusicService::EVENT_MUSIC_PREV);
return true;
}
default: { default: {
return false; return false;
} }

View File

@ -18,6 +18,7 @@
#pragma once #pragma once
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include "displayapp/DisplayApp.h"
#include <lvgl/src/lv_core/lv_obj.h> #include <lvgl/src/lv_core/lv_obj.h>
#include <string> #include <string>
#include "displayapp/screens/Screen.h" #include "displayapp/screens/Screen.h"

View File

@ -37,6 +37,10 @@ namespace Pinetime {
static constexpr const char* eye = "\xEF\x81\xAE"; static constexpr const char* eye = "\xEF\x81\xAE";
static constexpr const char* home = "\xEF\x80\x95"; static constexpr const char* home = "\xEF\x80\x95";
static constexpr const char* sleep = "\xEE\xBD\x84"; static constexpr const char* sleep = "\xEE\xBD\x84";
static constexpr const char* calculator = "\xEF\x87\xAC";
static constexpr const char* backspace = "\xEF\x95\x9A";
// fontawesome_weathericons.c // fontawesome_weathericons.c
// static constexpr const char* sun = "\xEF\x86\x85"; // static constexpr const char* sun = "\xEF\x86\x85";

View File

@ -9,7 +9,6 @@
#include "components/battery/BatteryController.h" #include "components/battery/BatteryController.h"
#include "components/ble/BleController.h" #include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h" #include "components/ble/NotificationManager.h"
#include "components/heartrate/HeartRateController.h"
#include "components/motion/MotionController.h" #include "components/motion/MotionController.h"
#include "components/settings/Settings.h" #include "components/settings/Settings.h"
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
@ -19,7 +18,6 @@ WatchFaceCasioStyleG7710::WatchFaceCasioStyleG7710(Controllers::DateTime& dateTi
const Controllers::Ble& bleController, const Controllers::Ble& bleController,
Controllers::NotificationManager& notificatioManager, Controllers::NotificationManager& notificatioManager,
Controllers::Settings& settingsController, Controllers::Settings& settingsController,
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController, Controllers::MotionController& motionController,
Controllers::FS& filesystem) Controllers::FS& filesystem)
: currentDateTime {{}}, : currentDateTime {{}},
@ -29,7 +27,6 @@ WatchFaceCasioStyleG7710::WatchFaceCasioStyleG7710(Controllers::DateTime& dateTi
bleController {bleController}, bleController {bleController},
notificatioManager {notificatioManager}, notificatioManager {notificatioManager},
settingsController {settingsController}, settingsController {settingsController},
heartRateController {heartRateController},
motionController {motionController} { motionController {motionController} {
lfs_file f = {}; lfs_file f = {};
@ -148,25 +145,20 @@ WatchFaceCasioStyleG7710::WatchFaceCasioStyleG7710(Controllers::DateTime& dateTi
lv_obj_set_pos(backgroundLabel, 0, 0); lv_obj_set_pos(backgroundLabel, 0, 0);
lv_label_set_text_static(backgroundLabel, ""); lv_label_set_text_static(backgroundLabel, "");
heartbeatIcon = lv_label_create(lv_scr_act(), nullptr); stepIcon = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(heartbeatIcon, Symbols::heartBeat); lv_label_set_text_static(stepIcon, Symbols::shoe);
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text); lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2); lv_obj_align(stepIcon, 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); 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_obj_set_style_local_text_color(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
lv_label_set_text_static(stepValue, "0"); lv_label_set_text_static(stepValue, "");
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2); lv_obj_align(stepValue, stepIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
stepIcon = lv_label_create(lv_scr_act(), nullptr); label_seconds = 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_obj_set_style_local_text_color(label_seconds, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
lv_label_set_text_static(stepIcon, Symbols::shoe); lv_label_set_text_static(label_seconds, "0");
lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0); lv_obj_align(label_seconds, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -9, -2);
taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
Refresh(); Refresh();
@ -222,10 +214,11 @@ void WatchFaceCasioStyleG7710::Refresh() {
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get())); lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
} }
currentDateTime = std::chrono::time_point_cast<std::chrono::minutes>(dateTimeController.CurrentDateTime()); currentDateTime = std::chrono::time_point_cast<std::chrono::seconds>(dateTimeController.CurrentDateTime());
if (currentDateTime.IsUpdated()) { if (currentDateTime.IsUpdated()) {
uint8_t hour = dateTimeController.Hours(); uint8_t hour = dateTimeController.Hours();
uint8_t minute = dateTimeController.Minutes(); uint8_t minute = dateTimeController.Minutes();
uint8_t second = dateTimeController.Seconds();
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) { if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
char ampmChar[2] = "A"; char ampmChar[2] = "A";
@ -244,6 +237,9 @@ void WatchFaceCasioStyleG7710::Refresh() {
} }
lv_obj_realign(label_time); lv_obj_realign(label_time);
lv_label_set_text_fmt(label_seconds, "%02d", second);
lv_obj_realign(label_seconds);
currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get()); currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get());
if (currentDate.IsUpdated()) { if (currentDate.IsUpdated()) {
const char* weekNumberFormat = "%V"; const char* weekNumberFormat = "%V";
@ -289,26 +285,11 @@ void WatchFaceCasioStyleG7710::Refresh() {
} }
} }
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(); stepCount = motionController.NbSteps();
if (stepCount.IsUpdated()) { if (stepCount.IsUpdated()) {
lv_label_set_text_fmt(stepValue, "%lu", stepCount.Get()); lv_label_set_text_fmt(stepValue, "%lu", stepCount.Get());
lv_obj_realign(stepValue); lv_obj_realign(stepValue);
lv_obj_realign(stepIcon); lv_obj_realign(stepValue);
} }
} }

View File

@ -16,7 +16,6 @@ namespace Pinetime {
class Battery; class Battery;
class Ble; class Ble;
class NotificationManager; class NotificationManager;
class HeartRateController;
class MotionController; class MotionController;
} }
@ -30,7 +29,6 @@ namespace Pinetime {
const Controllers::Ble& bleController, const Controllers::Ble& bleController,
Controllers::NotificationManager& notificatioManager, Controllers::NotificationManager& notificatioManager,
Controllers::Settings& settingsController, Controllers::Settings& settingsController,
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController, Controllers::MotionController& motionController,
Controllers::FS& filesystem); Controllers::FS& filesystem);
~WatchFaceCasioStyleG7710() override; ~WatchFaceCasioStyleG7710() override;
@ -44,10 +42,8 @@ namespace Pinetime {
Utility::DirtyValue<bool> powerPresent {}; Utility::DirtyValue<bool> powerPresent {};
Utility::DirtyValue<bool> bleState {}; Utility::DirtyValue<bool> bleState {};
Utility::DirtyValue<bool> bleRadioEnabled {}; Utility::DirtyValue<bool> bleRadioEnabled {};
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>> currentDateTime {}; Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> currentDateTime {};
Utility::DirtyValue<uint32_t> stepCount {}; Utility::DirtyValue<uint32_t> stepCount {};
Utility::DirtyValue<uint8_t> heartbeat {};
Utility::DirtyValue<bool> heartbeatRunning {};
Utility::DirtyValue<bool> notificationState {}; Utility::DirtyValue<bool> notificationState {};
using days = std::chrono::duration<int32_t, std::ratio<86400>>; // TODO: days is standard in c++20 using days = std::chrono::duration<int32_t, std::ratio<86400>>; // TODO: days is standard in c++20
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate; Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate;
@ -64,6 +60,7 @@ namespace Pinetime {
lv_style_t style_border; lv_style_t style_border;
lv_obj_t* label_time; lv_obj_t* label_time;
lv_obj_t* label_seconds;
lv_obj_t* line_time; lv_obj_t* line_time;
lv_obj_t* label_time_ampm; lv_obj_t* label_time_ampm;
lv_obj_t* label_date; lv_obj_t* label_date;
@ -77,8 +74,6 @@ namespace Pinetime {
lv_obj_t* bleIcon; lv_obj_t* bleIcon;
lv_obj_t* batteryPlug; lv_obj_t* batteryPlug;
lv_obj_t* label_battery_value; lv_obj_t* label_battery_value;
lv_obj_t* heartbeatIcon;
lv_obj_t* heartbeatValue;
lv_obj_t* stepIcon; lv_obj_t* stepIcon;
lv_obj_t* stepValue; lv_obj_t* stepValue;
lv_obj_t* notificationIcon; lv_obj_t* notificationIcon;
@ -91,7 +86,6 @@ namespace Pinetime {
const Controllers::Ble& bleController; const Controllers::Ble& bleController;
Controllers::NotificationManager& notificatioManager; Controllers::NotificationManager& notificatioManager;
Controllers::Settings& settingsController; Controllers::Settings& settingsController;
Controllers::HeartRateController& heartRateController;
Controllers::MotionController& motionController; Controllers::MotionController& motionController;
lv_task_t* taskRefresh; lv_task_t* taskRefresh;

View File

@ -35,6 +35,10 @@ WatchFaceDigital::WatchFaceDigital(Controllers::DateTime& dateTimeController,
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(false)); lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(false));
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0); lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
label_seconds = lv_label_create(lv_scr_act(), nullptr); // for secs
lv_obj_align(label_seconds, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, -55);
lv_obj_set_style_local_text_color(label_seconds, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999));
label_date = lv_label_create(lv_scr_act(), nullptr); label_date = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_CENTER, 0, 60); lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_CENTER, 0, 60);
lv_obj_set_style_local_text_color(label_date, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999)); lv_obj_set_style_local_text_color(label_date, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999));
@ -44,10 +48,6 @@ WatchFaceDigital::WatchFaceDigital(Controllers::DateTime& dateTimeController,
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0); lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0);
label_time_ampm = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(label_time_ampm, "");
lv_obj_align(label_time_ampm, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -30, -55);
heartbeatIcon = lv_label_create(lv_scr_act(), nullptr); heartbeatIcon = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(heartbeatIcon, Symbols::heartBeat); lv_label_set_text_static(heartbeatIcon, Symbols::heartBeat);
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xCE1B1B)); lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xCE1B1B));
@ -85,23 +85,19 @@ void WatchFaceDigital::Refresh() {
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get())); lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
} }
currentDateTime = std::chrono::time_point_cast<std::chrono::minutes>(dateTimeController.CurrentDateTime()); currentDateTime = std::chrono::time_point_cast<std::chrono::seconds>(dateTimeController.CurrentDateTime());
if (currentDateTime.IsUpdated()) { if (currentDateTime.IsUpdated()) {
uint8_t hour = dateTimeController.Hours(); uint8_t hour = dateTimeController.Hours();
uint8_t minute = dateTimeController.Minutes(); uint8_t minute = dateTimeController.Minutes();
uint8_t second = dateTimeController.Seconds();
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) { if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
char ampmChar[3] = "AM";
if (hour == 0) { if (hour == 0) {
hour = 12; hour = 12;
} else if (hour == 12) {
ampmChar[0] = 'P';
} else if (hour > 12) { } else if (hour > 12) {
hour = 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_label_set_text_fmt(label_time, "%2d:%02d", hour, minute);
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0); lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0);
} else { } else {
@ -109,6 +105,8 @@ void WatchFaceDigital::Refresh() {
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_CENTER, 0, 0); lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
} }
lv_label_set_text_fmt(label_seconds, "%02d", second); // for secs
currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get()); currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get());
if (currentDate.IsUpdated()) { if (currentDate.IsUpdated()) {
uint16_t year = dateTimeController.Year(); uint16_t year = dateTimeController.Year();

View File

@ -44,7 +44,7 @@ namespace Pinetime {
Utility::DirtyValue<bool> powerPresent {}; Utility::DirtyValue<bool> powerPresent {};
Utility::DirtyValue<bool> bleState {}; Utility::DirtyValue<bool> bleState {};
Utility::DirtyValue<bool> bleRadioEnabled {}; Utility::DirtyValue<bool> bleRadioEnabled {};
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>> currentDateTime {}; Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> currentDateTime {};
Utility::DirtyValue<uint32_t> stepCount {}; Utility::DirtyValue<uint32_t> stepCount {};
Utility::DirtyValue<uint8_t> heartbeat {}; Utility::DirtyValue<uint8_t> heartbeat {};
Utility::DirtyValue<bool> heartbeatRunning {}; Utility::DirtyValue<bool> heartbeatRunning {};
@ -53,7 +53,8 @@ namespace Pinetime {
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate; Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate;
lv_obj_t* label_time; lv_obj_t* label_time;
lv_obj_t* label_time_ampm; lv_obj_t* label_seconds; //for secs
lv_obj_t* batteryValue;
lv_obj_t* label_date; lv_obj_t* label_date;
lv_obj_t* heartbeatIcon; lv_obj_t* heartbeatIcon;
lv_obj_t* heartbeatValue; lv_obj_t* heartbeatValue;

View File

@ -0,0 +1,274 @@
#include "displayapp/screens/WatchFaceFace.h"
#include <cmath>
#include <lvgl/lvgl.h>
#include "displayapp/screens/BatteryIcon.h"
#include "displayapp/screens/BleIcon.h"
#include "displayapp/screens/Symbols.h"
#include "displayapp/screens/NotificationIcon.h"
#include "components/settings/Settings.h"
#include "displayapp/InfiniTimeTheme.h"
using namespace Pinetime::Applications::Screens;
namespace {
constexpr int16_t HourLength = 100;
constexpr int16_t MinuteLength = 90;
constexpr int16_t SecondLength = 110;
// sin(90) = 1 so the value of _lv_trigo_sin(90) is the scaling factor
const auto LV_TRIG_SCALE = _lv_trigo_sin(90);
int16_t Cosine(int16_t angle) {
return _lv_trigo_sin(angle + 90);
}
int16_t Sine(int16_t angle) {
return _lv_trigo_sin(angle);
}
int16_t CoordinateXRelocate(int16_t x) {
return (x + LV_HOR_RES / 2);
}
int16_t CoordinateYRelocate(int16_t y) {
return std::abs(y - LV_HOR_RES / 2);
}
lv_point_t CoordinateRelocate(int16_t radius, int16_t angle) {
return lv_point_t {.x = CoordinateXRelocate(radius * static_cast<int32_t>(Sine(angle)) / LV_TRIG_SCALE),
.y = CoordinateYRelocate(radius * static_cast<int32_t>(Cosine(angle)) / LV_TRIG_SCALE)};
}
}
WatchFaceFace::WatchFaceFace(Controllers::DateTime& dateTimeController,
const Controllers::Battery& batteryController,
const Controllers::Ble& bleController,
Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController)
: currentDateTime {{}},
batteryIcon(true),
dateTimeController {dateTimeController},
batteryController {batteryController},
bleController {bleController},
notificationManager {notificationManager},
settingsController {settingsController} {
sHour = 99;
sMinute = 99;
sSecond = 99;
mouth = lv_arc_create(lv_scr_act(), nullptr);
lv_arc_set_angles(mouth, 30, 110);
lv_arc_set_bg_angles(mouth, 45, 135);
lv_obj_set_size(mouth, 130, 130);
lv_obj_align(mouth, nullptr, LV_ALIGN_CENTER, 0, 0);
leye = lv_arc_create(lv_scr_act(), nullptr);
lv_arc_set_angles(leye, 0, 360);
lv_obj_set_size(leye, 30, 30);
lv_obj_align(leye, nullptr, LV_ALIGN_CENTER, -35, -35);
reye = lv_arc_create(lv_scr_act(), nullptr);
lv_arc_set_angles(reye, 0, 360);
lv_obj_set_size(reye, 30, 30);
lv_obj_align(reye, nullptr, LV_ALIGN_CENTER, 35, -35);
minor_scales = lv_linemeter_create(lv_scr_act(), nullptr);
lv_linemeter_set_scale(minor_scales, 300, 51);
lv_linemeter_set_angle_offset(minor_scales, 180);
lv_obj_set_size(minor_scales, 240, 240);
lv_obj_align(minor_scales, nullptr, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_local_bg_opa(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_obj_set_style_local_scale_width(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 4);
lv_obj_set_style_local_scale_end_line_width(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 1);
lv_obj_set_style_local_scale_end_color(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
major_scales = lv_linemeter_create(lv_scr_act(), nullptr);
lv_linemeter_set_scale(major_scales, 300, 11);
lv_linemeter_set_angle_offset(major_scales, 180);
lv_obj_set_size(major_scales, 240, 240);
lv_obj_align(major_scales, nullptr, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_local_bg_opa(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_obj_set_style_local_scale_width(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 6);
lv_obj_set_style_local_scale_end_line_width(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 4);
lv_obj_set_style_local_scale_end_color(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
large_scales = lv_linemeter_create(lv_scr_act(), nullptr);
lv_linemeter_set_scale(large_scales, 180, 3);
lv_linemeter_set_angle_offset(large_scales, 180);
lv_obj_set_size(large_scales, 240, 240);
lv_obj_align(large_scales, nullptr, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_local_bg_opa(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
lv_obj_set_style_local_scale_width(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 20);
lv_obj_set_style_local_scale_end_line_width(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 4);
lv_obj_set_style_local_scale_end_color(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_AQUA);
twelve = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_align(twelve, LV_LABEL_ALIGN_CENTER);
lv_label_set_text_static(twelve, "12");
lv_obj_set_pos(twelve, 110, 10);
lv_obj_set_style_local_text_color(twelve, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_AQUA);
batteryIcon.Create(lv_scr_act());
lv_obj_align(batteryIcon.GetObject(), nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 0);
plugIcon = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(plugIcon, Symbols::plug);
lv_obj_align(plugIcon, nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 0);
bleIcon = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(bleIcon, "");
lv_obj_align(bleIcon, nullptr, LV_ALIGN_IN_TOP_RIGHT, -30, 0);
notificationIcon = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_LIME);
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(false));
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
// Date - Day / Week day
label_date_day = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_color(label_date_day, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::orange);
lv_label_set_text_fmt(label_date_day, "%s\n%02i", dateTimeController.DayOfWeekShortToString(), dateTimeController.Day());
lv_label_set_align(label_date_day, LV_LABEL_ALIGN_CENTER);
lv_obj_align(label_date_day, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
minute_body = lv_line_create(lv_scr_act(), nullptr);
minute_body_trace = lv_line_create(lv_scr_act(), nullptr);
hour_body = lv_line_create(lv_scr_act(), nullptr);
hour_body_trace = lv_line_create(lv_scr_act(), nullptr);
second_body = lv_line_create(lv_scr_act(), nullptr);
lv_style_init(&second_line_style);
lv_style_set_line_width(&second_line_style, LV_STATE_DEFAULT, 3);
lv_style_set_line_color(&second_line_style, LV_STATE_DEFAULT, LV_COLOR_RED);
lv_style_set_line_rounded(&second_line_style, LV_STATE_DEFAULT, true);
lv_obj_add_style(second_body, LV_LINE_PART_MAIN, &second_line_style);
lv_style_init(&minute_line_style);
lv_style_set_line_width(&minute_line_style, LV_STATE_DEFAULT, 3);
lv_style_set_line_color(&minute_line_style, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_line_rounded(&minute_line_style, LV_STATE_DEFAULT, true);
lv_obj_add_style(minute_body, LV_LINE_PART_MAIN, &minute_line_style);
lv_style_init(&minute_line_style_trace);
lv_style_set_line_width(&minute_line_style_trace, LV_STATE_DEFAULT, 3);
lv_style_set_line_color(&minute_line_style_trace, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_line_rounded(&minute_line_style_trace, LV_STATE_DEFAULT, false);
lv_obj_add_style(minute_body_trace, LV_LINE_PART_MAIN, &minute_line_style_trace);
lv_style_init(&hour_line_style);
lv_style_set_line_width(&hour_line_style, LV_STATE_DEFAULT, 7);
lv_style_set_line_color(&hour_line_style, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_line_rounded(&hour_line_style, LV_STATE_DEFAULT, true);
lv_obj_add_style(hour_body, LV_LINE_PART_MAIN, &hour_line_style);
lv_style_init(&hour_line_style_trace);
lv_style_set_line_width(&hour_line_style_trace, LV_STATE_DEFAULT, 3);
lv_style_set_line_color(&hour_line_style_trace, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_style_set_line_rounded(&hour_line_style_trace, LV_STATE_DEFAULT, false);
lv_obj_add_style(hour_body_trace, LV_LINE_PART_MAIN, &hour_line_style_trace);
taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
Refresh();
}
WatchFaceFace::~WatchFaceFace() {
lv_task_del(taskRefresh);
lv_style_reset(&hour_line_style);
lv_style_reset(&hour_line_style_trace);
lv_style_reset(&minute_line_style);
lv_style_reset(&minute_line_style_trace);
lv_style_reset(&second_line_style);
lv_obj_clean(lv_scr_act());
}
void WatchFaceFace::UpdateClock() {
uint8_t hour = dateTimeController.Hours();
uint8_t minute = dateTimeController.Minutes();
uint8_t second = dateTimeController.Seconds();
if (sMinute != minute) {
auto const angle = minute * 6;
minute_point[0] = CoordinateRelocate(10, angle - 90);
minute_point[1] = CoordinateRelocate(MinuteLength, angle);
minute_point[2] = CoordinateRelocate(10, angle + 90);
lv_line_set_points(minute_body, minute_point, 3);
}
if (sHour != hour || sMinute != minute) {
sHour = hour;
sMinute = minute;
auto const angle = (hour * 30 + minute / 2);
hour_point[0] = CoordinateRelocate(90, angle);
hour_point[1] = CoordinateRelocate(HourLength, angle);
lv_line_set_points(hour_body, hour_point, 2);
}
if (sSecond != second) {
sSecond = second;
auto const angle = second * 6;
second_point[0] = CoordinateRelocate(105, angle);
second_point[1] = CoordinateRelocate(SecondLength, angle);
lv_line_set_points(second_body, second_point, 2);
}//TODO: redo seconds
}
void WatchFaceFace::SetBatteryIcon() {
auto batteryPercent = batteryPercentRemaining.Get();
batteryIcon.SetBatteryPercentage(batteryPercent);
}
void WatchFaceFace::Refresh() {
isCharging = batteryController.IsCharging();
if (isCharging.IsUpdated()) {
if (isCharging.Get()) {
lv_obj_set_hidden(batteryIcon.GetObject(), true);
lv_obj_set_hidden(plugIcon, false);
} else {
lv_obj_set_hidden(batteryIcon.GetObject(), false);
lv_obj_set_hidden(plugIcon, true);
SetBatteryIcon();
}
}
if (!isCharging.Get()) {
batteryPercentRemaining = batteryController.PercentRemaining();
if (batteryPercentRemaining.IsUpdated()) {
SetBatteryIcon();
}
}
bleState = bleController.IsConnected();
if (bleState.IsUpdated()) {
if (bleState.Get()) {
lv_label_set_text_static(bleIcon, Symbols::bluetooth);
} else {
lv_label_set_text_static(bleIcon, "");
}
}
notificationState = notificationManager.AreNewNotificationsAvailable();
if (notificationState.IsUpdated()) {
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
}
currentDateTime = dateTimeController.CurrentDateTime();
if (currentDateTime.IsUpdated()) {
UpdateClock();
currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get());
if (currentDate.IsUpdated()) {
lv_label_set_text_fmt(label_date_day, "%s\n%02i", dateTimeController.DayOfWeekShortToString(), dateTimeController.Day());
}
}
}

View File

@ -0,0 +1,95 @@
#pragma once
#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/battery/BatteryController.h"
#include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h"
#include "displayapp/screens/BatteryIcon.h"
#include "utility/DirtyValue.h"
namespace Pinetime {
namespace Controllers {
class Settings;
class Battery;
class Ble;
class NotificationManager;
}
namespace Applications {
namespace Screens {
class WatchFaceFace : public Screen {
public:
WatchFaceFace(Controllers::DateTime& dateTimeController,
const Controllers::Battery& batteryController,
const Controllers::Ble& bleController,
Controllers::NotificationManager& notificationManager,
Controllers::Settings& settingsController);
~WatchFaceFace() override;
void Refresh() override;
private:
uint8_t sHour, sMinute, sSecond;
Utility::DirtyValue<uint8_t> batteryPercentRemaining {0};
Utility::DirtyValue<bool> isCharging {};
Utility::DirtyValue<bool> bleState {};
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime;
Utility::DirtyValue<bool> notificationState {false};
using days = std::chrono::duration<int32_t, std::ratio<86400>>; // TODO: days is standard in c++20
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate;
lv_obj_t* minor_scales;
lv_obj_t* major_scales;
lv_obj_t* large_scales;
lv_obj_t* twelve;
lv_obj_t* hour_body;
lv_obj_t* hour_body_trace;
lv_obj_t* minute_body;
lv_obj_t* minute_body_trace;
lv_obj_t* second_body;
lv_point_t hour_point[2];
lv_point_t minute_point[3];
lv_point_t second_point[2];
lv_style_t hour_line_style;
lv_style_t hour_line_style_trace;
lv_style_t minute_line_style;
lv_style_t minute_line_style_trace;
lv_style_t second_line_style;
lv_obj_t* label_date_day;
lv_obj_t* plugIcon;
lv_obj_t* notificationIcon;
lv_obj_t* bleIcon;
lv_obj_t* mouth;
lv_obj_t* leye;
lv_obj_t* reye;
BatteryIcon batteryIcon;
const Controllers::DateTime& dateTimeController;
const Controllers::Battery& batteryController;
const Controllers::Ble& bleController;
Controllers::NotificationManager& notificationManager;
Controllers::Settings& settingsController;
void UpdateClock();
void SetBatteryIcon();
lv_task_t* taskRefresh;
};
}
}
}

View File

@ -10,8 +10,11 @@
#include "components/motion/MotionController.h" #include "components/motion/MotionController.h"
#include "components/settings/Settings.h" #include "components/settings/Settings.h"
using namespace Pinetime::Applications::Screens; using namespace Pinetime::Applications::Screens;
WatchFaceTerminal::WatchFaceTerminal(Controllers::DateTime& dateTimeController, WatchFaceTerminal::WatchFaceTerminal(Controllers::DateTime& dateTimeController,
const Controllers::Battery& batteryController, const Controllers::Battery& batteryController,
const Controllers::Ble& bleController, const Controllers::Ble& bleController,
@ -43,12 +46,14 @@ WatchFaceTerminal::WatchFaceTerminal(Controllers::DateTime& dateTimeController,
lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, -40); lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, -40);
label_prompt_1 = lv_label_create(lv_scr_act(), nullptr); label_prompt_1 = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_recolor(label_prompt_1, true);
lv_obj_align(label_prompt_1, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, -80); lv_obj_align(label_prompt_1, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, -80);
lv_label_set_text_static(label_prompt_1, "user@watch:~ $ now"); lv_label_set_text_static(label_prompt_1, "user@watch:~ #00ff00 $# now");
label_prompt_2 = lv_label_create(lv_scr_act(), nullptr); label_prompt_2 = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_recolor(label_prompt_2, true);
lv_obj_align(label_prompt_2, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 60); lv_obj_align(label_prompt_2, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, 60);
lv_label_set_text_static(label_prompt_2, "user@watch:~ $"); lv_label_set_text_static(label_prompt_2, "user@watch:~ #00ff00 $#");
label_time = lv_label_create(lv_scr_act(), nullptr); label_time = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_recolor(label_time, true); lv_label_set_recolor(label_time, true);
@ -144,6 +149,8 @@ void WatchFaceTerminal::Refresh() {
} }
} }
stepCount = motionController.NbSteps(); stepCount = motionController.NbSteps();
if (stepCount.IsUpdated()) { if (stepCount.IsUpdated()) {
lv_label_set_text_fmt(stepValue, "[STEP]#ee3377 %lu steps#", stepCount.Get()); lv_label_set_text_fmt(stepValue, "[STEP]#ee3377 %lu steps#", stepCount.Get());

View File

@ -0,0 +1,77 @@
#include "displayapp/screens/settings/SettingQuickR.h"
#include <lvgl/lvgl.h>
#include "displayapp/DisplayApp.h"
#include "displayapp/screens/Symbols.h"
#include "displayapp/InfiniTimeTheme.h"
using namespace Pinetime::Applications::Screens;
constexpr std::array<SettingQuickR::Option, 5> SettingQuickR::options;
namespace {
void event_handler(lv_obj_t* obj, lv_event_t event) {
auto* screen = static_cast<SettingQuickR*>(obj->user_data);
if (event == LV_EVENT_VALUE_CHANGED) {
screen->UpdateSelected(obj);
}
}
}
SettingQuickR::SettingQuickR(Pinetime::Controllers::Settings& settingsController) : 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, 35);
lv_obj_set_width(container1, LV_HOR_RES - 20);
lv_obj_set_height(container1, LV_VER_RES - 20);
lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT);
lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr);
lv_label_set_text_static(title, "Quick Ring");
lv_label_set_align(title, LV_LABEL_ALIGN_CENTER);
lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 15, 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::check);
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 < 5; i++) {
cbOption[i] = lv_checkbox_create(container1, nullptr);
lv_checkbox_set_text(cbOption[i], options[i].name);
if (settingsController.isQuickROn(static_cast<Controllers::Settings::QuickApp>(i))) {
lv_checkbox_set_checked(cbOption[i], true);
}
cbOption[i]->user_data = this;
lv_obj_set_event_cb(cbOption[i], event_handler);
}
}
SettingQuickR::~SettingQuickR() {
lv_obj_clean(lv_scr_act());
settingsController.SaveSettings();
}
void SettingQuickR::UpdateSelected(lv_obj_t* object) {
// Find the index of the checkbox that triggered the event
for (size_t i = 0; i < 5; i++) {
if (cbOption[i] == object) {
bool currentState = settingsController.isQuickROn(options[i].quickApp);
settingsController.SetQuickRModes(options[i].quickApp, !currentState);
break;
}
}
// Update checkbox according to current wakeup modes.
// This is needed because we can have extra logic when setting or unsetting wakeup modes,
// for example, when setting SingleTap, DoubleTap is unset and vice versa.
auto modes = settingsController.getQuickRModes();
for (size_t i = 0; i < 5; ++i) {
lv_checkbox_set_checked(cbOption[i], modes[i]);
}
}

View File

@ -0,0 +1,39 @@
#pragma once
#include <array>
#include <cstdint>
#include <lvgl/lvgl.h>
#include "components/settings/Settings.h"
#include "displayapp/screens/Screen.h"
namespace Pinetime {
namespace Applications {
namespace Screens {
class SettingQuickR : public Screen {
public:
SettingQuickR(Pinetime::Controllers::Settings& settingsController);
~SettingQuickR() override;
void UpdateSelected(lv_obj_t* object);
private:
struct Option {
Controllers::Settings::QuickApp quickApp;
const char* name;
};
static constexpr std::array<Option, 5> options = {{
{Controllers::Settings::QuickApp::MusicPlayer, "Music Player"},
{Controllers::Settings::QuickApp::Calculator, "Calculator"},
{Controllers::Settings::QuickApp::Alarm, "Alarms"},
{Controllers::Settings::QuickApp::Timer, "Timer"},
{Controllers::Settings::QuickApp::HeartRate, "Heart Rate"},
}};
lv_obj_t* cbOption[5];
Controllers::Settings& settingsController;
};
}
}
}

View File

@ -11,6 +11,7 @@
#include "displayapp/screens/CheckboxList.h" #include "displayapp/screens/CheckboxList.h"
#include "displayapp/screens/WatchFaceInfineat.h" #include "displayapp/screens/WatchFaceInfineat.h"
#include "displayapp/screens/WatchFaceCasioStyleG7710.h" #include "displayapp/screens/WatchFaceCasioStyleG7710.h"
#include "displayapp/screens/WatchFaceFace.h"
namespace Pinetime { namespace Pinetime {
@ -47,8 +48,9 @@ namespace Pinetime {
{"Terminal", true}, {"Terminal", true},
{"Infineat face", Applications::Screens::WatchFaceInfineat::IsAvailable(filesystem)}, {"Infineat face", Applications::Screens::WatchFaceInfineat::IsAvailable(filesystem)},
{"Casio G7710", Applications::Screens::WatchFaceCasioStyleG7710::IsAvailable(filesystem)}, {"Casio G7710", Applications::Screens::WatchFaceCasioStyleG7710::IsAvailable(filesystem)},
{"", false}, {"Face face", true},
{"", false}}}; {"", false}
}};
ScreenList<nScreens> screens; ScreenList<nScreens> screens;
}; };
} }

View File

@ -29,7 +29,7 @@ namespace Pinetime {
static constexpr int entriesPerScreen = 4; static constexpr int entriesPerScreen = 4;
// Increment this when more space is needed // Increment this when more space is needed
static constexpr int nScreens = 3; static constexpr int nScreens = 4;
static constexpr std::array<List::Applications, entriesPerScreen * nScreens> entries {{ static constexpr std::array<List::Applications, entriesPerScreen * nScreens> entries {{
{Symbols::sun, "Display", Apps::SettingDisplay}, {Symbols::sun, "Display", Apps::SettingDisplay},
@ -40,11 +40,14 @@ namespace Pinetime {
{Symbols::shoe, "Steps", Apps::SettingSteps}, {Symbols::shoe, "Steps", Apps::SettingSteps},
{Symbols::clock, "Date&Time", Apps::SettingSetDateTime}, {Symbols::clock, "Date&Time", Apps::SettingSetDateTime},
{Symbols::batteryHalf, "Battery", Apps::BatteryInfo}, {Symbols::batteryHalf, "Battery", Apps::BatteryInfo},
{Symbols::clock, "Chimes", Apps::SettingChimes}, {Symbols::check, "QuickRing", Apps::SettingQuickR},
{Symbols::clock, "Chimes", Apps::SettingChimes},
{Symbols::tachometer, "Shake Calib.", Apps::SettingShakeThreshold}, {Symbols::tachometer, "Shake Calib.", Apps::SettingShakeThreshold},
{Symbols::check, "Firmware", Apps::FirmwareValidation}, {Symbols::check, "Firmware", Apps::FirmwareValidation},
{Symbols::bluetooth, "Bluetooth", Apps::SettingBluetooth}, {Symbols::bluetooth, "Bluetooth", Apps::SettingBluetooth},
{Symbols::list, "About", Apps::SysInfo}, {Symbols::list, "About", Apps::SysInfo},
// {Symbols::none, "None", Apps::None}, // {Symbols::none, "None", Apps::None},