~aleteoryx/muditaos

048dd61892231440ecb940399a0b08bf856565a0 — Maciej Gibowicz 2 years ago 1e496a8
[BH-1636] Add low battery info when the alarm is activated

Below the 10% battery level threshold, we remind the user to recharge
the battery to be sure that the alarm will ring.
35 files changed, 431 insertions(+), 94 deletions(-)

M harmony_changelog.md
M image/system_a/data/lang/Deutsch.json
M image/system_a/data/lang/English.json
M image/system_a/data/lang/Espanol.json
M image/system_a/data/lang/Francais.json
M image/system_a/data/lang/Polski.json
M products/BellHybrid/apps/Application.cpp
M products/BellHybrid/apps/application-bell-alarm/ApplicationBellAlarm.cpp
M products/BellHybrid/apps/application-bell-alarm/presenter/BellAlarmSetPresenter.cpp
M products/BellHybrid/apps/application-bell-alarm/presenter/BellAlarmSetPresenter.hpp
M products/BellHybrid/apps/application-bell-alarm/windows/BellAlarmSetWindow.cpp
M products/BellHybrid/apps/application-bell-alarm/windows/BellAlarmSetWindow.hpp
M products/BellHybrid/apps/application-bell-main/include/application-bell-main/presenters/HomeScreenPresenter.hpp
M products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.cpp
M products/BellHybrid/apps/application-bell-main/presenters/StateController.cpp
M products/BellHybrid/apps/application-bell-meditation-timer/presenter/MeditationTimerPresenter.cpp
M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningLoopPresenter.cpp
M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningLoopPresenter.hpp
M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationTimerSelectPresenter.cpp
M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationTimerSelectPresenter.hpp
M products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationRunningLoopWindow.cpp
M products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationTimerSelectWindow.cpp
M products/BellHybrid/apps/common/include/common/data/StyleCommon.hpp
M products/BellHybrid/apps/common/include/common/layouts/BaseHomeScreenLayoutProvider.hpp
M products/BellHybrid/apps/common/include/common/layouts/HomeScreenLayoutClassic.hpp
M products/BellHybrid/apps/common/include/common/models/BatteryModel.hpp
M products/BellHybrid/apps/common/include/common/popups/AlarmActivatedWindow.hpp
M products/BellHybrid/apps/common/include/common/popups/presenter/AlarmActivatedPresenter.hpp
M products/BellHybrid/apps/common/src/BatteryModel.cpp
M products/BellHybrid/apps/common/src/layouts/HomeScreenLayoutClassic.cpp
M products/BellHybrid/apps/common/src/layouts/HomeScreenLayoutVertical.cpp
M products/BellHybrid/apps/common/src/popups/AlarmActivatedWindow.cpp
M products/BellHybrid/apps/common/src/popups/presenter/AlarmActivatedPresenter.cpp
M products/BellHybrid/apps/common/src/widgets/LayoutVertical.cpp
M products/BellHybrid/apps/include/Application.hpp
M harmony_changelog.md => harmony_changelog.md +1 -0
@@ 13,6 13,7 @@
* Added entering WFI when CPU is idle to reduce power consumption
* Added switching SDRAM to self-refresh before entering WFI for further power consumption reduction
* Added watchdog protection to WFI mode
* Added low battery warning when the alarm is activated

### Changed / Improved


M image/system_a/data/lang/Deutsch.json => image/system_a/data/lang/Deutsch.json +1 -0
@@ 116,6 116,7 @@
    "app_bell_turn_off_question": "Schalten Sie das Ger\u00e4t aus?",
    "app_bell_welcome_charge_message": "<text>Charge Harmony<br/>und leicht dr\u00fccken</text>",
    "app_bell_welcome_message": "<text>Mudita Harmony<br/>ist ausgeschaltet</text>",
    "app_bell_alarm_lowBattery_info": "Lade deinen Harmony, damit der Wecker auch sicher klingelt",
    "app_bellmain_alarm": "Alarm",
    "app_bellmain_bedtime": "Schlafenzeit",
    "app_bellmain_home_screen_bottom_desc": "Der n\u00e4chste Alarm klingelt",

M image/system_a/data/lang/English.json => image/system_a/data/lang/English.json +1 -0
@@ 150,6 150,7 @@
    "app_bell_turn_off_question": "Turn off Mudita Harmony?",
    "app_bell_welcome_charge_message": "<text>Charge Harmony<br/>and press light click</text>",
    "app_bell_welcome_message": "<text>Mudita Harmony<br/>is switched OFF</text>",
    "app_bell_alarm_lowBattery_info": "Charge your Harmony to make sure your alarm will ring",
    "app_bellmain_alarm": "Alarm",
    "app_bellmain_bedtime": "Bedtime",
    "app_bellmain_home_screen_bottom_desc": "Next alarm will ring",

M image/system_a/data/lang/Espanol.json => image/system_a/data/lang/Espanol.json +1 -0
@@ 115,6 115,7 @@
    "app_bell_turn_off_question": "\u00bfApagar Mudita Harmony?",
    "app_bell_welcome_charge_message": "<text>Carga tu Harmony<br/>y pulsa levemente</text>",
    "app_bell_welcome_message": "<text>Mudita Harmony<br/>est\u00e1 apagado</text>",
    "app_bell_alarm_lowBattery_info": "Carga tu Harmony para asegurarte de que suene la alarma",
    "app_bellmain_alarm": "Alarma",
    "app_bellmain_bedtime": "Hora de dormir",
    "app_bellmain_home_screen_bottom_desc": "La siguiente alarma sonar\u00e1",

M image/system_a/data/lang/Francais.json => image/system_a/data/lang/Francais.json +1 -0
@@ 117,6 117,7 @@
    "app_bell_turn_off_question": "\u00c9teindre l'appareil ?",
    "app_bell_welcome_charge_message": "<text>Rechargez Harmony<br/>et appuyez l\u00e9g\u00e8rement</text>",
    "app_bell_welcome_message": "<text>Mudita Harmony<br/>est d\u00e9sactiv\u00e9</text>",
    "app_bell_alarm_lowBattery_info": "Chargez votre Harmony pour vous assurer que l'alarme sonne",
    "app_bellmain_alarm": "Alarme",
    "app_bellmain_bedtime": "Heure du coucher",
    "app_bellmain_home_screen_bottom_desc": "La prochaine alarme sonnera",

M image/system_a/data/lang/Polski.json => image/system_a/data/lang/Polski.json +1 -0
@@ 116,6 116,7 @@
    "app_bell_turn_off_question": "Wy\u0142\u0105czy\u0107 Mudita Harmony?",
    "app_bell_welcome_charge_message": "<text>Na\u0142aduj Harmony<br/>i kliknij lekko</text>",
    "app_bell_welcome_message": "<text>Mudita Harmony<br/>jest wy\u0142\u0105czony</text>",
    "app_bell_alarm_lowBattery_info": "Naładuj Harmony aby mieć pewność, że alarm zadzwoni",
    "app_bellmain_alarm": "Alarm",
    "app_bellmain_bedtime": "Pora snu",
    "app_bellmain_home_screen_bottom_desc": "Alarm zadzwoni",

M products/BellHybrid/apps/Application.cpp => products/BellHybrid/apps/Application.cpp +6 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <Application.hpp>


@@ 48,6 48,7 @@ namespace app

        alarmModel = std::make_unique<app::AlarmModel>(this);
        alarmModel->update(nullptr);
        batteryModel = std::make_unique<app::BatteryModel>(this);
        return sys::ReturnCodes::Success;
    }



