~aleteoryx/muditaos

1220a09025c250e73a27e05f41d85a0318f83dba — Paweł Joński 4 years ago e959f48
[BH-1067] Snooze countdown

Add Snooze countdown
26 files changed, 417 insertions(+), 119 deletions(-)

M module-apps/application-meditation/widgets/MeditationTimer.cpp
M module-apps/application-meditation/widgets/MeditationTimer.hpp
M module-apps/apps-common/CMakeLists.txt
M module-apps/apps-common/widgets/ProgressTimer.cpp
M module-apps/apps-common/widgets/ProgressTimer.hpp
A module-apps/apps-common/widgets/ProgressTimerWithBarGraphAndCounter.cpp
A module-apps/apps-common/widgets/ProgressTimerWithBarGraphAndCounter.hpp
M module-utils/time/time/time_conversion.hpp
M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsProgressPresenter.cpp
M products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsProgressWindow.cpp
M products/BellHybrid/apps/application-bell-main/CMakeLists.txt
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
A products/BellHybrid/apps/application-bell-main/widgets/ProgressTimerWithSnoozeTimer.cpp
A products/BellHybrid/apps/application-bell-main/widgets/ProgressTimerWithSnoozeTimer.hpp
A products/BellHybrid/apps/application-bell-main/widgets/SnoozeTimer.cpp
A products/BellHybrid/apps/application-bell-main/widgets/SnoozeTimer.hpp
M products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.cpp
M products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.hpp
M products/BellHybrid/apps/application-bell-powernap/presenter/PowerNapProgressPresenter.cpp
M products/BellHybrid/apps/application-bell-powernap/windows/PowerNapProgressWindow.cpp
M products/BellHybrid/apps/common/include/common/models/AbstractAlarmModel.hpp
M products/BellHybrid/apps/common/include/common/models/AlarmModel.hpp
M products/BellHybrid/apps/common/src/AlarmModel.cpp
M products/BellHybrid/apps/common/src/TimeUtils.cpp
M module-apps/application-meditation/widgets/MeditationTimer.cpp => module-apps/application-meditation/widgets/MeditationTimer.cpp +3 -2
@@ 7,7 7,7 @@
#include <GuiTimer.hpp>
#include <purefs/filesystem_paths.hpp>
#include <service-audio/AudioServiceAPI.hpp>
#include <apps-common/widgets/ProgressTimer.hpp>
#include <apps-common/widgets/ProgressTimerWithBarGraphAndCounter.hpp>

#include <gsl/assert>



@@ 55,7 55,8 @@ namespace gui
        text->setAlignment(Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        text->setEditMode(EditMode::Browse);

        timer = std::make_unique<app::ProgressTimer>(application, *this, meditationTimerName, timerTick);
        timer = std::make_unique<app::ProgressTimerWithBarGraphAndCounter>(
            application, *this, meditationTimerName, timerTick);
        timer->attach(progressBar);
        timer->attach(text);
        auto intervalCallback = [app = application] {

M module-apps/application-meditation/widgets/MeditationTimer.hpp => module-apps/application-meditation/widgets/MeditationTimer.hpp +2 -2
@@ 12,7 12,7 @@

namespace app
{
    class ProgressTimer;
    class ProgressTimerWithBarGraphAndCounter;
}

namespace gui


@@ 40,6 40,6 @@ namespace gui
        app::ApplicationMeditation *application = nullptr;
        CircularProgressBar *progressBar        = nullptr;
        Text *text                              = nullptr;
        std::unique_ptr<app::ProgressTimer> timer;
        std::unique_ptr<app::ProgressTimerWithBarGraphAndCounter> timer;
    };
} // namespace gui

