~aleteoryx/muditaos

4a792563da5a05e3fa17f073d295ea69bd8fdc5c — ChunjianLee 4 years ago 8471b6a
[BH-453] Add Meditation Timer application

Add Meditation Timer application implementation provided by ATEL.
33 files changed, 1537 insertions(+), 21 deletions(-)

M image/assets/lang/English.json
M products/BellHybrid/BellHybridMain.cpp
M products/BellHybrid/CMakeLists.txt
M products/BellHybrid/apps/CMakeLists.txt
M products/BellHybrid/apps/application-bell-main/CMakeLists.txt
M products/BellHybrid/apps/application-bell-main/windows/BellMainMenuWindow.cpp
A products/BellHybrid/apps/application-bell-meditation-timer/ApplicationBellMeditationTimer.cpp
A products/BellHybrid/apps/application-bell-meditation-timer/CMakeLists.txt
A products/BellHybrid/apps/application-bell-meditation-timer/data/MeditationCommon.hpp
A products/BellHybrid/apps/application-bell-meditation-timer/data/MeditationStyle.hpp
A products/BellHybrid/apps/application-bell-meditation-timer/include/application-bell-meditation-timer/ApplicationBellMeditationTimer.hpp
A products/BellHybrid/apps/application-bell-meditation-timer/presenter/IntervalChimePresenter.cpp
A products/BellHybrid/apps/application-bell-meditation-timer/presenter/IntervalChimePresenter.hpp
A products/BellHybrid/apps/application-bell-meditation-timer/presenter/MeditationProgressPresenter.cpp
A products/BellHybrid/apps/application-bell-meditation-timer/presenter/MeditationProgressPresenter.hpp
A products/BellHybrid/apps/application-bell-meditation-timer/presenter/MeditationTimerPresenter.cpp
A products/BellHybrid/apps/application-bell-meditation-timer/presenter/MeditationTimerPresenter.hpp
A products/BellHybrid/apps/application-bell-meditation-timer/presenter/ReadyGoingPresenter.cpp
A products/BellHybrid/apps/application-bell-meditation-timer/presenter/ReadyGoingPresenter.hpp
A products/BellHybrid/apps/application-bell-meditation-timer/presenter/SessionEndedPresenter.cpp
A products/BellHybrid/apps/application-bell-meditation-timer/presenter/SessionEndedPresenter.hpp
A products/BellHybrid/apps/application-bell-meditation-timer/windows/IntervalChimeWindow.cpp
A products/BellHybrid/apps/application-bell-meditation-timer/windows/IntervalChimeWindow.hpp
A products/BellHybrid/apps/application-bell-meditation-timer/windows/MeditationRunningWindow.cpp
A products/BellHybrid/apps/application-bell-meditation-timer/windows/MeditationRunningWindow.hpp
A products/BellHybrid/apps/application-bell-meditation-timer/windows/MeditationTimerWindow.cpp
A products/BellHybrid/apps/application-bell-meditation-timer/windows/MeditationTimerWindow.hpp
A products/BellHybrid/apps/application-bell-meditation-timer/windows/ReadyGoingWindow.cpp
A products/BellHybrid/apps/application-bell-meditation-timer/windows/ReadyGoingWindow.hpp
A products/BellHybrid/apps/application-bell-meditation-timer/windows/SessionEndWindow.cpp
A products/BellHybrid/apps/application-bell-meditation-timer/windows/SessionEndWindow.hpp
A products/BellHybrid/apps/application-bell-meditation-timer/windows/SessionPausedWindow.cpp
A products/BellHybrid/apps/application-bell-meditation-timer/windows/SessionPausedWindow.hpp
M image/assets/lang/English.json => image/assets/lang/English.json +10 -0
@@ 596,6 596,16 @@
  "app_bellmain_background_sounds": "Library",
  "app_bellmain_settings": "Settings",
  "app_bellmain_main_window_title": "Bell Hybrid+",
  "app_bell_meditation_timer": "Meditation",
  "app_bell_meditation_interval_chime": "Interval chime",
  "app_bell_meditation_progress": "Meditation timer",
  "app_bell_meditation_interval_none": "None",
  "app_bell_meditation_interval_every_x_minutes": "every %0",
  "app_bell_meditation_put_down_and_wait": "<text>Put down Bell Harmony<br>and wait for the gong</text>",
  "app_bell_meditation_thank_you_for_session": "<text>Thank you for<br>the session</text>",
  "app_bell_meditation_minute": "minute",
  "app_bell_meditation_minutes": "minutes",
  "app_bell_meditation_paused": "<text>Paused</text>",
  "app_bell_onboarding_welcome_message": "Mudita Harmony",
  "app_bell_onboarding_info_rotate": "<text font='gt_pressura' weight='regular' size='38'>Rotate </text><text font='gt_pressura' weight='light' size='38'>to select</text>",
  "app_bell_onboarding_info_light_click": "<text font='gt_pressura' weight='regular' size='38'>Light click </text><text font='gt_pressura' weight='light' size='38'>to continue</text>",

M products/BellHybrid/BellHybridMain.cpp => products/BellHybrid/BellHybridMain.cpp +3 -0
@@ 9,6 9,7 @@
#include <application-bell-background-sounds/ApplicationBellBackgroundSounds.hpp>
#include <application-bell-bedtime/ApplicationBellBedtime.hpp>
#include <application-bell-main/ApplicationBellMain.hpp>
#include <application-bell-meditation-timer/ApplicationBellMeditationTimer.hpp>
#include <application-bell-settings/ApplicationBellSettings.hpp>
#include <application-bell-powernap/ApplicationBellPowerNap.hpp>