@@ 60,14 61,16 @@ namespace app
            case ID::AlarmActivated:
                windowsFactory.attach(
                    window::alarm_activated_window, [this](app::ApplicationCommon *app, const std::string &name) {
                        auto presenter = std::make_unique<app::popup::AlarmActivatedPresenter>(*alarmModel);
                        auto presenter =
                            std::make_unique<app::popup::AlarmActivatedPresenter>(*alarmModel, *batteryModel);
                        return std::make_unique<gui::AlarmActivatedWindow>(app, std::move(presenter));
                    });
                break;
            case ID::AlarmDeactivated:
                windowsFactory.attach(
                    window::alarm_deactivated_window, [this](app::ApplicationCommon *app, const std::string &name) {
                        auto presenter = std::make_unique<app::popup::AlarmActivatedPresenter>(*alarmModel);
                        auto presenter =
                            std::make_unique<app::popup::AlarmActivatedPresenter>(*alarmModel, *batteryModel);
                        return std::make_unique<gui::AlarmDeactivatedWindow>(app, std::move(presenter));
                    });
                break;

M products/BellHybrid/apps/application-bell-alarm/ApplicationBellAlarm.cpp => products/BellHybrid/apps/application-bell-alarm/ApplicationBellAlarm.cpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ApplicationBellAlarm.hpp"


@@ 53,7 53,7 @@ namespace app
        });

        windowsFactory.attach(gui::window::name::bellAlarmSet, [&](ApplicationCommon *app, const std::string &) {
            auto alarmPresenter = std::make_unique<bell_alarm::BellAlarmSetPresenter>(app, *alarmModel);
            auto alarmPresenter = std::make_unique<bell_alarm::BellAlarmSetPresenter>(app, *alarmModel, *batteryModel);
            return std::make_unique<gui::BellAlarmSetWindow>(app, std::move(alarmPresenter));
        });


M products/BellHybrid/apps/application-bell-alarm/presenter/BellAlarmSetPresenter.cpp => products/BellHybrid/apps/application-bell-alarm/presenter/BellAlarmSetPresenter.cpp +29 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "BellAlarmSetPresenter.hpp"


@@ 7,8 7,10 @@

namespace app::bell_alarm
{
    BellAlarmSetPresenter::BellAlarmSetPresenter(app::ApplicationCommon *app, AbstractAlarmModel &alarmModel)
        : app{app}, alarmModel{alarmModel}
    BellAlarmSetPresenter::BellAlarmSetPresenter(app::ApplicationCommon *app,
                                                 AbstractAlarmModel &alarmModel,
                                                 AbstractBatteryModel &batteryModel)
        : app{app}, alarmModel{alarmModel}, batteryModel{batteryModel}
    {}

    bool BellAlarmSetPresenter::isAlarmActive() const noexcept


@@ 26,4 28,28 @@ namespace app::bell_alarm
        app::manager::Controller::sendAction(
            app, app::manager::actions::Launch, std::make_unique<app::ApplicationLaunchData>(app::applicationBellName));
    }

    Layout BellAlarmSetPresenter::getLayout()
    {
        if (not isAlarmActive()) {
            layout = Layout::AlarmInactive;
        }
        else if (layout == Layout::Undefined) {
            const auto batteryStatus = batteryModel.getLevelState();
            const units::SOC soc     = batteryStatus.level;
            const auto state         = batteryStatus.state;
            if (not batteryModel.isBatteryCharging(state) && soc < constants::lowBatteryInfoThreshold) {
                layout = Layout::LowBatteryInfo;
            }
            else {
                layout = Layout::AlarmInfo;
            }
        }
        return layout;
    }

    void BellAlarmSetPresenter::lowBatteryInfoHandled()
    {
        layout = Layout::AlarmInfo;
    }
} // namespace app::bell_alarm

M products/BellHybrid/apps/application-bell-alarm/presenter/BellAlarmSetPresenter.hpp => products/BellHybrid/apps/application-bell-alarm/presenter/BellAlarmSetPresenter.hpp +19 -2
@@ 1,10 1,11 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <apps-common/BasePresenter.hpp>
#include <common/models/AbstractAlarmModel.hpp>
#include <common/models/BatteryModel.hpp>
#include <memory>

namespace app


@@ 14,6 15,14 @@ namespace app

namespace app::bell_alarm
{
    enum class Layout
    {
        Undefined,
        LowBatteryInfo,
        AlarmInfo,
        AlarmInactive
    };

    class BellAlarmSetContract
    {
      public:


@@ 28,21 37,29 @@ namespace app::bell_alarm
            virtual time_t getAlarmTime() const noexcept = 0;
            virtual bool isAlarmActive() const noexcept  = 0;
            virtual void activate()                      = 0;
            virtual Layout getLayout()                   = 0;
            virtual void lowBatteryInfoHandled()         = 0;
        };
    };

    class BellAlarmSetPresenter : public BellAlarmSetContract::Presenter
    {
      public:
        explicit BellAlarmSetPresenter(app::ApplicationCommon *app, AbstractAlarmModel &alarmModel);
        BellAlarmSetPresenter(app::ApplicationCommon *app,
                              AbstractAlarmModel &alarmModel,
                              AbstractBatteryModel &batteryModel);

        time_t getAlarmTime() const noexcept;
        bool isAlarmActive() const noexcept;
        void activate() override;
        Layout getLayout();
        void lowBatteryInfoHandled();

      private:
        app::ApplicationCommon *app{};

        AbstractAlarmModel &alarmModel;
        AbstractBatteryModel &batteryModel;
        Layout layout{Layout::Undefined};
    };
} // namespace app::bell_alarm

M products/BellHybrid/apps/application-bell-alarm/windows/BellAlarmSetWindow.cpp => products/BellHybrid/apps/application-bell-alarm/windows/BellAlarmSetWindow.cpp +78 -17
@@ 1,8 1,9 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "BellAlarmSetWindow.hpp"
#include "data/BellAlarmStyle.hpp"
#include <common/data/StyleCommon.hpp>
#include <application-bell-alarm/ApplicationBellAlarm.hpp>

#include <gui/input/InputEvent.hpp>


@@ 10,6 11,11 @@

#include <common/TimeUtils.hpp>

namespace
{
    constexpr std::chrono::seconds screenTimeout{5};
} // namespace

