~aleteoryx/muditaos

25250fc9c8730d958eb4b7417bac2be0686a6eaf — Tomasz Rybarski 4 years ago 0fbe6e9
[BH-1314] Home Screen Layouts Selectable

Layouts can be changed in Settings-Clock Face
21 files changed, 649 insertions(+), 12 deletions(-)

M image/assets/lang/English.json
M image/user/db/settings_bell_002.sql
M module-apps/apps-common/CMakeLists.txt
A module-apps/apps-common/widgets/spinners/ItemSpinner.hpp
M module-apps/apps-common/widgets/spinners/SpinnerPolicies.hpp
M module-apps/apps-common/widgets/spinners/Spinners.hpp
M products/BellHybrid/apps/application-bell-main/ApplicationBellMain.cpp
M products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.cpp
M products/BellHybrid/apps/application-bell-settings/ApplicationBellSettings.cpp
M products/BellHybrid/apps/application-bell-settings/CMakeLists.txt
M products/BellHybrid/apps/application-bell-settings/include/application-bell-settings/ApplicationBellSettings.hpp
A products/BellHybrid/apps/application-bell-settings/models/LayoutModel.cpp
A products/BellHybrid/apps/application-bell-settings/presenter/LayoutWindowPresenter.cpp
A products/BellHybrid/apps/application-bell-settings/presenter/LayoutWindowPresenter.hpp
A products/BellHybrid/apps/application-bell-settings/windows/BellSettingsLayoutWindow.cpp
A products/BellHybrid/apps/application-bell-settings/windows/BellSettingsLayoutWindow.hpp
M products/BellHybrid/apps/application-bell-settings/windows/BellSettingsWindow.cpp
M products/BellHybrid/apps/common/CMakeLists.txt
A products/BellHybrid/apps/common/include/common/models/LayoutModel.hpp
M products/BellHybrid/apps/common/src/layouts/HomeScreenLayouts.cpp
M products/BellHybrid/services/db/include/db/SystemSettings.hpp
M image/assets/lang/English.json => image/assets/lang/English.json +1 -0
@@ 612,6 612,7 @@
  "app_bell_settings_time_units": "Time",
  "app_bell_settings_temp_scale": "Temperature scale",
  "app_bell_settings_language": "Language",
  "app_bell_settings_layout": "Clock face",
  "app_bell_settings_about": "About",
  "app_bell_settings_about_product": "Mudita Harmony",
  "app_bell_settings_about_version": "<text>OS version: <token>$VERSION</token></text>",

M image/user/db/settings_bell_002.sql => image/user/db/settings_bell_002.sql +2 -1
@@ 1,4 1,4 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

-- ----------- insert default values ----------------------


@@ 45,6 45,7 @@ INSERT OR IGNORE INTO settings_tab (path, value) VALUES
    ('bedtime_time','21:00'),
    ('bedtime_tone','Evening Horizon'),
    ('bedtime_duration','5'),
    ('layout','ClassicWithTemp'),
    ('\ServiceEink\\display_inverted_mode', '0');



M module-apps/apps-common/CMakeLists.txt => module-apps/apps-common/CMakeLists.txt +1 -0
@@ 69,6 69,7 @@ target_sources(apps-common
        models/SongsModelInterface.hpp

        widgets/spinners/GenericSpinner.hpp
        widgets/spinners/ItemSpinner.hpp
        widgets/spinners/SpinnerPolicies.hpp
        widgets/spinners/Spinners.hpp
)

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

#pragma once

#include "SpinnerPolicies.hpp"
#include <Item.hpp>

namespace gui
{
    template <typename Policy> class ItemSpinner : public Item
    {
      public:
        using Range = typename Policy::Range;
        using Type  = typename Policy::Type;

        using OnValueChanged = std::function<void(const Type &&)>;

        explicit ItemSpinner(Item *parent,
                             Range range,
                             Boundaries boundaries   = Boundaries::Continuous,
                             Orientation orientation = Orientation::Vertical);

        [[nodiscard]] Type getCurrentValue() const noexcept;
        void setCurrentValue(Type val);
        void setRange(Range range);

        void setFocusEdges(RectangleEdge edges);
        bool onInput(const InputEvent &inputEvent) override;
        [[nodiscard]] bool isAtMin() const;
        [[nodiscard]] bool isAtMax() const;

        OnValueChanged onValueChanged;

      private:
        void stepNext();
        void stepPrevious();
        bool isPreviousEvent(const InputEvent &inputEvent);
        bool isNextEvent(const InputEvent &inputEvent);
        void update();
        void invoke();

        Policy policy;
        RectangleEdge focusEdges = RectangleEdge::Bottom;
        Orientation orientation  = Orientation::Vertical;
        gui::Item *currentLayout = nullptr;
    };