@@ 104,6 105,8 @@ int main()
                app::CreateLauncher<app::ApplicationBellOnBoarding>(app::applicationBellOnBoardingName));
            applications.push_back(
                app::CreateLauncher<app::ApplicationBellBackgroundSounds>(app::applicationBellBackgroundSoundsName));
            applications.push_back(
                app::CreateLauncher<app::ApplicationBellMeditationTimer>(app::applicationBellMeditationTimerName));
            // start application manager
            return sysmgr->RunSystemService(
                std::make_shared<app::manager::ApplicationManager>(

M products/BellHybrid/CMakeLists.txt => products/BellHybrid/CMakeLists.txt +1 -0
@@ 44,6 44,7 @@ target_link_libraries(BellHybrid
        bell::app-bedtime
        bell::app-common
        bell::app-main
        bell::app-meditation-timer
        bell::app-powernap
        bell::app-settings
        bell::db

M products/BellHybrid/apps/CMakeLists.txt => products/BellHybrid/apps/CMakeLists.txt +1 -0
@@ 25,6 25,7 @@ add_subdirectory(application-bell-onboarding)
add_subdirectory(application-bell-background-sounds)
add_subdirectory(application-bell-alarm)
add_subdirectory(application-bell-bedtime)
add_subdirectory(application-bell-meditation-timer)
add_subdirectory(application-bell-settings)
add_subdirectory(application-bell-powernap)
add_subdirectory(common)

M products/BellHybrid/apps/application-bell-main/CMakeLists.txt => products/BellHybrid/apps/application-bell-main/CMakeLists.txt +1 -0
@@ 58,6 58,7 @@ target_link_libraries(application-bell-main
        bell::app-onboarding
        bell::app-background-sounds
        bell::app-bedtime
        bell::app-meditation-timer
        bell::app-settings
        bell::app-powernap
        bell::keymap

M products/BellHybrid/apps/application-bell-main/windows/BellMainMenuWindow.cpp => products/BellHybrid/apps/application-bell-main/windows/BellMainMenuWindow.cpp +2 -21
@@ 7,6 7,7 @@
#include <application-bell-background-sounds/ApplicationBellBackgroundSounds.hpp>
#include <application-bell-bedtime/ApplicationBellBedtime.hpp>
#include <application-bell-main/ApplicationBellMain.hpp>
#include <application-bell-meditation-timer/ApplicationBellMeditationTimer.hpp>
#include <application-bell-settings/ApplicationBellSettings.hpp>
#include <application-bell-powernap/ApplicationBellPowerNap.hpp>
#include <data/BellMainStyle.hpp>


@@ 28,26 29,6 @@ namespace gui
    {
        std::list<gui::Option> menuOptionList;

        auto addWinMenu = [&](const UTF8 &name, const std::string &window) {
            menuOptionList.emplace_back(std::make_unique<gui::option::OptionBellMenu>(
                name,
                [=](gui::Item &item) {
                    if (window.empty()) {
                        return false;
                    }
                    application->switchWindow(
                        window,
                        gui::ShowMode::GUI_SHOW_INIT,
                        std::make_unique<gui::DialogMetadataMessage>(gui::DialogMetadata{name, "search_big", " "}));
                    return true;
                },
                [=](gui::Item &item) {
                    // put focus change callback here
                    return true;
                },
                this));
        };

        auto addAppMenu = [&](const UTF8 &name, const std::string &appName) {
            menuOptionList.emplace_back(std::make_unique<gui::option::OptionBellMenu>(
                name,


@@ 70,7 51,7 @@ namespace gui
        addAppMenu(utils::translate("app_bellmain_power_nap"), app::applicationBellPowerNapName);
        addAppMenu(utils::translate("app_bellmain_background_sounds"), app::applicationBellBackgroundSoundsName);
        // for demo only - to be replaced by call o final window
        addWinMenu(utils::translate("app_bellmain_meditation_timer"), gui::window::name::bell_main_menu_dialog);
        addAppMenu(utils::translate("app_bellmain_meditation_timer"), app::applicationBellMeditationTimerName);
        addAppMenu(utils::translate("app_bellmain_bedtime"), app::applicationBellBedtimeName);

        addAppMenu(utils::translate("app_bellmain_settings"), app::applicationBellSettingsName);

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

#include "ApplicationBellMeditationTimer.hpp"
#include "windows/IntervalChimeWindow.hpp"
#include "windows/MeditationRunningWindow.hpp"
#include "windows/MeditationTimerWindow.hpp"
#include "windows/ReadyGoingWindow.hpp"
#include "windows/SessionEndWindow.hpp"
#include "windows/SessionPausedWindow.hpp"

#include <common/models/TimeModel.hpp>

namespace app
{
    ApplicationBellMeditationTimer::ApplicationBellMeditationTimer(std::string name,
                                                                   std::string parent,
                                                                   StatusIndicators statusIndicators,
                                                                   StartInBackground startInBackground)
        : Application(std::move(name), std::move(parent), statusIndicators, startInBackground)
    {}

    ApplicationBellMeditationTimer::~ApplicationBellMeditationTimer() = default;

    sys::ReturnCodes ApplicationBellMeditationTimer::InitHandler()
    {
        auto ret = Application::InitHandler();
        if (ret != sys::ReturnCodes::Success) {
            return ret;
        }

        createUserInterface();

        return sys::ReturnCodes::Success;
    }

    void ApplicationBellMeditationTimer::createUserInterface()
    {
        windowsFactory.attach(gui::name::window::main_window, [this](ApplicationCommon *app, const std::string &name) {
            auto presenter = std::make_unique<app::meditation::MeditationTimerPresenter>(app, settings.get());
            return std::make_unique<gui::MeditationTimerWindow>(app, std::move(presenter));
        });
        windowsFactory.attach(
            gui::name::window::intervalChime, [this](ApplicationCommon *app, const std::string &name) {
                auto presenter = std::make_unique<app::meditation::IntervalChimePresenter>(app, settings.get());
                return std::make_unique<gui::IntervalChimeWindow>(app, std::move(presenter));
            });
        windowsFactory.attach(gui::name::window::readyGoing, [](ApplicationCommon *app, const std::string &name) {
            auto presenter = std::make_unique<app::meditation::ReadyGoingPresenter>(app);
            return std::make_unique<gui::ReadyGoingWindow>(app, std::move(presenter));
        });
        windowsFactory.attach(gui::name::window::meditationProgress,
                              [this](ApplicationCommon *app, const std::string &name) {
                                  auto timeModel = std::make_unique<app::TimeModel>();
                                  auto presenter = std::make_unique<app::meditation::MeditationProgressPresenter>(
                                      app, settings.get(), std::move(timeModel));
                                  return std::make_unique<gui::MeditationRunningWindow>(app, std::move(presenter));
                              });
        windowsFactory.attach(gui::name::window::sessionPaused, [](ApplicationCommon *app, const std::string &name) {
            return std::make_unique<gui::SessionPausedWindow>(app);
        });
        windowsFactory.attach(gui::name::window::sessionEnded, [](ApplicationCommon *app, const std::string &name) {
            auto presenter = std::make_unique<app::meditation::SessionEndedPresenter>(app);
            return std::make_unique<gui::SessionEndWindow>(app, std::move(presenter));
        });

        attachPopups({gui::popup::ID::AlarmActivated,
                      gui::popup::ID::AlarmDeactivated,
                      gui::popup::ID::PowerOff,
                      gui::popup::ID::Reboot,
                      gui::popup::ID::BedtimeNotification});
    }

    sys::MessagePointer ApplicationBellMeditationTimer::DataReceivedHandler(sys::DataMessage *msgl,
                                                                            sys::ResponseMessage *resp)
    {
        auto retMsg = Application::DataReceivedHandler(msgl);
        if (auto response = dynamic_cast<sys::ResponseMessage *>(retMsg.get());
            response != nullptr && response->retCode == sys::ReturnCodes::Success) {
            return retMsg;
        }

        return handleAsyncResponse(resp);
    }

    // Empty: do not start idleTimer on application run
    void ApplicationBellMeditationTimer::onStart()
    {}
} // namespace app

A products/BellHybrid/apps/application-bell-meditation-timer/CMakeLists.txt => products/BellHybrid/apps/application-bell-meditation-timer/CMakeLists.txt +48 -0
@@ 0,0 1,48 @@
add_library(application-bell-meditation-timer STATIC)
add_library(bell::app-meditation-timer ALIAS application-bell-meditation-timer)

target_include_directories(application-bell-meditation-timer
    PRIVATE
        $<BUILD_INTERFACE:
            ${CMAKE_CURRENT_SOURCE_DIR}
            data
            include/application-bell-meditation-timer
            models
            presenter
            widgets
            windows
        >
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)

target_sources(application-bell-meditation-timer
    PRIVATE
        ApplicationBellMeditationTimer.cpp
        presenter/IntervalChimePresenter.cpp
        presenter/MeditationProgressPresenter.cpp
        presenter/MeditationTimerPresenter.cpp
        presenter/ReadyGoingPresenter.cpp
        presenter/SessionEndedPresenter.cpp 
        windows/IntervalChimeWindow.cpp
        windows/MeditationRunningWindow.cpp
        windows/MeditationTimerWindow.cpp
        windows/ReadyGoingWindow.cpp
        windows/SessionEndWindow.cpp
        windows/SessionPausedWindow.cpp
    PUBLIC
        include/application-bell-meditation-timer/ApplicationBellMeditationTimer.hpp
)

target_link_libraries(application-bell-meditation-timer
    PRIVATE
        app
        bell::app-common
        bell::app-main
        bellgui
        service-appmgr
        service-time
    PUBLIC
        apps-common
        module-gui
)

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

#pragma once

namespace app::meditation
{
    constexpr auto meditationDBRecordName = "MeditationTimer";
    constexpr auto intervalDBRecordName   = "IntervalChime";
} // namespace app::meditation

A products/BellHybrid/apps/application-bell-meditation-timer/data/MeditationStyle.hpp => products/BellHybrid/apps/application-bell-meditation-timer/data/MeditationStyle.hpp +84 -0
@@ 0,0 1,84 @@
// 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 <Style.hpp>

namespace app::meditationStyle
{
    namespace itStyle
    {
        namespace icon
        {
            constexpr inline auto imageLogo         = "bell_big_logo";
            constexpr inline auto imagePause        = "big_pause";
            constexpr inline auto imageTopMargin    = 112;
            constexpr inline auto imageBottomMargin = 30;
        } // namespace icon

        namespace text
        {
            constexpr inline auto font = style::window::font::verybiglight;
        } // namespace text
    }     // namespace itStyle

    namespace icStyle
    {
        namespace text
        {
            constexpr inline auto font = style::window::font::supersizemelight;
        } // namespace text

        namespace minute
        {
            constexpr inline auto font = style::window::font::largelight;
        } // namespace minute

        namespace list
        {
            constexpr inline auto timeUnitSingular = "common_minute_lower";
            constexpr inline auto timeUnitPlural   = "common_minutes_lower";
        } // namespace list
    }     // namespace icStyle

    namespace mrStyle
    {
        namespace title
        {
            constexpr inline auto font = style::window::font::verybiglight;
        } // namespace title

        namespace progress
        {
            constexpr inline auto progressMarginTop  = 40;
            constexpr inline auto progressMarginLeft = 60;
            constexpr inline auto boxesCount         = 16;
        } // namespace progress

        namespace timer
        {
            constexpr inline auto timerMarginBottom = 20;
            constexpr inline auto font              = style::window::font::largelight;
        } // namespace timer
    }     // namespace mrStyle

    namespace mtStyle
    {
        namespace text
        {
            constexpr inline auto font = style::window::font::supersizemelight;
        } // namespace text

        namespace minute
        {
            constexpr inline auto font = style::window::font::largelight;
        } // namespace minute

        namespace list
        {
            constexpr inline auto timeUnitSingular = "common_minute_lower";
            constexpr inline auto timeUnitPlural   = "common_minutes_lower";
        } // namespace list
    }     // namespace mtStyle
} // namespace app::meditationStyle

A products/BellHybrid/apps/application-bell-meditation-timer/include/application-bell-meditation-timer/ApplicationBellMeditationTimer.hpp => products/BellHybrid/apps/application-bell-meditation-timer/include/application-bell-meditation-timer/ApplicationBellMeditationTimer.hpp +55 -0
@@ 0,0 1,55 @@
// 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 <Application.hpp>

namespace gui::name::window
{
    inline constexpr auto intervalChime      = "IntervalChimeWindow";
    inline constexpr auto meditationProgress = "MeditationProgressWindow";
    inline constexpr auto meditationTimer    = "MeditationTimerWindow";
    inline constexpr auto readyGoing         = "ReadyGoingWindow";
    inline constexpr auto sessionEnded       = "SessionEndedWindow";
    inline constexpr auto sessionPaused      = "SensionPausedWindow";
} // namespace gui::name::window

namespace app
{
    inline constexpr auto applicationBellMeditationTimerName = "ApplicationBellMeditationTimer";

    class ApplicationBellMeditationTimer : public Application
    {
      public:
        ApplicationBellMeditationTimer(std::string name                    = applicationBellMeditationTimerName,
                                       std::string parent                  = "",
                                       StatusIndicators statusIndicators   = StatusIndicators{},
                                       StartInBackground startInBackground = {false});
        ~ApplicationBellMeditationTimer();

        sys::ReturnCodes InitHandler() override;

        void createUserInterface() override;
        void destroyUserInterface() override
        {}

        sys::MessagePointer DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp) override;

        sys::ReturnCodes SwitchPowerModeHandler(const sys::ServicePowerMode mode) override final
        {
            return sys::ReturnCodes::Success;
        }

      private:
        void onStart() override;
    };

    template <> struct ManifestTraits<ApplicationBellMeditationTimer>
    {
        static auto GetManifest() -> manager::ApplicationManifest
        {
            return {{manager::actions::Launch}};
        }
    };
} // namespace app

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

#include "ApplicationBellMeditationTimer.hpp"
#include "IntervalChimePresenter.hpp"
#include "MeditationCommon.hpp"
#include "MeditationStyle.hpp"

#include <service-db/Settings.hpp>

namespace app::meditation
{
    IntervalChimePresenter::IntervalChimePresenter(app::ApplicationCommon *app, settings::Settings *settings)
        : app{app}, settings{settings}
    {
        initIntervalOptions();
    }

    std::vector<std::string> IntervalChimePresenter::getIntervals() const
    {
        std::vector<std::string> intervalNames;
        for (auto const &option : intervalOptions) {
            intervalNames.push_back(option.second);
        }
        return intervalNames;
    }

    std::string IntervalChimePresenter::getCurrentInterval() const
    {
        // Always set interval as none
        for (auto const &option : intervalOptions) {
            if (option.first == std::chrono::minutes{0}) {
                return option.second;
            }
        }
        return intervalOptions.at(0).second;
    }

    std::string IntervalChimePresenter::getTimeUnitName(std::string value)
    {
        using namespace app::meditationStyle::mtStyle::list;
        for (auto const &option : intervalOptions) {
            if (option.second == value) {
                if (option.first == std::chrono::minutes{0}) {
                    return "";
                }
                if (option.first == std::chrono::minutes{1}) {
                    return utils::translate(timeUnitSingular);
                }
                return utils::translate(timeUnitPlural);
            }
        }
        return "";
    }

    void IntervalChimePresenter::activate(std::string value)
    {
        for (auto const &option : intervalOptions) {
            if (option.second == value) {
                settings->setValue(
                    intervalDBRecordName, utils::to_string(option.first.count()), settings::SettingsScope::AppLocal);
                app->switchWindow(gui::name::window::readyGoing);
                break;
            }
        }
    }

    void IntervalChimePresenter::initIntervalOptions()
    {
        intervalOptions.push_back({std::chrono::minutes{0}, utils::translate("app_bell_meditation_interval_none")});
        intervalOptions.push_back({std::chrono::minutes{1}, getIntervalString(1)});
        intervalOptions.push_back({std::chrono::minutes{2}, getIntervalString(2)});
        intervalOptions.push_back({std::chrono::minutes{5}, getIntervalString(5)});
        intervalOptions.push_back({std::chrono::minutes{10}, getIntervalString(10)});
        intervalOptions.push_back({std::chrono::minutes{15}, getIntervalString(15)});
        intervalOptions.push_back({std::chrono::minutes{30}, getIntervalString(30)});
    }

    std::string IntervalChimePresenter::getIntervalString(std::uint32_t value)
    {
        const std::string toReplace = "%0";
        std::string temp            = utils::translate("app_bell_meditation_interval_every_x_minutes");
        temp.replace(temp.find(toReplace), toReplace.size(), std::to_string(value));
        return temp;
    }
} // namespace app::meditation

A products/BellHybrid/apps/application-bell-meditation-timer/presenter/IntervalChimePresenter.hpp => products/BellHybrid/apps/application-bell-meditation-timer/presenter/IntervalChimePresenter.hpp +54 -0
@@ 0,0 1,54 @@
// 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/ApplicationCommon.hpp>
#include <apps-common/BasePresenter.hpp>

namespace app
{
    class ApplicationCommon;
}

namespace app::meditation
{
    class IntervalChimeContract
    {
      public:
        class View
        {
          public:
            virtual ~View() = default;
        };

        class Presenter : public BasePresenter<IntervalChimeContract::View>
        {
          public:
            virtual ~Presenter() noexcept                          = default;
            virtual std::vector<std::string> getIntervals() const  = 0;
            virtual std::string getCurrentInterval() const         = 0;
            virtual std::string getTimeUnitName(std::string value) = 0;
            virtual void activate(std::string value)               = 0;
        };
    };

    class IntervalChimePresenter : public IntervalChimeContract::Presenter
    {
      public:
        IntervalChimePresenter(app::ApplicationCommon *app, settings::Settings *settings);

        std::vector<std::string> getIntervals() const override;
        std::string getCurrentInterval() const override;
        std::string getTimeUnitName(std::string value) override;
        void activate(std::string value) override;

      private:
        app::ApplicationCommon *app  = nullptr;
        settings::Settings *settings = nullptr;
        std::vector<std::pair<std::chrono::minutes, const std::string>> intervalOptions;

        void initIntervalOptions();
        std::string getIntervalString(std::uint32_t value);
    };
} // namespace app::meditation

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

#include "ApplicationBellMeditationTimer.hpp"
#include "MeditationCommon.hpp"
#include "MeditationProgressPresenter.hpp"

#include <common/models/TimeModel.hpp>
#include <service-appmgr/Controller.hpp>
#include <service-db/Settings.hpp>

namespace
{
    constexpr std::chrono::minutes emptyValue{0};
} // namespace

namespace app::meditation
{
    MeditationProgressPresenter ::MeditationProgressPresenter(app::ApplicationCommon *app,
                                                              settings::Settings *settings,
                                                              std::unique_ptr<AbstractTimeModel> timeModel)
        : app{app}, settings{settings}, timeModel{std::move(timeModel)}
    {
        duration = std::chrono::minutes{
            utils::getNumericValue<int>(settings->getValue(meditationDBRecordName, settings::SettingsScope::AppLocal))};
        interval = std::chrono::minutes{
            utils::getNumericValue<int>(settings->getValue(intervalDBRecordName, settings::SettingsScope::AppLocal))};
    }

    void MeditationProgressPresenter::setTimer(std::unique_ptr<app::TimerWithCallbacks> &&_timer)
    {
        timer = std::move(_timer);
        timer->registerOnFinishedCallback([this]() { onProgressFinished(); });
        if (interval != emptyValue) {
            timer->registerOnIntervalCallback([this]() { onIntervalReached(); });
        }
    }

    void MeditationProgressPresenter::handleUpdateTimeEvent()
    {
        getView()->setTime(timeModel->getCurrentTime());
    }

    void MeditationProgressPresenter::start()
    {
        timer->reset(std::chrono::seconds(duration), std::chrono::seconds(interval));
        timer->start();
    }

    void MeditationProgressPresenter::stop()
    {
        timer->stop();
    }

    void MeditationProgressPresenter::pause()
    {
        timer->stop();
        app->switchWindow(gui::name::window::sessionPaused);
    }

    void MeditationProgressPresenter::resume()
    {
        timer->start();
    }

    void MeditationProgressPresenter::abandon()
    {
        timer->stop();
        app::manager::Controller::sendAction(
            app,
            app::manager::actions::Launch,
            std::make_unique<app::ApplicationLaunchData>(app::applicationBellMeditationTimerName));
    }

    void MeditationProgressPresenter::finish()
    {
        timer->stop();
        app->switchWindow(gui::name::window::sessionEnded);
    }

    void MeditationProgressPresenter::onProgressFinished()
    {
        getView()->progressFinished();
    }

    void MeditationProgressPresenter::onIntervalReached()
    {
        if (interval != emptyValue) {
            getView()->intervalReached();
        }
    }
} // namespace app::meditation

A products/BellHybrid/apps/application-bell-meditation-timer/presenter/MeditationProgressPresenter.hpp => products/BellHybrid/apps/application-bell-meditation-timer/presenter/MeditationProgressPresenter.hpp +84 -0
@@ 0,0 1,84 @@
// 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/ApplicationCommon.hpp>
#include <apps-common/BasePresenter.hpp>
#include <apps-common/widgets/TimerWithCallbacks.hpp>
#include <time/time_locale.hpp>

#include <memory>

namespace app
{
    class AbstractTimeModel;
    class ApplicationCommon;
} // namespace app

namespace gui
{
    class Item;
}

namespace settings
{
    class Settings;
}

namespace app::meditation
{
    class MeditationProgressContract
    {
      public:
        class View
        {
          public:
            virtual ~View()                                                 = default;
            virtual void progressFinished()                                 = 0;
            virtual void intervalReached()                                  = 0;
            virtual void setTime(std::time_t newTime)                       = 0;
            virtual void setTimeFormat(utils::time::Locale::TimeFormat fmt) = 0;
        };
        class Presenter : public BasePresenter<MeditationProgressContract::View>
        {
          public:
            virtual void setTimer(std::unique_ptr<app::TimerWithCallbacks> &&timer) = 0;
            virtual void handleUpdateTimeEvent()                                    = 0;
            virtual void start()                                                    = 0;
            virtual void stop()                                                     = 0;
            virtual void pause()                                                    = 0;
            virtual void resume()                                                   = 0;
            virtual void abandon()                                                  = 0;
            virtual void finish()                                                   = 0;
        };
    };

    class MeditationProgressPresenter : public MeditationProgressContract::Presenter
    {
      private:
        app::ApplicationCommon *app  = nullptr;
        settings::Settings *settings = nullptr;
        std::unique_ptr<app::TimerWithCallbacks> timer;
        std::unique_ptr<AbstractTimeModel> timeModel;
        std::chrono::minutes duration;
        std::chrono::minutes interval;

        void onProgressFinished();
        void onIntervalReached();

      public:
        MeditationProgressPresenter(app::ApplicationCommon *app,
                                    settings::Settings *settings,
                                    std::unique_ptr<AbstractTimeModel> timeModel);

        void setTimer(std::unique_ptr<app::TimerWithCallbacks> &&_timer) override;
        void handleUpdateTimeEvent() override;
        void start() override;
        void stop() override;
        void pause() override;
        void resume() override;
        void abandon() override;
        void finish() override;
    };
} // namespace app::meditation

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

#include "ApplicationBellMeditationTimer.hpp"
#include "MeditationCommon.hpp"
#include "MeditationStyle.hpp"
#include "MeditationTimerPresenter.hpp"

#include <service-db/Settings.hpp>

namespace
{
    constexpr inline auto spinnerMax   = 180U;
    constexpr inline auto spinnerMin   = 1U;
    constexpr inline auto spinnerStep  = 1U;
    constexpr inline auto emptyValue   = 0U;
    constexpr inline auto defaultValue = 15U;
} // namespace

namespace app::meditation
{
    MeditationTimerPresenter ::MeditationTimerPresenter(app::ApplicationCommon *app, settings::Settings *settings)
        : app{app}, settings{settings}
    {}

    std::uint32_t MeditationTimerPresenter::getMinValue()
    {
        return spinnerMin;
    }

    std::uint32_t MeditationTimerPresenter::getMaxValue()
    {
        return spinnerMax;
    }

    std::uint32_t MeditationTimerPresenter::getStepValue()
    {
        return spinnerStep;
    }

    std::uint32_t MeditationTimerPresenter::getCurrentValue()
    {
        const auto value = settings->getValue(meditationDBRecordName, settings::SettingsScope::AppLocal);
        auto defTimer    = utils::getNumericValue<std::uint32_t>(value);
        if (defTimer == emptyValue) {
            defTimer = defaultValue;
        }
        return defTimer;
    }

    std::string MeditationTimerPresenter::getTimeUnitName(std::uint32_t value)
    {
        using namespace app::meditationStyle::mtStyle::list;
        return utils::translate(value == 1 ? timeUnitSingular : timeUnitPlural);
    }

    void MeditationTimerPresenter::activate(std::uint32_t value)
    {
        settings->setValue(meditationDBRecordName, utils::to_string(value), settings::SettingsScope::AppLocal);
        app->switchWindow(gui::name::window::intervalChime);
    }
} // namespace app::meditation

A products/BellHybrid/apps/application-bell-meditation-timer/presenter/MeditationTimerPresenter.hpp => products/BellHybrid/apps/application-bell-meditation-timer/presenter/MeditationTimerPresenter.hpp +59 -0
@@ 0,0 1,59 @@
// 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/ApplicationCommon.hpp>
#include <apps-common/BasePresenter.hpp>

namespace app
{
    class ApplicationCommon;
}

namespace settings
{
    class Settings;
}

namespace app::meditation
{
    class MeditationTimerContract
    {
      public:
        class View
        {
          public:
            virtual ~View() = default;
        };

        class Presenter : public BasePresenter<MeditationTimerContract::View>
        {
          public:
            virtual ~Presenter() noexcept                            = default;
            virtual std::uint32_t getMinValue()                      = 0;
            virtual std::uint32_t getMaxValue()                      = 0;
            virtual std::uint32_t getStepValue()                     = 0;
            virtual std::uint32_t getCurrentValue()                  = 0;
            virtual std::string getTimeUnitName(std::uint32_t value) = 0;
            virtual void activate(std::uint32_t value)               = 0;
        };
    };

    class MeditationTimerPresenter : public MeditationTimerContract::Presenter
    {
      public:
        MeditationTimerPresenter(app::ApplicationCommon *app, settings::Settings *settings);

        std::uint32_t getMinValue() override;
        std::uint32_t getMaxValue() override;
        std::uint32_t getStepValue() override;
        std::uint32_t getCurrentValue() override;
        std::string getTimeUnitName(std::uint32_t value) override;
        void activate(std::uint32_t value) override;

      private:
        app::ApplicationCommon *app  = nullptr;
        settings::Settings *settings = nullptr;
    };
} // namespace app::meditation

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

#include "ApplicationBellMeditationTimer.hpp"
#include "ReadyGoingPresenter.hpp"

namespace app::meditation
{
    ReadyGoingPresenter::ReadyGoingPresenter(app::ApplicationCommon *app) : app{app}
    {}

    void ReadyGoingPresenter::activate()
    {
        app->switchWindow(gui::name::window::meditationProgress);
    }
} // namespace app::meditation

A products/BellHybrid/apps/application-bell-meditation-timer/presenter/ReadyGoingPresenter.hpp => products/BellHybrid/apps/application-bell-meditation-timer/presenter/ReadyGoingPresenter.hpp +39 -0
@@ 0,0 1,39 @@
// 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 <Application.hpp>
#include <apps-common/BasePresenter.hpp>

namespace app
{
    class ApplicationCommon;
}

namespace app::meditation
{
    class ReadyGoingPresenterContract
    {
      public:
        class View
        {
          public:
            virtual ~View() = default;
        };
        class Presenter : public BasePresenter<ReadyGoingPresenterContract::View>
        {
          public:
            virtual void activate() = 0;
        };
    };

    class ReadyGoingPresenter : public ReadyGoingPresenterContract::Presenter
    {
        app::ApplicationCommon *app{};
        void activate() override;

      public:
        explicit ReadyGoingPresenter(app::ApplicationCommon *app);
    };
} // namespace app::meditation

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

#include "SessionEndedPresenter.hpp"

#include <service-appmgr/Controller.hpp>

namespace app::meditation
{
    SessionEndedPresenter::SessionEndedPresenter(app::ApplicationCommon *app) : app{app}
    {}

    void SessionEndedPresenter::activate()
    {
        app::manager::Controller::sendAction(app, app::manager::actions::Home);
    }
} // namespace app::meditation

A products/BellHybrid/apps/application-bell-meditation-timer/presenter/SessionEndedPresenter.hpp => products/BellHybrid/apps/application-bell-meditation-timer/presenter/SessionEndedPresenter.hpp +39 -0
@@ 0,0 1,39 @@
// 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 <Application.hpp>
#include <apps-common/BasePresenter.hpp>

namespace app
{
    class ApplicationCommon;
}

namespace app::meditation
{
    class SessionEndedPresenterContract
    {
      public:
        class View
        {
          public:
            virtual ~View() = default;
        };
        class Presenter : public BasePresenter<SessionEndedPresenterContract::View>
        {
          public:
            virtual void activate() = 0;
        };
    };

    class SessionEndedPresenter : public SessionEndedPresenterContract::Presenter
    {
        app::ApplicationCommon *app{};
        void activate() override;

      public:
        explicit SessionEndedPresenter(app::ApplicationCommon *app);
    };
} // namespace app::meditation

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

#include "ApplicationBellMeditationTimer.hpp"
#include "IntervalChimeWindow.hpp"
#include "MeditationStyle.hpp"

namespace gui
{
    IntervalChimeWindow::IntervalChimeWindow(
        app::ApplicationCommon *app,
        std::unique_ptr<app::meditation::IntervalChimeContract::Presenter> &&windowPresenter)
        : AppWindow(app, gui::name::window::intervalChime), presenter{std::move(windowPresenter)}
    {
        presenter->attach(this);
        buildInterface();
    }

    void IntervalChimeWindow::buildInterface()
    {
        AppWindow::buildInterface();

        statusBar->setVisible(false);
        header->setTitleVisibility(false);
        navBar->setVisible(false);

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

        auto topMessage = new TextFixedSize(body->firstBox);
        topMessage->setMaximumSize(style::bell_base_layout::w, style::bell_base_layout::outer_layouts_h);
        topMessage->setFont(style::window::font::largelight);
        topMessage->setEdges(gui::RectangleEdge::None);
        topMessage->activeItem = false;
        topMessage->setAlignment(Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        topMessage->setText(utils::translate("app_bell_meditation_interval_chime"));
        topMessage->drawUnderline(false);

        auto titles             = presenter->getIntervals();
        spinner                 = new UTF8Spinner({titles.begin(), titles.end()}, Boundaries::Fixed);
        spinner->onValueChanged = [this](const auto val) { this->onValueChanged(val); };
        spinner->setMaximumSize(style::bell_base_layout::w, style::bell_base_layout::h);
        spinner->setFont(app::meditationStyle::icStyle::text::font);
        spinner->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        spinner->setFocusEdges(RectangleEdge::None);
        body->getCenterBox()->addWidget(spinner);

        auto currentValue = presenter->getCurrentInterval();
        spinner->setCurrentValue(currentValue);
        body->setMinMaxArrowsVisibility(currentValue == titles.front(), currentValue == titles.back());

        bottomDescription = new Label(body->lastBox);
        bottomDescription->setMaximumSize(style::bell_base_layout::w, style::bell_base_layout::outer_layouts_h);
        bottomDescription->setFont(app::meditationStyle::icStyle::minute::font);
        bottomDescription->setEdges(RectangleEdge::None);
        bottomDescription->activeItem = false;
        bottomDescription->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Top));
        bottomDescription->setText(presenter->getTimeUnitName(spinner->getCurrentValue()));

        setFocusItem(spinner);
        body->resize();
    }

    bool IntervalChimeWindow::onInput(const gui::InputEvent &inputEvent)
    {
        if (spinner->onInput(inputEvent)) {
            return true;
        }

        if (inputEvent.isShortRelease(gui::KeyCode::KEY_ENTER)) {
            presenter->activate(spinner->getCurrentValue());
            return true;
        }

        return AppWindow::onInput(inputEvent);
    }

    void IntervalChimeWindow::onValueChanged(const std::string currentValue)
    {
        auto titles = presenter->getIntervals();
        body->setMinMaxArrowsVisibility(currentValue == titles.front(), currentValue == titles.back());
        bottomDescription->setText(presenter->getTimeUnitName(spinner->getCurrentValue()));
    }
} // namespace gui

A products/BellHybrid/apps/application-bell-meditation-timer/windows/IntervalChimeWindow.hpp => products/BellHybrid/apps/application-bell-meditation-timer/windows/IntervalChimeWindow.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 <apps-common/widgets/spinners/Spinners.hpp>
#include <widgets/BellBaseLayout.hpp>

#include <Application.hpp>
#include <AppWindow.hpp>
#include <InputEvent.hpp>

#include "IntervalChimePresenter.hpp"

namespace gui
{
    class IntervalChimeWindow : public AppWindow, public app::meditation::IntervalChimeContract::View
    {
      public:
        explicit IntervalChimeWindow(
            app::ApplicationCommon *app,
            std::unique_ptr<app::meditation::IntervalChimeContract::Presenter> &&windowPresenter);

        // virtual methods
        void buildInterface() override;
        bool onInput(const gui::InputEvent &inputEvent) override;

        void onValueChanged(const std::string currentValue);

      private:
        std::unique_ptr<app::meditation::IntervalChimeContract::Presenter> presenter;
        BellBaseLayout *body{};
        UTF8Spinner *spinner{};
        Label *bottomDescription{};
    };
} // namespace gui

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

#include "ApplicationBellMeditationTimer.hpp"
#include "MeditationRunningWindow.hpp"
#include "MeditationStyle.hpp"

#include <apps-common/widgets/BellBaseLayout.hpp>
#include <apps-common/widgets/ProgressTimerWithBarGraphAndCounter.hpp>
#include <purefs/filesystem_paths.hpp>
#include <service-audio/AudioServiceAPI.hpp>

namespace
{
    inline constexpr auto meditationProgressTimerName = "MeditationProgressTimer";
    inline constexpr std::chrono::seconds baseTick{1};
    inline constexpr auto meditationProgressMode = app::ProgressCountdownMode::Increasing;
    inline constexpr auto meditationAudioPath    = "assets/audio/bell/meditation/Meditation_Gong.mp3";

    using namespace app::meditationStyle;

    void decorateProgressItem(gui::Rect *item, gui::Alignment::Vertical alignment)
    {
        item->setEdges(gui::RectangleEdge::None);
        item->activeItem = false;
        item->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, alignment));
    }

    gui::Label *createTitle(gui::VBox *parent)
    {
        auto title = new gui::Label(parent, 0, 0, parent->getWidth(), parent->getHeight() / 2);
        title->setText(utils::translate("app_bell_meditation_progress"));
        title->setFont(mrStyle::title::font);
        decorateProgressItem(title, gui::Alignment::Vertical::Top);
        return title;
    }

    gui::HBarGraph *createProgress(gui::VBox *parent)
    {
        auto progressBox = new gui::HBox(parent, 0, 0, parent->getWidth(), parent->getHeight() / 2);
        decorateProgressItem(progressBox, gui::Alignment::Vertical::Bottom);
        auto progressBar =
            new gui::HBarGraph(progressBox, 0, 0, mrStyle::progress::boxesCount, gui::BarGraphStyle::Heavy);
        decorateProgressItem(progressBar, gui::Alignment::Vertical::Center);
        return progressBar;
    }

    gui::Text *createTimer(gui::Item *parent)
    {
        auto timer = new gui::Text(parent,
                                   0,
                                   0,
                                   style::bell_base_layout::w,
                                   style::bell_base_layout::outer_layouts_h - mrStyle::timer::timerMarginBottom);
        timer->setFont(mrStyle::timer::font);
        timer->setMargins(gui::Margins(0, mrStyle::timer::timerMarginBottom, 0, 0));
        decorateProgressItem(timer, gui::Alignment::Vertical::Top);
        return timer;
    }
} // namespace

namespace gui
{
    MeditationRunningWindow::MeditationRunningWindow(
        app::ApplicationCommon *app,
        std::unique_ptr<app::meditation::MeditationProgressContract::Presenter> &&windowPresenter)
        : AppWindow(app, gui::name::window::meditationProgress), presenter{std::move(windowPresenter)}
    {
        presenter->attach(this);
        buildInterface();
    }

    void MeditationRunningWindow::buildInterface()
    {
        AppWindow::buildInterface();

        statusBar->setVisible(false);
        header->setTitleVisibility(false);
        navBar->setVisible(false);

        buildLayout();
        configureTimer();
    }

    void MeditationRunningWindow::buildLayout()
    {
        auto body = new gui::BellBaseLayout(this, 0, 0, style::bell_base_layout::w, style::bell_base_layout::h, false);
        auto vBox =
            new VBox(body->getCenterBox(), 0, 0, style::bell_base_layout::w, style::bell_base_layout::center_layout_h);

        decorateProgressItem(vBox, gui::Alignment::Vertical::Top);
        createTitle(vBox);
        progress = createProgress(vBox);
        timer    = createTimer(body->lastBox);

        time = new BellStatusClock(body->firstBox);
        time->setMaximumSize(body->firstBox->getWidth(), body->firstBox->getHeight());
        time->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Top));
        body->firstBox->resizeItems();

        dimensionChangedCallback = [&](Item &, const BoundingBox &newDim) -> bool {
            body->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    void MeditationRunningWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        AppWindow::onBeforeShow(mode, data);

        if (mode == ShowMode::GUI_SHOW_INIT) {
            playGong();
            presenter->start();
        }
        else {
            presenter->resume();
        }
    }

    bool MeditationRunningWindow::onInput(const InputEvent &inputEvent)
    {
        if (inputEvent.isShortRelease(gui::KeyCode::KEY_ENTER)) {
            presenter->pause();
            return true;
        }
        if (inputEvent.isShortRelease(gui::KeyCode::KEY_RF)) {
            presenter->abandon();
            return true;
        }

        return AppWindow::onInput(inputEvent);
    }

    void MeditationRunningWindow::progressFinished()
    {
        endSession();
    }

    void MeditationRunningWindow::intervalReached()
    {
        intervalTimeout();
    }

    void MeditationRunningWindow::configureTimer()
    {
        auto progressTimer = std::make_unique<app::ProgressTimerWithBarGraphAndCounter>(
            application, *this, meditationProgressTimerName, baseTick, meditationProgressMode);
        progressTimer->attach(progress);
        progressTimer->attach(timer);
        presenter->setTimer(std::move(progressTimer));
    }

    void MeditationRunningWindow::setTime(std::time_t newTime)
    {
        time->setTime(newTime);
        time->setTimeFormatSpinnerVisibility(true);
    }

    void MeditationRunningWindow::setTimeFormat(utils::time::Locale::TimeFormat fmt)
    {
        time->setTimeFormat(fmt);
    }

    bool MeditationRunningWindow::updateTime()
    {
        if (presenter != nullptr) {
            presenter->handleUpdateTimeEvent();
        }
        return true;
    }

    void MeditationRunningWindow::intervalTimeout()
    {
        playGong();
    }

    void MeditationRunningWindow::endSession()
    {
        playGong();
        presenter->finish();
    }

    void MeditationRunningWindow::playGong()
    {
        AudioServiceAPI::PlaybackStart(
            application, audio::PlaybackType::Meditation, purefs::dir::getCurrentOSPath() / meditationAudioPath);
    }
} // namespace gui

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

#pragma once

#include <Application.hpp>
#include <AppWindow.hpp>
#include <InputEvent.hpp>
#include <Text.hpp>
#include <apps-common/widgets/BarGraph.hpp>
#include <common/widgets/BellStatusClock.hpp>

#include "MeditationProgressPresenter.hpp"

namespace gui
{
    class MeditationRunningWindow : public AppWindow, public app::meditation::MeditationProgressContract::View
    {
      public:
        MeditationRunningWindow(
            app::ApplicationCommon *app,
            std::unique_ptr<app::meditation::MeditationProgressContract::Presenter> &&windowPresenter);

        // virtual methods
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        bool onInput(const InputEvent &inputEvent) override;
        void buildInterface() override;
        void progressFinished() override;
        void intervalReached() override;

      private:
        std::unique_ptr<app::meditation::MeditationProgressContract::Presenter> presenter;
        gui::HBarGraph *progress   = nullptr;
        gui::Text *timer           = nullptr;
        gui::BellStatusClock *time = nullptr;

        void setTime(std::time_t newTime) override;
        void setTimeFormat(utils::time::Locale::TimeFormat fmt) override;
        bool updateTime() override;

        void buildLayout();
        void configureTimer();

        void endSession();
        void intervalTimeout();
        void playGong();
    };
} // namespace gui

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

#include "ApplicationBellMeditationTimer.hpp"
#include "MeditationStyle.hpp"
#include "MeditationTimerWindow.hpp"

namespace gui
{
    MeditationTimerWindow::MeditationTimerWindow(
        app::ApplicationCommon *app,
        std::unique_ptr<app::meditation::MeditationTimerContract::Presenter> &&windowPresenter)
        : AppWindow(app, gui::name::window::meditationTimer), presenter{std::move(windowPresenter)}
    {
        presenter->attach(this);
        buildInterface();
    }

    void MeditationTimerWindow::buildInterface()
    {
        AppWindow::buildInterface();

        statusBar->setVisible(false);
        header->setTitleVisibility(false);
        navBar->setVisible(false);

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

        auto topMessage = new TextFixedSize(body->firstBox);
        topMessage->setMaximumSize(style::bell_base_layout::w, style::bell_base_layout::outer_layouts_h);
        topMessage->setFont(style::window::font::largelight);
        topMessage->setEdges(gui::RectangleEdge::None);
        topMessage->activeItem = false;
        topMessage->setAlignment(Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        topMessage->setText(utils::translate("app_bell_meditation_timer"));
        topMessage->drawUnderline(false);

        spinner = new UIntegerSpinner(
            UIntegerSpinner ::Range{presenter->getMinValue(), presenter->getMaxValue(), presenter->getStepValue()},
            Boundaries::Fixed);
        spinner->onValueChanged = [this](const auto val) { this->onValueChanged(val); };
        spinner->setMaximumSize(style::bell_base_layout::w, style::bell_base_layout::h);
        spinner->setFont(app::meditationStyle::mtStyle::text::font);
        spinner->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        spinner->setEdges(RectangleEdge::None);
        spinner->setFocusEdges(RectangleEdge::None);
        body->getCenterBox()->addWidget(spinner);

        auto currentValue = presenter->getCurrentValue();
        spinner->setCurrentValue(currentValue);
        body->setMinMaxArrowsVisibility(currentValue == presenter->getMinValue(),
                                        currentValue == presenter->getMaxValue());

        bottomDescription = new Label(body->lastBox);
        bottomDescription->setMaximumSize(style::bell_base_layout::w, style::bell_base_layout::outer_layouts_h);
        bottomDescription->setFont(app::meditationStyle::mtStyle::minute::font);
        bottomDescription->setEdges(RectangleEdge::None);
        bottomDescription->activeItem = false;
        bottomDescription->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Top));
        bottomDescription->setText(presenter->getTimeUnitName(spinner->getCurrentValue()));

        setFocusItem(spinner);
        body->resize();
    }

    bool MeditationTimerWindow::onInput(const gui::InputEvent &inputEvent)
    {
        if (spinner->onInput(inputEvent)) {
            return true;
        }

        if (inputEvent.isShortRelease(gui::KeyCode::KEY_ENTER)) {
            presenter->activate(spinner->getCurrentValue());
            return true;
        }

        return AppWindow::onInput(inputEvent);
    }

    void MeditationTimerWindow::onValueChanged(const std::uint32_t currentValue)
    {
        body->setMinMaxArrowsVisibility(currentValue == presenter->getMinValue(),
                                        currentValue == presenter->getMaxValue());
        bottomDescription->setText(presenter->getTimeUnitName(spinner->getCurrentValue()));
    }
} // namespace gui

A products/BellHybrid/apps/application-bell-meditation-timer/windows/MeditationTimerWindow.hpp => products/BellHybrid/apps/application-bell-meditation-timer/windows/MeditationTimerWindow.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 <apps-common/widgets/spinners/Spinners.hpp>
#include <widgets/BellBaseLayout.hpp>

#include <Application.hpp>
#include <AppWindow.hpp>
#include <InputEvent.hpp>

#include "MeditationTimerPresenter.hpp"

namespace gui
{
    class MeditationTimerWindow : public AppWindow, public app::meditation::MeditationTimerContract::View
    {
      public:
        explicit MeditationTimerWindow(
            app::ApplicationCommon *app,
            std::unique_ptr<app::meditation::MeditationTimerContract::Presenter> &&windowPresenter);

        // virtual methods
        void buildInterface() override;
        bool onInput(const gui::InputEvent &inputEvent) override;

        void onValueChanged(const std::uint32_t currentValue);

      private:
        std::unique_ptr<app::meditation::MeditationTimerContract::Presenter> presenter;
        BellBaseLayout *body{};
        UIntegerSpinner *spinner{};
        Label *bottomDescription{};
    };
} // namespace gui

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

#include "ApplicationBellMeditationTimer.hpp"
#include "MeditationStyle.hpp"
#include "ReadyGoingWindow.hpp"

namespace
{
    constexpr inline auto defaultTimeout = std::chrono::seconds{5};
}

namespace gui
{
    ReadyGoingWindow::ReadyGoingWindow(
        app::ApplicationCommon *app,
        std::shared_ptr<app::meditation::ReadyGoingPresenterContract::Presenter> winPresenter)
        : WindowWithTimer(app, gui::name::window::readyGoing, defaultTimeout), presenter{std::move(winPresenter)}
    {
        buildInterface();

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

    void ReadyGoingWindow::buildInterface()
    {
        WindowWithTimer::buildInterface();

        statusBar->setVisible(false);
        header->setTitleVisibility(false);
        navBar->setVisible(false);

        if (icon == nullptr) {
            using namespace app::meditationStyle;
            icon = new Icon(this, 0, 0, style::window_width, style::window_height, {}, {});
            icon->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Top));
            icon->image->setMargins({0, itStyle::icon::imageTopMargin, 0, itStyle::icon::imageBottomMargin});
            icon->image->set(app::meditationStyle::itStyle::icon::imageLogo, ImageTypeSpecifier::W_G);
            icon->text->setFont(itStyle::text::font);
            icon->text->setRichText(utils::translate("app_bell_meditation_put_down_and_wait"));
        }
    }

    bool ReadyGoingWindow::onInput(const InputEvent &inputEvent)
    {
        return true;
    }
} // namespace gui

A products/BellHybrid/apps/application-bell-meditation-timer/windows/ReadyGoingWindow.hpp => products/BellHybrid/apps/application-bell-meditation-timer/windows/ReadyGoingWindow.hpp +29 -0
@@ 0,0 1,29 @@
// 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 "ReadyGoingPresenter.hpp"

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

namespace gui
{
    class ReadyGoingWindow : public WindowWithTimer
    {
      private:
        std::shared_ptr<app::meditation::ReadyGoingPresenterContract::Presenter> presenter;

      public:
        ReadyGoingWindow(app::ApplicationCommon *app,
                         std::shared_ptr<app::meditation::ReadyGoingPresenterContract::Presenter> winPresenter);

        // virtual methods
        void buildInterface() override;
        bool onInput(const InputEvent &inputEvent) override;

      private:
        Icon *icon{};
    };
} // namespace gui

A products/BellHybrid/apps/application-bell-meditation-timer/windows/SessionEndWindow.cpp => products/BellHybrid/apps/application-bell-meditation-timer/windows/SessionEndWindow.cpp +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

#include "ApplicationBellMeditationTimer.hpp"
#include "SessionEndWindow.hpp"

namespace gui
{
    SessionEndWindow::SessionEndWindow(
        app::ApplicationCommon *app,
        std::shared_ptr<app::meditation::SessionEndedPresenterContract::Presenter> winPresenter)
        : BellFinishedWindow(app, gui::name::window::sessionEnded), presenter{std::move(winPresenter)}
    {
        timerCallback = [this](Item &, sys::Timer &) {
            presenter->activate();
            return true;
        };
    }

    bool SessionEndWindow::onInput(const InputEvent &inputEvent)
    {
        return true;
    }

    void SessionEndWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        BellFinishedWindow::onBeforeShow(mode, data);

        icon->image->set("big_namaste", ImageTypeSpecifier::W_G);
        icon->text->setRichText(utils::translate("app_bell_meditation_thank_you_for_session"));
    }
} // namespace gui