M module-apps/apps-common/CMakeLists.txt => module-apps/apps-common/CMakeLists.txt +1 -0
@@ 50,6 50,7 @@ target_sources(apps-common
        widgets/TimeWidget.cpp
        widgets/WidgetsUtils.cpp
        widgets/ProgressTimer.cpp
        widgets/ProgressTimerWithBarGraphAndCounter.cpp
        windows/AppWindow.cpp
        windows/BrightnessWindow.cpp
        windows/Dialog.cpp

M module-apps/apps-common/widgets/ProgressTimer.cpp => module-apps/apps-common/widgets/ProgressTimer.cpp +1 -52
@@ 8,10 8,6 @@
#include <apps-common/GuiTimer.hpp>
#include <gsl/assert>

namespace
{
    inline constexpr auto increasingModePrefix = "-";
}
namespace app
{



@@ 25,45 21,11 @@ namespace app
          countdownMode{countdownMode}, displayFormat{displayFormat}
    {}

    void ProgressTimer::resetProgress()
    {
        if (progress != nullptr) {
            progress->setValue(0);
        }
    }

    void ProgressTimer::update()
    {
        updateText();
        updateProgress();
        app->refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
    }

    void ProgressTimer::updateText()
    {
        using utils::time::Duration;
        if (text == nullptr) {
            return;
        }
        const auto secondsRemaining = duration - elapsed;
        const Duration remainingDuration{std::time_t{secondsRemaining.count()}};
        UTF8 timerText;
        if (countdownMode == ProgressCountdownMode::Increasing && secondsRemaining != std::chrono::seconds::zero()) {
            timerText += increasingModePrefix;
        }
        timerText += remainingDuration.str(displayFormat);
        text->setText(std::move(timerText));
    }

    void ProgressTimer::updateProgress()
    {
        if (progress != nullptr) {
            const auto percentage  = static_cast<float>(elapsed.count()) / duration.count();
            const auto currentStep = percentage * progress->getMaximum();
            progress->setValue(std::ceil(currentStep));
        }
    }

    void ProgressTimer::reset(std::chrono::seconds _duration, std::chrono::seconds _interval)
    {
        Expects(_duration != std::chrono::seconds::zero());


@@ 73,8 35,7 @@ namespace app
        interval    = _interval;
        hasInterval = _interval != std::chrono::seconds::zero();

        updateText();
        resetProgress();
        update();
    }

    void ProgressTimer::start()


@@ 138,16 99,4 @@ namespace app
    {
        onIntervalCallback = std::move(cb);
    }

    void ProgressTimer::attach(gui::Progress *_progress)
    {
        Expects(_progress != nullptr);
        progress = _progress;
    }

    void ProgressTimer::attach(gui::Text *_text)
    {
        Expects(_text != nullptr);
        text = _text;
    }
} // namespace app

M module-apps/apps-common/widgets/ProgressTimer.hpp => module-apps/apps-common/widgets/ProgressTimer.hpp +4 -11
@@ 15,8 15,6 @@ namespace
namespace gui
{
    class Item;
    class Text;
    class Progress;
} // namespace gui

namespace app


@@ 41,10 39,9 @@ namespace app
    {
        app::ApplicationCommon *app = nullptr;
        gui::Item &parent;
        gui::Text *text         = nullptr;
        gui::Progress *progress = nullptr;
        const std::string name;

      protected:
        std::atomic_bool isRunning{false};
        std::chrono::seconds duration{std::chrono::seconds::zero()};
        std::chrono::seconds elapsed{std::chrono::seconds::zero()};


@@ 60,14 57,13 @@ namespace app
        utils::time::Duration::DisplayedFormat displayFormat;

        void startTimer();
        void update();
        void updateText();
        void updateProgress();
        void resetProgress();

        [[nodiscard]] auto onTimerTimeout(sys::Timer &timerTask) -> bool;
        [[nodiscard]] auto isFinished() const noexcept -> bool;
        [[nodiscard]] auto intervalReached() const noexcept -> bool;

        virtual void update();

      public:
        ProgressTimer(
            app::ApplicationCommon *app,


@@ 83,9 79,6 @@ namespace app
        void registerOnFinishedCallback(std::function<void()> cb) override;
        void registerOnIntervalCallback(std::function<void()> cb) override;
        [[nodiscard]] auto isStopped() const noexcept -> bool override;

        void attach(gui::Progress *_progress);
        void attach(gui::Text *_text);
    };

} // namespace app

A module-apps/apps-common/widgets/ProgressTimerWithBarGraphAndCounter.cpp => module-apps/apps-common/widgets/ProgressTimerWithBarGraphAndCounter.cpp +60 -0
@@ 0,0 1,60 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ProgressTimerWithBarGraphAndCounter.hpp"
#include <Text.hpp>
#include <ProgressBar.hpp>
#include <ApplicationCommon.hpp>
#include <apps-common/GuiTimer.hpp>
#include <gsl/assert>

namespace
{
    inline constexpr auto increasingModePrefix = "-";
}
namespace app
{
    void ProgressTimerWithBarGraphAndCounter::update()
    {
        updateText();
        updateProgress();
        ProgressTimer::update();
    }

    void ProgressTimerWithBarGraphAndCounter::updateText()
    {
        using utils::time::Duration;
        if (text == nullptr) {
            return;
        }
        const auto secondsRemaining = duration - elapsed;
        const Duration remainingDuration{std::time_t{secondsRemaining.count()}};
        UTF8 timerText;
        if (countdownMode == ProgressCountdownMode::Increasing && secondsRemaining != std::chrono::seconds::zero()) {
            timerText += increasingModePrefix;
        }
        timerText += remainingDuration.str(displayFormat);
        text->setText(std::move(timerText));
    }

    void ProgressTimerWithBarGraphAndCounter::updateProgress()
    {
        if (progress != nullptr) {
            const auto percentage  = static_cast<float>(elapsed.count()) / duration.count();
            const auto currentStep = percentage * progress->getMaximum();
            progress->setValue(std::ceil(currentStep));
        }
    }

    void ProgressTimerWithBarGraphAndCounter::attach(gui::Progress *_progress)
    {
        Expects(_progress != nullptr);
        progress = _progress;
    }

    void ProgressTimerWithBarGraphAndCounter::attach(gui::Text *_text)
    {
        Expects(_text != nullptr);
        text = _text;
    }
} // namespace app

A module-apps/apps-common/widgets/ProgressTimerWithBarGraphAndCounter.hpp => module-apps/apps-common/widgets/ProgressTimerWithBarGraphAndCounter.hpp +36 -0
@@ 0,0 1,36 @@
// 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 "ProgressTimer.hpp"
#include <Timers/TimerHandle.hpp>
#include <time/time_conversion.hpp>
#include <atomic>
#include <chrono>
#include <string>

namespace gui
{
    class Text;
    class Progress;
} // namespace gui

namespace app
{
    class ProgressTimerWithBarGraphAndCounter : public ProgressTimer
    {
        gui::Text *text         = nullptr;
        gui::Progress *progress = nullptr;

        void update() override;
        void updateText();
        void updateProgress();

      public:
        using ProgressTimer::ProgressTimer;

        void attach(gui::Progress *_progress);
        void attach(gui::Text *_text);
    };

} // namespace app

M module-utils/time/time/time_conversion.hpp => module-utils/time/time/time_conversion.hpp +5 -0
@@ 212,6 212,11 @@ namespace utils
                return duration;
            }

            time_t getSeconds() const
            {
                return seconds;
            }

            time_t getMinutes() const
            {
                return minutes;

M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsProgressPresenter.cpp => products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsProgressPresenter.cpp +1 -1
@@ 5,7 5,7 @@
#include "data/BGSoundsCommon.hpp"
#include "widgets/BGSoundsPlayer.hpp"
#include <ApplicationBellBackgroundSounds.hpp>
#include <apps-common/widgets/ProgressTimer.hpp>
#include <apps-common/widgets/ProgressTimerWithBarGraphAndCounter.hpp>
#include <common/models/TimeModel.hpp>
#include <service-db/Settings.hpp>
#include <Timers/TimerFactory.hpp>

M products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsProgressWindow.cpp => products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsProgressWindow.cpp +2 -2
@@ 6,7 6,7 @@
#include "data/BGSoundsStyle.hpp"
#include <apps-common/widgets/BellBaseLayout.hpp>
#include <apps-common/widgets/BarGraph.hpp>
#include <apps-common/widgets/ProgressTimer.hpp>
#include <apps-common/widgets/ProgressTimerWithBarGraphAndCounter.hpp>
#include <apps-common/GuiTimer.hpp>
#include <TextFixedSize.hpp>
#include <time/dateCommon.hpp>


@@ 115,7 115,7 @@ namespace gui
    }
    void BGSoundsProgressWindow::configureTimer()
    {
        auto timer = std::make_unique<app::ProgressTimer>(
        auto timer = std::make_unique<app::ProgressTimerWithBarGraphAndCounter>(
            application, *this, bgSoundsTimerName, timerTick, app::ProgressCountdownMode::Increasing);
        timer->attach(progressBar);
        timer->attach(timerText);

M products/BellHybrid/apps/application-bell-main/CMakeLists.txt => products/BellHybrid/apps/application-bell-main/CMakeLists.txt +4 -0
@@ 5,6 5,8 @@ target_sources(application-bell-main
    PRIVATE
        ApplicationBellMain.cpp
        widgets/BellBattery.cpp
        widgets/ProgressTimerWithSnoozeTimer.cpp
        widgets/SnoozeTimer.cpp
        windows/BellBatteryShutdownWindow.cpp
        windows/BellHomeScreenWindow.cpp
        windows/BellMainMenuWindow.cpp


@@ 16,6 18,8 @@ target_sources(application-bell-main
        presenters/StateController.cpp

        widgets/BellBattery.hpp
        widgets/ProgressTimerWithSnoozeTimer.hpp
        widgets/SnoozeTimer.hpp

        windows/BellBatteryShutdownWindow.hpp
        windows/BellHomeScreenWindow.hpp

M products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.cpp => products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.cpp +22 -0
@@ 92,4 92,26 @@ namespace app::home_screen
        getView()->setAlarmTime(alarmModel->getAlarmTime());
        stateController->handleAlarmModelReady();
    }

    void HomeScreenPresenter::setSnoozeTimer(std::unique_ptr<app::ProgressTimerWithSnoozeTimer> &&_timer)
    {
        snoozeTimer = std::move(_timer);
    }

    void HomeScreenPresenter::startSnoozeTimer(std::chrono::seconds snoozeDuration)
    {
        snoozeTimer->reset(snoozeDuration, snoozeTick);
        snoozeTimer->start();
    }

    void HomeScreenPresenter::stopSnoozeTimer()
    {
        snoozeTimer->start();
    }

    void HomeScreenPresenter::restartSnoozeTimer(std::chrono::seconds snoozeDuration)
    {
        snoozeTimer->reset(snoozeDuration, snoozeTick);
    }

} // namespace app::home_screen

M products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.hpp => products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.hpp +22 -3
@@ 4,6 4,7 @@
#pragma once

#include "models/TemperatureModel.hpp"
#include "widgets/ProgressTimerWithSnoozeTimer.hpp"

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


@@ 39,6 40,14 @@ namespace app::home_screen
    class AbstractBatteryModel;
    class AbstractTemperatureModel;

    enum class HeaderViewMode
    {
        Empty,
        AlarmIcon,
        AlarmIconAndTime,
        SnoozeCountdown
    };

    class AbstractView
    {
      public:


@@ 46,11 55,9 @@ namespace app::home_screen

        /// Alarm widget related API
        virtual void setAlarmTriggered()                                     = 0;
        virtual void setAlarmSnoozed()                                       = 0;
        virtual void setAlarmActive(bool)                                    = 0;
        virtual void setAlarmEdit(bool)                                      = 0;
        virtual void setAlarmVisible(bool)                                   = 0;
        virtual void setAlarmTimeVisible(bool)                               = 0;
        virtual void setHeaderViewMode(HeaderViewMode mode)                  = 0;
        virtual std::time_t getAlarmTime() const                             = 0;
        virtual void setAlarmTime(std::time_t time)                          = 0;
        virtual void setAlarmTimeFormat(utils::time::Locale::TimeFormat fmt) = 0;


@@ 84,6 91,10 @@ namespace app::home_screen
        virtual void detachTimer()                                                  = 0;
        virtual void handleAlarmRingingEvent()                                      = 0;
        virtual void handleAlarmModelReady()                                        = 0;
        virtual void setSnoozeTimer(std::unique_ptr<app::ProgressTimerWithSnoozeTimer> &&_timer) = 0;
        virtual void startSnoozeTimer(std::chrono::seconds snoozeDuration)                       = 0;
        virtual void stopSnoozeTimer()                                                           = 0;
        virtual void restartSnoozeTimer(std::chrono::seconds snoozeDuration)                     = 0;

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


@@ 115,6 126,12 @@ namespace app::home_screen
        void handleAlarmRingingEvent() override;
        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);

      private:
        ApplicationCommon *app;
        sys::TimerHandle timer;


@@ 123,7 140,9 @@ namespace app::home_screen
        std::unique_ptr<AbstractTemperatureModel> temperatureModel;
        std::unique_ptr<AbstractTimeModel> timeModel;
        std::shared_ptr<AbstractController> stateController;
        std::unique_ptr<ProgressTimerWithSnoozeTimer> snoozeTimer;

        static constexpr auto timerName = "HS_timer";
        static constexpr auto snoozeTick = std::chrono::seconds(1);
    };
} // namespace app::home_screen

