~aleteoryx/muditaos

b934328d5ef3e23154d1f8d86d15ea5a2b80d3ad — Mateusz Piesta 4 years ago 4a79256
[BH-1053] Battery status window

Added battery status window accessible
from home screen.

Added battery charge level translator.
27 files changed, 257 insertions(+), 30 deletions(-)

A art/bell/bell_lightning_W_G.png
A art/bell/bell_lightning_W_M.png
A art/bell/bell_status_battery_lvl0_W_G.png
A art/bell/bell_status_battery_lvl1_W_G.png
A art/bell/bell_status_battery_lvl2_W_G.png
A art/bell/bell_status_battery_lvl3_W_G.png
A art/bell/bell_status_battery_lvl4_W_G.png
A art/bell/bell_status_battery_lvl5_W_G.png
A image/assets/images/bell/bell_lightning_W_G.vpi
A image/assets/images/bell/bell_lightning_W_M.vpi
A image/assets/images/bell/bell_status_battery_lvl0_W_G.vpi
A image/assets/images/bell/bell_status_battery_lvl1_W_G.vpi
A image/assets/images/bell/bell_status_battery_lvl2_W_G.vpi
A image/assets/images/bell/bell_status_battery_lvl3_W_G.vpi
A image/assets/images/bell/bell_status_battery_lvl4_W_G.vpi
A image/assets/images/bell/bell_status_battery_lvl5_W_G.vpi
M products/BellHybrid/apps/application-bell-main/ApplicationBellMain.cpp
M products/BellHybrid/apps/application-bell-main/CMakeLists.txt
A products/BellHybrid/apps/application-bell-main/data/BatteryUtils.hpp
M products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.cpp
M products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.hpp
M products/BellHybrid/apps/application-bell-main/presenters/StateController.cpp
M products/BellHybrid/apps/application-bell-main/widgets/BellBattery.cpp
A products/BellHybrid/apps/application-bell-main/windows/BellBatteryStatusWindow.cpp
A products/BellHybrid/apps/application-bell-main/windows/BellBatteryStatusWindow.hpp
M products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.cpp
M products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.hpp
A art/bell/bell_lightning_W_G.png => art/bell/bell_lightning_W_G.png +0 -0
A art/bell/bell_lightning_W_M.png => art/bell/bell_lightning_W_M.png +0 -0
A art/bell/bell_status_battery_lvl0_W_G.png => art/bell/bell_status_battery_lvl0_W_G.png +0 -0
A art/bell/bell_status_battery_lvl1_W_G.png => art/bell/bell_status_battery_lvl1_W_G.png +0 -0
A art/bell/bell_status_battery_lvl2_W_G.png => art/bell/bell_status_battery_lvl2_W_G.png +0 -0
A art/bell/bell_status_battery_lvl3_W_G.png => art/bell/bell_status_battery_lvl3_W_G.png +0 -0
A art/bell/bell_status_battery_lvl4_W_G.png => art/bell/bell_status_battery_lvl4_W_G.png +0 -0
A art/bell/bell_status_battery_lvl5_W_G.png => art/bell/bell_status_battery_lvl5_W_G.png +0 -0
A image/assets/images/bell/bell_lightning_W_G.vpi => image/assets/images/bell/bell_lightning_W_G.vpi +0 -0
A image/assets/images/bell/bell_lightning_W_M.vpi => image/assets/images/bell/bell_lightning_W_M.vpi +0 -0
A image/assets/images/bell/bell_status_battery_lvl0_W_G.vpi => image/assets/images/bell/bell_status_battery_lvl0_W_G.vpi +0 -0
A image/assets/images/bell/bell_status_battery_lvl1_W_G.vpi => image/assets/images/bell/bell_status_battery_lvl1_W_G.vpi +0 -0
A image/assets/images/bell/bell_status_battery_lvl2_W_G.vpi => image/assets/images/bell/bell_status_battery_lvl2_W_G.vpi +0 -0
A image/assets/images/bell/bell_status_battery_lvl3_W_G.vpi => image/assets/images/bell/bell_status_battery_lvl3_W_G.vpi +0 -0
A image/assets/images/bell/bell_status_battery_lvl4_W_G.vpi => image/assets/images/bell/bell_status_battery_lvl4_W_G.vpi +0 -0
A image/assets/images/bell/bell_status_battery_lvl5_W_G.vpi => image/assets/images/bell/bell_status_battery_lvl5_W_G.vpi +0 -0
M products/BellHybrid/apps/application-bell-main/ApplicationBellMain.cpp => products/BellHybrid/apps/application-bell-main/ApplicationBellMain.cpp +5 -1
@@ 9,6 9,7 @@
#include "windows/BellBatteryShutdownWindow.hpp"
#include "windows/BellHomeScreenWindow.hpp"
#include "windows/BellMainMenuWindow.hpp"
#include "windows/BellBatteryStatusWindow.hpp"