namespace gui
{
    BellAlarmSetWindow::BellAlarmSetWindow(app::ApplicationCommon *app,


@@ 24,7 30,7 @@ namespace gui
    {
        AppWindow::buildInterface();
        buildLayout();
        registerCallbacks();
        resetTimer(screenTimeout);
    }

    void BellAlarmSetWindow::buildLayout()


@@ 33,41 39,96 @@ namespace gui
        header->setTitleVisibility(false);
        navBar->setVisible(false);

        icon = new Icon(this, 0, 0, style::window_width, style::window_height, {}, {});
        icon->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Top));
        switch (presenter->getLayout()) {
        case app::bell_alarm::Layout::AlarmInactive:
            buildAlarmInactiveLayout();
            break;
        case app::bell_alarm::Layout::LowBatteryInfo:
            buildLowBatteryLayout();
            break;
        case app::bell_alarm::Layout::AlarmInfo:
        case app::bell_alarm::Layout::Undefined:
            buildAlarmInfoLayout();
            break;
        }
    }

    void BellAlarmSetWindow::buildLowBatteryLayout()
    {
        icon = new Icon(this,
                        0,
                        0,
                        style::window_width,
                        style::window_height,
                        "bell_status_battery_lvl0",
                        {},
                        gui::ImageTypeSpecifier::W_G);
        icon->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Top));
        icon->image->setMargins({0, gui::bell_style::warning_icon_top_margin, 0, 0});
        icon->text->setMaximumWidth(gui::bell_style::warning_text_width);
        icon->text->setFont(style::window::font::verybiglight);
        icon->text->setRichText(utils::translate("app_bell_alarm_lowBattery_info"));

        timerCallback = [this](Item &, sys::Timer &timer) {
            lowBatteryInfoHandled();
            return true;
        };
    }

    void BellAlarmSetWindow::buildAlarmInfoLayout()
    {
        icon = new Icon(this, 0, 0, style::window_width, style::window_height, "big_alarm_W_G", {});
        icon->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Top));
        icon->image->setMargins(
            {0, bell_alarm_style::icon::imageTopMargin, 0, bell_alarm_style::icon::imageBottomMargin});
        icon->text->setFont(style::window::font::verybiglight);
        icon->text->setRichText(utils::time::getBottomDescription(
            utils::time::calculateMinutesDifference(presenter->getAlarmTime(), utils::time::getCurrentTime())));

        timerCallback = [this](Item &, sys::Timer &timer) {
            presenter->activate();
            return true;
        };
    }

    void BellAlarmSetWindow::registerCallbacks()
    void BellAlarmSetWindow::buildAlarmInactiveLayout()
    {
        icon = new Icon(this, 0, 0, style::window_width, style::window_height, "big_no-alarm_W_G", {});
        icon->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Top));
        icon->image->setMargins(
            {0, bell_alarm_style::icon::imageTopMargin, 0, bell_alarm_style::icon::imageBottomMargin});
        icon->text->setFont(style::window::font::verybiglight);
        icon->text->setRichText(utils::translate("app_bell_alarm_set_not_active"));

        timerCallback = [this](Item &, sys::Timer &timer) {
            presenter->activate();
            return true;
        };
    }

    void BellAlarmSetWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    void BellAlarmSetWindow::rebuild()
    {
        WindowWithTimer::onBeforeShow(mode, data);
        erase();
        buildInterface();
    }

        if (presenter->isAlarmActive()) {
            icon->image->set("big_alarm_W_G");
            icon->text->setRichText(utils::time::getBottomDescription(
                utils::time::calculateMinutesDifference(presenter->getAlarmTime(), utils::time::getCurrentTime())));
        }
        else {
            icon->image->set("big_no-alarm_W_G");
            icon->text->setRichText(utils::translate("app_bell_alarm_set_not_active"));
        }
    void BellAlarmSetWindow::lowBatteryInfoHandled()
    {
        presenter->lowBatteryInfoHandled();
        rebuild();
        application->refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
    }

    bool BellAlarmSetWindow::onInput(const InputEvent &inputEvent)
    {
        if (inputEvent.isShortRelease(KeyCode::KEY_ENTER) || inputEvent.isShortRelease(KeyCode::KEY_RF)) {
            detachTimerIfExists();
            presenter->activate();
            if (presenter->getLayout() == app::bell_alarm::Layout::LowBatteryInfo) {
                lowBatteryInfoHandled();
            }
            else {
                presenter->activate();
            }
            return true;
        }
        return false;

M products/BellHybrid/apps/application-bell-alarm/windows/BellAlarmSetWindow.hpp => products/BellHybrid/apps/application-bell-alarm/windows/BellAlarmSetWindow.hpp +6 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 15,13 15,16 @@ namespace gui
      public:
        explicit BellAlarmSetWindow(app::ApplicationCommon *app,
                                    std::shared_ptr<app::bell_alarm::BellAlarmSetContract::Presenter> presenter);
        void rebuild() override;

      protected:
        void buildInterface() override;
        void buildLayout();
        void registerCallbacks();
        bool onInput(const InputEvent &inputEvent) override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        void buildLowBatteryLayout();
        void buildAlarmInfoLayout();
        void buildAlarmInactiveLayout();
        void lowBatteryInfoHandled();

      private:
        constexpr static auto alarmSummaryDisplayDuration = std::chrono::seconds{5};

M products/BellHybrid/apps/application-bell-main/include/application-bell-main/presenters/HomeScreenPresenter.hpp => products/BellHybrid/apps/application-bell-main/include/application-bell-main/presenters/HomeScreenPresenter.hpp +2 -0
@@ 108,6 108,7 @@ namespace app::home_screen
        virtual bool isLowBatteryWarningNeeded()                                                 = 0;
        virtual void updateBatteryLevelInterval()                                                = 0;
        virtual void refreshUserSession()                                                        = 0;
        virtual bool isLowBatteryLevel() const                                                   = 0;

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


@@ 161,6 162,7 @@ namespace app::home_screen
        bool isLowBatteryWarningNeeded() override;
        void updateBatteryLevelInterval() override;
        void refreshUserSession() override;
        bool isLowBatteryLevel() const override;

        void setLayout(gui::LayoutGenerator layoutGenerator) override;


M products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.cpp => products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.cpp +7 -0
@@ 308,4 308,11 @@ namespace app::home_screen
        batteryLevelNotificationModel.updateBatteryLevelInterval(getBatteryLvl());
    }

    bool HomeScreenPresenter::isLowBatteryLevel() const
    {
        const auto batteryStatus = batteryModel.getLevelState();
        return not batteryModel.isBatteryCharging(batteryStatus.state) &&
               (batteryStatus.level < constants::lowBatteryInfoThreshold);
    }

} // namespace app::home_screen

M products/BellHybrid/apps/application-bell-main/presenters/StateController.cpp => products/BellHybrid/apps/application-bell-main/presenters/StateController.cpp +41 -10
@@ 91,6 91,7 @@ namespace app::home_screen
                view.setBatteryLevelState(batteryModel.getLevelState());
            };
            auto switchToBatteryStatus = [](AbstractPresenter &presenter) { presenter.switchToBatteryStatus(); };
            auto isLowBatteryLevel     = [](AbstractPresenter &presenter) { return presenter.isLowBatteryLevel(); };

        } // namespace Helpers



@@ 122,7 123,9 @@ namespace app::home_screen
            {};
            struct BatteryUpdate
            {};
            struct LowBatteryWarning
            struct LowBatterySessionWarning
            {};
            struct LowBatteryAlarmWarning
            {};
        } // namespace Events