    template <typename ValuePolicy>
    ItemSpinner<ValuePolicy>::ItemSpinner(Item *parent,
                                          ItemSpinner::Range range,
                                          Boundaries boundaries,
                                          Orientation orientation)
        : Item(), policy{range, boundaries}, orientation(orientation)
    {
        this->parent = parent;
        if (parent != nullptr) {
            parent->addWidget(this);
        }
    }

    template <typename ValuePolicy> void ItemSpinner<ValuePolicy>::setFocusEdges(RectangleEdge edges)
    {
        focusEdges = edges;
    }

    template <typename Policy> void ItemSpinner<Policy>::setRange(Range range)
    {
        policy.updateRange(range);
        update();
    }

    template <typename ValuePolicy> void ItemSpinner<ValuePolicy>::setCurrentValue(const Type val)
    {
        policy.set(val);
        update();
    }

    template <typename Policy> typename ItemSpinner<Policy>::Type ItemSpinner<Policy>::getCurrentValue() const noexcept
    {
        return policy.get();
    }

    template <typename ValuePolicy> bool ItemSpinner<ValuePolicy>::isPreviousEvent(const InputEvent &inputEvent)
    {
        return (orientation == Orientation::Vertical && inputEvent.is(KeyCode::KEY_DOWN)) ||
               (orientation == Orientation::Horizontal && inputEvent.is(KeyCode::KEY_LEFT));
    }

    template <typename ValuePolicy> bool ItemSpinner<ValuePolicy>::isNextEvent(const InputEvent &inputEvent)
    {
        return (orientation == Orientation::Vertical && inputEvent.is(KeyCode::KEY_UP)) ||
               (orientation == Orientation::Horizontal && inputEvent.is(KeyCode::KEY_RIGHT));
    }

    template <typename ValuePolicy> bool ItemSpinner<ValuePolicy>::onInput(const InputEvent &inputEvent)
    {
        if (inputEvent.isShortRelease()) {
            if (isPreviousEvent(inputEvent)) {
                stepPrevious();
                return true;
            }
            else if (isNextEvent(inputEvent)) {
                stepNext();
                return true;
            }
        }
        return false;
    }

    template <typename Policy> void ItemSpinner<Policy>::stepNext()
    {
        if (policy.next()) {
            update();
            invoke();
        }
    }

    template <typename Policy> void ItemSpinner<Policy>::stepPrevious()
    {
        if (policy.previous()) {
            update();
            invoke();
        }
    }

    template <typename Policy> void ItemSpinner<Policy>::update()
    {
        if (currentLayout) {
            this->removeWidget(currentLayout);
        }
        currentLayout = policy.get();
        this->addWidget(currentLayout);
        informContentChanged();
    }
    template <typename Policy> void ItemSpinner<Policy>::invoke()
    {
        if (onValueChanged) {
            onValueChanged(getCurrentValue());
        }
    }
    template <typename Policy> bool ItemSpinner<Policy>::isAtMin() const
    {
        return policy.isAtMin();
    }
    template <typename Policy> bool ItemSpinner<Policy>::isAtMax() const
    {
        return policy.isAtMax();
    }
} // namespace gui