M products/BellHybrid/apps/application-bell-main/presenters/StateController.cpp => products/BellHybrid/apps/application-bell-main/presenters/StateController.cpp +20 -25
@@ 32,20 32,17 @@ namespace app::home_screen
    {
        namespace Helpers
        {
            auto detachTimer          = [](AbstractPresenter &presenter) { presenter.detachTimer(); };
            auto switchToMenu         = [](AbstractView &view) { view.switchToMenu(); };
            auto makeAlarmEditable    = [](AbstractView &view) { view.setAlarmEdit(true); };
            auto makeAlarmNonEditable = [](AbstractView &view) { view.setAlarmEdit(false); };
            auto hideAlarmTime        = [](AbstractView &view) { view.setAlarmTimeVisible(false); };
            auto showAlarmTime        = [](AbstractView &view) { view.setAlarmTimeVisible(true); };
            auto updateBottomStats =
                [](AbstractView &view, AbstractBatteryModel &batteryModel, AbstractTemperatureModel &temperatureModel) {
                    view.setTemperature(temperatureModel.getTemperature());
                    view.setBatteryLevelState(batteryModel.getLevelState());
                };
            auto setNewAlarmTime = [](AbstractView &view, AbstractAlarmModel &alarmModel) {
                alarmModel.setAlarmTime(view.getAlarmTime());
            };
            auto setNewAlarmTime = [](AbstractView &view,
                                      AbstractAlarmModel &alarmModel,
                                      AbstractPresenter &presenter) { alarmModel.setAlarmTime(view.getAlarmTime()); };

            auto isAlarmActive   = [](AbstractAlarmModel &alarmModel) -> bool { return alarmModel.isActive(); };
            auto isSnoozeAllowed = [](AbstractAlarmModel &alarmModel, AbstractController &controller) -> bool {


@@ 110,7 107,7 @@ namespace app::home_screen
                alarmModel.update([&]() { presenter.handleAlarmModelReady(); });
                view.setAlarmEdit(false);
                view.setAlarmActive(false);
                view.setAlarmVisible(false);
                view.setHeaderViewMode(HeaderViewMode::Empty);
                view.setTemperature(temperatureModel.getTemperature());
                view.setBatteryLevelState(batteryModel.getLevelState());
            };


@@ 126,8 123,7 @@ namespace app::home_screen
                controller.snooze(false);
                view.setAlarmEdit(false);
                view.setAlarmActive(false);
                view.setAlarmVisible(false);
                view.setAlarmTimeVisible(false);
                view.setHeaderViewMode(HeaderViewMode::Empty);
                view.setTemperature(temperatureModel.getTemperature());
            };
        } // namespace Deactivated