@@ 217,6 220,16 @@ namespace app::home_screen
            auto exit = [](AbstractView &view, AbstractPresenter &presenter) { presenter.detachTimer(); };
        } // namespace ActivatedWait

        namespace ActivatedLowBattery
        {
            auto entry = [](AbstractView &view, AbstractPresenter &presenter, AbstractTimeModel &timeModel) {
                presenter.spawnTimer();
                view.setTextDescription(utils::translate("app_bell_alarm_lowBattery_info"));
                view.setViewState(ViewState::ActivatedLowBattery);
            };
            auto exit = [](AbstractView &view, AbstractPresenter &presenter) { presenter.detachTimer(); };
        } // namespace ActivatedLowBattery

        namespace Activated
        {
            auto entry = [](AbstractController &controller,


@@ 303,18 316,20 @@ namespace app::home_screen
                                             "Deactivated"_s + event<Events::LightPress> [not Helpers::isLowBatteryWarningToShow] / Helpers::switchToMenu,
                                             "Deactivated"_s + event<Events::RotateLeftPress> [not Helpers::isLowBatteryWarningToShow] = "DeactivatedEdit"_s,
                                             "Deactivated"_s + event<Events::RotateRightPress> [not Helpers::isLowBatteryWarningToShow] = "DeactivatedEdit"_s,
                                             "Deactivated"_s + event<Events::DeepUpPress> = "ActivatedWait"_s,
                                             "Deactivated"_s + event<Events::DeepUpPress> [not Helpers::isLowBatteryLevel] = "ActivatedWait"_s,
                                             "Deactivated"_s + event<Events::LowBatteryAlarmWarning> = "ActivatedLowBattery"_s,
                                             "Deactivated"_s + event<Events::TimeUpdate> / Helpers::updateTemperature,
                                             "Deactivated"_s + event<Events::BackPress> / Helpers::endUserSessionWithDelay,
                                             "Deactivated"_s + event<Events::LongBackPress> [not Helpers::isLowBatteryWarningToShow]  / Helpers::switchToBatteryStatus,
                                             "Deactivated"_s + event<Events::BatteryUpdate>  / Helpers::updateBatteryStatus,
                                             "Deactivated"_s + event<Events::LowBatteryWarning> [Helpers::isLowBatteryWarningToShow] / Helpers::showLowBatteryWarning,
                                             "Deactivated"_s + event<Events::LowBatterySessionWarning> [Helpers::isLowBatteryWarningToShow] / Helpers::showLowBatteryWarning,

                                             "DeactivatedWait"_s + sml::on_entry<_> / DeactivatedWait::entry,
                                             "DeactivatedWait"_s + sml::on_exit<_> / DeactivatedWait::exit,
                                             "DeactivatedWait"_s + event<Events::Timer> = "Deactivated"_s,
                                             "DeactivatedWait"_s + event<Events::LightPress> = "Deactivated"_s,
                                             "DeactivatedWait"_s + event<Events::DeepUpPress> = "ActivatedWait"_s,
                                             "DeactivatedWait"_s + event<Events::DeepUpPress> [not Helpers::isLowBatteryLevel] = "ActivatedWait"_s,
                                             "DeactivatedWait"_s + event<Events::LowBatteryAlarmWarning> = "ActivatedLowBattery"_s,
                                             "DeactivatedWait"_s + event<Events::RotateLeftPress> = "DeactivatedEdit"_s,
                                             "DeactivatedWait"_s + event<Events::RotateRightPress> = "DeactivatedEdit"_s,
                                             "DeactivatedWait"_s + event<Events::BackPress> = "Deactivated"_s,


@@ 325,7 340,8 @@ namespace app::home_screen
                                             "DeactivatedEdit"_s + event<Events::Timer> / AlarmEdit::revertChanges = "Deactivated"_s,
                                             "DeactivatedEdit"_s + event<Events::RotateLeftPress> / AlarmEdit::processRotateLeft,
                                             "DeactivatedEdit"_s + event<Events::RotateRightPress> / AlarmEdit::processRotateRight,
                                             "DeactivatedEdit"_s + event<Events::DeepUpPress> / Helpers::setNewAlarmTime = "ActivatedWait"_s,
                                             "DeactivatedEdit"_s + event<Events::DeepUpPress> [not Helpers::isLowBatteryLevel] / Helpers::setNewAlarmTime = "ActivatedWait"_s,
                                             "DeactivatedEdit"_s + event<Events::LowBatteryAlarmWarning> / Helpers::setNewAlarmTime = "ActivatedLowBattery"_s,
                                             "DeactivatedEdit"_s + event<Events::LightPress> / Helpers::setNewAlarmTime = "WaitForConfirmation"_s,
                                             "DeactivatedEdit"_s + event<Events::BackPress> / AlarmEdit::revertChanges = "Deactivated"_s,
                                             "DeactivatedEdit"_s + event<Events::BatteryUpdate>  / Helpers::updateBatteryStatus,


@@ 333,12 349,23 @@ namespace app::home_screen
                                             "WaitForConfirmation"_s + sml::on_entry<_> / WaitForConfirmation::entry,
                                             "WaitForConfirmation"_s + sml::on_exit<_> / WaitForConfirmation::exit,
                                             "WaitForConfirmation"_s + event<Events::Timer> = "Deactivated"_s,
                                             "WaitForConfirmation"_s + event<Events::DeepUpPress> / WaitForConfirmation::action = "ActivatedWait"_s,
                                             "WaitForConfirmation"_s + event<Events::DeepUpPress> [not Helpers::isLowBatteryLevel] / WaitForConfirmation::action = "ActivatedWait"_s,
                                             "WaitForConfirmation"_s + event<Events::LowBatteryAlarmWarning> / WaitForConfirmation::action = "ActivatedLowBattery"_s,
                                             "WaitForConfirmation"_s + event<Events::LightPress>/ Helpers::switchToMenu = "Deactivated"_s,
                                             "WaitForConfirmation"_s + event<Events::BackPress> = "Deactivated"_s,
                                             "WaitForConfirmation"_s + event<Events::RotateLeftPress> = "DeactivatedEdit"_s,
                                             "WaitForConfirmation"_s + event<Events::RotateRightPress> = "DeactivatedEdit"_s,

                                             "ActivatedLowBattery"_s + sml::on_entry<_> / ActivatedLowBattery::entry,
                                             "ActivatedLowBattery"_s + sml::on_exit<_> / ActivatedLowBattery::exit,
                                             "ActivatedLowBattery"_s + event<Events::Timer> = "ActivatedWait"_s,
                                             "ActivatedLowBattery"_s + event<Events::LightPress> = "ActivatedWait"_s,
                                             "ActivatedLowBattery"_s + event<Events::DeepDownPress> = "DeactivatedWait"_s,
                                             "ActivatedLowBattery"_s + event<Events::BackPress> = "ActivatedWait"_s,
                                             "ActivatedLowBattery"_s + event<Events::RotateLeftPress> = "ActivatedEdit"_s,
                                             "ActivatedLowBattery"_s + event<Events::RotateRightPress> = "ActivatedEdit"_s,
                                             "ActivatedLowBattery"_s + event<Events::AlarmRinging>  = "AlarmRinging"_s,

                                             "ActivatedWait"_s + sml::on_entry<_> / ActivatedWait::entry,
                                             "ActivatedWait"_s + sml::on_exit<_> / ActivatedWait::exit,
                                             "ActivatedWait"_s + event<Events::Timer> = "Activated"_s,


@@ 360,7 387,7 @@ namespace app::home_screen
                                             "Activated"_s + event<Events::BackPress>  / Helpers::endUserSessionWithDelay,
                                             "Activated"_s + event<Events::LongBackPress> [not Helpers::isLowBatteryWarningToShow]  / Helpers::switchToBatteryStatus,
                                             "Activated"_s + event<Events::BatteryUpdate>  / Helpers::updateBatteryStatus,
                                             "Activated"_s + event<Events::LowBatteryWarning> [Helpers::isLowBatteryWarningToShow] / Helpers::showLowBatteryWarning,
                                             "Activated"_s + event<Events::LowBatterySessionWarning> [Helpers::isLowBatteryWarningToShow] / Helpers::showLowBatteryWarning,

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


@@ 391,7 418,8 @@ namespace app::home_screen
                                             "AlarmRingingDeactivatedWait"_s + sml::on_entry<_> / AlarmRingingDeactivatedWait::entry,
                                             "AlarmRingingDeactivatedWait"_s + sml::on_exit<_> / AlarmRingingDeactivatedWait::exit,
                                             "AlarmRingingDeactivatedWait"_s + event<Events::Timer> = "Deactivated"_s,
                                             "AlarmRingingDeactivatedWait"_s + event<Events::DeepUpPress> = "ActivatedWait"_s,
                                             "AlarmRingingDeactivatedWait"_s + event<Events::DeepUpPress> [not Helpers::isLowBatteryLevel] = "ActivatedWait"_s,
                                             "AlarmRingingDeactivatedWait"_s + event<Events::LowBatteryAlarmWarning> = "ActivatedLowBattery"_s,
                                             "AlarmRingingDeactivatedWait"_s + event<Events::BackPress> = "Deactivated"_s,
                                             "AlarmRingingDeactivatedWait"_s + event<Events::LightPress>/ Helpers::switchToMenu = "Deactivated"_s,
                                             "AlarmRingingDeactivatedWait"_s + event<Events::RotateLeftPress> = "DeactivatedEdit"_s,


@@ 413,7 441,7 @@ namespace app::home_screen
                                             "AlarmSnoozed"_s + event<Events::TimeUpdate> / Helpers::updateTemperature,
                                             "AlarmSnoozed"_s + event<Events::BatteryUpdate> / Helpers::updateBatteryStatus,
                                             "AlarmSnoozed"_s + event<Events::LongBackPress> [not Helpers::isLowBatteryWarningToShow]  / Helpers::switchToBatteryStatus,
                                             "AlarmSnoozed"_s + event<Events::LowBatteryWarning> [Helpers::isLowBatteryWarningToShow] / Helpers::showLowBatteryWarning
                                             "AlarmSnoozed"_s + event<Events::LowBatterySessionWarning> [Helpers::isLowBatteryWarningToShow] / Helpers::showLowBatteryWarning
                    );
                // clang-format on
            }


