Compare commits
48 Commits
Watch_Bina
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
77f1a3e230 | ||
|
11acaeadd3 | ||
4fa8e02289 | |||
0cc8017cbd | |||
d758394ce9 | |||
dd5d65d315 | |||
2bffcf99f0 | |||
|
9b8eb75f34 | ||
|
b191a30947 | ||
|
d930fd4fa2 | ||
|
e6b96c2863 | ||
|
77546c9fe2 | ||
e24323b454 | |||
79820669b6 | |||
1ff782cb0e | |||
5d7e1ae1f3 | |||
415ff6c05c | |||
2cb36d6e80 | |||
cbfd82959e | |||
ce136ea148 | |||
ab03504d0c | |||
e4b4fba5a1 | |||
2ffc63b1d8 | |||
31250fd579 | |||
94044d2fdc | |||
|
858bf4f96e | ||
|
1487a16f4a | ||
|
13af2bb96c | ||
e0e2126c29 | |||
6a772e7e8e | |||
191f99904c | |||
8ada9410a1 | |||
bbfef9ee5c | |||
886e425461 | |||
|
6bbdb581cc | ||
|
270d15a8ea | ||
|
bf8c4dfef8 | ||
|
ecbcce41d7 | ||
|
3d75a7dc9f | ||
|
9d749434b5 | ||
|
8ff6bd9cae | ||
|
eac460f030 | ||
|
46b664b528 | ||
|
b488811f7f | ||
|
e19e870290 | ||
cdd97b3871 | |||
b95823e745 | |||
292b7e3fb1 |
@ -11,6 +11,7 @@ RUN apt-get update -qq \
|
||||
make \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-pil \
|
||||
tar \
|
||||
unzip \
|
||||
wget \
|
||||
|
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@ -31,6 +31,10 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install resource build dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get -y install --no-install-recommends python3-pil
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: /opt/build.sh all
|
||||
|
@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose Debug or Release")
|
||||
|
||||
project(pinetime VERSION 1.13.0 LANGUAGES C CXX ASM)
|
||||
project(pinetime VERSION 1.13.1.1 LANGUAGES C CXX ASM)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
18
README.md
18
README.md
@ -1,5 +1,8 @@
|
||||
# TODO
|
||||
## Completed
|
||||
### Pending version
|
||||
|
||||
### v1.13.1
|
||||
- [x] Make $s in terminal watch face green
|
||||
- [x] Swipe left to access music control
|
||||
- [x] Flashlight starts on
|
||||
@ -9,6 +12,8 @@
|
||||
- [x] Swipe left/right goes to watchface/another app
|
||||
- [x] Rearrange quick acces and apps
|
||||
- [x] Exponent button on calculator
|
||||
- [x] Seconds on digital watchface
|
||||
- [x] Add quick ring settings
|
||||
|
||||
## Pending merge
|
||||
|
||||
@ -17,18 +22,17 @@
|
||||
- [ ] Countdown timer presistant buzz
|
||||
|
||||
### Josh
|
||||
- [ ] Seconds on digital watchface
|
||||
- [ ] Battery on digital watchface
|
||||
- [ ] Remap button double click (to music app)
|
||||
- [ ] Smiley face watchface :)
|
||||
- [ ] Remap button double click
|
||||
|
||||
### Moses
|
||||
|
||||
|
||||
- [ ] Add quick ring settings
|
||||
|
||||
## Medium priority
|
||||
- [ ] Temp sensor w/ arduino
|
||||
- [ ] Homeassistant control
|
||||
- [ ] Battery on digital watchface
|
||||
- [ ] More watchfaces!
|
||||
|
||||
## Lower priority
|
||||
- [ ] Maybe some prank watchfaces
|
||||
- [ ] Temp sensor w/ arduino
|
||||
- [ ] Homeassistant control
|
||||
|
@ -42,7 +42,7 @@ CMake configures the project according to variables you specify the command line
|
||||
**NRF5_SDK_PATH**|path to the NRF52 SDK|`-DNRF5_SDK_PATH=/home/jf/nrf52/Pinetime/sdk`|
|
||||
**CMAKE_BUILD_TYPE (\*)**| Build type (Release or Debug). Release is applied by default if this variable is not specified.|`-DCMAKE_BUILD_TYPE=Debug`
|
||||
**BUILD_DFU (\*\*)**|Build DFU files while building (needs [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil)).|`-DBUILD_DFU=1`
|
||||
**BUILD_RESOURCES (\*\*)**| Generate external resource while building (needs [lv_font_conv](https://github.com/lvgl/lv_font_conv) and [lv_img_conv](https://github.com/lvgl/lv_img_conv). |`-DBUILD_RESOURCES=1`
|
||||
**BUILD_RESOURCES (\*\*)**| Generate external resource while building (needs [lv_font_conv](https://github.com/lvgl/lv_font_conv) and [python3-pil/pillow](https://pillow.readthedocs.io) module). |`-DBUILD_RESOURCES=1`
|
||||
**TARGET_DEVICE**|Target device, used for hardware configuration. Allowed: `PINETIME, MOY-TFK5, MOY-TIN5, MOY-TON5, MOY-UNK`|`-DTARGET_DEVICE=PINETIME` (Default)
|
||||
|
||||
#### (\*) Note about **CMAKE_BUILD_TYPE**
|
||||
@ -98,4 +98,4 @@ Binary files are generated into the folder `src`:
|
||||
- **pinetime-mcuboot-app-image** : MCUBoot image of the firmware
|
||||
- **pinetime-mcuboot-app-dfu** : DFU file of the firmware
|
||||
|
||||
The same files are generated for **pinetime-recovery** and **pinetime-recoveryloader**
|
||||
The same files are generated for **pinetime-recovery** and **pinetime-recovery-loader**
|
||||
|
@ -1,7 +1,13 @@
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ARG NODE_MAJOR=20
|
||||
RUN apt-get update -qq \
|
||||
&& apt-get install -y ca-certificates curl gnupg \
|
||||
&& mkdir -p /etc/apt/keyrings \
|
||||
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
|
||||
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \
|
||||
&& apt-get update -qq \
|
||||
&& apt-get install -y \
|
||||
# x86_64 / generic packages
|
||||
bash \
|
||||
@ -9,13 +15,14 @@ RUN apt-get update -qq \
|
||||
cmake \
|
||||
git \
|
||||
make \
|
||||
nodejs \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-pil \
|
||||
python-is-python3 \
|
||||
tar \
|
||||
unzip \
|
||||
wget \
|
||||
curl \
|
||||
# aarch64 packages
|
||||
libffi-dev \
|
||||
libssl-dev \
|
||||
@ -28,8 +35,6 @@ RUN apt-get update -qq \
|
||||
libpango-1.0-0 \
|
||||
ibpango1.0-dev \
|
||||
libpangocairo-1.0-0 \
|
||||
&& curl -sL https://deb.nodesource.com/setup_18.x | bash - \
|
||||
&& apt-get install -y nodejs \
|
||||
&& rm -rf /var/cache/apt/* /var/lib/apt/lists/*;
|
||||
|
||||
# Git needed for PROJECT_GIT_COMMIT_HASH variable setting
|
||||
@ -39,10 +44,6 @@ RUN pip3 install -Iv cryptography==3.3
|
||||
RUN pip3 install cbor
|
||||
RUN npm i lv_font_conv@1.5.2 -g
|
||||
|
||||
RUN npm i ts-node@10.9.1 -g
|
||||
RUN npm i @swc/core -g
|
||||
RUN npm i lv_img_conv@0.3.0 -g
|
||||
|
||||
# build.sh knows how to compile
|
||||
COPY build.sh /opt/
|
||||
|
||||
|
@ -426,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
|
||||
@ -434,6 +435,7 @@ list(APPEND SOURCE_FILES
|
||||
displayapp/screens/WatchFaceTerminal.cpp
|
||||
displayapp/screens/WatchFacePineTimeStyle.cpp
|
||||
displayapp/screens/WatchFaceCasioStyleG7710.cpp
|
||||
displayapp/screens/WatchFaceFace.cpp
|
||||
|
||||
##
|
||||
|
||||
|
@ -404,7 +404,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::Clouds>& WeatherService::GetCurrentClouds() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::Clouds && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::Clouds && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::Clouds>&>(header);
|
||||
}
|
||||
}
|
||||
@ -415,7 +416,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::Obscuration>& WeatherService::GetCurrentObscuration() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::Obscuration && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::Obscuration && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::Obscuration>&>(header);
|
||||
}
|
||||
}
|
||||
@ -426,7 +428,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::Precipitation>& WeatherService::GetCurrentPrecipitation() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::Precipitation && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::Precipitation && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::Precipitation>&>(header);
|
||||
}
|
||||
}
|
||||
@ -437,7 +440,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::Wind>& WeatherService::GetCurrentWind() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::Wind && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::Wind && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::Wind>&>(header);
|
||||
}
|
||||
}
|
||||
@ -448,7 +452,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::Temperature>& WeatherService::GetCurrentTemperature() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::Temperature && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::Temperature>&>(header);
|
||||
}
|
||||
}
|
||||
@ -459,7 +464,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::Humidity>& WeatherService::GetCurrentHumidity() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::Humidity && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::Humidity && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::Humidity>&>(header);
|
||||
}
|
||||
}
|
||||
@ -470,7 +476,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::Pressure>& WeatherService::GetCurrentPressure() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::Pressure && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::Pressure && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::Pressure>&>(header);
|
||||
}
|
||||
}
|
||||
@ -481,7 +488,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::Location>& WeatherService::GetCurrentLocation() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::Location && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::Location && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::Location>&>(header);
|
||||
}
|
||||
}
|
||||
@ -492,7 +500,8 @@ namespace Pinetime {
|
||||
std::unique_ptr<WeatherData::AirQuality>& WeatherService::GetCurrentQuality() {
|
||||
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
|
||||
for (auto&& header : this->timeline) {
|
||||
if (header->eventType == WeatherData::eventtype::AirQuality && IsEventStillValid(header, currentTimestamp)) {
|
||||
if (header->eventType == WeatherData::eventtype::AirQuality && currentTimestamp >= header->timestamp &&
|
||||
IsEventStillValid(header, currentTimestamp)) {
|
||||
return reinterpret_cast<std::unique_ptr<WeatherData::AirQuality>&>(header);
|
||||
}
|
||||
}
|
||||
|
@ -5,14 +5,58 @@
|
||||
|
||||
using namespace Pinetime::Controllers;
|
||||
|
||||
namespace {
|
||||
TimerHandle_t vibTimer;
|
||||
|
||||
void PatternStep(TimerHandle_t xTimer) {
|
||||
/* Vibration pattern format:
|
||||
* {
|
||||
* durationOfVibration,
|
||||
* durationOfPause,
|
||||
* durationOfVibration,
|
||||
* durationOfPause,
|
||||
* ...,
|
||||
* durationOfVibration,
|
||||
* zeroTerminator
|
||||
* }
|
||||
*
|
||||
* Patterns can be any length
|
||||
* The pattern must end with a duration of vibration and a terminator.
|
||||
*/
|
||||
|
||||
static constexpr uint8_t vibrationPattern[] = {10, 100, 50, 200, 10, 0};
|
||||
|
||||
static size_t patternPosition = 0;
|
||||
if (vibrationPattern[patternPosition] != 0 && xTimerChangePeriod(vibTimer, vibrationPattern[patternPosition] << 1, 0) == pdPASS &&
|
||||
xTimerStart(vibTimer, 0) == pdPASS) {
|
||||
if (patternPosition % 2 == 0) {
|
||||
nrf_gpio_pin_clear(Pinetime::PinMap::Motor);
|
||||
} else {
|
||||
nrf_gpio_pin_set(Pinetime::PinMap::Motor);
|
||||
}
|
||||
patternPosition++;
|
||||
} else {
|
||||
patternPosition = 0;
|
||||
nrf_gpio_pin_set(Pinetime::PinMap::Motor);
|
||||
auto* motorController = static_cast<MotorController*>(pvTimerGetTimerID(xTimer));
|
||||
motorController->PatternFinished();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MotorController::Init() {
|
||||
nrf_gpio_cfg_output(PinMap::Motor);
|
||||
nrf_gpio_pin_set(PinMap::Motor);
|
||||
|
||||
vibTimer = xTimerCreate("vibration", 1, pdFALSE, this, PatternStep);
|
||||
shortVib = xTimerCreate("shortVib", 1, pdFALSE, nullptr, StopMotor);
|
||||
longVib = xTimerCreate("longVib", pdMS_TO_TICKS(1000), pdTRUE, this, Ring);
|
||||
}
|
||||
|
||||
void MotorController::PatternFinished() {
|
||||
patternPlaying = false;
|
||||
}
|
||||
|
||||
void MotorController::Ring(TimerHandle_t xTimer) {
|
||||
auto* motorController = static_cast<MotorController*>(pvTimerGetTimerID(xTimer));
|
||||
motorController->RunForDuration(50);
|
||||
@ -24,6 +68,15 @@ void MotorController::RunForDuration(uint8_t motorDuration) {
|
||||
}
|
||||
}
|
||||
|
||||
bool MotorController::StartPattern() {
|
||||
if (!patternPlaying) {
|
||||
patternPlaying = true;
|
||||
PatternStep(vibTimer);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MotorController::StartRinging() {
|
||||
RunForDuration(50);
|
||||
xTimerStart(longVib, 0);
|
||||
|
@ -15,10 +15,13 @@ namespace Pinetime {
|
||||
void RunForDuration(uint8_t motorDuration);
|
||||
void StartRinging();
|
||||
void StopRinging();
|
||||
void PatternFinished();
|
||||
bool StartPattern();
|
||||
|
||||
private:
|
||||
static void Ring(TimerHandle_t xTimer);
|
||||
static void StopMotor(TimerHandle_t xTimer);
|
||||
bool patternPlaying;
|
||||
TimerHandle_t shortVib;
|
||||
TimerHandle_t longVib;
|
||||
};
|
||||
|
@ -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,14 @@ namespace Pinetime {
|
||||
return getWakeUpModes()[static_cast<size_t>(mode)];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void SetBrightness(Controllers::BrightnessController::Levels level) {
|
||||
if (level != settings.brightLevel) {
|
||||
settingsChanged = true;
|
||||
@ -271,6 +280,26 @@ namespace Pinetime {
|
||||
return bleRadioEnabled;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// New Settings
|
||||
void SetQuickRModes(QuickApp App_now, bool enabled) {
|
||||
if (enabled != isQuickROn(App_now)) {
|
||||
settingsChanged = true;
|
||||
}
|
||||
settings.quickApp.set(static_cast<size_t>(App_now), enabled);
|
||||
};
|
||||
|
||||
std::bitset<5> getQuickRModes() const {
|
||||
return settings.quickApp;
|
||||
}
|
||||
|
||||
bool isQuickROn(const QuickApp app_holder) const {
|
||||
return getQuickRModes()[static_cast<size_t>(app_holder)];
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
Pinetime::Controllers::FS& fs;
|
||||
|
||||
@ -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;
|
||||
|
@ -38,7 +38,8 @@ namespace Pinetime {
|
||||
SettingShakeThreshold,
|
||||
SettingBluetooth,
|
||||
Error,
|
||||
Calculator
|
||||
Calculator,
|
||||
SettingQuickR
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -51,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"
|
||||
|
||||
@ -292,9 +293,9 @@ void DisplayApp::Refresh() {
|
||||
return TouchEvents::SwipeLeft;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if (!currentScreen->OnTouchEvent(gesture)) {
|
||||
if (currentApp == Apps::Clock || currentApp == Apps::Music || currentApp == Apps::Calculator || currentApp == Apps::QuickSettings) {
|
||||
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:
|
||||
if (currentApp == Apps::Clock) {
|
||||
@ -311,26 +312,10 @@ void DisplayApp::Refresh() {
|
||||
}
|
||||
break;
|
||||
case TouchEvents::SwipeRight:
|
||||
if (currentApp == Apps::Clock) {
|
||||
LoadNewScreen(Apps::QuickSettings, DisplayApp::FullRefreshDirections::RightAnim);
|
||||
} else if (currentApp == Apps::QuickSettings) {
|
||||
LoadNewScreen(Apps::Calculator, DisplayApp::FullRefreshDirections::RightAnim);
|
||||
} else if (currentApp == Apps::Calculator) {
|
||||
LoadNewScreen(Apps::Music, DisplayApp::FullRefreshDirections::RightAnim);
|
||||
} else {
|
||||
LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::RightAnim);
|
||||
}
|
||||
gotoquickapp(-1);
|
||||
break;
|
||||
case TouchEvents::SwipeLeft:
|
||||
if (currentApp == Apps::Clock) {
|
||||
LoadNewScreen(Apps::Music, DisplayApp::FullRefreshDirections::LeftAnim);
|
||||
} else if (currentApp == Apps::Music) {
|
||||
LoadNewScreen(Apps::Calculator, DisplayApp::FullRefreshDirections::LeftAnim);
|
||||
} else if (currentApp == Apps::Calculator) {
|
||||
LoadNewScreen(Apps::QuickSettings, DisplayApp::FullRefreshDirections::LeftAnim);
|
||||
} else {
|
||||
LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::LeftAnim);
|
||||
}
|
||||
gotoquickapp(1);
|
||||
break;
|
||||
case TouchEvents::DoubleTap:
|
||||
PushMessageToSystemTask(System::Messages::GoToSleep);
|
||||
@ -349,6 +334,7 @@ void DisplayApp::Refresh() {
|
||||
if (!currentScreen->OnButtonPushed()) {
|
||||
if (currentApp == Apps::Clock) {
|
||||
PushMessageToSystemTask(System::Messages::GoToSleep);
|
||||
|
||||
} else {
|
||||
LoadPreviousScreen();
|
||||
}
|
||||
@ -388,8 +374,19 @@ void DisplayApp::Refresh() {
|
||||
// What should happen here?
|
||||
break;
|
||||
case Messages::Chime:
|
||||
LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None);
|
||||
motorController.RunForDuration(35);
|
||||
|
||||
time_var = dateTimeController.Minutes();
|
||||
if (time_var == 30){
|
||||
//NRF_LOG_INFO("Short: %d", time_var);
|
||||
motorController.StartPattern();
|
||||
}
|
||||
else
|
||||
{
|
||||
//NRF_LOG_INFO("Long: %d", time_var);
|
||||
motorController.RunForDuration(200);
|
||||
}
|
||||
//LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None);
|
||||
|
||||
break;
|
||||
case Messages::OnChargingEvent:
|
||||
RestoreBrightness();
|
||||
@ -442,6 +439,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,
|
||||
@ -583,6 +581,9 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
|
||||
case Apps::Calculator:
|
||||
currentScreen = std::make_unique<Screens::Calculator>();
|
||||
break;
|
||||
case Apps::SettingQuickR:
|
||||
currentScreen = std::make_unique<Screens::SettingQuickR>(settingsController);
|
||||
break;
|
||||
}
|
||||
currentApp = app;
|
||||
}
|
||||
@ -642,3 +643,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;
|
||||
@ -128,7 +130,10 @@ namespace Pinetime {
|
||||
Utility::StaticStack<Apps, returnAppStackSize> returnAppStack;
|
||||
Utility::StaticStack<FullRefreshDirections, returnAppStackSize> appStackDirections;
|
||||
|
||||
int time_var;
|
||||
bool isDimmed = false;
|
||||
int currentQ_app = 2;
|
||||
std::bitset<5> quick_app;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ namespace Pinetime {
|
||||
Terminal = 3,
|
||||
Infineat = 4,
|
||||
CasioStyleG7710 = 5,
|
||||
FaceFace = 6,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"file": "JetBrainsMono-Regular.ttf",
|
||||
"range": "0x25, 0x2b, 0x2d, 0x30-0x3a"
|
||||
"range": "0x25, 0x2b, 0x2d, 0x30-0x3a, 0x4b-0x4d, 0x66, 0x69, 0x6b, 0x6d, 0x74"
|
||||
}
|
||||
],
|
||||
"bpp": 1,
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "displayapp/screens/WatchFaceAnalog.h"
|
||||
#include "displayapp/screens/WatchFacePineTimeStyle.h"
|
||||
#include "displayapp/screens/WatchFaceCasioStyleG7710.h"
|
||||
#include "displayapp/screens/WatchFaceFace.h"
|
||||
|
||||
using namespace Pinetime::Applications::Screens;
|
||||
using namespace Pinetime::Applications;
|
||||
@ -55,6 +56,9 @@ Clock::Clock(Controllers::DateTime& dateTimeController,
|
||||
case WatchFace::CasioStyleG7710:
|
||||
return WatchFaceCasioStyleG7710();
|
||||
break;
|
||||
case WatchFace::FaceFace:
|
||||
return WatchFaceFaceScreen();
|
||||
break;
|
||||
}
|
||||
return WatchFaceDigitalScreen();
|
||||
}()} {
|
||||
@ -127,7 +131,15 @@ std::unique_ptr<Screen> Clock::WatchFaceCasioStyleG7710() {
|
||||
bleController,
|
||||
notificationManager,
|
||||
settingsController,
|
||||
heartRateController,
|
||||
motionController,
|
||||
filesystem);
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<Screen> Clock::WatchFaceFaceScreen() {
|
||||
return std::make_unique<Screens::WatchFaceFace>(dateTimeController,
|
||||
batteryController,
|
||||
bleController,
|
||||
notificationManager,
|
||||
settingsController);
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ namespace Pinetime {
|
||||
std::unique_ptr<Screen> WatchFaceTerminalScreen();
|
||||
std::unique_ptr<Screen> WatchFaceInfineatScreen();
|
||||
std::unique_ptr<Screen> WatchFaceCasioStyleG7710();
|
||||
std::unique_ptr<Screen> WatchFaceFaceScreen();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "displayapp/DisplayApp.h"
|
||||
#include "displayapp/screens/Symbols.h"
|
||||
#include "displayapp/InfiniTimeTheme.h"
|
||||
#include <libraries/log/nrf_log.h>
|
||||
|
||||
using namespace Pinetime::Applications::Screens;
|
||||
|
||||
@ -58,8 +59,17 @@ FlashLight::~FlashLight() {
|
||||
}
|
||||
|
||||
void FlashLight::SetColors() {
|
||||
lv_color_t bgColor = isOn ? LV_COLOR_WHITE : LV_COLOR_BLACK;
|
||||
lv_color_t fgColor = isOn ? Colors::lightGray : LV_COLOR_WHITE;
|
||||
lv_color_t bgColor = LV_COLOR_BLACK;
|
||||
if(State_l == 0){
|
||||
bgColor = LV_COLOR_BLACK;
|
||||
} else if (State_l == 1){
|
||||
bgColor = LV_COLOR_WHITE;
|
||||
}
|
||||
else {
|
||||
bgColor = lv_color_hex(0xff0000);
|
||||
}
|
||||
|
||||
lv_color_t fgColor = State_l ? Colors::lightGray : LV_COLOR_WHITE;
|
||||
|
||||
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, bgColor);
|
||||
lv_obj_set_style_local_text_color(flashLight, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, fgColor);
|
||||
@ -86,9 +96,11 @@ void FlashLight::SetIndicators() {
|
||||
}
|
||||
|
||||
void FlashLight::Toggle() {
|
||||
isOn = !isOn;
|
||||
State_l++;
|
||||
if(State_l > 2)
|
||||
State_l = 0;
|
||||
SetColors();
|
||||
if (isOn) {
|
||||
if (State_l) {
|
||||
brightnessController.Set(brightnessLevel);
|
||||
} else {
|
||||
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
|
||||
@ -99,7 +111,7 @@ bool FlashLight::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
|
||||
using namespace Pinetime::Controllers;
|
||||
|
||||
auto SetState = [this]() {
|
||||
if (isOn) {
|
||||
if (State_l) {
|
||||
brightnessController.Set(brightnessLevel);
|
||||
}
|
||||
SetIndicators();
|
||||
@ -127,4 +139,4 @@ bool FlashLight::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -26,12 +26,13 @@ namespace Pinetime {
|
||||
Pinetime::System::SystemTask& systemTask;
|
||||
Controllers::BrightnessController& brightnessController;
|
||||
|
||||
|
||||
Controllers::BrightnessController::Levels brightnessLevel = Controllers::BrightnessController::Levels::High;
|
||||
|
||||
lv_obj_t* flashLight;
|
||||
lv_obj_t* backgroundAction;
|
||||
lv_obj_t* indicators[3];
|
||||
bool isOn = false;
|
||||
int State_l = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -203,19 +203,21 @@ Navigation::Navigation(Pinetime::Controllers::NavigationService& nav) : navServi
|
||||
lv_obj_align(imgFlag, nullptr, LV_ALIGN_CENTER, 0, -60);
|
||||
|
||||
txtNarrative = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_long_mode(txtNarrative, LV_LABEL_LONG_BREAK);
|
||||
lv_label_set_long_mode(txtNarrative, LV_LABEL_LONG_DOT);
|
||||
lv_obj_set_width(txtNarrative, LV_HOR_RES);
|
||||
lv_obj_set_height(txtNarrative, 80);
|
||||
lv_label_set_text_static(txtNarrative, "Navigation");
|
||||
lv_label_set_align(txtNarrative, LV_LABEL_ALIGN_CENTER);
|
||||
lv_obj_align(txtNarrative, nullptr, LV_ALIGN_CENTER, 0, 10);
|
||||
lv_obj_align(txtNarrative, nullptr, LV_ALIGN_CENTER, 0, 30);
|
||||
|
||||
txtManDist = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_long_mode(txtManDist, LV_LABEL_LONG_BREAK);
|
||||
lv_obj_set_style_local_text_color(txtManDist, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN);
|
||||
lv_obj_set_style_local_text_font(txtManDist, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42);
|
||||
lv_obj_set_width(txtManDist, LV_HOR_RES);
|
||||
lv_label_set_text_static(txtManDist, "--M");
|
||||
lv_label_set_align(txtManDist, LV_LABEL_ALIGN_CENTER);
|
||||
lv_obj_align(txtManDist, nullptr, LV_ALIGN_CENTER, 0, 60);
|
||||
lv_obj_align(txtManDist, nullptr, LV_ALIGN_CENTER, 0, 90);
|
||||
|
||||
// Route Progress
|
||||
barProgress = lv_bar_create(lv_scr_act(), nullptr);
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "components/battery/BatteryController.h"
|
||||
#include "components/ble/BleController.h"
|
||||
#include "components/ble/NotificationManager.h"
|
||||
#include "components/heartrate/HeartRateController.h"
|
||||
#include "components/motion/MotionController.h"
|
||||
#include "components/settings/Settings.h"
|
||||
using namespace Pinetime::Applications::Screens;
|
||||
@ -19,7 +18,6 @@ WatchFaceCasioStyleG7710::WatchFaceCasioStyleG7710(Controllers::DateTime& dateTi
|
||||
const Controllers::Ble& bleController,
|
||||
Controllers::NotificationManager& notificatioManager,
|
||||
Controllers::Settings& settingsController,
|
||||
Controllers::HeartRateController& heartRateController,
|
||||
Controllers::MotionController& motionController,
|
||||
Controllers::FS& filesystem)
|
||||
: currentDateTime {{}},
|
||||
@ -29,7 +27,6 @@ WatchFaceCasioStyleG7710::WatchFaceCasioStyleG7710(Controllers::DateTime& dateTi
|
||||
bleController {bleController},
|
||||
notificatioManager {notificatioManager},
|
||||
settingsController {settingsController},
|
||||
heartRateController {heartRateController},
|
||||
motionController {motionController} {
|
||||
|
||||
lfs_file f = {};
|
||||
@ -148,25 +145,20 @@ WatchFaceCasioStyleG7710::WatchFaceCasioStyleG7710(Controllers::DateTime& dateTi
|
||||
lv_obj_set_pos(backgroundLabel, 0, 0);
|
||||
lv_label_set_text_static(backgroundLabel, "");
|
||||
|
||||
heartbeatIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_text_static(heartbeatIcon, Symbols::heartBeat);
|
||||
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
|
||||
lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2);
|
||||
|
||||
heartbeatValue = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_color(heartbeatValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
|
||||
lv_label_set_text_static(heartbeatValue, "");
|
||||
lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
|
||||
stepIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_text_static(stepIcon, Symbols::shoe);
|
||||
lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
|
||||
lv_obj_align(stepIcon, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 5, -2);
|
||||
|
||||
stepValue = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_color(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
|
||||
lv_label_set_text_static(stepValue, "0");
|
||||
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2);
|
||||
lv_label_set_text_static(stepValue, "");
|
||||
lv_obj_align(stepValue, stepIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
|
||||
|
||||
stepIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
|
||||
lv_label_set_text_static(stepIcon, Symbols::shoe);
|
||||
lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0);
|
||||
label_seconds = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_color(label_seconds, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
|
||||
lv_label_set_text_static(label_seconds, "0");
|
||||
lv_obj_align(label_seconds, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -9, -2);
|
||||
|
||||
taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
|
||||
Refresh();
|
||||
@ -222,10 +214,11 @@ void WatchFaceCasioStyleG7710::Refresh() {
|
||||
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
|
||||
}
|
||||
|
||||
currentDateTime = std::chrono::time_point_cast<std::chrono::minutes>(dateTimeController.CurrentDateTime());
|
||||
currentDateTime = std::chrono::time_point_cast<std::chrono::seconds>(dateTimeController.CurrentDateTime());
|
||||
if (currentDateTime.IsUpdated()) {
|
||||
uint8_t hour = dateTimeController.Hours();
|
||||
uint8_t minute = dateTimeController.Minutes();
|
||||
uint8_t second = dateTimeController.Seconds();
|
||||
|
||||
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
|
||||
char ampmChar[2] = "A";
|
||||
@ -244,6 +237,9 @@ void WatchFaceCasioStyleG7710::Refresh() {
|
||||
}
|
||||
lv_obj_realign(label_time);
|
||||
|
||||
lv_label_set_text_fmt(label_seconds, "%02d", second);
|
||||
lv_obj_realign(label_seconds);
|
||||
|
||||
currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get());
|
||||
if (currentDate.IsUpdated()) {
|
||||
const char* weekNumberFormat = "%V";
|
||||
@ -289,26 +285,11 @@ void WatchFaceCasioStyleG7710::Refresh() {
|
||||
}
|
||||
}
|
||||
|
||||
heartbeat = heartRateController.HeartRate();
|
||||
heartbeatRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped;
|
||||
if (heartbeat.IsUpdated() || heartbeatRunning.IsUpdated()) {
|
||||
if (heartbeatRunning.Get()) {
|
||||
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text);
|
||||
lv_label_set_text_fmt(heartbeatValue, "%d", heartbeat.Get());
|
||||
} else {
|
||||
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x1B1B1B));
|
||||
lv_label_set_text_static(heartbeatValue, "");
|
||||
}
|
||||
|
||||
lv_obj_realign(heartbeatIcon);
|
||||
lv_obj_realign(heartbeatValue);
|
||||
}
|
||||
|
||||
stepCount = motionController.NbSteps();
|
||||
if (stepCount.IsUpdated()) {
|
||||
lv_label_set_text_fmt(stepValue, "%lu", stepCount.Get());
|
||||
lv_obj_realign(stepValue);
|
||||
lv_obj_realign(stepIcon);
|
||||
lv_obj_realign(stepValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@ namespace Pinetime {
|
||||
class Battery;
|
||||
class Ble;
|
||||
class NotificationManager;
|
||||
class HeartRateController;
|
||||
class MotionController;
|
||||
}
|
||||
|
||||
@ -30,7 +29,6 @@ namespace Pinetime {
|
||||
const Controllers::Ble& bleController,
|
||||
Controllers::NotificationManager& notificatioManager,
|
||||
Controllers::Settings& settingsController,
|
||||
Controllers::HeartRateController& heartRateController,
|
||||
Controllers::MotionController& motionController,
|
||||
Controllers::FS& filesystem);
|
||||
~WatchFaceCasioStyleG7710() override;
|
||||
@ -44,10 +42,8 @@ namespace Pinetime {
|
||||
Utility::DirtyValue<bool> powerPresent {};
|
||||
Utility::DirtyValue<bool> bleState {};
|
||||
Utility::DirtyValue<bool> bleRadioEnabled {};
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>> currentDateTime {};
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> currentDateTime {};
|
||||
Utility::DirtyValue<uint32_t> stepCount {};
|
||||
Utility::DirtyValue<uint8_t> heartbeat {};
|
||||
Utility::DirtyValue<bool> heartbeatRunning {};
|
||||
Utility::DirtyValue<bool> notificationState {};
|
||||
using days = std::chrono::duration<int32_t, std::ratio<86400>>; // TODO: days is standard in c++20
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate;
|
||||
@ -64,6 +60,7 @@ namespace Pinetime {
|
||||
lv_style_t style_border;
|
||||
|
||||
lv_obj_t* label_time;
|
||||
lv_obj_t* label_seconds;
|
||||
lv_obj_t* line_time;
|
||||
lv_obj_t* label_time_ampm;
|
||||
lv_obj_t* label_date;
|
||||
@ -77,8 +74,6 @@ namespace Pinetime {
|
||||
lv_obj_t* bleIcon;
|
||||
lv_obj_t* batteryPlug;
|
||||
lv_obj_t* label_battery_value;
|
||||
lv_obj_t* heartbeatIcon;
|
||||
lv_obj_t* heartbeatValue;
|
||||
lv_obj_t* stepIcon;
|
||||
lv_obj_t* stepValue;
|
||||
lv_obj_t* notificationIcon;
|
||||
@ -91,7 +86,6 @@ namespace Pinetime {
|
||||
const Controllers::Ble& bleController;
|
||||
Controllers::NotificationManager& notificatioManager;
|
||||
Controllers::Settings& settingsController;
|
||||
Controllers::HeartRateController& heartRateController;
|
||||
Controllers::MotionController& motionController;
|
||||
|
||||
lv_task_t* taskRefresh;
|
||||
|
@ -34,7 +34,11 @@ WatchFaceDigital::WatchFaceDigital(Controllers::DateTime& dateTimeController,
|
||||
lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_LIME);
|
||||
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(false));
|
||||
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
|
||||
|
||||
|
||||
label_seconds = lv_label_create(lv_scr_act(), nullptr); // for secs
|
||||
lv_obj_align(label_seconds, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, -55);
|
||||
lv_obj_set_style_local_text_color(label_seconds, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999));
|
||||
|
||||
label_date = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_align(label_date, lv_scr_act(), LV_ALIGN_CENTER, 0, 60);
|
||||
lv_obj_set_style_local_text_color(label_date, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x999999));
|
||||
@ -44,10 +48,6 @@ WatchFaceDigital::WatchFaceDigital(Controllers::DateTime& dateTimeController,
|
||||
|
||||
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0);
|
||||
|
||||
label_time_ampm = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_text_static(label_time_ampm, "");
|
||||
lv_obj_align(label_time_ampm, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, -30, -55);
|
||||
|
||||
heartbeatIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_text_static(heartbeatIcon, Symbols::heartBeat);
|
||||
lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xCE1B1B));
|
||||
@ -85,29 +85,27 @@ void WatchFaceDigital::Refresh() {
|
||||
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
|
||||
}
|
||||
|
||||
currentDateTime = std::chrono::time_point_cast<std::chrono::minutes>(dateTimeController.CurrentDateTime());
|
||||
currentDateTime = std::chrono::time_point_cast<std::chrono::seconds>(dateTimeController.CurrentDateTime());
|
||||
|
||||
if (currentDateTime.IsUpdated()) {
|
||||
uint8_t hour = dateTimeController.Hours();
|
||||
uint8_t minute = dateTimeController.Minutes();
|
||||
uint8_t second = dateTimeController.Seconds();
|
||||
|
||||
if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) {
|
||||
char ampmChar[3] = "AM";
|
||||
if (hour == 0) {
|
||||
hour = 12;
|
||||
} else if (hour == 12) {
|
||||
ampmChar[0] = 'P';
|
||||
} else if (hour > 12) {
|
||||
hour = hour - 12;
|
||||
ampmChar[0] = 'P';
|
||||
}
|
||||
lv_label_set_text(label_time_ampm, ampmChar);
|
||||
lv_label_set_text_fmt(label_time, "%2d:%02d", hour, minute);
|
||||
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 0, 0);
|
||||
} else {
|
||||
lv_label_set_text_fmt(label_time, "%02d:%02d", hour, minute);
|
||||
lv_obj_align(label_time, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
|
||||
}
|
||||
|
||||
lv_label_set_text_fmt(label_seconds, "%02d", second); // for secs
|
||||
|
||||
currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get());
|
||||
if (currentDate.IsUpdated()) {
|
||||
|
@ -44,7 +44,7 @@ namespace Pinetime {
|
||||
Utility::DirtyValue<bool> powerPresent {};
|
||||
Utility::DirtyValue<bool> bleState {};
|
||||
Utility::DirtyValue<bool> bleRadioEnabled {};
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>> currentDateTime {};
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> currentDateTime {};
|
||||
Utility::DirtyValue<uint32_t> stepCount {};
|
||||
Utility::DirtyValue<uint8_t> heartbeat {};
|
||||
Utility::DirtyValue<bool> heartbeatRunning {};
|
||||
@ -53,7 +53,8 @@ namespace Pinetime {
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate;
|
||||
|
||||
lv_obj_t* label_time;
|
||||
lv_obj_t* label_time_ampm;
|
||||
lv_obj_t* label_seconds; //for secs
|
||||
lv_obj_t* batteryValue;
|
||||
lv_obj_t* label_date;
|
||||
lv_obj_t* heartbeatIcon;
|
||||
lv_obj_t* heartbeatValue;
|
||||
|
274
src/displayapp/screens/WatchFaceFace.cpp
Normal file
274
src/displayapp/screens/WatchFaceFace.cpp
Normal file
@ -0,0 +1,274 @@
|
||||
#include "displayapp/screens/WatchFaceFace.h"
|
||||
#include <cmath>
|
||||
#include <lvgl/lvgl.h>
|
||||
#include "displayapp/screens/BatteryIcon.h"
|
||||
#include "displayapp/screens/BleIcon.h"
|
||||
#include "displayapp/screens/Symbols.h"
|
||||
#include "displayapp/screens/NotificationIcon.h"
|
||||
#include "components/settings/Settings.h"
|
||||
#include "displayapp/InfiniTimeTheme.h"
|
||||
|
||||
using namespace Pinetime::Applications::Screens;
|
||||
|
||||
namespace {
|
||||
constexpr int16_t HourLength = 100;
|
||||
constexpr int16_t MinuteLength = 90;
|
||||
constexpr int16_t SecondLength = 110;
|
||||
|
||||
// sin(90) = 1 so the value of _lv_trigo_sin(90) is the scaling factor
|
||||
const auto LV_TRIG_SCALE = _lv_trigo_sin(90);
|
||||
|
||||
int16_t Cosine(int16_t angle) {
|
||||
return _lv_trigo_sin(angle + 90);
|
||||
}
|
||||
|
||||
int16_t Sine(int16_t angle) {
|
||||
return _lv_trigo_sin(angle);
|
||||
}
|
||||
|
||||
int16_t CoordinateXRelocate(int16_t x) {
|
||||
return (x + LV_HOR_RES / 2);
|
||||
}
|
||||
|
||||
int16_t CoordinateYRelocate(int16_t y) {
|
||||
return std::abs(y - LV_HOR_RES / 2);
|
||||
}
|
||||
|
||||
lv_point_t CoordinateRelocate(int16_t radius, int16_t angle) {
|
||||
return lv_point_t {.x = CoordinateXRelocate(radius * static_cast<int32_t>(Sine(angle)) / LV_TRIG_SCALE),
|
||||
.y = CoordinateYRelocate(radius * static_cast<int32_t>(Cosine(angle)) / LV_TRIG_SCALE)};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
WatchFaceFace::WatchFaceFace(Controllers::DateTime& dateTimeController,
|
||||
const Controllers::Battery& batteryController,
|
||||
const Controllers::Ble& bleController,
|
||||
Controllers::NotificationManager& notificationManager,
|
||||
Controllers::Settings& settingsController)
|
||||
: currentDateTime {{}},
|
||||
batteryIcon(true),
|
||||
dateTimeController {dateTimeController},
|
||||
batteryController {batteryController},
|
||||
bleController {bleController},
|
||||
notificationManager {notificationManager},
|
||||
settingsController {settingsController} {
|
||||
|
||||
sHour = 99;
|
||||
sMinute = 99;
|
||||
sSecond = 99;
|
||||
|
||||
mouth = lv_arc_create(lv_scr_act(), nullptr);
|
||||
lv_arc_set_angles(mouth, 30, 110);
|
||||
lv_arc_set_bg_angles(mouth, 45, 135);
|
||||
lv_obj_set_size(mouth, 130, 130);
|
||||
lv_obj_align(mouth, nullptr, LV_ALIGN_CENTER, 0, 0);
|
||||
|
||||
leye = lv_arc_create(lv_scr_act(), nullptr);
|
||||
lv_arc_set_angles(leye, 0, 360);
|
||||
lv_obj_set_size(leye, 30, 30);
|
||||
lv_obj_align(leye, nullptr, LV_ALIGN_CENTER, -35, -35);
|
||||
|
||||
reye = lv_arc_create(lv_scr_act(), nullptr);
|
||||
lv_arc_set_angles(reye, 0, 360);
|
||||
lv_obj_set_size(reye, 30, 30);
|
||||
lv_obj_align(reye, nullptr, LV_ALIGN_CENTER, 35, -35);
|
||||
|
||||
|
||||
minor_scales = lv_linemeter_create(lv_scr_act(), nullptr);
|
||||
lv_linemeter_set_scale(minor_scales, 300, 51);
|
||||
lv_linemeter_set_angle_offset(minor_scales, 180);
|
||||
lv_obj_set_size(minor_scales, 240, 240);
|
||||
lv_obj_align(minor_scales, nullptr, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_obj_set_style_local_bg_opa(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
|
||||
lv_obj_set_style_local_scale_width(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 4);
|
||||
lv_obj_set_style_local_scale_end_line_width(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 1);
|
||||
lv_obj_set_style_local_scale_end_color(minor_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
|
||||
|
||||
major_scales = lv_linemeter_create(lv_scr_act(), nullptr);
|
||||
lv_linemeter_set_scale(major_scales, 300, 11);
|
||||
lv_linemeter_set_angle_offset(major_scales, 180);
|
||||
lv_obj_set_size(major_scales, 240, 240);
|
||||
lv_obj_align(major_scales, nullptr, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_obj_set_style_local_bg_opa(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
|
||||
lv_obj_set_style_local_scale_width(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 6);
|
||||
lv_obj_set_style_local_scale_end_line_width(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 4);
|
||||
lv_obj_set_style_local_scale_end_color(major_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||
|
||||
large_scales = lv_linemeter_create(lv_scr_act(), nullptr);
|
||||
lv_linemeter_set_scale(large_scales, 180, 3);
|
||||
lv_linemeter_set_angle_offset(large_scales, 180);
|
||||
lv_obj_set_size(large_scales, 240, 240);
|
||||
lv_obj_align(large_scales, nullptr, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_obj_set_style_local_bg_opa(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
|
||||
lv_obj_set_style_local_scale_width(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 20);
|
||||
lv_obj_set_style_local_scale_end_line_width(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, 4);
|
||||
lv_obj_set_style_local_scale_end_color(large_scales, LV_LINEMETER_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_AQUA);
|
||||
|
||||
twelve = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_align(twelve, LV_LABEL_ALIGN_CENTER);
|
||||
lv_label_set_text_static(twelve, "12");
|
||||
lv_obj_set_pos(twelve, 110, 10);
|
||||
lv_obj_set_style_local_text_color(twelve, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_AQUA);
|
||||
|
||||
batteryIcon.Create(lv_scr_act());
|
||||
lv_obj_align(batteryIcon.GetObject(), nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 0);
|
||||
|
||||
plugIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_text_static(plugIcon, Symbols::plug);
|
||||
lv_obj_align(plugIcon, nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 0);
|
||||
|
||||
bleIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_label_set_text_static(bleIcon, "");
|
||||
lv_obj_align(bleIcon, nullptr, LV_ALIGN_IN_TOP_RIGHT, -30, 0);
|
||||
|
||||
notificationIcon = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_LIME);
|
||||
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(false));
|
||||
lv_obj_align(notificationIcon, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
|
||||
|
||||
// Date - Day / Week day
|
||||
|
||||
label_date_day = lv_label_create(lv_scr_act(), nullptr);
|
||||
lv_obj_set_style_local_text_color(label_date_day, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::orange);
|
||||
lv_label_set_text_fmt(label_date_day, "%s\n%02i", dateTimeController.DayOfWeekShortToString(), dateTimeController.Day());
|
||||
lv_label_set_align(label_date_day, LV_LABEL_ALIGN_CENTER);
|
||||
lv_obj_align(label_date_day, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
|
||||
|
||||
minute_body = lv_line_create(lv_scr_act(), nullptr);
|
||||
minute_body_trace = lv_line_create(lv_scr_act(), nullptr);
|
||||
hour_body = lv_line_create(lv_scr_act(), nullptr);
|
||||
hour_body_trace = lv_line_create(lv_scr_act(), nullptr);
|
||||
second_body = lv_line_create(lv_scr_act(), nullptr);
|
||||
|
||||
lv_style_init(&second_line_style);
|
||||
lv_style_set_line_width(&second_line_style, LV_STATE_DEFAULT, 3);
|
||||
lv_style_set_line_color(&second_line_style, LV_STATE_DEFAULT, LV_COLOR_RED);
|
||||
lv_style_set_line_rounded(&second_line_style, LV_STATE_DEFAULT, true);
|
||||
lv_obj_add_style(second_body, LV_LINE_PART_MAIN, &second_line_style);
|
||||
|
||||
lv_style_init(&minute_line_style);
|
||||
lv_style_set_line_width(&minute_line_style, LV_STATE_DEFAULT, 3);
|
||||
lv_style_set_line_color(&minute_line_style, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||
lv_style_set_line_rounded(&minute_line_style, LV_STATE_DEFAULT, true);
|
||||
lv_obj_add_style(minute_body, LV_LINE_PART_MAIN, &minute_line_style);
|
||||
|
||||
lv_style_init(&minute_line_style_trace);
|
||||
lv_style_set_line_width(&minute_line_style_trace, LV_STATE_DEFAULT, 3);
|
||||
lv_style_set_line_color(&minute_line_style_trace, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||
lv_style_set_line_rounded(&minute_line_style_trace, LV_STATE_DEFAULT, false);
|
||||
lv_obj_add_style(minute_body_trace, LV_LINE_PART_MAIN, &minute_line_style_trace);
|
||||
|
||||
lv_style_init(&hour_line_style);
|
||||
lv_style_set_line_width(&hour_line_style, LV_STATE_DEFAULT, 7);
|
||||
lv_style_set_line_color(&hour_line_style, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||
lv_style_set_line_rounded(&hour_line_style, LV_STATE_DEFAULT, true);
|
||||
lv_obj_add_style(hour_body, LV_LINE_PART_MAIN, &hour_line_style);
|
||||
|
||||
lv_style_init(&hour_line_style_trace);
|
||||
lv_style_set_line_width(&hour_line_style_trace, LV_STATE_DEFAULT, 3);
|
||||
lv_style_set_line_color(&hour_line_style_trace, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||
lv_style_set_line_rounded(&hour_line_style_trace, LV_STATE_DEFAULT, false);
|
||||
lv_obj_add_style(hour_body_trace, LV_LINE_PART_MAIN, &hour_line_style_trace);
|
||||
|
||||
taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
WatchFaceFace::~WatchFaceFace() {
|
||||
lv_task_del(taskRefresh);
|
||||
|
||||
lv_style_reset(&hour_line_style);
|
||||
lv_style_reset(&hour_line_style_trace);
|
||||
lv_style_reset(&minute_line_style);
|
||||
lv_style_reset(&minute_line_style_trace);
|
||||
lv_style_reset(&second_line_style);
|
||||
|
||||
lv_obj_clean(lv_scr_act());
|
||||
}
|
||||
|
||||
void WatchFaceFace::UpdateClock() {
|
||||
uint8_t hour = dateTimeController.Hours();
|
||||
uint8_t minute = dateTimeController.Minutes();
|
||||
uint8_t second = dateTimeController.Seconds();
|
||||
|
||||
if (sMinute != minute) {
|
||||
auto const angle = minute * 6;
|
||||
minute_point[0] = CoordinateRelocate(10, angle - 90);
|
||||
minute_point[1] = CoordinateRelocate(MinuteLength, angle);
|
||||
minute_point[2] = CoordinateRelocate(10, angle + 90);
|
||||
|
||||
lv_line_set_points(minute_body, minute_point, 3);
|
||||
}
|
||||
|
||||
if (sHour != hour || sMinute != minute) {
|
||||
sHour = hour;
|
||||
sMinute = minute;
|
||||
auto const angle = (hour * 30 + minute / 2);
|
||||
|
||||
hour_point[0] = CoordinateRelocate(90, angle);
|
||||
hour_point[1] = CoordinateRelocate(HourLength, angle);
|
||||
|
||||
lv_line_set_points(hour_body, hour_point, 2);
|
||||
}
|
||||
|
||||
if (sSecond != second) {
|
||||
sSecond = second;
|
||||
auto const angle = second * 6;
|
||||
|
||||
second_point[0] = CoordinateRelocate(105, angle);
|
||||
second_point[1] = CoordinateRelocate(SecondLength, angle);
|
||||
lv_line_set_points(second_body, second_point, 2);
|
||||
}//TODO: redo seconds
|
||||
}
|
||||
|
||||
void WatchFaceFace::SetBatteryIcon() {
|
||||
auto batteryPercent = batteryPercentRemaining.Get();
|
||||
batteryIcon.SetBatteryPercentage(batteryPercent);
|
||||
}
|
||||
|
||||
void WatchFaceFace::Refresh() {
|
||||
isCharging = batteryController.IsCharging();
|
||||
if (isCharging.IsUpdated()) {
|
||||
if (isCharging.Get()) {
|
||||
lv_obj_set_hidden(batteryIcon.GetObject(), true);
|
||||
lv_obj_set_hidden(plugIcon, false);
|
||||
} else {
|
||||
lv_obj_set_hidden(batteryIcon.GetObject(), false);
|
||||
lv_obj_set_hidden(plugIcon, true);
|
||||
SetBatteryIcon();
|
||||
}
|
||||
}
|
||||
if (!isCharging.Get()) {
|
||||
batteryPercentRemaining = batteryController.PercentRemaining();
|
||||
if (batteryPercentRemaining.IsUpdated()) {
|
||||
SetBatteryIcon();
|
||||
}
|
||||
}
|
||||
|
||||
bleState = bleController.IsConnected();
|
||||
if (bleState.IsUpdated()) {
|
||||
if (bleState.Get()) {
|
||||
lv_label_set_text_static(bleIcon, Symbols::bluetooth);
|
||||
} else {
|
||||
lv_label_set_text_static(bleIcon, "");
|
||||
}
|
||||
}
|
||||
|
||||
notificationState = notificationManager.AreNewNotificationsAvailable();
|
||||
|
||||
if (notificationState.IsUpdated()) {
|
||||
lv_label_set_text_static(notificationIcon, NotificationIcon::GetIcon(notificationState.Get()));
|
||||
}
|
||||
|
||||
currentDateTime = dateTimeController.CurrentDateTime();
|
||||
if (currentDateTime.IsUpdated()) {
|
||||
UpdateClock();
|
||||
|
||||
currentDate = std::chrono::time_point_cast<days>(currentDateTime.Get());
|
||||
if (currentDate.IsUpdated()) {
|
||||
lv_label_set_text_fmt(label_date_day, "%s\n%02i", dateTimeController.DayOfWeekShortToString(), dateTimeController.Day());
|
||||
}
|
||||
}
|
||||
}
|
95
src/displayapp/screens/WatchFaceFace.h
Normal file
95
src/displayapp/screens/WatchFaceFace.h
Normal file
@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#include <lvgl/src/lv_core/lv_obj.h>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include "displayapp/screens/Screen.h"
|
||||
#include "components/datetime/DateTimeController.h"
|
||||
#include "components/battery/BatteryController.h"
|
||||
#include "components/ble/BleController.h"
|
||||
#include "components/ble/NotificationManager.h"
|
||||
#include "displayapp/screens/BatteryIcon.h"
|
||||
#include "utility/DirtyValue.h"
|
||||
|
||||
namespace Pinetime {
|
||||
namespace Controllers {
|
||||
class Settings;
|
||||
class Battery;
|
||||
class Ble;
|
||||
class NotificationManager;
|
||||
}
|
||||
|
||||
namespace Applications {
|
||||
namespace Screens {
|
||||
|
||||
class WatchFaceFace : public Screen {
|
||||
public:
|
||||
WatchFaceFace(Controllers::DateTime& dateTimeController,
|
||||
const Controllers::Battery& batteryController,
|
||||
const Controllers::Ble& bleController,
|
||||
Controllers::NotificationManager& notificationManager,
|
||||
Controllers::Settings& settingsController);
|
||||
|
||||
~WatchFaceFace() override;
|
||||
|
||||
void Refresh() override;
|
||||
|
||||
private:
|
||||
uint8_t sHour, sMinute, sSecond;
|
||||
|
||||
Utility::DirtyValue<uint8_t> batteryPercentRemaining {0};
|
||||
Utility::DirtyValue<bool> isCharging {};
|
||||
Utility::DirtyValue<bool> bleState {};
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime;
|
||||
Utility::DirtyValue<bool> notificationState {false};
|
||||
using days = std::chrono::duration<int32_t, std::ratio<86400>>; // TODO: days is standard in c++20
|
||||
Utility::DirtyValue<std::chrono::time_point<std::chrono::system_clock, days>> currentDate;
|
||||
|
||||
lv_obj_t* minor_scales;
|
||||
lv_obj_t* major_scales;
|
||||
lv_obj_t* large_scales;
|
||||
lv_obj_t* twelve;
|
||||
|
||||
lv_obj_t* hour_body;
|
||||
lv_obj_t* hour_body_trace;
|
||||
lv_obj_t* minute_body;
|
||||
lv_obj_t* minute_body_trace;
|
||||
lv_obj_t* second_body;
|
||||
|
||||
lv_point_t hour_point[2];
|
||||
lv_point_t minute_point[3];
|
||||
lv_point_t second_point[2];
|
||||
|
||||
lv_style_t hour_line_style;
|
||||
lv_style_t hour_line_style_trace;
|
||||
lv_style_t minute_line_style;
|
||||
lv_style_t minute_line_style_trace;
|
||||
lv_style_t second_line_style;
|
||||
|
||||
lv_obj_t* label_date_day;
|
||||
lv_obj_t* plugIcon;
|
||||
lv_obj_t* notificationIcon;
|
||||
lv_obj_t* bleIcon;
|
||||
|
||||
lv_obj_t* mouth;
|
||||
lv_obj_t* leye;
|
||||
lv_obj_t* reye;
|
||||
|
||||
|
||||
BatteryIcon batteryIcon;
|
||||
|
||||
const Controllers::DateTime& dateTimeController;
|
||||
const Controllers::Battery& batteryController;
|
||||
const Controllers::Ble& bleController;
|
||||
Controllers::NotificationManager& notificationManager;
|
||||
Controllers::Settings& settingsController;
|
||||
|
||||
void UpdateClock();
|
||||
void SetBatteryIcon();
|
||||
|
||||
lv_task_t* taskRefresh;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@
|
||||
#include "displayapp/screens/CheckboxList.h"
|
||||
#include "displayapp/screens/WatchFaceInfineat.h"
|
||||
#include "displayapp/screens/WatchFaceCasioStyleG7710.h"
|
||||
#include "displayapp/screens/WatchFaceFace.h"
|
||||
|
||||
namespace Pinetime {
|
||||
|
||||
@ -47,8 +48,9 @@ namespace Pinetime {
|
||||
{"Terminal", true},
|
||||
{"Infineat face", Applications::Screens::WatchFaceInfineat::IsAvailable(filesystem)},
|
||||
{"Casio G7710", Applications::Screens::WatchFaceCasioStyleG7710::IsAvailable(filesystem)},
|
||||
{"", false},
|
||||
{"", false}}};
|
||||
{"Face face", true},
|
||||
{"", false}
|
||||
}};
|
||||
ScreenList<nScreens> screens;
|
||||
};
|
||||
}
|
||||
|
@ -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},
|
||||
|
@ -3,8 +3,8 @@ find_program(LV_FONT_CONV "lv_font_conv" NO_CACHE REQUIRED
|
||||
HINTS "${CMAKE_SOURCE_DIR}/node_modules/.bin")
|
||||
message(STATUS "Using ${LV_FONT_CONV} to generate font files")
|
||||
|
||||
find_program(LV_IMG_CONV "lv_img_conv" NO_CACHE REQUIRED
|
||||
HINTS "${CMAKE_SOURCE_DIR}/node_modules/.bin")
|
||||
find_program(LV_IMG_CONV "lv_img_conv.py" NO_CACHE REQUIRED
|
||||
HINTS "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
message(STATUS "Using ${LV_IMG_CONV} to generate font files")
|
||||
|
||||
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
|
||||
|
@ -11,6 +11,9 @@ import subprocess
|
||||
|
||||
def gen_lvconv_line(lv_img_conv: str, dest: str, color_format: str, output_format: str, binary_format: str, sources: str):
|
||||
args = [lv_img_conv, sources, '--force', '--output-file', dest, '--color-format', color_format, '--output-format', output_format, '--binary-format', binary_format]
|
||||
if lv_img_conv.endswith(".py"):
|
||||
# lv_img_conv is a python script, call with current python executable
|
||||
args = [sys.executable] + args
|
||||
|
||||
return args
|
||||
|
||||
|
193
src/resources/lv_img_conv.py
Executable file
193
src/resources/lv_img_conv.py
Executable file
@ -0,0 +1,193 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import pathlib
|
||||
import sys
|
||||
import decimal
|
||||
from PIL import Image
|
||||
|
||||
|
||||
def classify_pixel(value, bits):
|
||||
def round_half_up(v):
|
||||
"""python3 implements "propper" "banker's rounding" by rounding to the nearest
|
||||
even number. Javascript rounds to the nearest integer.
|
||||
To have the same output as the original JavaScript implementation add a custom
|
||||
rounding function, which does "school" rounding (to the nearest integer).
|
||||
|
||||
see: https://stackoverflow.com/questions/43851273/how-to-round-float-0-5-up-to-1-0-while-still-rounding-0-45-to-0-0-as-the-usual
|
||||
"""
|
||||
return int(decimal.Decimal(v).quantize(decimal.Decimal('1'), rounding=decimal.ROUND_HALF_UP))
|
||||
tmp = 1 << (8 - bits)
|
||||
val = round_half_up(value / tmp) * tmp
|
||||
if val < 0:
|
||||
val = 0
|
||||
return val
|
||||
|
||||
|
||||
def test_classify_pixel():
|
||||
# test difference between round() and round_half_up()
|
||||
assert classify_pixel(18, 5) == 16
|
||||
# school rounding 4.5 to 5, but banker's rounding 4.5 to 4
|
||||
assert classify_pixel(18, 6) == 20
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument("img",
|
||||
help="Path to image to convert to C header file")
|
||||
parser.add_argument("-o", "--output-file",
|
||||
help="output file path (for single-image conversion)",
|
||||
required=True)
|
||||
parser.add_argument("-f", "--force",
|
||||
help="allow overwriting the output file",
|
||||
action="store_true")
|
||||
parser.add_argument("-i", "--image-name",
|
||||
help="name of image structure (not implemented)")
|
||||
parser.add_argument("-c", "--color-format",
|
||||
help="color format of image",
|
||||
default="CF_TRUE_COLOR_ALPHA",
|
||||
choices=[
|
||||
"CF_ALPHA_1_BIT", "CF_ALPHA_2_BIT", "CF_ALPHA_4_BIT",
|
||||
"CF_ALPHA_8_BIT", "CF_INDEXED_1_BIT", "CF_INDEXED_2_BIT", "CF_INDEXED_4_BIT",
|
||||
"CF_INDEXED_8_BIT", "CF_RAW", "CF_RAW_CHROMA", "CF_RAW_ALPHA",
|
||||
"CF_TRUE_COLOR", "CF_TRUE_COLOR_ALPHA", "CF_TRUE_COLOR_CHROMA", "CF_RGB565A8",
|
||||
],
|
||||
required=True)
|
||||
parser.add_argument("-t", "--output-format",
|
||||
help="output format of image",
|
||||
default="bin", # default in original is 'c'
|
||||
choices=["c", "bin"])
|
||||
parser.add_argument("--binary-format",
|
||||
help="binary color format (needed if output-format is binary)",
|
||||
default="ARGB8565_RBSWAP",
|
||||
choices=["ARGB8332", "ARGB8565", "ARGB8565_RBSWAP", "ARGB8888"])
|
||||
parser.add_argument("-s", "--swap-endian",
|
||||
help="swap endian of image (not implemented)",
|
||||
action="store_true")
|
||||
parser.add_argument("-d", "--dither",
|
||||
help="enable dither (not implemented)",
|
||||
action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
img_path = pathlib.Path(args.img)
|
||||
out = pathlib.Path(args.output_file)
|
||||
if not img_path.is_file():
|
||||
print(f"Input file is missing: '{args.img}'")
|
||||
return 1
|
||||
print(f"Beginning conversion of {args.img}")
|
||||
if out.exists():
|
||||
if args.force:
|
||||
print(f"overwriting {args.output_file}")
|
||||
else:
|
||||
pritn(f"Error: refusing to overwrite {args.output_file} without -f specified.")
|
||||
return 1
|
||||
out.touch()
|
||||
|
||||
# only implemented the bare minimum, everything else is not implemented
|
||||
if args.color_format not in ["CF_INDEXED_1_BIT", "CF_TRUE_COLOR_ALPHA"]:
|
||||
raise NotImplementedError(f"argument --color-format '{args.color_format}' not implemented")
|
||||
if args.output_format != "bin":
|
||||
raise NotImplementedError(f"argument --output-format '{args.output_format}' not implemented")
|
||||
if args.binary_format not in ["ARGB8565_RBSWAP", "ARGB8888"]:
|
||||
raise NotImplementedError(f"argument --binary-format '{args.binary_format}' not implemented")
|
||||
if args.image_name:
|
||||
raise NotImplementedError(f"argument --image-name not implemented")
|
||||
if args.swap_endian:
|
||||
raise NotImplementedError(f"argument --swap-endian not implemented")
|
||||
if args.dither:
|
||||
raise NotImplementedError(f"argument --dither not implemented")
|
||||
|
||||
# open image using Pillow
|
||||
img = Image.open(img_path)
|
||||
img_height = img.height
|
||||
img_width = img.width
|
||||
if args.color_format == "CF_TRUE_COLOR_ALPHA" and args.binary_format == "ARGB8888":
|
||||
buf = bytearray(img_height*img_width*4) # 4 bytes (32 bit) per pixel
|
||||
for y in range(img_height):
|
||||
for x in range(img_width):
|
||||
i = (y*img_width + x)*4 # buffer-index
|
||||
pixel = img.getpixel((x,y))
|
||||
r, g, b, a = pixel
|
||||
buf[i + 0] = r
|
||||
buf[i + 1] = g
|
||||
buf[i + 2] = b
|
||||
buf[i + 3] = a
|
||||
|
||||
elif args.color_format == "CF_TRUE_COLOR_ALPHA" and args.binary_format == "ARGB8565_RBSWAP":
|
||||
buf = bytearray(img_height*img_width*3) # 3 bytes (24 bit) per pixel
|
||||
for y in range(img_height):
|
||||
for x in range(img_width):
|
||||
i = (y*img_width + x)*3 # buffer-index
|
||||
pixel = img.getpixel((x,y))
|
||||
r_act = classify_pixel(pixel[0], 5)
|
||||
g_act = classify_pixel(pixel[1], 6)
|
||||
b_act = classify_pixel(pixel[2], 5)
|
||||
a = pixel[3]
|
||||
r_act = min(r_act, 0xF8)
|
||||
g_act = min(g_act, 0xFC)
|
||||
b_act = min(b_act, 0xF8)
|
||||
c16 = ((r_act) << 8) | ((g_act) << 3) | ((b_act) >> 3) # RGR565
|
||||
buf[i + 0] = (c16 >> 8) & 0xFF
|
||||
buf[i + 1] = c16 & 0xFF
|
||||
buf[i + 2] = a
|
||||
|
||||
elif args.color_format == "CF_INDEXED_1_BIT": # ignore binary format, use color format as binary format
|
||||
w = img_width >> 3
|
||||
if img_width & 0x07:
|
||||
w+=1
|
||||
max_p = w * (img_height-1) + ((img_width-1) >> 3) + 8 # +8 for the palette
|
||||
buf = bytearray(max_p+1)
|
||||
|
||||
for y in range(img_height):
|
||||
for x in range(img_width):
|
||||
c, a = img.getpixel((x,y))
|
||||
p = w * y + (x >> 3) + 8 # +8 for the palette
|
||||
buf[p] |= (c & 0x1) << (7 - (x & 0x7))
|
||||
# write palette information, for indexed-1-bit we need palette with two values
|
||||
# write 8 palette bytes
|
||||
buf[0] = 0
|
||||
buf[1] = 0
|
||||
buf[2] = 0
|
||||
buf[3] = 0
|
||||
# Normally there is much math behind this, but for the current use case this is close enough
|
||||
# only needs to be more complicated if we have more than 2 colors in the palette
|
||||
buf[4] = 255
|
||||
buf[5] = 255
|
||||
buf[6] = 255
|
||||
buf[7] = 255
|
||||
else:
|
||||
# raise just to be sure
|
||||
raise NotImplementedError(f"args.color_format '{args.color_format}' with args.binary_format '{args.binary_format}' not implemented")
|
||||
|
||||
# write header
|
||||
match args.color_format:
|
||||
case "CF_TRUE_COLOR_ALPHA":
|
||||
lv_cf = 5
|
||||
case "CF_INDEXED_1_BIT":
|
||||
lv_cf = 7
|
||||
case _:
|
||||
# raise just to be sure
|
||||
raise NotImplementedError(f"args.color_format '{args.color_format}' not implemented")
|
||||
header_32bit = lv_cf | (img_width << 10) | (img_height << 21)
|
||||
buf_out = bytearray(4 + len(buf))
|
||||
buf_out[0] = header_32bit & 0xFF
|
||||
buf_out[1] = (header_32bit & 0xFF00) >> 8
|
||||
buf_out[2] = (header_32bit & 0xFF0000) >> 16
|
||||
buf_out[3] = (header_32bit & 0xFF000000) >> 24
|
||||
buf_out[4:] = buf
|
||||
|
||||
# write byte buffer to file
|
||||
with open(out, "wb") as f:
|
||||
f.write(buf_out)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if "--test" in sys.argv:
|
||||
# run small set of tests and exit
|
||||
print("running tests")
|
||||
test_classify_pixel()
|
||||
print("success!")
|
||||
sys.exit(0)
|
||||
# run normal program
|
||||
sys.exit(main())
|
Loading…
Reference in New Issue
Block a user