@@ 139,7 135,7 @@ namespace app::home_screen
                presenter.spawnTimer();
                view.setBottomDescription(utils::translate("app_bell_alarm_deactivated"));
                view.setAlarmActive(false);
                view.setAlarmTimeVisible(false);
                view.setHeaderViewMode(HeaderViewMode::AlarmIcon);
            };
            auto exit = [](AbstractPresenter &presenter) { presenter.detachTimer(); };
        } // namespace DeactivatedWait


@@ 148,8 144,7 @@ namespace app::home_screen
        {
            auto entry = [](AbstractView &view, AbstractPresenter &presenter) {
                view.setAlarmEdit(true);
                view.setAlarmTimeVisible(true);
                view.setAlarmVisible(true);
                view.setHeaderViewMode(HeaderViewMode::AlarmIconAndTime);
            };
            auto exit = [](AbstractView &view, AbstractPresenter &presenter) {
                view.setAlarmEdit(false);


@@ 192,8 187,7 @@ namespace app::home_screen
                view.setBottomDescription(utils::time::getBottomDescription(
                    utils::time::calculateMinutesDifference(view.getAlarmTime(), timeModel.getCurrentTime())));
                view.setAlarmActive(true);
                view.setAlarmVisible(true);
                view.setAlarmTimeVisible(true);
                view.setHeaderViewMode(HeaderViewMode::AlarmIconAndTime);
            };
            auto exit = [](AbstractPresenter &presenter) { presenter.detachTimer(); };
        } // namespace ActivatedWait