A products/BellHybrid/apps/application-bell-meditation-timer/windows/SessionEndWindow.hpp => products/BellHybrid/apps/application-bell-meditation-timer/windows/SessionEndWindow.hpp +26 -0
@@ 0,0 1,26 @@
// 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 "SessionEndedPresenter.hpp"

#include <common/windows/BellFinishedWindow.hpp>
#include <gui/widgets/Icon.hpp>

namespace gui
{
    class SessionEndWindow : public BellFinishedWindow
    {
      private:
        std::shared_ptr<app::meditation::SessionEndedPresenterContract::Presenter> presenter;

      public:
        SessionEndWindow(app::ApplicationCommon *app,
                         std::shared_ptr<app::meditation::SessionEndedPresenterContract::Presenter> winPresenter);

        // virtual methods
        bool onInput(const InputEvent &inputEvent) override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
    };
} // namespace gui

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

#include "ApplicationBellMeditationTimer.hpp"
#include "MeditationStyle.hpp"
#include "SessionPausedWindow.hpp"

namespace gui
{
    SessionPausedWindow::SessionPausedWindow(app::ApplicationCommon *app)
        : AppWindow(app, gui::name::window::sessionPaused)
    {
        buildInterface();
    }

    bool SessionPausedWindow::onInput(const gui::InputEvent &inputEvent)
    {
        if (inputEvent.isShortRelease(gui::KeyCode::KEY_RF) || inputEvent.isShortRelease(gui::KeyCode::KEY_ENTER)) {
            application->returnToPreviousWindow();
            return true;
        }

        return AppWindow::onInput(inputEvent);
    }