#include <apps-common/messages/AppMessage.hpp>
#include <common/BellPowerOffPresenter.hpp>


@@ 78,7 79,10 @@ namespace app
            return std::make_unique<gui::BellFactoryReset>(app, std::make_unique<gui::BellPowerOffPresenter>(app));
        });

        // for demo only - to be removed
        windowsFactory.attach(gui::BellBatteryStatusWindow::name, [](ApplicationCommon *app, const std::string &name) {
            return std::make_unique<gui::BellBatteryStatusWindow>(app);
        });

        windowsFactory.attach(
            gui::window::name::bell_main_menu_dialog,
            [](ApplicationCommon *app, const std::string &name) { return std::make_unique<gui::Dialog>(app, name); });

M products/BellHybrid/apps/application-bell-main/CMakeLists.txt => products/BellHybrid/apps/application-bell-main/CMakeLists.txt +3 -0
@@ 10,6 10,7 @@ target_sources(application-bell-main
        windows/BellBatteryShutdownWindow.cpp
        windows/BellHomeScreenWindow.cpp
        windows/BellMainMenuWindow.cpp
        windows/BellBatteryStatusWindow.cpp

        models/BatteryModel.cpp
        models/TemperatureModel.cpp


@@ 24,6 25,8 @@ target_sources(application-bell-main
        windows/BellBatteryShutdownWindow.hpp
        windows/BellHomeScreenWindow.hpp
        windows/BellMainMenuWindow.hpp
        windows/BellBatteryStatusWindow.hpp
        data/BatteryUtils.hpp

        models/BatteryModel.cpp
        models/TemperatureModel.hpp

A products/BellHybrid/apps/application-bell-main/data/BatteryUtils.hpp => products/BellHybrid/apps/application-bell-main/data/BatteryUtils.hpp +48 -0
@@ 0,0 1,48 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <cstdint>
#include <algorithm>
#include <optional>

namespace battery_utils
{
    struct BatteryLevelEntry
    {
        using Range = std::pair<std::uint32_t, std::uint32_t>;
        Range realLvl;
        Range displayedLvl;
        std::string_view image;
    };

    struct ScaledBatteryLevel
    {
        std::uint32_t level;
        std::string image;
    };

    template <std::size_t N>
    [[nodiscard]] std::optional<ScaledBatteryLevel> getScaledBatteryLevel(
        const std::array<BatteryLevelEntry, N> &entries, const std::uint32_t val)
    {
        auto result = std::find_if(entries.begin(), entries.end(), [val](const auto &e) {
            return val >= e.realLvl.first && val <= e.realLvl.second;
        });

        if (result != entries.end()) {
            const auto outputStart = result->displayedLvl.first;
            const auto outputEnd   = result->displayedLvl.second;
            const auto inputStart  = result->realLvl.first;
            const auto inputEnd    = result->realLvl.second;
            float slope            = 1.0 * (outputEnd - outputStart) / (inputEnd - inputStart);
            auto output            = outputStart + slope * (val - inputStart);

            return ScaledBatteryLevel{static_cast<std::uint32_t>(std::floor(output)), result->image.data()};
        }

        return {};
    }

} // namespace battery_utils

M products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.cpp => products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.cpp +8 -0
@@ 114,4 114,12 @@ namespace app::home_screen
        snoozeTimer->reset(snoozeDuration, snoozeTick);
    }

    std::uint32_t HomeScreenPresenter::getBatteryLvl() const
    {
        return batteryModel->getLevelState().level;
    }
    bool HomeScreenPresenter::isBatteryCharging() const
    {
        return batteryModel->getLevelState().state == Store::Battery::State::Charging;
    }
} // namespace app::home_screen

M products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.hpp => products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.hpp +6 -2
@@ 74,7 74,8 @@ namespace app::home_screen
        virtual void setBatteryLevelState(const Store::Battery &batteryContext) = 0;

        /// Various
        virtual void switchToMenu() = 0;
        virtual void switchToMenu()          = 0;
        virtual void switchToBatteryStatus() = 0;
    };

    class AbstractPresenter : public BasePresenter<AbstractView>