@@ 207,8 201,7 @@ namespace app::home_screen
                controller.snooze(false);
                view.setTemperature(temperatureModel.getTemperature());
                view.setAlarmActive(true);
                view.setAlarmVisible(true);
                view.setAlarmTimeVisible(true);
                view.setHeaderViewMode(HeaderViewMode::AlarmIconAndTime);
                view.setAlarmTime(alarmModel.getAlarmTime());
            };
        } // namespace Activated


@@ 218,7 211,7 @@ namespace app::home_screen
            auto entry =
                [](AbstractView &view, AbstractTemperatureModel &temperatureModel, AbstractPresenter &presenter) {
                    presenter.spawnTimer(defaultAlarmRingingTime);
                    view.setAlarmTimeVisible(false);
                    view.setHeaderViewMode(HeaderViewMode::AlarmIcon);
                    view.setAlarmTriggered();
                    view.setTemperature(temperatureModel.getTemperature());
                };


@@ 231,7 224,7 @@ namespace app::home_screen
                presenter.spawnTimer();
                alarmModel.turnOff();
                alarmModel.activate(false);
                view.setAlarmTimeVisible(false);
                view.setHeaderViewMode(HeaderViewMode::AlarmIcon);
                view.setBottomDescription(Helpers::getGreeting());
                view.setAlarmActive(false);
            };