@@ 523,6 551,9 @@ namespace app::home_screen
            break;
        case KeyMap::DeepPressUp:
            pimpl->sm->process_event(Events::DeepUpPress{});
            if (presenter.isLowBatteryLevel()) {
                pimpl->sm->process_event(Events::LowBatteryAlarmWarning{});
            }
            break;
        case KeyMap::DeepPressDown:
            pimpl->sm->process_event(Events::DeepDownPress{});


@@ 532,7 563,7 @@ namespace app::home_screen
        }

        if (key != KeyMap::DeepPressUp && key != KeyMap::DeepPressDown && presenter.isLowBatteryWarningNeeded()) {
            pimpl->sm->process_event(Events::LowBatteryWarning{});
            pimpl->sm->process_event(Events::LowBatterySessionWarning{});
        }

        return true;

M products/BellHybrid/apps/application-bell-meditation-timer/presenter/MeditationTimerPresenter.cpp => products/BellHybrid/apps/application-bell-meditation-timer/presenter/MeditationTimerPresenter.cpp +2 -9
@@ 11,18 11,11 @@

namespace
{
    using State                 = Store::Battery::State;
    constexpr auto spinnerMax   = 180U;
    constexpr auto spinnerMin   = 1U;
    constexpr auto spinnerStep  = 1U;
    constexpr auto emptyValue   = 0U;
    constexpr auto defaultValue = 15U;
    constexpr units::SOC lowBatteryThreshold{10};

    bool isBatteryCharging(const State state)
    {
        return state == State::Charging or state == State::ChargingDone;
    }
} // namespace

namespace app::meditation


@@ 72,8 65,8 @@ namespace app::meditation

        const auto batteryState = batteryModel.getLevelState();
        const units::SOC soc    = batteryState.level;
        const bool isCharging   = isBatteryCharging(batteryState.state);
        if (not lowBatteryInfoModel.isInfoHandled() && not isCharging && soc < lowBatteryThreshold) {
        const bool isCharging   = batteryModel.isBatteryCharging(batteryState.state);
        if (not lowBatteryInfoModel.isInfoHandled() && not isCharging && soc < constants::lowBatteryInfoThreshold) {
            auto lowBatterySwitchData =
                std::make_unique<gui::AppsBatteryStatusSwitchData>(soc, isCharging, switchToNextScreen);
            app->switchWindow(windows::meditationLowBattery, std::move(lowBatterySwitchData));

M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningLoopPresenter.cpp => products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningLoopPresenter.cpp +5 -0
@@ 124,4 124,9 @@ namespace app::relaxation
    {
        return batteryModel.getLevelState();
    }

    bool RelaxationRunningLoopPresenter::isBatteryCharging(Store::Battery::State state) const
    {
        return batteryModel.isBatteryCharging(state);
    }
} // namespace app::relaxation

M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningLoopPresenter.hpp => products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningLoopPresenter.hpp +2 -0
@@ 57,6 57,7 @@ namespace app::relaxation
            virtual bool isPaused()                                                        = 0;
            virtual void onBeforeShow()                                                    = 0;
            virtual Store::Battery getBatteryState()                                       = 0;
            virtual bool isBatteryCharging(Store::Battery::State state) const              = 0;
        };
    };



@@ 80,6 81,7 @@ namespace app::relaxation
        bool isPaused() override;
        void onBeforeShow() override;
        Store::Battery getBatteryState() override;
        bool isBatteryCharging(Store::Battery::State state) const override;

        void onFinished();


M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationTimerSelectPresenter.cpp => products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationTimerSelectPresenter.cpp +10 -0
@@ 47,6 47,16 @@ namespace app::relaxation
        return batteryModel.getLevelState();
    }

    bool RelaxationTimerSelectPresenter::isBatteryCharging(Store::Battery::State state) const
    {
        return batteryModel.isBatteryCharging(state);
    }

    bool RelaxationTimerSelectPresenter::isBatteryBelowLowLevelThreshold(units::SOC soc) const
    {
        return soc < constants::lowBatteryInfoThreshold;
    }

    bool RelaxationTimerSelectPresenter::isLowBatteryWindowHandled() const
    {
        return lowBatteryInfoModel.isInfoHandled();

M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationTimerSelectPresenter.hpp => products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationTimerSelectPresenter.hpp +4 -0
@@ 43,6 43,8 @@ namespace app::relaxation
            virtual std::chrono::minutes getCurrentTimerValue() const = 0;
            virtual void setTimerValue(std::chrono::minutes)          = 0;
            virtual Store::Battery getBatteryState()                     = 0;
            virtual bool isBatteryCharging(Store::Battery::State state) const  = 0;
            virtual bool isBatteryBelowLowLevelThreshold(units::SOC soc) const = 0;
            [[nodiscard]] virtual bool isLowBatteryWindowHandled() const = 0;
            virtual void handleLowBatteryWindow()                        = 0;
        };


@@ 58,6 60,8 @@ namespace app::relaxation
        std::chrono::minutes getCurrentTimerValue() const override;
        void setTimerValue(std::chrono::minutes) override;
        Store::Battery getBatteryState() override;
        bool isBatteryCharging(Store::Battery::State state) const override;
        bool isBatteryBelowLowLevelThreshold(units::SOC soc) const override;
        [[nodiscard]] bool isLowBatteryWindowHandled() const override;
        void handleLowBatteryWindow() override;


M products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationRunningLoopWindow.cpp => products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationRunningLoopWindow.cpp +1 -7
@@ 13,7 13,6 @@

namespace
{
    using State = Store::Battery::State;
    constexpr auto relaxationLoopTimerName{"RelaxationLoopTimer"};
    constexpr std::chrono::seconds relaxationLoopTimerPeriod{1};
    constexpr units::SOC dischargingLevelShowTop{20};


@@ 22,11 21,6 @@ namespace
    /* charsMultiplier is set a little bit less than max lines which is 2, because of final text formatting */
    constexpr auto charsMultiplier{1.8f};

    bool isBatteryCharging(const State state)
    {
        return state == State::Charging or state == State::ChargingDone;
    }

