Merge remote-tracking branch 'upstream/develop' into pinetimestyle-colorpicker
This commit is contained in:
commit
30b32e4c8a
24 changed files with 357 additions and 334 deletions
20
.vscode/launch.json
vendored
20
.vscode/launch.json
vendored
|
@ -40,7 +40,25 @@
|
|||
"break main",
|
||||
"continue"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cwd": "${workspaceRoot}",
|
||||
// TODO: find better way to get latest build filename
|
||||
"executable": "./build/src/pinetime-app-1.3.0.out",
|
||||
"name": "Debug OpenOCD ST-LINK pinetime-app-1.3.0.out",
|
||||
"request": "launch",
|
||||
"type": "cortex-debug",
|
||||
"showDevDebugOutput": false,
|
||||
"servertype": "openocd",
|
||||
"runToMain": true,
|
||||
// Only use armToolchainPath if your arm-none-eabi-gdb is not in your path (some GCC packages does not contain arm-none-eabi-gdb)
|
||||
"armToolchainPath": "${workspaceRoot}/../gcc-arm-none-eabi-9-2020-q2-update/bin",
|
||||
"svdFile": "${workspaceRoot}/nrf52.svd",
|
||||
"configFiles": [
|
||||
"interface/stlink.cfg",
|
||||
"target/nrf52.cfg"
|
||||
],
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
This contribution guide is in progress, improvements are welcome.
|
||||
|
||||
### Code style
|
||||
|
||||
Any C++ code PRs should aim to follow the style of existing code in the project.
|
||||
|
||||
Using an autoformatter is heavily recommended, but make sure it's configured properly.
|
||||
|
||||
There's currently preconfigured autoformatter rules for:
|
||||
|
||||
* CLion (IntelliJ) in .idea/codeStyles/Project.xml
|
||||
|
||||
You can use those to configure your own IDE if it's not already on the list.
|
||||
|
||||
#### Linting errors and compiler warnings
|
||||
|
||||
Try to avoid any currently enabled warnings and try to reduce the amount of linter errors.
|
||||
|
||||
#### Spelling
|
||||
|
||||
Make sure you spellcheck your code before commiting it.
|
||||
|
||||
#### TODO, FIXME
|
||||
|
||||
Check before commiting that you haven't forgotten anything, preferably don't leave these in your commits.
|
||||
|
||||
#### Licence headers
|
||||
|
||||
You should add your name to the comma-space separated list of contributors if there's a license header.
|
||||
|
||||
### License
|
||||
|
||||
By contributing you agree to licence your code under the repository's general license (which is currently GPL-v3+).
|
1
CONTRIBUTING.md
Symbolic link
1
CONTRIBUTING.md
Symbolic link
|
@ -0,0 +1 @@
|
|||
doc/contribute.md
|
|
@ -1,68 +1,89 @@
|
|||
# How to contribute?
|
||||
|
||||
## Report bugs
|
||||
You use your Pinetime and find a bug in the firmware? [Create an issue on Github](https://github.com/JF002/InfiniTime/issues) explaining the bug, how to reproduce it, the version of the firmware you use...
|
||||
|
||||
Have you found a bug in the firmware? [Create an issue on Github](https://github.com/JF002/InfiniTime/issues) explaining the bug, how to reproduce it, the version of the firmware you use...
|
||||
|
||||
## Write and improve documentation
|
||||
|
||||
Documentation might be incomplete, or not clear enough, and it is always possible to improve it with better wording, pictures, photo, video,...
|
||||
|
||||
As the documentation is part of the source code, you can submit your improvements to the documentation by submitting a pull request (see below).
|
||||
|
||||
## Fix bugs, add functionalities and improve the code
|
||||
|
||||
You want to fix a bug, add a cool new functionality or improve the code? See *How to submit a pull request below*.
|
||||
|
||||
## Spread the word
|
||||
The Pinetime is a cool open source project that deserves to be known. Talk about it around you, on social networks, on your blog,... and let people know that we are working on an open-source firmware for a smartwatch!
|
||||
|
||||
The Pinetime is a cool open source project that deserves to be known. Talk about it around you, on social networks, on your blog,... and let people know that we are working on an open source firmware for a smartwatch!
|
||||
|
||||
# How to submit a pull request ?
|
||||
|
||||
## TL;DR
|
||||
|
||||
- Create a branch from develop;
|
||||
- Work on a single subject in this branch. Create multiple branches/pulls-requests if you want to work on multiple subjects (bugs, features,...);
|
||||
- Test your modifications on the actual hardware;
|
||||
- Check the code formatting against our coding conventions and [clang-format](../.clang-format) and [clang-tidy](../.clang-tidy);
|
||||
- Clean your code and remove files that are not needed;
|
||||
- Write documentation related to your new feature is applicable;
|
||||
- Create the pull-request and write a great description about it : what does your PR do, why, how,... Add pictures and video if possible;
|
||||
- Write documentation related to your new feature if applicable;
|
||||
- Create a pull request and write a great description about it : what does your PR do, why, how,... Add pictures and video if possible;
|
||||
- Wait for someone to review your PR and take part in the review process;
|
||||
- Your PR will eventually be merged :)
|
||||
|
||||
Your contribution is more than welcome!
|
||||
Your contributions are more than welcome!
|
||||
|
||||
If you want to fix a bug, add a functionality or improve the code, you'll first need to create a branch from the **develop** branch (see [this page about the branching model](./branches.md)). This branch is called a feature branch, and you should choose a name that explains what you are working on (ex: "add-doc-about-contributions"). In this branch, **focus on only one topic, bug or feature**. For example, if you created this branch to work on the UI of a specific application, do not commit modifications about the SPI driver. If you want to work on multiple topics, create one branch per topic.
|
||||
If you want to fix a bug, add functionality or improve the code, you'll first need to create a branch from the **develop** branch (see [this page about the branching model](./branches.md)). This branch is called a feature branch, and you should choose a name that explains what you are working on (ex: "add-doc-about-contributions"). In this branch, **focus on only one topic, bug or feature**. For example, if you created this branch to work on the UI of a specific application, do not commit modifications about the SPI driver. If you want to work on multiple topics, create one branch for each topic.
|
||||
|
||||
When your feature branch is ready, **make sure it actually works** and **do not forget to write documentation** about it if it's relevant.
|
||||
|
||||
I **strongly discourage to create a PR containing modifications that haven't been tested**. If, for any reason, you cannot test your modifications but want to publish them anyway, **please mention it in the description**. This way, other contributors might be willing to test it and provide feedback about your code.
|
||||
**Creating a pull request containing modifications that haven't been tested is strongly discouraged.** If, for any reason, you cannot test your modifications but want to publish them anyway, **please mention it in the description**. This way, other contributors might be willing to test it and provide feedback about your code.
|
||||
|
||||
Also, before submitting your PR, check the coding style of your code against the **coding conventions** detailed below. This project also provides [clang-format](../.clang-format) and [clang-tidy](../.clang-tidy) configuration files. You can use them to ensure correct formatting of your code.
|
||||
|
||||
Do not forget to check the files you are going to commit and remove those who are not necessary (config files from your IDE, for example). Remove old comments, commented code,...
|
||||
Don't forget to check the files you are going to commit and remove those which aren't necessary (config files from your IDE, for example). Remove old comments, commented code,...
|
||||
|
||||
Then, you can submit a pull-request for review. Try to **describe your pull request as much as possible**: what did you do in this branch, how does it work, how is it designed, are there any limitations,... This will help the contributors to understand and review your code easily. You can add pictures and video to the description so that contributors will have a quick overview of your work.
|
||||
Then, you can submit a pull request for review. Try to **describe your pull request as much as possible**: what did you do in this branch, how does it work, how it is designed, are there any limitations,... This will help the contributors to understand and review your code easily. You can add pictures and video to the description so that contributors will have a quick overview of your work.
|
||||
|
||||
Other contributors can post comments about the pull request, maybe ask for more info or adjustments in the code.
|
||||
|
||||
Once the pull request is reviewed and accepted, it'll be merge in **develop** and will be released in the next release version of the firmware.
|
||||
Once the pull request is reviewed and accepted, it'll be merged into **develop** and will be released in the next version of the firmware.
|
||||
|
||||
## Why all these rules?
|
||||
Reviewing pull-requests is a **very time consuming task** for the creator of this project ([JF002](https://github.com/JF002)) and for other contributors who take the time to review them. Every little thing you do to make their lives easier will **increase the chances your PR will be merge quickly**.
|
||||
|
||||
When reviewing PR, the author and contributors will first look at the **description**. If it's easy to understand what the PR does, why the modification is needed or interesting and how it's done, a good part of the work is already done : we understand the PR and its context.
|
||||
Reviewing pull requests is a **very time consuming task** for the creator of this project ([JF002](https://github.com/JF002)) and for other contributors who take the time to review them. Everything you do to make reviewing easier will **get your PR merged faster**.
|
||||
|
||||
Then, reviewing **a few files that were modified for a single purpose** is a lot more easier than to review 30 files modified for many reasons (bug fix, UI improvements, typos in doc,...), even if all these changes make sense. Also, it's possible that we agree on some modification but not on some other, and we won't be able to merge the PR because of the changes that are not accepted.
|
||||
When reviewing PRs, the author and contributors will first look at the **description**. If it's easy to understand what the PR does, why the modification is needed or interesting and how it's done, a good part of the work is already done : we understand the PR and its context.
|
||||
|
||||
We do our best to keep the code as consistent as possible, and that means we pay attention to the **formatting** of the code. If the code formatting is not consistent with our code base, we'll ask you to review it, which will take more time.
|
||||
Then, reviewing **a few files that were modified for a single purpose** is a lot more easier than to review 30 files modified for many reasons (bug fix, UI improvements, typos in doc,...), even if all these changes make sense. Also, it's possible that we agree on some modification but not on some other, so we won't be able to merge the PR because of the changes that are not accepted.
|
||||
|
||||
The last step of the review consists in **testing** the modification. If it doesn't work out of the box, we'll ask your to review your code and to ensure that it works as expected.
|
||||
We do our best to keep the code as consistent as possible. If the formatting of the code in your PR is not consistent with our code base, we'll ask you to review it, which will take more time.
|
||||
|
||||
It's totally normal for a PR to need some more work even after it was created, that's why we review them. But every round trip takes time, and it's good practice to try to reduce them as much as possible by following those simple rules.
|
||||
The last step of the review consists of **testing** the modification. If it doesn't work out of the box, we'll ask your to review your code and to ensure that it works as expected.
|
||||
|
||||
It's totally normal for a PR to need some more work even after it was created, that's why we review them. But every round trip takes time, so it's good practice to try to reduce them as much as possible by following those simple rules.
|
||||
|
||||
# Coding convention
|
||||
## Language
|
||||
The language of this project is **C++**, and all new code must be written in C++. (Modern) C++ provides a lot of useful tools and functionalities that are beneficial for embedded software development like `constexpr`, `template` and anything that provides zero-cost abstraction.
|
||||
|
||||
It's OK to include C code if this code comes from another library like FreeRTOS, NimBLE, LVGL or the NRF-SDK.
|
||||
## Language
|
||||
|
||||
The language of this project is **C++**, and all new code must be written in C++. (Modern) C++ provides a lot of useful tools and functionalities that are beneficial for embedded software development like `constexpr`, `template` and anything that provides zero-cost abstraction.
|
||||
|
||||
C code is accepted if it comes from another library like FreeRTOS, NimBLE, LVGL or the NRF-SDK.
|
||||
|
||||
## Coding style
|
||||
|
||||
The most important rule to follow is to try to keep the code as easy to read and maintain as possible.
|
||||
|
||||
Using an autoformatter is highly recommended, but make sure it's configured properly.
|
||||
|
||||
There are preconfigured autoformatter rules for:
|
||||
|
||||
* CLion (IntelliJ) in .idea/codeStyles/Project.xml
|
||||
|
||||
If there are no preconfigured rules for your IDE, you can use one of the existing ones to configure your IDE.
|
||||
|
||||
- **Indentation** : 2 spaces, no tabulation
|
||||
- **Opening brace** at the end of the line
|
||||
- **Naming** : Choose self-describing variable name
|
||||
|
|
BIN
doc/ui/example.png
Normal file
BIN
doc/ui/example.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
16
doc/ui_guidelines.md
Normal file
16
doc/ui_guidelines.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# UI design guidelines
|
||||
|
||||
- Align objects all the way to the edge or corner
|
||||
- Buttons should generally be at least 50px high
|
||||
- Buttons should generally be on the bottom edge
|
||||
- Make interactable objects **big**
|
||||
- Recommendations for inner padding, aka distance between buttons:
|
||||
- When aligning 4 objects: 4px, e.g. Settings
|
||||
- When aligning 3 objects: 6px, e.g. App list
|
||||
- When aligning 2 objects: 10px, e.g. Quick settings
|
||||
- When using a page indicator, leave 8px for it on the right side
|
||||
- It is acceptable to leave 8px on the left side as well to center the content
|
||||
- Top bar takes at least 20px + padding
|
||||
- Top bar right icons move 8px to the left when using a page indicator
|
||||
|
||||
![example layouts](./ui/example.png)
|
|
@ -3,7 +3,8 @@
|
|||
#include "systemtask/SystemTask.h"
|
||||
#include "app_timer.h"
|
||||
|
||||
APP_TIMER_DEF(vibTimer);
|
||||
APP_TIMER_DEF(shortVibTimer);
|
||||
APP_TIMER_DEF(longVibTimer);
|
||||
|
||||
using namespace Pinetime::Controllers;
|
||||
|
||||
|
@ -13,19 +14,39 @@ MotorController::MotorController(Controllers::Settings& settingsController) : se
|
|||
void MotorController::Init() {
|
||||
nrf_gpio_cfg_output(pinMotor);
|
||||
nrf_gpio_pin_set(pinMotor);
|
||||
app_timer_create(&vibTimer, APP_TIMER_MODE_SINGLE_SHOT, vibrate);
|
||||
app_timer_init();
|
||||
|
||||
app_timer_create(&shortVibTimer, APP_TIMER_MODE_SINGLE_SHOT, StopMotor);
|
||||
app_timer_create(&longVibTimer, APP_TIMER_MODE_REPEATED, Ring);
|
||||
}
|
||||
|
||||
void MotorController::SetDuration(uint8_t motorDuration) {
|
||||
void MotorController::Ring(void* p_context) {
|
||||
auto* motorController = static_cast<MotorController*>(p_context);
|
||||
motorController->RunForDuration(50);
|
||||
}
|
||||
|
||||
if (settingsController.GetVibrationStatus() == Controllers::Settings::Vibration::OFF)
|
||||
void MotorController::RunForDuration(uint8_t motorDuration) {
|
||||
if (settingsController.GetVibrationStatus() == Controllers::Settings::Vibration::OFF) {
|
||||
return;
|
||||
}
|
||||
|
||||
nrf_gpio_pin_clear(pinMotor);
|
||||
/* Start timer for motorDuration miliseconds and timer triggers vibrate() when it finishes*/
|
||||
app_timer_start(vibTimer, APP_TIMER_TICKS(motorDuration), NULL);
|
||||
app_timer_start(shortVibTimer, APP_TIMER_TICKS(motorDuration), nullptr);
|
||||
}
|
||||
|
||||
void MotorController::vibrate(void* p_context) {
|
||||
void MotorController::StartRinging() {
|
||||
if (settingsController.GetVibrationStatus() == Controllers::Settings::Vibration::OFF) {
|
||||
return;
|
||||
}
|
||||
Ring(this);
|
||||
app_timer_start(longVibTimer, APP_TIMER_TICKS(1000), this);
|
||||
}
|
||||
|
||||
void MotorController::StopRinging() {
|
||||
app_timer_stop(longVibTimer);
|
||||
nrf_gpio_pin_set(pinMotor);
|
||||
}
|
||||
}
|
||||
|
||||
void MotorController::StopMotor(void* p_context) {
|
||||
nrf_gpio_pin_set(pinMotor);
|
||||
}
|
||||
|
|
|
@ -12,11 +12,14 @@ namespace Pinetime {
|
|||
public:
|
||||
MotorController(Controllers::Settings& settingsController);
|
||||
void Init();
|
||||
void SetDuration(uint8_t motorDuration);
|
||||
void RunForDuration(uint8_t motorDuration);
|
||||
void StartRinging();
|
||||
static void StopRinging();
|
||||
|
||||
private:
|
||||
static void Ring(void* p_context);
|
||||
Controllers::Settings& settingsController;
|
||||
static void vibrate(void* p_context);
|
||||
static void StopMotor(void* p_context);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -337,12 +337,12 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
|
|||
|
||||
case Apps::Notifications:
|
||||
currentScreen = std::make_unique<Screens::Notifications>(
|
||||
this, notificationManager, systemTask->nimble().alertService(), Screens::Notifications::Modes::Normal);
|
||||
this, notificationManager, systemTask->nimble().alertService(), motorController, Screens::Notifications::Modes::Normal);
|
||||
ReturnApp(Apps::Clock, FullRefreshDirections::Up, TouchEvents::SwipeUp);
|
||||
break;
|
||||
case Apps::NotificationsPreview:
|
||||
currentScreen = std::make_unique<Screens::Notifications>(
|
||||
this, notificationManager, systemTask->nimble().alertService(), Screens::Notifications::Modes::Preview);
|
||||
this, notificationManager, systemTask->nimble().alertService(), motorController, Screens::Notifications::Modes::Preview);
|
||||
ReturnApp(Apps::Clock, FullRefreshDirections::Up, TouchEvents::SwipeUp);
|
||||
break;
|
||||
case Apps::Timer:
|
||||
|
@ -419,6 +419,7 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
|
|||
break;
|
||||
case Apps::Metronome:
|
||||
currentScreen = std::make_unique<Screens::Metronome>(this, motorController, *systemTask);
|
||||
ReturnApp(Apps::Launcher, FullRefreshDirections::Down, TouchEvents::None);
|
||||
break;
|
||||
case Apps::Motion:
|
||||
currentScreen = std::make_unique<Screens::Motion>(this, motionController);
|
||||
|
|
|
@ -38,8 +38,9 @@ FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp* app,
|
|||
lv_label_set_text(labelIsValidated, "Please #00ff00 Validate# this version or\n#ff0000 Reset# to rollback to the previous version.");
|
||||
|
||||
buttonValidate = lv_btn_create(lv_scr_act(), nullptr);
|
||||
lv_obj_align(buttonValidate, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
|
||||
buttonValidate->user_data = this;
|
||||
lv_obj_set_size(buttonValidate, 115, 50);
|
||||
lv_obj_align(buttonValidate, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
|
||||
lv_obj_set_event_cb(buttonValidate, ButtonEventHandler);
|
||||
lv_obj_set_style_local_bg_color(buttonValidate, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x009900));
|
||||
|
||||
|
@ -48,6 +49,7 @@ FirmwareValidation::FirmwareValidation(Pinetime::Applications::DisplayApp* app,
|
|||
|
||||
buttonReset = lv_btn_create(lv_scr_act(), nullptr);
|
||||
buttonReset->user_data = this;
|
||||
lv_obj_set_size(buttonReset, 115, 50);
|
||||
lv_obj_align(buttonReset, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
|
||||
lv_obj_set_style_local_bg_color(buttonReset, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x990000));
|
||||
lv_obj_set_event_cb(buttonReset, ButtonEventHandler);
|
||||
|
|
|
@ -6,10 +6,10 @@ Label::Label(uint8_t screenID, uint8_t numScreens, Pinetime::Applications::Displ
|
|||
: Screen(app), labelText {labelText} {
|
||||
|
||||
if (numScreens > 1) {
|
||||
pageIndicatorBasePoints[0].x = 240 - 1;
|
||||
pageIndicatorBasePoints[0].y = 6;
|
||||
pageIndicatorBasePoints[1].x = 240 - 1;
|
||||
pageIndicatorBasePoints[1].y = 240 - 6;
|
||||
pageIndicatorBasePoints[0].x = LV_HOR_RES - 1;
|
||||
pageIndicatorBasePoints[0].y = 0;
|
||||
pageIndicatorBasePoints[1].x = LV_HOR_RES - 1;
|
||||
pageIndicatorBasePoints[1].y = LV_VER_RES;
|
||||
|
||||
pageIndicatorBase = lv_line_create(lv_scr_act(), NULL);
|
||||
lv_obj_set_style_local_line_width(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3);
|
||||
|
@ -17,13 +17,13 @@ Label::Label(uint8_t screenID, uint8_t numScreens, Pinetime::Applications::Displ
|
|||
lv_obj_set_style_local_line_rounded(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, true);
|
||||
lv_line_set_points(pageIndicatorBase, pageIndicatorBasePoints, 2);
|
||||
|
||||
uint16_t indicatorSize = 228 / numScreens;
|
||||
uint16_t indicatorSize = LV_VER_RES / numScreens;
|
||||
uint16_t indicatorPos = indicatorSize * screenID;
|
||||
|
||||
pageIndicatorPoints[0].x = 240 - 1;
|
||||
pageIndicatorPoints[0].y = (6 + indicatorPos);
|
||||
pageIndicatorPoints[1].x = 240 - 1;
|
||||
pageIndicatorPoints[1].y = (6 + indicatorPos) + indicatorSize;
|
||||
pageIndicatorPoints[0].x = LV_HOR_RES - 1;
|
||||
pageIndicatorPoints[0].y = indicatorPos;
|
||||
pageIndicatorPoints[1].x = LV_HOR_RES - 1;
|
||||
pageIndicatorPoints[1].y = indicatorPos + indicatorSize;
|
||||
|
||||
pageIndicator = lv_line_create(lv_scr_act(), NULL);
|
||||
lv_obj_set_style_local_line_width(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3);
|
||||
|
|
|
@ -25,42 +25,38 @@ List::List(uint8_t screenID,
|
|||
settingsController.SetSettingsMenu(screenID);
|
||||
|
||||
if (numScreens > 1) {
|
||||
pageIndicatorBasePoints[0].x = 240 - 1;
|
||||
pageIndicatorBasePoints[0].y = 6;
|
||||
pageIndicatorBasePoints[1].x = 240 - 1;
|
||||
pageIndicatorBasePoints[1].y = 240 - 6;
|
||||
pageIndicatorBasePoints[0].x = LV_HOR_RES - 1;
|
||||
pageIndicatorBasePoints[0].y = 0;
|
||||
pageIndicatorBasePoints[1].x = LV_HOR_RES - 1;
|
||||
pageIndicatorBasePoints[1].y = LV_VER_RES;
|
||||
|
||||
pageIndicatorBase = lv_line_create(lv_scr_act(), NULL);
|
||||
lv_obj_set_style_local_line_width(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3);
|
||||
lv_obj_set_style_local_line_color(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111));
|
||||
lv_obj_set_style_local_line_rounded(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, true);
|
||||
lv_line_set_points(pageIndicatorBase, pageIndicatorBasePoints, 2);
|
||||
|
||||
uint16_t indicatorSize = 228 / numScreens;
|
||||
uint16_t indicatorPos = indicatorSize * screenID;
|
||||
const uint16_t indicatorSize = LV_VER_RES / numScreens;
|
||||
const uint16_t indicatorPos = indicatorSize * screenID;
|
||||
|
||||
pageIndicatorPoints[0].x = 240 - 1;
|
||||
pageIndicatorPoints[0].y = 6 + indicatorPos;
|
||||
pageIndicatorPoints[1].x = 240 - 1;
|
||||
pageIndicatorPoints[1].y = 6 + indicatorPos + indicatorSize;
|
||||
pageIndicatorPoints[0].x = LV_HOR_RES - 1;
|
||||
pageIndicatorPoints[0].y = indicatorPos;
|
||||
pageIndicatorPoints[1].x = LV_HOR_RES - 1;
|
||||
pageIndicatorPoints[1].y = indicatorPos + indicatorSize;
|
||||
|
||||
pageIndicator = lv_line_create(lv_scr_act(), NULL);
|
||||
lv_obj_set_style_local_line_width(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3);
|
||||
lv_obj_set_style_local_line_color(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
|
||||
lv_obj_set_style_local_line_rounded(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, true);
|
||||
lv_line_set_points(pageIndicator, pageIndicatorPoints, 2);
|
||||
}
|
||||
|
||||
lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr);
|
||||
|
||||
// lv_obj_set_style_local_bg_color(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111));
|
||||
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_pad_inner(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 4);
|
||||
lv_obj_set_style_local_border_width(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
|
||||
|
||||
lv_obj_set_pos(container1, 0, 0);
|
||||
lv_obj_set_width(container1, LV_HOR_RES - 15);
|
||||
lv_obj_set_width(container1, LV_HOR_RES - 8);
|
||||
lv_obj_set_height(container1, LV_VER_RES);
|
||||
lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT);
|
||||
|
||||
|
@ -73,11 +69,11 @@ List::List(uint8_t screenID,
|
|||
|
||||
itemApps[i] = lv_btn_create(container1, nullptr);
|
||||
lv_obj_set_style_local_bg_opa(itemApps[i], LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_20);
|
||||
lv_obj_set_style_local_radius(itemApps[i], LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 20);
|
||||
lv_obj_set_style_local_radius(itemApps[i], LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 57);
|
||||
lv_obj_set_style_local_bg_color(itemApps[i], LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_AQUA);
|
||||
|
||||
lv_obj_set_width(itemApps[i], LV_HOR_RES - 25);
|
||||
lv_obj_set_height(itemApps[i], 52);
|
||||
lv_obj_set_width(itemApps[i], LV_HOR_RES - 8);
|
||||
lv_obj_set_height(itemApps[i], 57);
|
||||
lv_obj_set_event_cb(itemApps[i], ButtonEventHandler);
|
||||
lv_btn_set_layout(itemApps[i], LV_LAYOUT_ROW_MID);
|
||||
itemApps[i]->user_data = this;
|
||||
|
|
|
@ -1,35 +1,15 @@
|
|||
#include "Metronome.h"
|
||||
|
||||
#include "Screen.h"
|
||||
#include "Symbols.h"
|
||||
#include "lvgl/lvgl.h"
|
||||
#include "FreeRTOSConfig.h"
|
||||
#include "task.h"
|
||||
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
using namespace Pinetime::Applications::Screens;
|
||||
|
||||
namespace {
|
||||
float calculateDelta(const TickType_t startTime, const TickType_t currentTime) {
|
||||
TickType_t delta = 0;
|
||||
// Take care of overflow
|
||||
if (startTime > currentTime) {
|
||||
delta = 0xffffffff - startTime;
|
||||
delta += (currentTime + 1);
|
||||
} else {
|
||||
delta = currentTime - startTime;
|
||||
}
|
||||
return static_cast<float>(delta) / static_cast<float>(configTICK_RATE_HZ);
|
||||
}
|
||||
|
||||
static void eventHandler(lv_obj_t* obj, lv_event_t event) {
|
||||
Metronome* screen = static_cast<Metronome*>(obj->user_data);
|
||||
void eventHandler(lv_obj_t* obj, lv_event_t event) {
|
||||
auto* screen = static_cast<Metronome*>(obj->user_data);
|
||||
screen->OnEvent(obj, event);
|
||||
}
|
||||
|
||||
lv_obj_t* createLabel(const char* name, lv_obj_t* reference, lv_align_t align, lv_font_t* font, uint8_t x = 0, uint8_t y = 0) {
|
||||
lv_obj_t* createLabel(const char* name, lv_obj_t* reference, lv_align_t align, lv_font_t* font, uint8_t x, uint8_t y) {
|
||||
lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font);
|
||||
lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
|
||||
|
@ -41,7 +21,7 @@ namespace {
|
|||
}
|
||||
|
||||
Metronome::Metronome(DisplayApp* app, Controllers::MotorController& motorController, System::SystemTask& systemTask)
|
||||
: Screen(app), running {true}, currentState {States::Stopped}, startTime {}, motorController {motorController}, systemTask {systemTask} {
|
||||
: Screen(app), motorController {motorController}, systemTask {systemTask} {
|
||||
|
||||
bpmArc = lv_arc_create(lv_scr_act(), nullptr);
|
||||
bpmArc->user_data = this;
|
||||
|
@ -52,10 +32,10 @@ Metronome::Metronome(DisplayApp* app, Controllers::MotorController& motorControl
|
|||
lv_arc_set_value(bpmArc, bpm);
|
||||
lv_obj_set_size(bpmArc, 210, 210);
|
||||
lv_arc_set_adjustable(bpmArc, true);
|
||||
lv_obj_align(bpmArc, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, 7);
|
||||
lv_obj_align(bpmArc, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, 0);
|
||||
|
||||
bpmValue = createLabel(std::to_string(lv_arc_get_value(bpmArc)).c_str(), bpmArc, LV_ALIGN_IN_TOP_MID, &jetbrains_mono_76, 0, 55);
|
||||
bpmLegend = createLabel("bpm", bpmValue, LV_ALIGN_OUT_BOTTOM_MID, &jetbrains_mono_bold_20, 0, 0);
|
||||
bpmValue = createLabel("120", bpmArc, LV_ALIGN_IN_TOP_MID, &jetbrains_mono_76, 0, 55);
|
||||
createLabel("bpm", bpmValue, LV_ALIGN_OUT_BOTTOM_MID, &jetbrains_mono_bold_20, 0, 0);
|
||||
|
||||
bpmTap = lv_btn_create(lv_scr_act(), nullptr);
|
||||
bpmTap->user_data = this;
|
||||
|
@ -69,20 +49,23 @@ Metronome::Metronome(DisplayApp* app, Controllers::MotorController& motorControl
|
|||
lv_obj_set_event_cb(bpbDropdown, eventHandler);
|
||||
lv_obj_set_style_local_pad_left(bpbDropdown, LV_DROPDOWN_PART_MAIN, LV_STATE_DEFAULT, 20);
|
||||
lv_obj_set_style_local_pad_left(bpbDropdown, LV_DROPDOWN_PART_LIST, LV_STATE_DEFAULT, 20);
|
||||
lv_obj_align(bpbDropdown, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 15, -4);
|
||||
lv_obj_set_size(bpbDropdown, 115, 50);
|
||||
lv_obj_align(bpbDropdown, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
|
||||
lv_dropdown_set_options(bpbDropdown, "1\n2\n3\n4\n5\n6\n7\n8\n9");
|
||||
lv_dropdown_set_selected(bpbDropdown, bpb - 1);
|
||||
bpbLegend = lv_label_create(bpbDropdown, nullptr);
|
||||
lv_label_set_text(bpbLegend, "bpb");
|
||||
lv_obj_align(bpbLegend, bpbDropdown, LV_ALIGN_IN_RIGHT_MID, -15, 0);
|
||||
lv_dropdown_set_show_selected(bpbDropdown, false);
|
||||
lv_dropdown_set_text(bpbDropdown, "");
|
||||
|
||||
currentBpbText = lv_label_create(bpbDropdown, nullptr);
|
||||
lv_label_set_text_fmt(currentBpbText, "%d bpb", bpb);
|
||||
lv_obj_align(currentBpbText, bpbDropdown, LV_ALIGN_CENTER, 0, 0);
|
||||
|
||||
playPause = lv_btn_create(lv_scr_act(), nullptr);
|
||||
playPause->user_data = this;
|
||||
lv_obj_set_event_cb(playPause, eventHandler);
|
||||
lv_obj_align(playPause, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -15, -10);
|
||||
lv_obj_set_height(playPause, 39);
|
||||
playPauseLabel = lv_label_create(playPause, nullptr);
|
||||
lv_label_set_text(playPauseLabel, Symbols::play);
|
||||
lv_obj_set_size(playPause, 115, 50);
|
||||
lv_obj_align(playPause, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
|
||||
lv_obj_set_style_local_value_str(playPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Symbols::play);
|
||||
|
||||
app->SetTouchMode(DisplayApp::TouchModes::Polling);
|
||||
}
|
||||
|
@ -93,28 +76,17 @@ Metronome::~Metronome() {
|
|||
lv_obj_clean(lv_scr_act());
|
||||
}
|
||||
|
||||
bool Metronome::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Metronome::Refresh() {
|
||||
switch (currentState) {
|
||||
case States::Stopped: {
|
||||
break;
|
||||
}
|
||||
case States::Running: {
|
||||
if (calculateDelta(startTime, xTaskGetTickCount()) >= (60.0 / bpm)) {
|
||||
counter--;
|
||||
startTime -= 60.0 / bpm;
|
||||
startTime = xTaskGetTickCount();
|
||||
if (counter == 0) {
|
||||
counter = bpb;
|
||||
motorController.SetDuration(90);
|
||||
} else {
|
||||
motorController.SetDuration(30);
|
||||
}
|
||||
if (metronomeStarted) {
|
||||
if (xTaskGetTickCount() - startTime > 60 * configTICK_RATE_HZ / bpm) {
|
||||
startTime += 60 * configTICK_RATE_HZ / bpm;
|
||||
counter--;
|
||||
if (counter == 0) {
|
||||
counter = bpb;
|
||||
motorController.RunForDuration(90);
|
||||
} else {
|
||||
motorController.RunForDuration(30);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return running;
|
||||
|
@ -128,42 +100,39 @@ void Metronome::OnEvent(lv_obj_t* obj, lv_event_t event) {
|
|||
lv_label_set_text_fmt(bpmValue, "%03d", bpm);
|
||||
} else if (obj == bpbDropdown) {
|
||||
bpb = lv_dropdown_get_selected(obj) + 1;
|
||||
lv_label_set_text_fmt(currentBpbText, "%d bpb", bpb);
|
||||
lv_obj_realign(currentBpbText);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LV_EVENT_PRESSED: {
|
||||
if (obj == bpmTap) {
|
||||
float timeDelta = calculateDelta(tappedTime, xTaskGetTickCount());
|
||||
if (tappedTime == 0 || timeDelta > 3) {
|
||||
tappedTime = xTaskGetTickCount();
|
||||
} else {
|
||||
bpm = ceil(60.0 / timeDelta);
|
||||
TickType_t delta = xTaskGetTickCount() - tappedTime;
|
||||
if (tappedTime != 0 && delta < configTICK_RATE_HZ * 3) {
|
||||
bpm = configTICK_RATE_HZ * 60 / delta;
|
||||
lv_arc_set_value(bpmArc, bpm);
|
||||
lv_label_set_text_fmt(bpmValue, "%03d", bpm);
|
||||
tappedTime = xTaskGetTickCount();
|
||||
}
|
||||
tappedTime = xTaskGetTickCount();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LV_EVENT_CLICKED: {
|
||||
if (obj == playPause) {
|
||||
currentState = (currentState == States::Stopped ? States::Running : States::Stopped);
|
||||
switch (currentState) {
|
||||
case States::Stopped: {
|
||||
lv_label_set_text(playPauseLabel, Symbols::play);
|
||||
systemTask.PushMessage(System::Messages::EnableSleeping);
|
||||
break;
|
||||
}
|
||||
case States::Running: {
|
||||
lv_label_set_text(playPauseLabel, Symbols::pause);
|
||||
systemTask.PushMessage(System::Messages::DisableSleeping);
|
||||
startTime = xTaskGetTickCount();
|
||||
counter = 1;
|
||||
break;
|
||||
}
|
||||
metronomeStarted = !metronomeStarted;
|
||||
if (metronomeStarted) {
|
||||
lv_obj_set_style_local_value_str(playPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Symbols::pause);
|
||||
systemTask.PushMessage(System::Messages::DisableSleeping);
|
||||
startTime = xTaskGetTickCount();
|
||||
counter = 1;
|
||||
} else {
|
||||
lv_obj_set_style_local_value_str(playPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Symbols::play);
|
||||
systemTask.PushMessage(System::Messages::EnableSleeping);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,32 +3,32 @@
|
|||
#include "systemtask/SystemTask.h"
|
||||
#include "components/motor/MotorController.h"
|
||||
|
||||
#include <array>
|
||||
namespace Pinetime {
|
||||
namespace Applications {
|
||||
namespace Screens {
|
||||
|
||||
namespace Pinetime::Applications::Screens {
|
||||
class Metronome : public Screen {
|
||||
public:
|
||||
Metronome(DisplayApp* app, Controllers::MotorController& motorController, System::SystemTask& systemTask);
|
||||
~Metronome() override;
|
||||
bool Refresh() override;
|
||||
void OnEvent(lv_obj_t* obj, lv_event_t event);
|
||||
|
||||
class Metronome : public Screen {
|
||||
public:
|
||||
Metronome(DisplayApp* app, Controllers::MotorController& motorController, System::SystemTask& systemTask);
|
||||
~Metronome() override;
|
||||
bool Refresh() override;
|
||||
bool OnTouchEvent(TouchEvents event) override;
|
||||
void OnEvent(lv_obj_t* obj, lv_event_t event);
|
||||
enum class States { Running, Stopped };
|
||||
private:
|
||||
TickType_t startTime = 0;
|
||||
TickType_t tappedTime = 0;
|
||||
Controllers::MotorController& motorController;
|
||||
System::SystemTask& systemTask;
|
||||
int16_t bpm = 120;
|
||||
uint8_t bpb = 4;
|
||||
uint8_t counter = 1;
|
||||
|
||||
private:
|
||||
bool running;
|
||||
States currentState;
|
||||
TickType_t startTime;
|
||||
TickType_t tappedTime = 0;
|
||||
Controllers::MotorController& motorController;
|
||||
System::SystemTask& systemTask;
|
||||
uint16_t bpm = 120;
|
||||
uint8_t bpb = 4;
|
||||
uint8_t counter = 1;
|
||||
bool metronomeStarted = false;
|
||||
|
||||
lv_obj_t *bpmArc, *bpmTap, *bpmValue, *bpmLegend;
|
||||
lv_obj_t *bpbDropdown, *bpbLegend;
|
||||
lv_obj_t *playPause, *playPauseLabel;
|
||||
};
|
||||
lv_obj_t *bpmArc, *bpmTap, *bpmValue;
|
||||
lv_obj_t *bpbDropdown, *currentBpbText;
|
||||
lv_obj_t *playPause;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,60 +50,55 @@ inline void lv_img_set_src_arr(lv_obj_t* img, const lv_img_dsc_t* src_img) {
|
|||
Music::Music(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::MusicService& music) : Screen(app), musicService(music) {
|
||||
lv_obj_t* label;
|
||||
|
||||
lv_style_init(&btn_style);
|
||||
lv_style_set_radius(&btn_style, LV_STATE_DEFAULT, 20);
|
||||
lv_style_set_bg_color(&btn_style, LV_STATE_DEFAULT, LV_COLOR_AQUA);
|
||||
lv_style_set_bg_opa(&btn_style, LV_STATE_DEFAULT, LV_OPA_20);
|
||||
|
||||
btnVolDown = lv_btn_create(lv_scr_act(), nullptr);
|
||||
btnVolDown->user_data = this;
|
||||
lv_obj_set_event_cb(btnVolDown, event_handler);
|
||||
lv_obj_set_size(btnVolDown, 65, 75);
|
||||
lv_obj_align(btnVolDown, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 15, -10);
|
||||
lv_obj_set_style_local_radius(btnVolDown, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 20);
|
||||
lv_obj_set_style_local_bg_color(btnVolDown, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_AQUA);
|
||||
lv_obj_set_style_local_bg_opa(btnVolDown, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_20);
|
||||
lv_obj_set_size(btnVolDown, 76, 76);
|
||||
lv_obj_align(btnVolDown, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
|
||||
lv_obj_add_style(btnVolDown, LV_STATE_DEFAULT, &btn_style);
|
||||
label = lv_label_create(btnVolDown, nullptr);
|
||||
lv_label_set_text(label, Symbols::volumDown);
|
||||
lv_obj_set_hidden(btnVolDown, !displayVolumeButtons);
|
||||
lv_obj_set_hidden(btnVolDown, true);
|
||||
|
||||
btnVolUp = lv_btn_create(lv_scr_act(), nullptr);
|
||||
btnVolUp->user_data = this;
|
||||
lv_obj_set_event_cb(btnVolUp, event_handler);
|
||||
lv_obj_set_size(btnVolUp, 65, 75);
|
||||
lv_obj_align(btnVolUp, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, -15, -10);
|
||||
lv_obj_set_style_local_radius(btnVolUp, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 20);
|
||||
lv_obj_set_style_local_bg_color(btnVolUp, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_AQUA);
|
||||
lv_obj_set_style_local_bg_opa(btnVolUp, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_20);
|
||||
lv_obj_set_size(btnVolUp, 76, 76);
|
||||
lv_obj_align(btnVolUp, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
|
||||
lv_obj_add_style(btnVolUp, LV_STATE_DEFAULT, &btn_style);
|
||||
label = lv_label_create(btnVolUp, nullptr);
|
||||
lv_label_set_text(label, Symbols::volumUp);
|
||||
lv_obj_set_hidden(btnVolUp, !displayVolumeButtons);
|
||||
lv_obj_set_hidden(btnVolUp, true);
|
||||
|
||||
btnPrev = lv_btn_create(lv_scr_act(), nullptr);
|
||||
btnPrev->user_data = this;
|
||||
lv_obj_set_event_cb(btnPrev, event_handler);
|
||||
lv_obj_set_size(btnPrev, 65, 75);
|
||||
lv_obj_align(btnPrev, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 15, -10);
|
||||
lv_obj_set_style_local_radius(btnPrev, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 20);
|
||||
lv_obj_set_style_local_bg_color(btnPrev, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_AQUA);
|
||||
lv_obj_set_style_local_bg_opa(btnPrev, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_20);
|
||||
lv_obj_set_size(btnPrev, 76, 76);
|
||||
lv_obj_align(btnPrev, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
|
||||
lv_obj_add_style(btnPrev, LV_STATE_DEFAULT, &btn_style);
|
||||
label = lv_label_create(btnPrev, nullptr);
|
||||
lv_label_set_text(label, Symbols::stepBackward);
|
||||
|
||||
btnNext = lv_btn_create(lv_scr_act(), nullptr);
|
||||
btnNext->user_data = this;
|
||||
lv_obj_set_event_cb(btnNext, event_handler);
|
||||
lv_obj_set_size(btnNext, 65, 75);
|
||||
lv_obj_align(btnNext, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, -15, -10);
|
||||
lv_obj_set_style_local_radius(btnNext, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 20);
|
||||
lv_obj_set_style_local_bg_color(btnNext, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_AQUA);
|
||||
lv_obj_set_style_local_bg_opa(btnNext, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_20);
|
||||
lv_obj_set_size(btnNext, 76, 76);
|
||||
lv_obj_align(btnNext, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
|
||||
lv_obj_add_style(btnNext, LV_STATE_DEFAULT, &btn_style);
|
||||
label = lv_label_create(btnNext, nullptr);
|
||||
lv_label_set_text(label, Symbols::stepForward);
|
||||
|
||||
btnPlayPause = lv_btn_create(lv_scr_act(), nullptr);
|
||||
btnPlayPause->user_data = this;
|
||||
lv_obj_set_event_cb(btnPlayPause, event_handler);
|
||||
lv_obj_set_size(btnPlayPause, 65, 75);
|
||||
lv_obj_align(btnPlayPause, nullptr, LV_ALIGN_IN_BOTTOM_MID, 0, -10);
|
||||
lv_obj_set_style_local_radius(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 20);
|
||||
lv_obj_set_style_local_bg_color(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_AQUA);
|
||||
lv_obj_set_style_local_bg_opa(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_20);
|
||||
lv_obj_set_size(btnPlayPause, 76, 76);
|
||||
lv_obj_align(btnPlayPause, nullptr, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
|
||||
lv_obj_add_style(btnPlayPause, LV_STATE_DEFAULT, &btn_style);
|
||||
txtPlayPause = lv_label_create(btnPlayPause, nullptr);
|
||||
lv_label_set_text(txtPlayPause, Symbols::play);
|
||||
|
||||
|
@ -147,6 +142,7 @@ Music::Music(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Mus
|
|||
}
|
||||
|
||||
Music::~Music() {
|
||||
lv_style_reset(&btn_style);
|
||||
lv_obj_clean(lv_scr_act());
|
||||
}
|
||||
|
||||
|
@ -272,21 +268,19 @@ void Music::OnObjectEvent(lv_obj_t* obj, lv_event_t event) {
|
|||
bool Music::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
|
||||
switch (event) {
|
||||
case TouchEvents::SwipeUp: {
|
||||
displayVolumeButtons = true;
|
||||
lv_obj_set_hidden(btnVolDown, !displayVolumeButtons);
|
||||
lv_obj_set_hidden(btnVolUp, !displayVolumeButtons);
|
||||
lv_obj_set_hidden(btnVolDown, false);
|
||||
lv_obj_set_hidden(btnVolUp, false);
|
||||
|
||||
lv_obj_set_hidden(btnNext, displayVolumeButtons);
|
||||
lv_obj_set_hidden(btnPrev, displayVolumeButtons);
|
||||
lv_obj_set_hidden(btnNext, true);
|
||||
lv_obj_set_hidden(btnPrev, true);
|
||||
return true;
|
||||
}
|
||||
case TouchEvents::SwipeDown: {
|
||||
displayVolumeButtons = false;
|
||||
lv_obj_set_hidden(btnNext, displayVolumeButtons);
|
||||
lv_obj_set_hidden(btnPrev, displayVolumeButtons);
|
||||
lv_obj_set_hidden(btnNext, false);
|
||||
lv_obj_set_hidden(btnPrev, false);
|
||||
|
||||
lv_obj_set_hidden(btnVolDown, !displayVolumeButtons);
|
||||
lv_obj_set_hidden(btnVolUp, !displayVolumeButtons);
|
||||
lv_obj_set_hidden(btnVolDown, true);
|
||||
lv_obj_set_hidden(btnVolUp, true);
|
||||
return true;
|
||||
}
|
||||
case TouchEvents::SwipeLeft: {
|
||||
|
|
|
@ -57,10 +57,11 @@ namespace Pinetime {
|
|||
lv_obj_t* imgDiscAnim;
|
||||
lv_obj_t* txtTrackDuration;
|
||||
|
||||
lv_style_t btn_style;
|
||||
|
||||
/** For the spinning disc animation */
|
||||
bool frameB;
|
||||
|
||||
bool displayVolumeButtons = false;
|
||||
Pinetime::Controllers::MusicService& musicService;
|
||||
|
||||
std::string artist;
|
||||
|
|
|
@ -11,6 +11,7 @@ extern lv_font_t jetbrains_mono_bold_20;
|
|||
Notifications::Notifications(DisplayApp* app,
|
||||
Pinetime::Controllers::NotificationManager& notificationManager,
|
||||
Pinetime::Controllers::AlertNotificationService& alertNotificationService,
|
||||
Pinetime::Controllers::MotorController& motorController,
|
||||
Modes mode)
|
||||
: Screen(app), notificationManager {notificationManager}, alertNotificationService {alertNotificationService}, mode {mode} {
|
||||
notificationManager.ClearNewNotificationFlag();
|
||||
|
@ -36,25 +37,31 @@ Notifications::Notifications(DisplayApp* app,
|
|||
}
|
||||
|
||||
if (mode == Modes::Preview) {
|
||||
if (notification.category == Controllers::NotificationManager::Categories::IncomingCall) {
|
||||
motorController.StartRinging();
|
||||
} else {
|
||||
motorController.RunForDuration(35);
|
||||
timeoutLine = lv_line_create(lv_scr_act(), nullptr);
|
||||
|
||||
timeoutLine = lv_line_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_line_width(timeoutLine, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3);
|
||||
lv_obj_set_style_local_line_color(timeoutLine, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||
lv_obj_set_style_local_line_rounded(timeoutLine, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, true);
|
||||
|
||||
lv_obj_set_style_local_line_width(timeoutLine, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3);
|
||||
lv_obj_set_style_local_line_color(timeoutLine, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||
lv_obj_set_style_local_line_rounded(timeoutLine, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, true);
|
||||
|
||||
lv_line_set_points(timeoutLine, timeoutLinePoints, 2);
|
||||
timeoutTickCountStart = xTaskGetTickCount();
|
||||
timeoutTickCountEnd = timeoutTickCountStart + (5 * 1024);
|
||||
lv_line_set_points(timeoutLine, timeoutLinePoints, 2);
|
||||
timeoutTickCountStart = xTaskGetTickCount();
|
||||
timeoutTickCountEnd = timeoutTickCountStart + (5 * 1024);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Notifications::~Notifications() {
|
||||
// make sure we stop any vibrations before exiting
|
||||
Controllers::MotorController::StopRinging();
|
||||
lv_obj_clean(lv_scr_act());
|
||||
}
|
||||
|
||||
bool Notifications::Refresh() {
|
||||
if (mode == Modes::Preview) {
|
||||
if (mode == Modes::Preview && timeoutLine != nullptr) {
|
||||
auto tick = xTaskGetTickCount();
|
||||
int32_t pos = 240 - ((tick - timeoutTickCountStart) / ((timeoutTickCountEnd - timeoutTickCountStart) / 240));
|
||||
if (pos < 0)
|
||||
|
@ -63,13 +70,13 @@ bool Notifications::Refresh() {
|
|||
timeoutLinePoints[1].x = pos;
|
||||
lv_line_set_points(timeoutLine, timeoutLinePoints, 2);
|
||||
}
|
||||
|
||||
return running;
|
||||
return running && currentItem->IsRunning();
|
||||
}
|
||||
|
||||
bool Notifications::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
|
||||
if (mode != Modes::Normal)
|
||||
return true;
|
||||
if (mode != Modes::Normal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (event) {
|
||||
case Pinetime::Applications::TouchEvents::SwipeDown: {
|
||||
|
@ -130,19 +137,9 @@ bool Notifications::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
|
|||
}
|
||||
|
||||
namespace {
|
||||
static void AcceptIncomingCallEventHandler(lv_obj_t* obj, lv_event_t event) {
|
||||
void CallEventHandler(lv_obj_t* obj, lv_event_t event) {
|
||||
auto* item = static_cast<Notifications::NotificationItem*>(obj->user_data);
|
||||
item->OnAcceptIncomingCall(event);
|
||||
}
|
||||
|
||||
static void MuteIncomingCallEventHandler(lv_obj_t* obj, lv_event_t event) {
|
||||
auto* item = static_cast<Notifications::NotificationItem*>(obj->user_data);
|
||||
item->OnMuteIncomingCall(event);
|
||||
}
|
||||
|
||||
static void RejectIncomingCallEventHandler(lv_obj_t* obj, lv_event_t event) {
|
||||
auto* item = static_cast<Notifications::NotificationItem*>(obj->user_data);
|
||||
item->OnRejectIncomingCall(event);
|
||||
item->OnCallButtonEvent(obj, event);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,7 +151,6 @@ Notifications::NotificationItem::NotificationItem(const char* title,
|
|||
Modes mode,
|
||||
Pinetime::Controllers::AlertNotificationService& alertNotificationService)
|
||||
: notifNr {notifNr}, notifNb {notifNb}, mode {mode}, alertNotificationService {alertNotificationService} {
|
||||
|
||||
lv_obj_t* container1 = lv_cont_create(lv_scr_act(), NULL);
|
||||
|
||||
lv_obj_set_style_local_bg_color(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x222222));
|
||||
|
@ -212,7 +208,7 @@ Notifications::NotificationItem::NotificationItem(const char* title,
|
|||
|
||||
bt_accept = lv_btn_create(lv_scr_act(), nullptr);
|
||||
bt_accept->user_data = this;
|
||||
lv_obj_set_event_cb(bt_accept, AcceptIncomingCallEventHandler);
|
||||
lv_obj_set_event_cb(bt_accept, CallEventHandler);
|
||||
lv_obj_set_size(bt_accept, 76, 76);
|
||||
lv_obj_align(bt_accept, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
|
||||
label_accept = lv_label_create(bt_accept, nullptr);
|
||||
|
@ -221,7 +217,7 @@ Notifications::NotificationItem::NotificationItem(const char* title,
|
|||
|
||||
bt_reject = lv_btn_create(lv_scr_act(), nullptr);
|
||||
bt_reject->user_data = this;
|
||||
lv_obj_set_event_cb(bt_reject, RejectIncomingCallEventHandler);
|
||||
lv_obj_set_event_cb(bt_reject, CallEventHandler);
|
||||
lv_obj_set_size(bt_reject, 76, 76);
|
||||
lv_obj_align(bt_reject, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
|
||||
label_reject = lv_label_create(bt_reject, nullptr);
|
||||
|
@ -230,7 +226,7 @@ Notifications::NotificationItem::NotificationItem(const char* title,
|
|||
|
||||
bt_mute = lv_btn_create(lv_scr_act(), nullptr);
|
||||
bt_mute->user_data = this;
|
||||
lv_obj_set_event_cb(bt_mute, MuteIncomingCallEventHandler);
|
||||
lv_obj_set_event_cb(bt_mute, CallEventHandler);
|
||||
lv_obj_set_size(bt_mute, 76, 76);
|
||||
lv_obj_align(bt_mute, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
|
||||
label_mute = lv_label_create(bt_mute, nullptr);
|
||||
|
@ -246,25 +242,22 @@ Notifications::NotificationItem::NotificationItem(const char* title,
|
|||
lv_label_set_text(backgroundLabel, "");
|
||||
}
|
||||
|
||||
void Notifications::NotificationItem::OnAcceptIncomingCall(lv_event_t event) {
|
||||
if (event != LV_EVENT_CLICKED)
|
||||
void Notifications::NotificationItem::OnCallButtonEvent(lv_obj_t* obj, lv_event_t event) {
|
||||
if (event != LV_EVENT_CLICKED) {
|
||||
return;
|
||||
}
|
||||
|
||||
alertNotificationService.AcceptIncomingCall();
|
||||
}
|
||||
Controllers::MotorController::StopRinging();
|
||||
|
||||
void Notifications::NotificationItem::OnMuteIncomingCall(lv_event_t event) {
|
||||
if (event != LV_EVENT_CLICKED)
|
||||
return;
|
||||
if (obj == bt_accept) {
|
||||
alertNotificationService.AcceptIncomingCall();
|
||||
} else if (obj == bt_reject) {
|
||||
alertNotificationService.RejectIncomingCall();
|
||||
} else if (obj == bt_mute) {
|
||||
alertNotificationService.MuteIncomingCall();
|
||||
}
|
||||
|
||||
alertNotificationService.MuteIncomingCall();
|
||||
}
|
||||
|
||||
void Notifications::NotificationItem::OnRejectIncomingCall(lv_event_t event) {
|
||||
if (event != LV_EVENT_CLICKED)
|
||||
return;
|
||||
|
||||
alertNotificationService.RejectIncomingCall();
|
||||
running = false;
|
||||
}
|
||||
|
||||
Notifications::NotificationItem::~NotificationItem() {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <memory>
|
||||
#include "Screen.h"
|
||||
#include "components/ble/NotificationManager.h"
|
||||
#include "components/motor/MotorController.h"
|
||||
|
||||
namespace Pinetime {
|
||||
namespace Controllers {
|
||||
|
@ -19,6 +20,7 @@ namespace Pinetime {
|
|||
explicit Notifications(DisplayApp* app,
|
||||
Pinetime::Controllers::NotificationManager& notificationManager,
|
||||
Pinetime::Controllers::AlertNotificationService& alertNotificationService,
|
||||
Pinetime::Controllers::MotorController& motorController,
|
||||
Modes mode);
|
||||
~Notifications() override;
|
||||
|
||||
|
@ -35,12 +37,10 @@ namespace Pinetime {
|
|||
Modes mode,
|
||||
Pinetime::Controllers::AlertNotificationService& alertNotificationService);
|
||||
~NotificationItem();
|
||||
bool Refresh() {
|
||||
return false;
|
||||
bool IsRunning() const {
|
||||
return running;
|
||||
}
|
||||
void OnAcceptIncomingCall(lv_event_t event);
|
||||
void OnMuteIncomingCall(lv_event_t event);
|
||||
void OnRejectIncomingCall(lv_event_t event);
|
||||
void OnCallButtonEvent(lv_obj_t*, lv_event_t event);
|
||||
|
||||
private:
|
||||
uint8_t notifNr = 0;
|
||||
|
@ -60,6 +60,7 @@ namespace Pinetime {
|
|||
lv_obj_t* bottomPlaceholder;
|
||||
Modes mode;
|
||||
Pinetime::Controllers::AlertNotificationService& alertNotificationService;
|
||||
bool running = true;
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -75,7 +76,7 @@ namespace Pinetime {
|
|||
bool validDisplay = false;
|
||||
|
||||
lv_point_t timeoutLinePoints[2] {{0, 1}, {239, 1}};
|
||||
lv_obj_t* timeoutLine;
|
||||
lv_obj_t* timeoutLine = nullptr;
|
||||
uint32_t timeoutTickCountStart;
|
||||
uint32_t timeoutTickCountEnd;
|
||||
};
|
||||
|
|
|
@ -35,37 +35,35 @@ Tile::Tile(uint8_t screenID,
|
|||
label_time = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_text_fmt(label_time, "%02i:%02i", dateTimeController.Hours(), dateTimeController.Minutes());
|
||||
lv_label_set_align(label_time, LV_LABEL_ALIGN_CENTER);
|
||||
lv_obj_align(label_time, nullptr, LV_ALIGN_IN_TOP_LEFT, 15, 6);
|
||||
lv_obj_align(label_time, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
|
||||
|
||||
// Battery
|
||||
batteryIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_text(batteryIcon, BatteryIcon::GetBatteryIcon(batteryController.PercentRemaining()));
|
||||
lv_obj_align(batteryIcon, nullptr, LV_ALIGN_IN_TOP_RIGHT, -15, 6);
|
||||
lv_obj_align(batteryIcon, nullptr, LV_ALIGN_IN_TOP_RIGHT, -8, 0);
|
||||
|
||||
if (numScreens > 1) {
|
||||
pageIndicatorBasePoints[0].x = 240 - 1;
|
||||
pageIndicatorBasePoints[0].y = 6;
|
||||
pageIndicatorBasePoints[1].x = 240 - 1;
|
||||
pageIndicatorBasePoints[1].y = 240 - 6;
|
||||
pageIndicatorBasePoints[0].x = LV_HOR_RES - 1;
|
||||
pageIndicatorBasePoints[0].y = 0;
|
||||
pageIndicatorBasePoints[1].x = LV_HOR_RES - 1;
|
||||
pageIndicatorBasePoints[1].y = LV_VER_RES;
|
||||
|
||||
pageIndicatorBase = lv_line_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_line_width(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3);
|
||||
lv_obj_set_style_local_line_color(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111));
|
||||
lv_obj_set_style_local_line_rounded(pageIndicatorBase, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, true);
|
||||
lv_line_set_points(pageIndicatorBase, pageIndicatorBasePoints, 2);
|
||||
|
||||
uint16_t indicatorSize = 228 / numScreens;
|
||||
uint16_t indicatorPos = indicatorSize * screenID;
|
||||
const uint16_t indicatorSize = LV_VER_RES / numScreens;
|
||||
const uint16_t indicatorPos = indicatorSize * screenID;
|
||||
|
||||
pageIndicatorPoints[0].x = 240 - 1;
|
||||
pageIndicatorPoints[0].y = 6 + indicatorPos;
|
||||
pageIndicatorPoints[1].x = 240 - 1;
|
||||
pageIndicatorPoints[1].y = 6 + indicatorPos + indicatorSize;
|
||||
pageIndicatorPoints[0].x = LV_HOR_RES - 1;
|
||||
pageIndicatorPoints[0].y = indicatorPos;
|
||||
pageIndicatorPoints[1].x = LV_HOR_RES - 1;
|
||||
pageIndicatorPoints[1].y = indicatorPos + indicatorSize;
|
||||
|
||||
pageIndicator = lv_line_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_line_width(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 3);
|
||||
lv_obj_set_style_local_line_color(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
|
||||
lv_obj_set_style_local_line_rounded(pageIndicator, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, true);
|
||||
lv_line_set_points(pageIndicator, pageIndicatorPoints, 2);
|
||||
}
|
||||
|
||||
|
@ -85,7 +83,7 @@ Tile::Tile(uint8_t screenID,
|
|||
|
||||
btnm1 = lv_btnmatrix_create(lv_scr_act(), nullptr);
|
||||
lv_btnmatrix_set_map(btnm1, btnmMap);
|
||||
lv_obj_set_size(btnm1, LV_HOR_RES - 10, LV_VER_RES - 60);
|
||||
lv_obj_set_size(btnm1, LV_HOR_RES - 16, LV_VER_RES - 60);
|
||||
lv_obj_align(btnm1, NULL, LV_ALIGN_CENTER, 0, 10);
|
||||
|
||||
lv_obj_set_style_local_radius(btnm1, LV_BTNMATRIX_PART_BTN, LV_STATE_DEFAULT, 20);
|
||||
|
@ -93,6 +91,8 @@ Tile::Tile(uint8_t screenID,
|
|||
lv_obj_set_style_local_bg_color(btnm1, LV_BTNMATRIX_PART_BTN, LV_STATE_DEFAULT, LV_COLOR_AQUA);
|
||||
lv_obj_set_style_local_bg_opa(btnm1, LV_BTNMATRIX_PART_BTN, LV_STATE_DISABLED, LV_OPA_20);
|
||||
lv_obj_set_style_local_bg_color(btnm1, LV_BTNMATRIX_PART_BTN, LV_STATE_DISABLED, lv_color_hex(0x111111));
|
||||
lv_obj_set_style_local_pad_all(btnm1, LV_BTNMATRIX_PART_BG, LV_STATE_DEFAULT, 0);
|
||||
lv_obj_set_style_local_pad_inner(btnm1, LV_BTNMATRIX_PART_BG, LV_STATE_DEFAULT, 10);
|
||||
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
if (applications[i].application == Apps::None) {
|
||||
|
|
|
@ -67,12 +67,12 @@ WatchFaceAnalog::WatchFaceAnalog(Pinetime::Applications::DisplayApp* app,
|
|||
|
||||
batteryIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_text(batteryIcon, Symbols::batteryHalf);
|
||||
lv_obj_align(batteryIcon, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, -8, -4);
|
||||
lv_obj_align(batteryIcon, NULL, LV_ALIGN_IN_TOP_RIGHT, 0, 0);
|
||||
|
||||
notificationIcon = lv_label_create(lv_scr_act(), NULL);
|
||||
lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FF00));
|
||||
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false));
|
||||
lv_obj_align(notificationIcon, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 8, -4);
|
||||
lv_obj_align(notificationIcon, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
|
||||
|
||||
// Date - Day / Week day
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ WatchFaceDigital::WatchFaceDigital(DisplayApp* app,
|
|||
|
||||
batteryIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_text(batteryIcon, Symbols::batteryFull);
|
||||
lv_obj_align(batteryIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, -5, 2);
|
||||
lv_obj_align(batteryIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, 0, 0);
|
||||
|
||||
batteryPlug = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_color(batteryPlug, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFF0000));
|
||||
|
@ -50,7 +50,7 @@ WatchFaceDigital::WatchFaceDigital(DisplayApp* app,
|
|||
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_hex(0x00FF00));
|
||||
lv_label_set_text(notificationIcon, NotificationIcon::GetIcon(false));
|
||||
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 10, 0);
|
||||
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
|
||||
|
||||
label_date = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_CENTER, 0, 60);
|
||||
|
@ -75,7 +75,7 @@ WatchFaceDigital::WatchFaceDigital(DisplayApp* app,
|
|||
heartbeatIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_text(heartbeatIcon, Symbols::heartBeat);
|
||||
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xCE1B1B));
|
||||
lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2);
|
||||
lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
|
||||
|
||||
heartbeatValue = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_color(heartbeatValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xCE1B1B));
|
||||
|
@ -85,7 +85,7 @@ WatchFaceDigital::WatchFaceDigital(DisplayApp* app,
|
|||
stepValue = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_color(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FFE7));
|
||||
lv_label_set_text(stepValue, "0");
|
||||
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2);
|
||||
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
|
||||
|
||||
stepIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x00FFE7));
|
||||
|
@ -110,7 +110,7 @@ bool WatchFaceDigital::Refresh() {
|
|||
if (bleState.IsUpdated()) {
|
||||
lv_label_set_text(bleIcon, BleIcon::GetIcon(bleState.Get()));
|
||||
}
|
||||
lv_obj_align(batteryIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, -5, 5);
|
||||
lv_obj_align(batteryIcon, lv_scr_act(), LV_ALIGN_IN_TOP_RIGHT, 0, 0);
|
||||
lv_obj_align(batteryPlug, batteryIcon, LV_ALIGN_OUT_LEFT_MID, -5, 0);
|
||||
lv_obj_align(bleIcon, batteryPlug, LV_ALIGN_OUT_LEFT_MID, -5, 0);
|
||||
|
||||
|
@ -208,7 +208,7 @@ bool WatchFaceDigital::Refresh() {
|
|||
lv_label_set_text_static(heartbeatValue, "");
|
||||
}
|
||||
|
||||
lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2);
|
||||
lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
|
||||
lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
|
||||
}
|
||||
|
||||
|
@ -216,7 +216,7 @@ bool WatchFaceDigital::Refresh() {
|
|||
motionSensorOk = motionController.IsSensorOk();
|
||||
if (stepCount.IsUpdated() || motionSensorOk.IsUpdated()) {
|
||||
lv_label_set_text_fmt(stepValue, "%lu", stepCount.Get());
|
||||
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2);
|
||||
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
|
||||
lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,27 +30,35 @@ QuickSettings::QuickSettings(Pinetime::Applications::DisplayApp* app,
|
|||
motorController {motorController},
|
||||
settingsController {settingsController} {
|
||||
|
||||
// This is the distance (padding) between all objects on this screen.
|
||||
static constexpr uint8_t innerDistance = 10;
|
||||
|
||||
// Time
|
||||
label_time = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_text_fmt(label_time, "%02i:%02i", dateTimeController.Hours(), dateTimeController.Minutes());
|
||||
lv_label_set_align(label_time, LV_LABEL_ALIGN_CENTER);
|
||||
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 15, 4);
|
||||
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 0, 0);
|
||||
|
||||
batteryIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_text(batteryIcon, BatteryIcon::GetBatteryIcon(batteryController.PercentRemaining()));
|
||||
lv_obj_align(batteryIcon, nullptr, LV_ALIGN_IN_TOP_RIGHT, -15, 4);
|
||||
lv_obj_align(batteryIcon, nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 0);
|
||||
|
||||
lv_obj_t* lbl_btn;
|
||||
static constexpr uint8_t barHeight = 20 + innerDistance;
|
||||
static constexpr uint8_t buttonHeight = (LV_VER_RES_MAX - barHeight - innerDistance) / 2;
|
||||
static constexpr uint8_t buttonWidth = (LV_HOR_RES_MAX - innerDistance) / 2; // wide buttons
|
||||
//static constexpr uint8_t buttonWidth = buttonHeight; // square buttons
|
||||
static constexpr uint8_t buttonXOffset = (LV_HOR_RES_MAX - buttonWidth * 2 - innerDistance) / 2;
|
||||
|
||||
lv_style_init(&btn_style);
|
||||
lv_style_set_radius(&btn_style, LV_STATE_DEFAULT, buttonHeight / 4);
|
||||
lv_style_set_bg_color(&btn_style, LV_STATE_DEFAULT, lv_color_hex(0x111111));
|
||||
|
||||
btn1 = lv_btn_create(lv_scr_act(), nullptr);
|
||||
btn1->user_data = this;
|
||||
lv_obj_set_event_cb(btn1, ButtonEventHandler);
|
||||
lv_obj_align(btn1, nullptr, LV_ALIGN_CENTER, -50, -30);
|
||||
lv_obj_set_style_local_radius(btn1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 20);
|
||||
lv_obj_set_style_local_bg_color(btn1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111));
|
||||
lv_obj_set_style_local_bg_grad_dir(btn1, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_GRAD_DIR_NONE);
|
||||
|
||||
lv_btn_set_fit2(btn1, LV_FIT_TIGHT, LV_FIT_TIGHT);
|
||||
lv_obj_add_style(btn1, LV_BTN_PART_MAIN, &btn_style);
|
||||
lv_obj_set_size(btn1, buttonWidth, buttonHeight);
|
||||
lv_obj_align(btn1, nullptr, LV_ALIGN_IN_TOP_LEFT, buttonXOffset, barHeight);
|
||||
|
||||
btn1_lvl = lv_label_create(btn1, nullptr);
|
||||
lv_obj_set_style_local_text_font(btn1_lvl, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48);
|
||||
|
@ -59,12 +67,11 @@ QuickSettings::QuickSettings(Pinetime::Applications::DisplayApp* app,
|
|||
btn2 = lv_btn_create(lv_scr_act(), nullptr);
|
||||
btn2->user_data = this;
|
||||
lv_obj_set_event_cb(btn2, ButtonEventHandler);
|
||||
lv_obj_align(btn2, nullptr, LV_ALIGN_CENTER, 50, -30);
|
||||
lv_obj_set_style_local_radius(btn2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 20);
|
||||
lv_obj_set_style_local_bg_color(btn2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111));
|
||||
lv_obj_set_style_local_bg_grad_dir(btn2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_GRAD_DIR_NONE);
|
||||
lv_btn_set_fit2(btn2, LV_FIT_TIGHT, LV_FIT_TIGHT);
|
||||
lv_obj_add_style(btn2, LV_BTN_PART_MAIN, &btn_style);
|
||||
lv_obj_set_size(btn2, buttonWidth, buttonHeight);
|
||||
lv_obj_align(btn2, nullptr, LV_ALIGN_IN_TOP_RIGHT, - buttonXOffset, barHeight);
|
||||
|
||||
lv_obj_t* lbl_btn;
|
||||
lbl_btn = lv_label_create(btn2, nullptr);
|
||||
lv_obj_set_style_local_text_font(lbl_btn, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48);
|
||||
lv_label_set_text_static(lbl_btn, Symbols::highlight);
|
||||
|
@ -72,14 +79,11 @@ QuickSettings::QuickSettings(Pinetime::Applications::DisplayApp* app,
|
|||
btn3 = lv_btn_create(lv_scr_act(), nullptr);
|
||||
btn3->user_data = this;
|
||||
lv_obj_set_event_cb(btn3, ButtonEventHandler);
|
||||
lv_obj_align(btn3, nullptr, LV_ALIGN_CENTER, -50, 60);
|
||||
lv_btn_set_checkable(btn3, true);
|
||||
lv_obj_set_style_local_radius(btn3, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 20);
|
||||
lv_obj_set_style_local_bg_color(btn3, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111));
|
||||
lv_obj_set_style_local_bg_grad_dir(btn3, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_GRAD_DIR_NONE);
|
||||
lv_obj_add_style(btn3, LV_BTN_PART_MAIN, &btn_style);
|
||||
lv_obj_set_style_local_bg_color(btn3, LV_BTN_PART_MAIN, LV_STATE_CHECKED, LV_COLOR_GREEN);
|
||||
lv_obj_set_style_local_bg_grad_dir(btn1, LV_BTN_PART_MAIN, LV_STATE_CHECKED, LV_GRAD_DIR_NONE);
|
||||
lv_btn_set_fit2(btn3, LV_FIT_TIGHT, LV_FIT_TIGHT);
|
||||
lv_obj_set_size(btn3, buttonWidth, buttonHeight);
|
||||
lv_obj_align(btn3, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, buttonXOffset, 0);
|
||||
|
||||
btn3_lvl = lv_label_create(btn3, nullptr);
|
||||
lv_obj_set_style_local_text_font(btn3_lvl, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48);
|
||||
|
@ -94,11 +98,9 @@ QuickSettings::QuickSettings(Pinetime::Applications::DisplayApp* app,
|
|||
btn4 = lv_btn_create(lv_scr_act(), nullptr);
|
||||
btn4->user_data = this;
|
||||
lv_obj_set_event_cb(btn4, ButtonEventHandler);
|
||||
lv_obj_align(btn4, nullptr, LV_ALIGN_CENTER, 50, 60);
|
||||
lv_obj_set_style_local_radius(btn4, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 20);
|
||||
lv_obj_set_style_local_bg_color(btn4, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x111111));
|
||||
lv_obj_set_style_local_bg_grad_dir(btn4, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_GRAD_DIR_NONE);
|
||||
lv_btn_set_fit2(btn4, LV_FIT_TIGHT, LV_FIT_TIGHT);
|
||||
lv_obj_add_style(btn4, LV_BTN_PART_MAIN, &btn_style);
|
||||
lv_obj_set_size(btn4, buttonWidth, buttonHeight);
|
||||
lv_obj_align(btn4, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, - buttonXOffset, 0);
|
||||
|
||||
lbl_btn = lv_label_create(btn4, nullptr);
|
||||
lv_obj_set_style_local_text_font(lbl_btn, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_sys_48);
|
||||
|
@ -114,6 +116,7 @@ QuickSettings::QuickSettings(Pinetime::Applications::DisplayApp* app,
|
|||
}
|
||||
|
||||
QuickSettings::~QuickSettings() {
|
||||
lv_style_reset(&btn_style);
|
||||
lv_task_del(taskUpdate);
|
||||
lv_obj_clean(lv_scr_act());
|
||||
settingsController.SaveSettings();
|
||||
|
@ -140,7 +143,7 @@ void QuickSettings::OnButtonEvent(lv_obj_t* object, lv_event_t event) {
|
|||
|
||||
if (lv_obj_get_state(btn3, LV_BTN_PART_MAIN) & LV_STATE_CHECKED) {
|
||||
settingsController.SetVibrationStatus(Controllers::Settings::Vibration::ON);
|
||||
motorController.SetDuration(35);
|
||||
motorController.RunForDuration(35);
|
||||
lv_label_set_text_static(btn3_lvl, Symbols::notificationsOn);
|
||||
} else {
|
||||
settingsController.SetVibrationStatus(Controllers::Settings::Vibration::OFF);
|
||||
|
|
|
@ -44,6 +44,8 @@ namespace Pinetime {
|
|||
lv_obj_t* batteryIcon;
|
||||
lv_obj_t* label_time;
|
||||
|
||||
lv_style_t btn_style;
|
||||
|
||||
lv_obj_t* btn1;
|
||||
lv_obj_t* btn1_lvl;
|
||||
lv_obj_t* btn2;
|
||||
|
|
15
src/main.cpp
15
src/main.cpp
|
@ -5,6 +5,7 @@
|
|||
#include <libraries/gpiote/app_gpiote.h>
|
||||
#include <libraries/timer/app_timer.h>
|
||||
#include <softdevice/common/nrf_sdh.h>
|
||||
#include <nrf_delay.h>
|
||||
|
||||
// nimble
|
||||
#define min // workaround: nimble's min/max macros conflict with libstdc++
|
||||
|
@ -300,6 +301,20 @@ int main(void) {
|
|||
|
||||
nrf_drv_clock_init();
|
||||
|
||||
// Unblock i2c?
|
||||
nrf_gpio_cfg(pinTwiScl,
|
||||
NRF_GPIO_PIN_DIR_OUTPUT,
|
||||
NRF_GPIO_PIN_INPUT_DISCONNECT,
|
||||
NRF_GPIO_PIN_NOPULL,
|
||||
NRF_GPIO_PIN_S0D1,
|
||||
NRF_GPIO_PIN_NOSENSE);
|
||||
nrf_gpio_pin_set(pinTwiScl);
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
nrf_gpio_pin_toggle(pinTwiScl);
|
||||
nrf_delay_us(5);
|
||||
}
|
||||
nrf_gpio_cfg_default(pinTwiScl);
|
||||
|
||||
debounceTimer = xTimerCreate("debounceTimer", 200, pdFALSE, (void*) 0, DebounceTimerCallback);
|
||||
debounceChargeTimer = xTimerCreate("debounceTimerCharge", 200, pdFALSE, (void*) 0, DebounceTimerChargeCallback);
|
||||
|
||||
|
|
|
@ -266,14 +266,13 @@ void SystemTask::Work() {
|
|||
if (isSleeping && !isWakingUp) {
|
||||
GoToRunning();
|
||||
}
|
||||
motorController.SetDuration(35);
|
||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::NewNotification);
|
||||
break;
|
||||
case Messages::OnTimerDone:
|
||||
if (isSleeping && !isWakingUp) {
|
||||
GoToRunning();
|
||||
}
|
||||
motorController.SetDuration(35);
|
||||
motorController.RunForDuration(35);
|
||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::TimerDone);
|
||||
break;
|
||||
case Messages::BleConnected:
|
||||
|
@ -326,7 +325,7 @@ void SystemTask::Work() {
|
|||
stepCounterMustBeReset = true;
|
||||
break;
|
||||
case Messages::OnChargingEvent:
|
||||
motorController.SetDuration(15);
|
||||
motorController.RunForDuration(15);
|
||||
// Battery level is updated on every message - there's no need to do anything
|
||||
break;
|
||||
|
||||
|
|
Loading…
Reference in a new issue