From 0f9f606b78ffbb93b1365156d8492bbf4ee4995d Mon Sep 17 00:00:00 2001 From: FintasticMan Date: Sun, 27 Aug 2023 18:15:21 +0200 Subject: [PATCH 1/3] lowersleep: Implement Lower to Sleep functionality (#827) --- src/components/motion/MotionController.cpp | 14 ++++++++++++++ src/components/motion/MotionController.h | 1 + src/components/settings/Settings.h | 14 +++++--------- src/displayapp/screens/settings/SettingWakeUp.cpp | 6 +++--- src/displayapp/screens/settings/SettingWakeUp.h | 3 ++- src/systemtask/SystemTask.cpp | 4 ++++ 6 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/components/motion/MotionController.cpp b/src/components/motion/MotionController.cpp index feb9ead015..d28378d542 100644 --- a/src/components/motion/MotionController.cpp +++ b/src/components/motion/MotionController.cpp @@ -115,6 +115,20 @@ bool MotionController::ShouldShakeWake(uint16_t thresh) { return accumulatedSpeed > thresh; } +bool MotionController::ShouldLowerSleep() const { + if (stats.yMean < 724 || DegreesRolled(stats.yMean, stats.zMean, stats.prevYMean, stats.prevZMean) < 30) { + return false; + } + + for (uint8_t i = AccelStats::numHistory + 1; i < yHistory.Size(); i++) { + if (yHistory[i] < 265) { + return false; + } + } + + return true; +} + void MotionController::Init(Pinetime::Drivers::Bma421::DeviceTypes types) { switch (types) { case Drivers::Bma421::DeviceTypes::BMA421: diff --git a/src/components/motion/MotionController.h b/src/components/motion/MotionController.h index 1ad032b83f..0aa7823e05 100644 --- a/src/components/motion/MotionController.h +++ b/src/components/motion/MotionController.h @@ -46,6 +46,7 @@ namespace Pinetime { bool ShouldShakeWake(uint16_t thresh); bool ShouldRaiseWake() const; + bool ShouldLowerSleep() const; int32_t CurrentShakeSpeed() const { return accumulatedSpeed; diff --git a/src/components/settings/Settings.h b/src/components/settings/Settings.h index efa44fdee4..81cf492332 100644 --- a/src/components/settings/Settings.h +++ b/src/components/settings/Settings.h @@ -12,12 +12,7 @@ namespace Pinetime { enum class ClockType : uint8_t { H24, H12 }; 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, - }; + enum class WakeUpMode : uint8_t { SingleTap = 0, DoubleTap = 1, RaiseWrist = 2, Shake = 3, LowerWrist = 4 }; enum class Colors : uint8_t { White, Silver, @@ -238,7 +233,7 @@ namespace Pinetime { } }; - std::bitset<4> getWakeUpModes() const { + std::bitset<5> getWakeUpModes() const { return settings.wakeUpMode; } @@ -279,7 +274,7 @@ namespace Pinetime { private: Pinetime::Controllers::FS& fs; - static constexpr uint32_t settingsVersion = 0x0005; + static constexpr uint32_t settingsVersion = 0x0006; struct SettingsData { uint32_t version = settingsVersion; @@ -296,8 +291,9 @@ namespace Pinetime { WatchFaceInfineat watchFaceInfineat; - std::bitset<4> wakeUpMode {0}; + std::bitset<5> wakeUpMode {0}; uint16_t shakeWakeThreshold = 150; + Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium; }; diff --git a/src/displayapp/screens/settings/SettingWakeUp.cpp b/src/displayapp/screens/settings/SettingWakeUp.cpp index 8df34c2084..4649dc82e2 100644 --- a/src/displayapp/screens/settings/SettingWakeUp.cpp +++ b/src/displayapp/screens/settings/SettingWakeUp.cpp @@ -8,7 +8,7 @@ using namespace Pinetime::Applications::Screens; -constexpr std::array SettingWakeUp::options; +constexpr std::array SettingWakeUp::options; namespace { void event_handler(lv_obj_t* obj, lv_event_t event) { @@ -27,9 +27,9 @@ SettingWakeUp::SettingWakeUp(Pinetime::Controllers::Settings& settingsController lv_obj_set_style_local_pad_inner(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5); lv_obj_set_style_local_border_width(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0); - lv_obj_set_pos(container1, 10, 60); + lv_obj_set_pos(container1, 10, 35); lv_obj_set_width(container1, LV_HOR_RES - 20); - lv_obj_set_height(container1, LV_VER_RES - 50); + 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); diff --git a/src/displayapp/screens/settings/SettingWakeUp.h b/src/displayapp/screens/settings/SettingWakeUp.h index 28219ca1c4..61edabce34 100644 --- a/src/displayapp/screens/settings/SettingWakeUp.h +++ b/src/displayapp/screens/settings/SettingWakeUp.h @@ -25,11 +25,12 @@ namespace Pinetime { }; Controllers::Settings& settingsController; - static constexpr std::array options = {{ + static constexpr std::array options = {{ {Controllers::Settings::WakeUpMode::SingleTap, "Single Tap"}, {Controllers::Settings::WakeUpMode::DoubleTap, "Double Tap"}, {Controllers::Settings::WakeUpMode::RaiseWrist, "Raise Wrist"}, {Controllers::Settings::WakeUpMode::Shake, "Shake Wake"}, + {Controllers::Settings::WakeUpMode::LowerWrist, "Lower Wrist"}, }}; lv_obj_t* cbOption[options.size()]; diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 4719bb50ae..e59a0ff7a4 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -436,6 +436,10 @@ void SystemTask::UpdateMotion() { GoToRunning(); } } + if (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::LowerWrist) && state == SystemTaskState::Running && + motionController.ShouldLowerSleep()) { + PushMessage(Messages::GoToSleep); + } } void SystemTask::HandleButtonAction(Controllers::ButtonActions action) { From 44d1798f4f20ac4bf9f66690e49c4bd0b2d863ff Mon Sep 17 00:00:00 2001 From: JF Date: Sat, 2 Sep 2023 19:41:51 +0200 Subject: [PATCH 2/3] navigation: Move font to external memory (#1838) The TTF font used by the navigation app is ~20KB and is stored in internal flash memory. To free this space, the TTF font is now converted in 2 "atlas pictures" (pictures that contain multiple concatenated images) stored in the external flash memory. The navigation app now accesses one of those 2 files and apply an offset to display the desired picture. The corresponding documentation has also been updated. Add comments about the layout of the pictures that contain the icon and about the indexing of those icons. In documentation (buildAndProgram.md), edit the section about the debug compilation mode. Remove the part about removing the Navigation app to free some memory (since it's not relevant anymore) and explain how to selectively build parts of the firmware in Debug mode. --- doc/buildAndProgram.md | 12 +- src/displayapp/fonts/CMakeLists.txt | 2 +- src/displayapp/fonts/README.md | 14 ++ src/displayapp/fonts/fonts.json | 11 -- src/displayapp/screens/Navigation.cpp | 265 ++++++++++++++++---------- src/displayapp/screens/Navigation.h | 4 +- src/resources/images.json | 14 ++ src/resources/images/navigation0.png | Bin 0 -> 22487 bytes src/resources/images/navigation1.png | Bin 0 -> 17794 bytes 9 files changed, 209 insertions(+), 113 deletions(-) create mode 100644 src/resources/images/navigation0.png create mode 100644 src/resources/images/navigation1.png diff --git a/doc/buildAndProgram.md b/doc/buildAndProgram.md index 7909c59ccf..ea1ddae1b8 100644 --- a/doc/buildAndProgram.md +++ b/doc/buildAndProgram.md @@ -48,7 +48,17 @@ CMake configures the project according to variables you specify the command line #### (\*) Note about **CMAKE_BUILD_TYPE** By default, this variable is set to *Release*. It compiles the code with size and speed optimizations. We use this value for all the binaries we publish when we [release](https://github.com/InfiniTimeOrg/InfiniTime/releases) new versions of InfiniTime. -The *Debug* mode disables all optimizations, which makes the code easier to debug. However, the binary size will likely be too big to fit in the internal flash memory. If you want to build and debug a *Debug* binary, you'll need to disable some parts of the code. For example, the icons for the **Navigation** app use a lot of memory space. You can comment the content of `m_iconMap` in the [Navigation](https://github.com/InfiniTimeOrg/InfiniTime/blob/main/src/displayapp/screens/Navigation.h#L148) application to free some memory. +The *Debug* mode disables all optimizations, which makes the code easier to debug. However, the binary size will likely be too big to fit in the internal flash memory. If you want to build and debug a *Debug* binary, you can disable some parts of the code that are not needed for the test you want to achieve. You can also apply the *Debug* mode selectively on parts of the application by applying the `DEBUG_FLAGS` only for the part (CMake target) you want to debug. For example, let's say you want to debug code related to LittleFS, simply set the compilation options for the RELEASE configuration of the target to `DEBUG_FLAGS` (in `src/CMakeLists.txt`). This will force the compilation of that target in *Debug* mode while the rest of the project will be built in *Release* mode. Example: + +``` +target_compile_options(littlefs PRIVATE + ${COMMON_FLAGS} + $<$: ${DEBUG_FLAGS}> + $<$: ${DEBUG_FLAGS}> # Change from RELEASE_FLAGS to DEBUG_FLAGS + $<$: ${CXX_FLAGS}> + $<$: ${ASM_FLAGS}> + ) +``` #### (\*\*) Note about **BUILD_DFU** DFU files are the files you'll need to install your build of InfiniTime using OTA (over-the-air) mechanism. To generate the DFU file, the Python tool [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil) is needed on your system. Check that this tool is properly installed before enabling this option. diff --git a/src/displayapp/fonts/CMakeLists.txt b/src/displayapp/fonts/CMakeLists.txt index 5a32151ec4..22627efcad 100644 --- a/src/displayapp/fonts/CMakeLists.txt +++ b/src/displayapp/fonts/CMakeLists.txt @@ -1,5 +1,5 @@ set(FONTS jetbrains_mono_42 jetbrains_mono_76 jetbrains_mono_bold_20 - jetbrains_mono_extrabold_compressed lv_font_navi_80 lv_font_sys_48 + jetbrains_mono_extrabold_compressed lv_font_sys_48 open_sans_light fontawesome_weathericons) find_program(LV_FONT_CONV "lv_font_conv" NO_CACHE REQUIRED HINTS "${CMAKE_SOURCE_DIR}/node_modules/.bin") diff --git a/src/displayapp/fonts/README.md b/src/displayapp/fonts/README.md index b2669a788c..bcc2d49c1c 100644 --- a/src/displayapp/fonts/README.md +++ b/src/displayapp/fonts/README.md @@ -33,3 +33,17 @@ and for each font there is: ### Navigation font `navigtion.ttf` is created with the web app [icomoon](https://icomoon.io/app) by importing the svg files from `src/displayapp/icons/navigation/unique` and generating the font. `lv_font_navi_80.json` is a project file for the site, which you can import to add or remove icons. + +To save space in the internal flash memory, the navigation icons are now moved into the external flash memory. To do this, the TTF font is converted into pictures (1 for each symbol). Those pictures are then concatenated into 2 big pictures (we need two files since LVGL supports maximum 2048px width/height). At runtime, a map is used to locate the desired icon in the corresponding file at a specific offset. + +Here is the command to convert the TTF font in PNG picture: + +```shell +convert -background none -fill white -font navigation.ttf -pointsize 80 -gravity center label:"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" navigation0.png + +convert -background none -fill white -font navigation.ttf -pointsize 80 -gravity center label:"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" navigation1.png +``` + +*Please note that the characters after `label:` are UTF-8 characters and might not be displayed correctly in this document.* + +The characters in the TTF font range from `0xEEA480` to `0xEEA4A9`. Characters from `0xEEA480` to `0xEEA498` are stored in `navigation0.png` and the others in `navigation1.png`. Each character is 80px height so displaying a specific character consists in multiplying its index in the file by -80 and use this value as the offset when calling `lv_img_set_offset_y()`. diff --git a/src/displayapp/fonts/fonts.json b/src/displayapp/fonts/fonts.json index e65f6dd44a..bcfc365f03 100644 --- a/src/displayapp/fonts/fonts.json +++ b/src/displayapp/fonts/fonts.json @@ -64,17 +64,6 @@ "bpp": 1, "size": 48 }, - "lv_font_navi_80": { - "sources": [ - { - "file": "navigation.ttf", - "range": "0xe900-0xe929" - } - ], - "bpp": 2, - "size": 80, - "compress": true - }, "fontawesome_weathericons": { "sources": [ { diff --git a/src/displayapp/screens/Navigation.cpp b/src/displayapp/screens/Navigation.cpp index 7baea09d8e..fe2f1eb6a0 100644 --- a/src/displayapp/screens/Navigation.cpp +++ b/src/displayapp/screens/Navigation.cpp @@ -23,105 +23,166 @@ using namespace Pinetime::Applications::Screens; -LV_FONT_DECLARE(lv_font_navi_80) +/* Notes about the navigation icons : + * - Icons are generated from a TTF font converted in PNG images. Those images are all appended + * vertically into a single PNG images. Since LVGL support images width and height up to + * 2048 px, the icons needs to be split into 2 separate PNG pictures. More info in + * src/displayapp/fonts/README.md + * - To make the handling of those icons easier, they must all have the same width and height + * - Those PNG are then converted into BINARY format using the classical image generator + * (in src/resources/generate-img.py) + * - The array `iconMap` maps each icon with an index. This index corresponds to the position of + * the icon in the file. All index lower than 25 (`maxIconsPerFile`) represent icons located + * in the first file (navigation0.bin). All the other icons are located in the second file + * (navigation1.bin). Since all icons have the same height, this index must be multiplied by + * 80px (`iconHeight`) to get the actual position (in pixels) of the icon in the image. + * - This is how the images are laid out in the PNG files : + * *---------------* + * | ICON 0 | + * | FILE 0 | + * | INDEX = 0 | + * | PIXEL# = 0 | + * *---------------* + * | ICON 1 | + * | FILE 0 | + * | INDEX = 1 | + * | PIXEL# = -80 | + * *---------------* + * | ICON 2 | + * | FILE 0 | + * | INDEX = 2 | + * | PIXEL# = -160 | + * *---------------* + * | ... | + * *---------------* + * | ICON 25 | + * | FILE 1 | + * | INDEX = 25 | + * | PIXEL# = 0 | + * *---------------* + * | ICON 26 | + * | FILE 1 | + * | INDEX = 26 | + * | PIXEL# = -80 | + * *---------------* + * - The source images are located in `src/resources/navigation0.png` and `src/resources/navigation1.png` + */ namespace { - constexpr std::array, 86> m_iconMap = {{ - {"arrive-left", "\xEE\xA4\x81"}, - {"arrive-right", "\xEE\xA4\x82"}, - {"arrive-straight", "\xEE\xA4\x80"}, - {"arrive", "\xEE\xA4\x80"}, - {"close", "\xEE\xA4\x83"}, - {"continue-left", "\xEE\xA4\x85"}, - {"continue-right", "\xEE\xA4\x86"}, - {"continue-slight-left", "\xEE\xA4\x87"}, - {"continue-slight-right", "\xEE\xA4\x88"}, - {"continue-straight", "\xEE\xA4\x84"}, - {"continue-uturn", "\xEE\xA4\x89"}, - {"continue", "\xEE\xA4\x84"}, - {"depart-left", "\xEE\xA4\x8B"}, - {"depart-right", "\xEE\xA4\x8C"}, - {"depart-straight", "\xEE\xA4\x8A"}, - {"end-of-road-left", "\xEE\xA4\x8D"}, - {"end-of-road-right", "\xEE\xA4\x8E"}, - {"ferry", "\xEE\xA4\x8F"}, - {"flag", "\xEE\xA4\x90"}, - {"fork-left", "\xEE\xA4\x92"}, - {"fork-right", "\xEE\xA4\x93"}, - {"fork-slight-left", "\xEE\xA4\x94"}, - {"fork-slight-right", "\xEE\xA4\x95"}, - {"fork-straight", "\xEE\xA4\x96"}, - {"invalid", "\xEE\xA4\x84"}, - {"invalid-left", "\xEE\xA4\x85"}, - {"invalid-right", "\xEE\xA4\x86"}, - {"invalid-slight-left", "\xEE\xA4\x87"}, - {"invalid-slight-right", "\xEE\xA4\x88"}, - {"invalid-straight", "\xEE\xA4\x84"}, - {"invalid-uturn", "\xEE\xA4\x89"}, - {"merge-left", "\xEE\xA4\x97"}, - {"merge-right", "\xEE\xA4\x98"}, - {"merge-slight-left", "\xEE\xA4\x99"}, - {"merge-slight-right", "\xEE\xA4\x9A"}, - {"merge-straight", "\xEE\xA4\x84"}, - {"new-name-left", "\xEE\xA4\x85"}, - {"new-name-right", "\xEE\xA4\x86"}, - {"new-name-sharp-left", "\xEE\xA4\x9B"}, - {"new-name-sharp-right", "\xEE\xA4\x9C"}, - {"new-name-slight-left", "\xEE\xA4\x87"}, - {"new-name-slight-right", "\xEE\xA4\x88"}, - {"new-name-straight", "\xEE\xA4\x84"}, - {"notification-left", "\xEE\xA4\x85"}, - {"notification-right", "\xEE\xA4\x86"}, - {"notification-sharp-left", "\xEE\xA4\x9B"}, - {"notification-sharp-right", "\xEE\xA4\xA5"}, - {"notification-slight-left", "\xEE\xA4\x87"}, - {"notification-slight-right", "\xEE\xA4\x88"}, - {"notification-straight", "\xEE\xA4\x84"}, - {"off-ramp-left", "\xEE\xA4\x9D"}, - {"off-ramp-right", "\xEE\xA4\x9E"}, - {"off-ramp-slight-left", "\xEE\xA4\x9F"}, - {"off-ramp-slight-right", "\xEE\xA4\xA0"}, - {"on-ramp-left", "\xEE\xA4\x85"}, - {"on-ramp-right", "\xEE\xA4\x86"}, - {"on-ramp-sharp-left", "\xEE\xA4\x9B"}, - {"on-ramp-sharp-right", "\xEE\xA4\xA5"}, - {"on-ramp-slight-left", "\xEE\xA4\x87"}, - {"on-ramp-slight-right", "\xEE\xA4\x88"}, - {"on-ramp-straight", "\xEE\xA4\x84"}, - {"rotary", "\xEE\xA4\xA1"}, - {"rotary-left", "\xEE\xA4\xA2"}, - {"rotary-right", "\xEE\xA4\xA3"}, - {"rotary-sharp-left", "\xEE\xA4\xA4"}, - {"rotary-sharp-right", "\xEE\xA4\xA5"}, - {"rotary-slight-left", "\xEE\xA4\xA6"}, - {"rotary-slight-right", "\xEE\xA4\xA7"}, - {"rotary-straight", "\xEE\xA4\xA8"}, - {"roundabout", "\xEE\xA4\xA1"}, - {"roundabout-left", "\xEE\xA4\xA2"}, - {"roundabout-right", "\xEE\xA4\xA3"}, - {"roundabout-sharp-left", "\xEE\xA4\xA4"}, - {"roundabout-sharp-right", "\xEE\xA4\xA5"}, - {"roundabout-slight-left", "\xEE\xA4\xA6"}, - {"roundabout-slight-right", "\xEE\xA4\xA7"}, - {"roundabout-straight", "\xEE\xA4\xA8"}, - {"turn-left", "\xEE\xA4\x85"}, - {"turn-right", "\xEE\xA4\x86"}, - {"turn-sharp-left", "\xEE\xA4\x9B"}, - {"turn-sharp-right", "\xEE\xA4\xA5"}, - {"turn-slight-left", "\xEE\xA4\x87"}, - {"turn-slight-right", "\xEE\xA4\x88"}, - {"turn-straight", "\xEE\xA4\x84"}, - {"updown", "\xEE\xA4\xA9"}, - {"uturn", "\xEE\xA4\x89"}, + struct Icon { + const char* fileName; + int16_t offset; + }; + + constexpr uint16_t iconHeight = -80; + constexpr uint8_t flagIndex = 18; + constexpr uint8_t maxIconsPerFile = 25; + const char* iconsFile0 = "F:/images/navigation0.bin"; + const char* iconsFile1 = "F:/images/navigation1.bin"; + + constexpr std::array, 86> iconMap = {{ + {"arrive-left", 1}, + {"arrive-right", 2}, + {"arrive-straight", 0}, + {"arrive", 0}, + {"close", 3}, + {"continue-left", 5}, + {"continue-right", 6}, + {"continue-slight-left", 7}, + {"continue-slight-right", 8}, + {"continue-straight", 4}, + {"continue-uturn", 9}, + {"continue", 4}, + {"depart-left", 11}, + {"depart-right", 12}, + {"depart-straight", 10}, + {"end-of-road-left", 13}, + {"end-of-road-right", 14}, + {"ferry", 15}, + {"flag", 16}, + {"fork-left", 18}, + {"fork-right", 19}, + {"fork-slight-left", 20}, + {"fork-slight-right", 21}, + {"fork-straight", 22}, + {"invalid", 4}, + {"invalid-left", 5}, + {"invalid-right", 6}, + {"invalid-slight-left", 7}, + {"invalid-slight-right", 8}, + {"invalid-straight", 4}, + {"invalid-uturn", 9}, + {"merge-left", 23}, + {"merge-right", 24}, + {"merge-slight-left", 25}, + {"merge-slight-right", 26}, + {"merge-straight", 4}, + {"new-name-left", 5}, + {"new-name-right", 6}, + {"new-name-sharp-left", 27}, + {"new-name-sharp-right", 28}, + {"new-name-slight-left", 7}, + {"new-name-slight-right", 8}, + {"new-name-straight", 4}, + {"notification-left", 5}, + {"notification-right", 6}, + {"notification-sharp-left", 27}, + {"notification-sharp-right", 37}, + {"notification-slight-left", 7}, + {"notification-slight-right", 8}, + {"notification-straight", 4}, + {"off-ramp-left", 29}, + {"off-ramp-right", 30}, + {"off-ramp-slight-left", 31}, + {"off-ramp-slight-right", 32}, + {"on-ramp-left", 5}, + {"on-ramp-right", 6}, + {"on-ramp-sharp-left", 27}, + {"on-ramp-sharp-right", 37}, + {"on-ramp-slight-left", 7}, + {"on-ramp-slight-right", 8}, + {"on-ramp-straight", 4}, + {"rotary", 33}, + {"rotary-left", 34}, + {"rotary-right", 35}, + {"rotary-sharp-left", 36}, + {"rotary-sharp-right", 37}, + {"rotary-slight-left", 38}, + {"rotary-slight-right", 39}, + {"rotary-straight", 40}, + {"roundabout", 33}, + {"roundabout-left", 34}, + {"roundabout-right", 35}, + {"roundabout-sharp-left", 36}, + {"roundabout-sharp-right", 37}, + {"roundabout-slight-left", 38}, + {"roundabout-slight-right", 39}, + {"roundabout-straight", 40}, + {"turn-left", 5}, + {"turn-right", 6}, + {"turn-sharp-left", 27}, + {"turn-sharp-right", 37}, + {"turn-slight-left", 7}, + {"turn-slight-right", 8}, + {"turn-straight", 4}, + {"updown", 41}, + {"uturn", 9}, }}; - const char* iconForName(const std::string& icon) { - for (auto iter : m_iconMap) { + Icon GetIcon(uint8_t index) { + if (index < maxIconsPerFile) { + return {iconsFile0, static_cast(iconHeight * index)}; + } + return {iconsFile1, static_cast(iconHeight * (index - maxIconsPerFile))}; + } + + Icon GetIcon(const std::string& icon) { + for (const auto& iter : iconMap) { if (iter.first == icon) { - return iter.second; + return GetIcon(iter.second); } } - return "\xEE\xA4\x90"; + return GetIcon(flagIndex); } } @@ -130,11 +191,15 @@ namespace { * */ Navigation::Navigation(Pinetime::Controllers::NavigationService& nav) : navService(nav) { - - imgFlag = lv_label_create(lv_scr_act(), nullptr); - lv_obj_set_style_local_text_font(imgFlag, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_navi_80); - lv_obj_set_style_local_text_color(imgFlag, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_CYAN); - lv_label_set_text_static(imgFlag, iconForName("flag")); + const auto& image = GetIcon("flag"); + imgFlag = lv_img_create(lv_scr_act(), nullptr); + lv_img_set_auto_size(imgFlag, false); + lv_obj_set_size(imgFlag, 80, 80); + lv_img_set_src(imgFlag, image.fileName); + lv_img_set_offset_x(imgFlag, 0); + lv_img_set_offset_y(imgFlag, image.offset); + lv_obj_set_style_local_image_recolor_opa(imgFlag, LV_IMG_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_COVER); + lv_obj_set_style_local_image_recolor(imgFlag, LV_IMG_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_CYAN); lv_obj_align(imgFlag, nullptr, LV_ALIGN_CENTER, 0, -60); txtNarrative = lv_label_create(lv_scr_act(), nullptr); @@ -173,7 +238,11 @@ Navigation::~Navigation() { void Navigation::Refresh() { if (flag != navService.getFlag()) { flag = navService.getFlag(); - lv_label_set_text_static(imgFlag, iconForName(flag)); + const auto& image = GetIcon(flag); + lv_img_set_src(imgFlag, image.fileName); + lv_obj_set_style_local_image_recolor_opa(imgFlag, LV_IMG_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_COVER); + lv_obj_set_style_local_image_recolor(imgFlag, LV_IMG_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_CYAN); + lv_img_set_offset_y(imgFlag, image.offset); } if (narrative != navService.getNarrative()) { diff --git a/src/displayapp/screens/Navigation.h b/src/displayapp/screens/Navigation.h index 6495edb2c3..ab81d48c73 100644 --- a/src/displayapp/screens/Navigation.h +++ b/src/displayapp/screens/Navigation.h @@ -32,7 +32,7 @@ namespace Pinetime { namespace Screens { class Navigation : public Screen { public: - Navigation(Pinetime::Controllers::NavigationService& nav); + explicit Navigation(Pinetime::Controllers::NavigationService& nav); ~Navigation() override; void Refresh() override; @@ -48,7 +48,7 @@ namespace Pinetime { std::string flag; std::string narrative; std::string manDist; - int progress; + int progress = 0; lv_task_t* taskRefresh; }; diff --git a/src/resources/images.json b/src/resources/images.json index db2ccab088..e424718808 100644 --- a/src/resources/images.json +++ b/src/resources/images.json @@ -5,5 +5,19 @@ "output_format": "bin", "binary_format": "ARGB8565_RBSWAP", "target_path": "/images/" + }, + "navigation0" : { + "sources": "images/navigation0.png", + "color_format": "CF_INDEXED_1_BIT", + "output_format": "bin", + "binary_format": "ARGB8565_RBSWAP", + "target_path": "/images/" + }, + "navigation1" : { + "sources": "images/navigation1.png", + "color_format": "CF_INDEXED_1_BIT", + "output_format": "bin", + "binary_format": "ARGB8565_RBSWAP", + "target_path": "/images/" } } diff --git a/src/resources/images/navigation0.png b/src/resources/images/navigation0.png new file mode 100644 index 0000000000000000000000000000000000000000..37d7abd842da080551e7c01ef1121b22cdd00f54 GIT binary patch literal 22487 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

qRboH=+coB;?@ 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 W5U=F?F7zB|D1)b~pUXO@geCwnKVmol literal 0 HcmV?d00001 From 0aead42fdf5137becefb11f056fe8e567e2f454a Mon Sep 17 00:00:00 2001 From: JF Date: Sat, 2 Sep 2023 19:43:39 +0200 Subject: [PATCH 3/3] navigation: Add is available (#1847) Navigation app now needs 2 images to be loaded from the resources on the external filesystem. This PR adds an 'enabled' field to the Applications struct. This field is true for all applications expect for Navigation which calls Navigation::IsAvailable(). This methods returns true if the 2 files are available in the resources. The application list disables the application (draws it in grey, disables the touch callback) if the enable flag is not set. --- src/displayapp/DisplayApp.cpp | 2 +- src/displayapp/screens/ApplicationList.cpp | 6 ++-- src/displayapp/screens/ApplicationList.h | 33 ++++++++++++---------- src/displayapp/screens/Navigation.cpp | 16 +++++++++++ src/displayapp/screens/Navigation.h | 2 ++ src/displayapp/screens/Tile.cpp | 2 +- src/displayapp/screens/Tile.h | 1 + 7 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index a930fe961c..cd941f16cc 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -404,7 +404,7 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio switch (app) { case Apps::Launcher: currentScreen = - std::make_unique(this, settingsController, batteryController, bleController, dateTimeController); + std::make_unique(this, settingsController, batteryController, bleController, dateTimeController, filesystem); break; case Apps::Motion: // currentScreen = std::make_unique(motionController); diff --git a/src/displayapp/screens/ApplicationList.cpp b/src/displayapp/screens/ApplicationList.cpp index 0a65a5d472..6014cf53e0 100644 --- a/src/displayapp/screens/ApplicationList.cpp +++ b/src/displayapp/screens/ApplicationList.cpp @@ -6,8 +6,6 @@ using namespace Pinetime::Applications::Screens; -constexpr std::array ApplicationList::applications; - auto ApplicationList::CreateScreenList() const { std::array()>, nScreens> screens; for (size_t i = 0; i < screens.size(); i++) { @@ -22,12 +20,14 @@ ApplicationList::ApplicationList(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Settings& settingsController, const Pinetime::Controllers::Battery& batteryController, const Pinetime::Controllers::Ble& bleController, - Controllers::DateTime& dateTimeController) + Controllers::DateTime& dateTimeController, + Pinetime::Controllers::FS& filesystem) : app {app}, settingsController {settingsController}, batteryController {batteryController}, bleController {bleController}, dateTimeController {dateTimeController}, + filesystem{filesystem}, screens {app, settingsController.GetAppMenu(), CreateScreenList(), Screens::ScreenListModes::UpDown} { } diff --git a/src/displayapp/screens/ApplicationList.h b/src/displayapp/screens/ApplicationList.h index 7bdd115408..371ee710b5 100644 --- a/src/displayapp/screens/ApplicationList.h +++ b/src/displayapp/screens/ApplicationList.h @@ -10,6 +10,7 @@ #include "components/battery/BatteryController.h" #include "displayapp/screens/Symbols.h" #include "displayapp/screens/Tile.h" +#include "displayapp/screens/Navigation.h" namespace Pinetime { namespace Applications { @@ -20,7 +21,8 @@ namespace Pinetime { Pinetime::Controllers::Settings& settingsController, const Pinetime::Controllers::Battery& batteryController, const Pinetime::Controllers::Ble& bleController, - Controllers::DateTime& dateTimeController); + Controllers::DateTime& dateTimeController, + Pinetime::Controllers::FS& filesystem); ~ApplicationList() override; bool OnTouchEvent(TouchEvents event) override; @@ -33,26 +35,27 @@ namespace Pinetime { const Pinetime::Controllers::Battery& batteryController; const Pinetime::Controllers::Ble& bleController; Controllers::DateTime& dateTimeController; + Pinetime::Controllers::FS& filesystem; static constexpr int appsPerScreen = 6; // Increment this when more space is needed static constexpr int nScreens = 2; - static constexpr std::array applications {{ - {Symbols::stopWatch, Apps::StopWatch}, - {Symbols::clock, Apps::Alarm}, - {Symbols::hourGlass, Apps::Timer}, - {Symbols::shoe, Apps::Steps}, - {Symbols::heartBeat, Apps::HeartRate}, - {Symbols::music, Apps::Music}, - - {Symbols::paintbrush, Apps::Paint}, - {Symbols::paddle, Apps::Paddle}, - {"2", Apps::Twos}, - {Symbols::drum, Apps::Metronome}, - {Symbols::map, Apps::Navigation}, - {Symbols::none, Apps::None}, + std::array applications {{ + {Symbols::stopWatch, Apps::StopWatch, true}, + {Symbols::clock, Apps::Alarm, true}, + {Symbols::hourGlass, Apps::Timer, true}, + {Symbols::shoe, Apps::Steps, true}, + {Symbols::heartBeat, Apps::HeartRate, true}, + {Symbols::music, Apps::Music, true}, + + {Symbols::paintbrush, Apps::Paint, true}, + {Symbols::paddle, Apps::Paddle, true}, + {"2", Apps::Twos, true}, + {Symbols::drum, Apps::Metronome, true}, + {Symbols::map, Apps::Navigation, Applications::Screens::Navigation::IsAvailable(filesystem)}, + {Symbols::none, Apps::None, false}, // {"M", Apps::Motion}, }}; diff --git a/src/displayapp/screens/Navigation.cpp b/src/displayapp/screens/Navigation.cpp index fe2f1eb6a0..799ac8a9fa 100644 --- a/src/displayapp/screens/Navigation.cpp +++ b/src/displayapp/screens/Navigation.cpp @@ -265,3 +265,19 @@ void Navigation::Refresh() { } } } + +bool Navigation::IsAvailable(Pinetime::Controllers::FS& filesystem) { + lfs_file file = {}; + + if (filesystem.FileOpen(&file, "/images/navigation0.bin", LFS_O_RDONLY) < 0) { + return false; + } + filesystem.FileClose(&file); + + if (filesystem.FileOpen(&file, "/images/navigation1.bin", LFS_O_RDONLY) < 0) { + return false; + } + filesystem.FileClose(&file); + + return true; +} diff --git a/src/displayapp/screens/Navigation.h b/src/displayapp/screens/Navigation.h index ab81d48c73..eb243b011a 100644 --- a/src/displayapp/screens/Navigation.h +++ b/src/displayapp/screens/Navigation.h @@ -26,6 +26,7 @@ namespace Pinetime { namespace Controllers { class NavigationService; + class FS; } namespace Applications { @@ -36,6 +37,7 @@ namespace Pinetime { ~Navigation() override; void Refresh() override; + static bool IsAvailable(Pinetime::Controllers::FS& filesystem); private: lv_obj_t* imgFlag; diff --git a/src/displayapp/screens/Tile.cpp b/src/displayapp/screens/Tile.cpp index 1266f379f8..343755e3f0 100644 --- a/src/displayapp/screens/Tile.cpp +++ b/src/displayapp/screens/Tile.cpp @@ -76,7 +76,7 @@ Tile::Tile(uint8_t screenID, for (uint8_t i = 0; i < 6; i++) { lv_btnmatrix_set_btn_ctrl(btnm1, i, LV_BTNMATRIX_CTRL_CLICK_TRIG); - if (applications[i].application == Apps::None) { + if (applications[i].application == Apps::None || !applications[i].enabled) { lv_btnmatrix_set_btn_ctrl(btnm1, i, LV_BTNMATRIX_CTRL_DISABLED); } } diff --git a/src/displayapp/screens/Tile.h b/src/displayapp/screens/Tile.h index 91acb26c7b..8c1cd12cdc 100644 --- a/src/displayapp/screens/Tile.h +++ b/src/displayapp/screens/Tile.h @@ -19,6 +19,7 @@ namespace Pinetime { struct Applications { const char* icon; Pinetime::Applications::Apps application; + bool enabled; }; explicit Tile(uint8_t screenID,