@@ 95,6 96,8 @@ namespace app::home_screen
        virtual void startSnoozeTimer(std::chrono::seconds snoozeDuration)                       = 0;
        virtual void stopSnoozeTimer()                                                           = 0;
        virtual void restartSnoozeTimer(std::chrono::seconds snoozeDuration)                     = 0;
        virtual std::uint32_t getBatteryLvl() const                                              = 0;
        virtual bool isBatteryCharging() const                                                   = 0;

        static constexpr auto defaultTimeout = std::chrono::milliseconds{5000};
    };


@@ 127,10 130,11 @@ namespace app::home_screen
        void handleAlarmModelReady() override;

        void setSnoozeTimer(std::unique_ptr<app::ProgressTimerWithSnoozeTimer> &&_timer) override;

        void startSnoozeTimer(std::chrono::seconds snoozeDuration);
        void stopSnoozeTimer();
        void restartSnoozeTimer(std::chrono::seconds snoozeDuration);
        std::uint32_t getBatteryLvl() const override;
        bool isBatteryCharging() const override;

      private:
        ApplicationCommon *app;

M products/BellHybrid/apps/application-bell-main/presenters/StateController.cpp => products/BellHybrid/apps/application-bell-main/presenters/StateController.cpp +6 -3
@@ 32,9 32,10 @@ namespace app::home_screen
    {
        namespace Helpers
        {
            auto switchToMenu         = [](AbstractView &view) { view.switchToMenu(); };
            auto makeAlarmEditable    = [](AbstractView &view) { view.setAlarmEdit(true); };
            auto makeAlarmNonEditable = [](AbstractView &view) { view.setAlarmEdit(false); };
            auto switchToMenu          = [](AbstractView &view) { view.switchToMenu(); };
            auto switchToBatteryStatus = [](AbstractView &view) { view.switchToBatteryStatus(); };
            auto makeAlarmEditable     = [](AbstractView &view) { view.setAlarmEdit(true); };
            auto makeAlarmNonEditable  = [](AbstractView &view) { view.setAlarmEdit(false); };
            auto updateBottomStats =
                [](AbstractView &view, AbstractBatteryModel &batteryModel, AbstractTemperatureModel &temperatureModel) {
                    view.setTemperature(temperatureModel.getTemperature());


@@ 274,6 275,7 @@ namespace app::home_screen
                                             "Deactivated"_s + event<Events::RotateRightPress> / Helpers::makeAlarmEditable = "DeactivatedEdit"_s,
                                             "Deactivated"_s + event<Events::DeepUpPress> = "ActivatedWait"_s,
                                             "Deactivated"_s + event<Events::TimeUpdate> / Helpers::updateBottomStats,
                                             "Deactivated"_s + event<Events::BackPress>  / Helpers::switchToBatteryStatus,

                                             "DeactivatedWait"_s + sml::on_entry<_> / DeactivatedWait::entry,
                                             "DeactivatedWait"_s + sml::on_exit<_> / DeactivatedWait::exit,


@@ 320,6 322,7 @@ namespace app::home_screen
                                             "Activated"_s + event<Events::TimeUpdate> / Helpers::updateBottomStats,
                                             "Activated"_s + event<Events::DeepDownPress>  = "DeactivatedWait"_s,
                                             "Activated"_s + event<Events::AlarmRinging>  = "AlarmRinging"_s,
                                             "Activated"_s + event<Events::BackPress>  / Helpers::switchToBatteryStatus,

                                             "ActivatedEdit"_s + sml::on_entry<_> / AlarmEdit::entry,
                                             "ActivatedEdit"_s + sml::on_exit<_> / AlarmEdit::exit,

M products/BellHybrid/apps/application-bell-main/widgets/BellBattery.cpp => products/BellHybrid/apps/application-bell-main/widgets/BellBattery.cpp +13 -24
@@ 2,21 2,19 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "BellBattery.hpp"
#include "data/BatteryUtils.hpp"
#include <Image.hpp>

namespace
{
    constexpr unsigned int minVal   = 80;
    constexpr unsigned int oldMax   = 95;
    constexpr unsigned int newMax   = 100;
    constexpr unsigned int oldRange = oldMax - minVal;
    constexpr unsigned int newRange = newMax - minVal;

    unsigned int translateBatteryLevel(unsigned int percentage)
    {
        return (((percentage - minVal) * newRange) / oldRange) + minVal;
    }
} // namespace
    constexpr auto entries =
        std::array<battery_utils::BatteryLevelEntry, 6>{{{{5, 10}, {1, 5}, "bell_battery_empty"},
                                                         {{11, 30}, {6, 29}, "bell_battery_lvl1"},
                                                         {{31, 50}, {30, 53}, "bell_battery_lvl2"},
                                                         {{51, 70}, {54, 77}, "bell_battery_lvl3"},
                                                         {{71, 95}, {78, 99}, "bell_battery_lvl4"},
                                                         {{96, 100}, {100, 100}, "bell_battery_lvl5"}}};
}

namespace gui
{


@@ 37,21 35,12 @@ namespace gui

    void BellBattery::update(const Store::Battery &batteryContext)
    {
        (void)batteryContext;

        // Fuel gauge and charger are different hw elements
        // Charger sometimes stops charging before FG shows 100%
        auto level = batteryContext.level;

        if (level > 95) {
            level = 100;
        }

        // Translate 80-95% range to 80-100% range for display
        if ((level > 80) && (level <= 95)) {
            level = translateBatteryLevel(level);
        const auto result = battery_utils::getScaledBatteryLevel(entries, batteryContext.level);
        if (not result) {
            return;
        }

        const auto level = result->level;
        if (batteryContext.state == Store::Battery::State::Charging) {
            img->set(battery::battery_charging, gui::ImageTypeSpecifier::W_M);


A products/BellHybrid/apps/application-bell-main/windows/BellBatteryStatusWindow.cpp => products/BellHybrid/apps/application-bell-main/windows/BellBatteryStatusWindow.cpp +111 -0
@@ 0,0 1,111 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "BellBatteryStatusWindow.hpp"
#include "data/BatteryUtils.hpp"

#include <gui/widgets/TextFixedSize.hpp>
#include <gui/widgets/ImageBox.hpp>
#include <gui/input/InputEvent.hpp>
#include <Application.hpp>

#include <chrono>

namespace
{
    constexpr auto imageType = gui::ImageTypeSpecifier::W_G;
    constexpr auto batteryEntries =
        std::array<battery_utils::BatteryLevelEntry, 6>{{{{5, 10}, {1, 5}, "bell_status_battery_lvl0"},
                                                         {{11, 30}, {6, 29}, "bell_status_battery_lvl1"},
                                                         {{31, 50}, {30, 53}, "bell_status_battery_lvl2"},
                                                         {{51, 70}, {54, 77}, "bell_status_battery_lvl3"},
                                                         {{71, 95}, {78, 99}, "bell_status_battery_lvl4"},
                                                         {{96, 100}, {100, 100}, "bell_status_battery_lvl5"}}};
} // namespace

namespace gui
{
    using namespace std::chrono_literals;

    BellBatteryStatusWindow::BellBatteryStatusWindow(app::ApplicationCommon *app) : WindowWithTimer{app, name, 5s}
    {
        buildInterface();
    }
    void BellBatteryStatusWindow::buildInterface()
    {
        WindowWithTimer::buildInterface();
        statusBar->setVisible(false);
        header->setTitleVisibility(false);
        navBar->setVisible(false);

        body = new BellBaseLayout(this, 0, 0, style::window_width, style::window_height, false);

        topDescription = new TextFixedSize(body->firstBox);
        topDescription->setMinimumSize(style::bell_base_layout::outer_layouts_w,
                                       style::bell_base_layout::outer_layouts_h);
        topDescription->setFont(top_description_font);
        topDescription->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        topDescription->setEdges(RectangleEdge::None);
        topDescription->activeItem = false;
        topDescription->drawUnderline(false);
        topDescription->setText(utils::translate("app_settings_tech_info_battery"));

        hbox = new HBox(body->lastBox);
        hbox->setMinimumSize(style::bell_base_layout::outer_layouts_w, style::bell_base_layout::outer_layouts_h);
        hbox->setEdges(RectangleEdge::None);
        hbox->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));

        chargingIcon = new ImageBox(hbox, 0, 0, 0, 0, new Image("bell_lightning", imageType));
        chargingIcon->setMargins(gui::Margins(0, 0, 8, 0));
        chargingIcon->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        chargingIcon->setMinimumSizeToFitImage();
        chargingIcon->activeItem = false;

        bottomDescription = new TextFixedSize(hbox);
        bottomDescription->setMaximumSize(bottom_description_max_size_w, bottom_description_max_size_h);
        bottomDescription->setFont(bottom_description_font);
        bottomDescription->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        bottomDescription->setEdges(RectangleEdge::None);
        bottomDescription->activeItem = false;
        bottomDescription->drawUnderline(false);

        center = new ImageBox(body->centerBox,
                              0,
                              0,
                              style::bell_base_layout::w,
                              style::bell_base_layout::h,
                              new Image("bell_status_battery_lvl0", imageType));
        center->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        center->setEdges(RectangleEdge::None);
        center->setMinimumSizeToFitImage();
        center->activeItem = false;

        body->resize();
    }
    bool BellBatteryStatusWindow::onInput(const InputEvent &inputEvent)
    {
        if (inputEvent.isShortRelease(KeyCode::KEY_ENTER) || inputEvent.isShortRelease(KeyCode::KEY_RF)) {
            application->returnToPreviousWindow();
            return true;
        }
        return false;
    }
    void BellBatteryStatusWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        if (data != nullptr) {
            WindowWithTimer::onBeforeShow(mode, data);
            const auto &switchData = static_cast<Data &>(*data);
            const auto entry       = battery_utils::getScaledBatteryLevel(batteryEntries, switchData.chargeLevel);
            if (entry) {
                center->setImage(entry->image, imageType);
                bottomDescription->setText(std::to_string(entry->level) + "%");
                chargingIcon->setVisible(switchData.isCharging);
                hbox->resizeItems();
                body->resize();
            }
        }
        else {
            LOG_ERROR("BellBatteryStatusWindow must be invoked with a valid SwitchData");
        }
    }
} // namespace gui

A products/BellHybrid/apps/application-bell-main/windows/BellBatteryStatusWindow.hpp => products/BellHybrid/apps/application-bell-main/windows/BellBatteryStatusWindow.hpp +48 -0
@@ 0,0 1,48 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <apps-common/widgets/BellBaseLayout.hpp>
#include <apps-common/popups/WindowWithTimer.hpp>

namespace gui
{
    class TextFixedSize;
    class ImageBox;
    class Image;

    class BellBatteryStatusWindow : public WindowWithTimer
    {
      public:
        struct Data : public gui::SwitchData
        {
            Data(std::uint32_t lvl, bool chargeState) : chargeLevel{lvl}, isCharging{chargeState}
            {}
            const std::uint32_t chargeLevel;
            const bool isCharging;
        };
        static constexpr auto name = "BellBatteryStatusWindow";
        explicit BellBatteryStatusWindow(app::ApplicationCommon *app);

      private:
        static constexpr auto top_description_font          = style::window::font::largelight;
        static constexpr auto bottom_description_font       = style::window::font::verybiglight;
        static constexpr auto bottom_description_max_size_w = 85U;
        static constexpr auto bottom_description_max_size_h = 85U;

        void buildInterface() override;
        bool onInput(const InputEvent &inputEvent) override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;

        BellBaseLayout *body{};
        TextFixedSize *topDescription{};

        TextFixedSize *bottomDescription{};
        ImageBox *chargingIcon{};

        HBox *hbox{};
        ImageBox *center{};
    };

} // namespace gui

M products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.cpp => products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.cpp +8 -0
@@ 4,6 4,7 @@
#include "BellHomeScreenWindow.hpp"
#include "data/BellMainStyle.hpp"
#include "widgets/SnoozeTimer.hpp"
#include "BellBatteryStatusWindow.hpp"

#include <application-bell-main/ApplicationBellMain.hpp>
#include <apps-common/widgets/BellBaseLayout.hpp>


@@ 308,4 309,11 @@ namespace gui
        }
        return false;
    }
    void BellHomeScreenWindow::switchToBatteryStatus()
    {
        application->switchWindow(gui::BellBatteryStatusWindow::name,
                                  std::make_unique<gui::BellBatteryStatusWindow::Data>(presenter->getBatteryLvl(),
                                                                                       presenter->isBatteryCharging()));
    }

} // namespace gui

M products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.hpp => products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.hpp +1 -0
@@ 62,6 62,7 @@ namespace gui
        void setTimeFormat(utils::time::Locale::TimeFormat fmt) override;
        void setAlarmTimeFormat(utils::time::Locale::TimeFormat fmt) override;
        void switchToMenu() override;
        void switchToBatteryStatus() override;

        BellBaseLayout *body{};