~aleteoryx/muditaos

ecd6189e127a90d5f60d4336cff08e4ad1e42bdc — Maciej Gibowicz 1 year, 10 months ago b3976f5
[BH-1900] Add focus time, repeats and short break time to settings

The user will be able to set the time and number of repetitions for the
focus session and the time of a short break in the Focus Timer
application
23 files changed, 377 insertions(+), 86 deletions(-)

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-bell-focus-timer/ApplicationFocusTimer.cpp
M products/BellHybrid/apps/application-bell-focus-timer/CMakeLists.txt
M products/BellHybrid/apps/application-bell-focus-timer/data/FocusCommon.hpp
M products/BellHybrid/apps/application-bell-focus-timer/include/application-bell-focus-timer/ApplicationFocusTimer.hpp
A products/BellHybrid/apps/application-bell-focus-timer/models/FocusSettingsModel.cpp
A products/BellHybrid/apps/application-bell-focus-timer/models/FocusSettingsModel.hpp
M products/BellHybrid/apps/application-bell-focus-timer/presenter/FocusSettingsPresenter.cpp
M products/BellHybrid/apps/application-bell-focus-timer/presenter/FocusSettingsPresenter.hpp
M products/BellHybrid/apps/application-bell-focus-timer/presenter/FocusTimerPresenter.cpp
M products/BellHybrid/apps/application-bell-focus-timer/presenter/FocusTimerPresenter.hpp
M products/BellHybrid/apps/application-bell-focus-timer/windows/FocusSettingsWindow.cpp
M products/BellHybrid/apps/application-bell-focus-timer/windows/FocusTimerWindow.cpp
M products/BellHybrid/apps/application-bell-focus-timer/windows/FocusTimerWindow.hpp
M products/BellHybrid/apps/common/include/common/LanguageUtils.hpp
A products/BellHybrid/apps/common/include/common/widgets/list_items/MinutesWithOff.hpp
A products/BellHybrid/apps/common/include/common/widgets/list_items/Multiplicity.hpp
A products/BellHybrid/apps/common/include/common/widgets/list_items/NumericWithOff.hpp
M products/BellHybrid/apps/common/src/LanguageUtils.cpp
M image/system_a/data/lang/Deutsch.json => image/system_a/data/lang/Deutsch.json +8 -3
@@ 29,7 29,10 @@
    "app_bell_charging_done_notification": "Vollst\u00e4ndig aufgeladen",
    "app_bell_charging_notification": "Wird aufgeladen",
    "app_bell_focus_settings": "Einstellungen",
    "app_bell_focus_start": "-",
    "app_bell_focus_start": "Nun konzentriert",
    "app_bell_focus_time": "Konzent.-Timer",
    "app_bell_focus_repeats": "-",
    "app_bell_focus_short_break": "Kurze Pause",
    "app_bell_goodbye": "Auf Wiedersehen",
    "app_bell_greeting_msg": [
        "Guten Morgen! Stehen Sie auf"


@@ 123,8 126,8 @@
    "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_bellmain_alarm": "Alarm",
    "app_bellmain_bedtime": "Schlafenszeit",
    "app_bellmain_focus_timer": "-",
    "app_bellmain_bedtime": "Schlafenzeit",
    "app_bellmain_focus_timer": "Konzent.-Timer",
    "app_bellmain_home_screen_bottom_desc": "Der n\u00e4chste Alarm klingelt",
    "app_bellmain_home_screen_bottom_desc_and": "&",
    "app_bellmain_home_screen_bottom_desc_dp": "Tief dr\u00fccken, um zu aktivieren",


@@ 614,6 617,8 @@
    "common_mo": "MO",
    "common_mon": "Mo",
    "common_monday": "Montag",
    "common_multiplicity_once": "mal",
    "common_multiplicity_many": "mal",
    "common_no": "Nein",
    "common_november": "November",
    "common_october": "Oktober",

M image/system_a/data/lang/English.json => image/system_a/data/lang/English.json +5 -0
@@ 31,6 31,9 @@
    "app_bell_charging_notification": "Charging",
    "app_bell_focus_settings": "Settings",
    "app_bell_focus_start": "Focus now",
    "app_bell_focus_time": "Focus timer",
    "app_bell_focus_repeats": "Focus timer repeats",
    "app_bell_focus_short_break": "Short break",
    "app_bell_goodbye": "Goodbye",
    "app_bell_greeting_msg": [
        "<text>Good Morning!<br />It's a Beautiful Day!</text>",


@@ 617,6 620,8 @@
    "common_mo": "MO",
    "common_mon": "Mon",
    "common_monday": "Monday",
    "common_multiplicity_once": "time",
    "common_multiplicity_many": "times",
    "common_no": "No",
    "common_november": "November",
    "common_october": "October",

M image/system_a/data/lang/Espanol.json => image/system_a/data/lang/Espanol.json +7 -2
@@ 28,7 28,10 @@
    "app_bell_charging_done_notification": "Completamente cargado",
    "app_bell_charging_notification": "Cargando",
    "app_bell_focus_settings": "Ajustes",
    "app_bell_focus_start": "-",
    "app_bell_focus_start": "Concéntrate",
    "app_bell_focus_time": "T. de concentración",
    "app_bell_focus_repeats": "-",
    "app_bell_focus_short_break": "Descanso pequeño",
    "app_bell_goodbye": "Adi\u00f3s",
    "app_bell_greeting_msg": [
        "\u00a1Hola! Lev\u00e1ntate y brilla"


@@ 123,7 126,7 @@
    "app_bell_welcome_message": "<text>Mudita Harmony<br/>est\u00e1 apagado</text>",
    "app_bellmain_alarm": "Alarma",
    "app_bellmain_bedtime": "Hora de dormir",
    "app_bellmain_focus_timer": "-",
    "app_bellmain_focus_timer": "T. de concentración",
    "app_bellmain_home_screen_bottom_desc": "La siguiente alarma sonar\u00e1",
    "app_bellmain_home_screen_bottom_desc_and": "&",
    "app_bellmain_home_screen_bottom_desc_dp": "Presionar a fondo para activar",


@@ 614,6 617,8 @@
    "common_mo": "L",
    "common_mon": "Lu.",
    "common_monday": "Lunes",
    "common_multiplicity_once": "vez",
    "common_multiplicity_many": "veces",
    "common_no": "No",
    "common_november": "Noviembre",
    "common_october": "Octubre",

M image/system_a/data/lang/Francais.json => image/system_a/data/lang/Francais.json +7 -2
@@ 30,7 30,10 @@
    "app_bell_charging_done_notification": "Compl\u00e8tement charg\u00e9",
    "app_bell_charging_notification": "En charge",
    "app_bell_focus_settings": "Param\u00e8tres",
    "app_bell_focus_start": "-",
    "app_bell_focus_start": "Concentration",
    "app_bell_focus_time": "Minuteur concen.",
    "app_bell_focus_repeats": "-",
    "app_bell_focus_short_break": "Petite pause",
    "app_bell_goodbye": "Au revoir",
    "app_bell_greeting_msg": [
        "Re-bonjour!"


@@ 125,7 128,7 @@
    "app_bell_welcome_message": "<text>Mudita Harmony<br/>est d\u00e9sactiv\u00e9</text>",
    "app_bellmain_alarm": "Alarme",
    "app_bellmain_bedtime": "Heure du coucher",
    "app_bellmain_focus_timer": "-",
    "app_bellmain_focus_timer": "Minuteur concen.",
    "app_bellmain_home_screen_bottom_desc": "La prochaine alarme sonnera",
    "app_bellmain_home_screen_bottom_desc_and": "&",
    "app_bellmain_home_screen_bottom_desc_dp": "Appuyer fort pour activer",


@@ 581,6 584,8 @@
    "common_mo": "LU",
    "common_mon": "Lun",
    "common_monday": "Lundi",
    "common_multiplicity_once": "fois",
    "common_multiplicity_many": "fois",
    "common_no": "Non",
    "common_november": "Novembre",
    "common_october": "Octobre",

M image/system_a/data/lang/Polski.json => image/system_a/data/lang/Polski.json +7 -2
@@ 29,7 29,10 @@
    "app_bell_charging_done_notification": "W pe\u0142ni na\u0142adowany",
    "app_bell_charging_notification": "\u0141adowanie",
    "app_bell_focus_settings": "Ustawienia",
    "app_bell_focus_start": "-",
    "app_bell_focus_start": "Skup się",
    "app_bell_focus_time": "Timer skupienia",
    "app_bell_focus_repeats": "-",
    "app_bell_focus_short_break": "Krótka przerwa",
    "app_bell_goodbye": "Do widzenia",
    "app_bell_greeting_msg": [
        "<text>Dzie\u0144 dobry!<br />Pobudka</text>"


@@ 124,7 127,7 @@
    "app_bell_welcome_message": "<text>Mudita Harmony<br/>jest wy\u0142\u0105czony</text>",
    "app_bellmain_alarm": "Alarm",
    "app_bellmain_bedtime": "Pora snu",
    "app_bellmain_focus_timer": "-",
    "app_bellmain_focus_timer": "Timer skupienia",
    "app_bellmain_home_screen_bottom_desc": "Alarm zadzwoni",
    "app_bellmain_home_screen_bottom_desc_and": "i",
    "app_bellmain_home_screen_bottom_desc_dp": "Naci\u015bnij g\u0142\u0119boko, aby aktywowa\u0107",


@@ 609,6 612,8 @@
    "common_mo": "PN",
    "common_mon": "Pon.",
    "common_monday": "Poniedzia\u0142ek",
    "common_multiplicity_once": "raz",
    "common_multiplicity_many": "razy",
    "common_no": "Nie",
    "common_november": "Listopad",
    "common_october": "Pa\u017adziernik",

M products/BellHybrid/apps/application-bell-focus-timer/ApplicationFocusTimer.cpp => products/BellHybrid/apps/application-bell-focus-timer/ApplicationFocusTimer.cpp +18 -3
@@ 11,6 11,8 @@
#include "presenter/FocusSettingsPresenter.hpp"
#include "presenter/FocusTimerPresenter.hpp"

#include "models/FocusSettingsModel.hpp"

#include "common/models/TimeModel.hpp"
#include <common/models/AudioModel.hpp>
#include <common/windows/SessionPausedWindow.hpp>


@@ 42,6 44,13 @@ namespace app
            return ret;
        }

        focusTimeModel = std::make_unique<focus::models::FocusSettingsModel>(
            this, focus::models::settings::focusTimeName, focus::models::settings::focusTimeDefault);
        focusRepeatsModel = std::make_unique<focus::models::FocusSettingsModel>(
            this, focus::models::settings::focusRepeatsName, focus::models::settings::focusRepeatsDefault);
        shortBreakTimeModel = std::make_unique<focus::models::FocusSettingsModel>(
            this, focus::models::settings::shortBreakTimeName, focus::models::settings::shortBreakTimeDefault);

        batteryModel                 = std::make_unique<app::BatteryModel>(this);
        lowBatteryInfoModel          = std::make_unique<app::LowBatteryInfoModel>();
        cpuSentinel                  = std::make_shared<sys::CpuSentinel>(applicationFocusTimerName, this);


@@ 61,14 70,20 @@ namespace app
        });

        windowsFactory.attach(focus::window::name::settings, [this](ApplicationCommon *app, const std::string &name) {
            auto presenter = std::make_unique<app::focus::SettingsPresenter>();
            auto presenter = std::make_unique<app::focus::SettingsPresenter>(
                *focusTimeModel, *focusRepeatsModel, *shortBreakTimeModel);
            return std::make_unique<focus::SettingsWindow>(app, std::move(presenter));
        });

        windowsFactory.attach(focus::window::name::timer, [this](ApplicationCommon *app, const std::string &name) {
            auto timeModel = std::make_unique<app::TimeModel>();
            auto presenter = std::make_unique<app::focus::FocusTimerPresenter>(
                app, settings.get(), std::move(timeModel), *batteryModel, *lowBatteryInfoModel);
            auto presenter = std::make_unique<app::focus::FocusTimerPresenter>(app,
                                                                               *focusTimeModel,
                                                                               *focusRepeatsModel,
                                                                               *shortBreakTimeModel,
                                                                               std::move(timeModel),
                                                                               *batteryModel,
                                                                               *lowBatteryInfoModel);
            return std::make_unique<focus::FocusTimerWindow>(app, std::move(presenter));
        });


M products/BellHybrid/apps/application-bell-focus-timer/CMakeLists.txt => products/BellHybrid/apps/application-bell-focus-timer/CMakeLists.txt +1 -0
@@ 26,6 26,7 @@ target_sources(application-bell-focus-timer
        windows/FocusTimerWindow.cpp
        presenter/FocusSettingsPresenter.cpp
        presenter/FocusTimerPresenter.cpp
        models/FocusSettingsModel.cpp

    PUBLIC
        include/application-bell-focus-timer/ApplicationFocusTimer.hpp

M products/BellHybrid/apps/application-bell-focus-timer/data/FocusCommon.hpp => products/BellHybrid/apps/application-bell-focus-timer/data/FocusCommon.hpp +20 -2
@@ 10,8 10,6 @@

namespace app::focus
{
    inline constexpr auto focusDBRecordName = "FocusTimer";

    namespace window::name
    {
        inline constexpr auto main     = gui::name::window::main_window;


@@ 24,4 22,24 @@ namespace app::focus
        return paths::audio::proprietary() / paths::audio::focusTimer() / "FocusTimer_Gong.mp3";
    }

    namespace models::settings
    {
        inline constexpr auto focusTimeMin     = 5U;
        inline constexpr auto focusTimeMax     = 60U;
        inline constexpr auto focusTimeStep    = 5U;
        inline constexpr auto focusTimeDefault = 25U;
        inline constexpr auto focusTimeName    = "focus_time";

        inline constexpr auto focusRepeatsMin     = 1U;
        inline constexpr auto focusRepeatsMax     = 19U;
        inline constexpr auto focusRepeatsStep    = 1U;
        inline constexpr auto focusRepeatsDefault = 10U;
        inline constexpr auto focusRepeatsName    = "focus_repeats";

        inline constexpr auto shortBreakTimeMin     = 0U;
        inline constexpr auto shortBreakTimeMax     = 15U;
        inline constexpr auto shortBreakTimeStep    = 1U;
        inline constexpr auto shortBreakTimeDefault = 15U;
        inline constexpr auto shortBreakTimeName    = "short_break";
    } // namespace models::settings
} // namespace app::focus

M products/BellHybrid/apps/application-bell-focus-timer/include/application-bell-focus-timer/ApplicationFocusTimer.hpp => products/BellHybrid/apps/application-bell-focus-timer/include/application-bell-focus-timer/ApplicationFocusTimer.hpp +9 -0
@@ 8,6 8,11 @@
#include <common/models/BatteryModel.hpp>
#include <common/models/LowBatteryInfoModel.hpp>

namespace app::focus::models
{
    class FocusSettingsModel;
} // namespace app::focus::models

namespace app
{
    inline constexpr auto applicationFocusTimerName      = "ApplicationFocusTimer";


@@ 36,6 41,10 @@ namespace app
        }

      private:
        std::unique_ptr<focus::models::FocusSettingsModel> focusTimeModel;
        std::unique_ptr<focus::models::FocusSettingsModel> focusRepeatsModel;
        std::unique_ptr<focus::models::FocusSettingsModel> shortBreakTimeModel;

        std::unique_ptr<AbstractAudioModel> audioModel;
        std::unique_ptr<AbstractBatteryModel> batteryModel;
        std::unique_ptr<AbstractLowBatteryInfoModel> lowBatteryInfoModel;

A products/BellHybrid/apps/application-bell-focus-timer/models/FocusSettingsModel.cpp => products/BellHybrid/apps/application-bell-focus-timer/models/FocusSettingsModel.cpp +25 -0
@@ 0,0 1,25 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "FocusSettingsModel.hpp"
#include <Utils.hpp>

namespace app::focus::models
{
    FocusSettingsModel::FocusSettingsModel(sys::Service *app, const std::string &name, std::uint8_t defaultValue)
        : SettingsModel{app}, name{name}, defaultValue{defaultValue}
    {}

    void FocusSettingsModel::setValue(std::uint8_t value)
    {
        settings.setValue(name, std::to_string(value));
    }
    std::uint8_t FocusSettingsModel::getValue() const
    {
        const auto &value = settings.getValue(name);
        if (value.empty()) {
            return defaultValue;
        }
        return utils::getNumericValue<std::uint8_t>(value);
    }
} // namespace app::focus::models

A products/BellHybrid/apps/application-bell-focus-timer/models/FocusSettingsModel.hpp => products/BellHybrid/apps/application-bell-focus-timer/models/FocusSettingsModel.hpp +24 -0
@@ 0,0 1,24 @@
// 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 <common/models/SettingsModel.hpp>

namespace app::focus::models
{
    class FocusSettingsModel : public gui::SettingsModel<std::uint8_t>
    {
      public:
        using SettingsModel::SettingsModel;

        FocusSettingsModel(sys::Service *app, const std::string &name, std::uint8_t defaultValue);

        void setValue(std::uint8_t value) override;
        std::uint8_t getValue() const override;

      private:
        const std::string name;
        const std::uint8_t defaultValue;
    };
} // namespace app::focus::models

M products/BellHybrid/apps/application-bell-focus-timer/presenter/FocusSettingsPresenter.cpp => products/BellHybrid/apps/application-bell-focus-timer/presenter/FocusSettingsPresenter.cpp +35 -2
@@ 2,16 2,49 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "FocusSettingsPresenter.hpp"
#include "data/FocusCommon.hpp"

#include "models/FocusSettingsModel.hpp"

#include <ApplicationCommon.hpp>
#include <apps-common/InternalModel.hpp>
#include <common/widgets/list_items/Numeric.hpp>
#include <common/widgets/list_items/MinutesWithOff.hpp>
#include <common/widgets/list_items/Multiplicity.hpp>

namespace app::focus
{
    using namespace gui;
    SettingsPresenter::SettingsPresenter()
    SettingsPresenter::SettingsPresenter(models::FocusSettingsModel &focusTimeModel,
                                         models::FocusSettingsModel &focusRepeatsModel,
                                         models::FocusSettingsModel &shortBreakTimeModel)
        : focusTimeModel{focusTimeModel}, focusRepeatsModel{focusRepeatsModel}, shortBreakTimeModel{shortBreakTimeModel}
    {
        listItemsProvider = std::make_shared<BellListItemProvider>(BellListItemProvider::Items{});
        auto focusTime = new list_items::Numeric{
            list_items::Numeric::spinner_type::range{
                models::settings::focusTimeMin, models::settings::focusTimeMax, models::settings::focusTimeStep},
            focusTimeModel,
            utils::translate("app_bell_focus_time"),
            utils::translate("common_minutes_lower_genitive")};

        auto focusRepeats = new list_items::Multiplicity{
            list_items::Multiplicity::spinner_type::range{models::settings::focusRepeatsMin,
                                                          models::settings::focusRepeatsMax,
                                                          models::settings::focusRepeatsStep},
            focusRepeatsModel,
            utils::translate("app_bell_focus_repeats"),
            utils::translate("common_multiplicity_many")};

        auto shortBreakTime = new list_items::MinutesWithOff{
            list_items::MinutesWithOff::spinner_type::range{models::settings::shortBreakTimeMin,
                                                            models::settings::shortBreakTimeMax,
                                                            models::settings::shortBreakTimeStep},
            shortBreakTimeModel,
            utils::translate("app_bell_focus_short_break"),
            utils::translate("common_minutes_lower_genitive")};

        listItemsProvider = std::make_shared<BellListItemProvider>(
            BellListItemProvider::Items{focusTime, focusRepeats, shortBreakTime});

        for (auto &item : listItemsProvider->getListItems()) {
            item->setValue();

M products/BellHybrid/apps/application-bell-focus-timer/presenter/FocusSettingsPresenter.hpp => products/BellHybrid/apps/application-bell-focus-timer/presenter/FocusSettingsPresenter.hpp +11 -1
@@ 14,6 14,11 @@ namespace app

namespace app::focus
{
    namespace models
    {
        class FocusSettingsModel;
    } // namespace models

    class SettingsContract
    {
      public:


@@ 39,7 44,9 @@ namespace app::focus
    class SettingsPresenter : public SettingsContract::Presenter
    {
      public:
        SettingsPresenter();
        SettingsPresenter(models::FocusSettingsModel &focusTimeModel,
                          models::FocusSettingsModel &focusRepeatsModel,
                          models::FocusSettingsModel &shortBreakTimeModel);

        void loadData() override;
        void saveData() override;


@@ 49,6 56,9 @@ namespace app::focus
        void exitWithoutSave() override;

      private:
        models::FocusSettingsModel &focusTimeModel;
        models::FocusSettingsModel &focusRepeatsModel;
        models::FocusSettingsModel &shortBreakTimeModel;
        std::shared_ptr<BellListItemProvider> listItemsProvider;
    };
} // namespace app::focus

M products/BellHybrid/apps/application-bell-focus-timer/presenter/FocusTimerPresenter.cpp => products/BellHybrid/apps/application-bell-focus-timer/presenter/FocusTimerPresenter.cpp +45 -39
@@ 2,20 2,18 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "FocusTimerPresenter.hpp"

#include "FocusCommon.hpp"
#include "models/FocusSettingsModel.hpp"
#include "Application.hpp"
#include "widgets/ProgressTimer.hpp"

#include <common/LanguageUtils.hpp>
#include <common/models/TimeModel.hpp>
#include <common/windows/BellFinishedWindow.hpp>
#include <service-db/Settings.hpp>
#include <common/LanguageUtils.hpp>

namespace
{
    constexpr std::chrono::milliseconds betweenSessionDelayTime{5000};
    constexpr auto summaryWindowTimeout = std::chrono::seconds{5};
    constexpr std::chrono::seconds delayBetweenSessions{5};
    constexpr std::chrono::seconds summaryWindowTimeout{5};

    std::string createSummaryText(const std::string &str, const std::string &minutesOfFocus)
    {


@@ 30,31 28,23 @@ namespace app::focus
{

    FocusTimerPresenter::FocusTimerPresenter(app::ApplicationCommon *app,
                                             settings::Settings *settings,
                                             models::FocusSettingsModel &focusTimeModel,
                                             models::FocusSettingsModel &focusRepeatsModel,
                                             models::FocusSettingsModel &shortBreakTimeModel,
                                             std::unique_ptr<AbstractTimeModel> timeModel,
                                             AbstractBatteryModel &batteryModel,
                                             AbstractLowBatteryInfoModel &lowBatteryInfoModel)
        : app{app}, settings{settings}, batteryModel{batteryModel},
        : app{app}, focusTimeModel{focusTimeModel}, focusRepeatsModel{focusRepeatsModel},
          shortBreakTimeModel{shortBreakTimeModel}, batteryModel{batteryModel},
          lowBatteryInfoModel{lowBatteryInfoModel}, timeModel{std::move(timeModel)}
    {
        //        focusSessionDuration = std::chrono::minutes{
        //                utils::getNumericValue<int>(settings->getValue(focusDBRecordName,
        //                settings::SettingsScope::AppLocal))};
        //        shortBreakDuration = std::chrono::minutes{
        //                utils::getNumericValue<int>(settings->getValue(focusDBRecordName,
        //                settings::SettingsScope::AppLocal))};
        //        focusSessionsLeft =
        //                utils::getNumericValue<int>(settings->getValue(focusDBRecordName,
        //                settings::SettingsScope::AppLocal));

        focusSessionDuration = std::chrono::minutes{25};
        shortBreakDuration   = std::chrono::minutes{5};

        allFocusSessionsCount = 10;
        focusSessionDuration  = std::chrono::minutes{focusTimeModel.getValue()};
        shortBreakDuration    = std::chrono::minutes{shortBreakTimeModel.getValue()};
        allFocusSessionsCount = focusRepeatsModel.getValue();
        focusSessionsLeft     = allFocusSessionsCount;

        betweenSessionTimer = sys::TimerFactory::createSingleShotTimer(
            app, "betweenSessionTimer", betweenSessionDelayTime, [this](sys::Timer &) { executeNextStep(); });
            app, "betweenSessionTimer", delayBetweenSessions, [this](sys::Timer &) { executeNextStep(); });
    }

    void FocusTimerPresenter::setTimer(std::unique_ptr<app::TimerWithCallbacks> &&_timer)


@@ 134,33 124,24 @@ namespace app::focus
        switch (currentTimerPhase) {
        case FocusTimerPhase::FocusTime:
            focusSessionsLeft--;
            if (focusSessionsLeft == 0) {
                currentTimerPhase = FocusTimerPhase::AllFocusSessionsEnded;
                getView()->onAllFocusSessionsFinished();
                startTime();
            }
            else {
                currentTimerPhase = FocusTimerPhase::FocusTimeEnded;
                getView()->onFocusSessionFinished();
                startTime();
            }
            currentTimerPhase = handleInfoAfterFocusPhase();
            startTime();
            break;

        case FocusTimerPhase::FocusTimeEnded:
            currentTimerPhase = FocusTimerPhase::ShortBreakTime;
            getView()->onShortBreakStarted();
            currentTimerPhase = handleCountdownAfterFocusPhase();
            startTime();
            break;

        case FocusTimerPhase::ShortBreakTime:
            currentTimerPhase = FocusTimerPhase::ShortBreakTimeEnded;
            getView()->onShortBreakFinished();
            getView()->showTimeForFocusInfo();
            startTime();
            break;

        case FocusTimerPhase::ShortBreakTimeEnded:
            currentTimerPhase = FocusTimerPhase::FocusTime;
            getView()->onFocusSessionStarted();
            getView()->showFocusSessionCountdown();
            startTime();
            break;



@@ 179,12 160,12 @@ namespace app::focus
    {
        switch (currentTimerPhase) {
        case FocusTimerPhase::FocusTime:
            timer->reset(std::chrono::minutes(focusSessionDuration));
            timer->reset(focusSessionDuration);
            timer->start();
            break;

        case FocusTimerPhase::ShortBreakTime:
            timer->reset(std::chrono::minutes(shortBreakDuration));
            timer->reset(shortBreakDuration);
            timer->start();
            break;



@@ 207,4 188,29 @@ namespace app::focus
        }
    }

    FocusTimerPresenter::FocusTimerPhase FocusTimerPresenter::handleInfoAfterFocusPhase()
    {
        if (focusSessionsLeft == 0) {
            getView()->showEndOfAllSessionsInfo();
            return FocusTimerPhase::AllFocusSessionsEnded;
        }
        if (shortBreakDuration.count() > 0) {
            getView()->showTimeForBreakInfo();
        }
        else {
            getView()->showTimeForFocusInfo();
        }
        return FocusTimerPhase::FocusTimeEnded;
    }

    FocusTimerPresenter::FocusTimerPhase FocusTimerPresenter::handleCountdownAfterFocusPhase()
    {
        if (shortBreakDuration.count() > 0) {
            getView()->showShortBreakCountdown();
            return FocusTimerPhase::ShortBreakTime;
        }
        getView()->showFocusSessionCountdown();
        return FocusTimerPhase::FocusTime;
    }

} // namespace app::focus

M products/BellHybrid/apps/application-bell-focus-timer/presenter/FocusTimerPresenter.hpp => products/BellHybrid/apps/application-bell-focus-timer/presenter/FocusTimerPresenter.hpp +29 -17
@@ 9,7 9,6 @@
#include <common/models/LowBatteryInfoModel.hpp>
#include <apps-common/widgets/TimerWithCallbacks.hpp>
#include <time/time_locale.hpp>
#include "widgets/ProgressTimer.hpp"

namespace app
{


@@ 19,18 18,24 @@ namespace app

namespace app::focus
{
    namespace models
    {
        class FocusSettingsModel;
    } // namespace models

    class FocusTimerContract
    {
      public:
        class View
        {
          public:
            virtual ~View() noexcept                                        = default;
            virtual void onAllFocusSessionsFinished()                       = 0;
            virtual void onFocusSessionStarted()                            = 0;
            virtual void onFocusSessionFinished()                           = 0;
            virtual void onShortBreakStarted()                              = 0;
            virtual void onShortBreakFinished()                             = 0;
            virtual ~View() noexcept = default;

            virtual void showFocusSessionCountdown()                        = 0;
            virtual void showShortBreakCountdown()                          = 0;
            virtual void showTimeForFocusInfo()                             = 0;
            virtual void showTimeForBreakInfo()                             = 0;
            virtual void showEndOfAllSessionsInfo()                         = 0;
            virtual void setTime(std::time_t newTime)                       = 0;
            virtual void setTimeFormat(utils::time::Locale::TimeFormat fmt) = 0;
            virtual void pause()                                            = 0;


@@ 58,16 63,10 @@ namespace app::focus
    class FocusTimerPresenter : public FocusTimerContract::Presenter
    {
      public:
        enum class FocusTimerPhase
        {
            FocusTime,
            FocusTimeEnded,
            ShortBreakTime,
            ShortBreakTimeEnded,
            AllFocusSessionsEnded
        };
        FocusTimerPresenter(app::ApplicationCommon *app,
                            settings::Settings *settings,
                            models::FocusSettingsModel &focusTimeModel,
                            models::FocusSettingsModel &focusRepeatsModel,
                            models::FocusSettingsModel &shortBreakTimeModel,
                            std::unique_ptr<AbstractTimeModel> timeModel,
                            AbstractBatteryModel &batteryModel,
                            AbstractLowBatteryInfoModel &lowBatteryInfoModel);


@@ 86,8 85,19 @@ namespace app::focus
        void startTime();

      private:
        enum class FocusTimerPhase
        {
            FocusTime,
            FocusTimeEnded,
            ShortBreakTime,
            ShortBreakTimeEnded,
            AllFocusSessionsEnded
        };

        app::ApplicationCommon *app{nullptr};
        settings::Settings *settings{nullptr};
        models::FocusSettingsModel &focusTimeModel;
        models::FocusSettingsModel &focusRepeatsModel;
        models::FocusSettingsModel &shortBreakTimeModel;
        AbstractBatteryModel &batteryModel;
        AbstractLowBatteryInfoModel &lowBatteryInfoModel;



@@ 102,5 112,7 @@ namespace app::focus

        void executeNextStep();
        bool isMiddleTimeBetweenBreakAndFocus();
        FocusTimerPhase handleInfoAfterFocusPhase();
        FocusTimerPhase handleCountdownAfterFocusPhase();
    };
} // namespace app::focus

M products/BellHybrid/apps/application-bell-focus-timer/windows/FocusSettingsWindow.cpp => products/BellHybrid/apps/application-bell-focus-timer/windows/FocusSettingsWindow.cpp +3 -1
@@ 67,7 67,9 @@ namespace app::focus
    }

    void SettingsWindow::switchToExitWindow()
    {}
    {
        application->switchWindow(window::name::main);
    }

    void SettingsWindow::onClose(const CloseReason reason)
    {

M products/BellHybrid/apps/application-bell-focus-timer/windows/FocusTimerWindow.cpp => products/BellHybrid/apps/application-bell-focus-timer/windows/FocusTimerWindow.cpp +5 -5
@@ 146,7 146,7 @@ namespace app::focus
        return AppWindow::onInput(inputEvent);
    }

    void FocusTimerWindow::onAllFocusSessionsFinished()
    void FocusTimerWindow::showEndOfAllSessionsInfo()
    {
        timer->setVisible(false);
        iconPause->setVisible(false);


@@ 157,7 157,7 @@ namespace app::focus
        playGong();
    }

    void FocusTimerWindow::onFocusSessionStarted()
    void FocusTimerWindow::showFocusSessionCountdown()
    {
        timer->setVisible(true);
        iconPause->setVisible(false);


@@ 167,7 167,7 @@ namespace app::focus
        mainVBox->resizeItems();
    }

    void FocusTimerWindow::onFocusSessionFinished()
    void FocusTimerWindow::showTimeForBreakInfo()
    {
        timer->setVisible(false);
        iconPause->setVisible(false);


@@ 178,7 178,7 @@ namespace app::focus
        playGong();
    }

    void FocusTimerWindow::onShortBreakStarted()
    void FocusTimerWindow::showShortBreakCountdown()
    {
        timer->setVisible(true);
        iconPause->setVisible(false);


@@ 188,7 188,7 @@ namespace app::focus
        mainVBox->resizeItems();
    }

    void FocusTimerWindow::onShortBreakFinished()
    void FocusTimerWindow::showTimeForFocusInfo()
    {
        timer->setVisible(false);
        iconPause->setVisible(false);

M products/BellHybrid/apps/application-bell-focus-timer/windows/FocusTimerWindow.hpp => products/BellHybrid/apps/application-bell-focus-timer/windows/FocusTimerWindow.hpp +5 -5
@@ 25,11 25,11 @@ namespace app::focus
        void onBeforeShow(gui::ShowMode mode, gui::SwitchData *data) override;
        void buildInterface() override;
        bool onInput(const gui::InputEvent &inputEvent) override;
        void onAllFocusSessionsFinished() override;
        void onFocusSessionStarted() override;
        void onFocusSessionFinished() override;
        void onShortBreakStarted() override;
        void onShortBreakFinished() override;
        void showFocusSessionCountdown() override;
        void showShortBreakCountdown() override;
        void showTimeForFocusInfo() override;
        void showTimeForBreakInfo() override;
        void showEndOfAllSessionsInfo() override;
        void pause() override;
        void resume() override;


M products/BellHybrid/apps/common/include/common/LanguageUtils.hpp => products/BellHybrid/apps/common/include/common/LanguageUtils.hpp +3 -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

#pragma once


@@ 11,4 11,6 @@ namespace utils::language
    auto getCorrectSecondsNumeralForm(std::uint32_t val) -> std::string;

    auto getCorrectMinutesAccusativeForm(std::uint32_t val) -> std::string;

    auto getCorrectMultiplicityForm(std::uint32_t val) -> std::string;
} // namespace utils::language

A products/BellHybrid/apps/common/include/common/widgets/list_items/MinutesWithOff.hpp => products/BellHybrid/apps/common/include/common/widgets/list_items/MinutesWithOff.hpp +28 -0
@@ 0,0 1,28 @@
// 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 "NumericWithOff.hpp"
#include <common/LanguageUtils.hpp>

namespace app::list_items
{
    class MinutesWithOff : public NumericWithOff
    {
      public:
        MinutesWithOff(spinner_type::range &&range,
                       gui::AbstractSettingsModel<spinner_type::value_type> &model,
                       const std::string &topDescription    = "",
                       const std::string &bottomDescription = "")
            : NumericWithOff{std::move(range), model, topDescription, bottomDescription}
        {}

      private:
        void control_bottom_description(const spinner_type::value_type &value) final
        {
            bottomText->setVisible(value != 0);
            bottomText->setRichText(utils::language::getCorrectMinutesAccusativeForm(value));
        }
    };
} // namespace app::list_items

A products/BellHybrid/apps/common/include/common/widgets/list_items/Multiplicity.hpp => products/BellHybrid/apps/common/include/common/widgets/list_items/Multiplicity.hpp +27 -0
@@ 0,0 1,27 @@
// 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 "Numeric.hpp"
#include <common/LanguageUtils.hpp>

namespace app::list_items
{
    class Multiplicity : public Numeric
    {
      public:
        Multiplicity(spinner_type::range &&range,
                     gui::AbstractSettingsModel<spinner_type::value_type> &model,
                     const std::string &topDescription    = "",
                     const std::string &bottomDescription = "")
            : Numeric{std::move(range), model, topDescription, bottomDescription}
        {}

      private:
        void control_bottom_description(const spinner_type::value_type &value) final
        {
            bottomText->setRichText(utils::language::getCorrectMultiplicityForm(value));
        }
    };
} // namespace app::list_items

A products/BellHybrid/apps/common/include/common/widgets/list_items/NumericWithOff.hpp => products/BellHybrid/apps/common/include/common/widgets/list_items/NumericWithOff.hpp +43 -0
@@ 0,0 1,43 @@
// 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 "details.hpp"
#include <i18n/i18n.hpp>
#include <utility>

namespace app::list_items
{
    struct NumericWithOffFormatter
    {
      public:
        NumericWithOffFormatter() : off(utils::translate("app_settings_toggle_off"))
        {}
        std::string operator()(const std::uint8_t &value) const
        {
            return (value == 0) ? off : std::to_string(value);
        }

      private:
        std::string off;
    };

    using NumericWithOffSpinner = gui::U8IntegerSpinnerWithFormatter<NumericWithOffFormatter>;
    class NumericWithOff : public details::ListItemBase<NumericWithOffSpinner>
    {
      public:
        NumericWithOff(spinner_type::range &&range,
                       gui::AbstractSettingsModel<spinner_type::value_type> &model,
                       const std::string &topDescription    = "",
                       const std::string &bottomDescription = "")
            : details::ListItemBase<spinner_type>(std::move(range), model, topDescription, bottomDescription)
        {}

      private:
        void control_bottom_description(const spinner_type::value_type &value) override
        {
            bottomText->setVisible(value != 0);
        }
    };
} // namespace app::list_items

M products/BellHybrid/apps/common/src/LanguageUtils.cpp => products/BellHybrid/apps/common/src/LanguageUtils.cpp +12 -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 <i18n/i18n.hpp>


@@ 38,6 38,11 @@ namespace
            return transformNumeral(val, minuteLower, minutesLower, minutesLowerGenitive);
        }
    }

    std::string transformMultiplicity(const std::uint32_t val, const std::string &once, const std::string &many)
    {
        return (val == 1) ? once : many;
    }
} // namespace

namespace utils::language


@@ 66,4 71,10 @@ namespace utils::language
                                   utils::translate("common_minutes_lower"),
                                   utils::translate("common_minutes_lower_genitive"));
    }

    auto getCorrectMultiplicityForm(std::uint32_t val) -> std::string
    {
        return transformMultiplicity(
            val, utils::translate("common_multiplicity_once"), utils::translate("common_multiplicity_many"));
    }
} // namespace utils::language