M module-apps/apps-common/widgets/spinners/SpinnerPolicies.hpp => module-apps/apps-common/widgets/spinners/SpinnerPolicies.hpp +89 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 105,6 105,94 @@ namespace gui
        const Boundaries boundaries{};
    };

    template <typename ValType> class WidgetPolicy
    {
      public:
        using Range = std::vector<ValType>;
        using Type  = ValType;

        WidgetPolicy(Range range, Boundaries boundaries) : range{range}, boundaries{boundaries}
        {}

        ValType get() const
        {
            return pos < range.size() ? range[pos] : ValType{};
        }

        void set(ValType val)
        {
            for (auto i = 0U; i < range.size(); i++) {
                if (range[i] == val) {
                    pos = i;
                    break;
                }
            }
        }

        bool next()
        {
            bool ret{true};
            if (pos >= upRange()) {
                if (boundaries == Boundaries::Continuous) {
                    pos = 0;
                }
                else {
                    pos = upRange();
                    ret = false;
                }
            }
            else {
                pos++;
            }
            return ret;
        }

        bool previous()
        {
            bool ret{true};
            if (pos <= 0) {
                if (boundaries == Boundaries::Continuous) {
                    pos = upRange();
                }
                else {
                    pos = 0;
                    ret = false;
                }
            }
            else {
                pos--;
            }
            return ret;
        }

        void updateRange(Range newRange)
        {
            if (range != newRange) {
                range = newRange;
                pos   = 0;
            }
        }

        [[nodiscard]] bool isAtMin() const
        {
            return pos == 0;
        }
        [[nodiscard]] bool isAtMax() const
        {
            return pos == upRange();
        }

      private:
        std::uint32_t upRange() const
        {
            return range.size() - 1;
        }

        Range range;
        std::uint32_t pos{};
        const Boundaries boundaries{};
    };

    template <typename ValType> class DefaultNumericFormatter
    {
      public:

M module-apps/apps-common/widgets/spinners/Spinners.hpp => module-apps/apps-common/widgets/spinners/Spinners.hpp +3 -1
@@ 1,9 1,10 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "GenericSpinner.hpp"
#include "ItemSpinner.hpp"

namespace gui
{


@@ 11,6 12,7 @@ namespace gui
    using UIntegerSpinner      = GenericSpinner<NumericPolicy<std::uint32_t>>;
    using UIntegerSpinnerFixed = GenericSpinner<NumericPolicy<std::uint32_t, FixedSizeFormatter<std::uint32_t, 2>>>;
    using IntegerSpinner       = GenericSpinner<NumericPolicy<std::int32_t>>;
    using WidgetSpinner        = ItemSpinner<WidgetPolicy<Item *>>;

    template <typename ModelType> using ModelDelegateSpinner = GenericSpinner<ModelDelegatePolicy<ModelType>>;
} // namespace gui

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

#include "include/application-bell-main/ApplicationBellMain.hpp"


@@ 15,6 15,7 @@
#include <common/layouts/HomeScreenLayouts.hpp>
#include <common/models/AlarmModel.hpp>
#include <common/models/TimeModel.hpp>
#include <common/models/LayoutModel.hpp>
#include <common/windows/BellWelcomeWindow.hpp>
#include <common/windows/BellFactoryReset.hpp>
#include <service-db/DBNotificationMessage.hpp>


@@ 91,9 92,16 @@ namespace app
    void ApplicationBellMain::createUserInterface()
    {
        windowsFactory.attach(gui::name::window::main_window, [this](ApplicationCommon *app, const std::string &name) {
            auto window = std::make_unique<gui::BellHomeScreenWindow>(app, homeScreenPresenter);
            // TODO: To be replaced with settings db read
            setHomeScreenLayout("ClassicWithTemp");
            auto timeModel        = std::make_unique<app::TimeModel>();
            auto batteryModel     = std::make_unique<app::home_screen::BatteryModel>(app);
            auto temperatureModel = std::make_unique<app::home_screen::TemperatureModel>(app);
            auto alarmModel       = std::make_unique<app::AlarmModel>(app);
            auto layoutModel      = std::make_unique<bell_settings::LayoutModel>(app);
            homeScreenPresenter   = std::make_shared<app::home_screen::HomeScreenPresenter>(
                app, std::move(alarmModel), std::move(batteryModel), std::move(temperatureModel), std::move(timeModel));
            auto window         = std::make_unique<gui::BellHomeScreenWindow>(app, homeScreenPresenter);
            auto selectedLayout = layoutModel->getValue();
            setHomeScreenLayout(selectedLayout);
            return window;
        });


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

#include "application-bell-main/presenters/HomeScreenPresenter.hpp"

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

#include "ApplicationBellSettings.hpp"
#include "presenter/TimeUnitsPresenter.hpp"
#include "presenter/LayoutWindowPresenter.hpp"
#include "models/TemperatureUnitModel.hpp"
#include "models/AboutYourBellModel.hpp"
#include "models/alarm_settings/AlarmSettingsListItemProvider.hpp"


@@ 17,6 18,7 @@
#include "presenter/FrontlightPresenter.hpp"
#include "windows/AboutYourBellWindow.hpp"
#include "windows/BellSettingsLanguageWindow.hpp"
#include "windows/BellSettingsLayoutWindow.hpp"
#include "windows/BellSettingsFrontlightWindow.hpp"
#include "windows/alarm_settings/BellSettingsAlarmSettingsMenuWindow.hpp"
#include "windows/alarm_settings/BellSettingsAlarmSettingsSnoozeWindow.hpp"


@@ 30,12 32,15 @@

#include <AlarmSoundPaths.hpp>
#include <apps-common/windows/Dialog.hpp>
#include <common/layouts/BaseHomeScreenLayoutProvider.hpp>
#include <common/BellPowerOffPresenter.hpp>
#include <common/models/BedtimeModel.hpp>
#include <common/models/LayoutModel.hpp>
#include <common/windows/BellFinishedWindow.hpp>
#include <common/windows/BellTurnOffWindow.hpp>
#include <common/popups/BellTurnOffOptionWindow.hpp>
#include <common/models/AudioModel.hpp>
#include <common/models/TimeModel.hpp>
#include <common/models/AlarmSettingsModel.hpp>
#include <service-evtmgr/EventManagerServiceAPI.hpp>
#include <service-appmgr/messages/GetCurrentDisplayLanguageResponse.hpp>


@@ 89,6 94,15 @@ namespace app
            });

        windowsFactory.attach(
            gui::window::name::bellSettingsLayout, [this](ApplicationCommon *app, const std::string &name) {
                auto layoutModel = std::make_unique<bell_settings::LayoutModel>(this);
                auto timeModel   = std::make_unique<app::TimeModel>();
                auto presenter   = std::make_unique<bell_settings::LayoutWindowPresenter>(
                    this, std::move(layoutModel), std::move(timeModel));
                return std::make_unique<gui::BellSettingsLayoutWindow>(app, std::move(presenter), name);
            });

        windowsFactory.attach(
            gui::BellSettingsFrontlightWindow::name, [](ApplicationCommon *app, const std::string &name) {
                auto model    = std::make_unique<bell_settings::FrontlightModel>(app);
                auto provider = std::make_shared<bell_settings::FrontlightListItemProvider>(*model);

M products/BellHybrid/apps/application-bell-settings/CMakeLists.txt => products/BellHybrid/apps/application-bell-settings/CMakeLists.txt +6 -0
@@ 35,12 35,14 @@ target_sources(application-bell-settings
        models/alarm_settings/SnoozeListItemProvider.cpp
        models/alarm_settings/SnoozeSettingsModel.cpp
        models/alarm_settings/SettingsListItemProvider.cpp
        models/LayoutModel.cpp

        presenter/BedtimeSettingsPresenter.cpp
        presenter/TimeUnitsPresenter.cpp
        presenter/FrontlightPresenter.cpp
        presenter/AboutYourBellWindowPresenter.cpp
        presenter/LanguageWindowPresenter.cpp
        presenter/LayoutWindowPresenter.cpp
        presenter/alarm_settings/AlarmSettingsPresenter.cpp
        presenter/alarm_settings/PrewakeUpPresenter.cpp
        presenter/alarm_settings/SnoozePresenter.cpp


@@ 57,6 59,7 @@ target_sources(application-bell-settings
        windows/BellSettingsWindow.cpp
        windows/AboutYourBellWindow.cpp
        windows/BellSettingsLanguageWindow.cpp
        windows/BellSettingsLayoutWindow.cpp
        windows/BellSettingsFrontlightWindow.cpp
        windows/alarm_settings/BellSettingsAlarmSettingsSnoozeWindow.cpp
        windows/alarm_settings/BellSettingsAlarmSettingsMenuWindow.cpp


@@ 79,6 82,7 @@ target_sources(application-bell-settings
        presenter/FrontlightPresenter.hpp
        presenter/AboutYourBellWindowPresenter.hpp
        presenter/LanguageWindowPresenter.hpp
        presenter/LayoutWindowPresenter.hpp
        presenter/alarm_settings/AlarmSettingsPresenter.hpp
        presenter/alarm_settings/PrewakeUpPresenter.hpp
        presenter/alarm_settings/SnoozePresenter.hpp


@@ 96,6 100,7 @@ target_sources(application-bell-settings
        windows/BellSettingsWindow.hpp
        windows/AboutYourBellWindow.hpp
        windows/BellSettingsLanguageWindow.hpp
        windows/BellSettingsLayoutWindow.hpp
        windows/BellSettingsFrontlightWindow.hpp
        windows/alarm_settings/BellSettingsAlarmSettingsSnoozeWindow.hpp
        windows/alarm_settings/BellSettingsAlarmSettingsMenuWindow.hpp


@@ 119,6 124,7 @@ target_link_libraries(application-bell-settings
        bell::db
        bell::alarms
        bell::app-main
        bell::appmgr
        service-appmgr
        apps-common
    PUBLIC

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

#pragma once


@@ 17,6 17,7 @@ namespace gui::window::name
    inline constexpr auto bellSettingsFrontlight   = "BellSettingsFrontlight";
    inline constexpr auto bellSettingsHomeView     = "BellSettingsHomeView";
    inline constexpr auto bellSettingsLanguage     = "BellSettingsLanguage";
    inline constexpr auto bellSettingsLayout       = "BellSettingsLayout";
    inline constexpr auto bellSettingsBedtimeTone  = "BellSettingsBedtimeTone";
    inline constexpr auto bellSettingsFactoryReset = "BellSettingsFactoryReset";
} // namespace gui::window::name

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

#include <common/models/LayoutModel.hpp>

#include <apps-common/ApplicationCommon.hpp>
#include <db/SystemSettings.hpp>

namespace app::bell_settings
{

    LayoutModel::LayoutModel(ApplicationCommon *app)
    {
        settings.init(service::ServiceProxy{app->weak_from_this()});
    }

    std::string LayoutModel::getValue() const
    {
        return settings.getValue(bell::settings::Layout::layout, settings::SettingsScope::Global);
    }

    void LayoutModel::setValue(const std::string &value) const
    {
        settings.setValue(bell::settings::Layout::layout, value, settings::SettingsScope::Global);
    }
} // namespace app::bell_settings
\ No newline at end of file

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

#include "LayoutWindowPresenter.hpp"
#include <service-appmgr/Controller.hpp>
#include <common/layouts/HomeScreenLayouts.hpp>
#include <common/layouts/BaseHomeScreenLayoutProvider.hpp>
#include <appmgr/messages/ChangeHomescreenLayoutMessage.hpp>

#include <EventStore.hpp>
#include <Temperature.hpp>
#include <service-appmgr/Constants.hpp>

constexpr auto alarmTime              = 1000 * 60 * 60 * 12;
constexpr auto clockTime              = 1000 * 60 * 60 * 12;
constexpr Store::Battery batteryState = {
    .levelState = Store::Battery::LevelState::Normal,
    .state      = Store::Battery::State::Discharging,
    .level      = 100,
};
constexpr utils::temperature::Temperature temperature = {
    .unit  = utils::temperature::Temperature::Unit::Celsius,
    .value = 21.0f,
};

namespace app::bell_settings
{
    LayoutWindowPresenter::LayoutWindowPresenter(app::ApplicationCommon *app,
                                                 std::unique_ptr<AbstractLayoutModel> &&layoutModel,
                                                 std::unique_ptr<AbstractTimeModel> &&timeModel)
        : app(app), layoutModel{std::move(layoutModel)}, timeModel{std::move(timeModel)}
    {
        initLayoutOptions();
    }

    std::vector<gui::Item *> LayoutWindowPresenter::getLayouts() const
    {
        std::vector<gui::Item *> layouts;

        for (auto const &option : layoutOptions) {
            layouts.push_back(option.first);
        }

        return layouts;
    }

    gui::Item *LayoutWindowPresenter::getSelectedLayout() const
    {
        const auto layoutSelected = layoutModel->getValue();

        for (auto const &option : layoutOptions) {
            if (option.second == layoutSelected) {
                return option.first;
            }
        }

        return layoutOptions.at(0).first;
    }

    void LayoutWindowPresenter::setLayout(gui::Item *selectedLayout)
    {
        for (auto const &option : layoutOptions) {
            if (option.first == selectedLayout) {
                layoutModel->setValue(option.second);
                auto layoutChangeRequest = std::make_shared<ChangeHomescreenLayoutMessage>(option.second);
                app->bus.sendUnicast(layoutChangeRequest, service::name::appmgr);
                break;
            }
        }
    }

    void LayoutWindowPresenter::initLayoutOptions()
    {
        auto layoutsList = gui::homeScreenLayouts();

        auto layoutClassicWithTemp = layoutsList.at("ClassicWithTemp")();
        layoutClassicWithTemp->setAlarmEdit(false);
        layoutClassicWithTemp->setAlarmActive(true);
        layoutClassicWithTemp->setTime(clockTime);
        // layoutClassicWithTemp->setTimeFormat(timeModel->getTimeFormat());
        layoutClassicWithTemp->setAlarmTime(alarmTime);
        layoutClassicWithTemp->setBatteryLevelState(batteryState);
        layoutClassicWithTemp->setViewState(app::home_screen::ViewState::Activated);
        layoutClassicWithTemp->setTemperature(temperature);

        auto layoutClassicWithBattery = layoutsList.at("ClassicWithBattery")();
        layoutClassicWithBattery->setAlarmEdit(false);
        layoutClassicWithBattery->setAlarmActive(true);
        layoutClassicWithBattery->setTime(clockTime);
        // Commented out to remove "AM"/"PM" from time spinner
        // layoutClassicWithBattery->setTimeFormat(timeModel->getTimeFormat());
        layoutClassicWithBattery->setAlarmTime(alarmTime);
        layoutClassicWithBattery->setBatteryLevelState(batteryState);
        layoutClassicWithBattery->setViewState(app::home_screen::ViewState::Activated);

        auto layoutClassicWithAmPm = layoutsList.at("ClassicWithAmPm")();
        layoutClassicWithAmPm->setAlarmEdit(false);
        layoutClassicWithAmPm->setAlarmActive(true);
        layoutClassicWithAmPm->setTime(clockTime);
        // layoutClassicWithAmPm->setTimeFormat(timeModel->getTimeFormat());
        layoutClassicWithAmPm->setAlarmTime(alarmTime);
        layoutClassicWithAmPm->setBatteryLevelState(batteryState);
        layoutClassicWithAmPm->setViewState(app::home_screen::ViewState::Activated);

        auto layoutClassic = layoutsList.at("Classic")();
        layoutClassic->setAlarmEdit(false);
        layoutClassic->setAlarmActive(true);
        layoutClassic->setTime(clockTime);
        // layoutClassic->setTimeFormat(timeModel->getTimeFormat());
        layoutClassic->setAlarmTime(alarmTime);
        layoutClassic->setBatteryLevelState(batteryState);
        layoutClassic->setViewState(app::home_screen::ViewState::Activated);

        layoutOptions.push_back({layoutClassicWithTemp->getLayout(), "ClassicWithTemp"});
        layoutOptions.push_back({layoutClassicWithBattery->getLayout(), "ClassicWithBattery"});
        layoutOptions.push_back({layoutClassicWithAmPm->getLayout(), "ClassicWithAmPm"});
        layoutOptions.push_back({layoutClassic->getLayout(), "Classic"});
    }
} // namespace app::bell_settings

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

#pragma once

#include <apps-common/BasePresenter.hpp>
#include <apps-common/ApplicationCommon.hpp>
#include <common/models/LayoutModel.hpp>
#include <common/models/TimeModel.hpp>
#include <common/layouts/HomeScreenLayouts.hpp>

#include <vector>
#include <string>
#include <Item.hpp>

namespace app::bell_settings
{
    class LayoutWindowContract
    {
      public:
        class View
        {
          public:
            virtual ~View() = default;
        };

        class Presenter : public BasePresenter<LayoutWindowContract::View>
        {
          public:
            virtual std::vector<gui::Item *> getLayouts() const = 0;
            virtual gui::Item *getSelectedLayout() const        = 0;
            virtual void setLayout(gui::Item *selectedLayout)   = 0;
        };
    };

    class LayoutWindowPresenter : public LayoutWindowContract::Presenter
    {
      private:
        app::ApplicationCommon *app{};
        std::unique_ptr<AbstractLayoutModel> layoutModel;
        std::unique_ptr<AbstractTimeModel> timeModel;
        std::vector<std::pair<gui::Item *, const std::string>> layoutOptions;
        void initLayoutOptions();

      public:
        explicit LayoutWindowPresenter(app::ApplicationCommon *app,
                                       std::unique_ptr<AbstractLayoutModel> &&layoutModel,
                                       std::unique_ptr<AbstractTimeModel> &&timeModel);

        std::vector<gui::Item *> getLayouts() const override;
        gui::Item *getSelectedLayout() const override;
        void setLayout(gui::Item *selectedLayout) override;
    };
} // namespace app::bell_settings

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

#include "BellSettingsLayoutWindow.hpp"
#include <common/options/OptionBellMenu.hpp>
#include <common/layouts/HomeScreenLayouts.hpp>
#include <common/windows/BellFinishedWindow.hpp>

namespace gui
{
    BellSettingsLayoutWindow::BellSettingsLayoutWindow(
        app::ApplicationCommon *app,
        std::unique_ptr<app::bell_settings::LayoutWindowPresenter::Presenter> &&presenter,
        const std::string &name)
        : AppWindow(app, name), presenter{std::move(presenter)}
    {
        this->presenter->attach(this);
        buildInterface();
    }

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

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

        auto layouts = presenter->getLayouts();
        spinner      = new WidgetSpinner(this, {layouts.begin(), layouts.end()}, Boundaries::Fixed);
        spinner->setSize(style::window_width, style::window_height);
        spinner->setAlignment(Alignment(Alignment::Horizontal::Center, Alignment::Vertical::Center));
        spinner->setFocusEdges(RectangleEdge::None);
        auto selectedLayout = presenter->getSelectedLayout();
        spinner->setCurrentValue(selectedLayout);

        createArrowsOverlay(0, 0, style::window_width, style::window_height);
        arrowLeft->setVisible(!spinner->isAtMin());
        arrowRight->setVisible(!spinner->isAtMax());

        spinner->onValueChanged = [this](const auto &) {
            arrowLeft->setVisible(!spinner->isAtMin());
            arrowRight->setVisible(!spinner->isAtMax());
        };
        setFocusItem(spinner);
    }

    void BellSettingsLayoutWindow::createArrowsOverlay(unsigned int x, unsigned y, unsigned int w, unsigned int h)
    {
        auto arrowsOverlay = new HBox{this, x, y, w, h};
        arrowsOverlay->setEdges(gui::RectangleEdge::None);
        arrowLeft = new Image("bell_arrow_left_W_M");
        arrowLeft->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center));
        arrowLeft->activeItem = false;
        arrowLeft->setEdges(RectangleEdge::None);
        arrowsOverlay->addWidget(arrowLeft);

        arrowRight = new Image("bell_arrow_right_W_M");
        arrowRight->setAlignment(Alignment(Alignment::Horizontal::Right, Alignment::Vertical::Center));
        arrowRight->activeItem = false;
        arrowRight->setEdges(RectangleEdge::None);

        auto rectFiller = new gui::Rect(arrowsOverlay,
                                        0U,
                                        0U,
                                        arrowsOverlay->getWidth() - arrowLeft->getWidth() - arrowRight->getWidth(),
                                        arrowsOverlay->getHeight());

        rectFiller->setMaximumSize(arrowsOverlay->getWidth(), arrowsOverlay->getHeight());
        rectFiller->setEdges(RectangleEdge::None);

        arrowsOverlay->addWidget(arrowRight);
    }
    bool BellSettingsLayoutWindow::onInput(const InputEvent &inputEvent)
    {
        if (spinner->onInput(inputEvent)) {
            return true;
        }
        else if (inputEvent.isShortRelease(KeyCode::KEY_ENTER)) {
            presenter->setLayout(spinner->getCurrentValue());
            application->switchWindow(
                window::bell_finished::defaultName,
                BellFinishedWindowData::Factory::create("circle_success_big", gui::name::window::main_window));
            return true;
        }
        return AppWindow::onInput(inputEvent);
    }

} // namespace gui

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

#pragma once

#include <ApplicationBellSettings.hpp>
#include <presenter/LayoutWindowPresenter.hpp>
#include <apps-common/widgets/spinners/Spinners.hpp>

#include <AppWindow.hpp>

namespace gui
{
    class BellSettingsLayoutWindow : public AppWindow, public app::bell_settings::LayoutWindowContract::View
    {
      public:
        BellSettingsLayoutWindow(app::ApplicationCommon *app,
                                 std::unique_ptr<app::bell_settings::LayoutWindowPresenter::Presenter> &&presenter,
                                 const std::string &name = gui::window::name::bellSettingsLayout);

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

      private:
        void createArrowsOverlay(unsigned int x, unsigned y, unsigned int w, unsigned int h);

        std::unique_ptr<app::bell_settings::LayoutWindowPresenter::Presenter> presenter;
        Image *arrowLeft{};
        Image *arrowRight{};
        WidgetSpinner *spinner = nullptr;
    };
} // namespace gui

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

#include "AboutYourBellWindow.hpp"


@@ 75,6 75,8 @@ namespace gui
                this));
        };

        addWinSettings(
            utils::translate("app_bell_settings_layout"), gui::window::name::bellSettingsLayout, defaultCallback);
        addWinSettings(utils::translate("app_bell_settings_alarm_settings"),
                       BellSettingsAlarmSettingsMenuWindow::name,
                       defaultCallback);