@@ 243,8 236,10 @@ namespace app::home_screen
            auto entry = [](AbstractView &view, AbstractAlarmModel &alarmModel, AbstractPresenter &presenter) {
                presenter.spawnTimer();
                alarmModel.snooze();
                view.setAlarmTimeVisible(false);
                view.setAlarmSnoozed();
                view.setHeaderViewMode(HeaderViewMode::SnoozeCountdown);

                const auto snoozeDuration = alarmModel.getTimeToNextSnooze();
                presenter.startSnoozeTimer(snoozeDuration);
                const auto bottomDescription = utils::translate("app_bellmain_home_screen_bottom_desc") + " " +
                                               std::to_string(alarmModel.getSnoozeDuration()) + " min";
                view.setBottomDescription(bottomDescription);


@@ 256,11 251,10 @@ namespace app::home_screen
        {
            auto entry =
                [](AbstractView &view, AbstractPresenter &presenter, AbstractTemperatureModel &temperatureModel) {
                    view.setAlarmTimeVisible(false);
                    view.setAlarmSnoozed();
                    view.setHeaderViewMode(HeaderViewMode::SnoozeCountdown);
                    view.setTemperature(temperatureModel.getTemperature());
                };
            auto exit = [](AbstractPresenter &presenter) {};
            auto exit = [](AbstractPresenter &presenter) { presenter.stopSnoozeTimer(); };
        } // namespace AlarmSnoozed

        class StateMachine


@@ 278,7 272,7 @@ namespace app::home_screen
                                             "Deactivated"_s + event<Events::LightPress>/ Helpers::switchToMenu,
                                             "Deactivated"_s + event<Events::RotateLeftPress> / Helpers::makeAlarmEditable = "DeactivatedEdit"_s,
                                             "Deactivated"_s + event<Events::RotateRightPress> / Helpers::makeAlarmEditable = "DeactivatedEdit"_s,
                                             "Deactivated"_s + event<Events::DeepUpPress> / Helpers::showAlarmTime = "ActivatedWait"_s,
                                             "Deactivated"_s + event<Events::DeepUpPress> = "ActivatedWait"_s,
                                             "Deactivated"_s + event<Events::TimeUpdate> / Helpers::updateBottomStats,

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


@@ 316,6 310,7 @@ namespace app::home_screen
                                             "ActivatedWait"_s + event<Events::BackPress> = "Activated"_s,
                                             "ActivatedWait"_s + event<Events::RotateLeftPress> = "ActivatedEdit"_s,
                                             "ActivatedWait"_s + event<Events::RotateRightPress> = "ActivatedEdit"_s,
                                             "ActivatedWait"_s + event<Events::AlarmRinging>  = "AlarmRinging"_s,

                                             "Activated"_s + sml::on_entry<_> / Activated::entry,
                                             "Activated"_s [not Helpers::isAlarmActive] = "Deactivated"_s,


@@ 323,7 318,7 @@ namespace app::home_screen
                                             "Activated"_s + event<Events::RotateLeftPress> / Helpers::makeAlarmEditable = "ActivatedEdit"_s,
                                             "Activated"_s + event<Events::RotateRightPress> / Helpers::makeAlarmEditable = "ActivatedEdit"_s,
                                             "Activated"_s + event<Events::TimeUpdate> / Helpers::updateBottomStats,
                                             "Activated"_s + event<Events::DeepDownPress> / Helpers::hideAlarmTime  = "DeactivatedWait"_s,
                                             "Activated"_s + event<Events::DeepDownPress>  = "DeactivatedWait"_s,
                                             "Activated"_s + event<Events::AlarmRinging>  = "AlarmRinging"_s,

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

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

#include "ProgressTimerWithSnoozeTimer.hpp"
#include "widgets/SnoozeTimer.hpp"

#include <Text.hpp>
#include <ProgressBar.hpp>
#include <ApplicationCommon.hpp>
#include <apps-common/GuiTimer.hpp>
#include <gsl/assert>

namespace
{
    inline constexpr auto increasingModePrefix = "-";
}
namespace app
{
    void ProgressTimerWithSnoozeTimer::update()
    {
        updateTime();
        ProgressTimer::update();
    }

    void ProgressTimerWithSnoozeTimer::updateTime()
    {
        using utils::time::Duration;
        if (timer == nullptr) {
            return;
        }
        const auto secondsRemaining = duration - elapsed;
        const Duration remainingDuration{std::time_t{secondsRemaining.count()}};

        timer->setTime(remainingDuration.getMinutes(), remainingDuration.getSeconds());
    }

    void ProgressTimerWithSnoozeTimer::attach(gui::SnoozeTimer *_timer)
    {
        Expects(_timer != nullptr);
        timer = _timer;
    }
} // namespace app

A products/BellHybrid/apps/application-bell-main/widgets/ProgressTimerWithSnoozeTimer.hpp => products/BellHybrid/apps/application-bell-main/widgets/ProgressTimerWithSnoozeTimer.hpp +32 -0
@@ 0,0 1,32 @@
// 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/ProgressTimer.hpp>
#include <Timers/TimerHandle.hpp>
#include <time/time_conversion.hpp>
#include <atomic>
#include <chrono>
#include <string>

namespace gui
{
    class SnoozeTimer;
} // namespace gui

namespace app
{
    class ProgressTimerWithSnoozeTimer : public ProgressTimer
    {
        gui::SnoozeTimer *timer = nullptr;

        void update() override;
        void updateTime();

      public:
        using ProgressTimer::ProgressTimer;

        void attach(gui::SnoozeTimer *time);
    };

} // namespace app

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

#include "SnoozeTimer.hpp"
#include <FontManager.hpp>
#include <RawFont.hpp>
#include <gui/widgets/Label.hpp>
#include <gui/widgets/ImageBox.hpp>
#include <apps-common/widgets/TimeSetSpinner.hpp>

namespace gui
{
    SnoozeTimer::SnoozeTimer(Item *parent, Position x, Position y, Length w, Length h) : HBox(parent, x, y, w, h)
    {
        setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        setEdges(RectangleEdge::None);

        auto alarmImg = new ImageBox(this, 0, 0, 0, 0, new Image("bell_alarm_snooze_W_M"));
        alarmImg->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        alarmImg->setMargins(Margins(10U, 0, 10U, 0));
        alarmImg->setMinimumSizeToFitImage();

        minusText = new TextFixedSize(this, 0, 0, 0, 0);
        minusText->setText("-");
        minusText->setFont(style::window::font::largelight);
        minusText->setMargins(Margins(0, 0, 0, 0));
        minusText->setMinimumHeightToFitText();
        minusText->setMinimumWidthToFitText();
        minusText->drawUnderline(false);
        minusText->setEditMode(EditMode::Browse);
        minusText->activeItem = false;
        minusText->setAlignment(Alignment(Alignment::Horizontal::Right, Alignment::Vertical::Center));

        timeSpinner = new TimeSetSpinner(this);
        timeSpinner->setFont(style::window::font::largelight);
        timeSpinner->setEditMode(EditMode::Browse);
        timeSpinner->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        timeSpinner->setMargins(Margins(0, 0, 10U, 0));
        timeSpinner->setEdges(RectangleEdge::None);

        resizeItems();
    }

    auto SnoozeTimer::setFont(std::string newFontName) noexcept -> void
    {
        fontName = std::move(newFontName);
        minusText->setFont(fontName);
        timeSpinner->setFont(fontName);
    }

    auto SnoozeTimer::setTime(std::uint8_t mins, std::uint8_t secs) noexcept -> void
    {
        timeSpinner->setTime(mins, secs);
    }

} /* namespace gui */

A products/BellHybrid/apps/application-bell-main/widgets/SnoozeTimer.hpp => products/BellHybrid/apps/application-bell-main/widgets/SnoozeTimer.hpp +42 -0
@@ 0,0 1,42 @@
// 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 <gui/widgets/BoxLayout.hpp>
#include <gui/widgets/Style.hpp>
#include <gui/widgets/TextFixedSize.hpp>
#include <gui/widgets/TextConstants.hpp>
#include <time/time_locale.hpp>
#include <string>

namespace gui
{
    class TimeSetSpinner;
    class ImageBox;

    class SnoozeTimer : public HBox
    {
      public:
        enum class Status
        {
            UNKNOWN,
            RINGING,
            ACTIVATED,
            DEACTIVATED,
            SNOOZE
        };

        explicit SnoozeTimer(Item *parent = nullptr, Position x = 0U, Position y = 0U, Length w = 0U, Length h = 0U);

        auto setFont(std::string newFontName) noexcept -> void;
        auto setTime(std::uint8_t mins, std::uint8_t secs) noexcept -> void;

      private:
        TimeSetSpinner *timeSpinner = nullptr;
        TextFixedSize *minusText    = nullptr;

        Status alarmStatus   = Status::DEACTIVATED;
        std::string fontName = style::window::font::largelight;
    };
} /* namespace gui */

M products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.cpp => products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.cpp +39 -12
@@ 3,6 3,7 @@

#include "BellHomeScreenWindow.hpp"
#include "data/BellMainStyle.hpp"
#include "widgets/SnoozeTimer.hpp"

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


@@ 66,6 67,8 @@ namespace
            tm.tm_min--;
        }
    }
    inline constexpr auto snoozeTimerName = "SnoozeTimer";
    inline constexpr std::chrono::seconds timerTick{1};
} // namespace