    gui::Text *createTitle(gui::VBox *parent)
    {
        namespace relaxationStyle = gui::relaxationStyle;


@@ 217,7 211,7 @@ namespace gui
        auto state     = batteryStatus.state;

        if (soc < dischargingLevelShowTop) {
            battery->update(soc, isBatteryCharging(state));
            battery->update(soc, presenter->isBatteryCharging(state));
            bottomText->setVisible(false);
            battery->setVisible(true);
            battery->informContentChanged();

M products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationTimerSelectWindow.cpp => products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationTimerSelectWindow.cpp +3 -9
@@ 14,12 14,10 @@

namespace
{
    using State   = Store::Battery::State;
    using minutes = std::chrono::minutes;
    using namespace std::chrono_literals;
    constexpr minutes onceValue{minutes::zero()};
    constexpr minutes loopValue{8760h};
    constexpr units::SOC lowBatteryThreshold{10};

    const std::string getOnceValueText()
    {


@@ 62,11 60,6 @@ namespace
        }
        return range;
    }

    bool isBatteryCharging(const State state)
    {
        return state == State::Charging or state == State::ChargingDone;
    }
} // namespace
namespace gui
{


@@ 202,8 195,9 @@ namespace gui

            const auto batteryState = presenter->getBatteryState();
            const units::SOC soc    = batteryState.level;
            const bool isCharging   = isBatteryCharging(batteryState.state);
            if (not presenter->isLowBatteryWindowHandled() && not isCharging && soc < lowBatteryThreshold) {
            const bool isCharging   = presenter->isBatteryCharging(batteryState.state);
            if (not presenter->isLowBatteryWindowHandled() && not isCharging &&
                presenter->isBatteryBelowLowLevelThreshold(soc)) {
                auto lowBatterySwitchData =
                    std::make_unique<AppsBatteryStatusSwitchData>(soc, isCharging, switchToNextScreen);
                lowBatterySwitchData->ignoreCurrentWindowOnStack = true;

M products/BellHybrid/apps/common/include/common/data/StyleCommon.hpp => products/BellHybrid/apps/common/include/common/data/StyleCommon.hpp +4 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 15,6 15,9 @@ namespace gui::bell_style
    inline constexpr auto popup_icon_top_margin    = 120;
    inline constexpr auto popup_icon_bottom_margin = 30;

    inline constexpr auto warning_icon_top_margin = 100;
    inline constexpr auto warning_text_width      = 400;

} // namespace gui::bell_style

namespace itStyle

M products/BellHybrid/apps/common/include/common/layouts/BaseHomeScreenLayoutProvider.hpp => products/BellHybrid/apps/common/include/common/layouts/BaseHomeScreenLayoutProvider.hpp +2 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 18,6 18,7 @@ namespace app::home_screen
        DeactivatedWait,
        WaitForConfirmation,
        AlarmEdit,
        ActivatedLowBattery,
        ActivatedWait,
        Activated,
        AlarmRinging,

M products/BellHybrid/apps/common/include/common/layouts/HomeScreenLayoutClassic.hpp => products/BellHybrid/apps/common/include/common/layouts/HomeScreenLayoutClassic.hpp +10 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 27,6 27,7 @@ namespace gui
    class BellBattery;
    class BellConnectionStatus;
    class DuoHBox;
    class Icon;

    enum class LayoutClassicVersion
    {


@@ 44,6 45,12 @@ namespace gui
        SnoozeIconAndTime,
    };

    enum class ScreenViewMode
    {
        Main,
        Warning
    };

    class HomeScreenLayoutClassic : public BaseHomeScreenLayoutProvider, protected BellBaseLayout
    {
      public:


@@ 66,6 73,7 @@ namespace gui

      protected:
        void setHeaderViewMode(HeaderViewMode mode);
        void setScreenMode(ScreenViewMode mode);
        virtual void buildInterface();
        virtual bool isBatteryVisibilityAllowed(const Store::Battery &batteryContext);
        void removeTextDescription();


@@ 85,5 93,6 @@ namespace gui
        TextFixedSize *bottomText              = nullptr;
        AlarmSetSpinner *alarm    = nullptr;
        SnoozeTimer *snoozeTimer  = nullptr;
        Icon *lowBatteryWarning                = nullptr;
    };
}; // namespace gui

M products/BellHybrid/apps/common/include/common/models/BatteryModel.hpp => products/BellHybrid/apps/common/include/common/models/BatteryModel.hpp +9 -1
@@ 1,10 1,11 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <module-utils/EventStore/EventStore.hpp>
#include <service-db/Settings.hpp>
#include <Units.hpp>

#include <cstdint>
#include <functional>


@@ 17,12 18,18 @@ namespace app

namespace app
{
    namespace constants
    {
        inline constexpr units::SOC lowBatteryInfoThreshold{10};
    }

    class AbstractBatteryModel
    {
      public:
        virtual ~AbstractBatteryModel() noexcept = default;

        virtual Store::Battery getLevelState() const = 0;
        virtual bool isBatteryCharging(Store::Battery::State state) const = 0;
    };

    class BatteryModel : public AbstractBatteryModel


@@ 30,6 37,7 @@ namespace app
      public:
        explicit BatteryModel(app::ApplicationCommon *app);
        Store::Battery getLevelState() const;
        bool isBatteryCharging(Store::Battery::State state) const;

      private:
        mutable settings::Settings settings;

M products/BellHybrid/apps/common/include/common/popups/AlarmActivatedWindow.hpp => products/BellHybrid/apps/common/include/common/popups/AlarmActivatedWindow.hpp +5 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 18,13 18,16 @@ namespace gui
      public:
        AlarmActivatedWindow(app::ApplicationCommon *app,
                             std::shared_ptr<app::popup::AlarmActivatedPresenter> presenter);
        void rebuild() override;

      private:
        bool onInput(const InputEvent &inputEvent) override;
        void buildInterface() override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        void returnToPreviousWindow();
        void setAlarmTime(time_t alarmTime);
        void buildLowBatteryLayout();
        void buildAlarmInfoLayout();
        void lowBatteryInfoHandled();

        Icon *icon{};
    };

M products/BellHybrid/apps/common/include/common/popups/presenter/AlarmActivatedPresenter.hpp => products/BellHybrid/apps/common/include/common/popups/presenter/AlarmActivatedPresenter.hpp +16 -2
@@ 1,15 1,23 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <apps-common/BasePresenter.hpp>
#include <common/models/AbstractAlarmModel.hpp>
#include <common/models/BatteryModel.hpp>

#include <memory>

namespace app::popup
{
    enum class Layout
    {
        Undefined,
        LowBatteryInfo,
        AlarmInfo
    };

    class AlarmActivatedContract
    {
      public:


@@ 38,18 46,24 @@ namespace app::popup
            virtual ~Presenter() noexcept                                  = default;
            virtual time_t getAlarmTime() const noexcept                   = 0;
            virtual bool isAlarmActive() const noexcept                    = 0;
            virtual Layout getLayout()                                     = 0;
            virtual void lowBatteryInfoHandled()                           = 0;
        };
    };

    class AlarmActivatedPresenter : public AlarmActivatedContract::Presenter
    {
      public:
        AlarmActivatedPresenter(AbstractAlarmModel &alarmModel);
        AlarmActivatedPresenter(AbstractAlarmModel &alarmModel, AbstractBatteryModel &batteryModel);

        time_t getAlarmTime() const noexcept;
        bool isAlarmActive() const noexcept;
        Layout getLayout();
        void lowBatteryInfoHandled();

      private:
        AbstractAlarmModel &alarmModel;
        AbstractBatteryModel &batteryModel;
        Layout layout{Layout::Undefined};
    };
} // namespace app::popup

M products/BellHybrid/apps/common/src/BatteryModel.cpp => products/BellHybrid/apps/common/src/BatteryModel.cpp +6 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "models/BatteryModel.hpp"


@@ 18,4 18,9 @@ namespace app
    {
        return Store::Battery::get();
    }

    bool BatteryModel::isBatteryCharging(Store::Battery::State state) const
    {
        return (state == Store::Battery::State::Charging) or (state == Store::Battery::State::ChargingDone);
    }
} // namespace app

M products/BellHybrid/apps/common/src/layouts/HomeScreenLayoutClassic.cpp => products/BellHybrid/apps/common/src/layouts/HomeScreenLayoutClassic.cpp +56 -1
@@ 1,9 1,10 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "layouts/HomeScreenLayoutClassic.hpp"
#include "layouts/HomeScreenLayoutNames.hpp"
#include "data/BellMainStyle.hpp"
#include "data/StyleCommon.hpp"
#include "widgets/BellBattery.hpp"
#include "widgets/BellConnectionStatus.hpp"
#include "widgets/DuoHBox.hpp"


@@ 15,6 16,7 @@
#include <time/time_constants.hpp>
#include <widgets/AlarmSetSpinner.hpp>
#include <widgets/TimeSetFmtSpinner.hpp>
#include <widgets/Icon.hpp>

namespace
{


@@ 111,6 113,22 @@ namespace gui
        bottomText->drawUnderline(false);
        bottomText->setVisible(false);

