Compare commits
28 Commits
v14
...
music_play
Author | SHA1 | Date | |
---|---|---|---|
|
858bf4f96e | ||
|
1487a16f4a | ||
|
b488811f7f | ||
|
e19e870290 | ||
|
8a40eaa9c5 | ||
|
c87a297a2f | ||
|
86695db883 | ||
|
c8de75ccd9 | ||
|
163eaf5ab4 | ||
|
21e86c4220 | ||
09f7306e31 | |||
a2da74d93a | |||
e3c7295729 | |||
|
990ad0c87e | ||
|
c74ae787ae | ||
9258331e9c | |||
62e89c1bd3 | |||
1fbd00b43d | |||
4449a48de7 | |||
4e6b1c421f | |||
c04bd3bd7d | |||
|
cbabd8914c | ||
|
5fc831542f | ||
|
b0019da9dd | ||
|
3bb0ac29e2 | ||
|
329e42c5d4 | ||
|
ecb91712fd | ||
e334735697 |
107
README.md
107
README.md
@ -1,88 +1,31 @@
|
||||
# [InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime)
|
||||
# TODO
|
||||
## Completed
|
||||
- [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
|
||||
|
||||
![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++.
|
||||
## Pending merge
|
||||
|
||||
## New to InfiniTime?
|
||||
## In progress
|
||||
### Elijah
|
||||
- [ ] Countdown timer presistant buzz
|
||||
|
||||
- [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)
|
||||
### Josh
|
||||
- [ ] Seconds on digital watchface
|
||||
|
||||
### Companion apps
|
||||
### Moses
|
||||
|
||||
## Medium priority
|
||||
- [ ] Temp sensor w/ arduino
|
||||
- [ ] Homeassistant control
|
||||
- [ ] Periodic heart rate measurement
|
||||
- [ ] Exponent button on calculator
|
||||
|
||||
- [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
|
||||
|
||||
I’m 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) : He’s 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!
|
||||
## Lower priority
|
||||
- [ ] Maybe some prank watchfaces
|
||||
|
88
README_orig.md
Normal file
88
README_orig.md
Normal 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
|
||||
|
||||
I’m 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) : He’s 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!
|
@ -33,7 +33,7 @@ For cloning the repo, see [these instructions](../doc/buildAndProgram.md#clone-t
|
||||
|
||||
```bash
|
||||
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`.
|
||||
@ -45,7 +45,7 @@ This means calling the script explicitly as it will override the `CMD`.
|
||||
Here's an example for `pinetime-app`:
|
||||
|
||||
```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
|
||||
|
@ -393,6 +393,7 @@ list(APPEND SOURCE_FILES
|
||||
displayapp/screens/ApplicationList.cpp
|
||||
displayapp/screens/Notifications.cpp
|
||||
displayapp/screens/Twos.cpp
|
||||
displayapp/screens/Calculator.cpp
|
||||
displayapp/screens/HeartRate.cpp
|
||||
displayapp/screens/Motion.cpp
|
||||
displayapp/screens/FlashLight.cpp
|
||||
@ -425,6 +426,7 @@ list(APPEND SOURCE_FILES
|
||||
displayapp/screens/settings/SettingChimes.cpp
|
||||
displayapp/screens/settings/SettingShakeThreshold.cpp
|
||||
displayapp/screens/settings/SettingBluetooth.cpp
|
||||
displayapp/screens/settings/SettingQuickR.cpp
|
||||
|
||||
## Watch faces
|
||||
displayapp/screens/WatchFaceAnalog.cpp
|
||||
|
@ -13,6 +13,7 @@ namespace Pinetime {
|
||||
enum class Notification : uint8_t { On, Off, Sleep };
|
||||
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 QuickApp : uint8_t { MusicPlayer = 0, Calculator = 1, Alarm = 2, Timer = 3, HeartRate = 4 }; //, Alarm = 5, Timer = 6, Stopwatch = 7 };
|
||||
enum class Colors : uint8_t {
|
||||
White,
|
||||
Silver,
|
||||
@ -241,6 +242,34 @@ namespace Pinetime {
|
||||
return getWakeUpModes()[static_cast<size_t>(mode)];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 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)];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void SetBrightness(Controllers::BrightnessController::Levels level) {
|
||||
if (level != settings.brightLevel) {
|
||||
settingsChanged = true;
|
||||
@ -279,6 +308,7 @@ namespace Pinetime {
|
||||
struct SettingsData {
|
||||
uint32_t version = settingsVersion;
|
||||
uint32_t stepsGoal = 10000;
|
||||
uint32_t setquickr = 10000;
|
||||
uint32_t screenTimeOut = 15000;
|
||||
|
||||
ClockType clockType = ClockType::H24;
|
||||
@ -292,6 +322,7 @@ namespace Pinetime {
|
||||
WatchFaceInfineat watchFaceInfineat;
|
||||
|
||||
std::bitset<5> wakeUpMode {0};
|
||||
std::bitset<5> quickApp {0};
|
||||
uint16_t shakeWakeThreshold = 150;
|
||||
|
||||
Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium;
|
||||
|
@ -37,7 +37,9 @@ namespace Pinetime {
|
||||
SettingChimes,
|
||||
SettingShakeThreshold,
|
||||
SettingBluetooth,
|
||||
Error
|
||||
Error,
|
||||
Calculator,
|
||||
SettingQuickR
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,9 @@
|
||||
#include "displayapp/screens/Error.h"
|
||||
#include "displayapp/screens/Weather.h"
|
||||
|
||||
// My New Apps
|
||||
#include "displayapp/screens/Calculator.h"
|
||||
|
||||
#include "drivers/Cst816s.h"
|
||||
#include "drivers/St7789.h"
|
||||
#include "drivers/Watchdog.h"
|
||||
@ -48,6 +51,7 @@
|
||||
#include "displayapp/screens/settings/SettingChimes.h"
|
||||
#include "displayapp/screens/settings/SettingShakeThreshold.h"
|
||||
#include "displayapp/screens/settings/SettingBluetooth.h"
|
||||
#include "displayapp/screens/settings/SettingQuickR.h"
|
||||
|
||||
#include "libs/lv_conf.h"
|
||||
|
||||
@ -289,17 +293,29 @@ void DisplayApp::Refresh() {
|
||||
return TouchEvents::SwipeLeft;
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
case TouchEvents::SwipeUp:
|
||||
LoadNewScreen(Apps::Launcher, DisplayApp::FullRefreshDirections::Up);
|
||||
if (currentApp == Apps::Clock) {
|
||||
LoadNewScreen(Apps::Launcher, DisplayApp::FullRefreshDirections::Up);
|
||||
} else if (currentApp == Apps::QuickSettings) {
|
||||
LoadNewScreen(Apps::Settings, DisplayApp::FullRefreshDirections::Up);
|
||||
}
|
||||
break;
|
||||
case TouchEvents::SwipeDown:
|
||||
LoadNewScreen(Apps::Notifications, DisplayApp::FullRefreshDirections::Down);
|
||||
if (currentApp == Apps::Clock) {
|
||||
LoadNewScreen(Apps::Notifications, DisplayApp::FullRefreshDirections::Down);
|
||||
} else if (currentApp == Apps::QuickSettings) {
|
||||
LoadNewScreen(Apps::FlashLight, DisplayApp::FullRefreshDirections::Down);
|
||||
}
|
||||
break;
|
||||
case TouchEvents::SwipeRight:
|
||||
LoadNewScreen(Apps::QuickSettings, DisplayApp::FullRefreshDirections::RightAnim);
|
||||
gotoquickapp(-1);
|
||||
break;
|
||||
case TouchEvents::SwipeLeft:
|
||||
gotoquickapp(1);
|
||||
break;
|
||||
case TouchEvents::DoubleTap:
|
||||
PushMessageToSystemTask(System::Messages::GoToSleep);
|
||||
@ -318,6 +334,7 @@ void DisplayApp::Refresh() {
|
||||
if (!currentScreen->OnButtonPushed()) {
|
||||
if (currentApp == Apps::Clock) {
|
||||
PushMessageToSystemTask(System::Messages::GoToSleep);
|
||||
|
||||
} else {
|
||||
LoadPreviousScreen();
|
||||
}
|
||||
@ -411,6 +428,7 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
|
||||
// break;
|
||||
case Apps::None:
|
||||
case Apps::Clock:
|
||||
currentQ_app = 2;
|
||||
currentScreen = std::make_unique<Screens::Clock>(dateTimeController,
|
||||
batteryController,
|
||||
bleController,
|
||||
@ -548,6 +566,13 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
|
||||
case Apps::Steps:
|
||||
currentScreen = std::make_unique<Screens::Steps>(motionController, settingsController);
|
||||
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;
|
||||
}
|
||||
@ -607,3 +632,84 @@ void DisplayApp::ApplyBrightness() {
|
||||
}
|
||||
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();
|
||||
}
|
@ -118,6 +118,8 @@ namespace Pinetime {
|
||||
void LoadNewScreen(Apps app, DisplayApp::FullRefreshDirections direction);
|
||||
void LoadScreen(Apps app, DisplayApp::FullRefreshDirections direction);
|
||||
void PushMessageToSystemTask(Pinetime::System::Messages message);
|
||||
void gotoquickapp(int app_step);
|
||||
|
||||
|
||||
Apps nextApp = Apps::None;
|
||||
DisplayApp::FullRefreshDirections nextDirection;
|
||||
@ -129,6 +131,8 @@ namespace Pinetime {
|
||||
Utility::StaticStack<FullRefreshDirections, returnAppStackSize> appStackDirections;
|
||||
|
||||
bool isDimmed = false;
|
||||
int currentQ_app = 2;
|
||||
std::bitset<5> quick_app;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
},
|
||||
{
|
||||
"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,
|
||||
|
@ -55,7 +55,8 @@ namespace Pinetime {
|
||||
{"2", Apps::Twos, true},
|
||||
{Symbols::drum, Apps::Metronome, true},
|
||||
{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},
|
||||
}};
|
||||
|
442
src/displayapp/screens/Calculator.cpp
Normal file
442
src/displayapp/screens/Calculator.cpp
Normal 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;
|
||||
}
|
||||
}
|
71
src/displayapp/screens/Calculator.h
Normal file
71
src/displayapp/screens/Calculator.h
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -47,6 +47,8 @@ FlashLight::FlashLight(System::SystemTask& systemTask, Controllers::BrightnessCo
|
||||
lv_obj_set_event_cb(backgroundAction, EventHandler);
|
||||
|
||||
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
|
||||
|
||||
Toggle();
|
||||
}
|
||||
|
||||
FlashLight::~FlashLight() {
|
||||
|
@ -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
|
||||
*/
|
||||
|
||||
|
||||
Music::Music(Pinetime::Controllers::MusicService& music) : musicService(music) {
|
||||
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) {
|
||||
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: {
|
||||
lv_obj_set_hidden(btnVolDown, false);
|
||||
lv_obj_set_hidden(btnVolUp, false);
|
||||
@ -258,24 +267,6 @@ bool Music::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
|
||||
lv_obj_set_hidden(btnPrev, 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: {
|
||||
return false;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
#include "displayapp/DisplayApp.h"
|
||||
#include <lvgl/src/lv_core/lv_obj.h>
|
||||
#include <string>
|
||||
#include "displayapp/screens/Screen.h"
|
||||
|
@ -37,6 +37,10 @@ namespace Pinetime {
|
||||
static constexpr const char* eye = "\xEF\x81\xAE";
|
||||
static constexpr const char* home = "\xEF\x80\x95";
|
||||
static constexpr const char* sleep = "\xEE\xBD\x84";
|
||||
static constexpr const char* calculator = "\xEF\x87\xAC";
|
||||
static constexpr const char* backspace = "\xEF\x95\x9A";
|
||||
|
||||
|
||||
|
||||
// fontawesome_weathericons.c
|
||||
// static constexpr const char* sun = "\xEF\x86\x85";
|
||||
|
@ -10,8 +10,11 @@
|
||||
#include "components/motion/MotionController.h"
|
||||
#include "components/settings/Settings.h"
|
||||
|
||||
|
||||
|
||||
using namespace Pinetime::Applications::Screens;
|
||||
|
||||
|
||||
WatchFaceTerminal::WatchFaceTerminal(Controllers::DateTime& dateTimeController,
|
||||
const Controllers::Battery& batteryController,
|
||||
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);
|
||||
|
||||
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_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);
|
||||
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_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);
|
||||
lv_label_set_recolor(label_time, true);
|
||||
@ -143,6 +148,8 @@ void WatchFaceTerminal::Refresh() {
|
||||
lv_label_set_text_static(heartbeatValue, "[L_HR]#ee3311 ---#");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
stepCount = motionController.NbSteps();
|
||||
if (stepCount.IsUpdated()) {
|
||||
|
77
src/displayapp/screens/settings/SettingQuickR.cpp
Normal file
77
src/displayapp/screens/settings/SettingQuickR.cpp
Normal 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]);
|
||||
}
|
||||
}
|
39
src/displayapp/screens/settings/SettingQuickR.h
Normal file
39
src/displayapp/screens/settings/SettingQuickR.h
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ namespace Pinetime {
|
||||
static constexpr int entriesPerScreen = 4;
|
||||
|
||||
// Increment this when more space is needed
|
||||
static constexpr int nScreens = 3;
|
||||
static constexpr int nScreens = 4;
|
||||
|
||||
static constexpr std::array<List::Applications, entriesPerScreen * nScreens> entries {{
|
||||
{Symbols::sun, "Display", Apps::SettingDisplay},
|
||||
@ -40,11 +40,14 @@ namespace Pinetime {
|
||||
{Symbols::shoe, "Steps", Apps::SettingSteps},
|
||||
{Symbols::clock, "Date&Time", Apps::SettingSetDateTime},
|
||||
{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::check, "Firmware", Apps::FirmwareValidation},
|
||||
{Symbols::bluetooth, "Bluetooth", Apps::SettingBluetooth},
|
||||
|
||||
{Symbols::list, "About", Apps::SysInfo},
|
||||
|
||||
// {Symbols::none, "None", Apps::None},
|
||||
|
Loading…
Reference in New Issue
Block a user