namespace gui


@@ 98,6 101,10 @@ namespace gui
        alarm->setAlarmStatus(AlarmSetSpinner::Status::DEACTIVATED);
        alarm->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));

        snoozeTimer = new SnoozeTimer(body->firstBox);
        snoozeTimer->setMinimumSize(style::bell_base_layout::outer_layouts_w, style::bell_base_layout::outer_layouts_h);
        snoozeTimer->setVisible(false);

        time = new TimeSetFmtSpinner(body->centerBox);
        time->setMaximumSize(style::bell_base_layout::w, style::bell_base_layout::h);
        time->setFont(bellMainStyle::mainWindow::time::font);


@@ 125,6 132,11 @@ namespace gui
        bottomText->drawUnderline(false);

        body->resize();

        auto timer = std::make_unique<app::ProgressTimerWithSnoozeTimer>(
            application, *this, snoozeTimerName, timerTick, app::ProgressCountdownMode::Increasing);
        timer->attach(snoozeTimer);
        presenter->setSnoozeTimer(std::move(timer));
    }

    void BellHomeScreenWindow::setAlarmTriggered()


@@ 132,11 144,6 @@ namespace gui
        alarm->setAlarmStatus(AlarmSetSpinner::Status::RINGING);
    }

    void BellHomeScreenWindow::setAlarmSnoozed()
    {
        alarm->setAlarmStatus(AlarmSetSpinner::Status::SNOOZE);
    }

    void BellHomeScreenWindow::setAlarmActive(bool val)
    {
        if (val) {


@@ 147,14 154,34 @@ namespace gui
        }
    }

    void BellHomeScreenWindow::setAlarmVisible(bool val)
    void BellHomeScreenWindow::setHeaderViewMode(app::home_screen::HeaderViewMode mode)
    {
        alarm->setVisible(val);
    }

    void BellHomeScreenWindow::setAlarmTimeVisible(bool val)
    {
        alarm->setAlarmTimeVisible(val);
        switch (mode) {
        case app::home_screen::HeaderViewMode::Empty:
            alarm->setVisible(false);
            alarm->setAlarmTimeVisible(false);
            snoozeTimer->setVisible(false);
            alarm->informContentChanged();
            break;
        case app::home_screen::HeaderViewMode::AlarmIconAndTime:
            alarm->setVisible(true);
            alarm->setAlarmTimeVisible(true);
            snoozeTimer->setVisible(false);
            alarm->informContentChanged();
            break;
        case app::home_screen::HeaderViewMode::AlarmIcon:
            alarm->setVisible(true);
            alarm->setAlarmTimeVisible(false);
            snoozeTimer->setVisible(false);
            alarm->informContentChanged();
            break;
        case app::home_screen::HeaderViewMode::SnoozeCountdown:
            alarm->setVisible(false);
            alarm->setAlarmTimeVisible(false);
            snoozeTimer->setVisible(true);
            snoozeTimer->informContentChanged();
            break;
        }
    }

    void BellHomeScreenWindow::setTemperature(utils::temperature::Temperature newTemp)