        lowBatteryWarning = new Icon(this,
                                     0,
                                     0,
                                     style::window_width,
                                     style::window_height,
                                     "bell_status_battery_lvl0",
                                     {},
                                     gui::ImageTypeSpecifier::W_G);
        lowBatteryWarning->setAlignment(
            gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Top));
        lowBatteryWarning->image->setMargins({0, gui::bell_style::warning_icon_top_margin, 0, 0});
        lowBatteryWarning->text->setMaximumWidth(gui::bell_style::warning_text_width);
        lowBatteryWarning->text->setFont(style::window::font::verybiglight);
        lowBatteryWarning->text->setRichText(utils::translate("app_bell_alarm_lowBattery_info"));
        lowBatteryWarning->setVisible(false);

        resizeItems();
    }



@@ 125,12 143,14 @@ namespace gui
        case app::home_screen::ViewState::Deactivated:
            alarm->setAlarmStatus(AlarmSetSpinner::Status::DEACTIVATED);
            setHeaderViewMode(HeaderViewMode::Empty);
            setScreenMode(ScreenViewMode::Main);
            alarm->setEditMode(EditMode::Browse);
            removeTextDescription();
            break;
        case app::home_screen::ViewState::DeactivatedWait:
            alarm->setAlarmStatus(AlarmSetSpinner::Status::DEACTIVATED);
            setHeaderViewMode(HeaderViewMode::AlarmIcon);
            setScreenMode(ScreenViewMode::Main);
            alarm->setEditMode(EditMode::Browse);
            break;
        case app::home_screen::ViewState::WaitForConfirmation:


@@ 139,36 159,49 @@ namespace gui
        case app::home_screen::ViewState::AlarmEdit:
            alarm->setEditMode(EditMode::Edit);
            setHeaderViewMode(HeaderViewMode::AlarmIconAndTime);
            setScreenMode(ScreenViewMode::Main);
            removeTextDescription();
            break;
        case app::home_screen::ViewState::ActivatedLowBattery:
            alarm->setAlarmStatus(AlarmSetSpinner::Status::DEACTIVATED);
            setHeaderViewMode(HeaderViewMode::Empty);
            setScreenMode(ScreenViewMode::Warning);
            alarm->setEditMode(EditMode::Browse);
            break;
        case app::home_screen::ViewState::ActivatedWait:
            alarm->setAlarmStatus(AlarmSetSpinner::Status::ACTIVATED);
            setHeaderViewMode(HeaderViewMode::AlarmIconAndTime);
            setScreenMode(ScreenViewMode::Main);
            alarm->setEditMode(EditMode::Browse);
            break;
        case app::home_screen::ViewState::Activated:
            alarm->setAlarmStatus(AlarmSetSpinner::Status::ACTIVATED);
            setHeaderViewMode(HeaderViewMode::AlarmIconAndTime);
            setScreenMode(ScreenViewMode::Main);
            alarm->setEditMode(EditMode::Browse);
            removeTextDescription();
            break;
        case app::home_screen::ViewState::AlarmRinging:
            alarm->setAlarmStatus(AlarmSetSpinner::Status::RINGING);
            setHeaderViewMode(HeaderViewMode::AlarmIcon);
            setScreenMode(ScreenViewMode::Main);
            alarm->setEditMode(EditMode::Browse);
            removeTextDescription();
            break;
        case app::home_screen::ViewState::AlarmRingingDeactivatedWait:
            alarm->setAlarmStatus(AlarmSetSpinner::Status::DEACTIVATED);
            setHeaderViewMode(HeaderViewMode::AlarmIcon);
            setScreenMode(ScreenViewMode::Main);
            alarm->setEditMode(EditMode::Browse);
            break;
        case app::home_screen::ViewState::AlarmSnoozedWait:
            setHeaderViewMode(HeaderViewMode::SnoozeIcon);
            setScreenMode(ScreenViewMode::Main);
            alarm->setEditMode(EditMode::Browse);
            break;
        case app::home_screen::ViewState::AlarmSnoozed:
            setHeaderViewMode(HeaderViewMode::SnoozeIconAndTime);
            setScreenMode(ScreenViewMode::Main);
            alarm->setEditMode(EditMode::Browse);
            removeTextDescription();
            break;