M products/BellHybrid/apps/common/CMakeLists.txt => products/BellHybrid/apps/common/CMakeLists.txt +1 -0
@@ 76,6 76,7 @@ target_sources(application-bell-common
        include/common/models/FrontlightModel.hpp
        include/common/models/AlarmSettingsModel.hpp
        include/common/models/AbstractAlarmSettingsModel.hpp
        include/common/models/LayoutModel.hpp
        include/common/popups/presenter/AlarmActivatedPresenter.hpp
        include/common/popups/AlarmActivatedWindow.hpp
        include/common/popups/AlarmDeactivatedWindow.hpp

A products/BellHybrid/apps/common/include/common/models/LayoutModel.hpp => products/BellHybrid/apps/common/include/common/models/LayoutModel.hpp +36 -0
@@ 0,0 1,36 @@
// Copyright (c) 2017-2022, 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>
#include <service-db/Settings.hpp>
#include <string>

namespace app
{
    class ApplicationCommon;
}

namespace app::bell_settings
{

    class AbstractLayoutModel
    {
      public:
        virtual ~AbstractLayoutModel()                        = default;
        virtual std::string getValue() const                  = 0;
        virtual void setValue(const std::string &value) const = 0;
    };

    class LayoutModel : public AbstractLayoutModel
    {
      public:
        explicit LayoutModel(ApplicationCommon *app);
        std::string getValue() const override;
        void setValue(const std::string &value) const override;

      private:
        mutable settings::Settings settings;
    };
} // namespace app::bell_settings
\ No newline at end of file

M products/BellHybrid/apps/common/src/layouts/HomeScreenLayouts.cpp => products/BellHybrid/apps/common/src/layouts/HomeScreenLayouts.cpp +2 -0
@@ 3,6 3,7 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <common/layouts/HomeScreenLayouts.hpp>
#include <common/layouts/HomeScreenLayoutClassic.hpp>
#include <common/layouts/HomeScreenLayoutClassicWithAmPm.hpp>
#include <common/layouts/HomeScreenLayoutClassicWithBattery.hpp>
#include <common/layouts/HomeScreenLayoutClassicWithTemp.hpp>


@@ 12,6 13,7 @@ namespace gui
    std::map<std::string, LayoutGenerator> homeScreenLayouts()
    {
        return {{"ClassicWithTemp", []() { return new HomeScreenLayoutClassicWithTemp("ClassicWithTemp"); }},
                {"Classic", []() { return new HomeScreenLayoutClassic("Classic"); }},
                {"ClassicWithAmPm", []() { return new HomeScreenLayoutClassicWithAmPm("ClassicWithAmPm"); }},
                {"ClassicWithBattery", []() { return new HomeScreenLayoutClassicWithBattery("ClassicWithBattery"); }}};
    };

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

#pragma once


@@ 35,4 35,8 @@ namespace bell::settings
        constexpr inline auto tone     = "bedtime_tone";
        constexpr inline auto duration = "bedtime_duration";
    } // namespace Bedtime
    namespace Layout
    {
        constexpr inline auto layout = "layout";
    } // namespace Layout
};    // namespace bell::settings