M products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.hpp => products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.hpp +3 -3
@@ 31,6 31,7 @@ namespace gui
    class TextFixedSize;
    class AlarmSetSpinner;
    class TimeSetFmtSpinner;
    class SnoozeTimer;

    class BellHomeScreenWindow : public AppWindow, public app::home_screen::AbstractView
    {


@@ 46,11 47,9 @@ namespace gui
        bool onDatabaseMessage(sys::Message *msg) override;

        void setAlarmTriggered() override;
        void setAlarmSnoozed() override;
        void setAlarmActive(bool val) override;
        void setAlarmEdit(bool val) override;
        void setAlarmVisible(bool val) override;
        void setAlarmTimeVisible(bool val) override;
        void setHeaderViewMode(app::home_screen::HeaderViewMode mode) override;
        std::time_t getAlarmTime() const override;
        void setAlarmTime(std::time_t newTime) override;
        void incAlarmMinute() override;


@@ 71,6 70,7 @@ namespace gui
        BellBattery *battery{};
        TextFixedSize *bottomText{};
        AlarmSetSpinner *alarm{};
        SnoozeTimer *snoozeTimer{};

        std::unique_ptr<app::home_screen::AbstractPresenter> presenter;


M products/BellHybrid/apps/application-bell-powernap/presenter/PowerNapProgressPresenter.cpp => products/BellHybrid/apps/application-bell-powernap/presenter/PowerNapProgressPresenter.cpp +1 -1
@@ 5,7 5,7 @@
#include "application-bell-powernap/ApplicationBellPowerNap.hpp"
#include "data/PowerNapCommon.hpp"

#include <apps-common/widgets/ProgressTimer.hpp>
#include <apps-common/widgets/ProgressTimerWithBarGraphAndCounter.hpp>
#include <common/models/TimeModel.hpp>
#include <db/SystemSettings.hpp>
#include <service-db/Settings.hpp>

M products/BellHybrid/apps/application-bell-powernap/windows/PowerNapProgressWindow.cpp => products/BellHybrid/apps/application-bell-powernap/windows/PowerNapProgressWindow.cpp +2 -2
@@ 7,7 7,7 @@
#include "data/PowerNapSwitchData.hpp"
#include <apps-common/widgets/BellBaseLayout.hpp>
#include <apps-common/widgets/BarGraph.hpp>
#include <apps-common/widgets/ProgressTimer.hpp>
#include <apps-common/widgets/ProgressTimerWithBarGraphAndCounter.hpp>
#include <apps-common/GuiTimer.hpp>
#include <Text.hpp>
#include <keymap/KeyMap.hpp>


@@ 96,7 96,7 @@ namespace gui
    }
    void PowerNapProgressWindow::configureTimer()
    {
        auto timer = std::make_unique<app::ProgressTimer>(
        auto timer = std::make_unique<app::ProgressTimerWithBarGraphAndCounter>(
            application, *this, powernapTimerName, timerTick, app::ProgressCountdownMode::Increasing);
        timer->attach(progressBar);
        timer->attach(timerText);

M products/BellHybrid/apps/common/include/common/models/AbstractAlarmModel.hpp => products/BellHybrid/apps/common/include/common/models/AbstractAlarmModel.hpp +2 -0
@@ 3,6 3,7 @@

#pragma once

#include <chrono>
#include <ctime>
#include <cstdint>
#include <functional>


@@ 26,6 27,7 @@ namespace app
        virtual bool isSnoozeActive()             = 0;
        virtual void turnOff()                    = 0;
        virtual void snooze()                     = 0;
        virtual std::chrono::seconds getTimeToNextSnooze() = 0;
        /// Command model to update its internal data
        virtual void update(AlarmModelReadyHandler callback = AlarmModelReadyHandler()) = 0;
    };

M products/BellHybrid/apps/common/include/common/models/AlarmModel.hpp => products/BellHybrid/apps/common/include/common/models/AlarmModel.hpp +2 -0
@@ 36,6 36,7 @@ namespace app
        bool isSnoozeActive() override;
        void turnOff() override;
        void snooze() override;
        std::chrono::seconds getTimeToNextSnooze() override;

      private:
        enum class State


@@ 55,6 56,7 @@ namespace app
        State state{State::Invalid};
        SingleEventRecord cachedRecord;
        std::uint32_t snoozeCount = 0;
        TimePoint nextSnoozeTime  = TIME_POINT_INVALID;

        std::function<bool(sys::ResponseMessage *)> responseCallback;


M products/BellHybrid/apps/common/src/AlarmModel.cpp => products/BellHybrid/apps/common/src/AlarmModel.cpp +9 -2
@@ 92,6 92,7 @@ namespace app
        auto request = AsyncRequest::createFromMessage(std::make_unique<alarms::TurnOffSnoozeRequestMessage>(alarm.ID),
                                                       service::name::service_time);
        request->execute(app, this, responseCallback);
        nextSnoozeTime = TIME_POINT_INVALID;
    }
    bool AlarmModel::isActive() const
    {


@@ 122,6 123,7 @@ namespace app
    void AlarmModel::turnOff()
    {
        snoozeCount = 0;
        nextSnoozeTime = TIME_POINT_INVALID;
        alarms::AlarmServiceAPI::requestTurnOffRingingAlarm(app, cachedRecord.parent->ID);
    }



@@ 132,9 134,14 @@ namespace app
        const auto snoozeDuration = utils::getNumericValue<std::uint32_t>(snoozeDurationStr);

        snoozeCount++;
        auto newAlarmTime =
        nextSnoozeTime =
            std::chrono::floor<std::chrono::minutes>(TimePointNow()) + std::chrono::minutes(snoozeDuration);
        alarms::AlarmServiceAPI::requestSnoozeRingingAlarm(app, cachedRecord.parent->ID, newAlarmTime);
        alarms::AlarmServiceAPI::requestSnoozeRingingAlarm(app, cachedRecord.parent->ID, nextSnoozeTime);
    }

    std::chrono::seconds AlarmModel::getTimeToNextSnooze()
    {
        return std::chrono::duration_cast<std::chrono::seconds>(nextSnoozeTime - TimePointNow());
    }

    AlarmEventRecord AlarmModel::generateDefaultAlarm() const

M products/BellHybrid/apps/common/src/TimeUtils.cpp => products/BellHybrid/apps/common/src/TimeUtils.cpp +4 -1
@@ 30,7 30,10 @@ namespace utils::time
        const auto duration = Duration{timestamp};
        const auto timeText = [](time_t hours, time_t minutes) -> std::string {
            if (hours == 0) {
                if (minutes == 1) {
                if (minutes == 0) {
                    return "24 h";
                }
                else if (minutes == 1) {
                    return translate("app_bellmain_home_screen_bottom_desc_less_than") + " 1 min";
                }
                else {