@@ 215,6 248,28 @@ namespace gui
        }
    }

    void HomeScreenLayoutClassic::setScreenMode(ScreenViewMode mode)
    {
        switch (mode) {
        case ScreenViewMode::Main:
            lowBatteryWarning->setVisible(false);
            firstBox->setVisible(true);
            firstBox->informContentChanged();
            lastBox->setVisible(true);
            lastBox->informContentChanged();
            centerBox->setVisible(true);
            centerBox->informContentChanged();
            break;
        case ScreenViewMode::Warning:
            firstBox->setVisible(false);
            lastBox->setVisible(false);
            centerBox->setVisible(false);
            lowBatteryWarning->setVisible(true);
            lowBatteryWarning->informContentChanged();
            break;
        }
    }

    void HomeScreenLayoutClassic::setTextDescription(const UTF8 &desc)
    {
        statusBox->setVisible(false);

M products/BellHybrid/apps/common/src/layouts/HomeScreenLayoutVertical.cpp => products/BellHybrid/apps/common/src/layouts/HomeScreenLayoutVertical.cpp +8 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "layouts/HomeScreenLayoutVertical.hpp"


@@ 47,6 47,7 @@ namespace gui
            setScreenMode(ScreenMode::AlarmActivatedDeactivated);
            alarmTopIcon->setStatus(AlarmIcon::Status::DEACTIVATED);
            alarmActivatedDeactivatedScreen->image->set("big_no-alarm_W_G", {});
            alarmActivatedDeactivatedScreen->image->setMaximumWidth(alarmActivatedDeactivatedScreen->image->getWidth());
            break;
        case app::home_screen::ViewState::WaitForConfirmation:
            setScreenMode(ScreenMode::AlarmActivatedDeactivated);


@@ 54,10 55,16 @@ namespace gui
        case app::home_screen::ViewState::AlarmEdit:
            setScreenMode(ScreenMode::AlarmEdit);
            break;
        case app::home_screen::ViewState::ActivatedLowBattery:
            setScreenMode(ScreenMode::AlarmActivatedDeactivated);
            alarmTopIcon->setStatus(AlarmIcon::Status::DEACTIVATED);
            alarmActivatedDeactivatedScreen->image->set("bell_status_battery_lvl0", gui::ImageTypeSpecifier::W_G);
            break;
        case app::home_screen::ViewState::ActivatedWait:
            setScreenMode(ScreenMode::AlarmActivatedDeactivated);
            alarmTopIcon->setStatus(AlarmIcon::Status::ACTIVATED);
            alarmActivatedDeactivatedScreen->image->set("big_alarm_W_G", {});
            alarmActivatedDeactivatedScreen->image->setMaximumWidth(alarmActivatedDeactivatedScreen->image->getWidth());
            break;
        case app::home_screen::ViewState::Activated:
            alarmActivatedDeactivatedScreen->image->set("big_alarm_W_G", {});

M products/BellHybrid/apps/common/src/popups/AlarmActivatedWindow.cpp => products/BellHybrid/apps/common/src/popups/AlarmActivatedWindow.cpp +54 -10
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <ApplicationCommon.hpp>


@@ 18,6 18,7 @@
namespace
{
    constexpr unsigned int y_position = 22U;
    constexpr std::chrono::seconds screenTimeout{5};
} // namespace

namespace gui


@@ 29,11 30,6 @@ namespace gui
    {
        getPresenter()->attach(this);
        buildInterface();

        timerCallback = [this](Item &, sys::Timer &) {
            returnToPreviousWindow();
            return true;
        };
    }

    void AlarmActivatedWindow::buildInterface()


@@ 44,16 40,59 @@ namespace gui
        header->setTitleVisibility(false);
        navBar->setVisible(false);

        getPresenter()->getLayout() == app::popup::Layout::LowBatteryInfo ? buildLowBatteryLayout()
                                                                          : buildAlarmInfoLayout();
    }

    void AlarmActivatedWindow::buildLowBatteryLayout()
    {
        icon = new Icon(this,
                        0,
                        0,
                        style::window_width,
                        style::window_height,
                        "bell_status_battery_lvl0",
                        {},
                        gui::ImageTypeSpecifier::W_G);
        icon->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Top));
        icon->image->setMargins({0, bell_style::warning_icon_top_margin, 0, 0});
        icon->text->setMaximumWidth(bell_style::warning_text_width);
        icon->text->setFont(style::window::font::verybiglight);
        icon->text->setRichText(utils::translate("app_bell_alarm_lowBattery_info"));

        timerCallback = [this](Item &, sys::Timer &timer) {
            lowBatteryInfoHandled();
            return true;
        };
        resetTimer(screenTimeout);
    }

    void AlarmActivatedWindow::buildAlarmInfoLayout()
    {
        icon = new Icon(this, 0, 0, style::window_width, style::window_height, "big_alarm_W_G", {});
        icon->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Top));
        icon->image->setMargins({0, bell_style::popup_icon_top_margin, 0, bell_style::popup_icon_bottom_margin});
        icon->text->setFont(style::window::font::verybiglight);
        setAlarmTime(getPresenter()->getAlarmTime());

        timerCallback = [this](Item &, sys::Timer &) {
            returnToPreviousWindow();
            return true;
        };
        resetTimer(screenTimeout);
    }

    void AlarmActivatedWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    void AlarmActivatedWindow::rebuild()
    {
        setAlarmTime(getPresenter()->getAlarmTime());
        WindowWithTimer::onBeforeShow(mode, data);
        erase();
        buildInterface();
    }

    void AlarmActivatedWindow::lowBatteryInfoHandled()
    {
        getPresenter()->lowBatteryInfoHandled();
        rebuild();
        application->refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
    }

    void AlarmActivatedWindow::returnToPreviousWindow()


@@ 75,7 114,12 @@ namespace gui
    {
        if (inputEvent.isShortRelease(KeyCode::KEY_ENTER) || inputEvent.isShortRelease(KeyCode::KEY_RF)) {
            detachTimerIfExists();
            returnToPreviousWindow();
            if (getPresenter()->getLayout() == app::popup::Layout::LowBatteryInfo) {
                lowBatteryInfoHandled();
            }
            else {
                returnToPreviousWindow();
            }
            return true;
        }
        return false;

M products/BellHybrid/apps/common/src/popups/presenter/AlarmActivatedPresenter.cpp => products/BellHybrid/apps/common/src/popups/presenter/AlarmActivatedPresenter.cpp +24 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <common/popups/presenter/AlarmActivatedPresenter.hpp>


@@ 8,7 8,8 @@

namespace app::popup
{
    AlarmActivatedPresenter::AlarmActivatedPresenter(AbstractAlarmModel &alarmModel) : alarmModel{alarmModel}
    AlarmActivatedPresenter::AlarmActivatedPresenter(AbstractAlarmModel &alarmModel, AbstractBatteryModel &batteryModel)
        : alarmModel{alarmModel}, batteryModel{batteryModel}
    {}

    bool AlarmActivatedPresenter::isAlarmActive() const noexcept


@@ 21,4 22,25 @@ namespace app::popup
        return alarmModel.getAlarmTime();
    }

    Layout AlarmActivatedPresenter::getLayout()
    {
        if (layout == Layout::Undefined) {
            const auto batteryStatus = batteryModel.getLevelState();
            const units::SOC soc     = batteryStatus.level;
            const auto state         = batteryStatus.state;
            if (not batteryModel.isBatteryCharging(state) && soc < constants::lowBatteryInfoThreshold) {
                layout = Layout::LowBatteryInfo;
            }
            else {
                layout = Layout::AlarmInfo;
            }
        }
        return layout;
    }

    void AlarmActivatedPresenter::lowBatteryInfoHandled()
    {
        layout = Layout::AlarmInfo;
    }

} // namespace app::popup

M products/BellHybrid/apps/common/src/widgets/LayoutVertical.cpp => products/BellHybrid/apps/common/src/widgets/LayoutVertical.cpp +11 -5
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "widgets/LayoutVertical.hpp"


@@ 114,15 114,21 @@ namespace gui
        setAlarmScreen->setVisible(false);

        // Activation / Deactivation
        alarmActivatedDeactivatedScreen =
            new Icon(this, 0, 0, style::window_width, style::window_height, "big_alarm_W_G", {});
        alarmActivatedDeactivatedScreen = new Icon(this,
                                                   0,
                                                   0,
                                                   style::window_width,
                                                   style::window_height,
                                                   "bell_status_battery_lvl0",
                                                   {},
                                                   gui::ImageTypeSpecifier::W_G);
        alarmActivatedDeactivatedScreen->setMinimumSize(style::window_width, style::window_height);
        alarmActivatedDeactivatedScreen->setAlignment(
            gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Top));
        alarmActivatedDeactivatedScreen->image->setMargins(
            {0, bell_style::popup_icon_top_margin, 0, bell_style::popup_icon_bottom_margin});
        alarmActivatedDeactivatedScreen->image->setMargins({0, bell_style::warning_icon_top_margin, 0, 0});
        alarmActivatedDeactivatedScreen->image->setAlignment(
            Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        alarmActivatedDeactivatedScreen->text->setMaximumWidth(bell_style::warning_text_width);
        alarmActivatedDeactivatedScreen->text->setFont(style::window::font::verybiglight);
        alarmActivatedDeactivatedScreen->resizeItems();
        alarmActivatedDeactivatedScreen->setVisible(false);

M products/BellHybrid/apps/include/Application.hpp => products/BellHybrid/apps/include/Application.hpp +3 -1
@@ 1,10 1,11 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <ApplicationCommon.hpp>
#include <common/models/AlarmModel.hpp>
#include <common/models/BatteryModel.hpp>

namespace app
{


@@ 33,6 34,7 @@ namespace app
        virtual void onStop();

        std::unique_ptr<app::AlarmModel> alarmModel;
        std::unique_ptr<AbstractBatteryModel> batteryModel;

      private:
        sys::MessagePointer handleKBDKeyEvent(sys::Message *msgl) override;