    void SessionPausedWindow::buildInterface()
    {
        AppWindow::buildInterface();

        statusBar->setVisible(false);
        header->setTitleVisibility(false);
        navBar->setVisible(false);

        if (icon == nullptr) {
            using namespace app::meditationStyle;

            icon = new Icon(this, 0, 0, style::window_width, style::window_height, {}, {});
            icon->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Top));
            icon->image->setMargins({0, itStyle::icon::imageTopMargin, 0, itStyle::icon::imageBottomMargin});
            icon->image->set(app::meditationStyle::itStyle::icon::imagePause, ImageTypeSpecifier::W_G);
            icon->text->setFont(itStyle::text::font);
            icon->text->setRichText(utils::translate("app_bell_meditation_paused"));
        }
    }
} // namespace gui

A products/BellHybrid/apps/application-bell-meditation-timer/windows/SessionPausedWindow.hpp => products/BellHybrid/apps/application-bell-meditation-timer/windows/SessionPausedWindow.hpp +25 -0
@@ 0,0 1,25 @@
// 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/Icon.hpp>
#include <Application.hpp>
#include <AppWindow.hpp>
#include <InputEvent.hpp>

namespace gui
{
    class SessionPausedWindow : public AppWindow
    {
      public:
        SessionPausedWindow(app::ApplicationCommon *app);

        // virtual methods
        void buildInterface() override;
        bool onInput(const gui::InputEvent &inputEvent) override;

      private:
        Icon *icon{};
    };
} // namespace gui