~aleteoryx/muditaos

a6173976722df15dfc4db3cd0581786e3b5fb747 — KacperLewandowski 5 years ago 991226b
[EGD-4788] Add custom repeat window for the alarm application

When choosing alarm repeat option now it is
possible to choose specific days of the week
and their combinations.
24 files changed, 622 insertions(+), 7 deletions(-)

M changelog.md
M image/assets/lang/English.json
M module-apps/application-alarm-clock/ApplicationAlarmClock.cpp
M module-apps/application-alarm-clock/CMakeLists.txt
A module-apps/application-alarm-clock/data/AlarmsData.cpp
M module-apps/application-alarm-clock/data/AlarmsData.hpp
A module-apps/application-alarm-clock/models/CustomRepeatModel.cpp
A module-apps/application-alarm-clock/models/CustomRepeatModel.hpp
M module-apps/application-alarm-clock/models/NewEditAlarmModel.cpp
M module-apps/application-alarm-clock/models/NewEditAlarmModel.hpp
M module-apps/application-alarm-clock/presenter/AlarmClockEditWindowPresenter.cpp
M module-apps/application-alarm-clock/presenter/AlarmClockEditWindowPresenter.hpp
A module-apps/application-alarm-clock/presenter/CustomRepeatWindowPresenter.cpp
A module-apps/application-alarm-clock/presenter/CustomRepeatWindowPresenter.hpp
M module-apps/application-alarm-clock/widgets/AlarmClockStyle.hpp
M module-apps/application-alarm-clock/widgets/AlarmInternalListItem.hpp
M module-apps/application-alarm-clock/widgets/AlarmItem.cpp
M module-apps/application-alarm-clock/widgets/AlarmOptionsItem.cpp
M module-apps/application-alarm-clock/widgets/AlarmOptionsItem.hpp
A module-apps/application-alarm-clock/widgets/CustomCheckBoxWithLabel.cpp
A module-apps/application-alarm-clock/widgets/CustomCheckBoxWithLabel.hpp
A module-apps/application-alarm-clock/windows/CustomRepeatWindow.cpp
A module-apps/application-alarm-clock/windows/CustomRepeatWindow.hpp
M module-apps/application-alarm-clock/windows/NewEditAlarmWindow.cpp
M changelog.md => changelog.md +1 -0
@@ 7,6 7,7 @@
* `[PowerManagement]` Critial battery level notification to SystemManager.
* `[Bluetooth]`  Add settings storage to bluetooth related items
* Add Bluetooth virtual audio device.
* Add custom repeat window for the alarm application

## [0.51.1 2020-12-18]


M image/assets/lang/English.json => image/assets/lang/English.json +7 -0
@@ 35,6 35,13 @@
  "common_fr": "FR",
  "common_sa": "SA",
  "common_su": "SU",
  "common_mon": "Mon",
  "common_tue": "Tue",
  "common_wed": "Wed",
  "common_thu": "Thu",
  "common_fri": "Fri",
  "common_sat": "Sat",
  "common_sun": "Sun",
  "common_monday": "Monday",
  "common_tuesday": "Tuesday",
  "common_wednesday": "Wednesday",

M module-apps/application-alarm-clock/ApplicationAlarmClock.cpp => module-apps/application-alarm-clock/ApplicationAlarmClock.cpp +9 -0
@@ 4,8 4,10 @@
#include "ApplicationAlarmClock.hpp"
#include "application-alarm-clock/windows/AlarmClockMainWindow.hpp"
#include "application-alarm-clock/windows/NewEditAlarmWindow.hpp"
#include "application-alarm-clock/windows/CustomRepeatWindow.hpp"
#include "application-alarm-clock/widgets/AlarmClockStyle.hpp"
#include "application-alarm-clock/presenter/AlarmClockMainWindowPresenter.hpp"
#include "application-alarm-clock/presenter/CustomRepeatWindowPresenter.hpp"
#include "windows/Dialog.hpp"
#include "windows/AppWindow.hpp"
#include "windows/OptionWindow.hpp"


@@ 101,6 103,13 @@ namespace app
                return std::make_unique<alarmClock::NewEditAlarmWindow>(app, std::move(presenter));
            });
        windowsFactory.attach(
            style::alarmClock::window::name::customRepeat, [](Application *app, const std::string &name) {
                auto alarmsRepository = std::make_unique<alarmClock::AlarmsDBRepository>(app);
                auto alarmsProvider = std::make_shared<alarmClock::CustomRepeatModel>(app, std::move(alarmsRepository));
                auto presenter      = std::make_unique<alarmClock::CustomRepeatWindowPresenter>(alarmsProvider);
                return std::make_unique<alarmClock::CustomRepeatWindow>(app, std::move(presenter));
            });
        windowsFactory.attach(
            utils::localize.get("app_alarm_clock_options_title"),
            [](Application *app, const std::string &name) { return std::make_unique<gui::OptionWindow>(app, name); });


M module-apps/application-alarm-clock/CMakeLists.txt => module-apps/application-alarm-clock/CMakeLists.txt +5 -0
@@ 5,14 5,19 @@ target_sources( ${PROJECT_NAME}
        "${CMAKE_CURRENT_LIST_DIR}/windows/AlarmClockMainWindow.cpp"
        "${CMAKE_CURRENT_LIST_DIR}/windows/AlarmClockOptions.cpp"
        "${CMAKE_CURRENT_LIST_DIR}/windows/NewEditAlarmWindow.cpp"
        "${CMAKE_CURRENT_LIST_DIR}/windows/CustomRepeatWindow.cpp"
        "${CMAKE_CURRENT_LIST_DIR}/models/AlarmsModel.cpp"
        "${CMAKE_CURRENT_LIST_DIR}/models/AlarmsRepository.cpp"
        "${CMAKE_CURRENT_LIST_DIR}/models/NewEditAlarmModel.cpp"
        "${CMAKE_CURRENT_LIST_DIR}/models/CustomRepeatModel.cpp"
        "${CMAKE_CURRENT_LIST_DIR}/widgets/AlarmItem.cpp"
        "${CMAKE_CURRENT_LIST_DIR}/widgets/AlarmTimeItem.cpp"
        "${CMAKE_CURRENT_LIST_DIR}/widgets/AlarmOptionsItem.cpp"
        "${CMAKE_CURRENT_LIST_DIR}/widgets/CustomCheckBoxWithLabel.cpp"
        "${CMAKE_CURRENT_LIST_DIR}/presenter/AlarmClockMainWindowPresenter.cpp"
        "${CMAKE_CURRENT_LIST_DIR}/presenter/AlarmClockEditWindowPresenter.cpp"
        "${CMAKE_CURRENT_LIST_DIR}/presenter/CustomRepeatWindowPresenter.cpp"
        "${CMAKE_CURRENT_LIST_DIR}/data/AlarmsData.cpp"
    PUBLIC
        "${CMAKE_CURRENT_LIST_DIR}/ApplicationAlarmClock.hpp"
        "${CMAKE_CURRENT_LIST_DIR}/widgets/AlarmClockStyle.hpp"

A module-apps/application-alarm-clock/data/AlarmsData.cpp => module-apps/application-alarm-clock/data/AlarmsData.cpp +47 -0
@@ 0,0 1,47 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "AlarmsData.hpp"

static const std::map<WeekDayIso, const char *> weekDaysAbbreviation = {{WeekDayIso::Monday, "common_mon"},
                                                                        {WeekDayIso::Tuesday, "common_tue"},
                                                                        {WeekDayIso::Wednesday, "common_wed"},
                                                                        {WeekDayIso::Thursday, "common_thu"},
                                                                        {WeekDayIso::Friday, "common_fri"},
                                                                        {WeekDayIso::Saturday, "common_sat"},
                                                                        {WeekDayIso::Sunday, "common_sun"}};

CustomRepeatValueParser::CustomRepeatValueParser(uint32_t repeatValue)
{
    OptionParser parser;
    weekDayData = parser.setWeekDayOptions(repeatValue, std::make_unique<WeekDaysRepeatData>());
}

std::string CustomRepeatValueParser::getWeekDaysText() const
{
    std::string weekDaysText;
    for (auto const &[key, val] : weekDaysAbbreviation) {
        if (weekDayData->getData(static_cast<uint32_t>(key))) {
            weekDaysText += utils::localize.get(val) + ", ";
        }
    }
    if (!weekDaysText.empty()) {
        weekDaysText.erase(weekDaysText.end() - 2);
    }
    return weekDaysText;
}

bool CustomRepeatValueParser::isCustomValueWeekDays() const
{
    return weekDayData->getData(static_cast<uint32_t>(WeekDayIso::Monday)) &&
           weekDayData->getData(static_cast<uint32_t>(WeekDayIso::Tuesday)) &&
           weekDayData->getData(static_cast<uint32_t>(WeekDayIso::Wednesday)) &&
           weekDayData->getData(static_cast<uint32_t>(WeekDayIso::Thursday)) &&
           weekDayData->getData(static_cast<uint32_t>(WeekDayIso::Friday));
}

bool CustomRepeatValueParser::isCustomValueEveryday() const
{
    return isCustomValueWeekDays() && weekDayData->getData(static_cast<uint32_t>(WeekDayIso::Saturday)) &&
           weekDayData->getData(static_cast<uint32_t>(WeekDayIso::Sunday));
}

M module-apps/application-alarm-clock/data/AlarmsData.hpp => module-apps/application-alarm-clock/data/AlarmsData.hpp +26 -1
@@ 2,6 2,8 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "application-calendar/data/OptionParser.hpp"
#include <module-db/Interface/AlarmsRecord.hpp>
#include <SwitchData.hpp>



@@ 33,6 35,17 @@ enum class AlarmAction
    Edit
};

enum class WeekDayIso
{
    Monday = date::Monday.iso_encoding() - 1,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
};

class AlarmRecordData : public gui::SwitchData
{
  protected:


@@ 41,7 54,7 @@ class AlarmRecordData : public gui::SwitchData
  public:
    explicit AlarmRecordData(std::shared_ptr<AlarmsRecord> record) : record{std::move(record)}
    {}
    std::shared_ptr<AlarmsRecord> getData() const
    std::shared_ptr<AlarmsRecord> getData()
    {
        return record;
    }


@@ 50,3 63,15 @@ class AlarmRecordData : public gui::SwitchData
        record = std::move(rec);
    }
};

class CustomRepeatValueParser
{
    std::unique_ptr<WeekDaysRepeatData> weekDayData;

  public:
    explicit CustomRepeatValueParser(uint32_t repeatValue);

    [[nodiscard]] std::string getWeekDaysText() const;
    [[nodiscard]] bool isCustomValueWeekDays() const;
    [[nodiscard]] bool isCustomValueEveryday() const;
};

A module-apps/application-alarm-clock/models/CustomRepeatModel.cpp => module-apps/application-alarm-clock/models/CustomRepeatModel.cpp +75 -0
@@ 0,0 1,75 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CustomRepeatModel.hpp"
#include "application-alarm-clock/widgets/CustomCheckBoxWithLabel.hpp"
#include "application-alarm-clock/widgets/AlarmClockStyle.hpp"
#include <ListView.hpp>

namespace app::alarmClock
{

    CustomRepeatModel::CustomRepeatModel(app::Application *app,
                                         std::shared_ptr<AbstractAlarmsRepository> alarmsRepository)
        : application(app), alarmsRepository{std::move(alarmsRepository)}
    {}

    unsigned int CustomRepeatModel::requestRecordsCount()
    {
        return internalData.size();
    }

    unsigned int CustomRepeatModel::getMinimalItemHeight() const
    {
        return style::alarmClock::window::item::checkBox::height;
    }

    void CustomRepeatModel::requestRecords(uint32_t offset, uint32_t limit)
    {
        setupModel(offset, limit);
        list->onProviderDataUpdate();
    }

    gui::ListItem *CustomRepeatModel::getItem(gui::Order order)
    {
        return getRecord(order);
    }

    void CustomRepeatModel::createData(const WeekDaysRepeatData &data)
    {
        for (auto const &[key, dayName] : gui::CustomCheckBoxWithLabel::weekDays) {
            internalData.push_back(new gui::CustomCheckBoxWithLabel(application, utils::localize.get(dayName), data));
        }

        for (auto &item : internalData) {
            item->deleteByList = false;
        }
    }

    void CustomRepeatModel::loadData(const WeekDaysRepeatData &data)
    {
        list->clear();
        eraseInternalData();

        createData(data);

        list->rebuildList();
    }

    std::vector<bool> CustomRepeatModel::getIsCheckedData()
    {
        std::vector<bool> isCheckedData;
        isCheckedData.reserve(internalData.size());
        for (const auto &item : internalData) {
            if (item->onContentChangedCallback) {
                isCheckedData.push_back(item->onContentChangedCallback());
            }
            else {
                isCheckedData.push_back(false);
            }
        }

        return isCheckedData;
    }

} // namespace app::alarmClock

A module-apps/application-alarm-clock/models/CustomRepeatModel.hpp => module-apps/application-alarm-clock/models/CustomRepeatModel.hpp +43 -0
@@ 0,0 1,43 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "application-alarm-clock/widgets/AlarmInternalListItem.hpp"
#include "application-alarm-clock/models/AlarmsRepository.hpp"
#include "application-alarm-clock/data/AlarmsData.hpp"
#include "application-calendar/data/CalendarData.hpp"
#include "Application.hpp"
#include "InternalModel.hpp"
#include <ListItemProvider.hpp>

namespace app::alarmClock
{
    class CustomRepeatListItemProvider : public InternalModel<gui::AlarmInternalListItem *>,
                                         public gui::ListItemProvider
    {
      public:
        CustomRepeatListItemProvider() = default;

        virtual void loadData(const WeekDaysRepeatData &data) = 0;
        virtual std::vector<bool> getIsCheckedData()          = 0;
    };

    class CustomRepeatModel : public CustomRepeatListItemProvider
    {
        app::Application *application = nullptr;
        std::shared_ptr<AbstractAlarmsRepository> alarmsRepository;
        void createData(const WeekDaysRepeatData &data);

      public:
        CustomRepeatModel(app::Application *app, std::shared_ptr<AbstractAlarmsRepository> alarmsRepository);

        [[nodiscard]] unsigned int getMinimalItemHeight() const override;
        [[nodiscard]] unsigned int requestRecordsCount() override;
        [[nodiscard]] gui::ListItem *getItem(gui::Order order) override;
        void requestRecords(uint32_t offset, uint32_t limit) override;

        void loadData(const WeekDaysRepeatData &data) override;
        std::vector<bool> getIsCheckedData() override;
    };
} // namespace app::alarmClock

M module-apps/application-alarm-clock/models/NewEditAlarmModel.cpp => module-apps/application-alarm-clock/models/NewEditAlarmModel.cpp +10 -2
@@ 57,11 57,12 @@ namespace app::alarmClock
            [app](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text, false); },
            [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); }));

        internalData.push_back(new gui::AlarmOptionsItem(
        repeatOption = new gui::AlarmOptionsItem(
            application,
            AlarmOptionItemName::Repeat,
            [app](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text, false); },
            [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); }));
            [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); });
        internalData.push_back(repeatOption);

        for (auto &item : internalData) {
            item->deleteByList = false;


@@ 84,6 85,13 @@ namespace app::alarmClock
        list->rebuildList();
    }

    void NewEditAlarmModel::loadRepeat(std::shared_ptr<AlarmsRecord> record)
    {
        if (repeatOption->onLoadCallback) {
            repeatOption->onLoadCallback(std::move(record));
        }
    }

    void NewEditAlarmModel::saveData(std::shared_ptr<AlarmsRecord> alarm, AlarmAction action)
    {
        for (auto &item : internalData) {

M module-apps/application-alarm-clock/models/NewEditAlarmModel.hpp => module-apps/application-alarm-clock/models/NewEditAlarmModel.hpp +3 -0
@@ 21,12 21,14 @@ namespace app::alarmClock

        virtual void loadData(std::shared_ptr<AlarmsRecord> record)                     = 0;
        virtual void saveData(std::shared_ptr<AlarmsRecord> record, AlarmAction action) = 0;
        virtual void loadRepeat(std::shared_ptr<AlarmsRecord> record)                   = 0;
    };

    class NewEditAlarmModel : public AlarmsInternalListItemProvider
    {
        app::Application *application = nullptr;
        std::shared_ptr<AbstractAlarmsRepository> alarmsRepository;
        gui::AlarmInternalListItem *repeatOption = nullptr;
        bool mode24H = false;

      public:


@@ 36,6 38,7 @@ namespace app::alarmClock

        void loadData(std::shared_ptr<AlarmsRecord> record) override;
        void saveData(std::shared_ptr<AlarmsRecord> alarm, AlarmAction action) override;
        void loadRepeat(std::shared_ptr<AlarmsRecord> record) override;
        void createData();

        [[nodiscard]] unsigned int getMinimalItemHeight() const override;

M module-apps/application-alarm-clock/presenter/AlarmClockEditWindowPresenter.cpp => module-apps/application-alarm-clock/presenter/AlarmClockEditWindowPresenter.cpp +12 -0
@@ 24,4 24,16 @@ namespace app::alarmClock
    {
        alarmFieldsProvider->saveData(std::move(record), action);
    }

    void AlarmClockEditWindowPresenter::loadRepeat(std::shared_ptr<AlarmsRecord> record)
    {
        alarmFieldsProvider->loadRepeat(std::move(record));
    }

    void AlarmClockEditWindowPresenter::updateRepeat(std::shared_ptr<AlarmsRecord> record, WeekDaysRepeatData data)
    {
        auto parser     = std::make_unique<OptionParser>();
        auto uniqueData = std::make_unique<WeekDaysRepeatData>(data);
        record->repeat  = parser->getDatabaseFieldValue(std::move(uniqueData));
    }
} // namespace app::alarmClock

M module-apps/application-alarm-clock/presenter/AlarmClockEditWindowPresenter.hpp => module-apps/application-alarm-clock/presenter/AlarmClockEditWindowPresenter.hpp +4 -0
@@ 25,6 25,8 @@ namespace app::alarmClock
            [[nodiscard]] virtual std::shared_ptr<gui::ListItemProvider> getAlarmsItemProvider() const = 0;
            virtual void loadData(std::shared_ptr<AlarmsRecord> record)                                = 0;
            virtual void saveData(std::shared_ptr<AlarmsRecord> record, AlarmAction action)            = 0;
            virtual void loadRepeat(std::shared_ptr<AlarmsRecord> record)                              = 0;
            virtual void updateRepeat(std::shared_ptr<AlarmsRecord> record, WeekDaysRepeatData data)   = 0;
        };
    };



@@ 36,6 38,8 @@ namespace app::alarmClock
        [[nodiscard]] std::shared_ptr<gui::ListItemProvider> getAlarmsItemProvider() const override;
        void loadData(std::shared_ptr<AlarmsRecord> record) override;
        void saveData(std::shared_ptr<AlarmsRecord> record, AlarmAction action) override;
        void loadRepeat(std::shared_ptr<AlarmsRecord> record) override;
        void updateRepeat(std::shared_ptr<AlarmsRecord> record, WeekDaysRepeatData data) override;

      private:
        std::shared_ptr<AlarmsInternalListItemProvider> alarmFieldsProvider;

A module-apps/application-alarm-clock/presenter/CustomRepeatWindowPresenter.cpp => module-apps/application-alarm-clock/presenter/CustomRepeatWindowPresenter.cpp +34 -0
@@ 0,0 1,34 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CustomRepeatWindowPresenter.hpp"

namespace app::alarmClock
{

    CustomRepeatWindowPresenter::CustomRepeatWindowPresenter(std::shared_ptr<CustomRepeatListItemProvider> itemProvider)
        : customRepeatProvider{std::move(itemProvider)}
    {}

    std::shared_ptr<gui::ListItemProvider> CustomRepeatWindowPresenter::getItemProvider()
    {
        return customRepeatProvider;
    }

    void CustomRepeatWindowPresenter::loadData(const WeekDaysRepeatData &data)
    {
        customRepeatProvider->loadData(data);
    }

    WeekDaysRepeatData CustomRepeatWindowPresenter::getWeekDaysRepeatData()
    {
        auto weekDaysOptData = WeekDaysRepeatData();
        auto isCheckedData   = customRepeatProvider->getIsCheckedData();
        uint32_t i           = 0;
        for (const auto &checked : isCheckedData) {
            weekDaysOptData.setData(i, checked);
            ++i;
        }
        return weekDaysOptData;
    }
} // namespace app::alarmClock

A module-apps/application-alarm-clock/presenter/CustomRepeatWindowPresenter.hpp => module-apps/application-alarm-clock/presenter/CustomRepeatWindowPresenter.hpp +43 -0
@@ 0,0 1,43 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "BasePresenter.hpp"
#include "application-alarm-clock/data/AlarmsData.hpp"
#include "application-alarm-clock/models/CustomRepeatModel.hpp"

namespace app::alarmClock
{
    class CustomRepeatWindowContract
    {
      public:
        class View
        {
          public:
            virtual ~View() noexcept = default;
        };
        class Presenter : public BasePresenter<CustomRepeatWindowContract::View>
        {
          public:
            virtual ~Presenter() noexcept = default;

            [[nodiscard]] virtual std::shared_ptr<gui::ListItemProvider> getItemProvider() = 0;
            virtual void loadData(const WeekDaysRepeatData &data)                          = 0;
            virtual WeekDaysRepeatData getWeekDaysRepeatData()                             = 0;
        };
    };

    class CustomRepeatWindowPresenter : public CustomRepeatWindowContract::Presenter
    {
      public:
        explicit CustomRepeatWindowPresenter(std::shared_ptr<CustomRepeatListItemProvider> itemProvider);

        [[nodiscard]] std::shared_ptr<gui::ListItemProvider> getItemProvider() override;
        void loadData(const WeekDaysRepeatData &data) override;
        WeekDaysRepeatData getWeekDaysRepeatData() override;

      private:
        std::shared_ptr<CustomRepeatListItemProvider> customRepeatProvider;
    };
} // namespace app::alarmClock

M module-apps/application-alarm-clock/widgets/AlarmClockStyle.hpp => module-apps/application-alarm-clock/widgets/AlarmClockStyle.hpp +8 -0
@@ 54,6 54,14 @@ namespace style::alarmClock
                inline constexpr auto label_h   = 30;
                inline constexpr auto arrow_w_h = 20;
            } // namespace options

            namespace checkBox
            {
                inline constexpr auto height        = 44;
                inline constexpr auto marginTop     = 18;
                inline constexpr auto inputBox_w    = style::window::label::big_h;
                inline constexpr auto description_w = 280;
            } // namespace checkBox
        } // namespace item

    } // namespace window

M module-apps/application-alarm-clock/widgets/AlarmInternalListItem.hpp => module-apps/application-alarm-clock/widgets/AlarmInternalListItem.hpp +1 -0
@@ 12,6 12,7 @@ namespace gui
      public:
        std::function<void(std::shared_ptr<AlarmsRecord> event)> onSaveCallback = nullptr;
        std::function<void(std::shared_ptr<AlarmsRecord> event)> onLoadCallback = nullptr;
        std::function<bool()> onContentChangedCallback                          = nullptr;
    };

} /* namespace gui */

M module-apps/application-alarm-clock/widgets/AlarmItem.cpp => module-apps/application-alarm-clock/widgets/AlarmItem.cpp +1 -1
@@ 66,7 66,7 @@ namespace gui
            periodLabel->setText(utils::localize.get("app_alarm_clock_repeat_week_days"));
        }
        else if (alarm->repeat != static_cast<uint32_t>(AlarmRepeat::never)) {
            periodLabel->setText(utils::localize.get("app_alarm_clock_repeat_custom"));
            periodLabel->setText(CustomRepeatValueParser(alarm->repeat).getWeekDaysText());
        }

        if (periodLabel->getText().empty()) {

M module-apps/application-alarm-clock/widgets/AlarmOptionsItem.cpp => module-apps/application-alarm-clock/widgets/AlarmOptionsItem.cpp +56 -3
@@ 142,11 142,20 @@ namespace gui
            if (event.state != gui::InputEvent::State::keyReleasedShort) {
                return false;
            }
            if (event.is(gui::KeyCode::KEY_RF)) {
                setFocusItem(nullptr);
            }

            if (event.is(gui::KeyCode::KEY_LEFT)) {
                actualVectorIndex--;
                if (actualVectorIndex >= optionsNames.size()) {
                    actualVectorIndex = optionsNames.size() - 1;
                    if (itemName == AlarmOptionItemName::Repeat) {
                        bottomBarTemporaryMode(utils::localize.get("app_alarm_clock_edit"));
                    }
                }
                else if (itemName == AlarmOptionItemName::Repeat) {
                    bottomBarRestoreFromTemporaryMode();
                }
                optionLabel->setText(optionsNames[actualVectorIndex]);
                return true;


@@ 157,10 166,24 @@ namespace gui
                if (actualVectorIndex >= optionsNames.size()) {
                    actualVectorIndex = 0;
                }
                if (actualVectorIndex == optionsNames.size() - 1 && itemName == AlarmOptionItemName::Repeat) {
                    bottomBarTemporaryMode(utils::localize.get("app_alarm_clock_edit"));
                }
                else if (itemName == AlarmOptionItemName::Repeat) {
                    bottomBarRestoreFromTemporaryMode();
                }
                optionLabel->setText(optionsNames[actualVectorIndex]);
                return true;
            }

            if (event.is(gui::KeyCode::KEY_LF) && itemName == AlarmOptionItemName::Repeat &&
                actualVectorIndex == optionsNames.size() - 1) {
                OptionParser parser;
                auto weekDayRepeatData = std::make_unique<WeekDaysRepeatData>();
                auto weekDayData       = parser.setWeekDayOptions(repeatOptionValue, std::move(weekDayRepeatData));
                application->switchWindow(style::alarmClock::window::name::customRepeat, std::move(weekDayData));
            }

            if (event.is(gui::KeyCode::KEY_LF) && itemName == AlarmOptionItemName::Sound) {
                if (musicStatus == MusicStatus::Stop) {
                    musicStatus = MusicStatus::Play;


@@ 186,7 209,14 @@ namespace gui
                break;
            }
            case AlarmOptionItemName::Repeat: {
                alarm->repeat = actualVectorIndex;
                if (alarm->repeat < optionsNames.size() - 1 && actualVectorIndex != optionsNames.size() - 1) {
                    alarm->repeat = actualVectorIndex;
                }
                else if (alarm->repeat == optionsNames.size() - 1 ||
                         optionsNames[optionsNames.size() - 1] ==
                             utils::localize.get("app_alarm_clock_repeat_custom")) {
                    alarm->repeat = static_cast<uint32_t>(AlarmRepeat::never);
                }
                break;
            }
            }


@@ 221,10 251,34 @@ namespace gui
            case AlarmOptionItemName::Repeat: {
                if (alarm->repeat < optionsNames.size() - 1) {
                    actualVectorIndex = alarm->repeat;
                    if (alarm->repeat == static_cast<uint32_t>(AlarmRepeat::never)) {
                        optionsNames[optionsNames.size() - 1] = utils::localize.get("app_alarm_clock_repeat_custom");
                    }
                    bottomBarRestoreFromTemporaryMode();
                }
                else {
                    actualVectorIndex = optionsNames.size() - 1;
                    auto parser = CustomRepeatValueParser(alarm->repeat);
                    if (parser.isCustomValueEveryday()) {
                        actualVectorIndex = static_cast<uint32_t>(AlarmRepeat::everyday);
                        alarm->repeat     = actualVectorIndex;
                        bottomBarRestoreFromTemporaryMode();
                        optionsNames[optionsNames.size() - 1] = utils::localize.get("app_alarm_clock_repeat_custom");
                    }
                    else if (parser.isCustomValueWeekDays()) {
                        actualVectorIndex = static_cast<uint32_t>(AlarmRepeat::weekDays);
                        alarm->repeat     = actualVectorIndex;
                        bottomBarRestoreFromTemporaryMode();
                        optionsNames[optionsNames.size() - 1] = utils::localize.get("app_alarm_clock_repeat_custom");
                    }
                    else {
                        actualVectorIndex                     = optionsNames.size() - 1;
                        optionsNames[optionsNames.size() - 1] = parser.getWeekDaysText();
                        if (this->focus) {
                            bottomBarTemporaryMode(utils::localize.get("app_alarm_clock_edit"));
                        }
                    }
                }
                repeatOptionValue = alarm->repeat;
                break;
            }
            }


@@ 260,5 314,4 @@ namespace gui
        LOG_INFO("Total number of music files found: %u", static_cast<unsigned int>(musicFiles.size()));
        return musicFiles;
    }

} /* namespace gui */

M module-apps/application-alarm-clock/widgets/AlarmOptionsItem.hpp => module-apps/application-alarm-clock/widgets/AlarmOptionsItem.hpp +1 -0
@@ 34,6 34,7 @@ namespace gui
        std::vector<audio::Tags> songsList;
        MusicStatus musicStatus        = MusicStatus::Stop;
        unsigned int actualVectorIndex = 0;
        uint32_t repeatOptionValue     = 0;

        std::function<void(const UTF8 &text)> bottomBarTemporaryMode = nullptr;
        std::function<void()> bottomBarRestoreFromTemporaryMode      = nullptr;

A module-apps/application-alarm-clock/widgets/CustomCheckBoxWithLabel.cpp => module-apps/application-alarm-clock/widgets/CustomCheckBoxWithLabel.cpp +100 -0
@@ 0,0 1,100 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CustomCheckBoxWithLabel.hpp"
#include "AlarmClockStyle.hpp"
#include <InputEvent.hpp>

namespace gui
{
    const std::map<WeekDayIso, std::string> CustomCheckBoxWithLabel::weekDays = {
        {WeekDayIso::Monday, style::strings::common::Monday},
        {WeekDayIso::Tuesday, style::strings::common::Tuesday},
        {WeekDayIso::Wednesday, style::strings::common::Wednesday},
        {WeekDayIso::Thursday, style::strings::common::Thursday},
        {WeekDayIso::Friday, style::strings::common::Friday},
        {WeekDayIso::Saturday, style::strings::common::Saturday},
        {WeekDayIso::Sunday, style::strings::common::Sunday}};

    CustomCheckBoxWithLabel::CustomCheckBoxWithLabel(app::Application *app,
                                                     const std::string &description,
                                                     const WeekDaysRepeatData &data)
        : application(app), checkBoxData(data)
    {
        assert(application != nullptr);

        setMinimumSize(style::window::default_body_width, style::alarmClock::window::item::checkBox::height);
        setMargins(gui::Margins(style::margins::small, style::alarmClock::window::item::checkBox::marginTop, 0, 0));
        setEdges(RectangleEdge::None);

        hBox = new gui::HBox(this, 0, 0, 0, 0);
        hBox->setEdges(gui::RectangleEdge::None);

        checkBox = new gui::CheckBox(
            hBox,
            0,
            0,
            0,
            0,
            [=](const UTF8 &text) { application->getCurrentWindow()->bottomBarTemporaryMode(text, false); },
            [=]() { application->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); });
        checkBox->setMinimumSize(style::alarmClock::window::item::checkBox::inputBox_w,
                                 style::alarmClock::window::item::checkBox::height);

        descriptionLabel = new gui::Label(hBox, 0, 0, 0, 0);
        descriptionLabel->setMinimumSize(style::alarmClock::window::item::checkBox::description_w,
                                         style::alarmClock::window::item::checkBox::height);
        descriptionLabel->setMargins(gui::Margins(style::margins::very_big, 0, 0, 0));
        descriptionLabel->setEdges(gui::RectangleEdge::None);
        descriptionLabel->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center));
        descriptionLabel->setFont(style::window::font::medium);
        descriptionLabel->setText(description);

        applyCallbacks();
        setCheckBoxes();
    }

    void CustomCheckBoxWithLabel::applyCallbacks()
    {
        focusChangedCallback = [&](Item &item) {
            if (focus) {
                descriptionLabel->setFont(style::window::font::mediumbold);
                setFocusItem(checkBox);
            }
            else {
                descriptionLabel->setFont(style::window::font::medium);
                setFocusItem(nullptr);
            }
            return true;
        };

        inputCallback = [&](gui::Item &item, const gui::InputEvent &event) {
            if (event.is(gui::KeyCode::KEY_RF) || event.is(gui::KeyCode::KEY_ENTER)) {
                setFocusItem(nullptr);
                return false;
            }
            if (checkBox->onInput(event)) {
                checkBox->resizeItems();
                return true;
            }
            return false;
        };
        onContentChangedCallback = [&]() { return checkBox->isChecked(); };
    }

    void CustomCheckBoxWithLabel::setCheckBoxes()
    {
        for (auto const &[key, dayName] : weekDays) {
            if (descriptionLabel->getText() == utils::localize.get(dayName)) {
                checkBox->setImageVisible(checkBoxData.getData(static_cast<uint32_t>(key)));
            }
        }
    }

    bool CustomCheckBoxWithLabel::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        hBox->setPosition(0, 0);
        hBox->setSize(newDim.w, newDim.h);
        return true;
    }
} // namespace gui

A module-apps/application-alarm-clock/widgets/CustomCheckBoxWithLabel.hpp => module-apps/application-alarm-clock/widgets/CustomCheckBoxWithLabel.hpp +33 -0
@@ 0,0 1,33 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "AlarmInternalListItem.hpp"
#include "application-alarm-clock/data/AlarmsData.hpp"
#include "Application.hpp"
#include "application-calendar/data/CalendarData.hpp"
#include <BoxLayout.hpp>
#include <CheckBox.hpp>

namespace gui
{
    class CustomCheckBoxWithLabel : public AlarmInternalListItem
    {
        gui::HBox *hBox               = nullptr;
        app::Application *application = nullptr;
        gui::Label *descriptionLabel  = nullptr;
        gui::CheckBox *checkBox       = nullptr;
        WeekDaysRepeatData checkBoxData;

        void setCheckBoxes();
        void applyCallbacks();

      public:
        CustomCheckBoxWithLabel(app::Application *app, const std::string &description, const WeekDaysRepeatData &data);

        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;

        static const std::map<WeekDayIso, std::string> weekDays;
    };
} // namespace gui

A module-apps/application-alarm-clock/windows/CustomRepeatWindow.cpp => module-apps/application-alarm-clock/windows/CustomRepeatWindow.cpp +65 -0
@@ 0,0 1,65 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CustomRepeatWindow.hpp"

namespace app::alarmClock
{

    CustomRepeatWindow::CustomRepeatWindow(app::Application *app,
                                           std::unique_ptr<CustomRepeatWindowContract::Presenter> &&windowPresenter)
        : AppWindow(app, style::alarmClock::window::name::customRepeat), presenter{std::move(windowPresenter)}
    {
        presenter->attach(this);
        buildInterface();
    }

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

        topBar->setActive(gui::TopBar::Elements::TIME, true);
        topBar->setActive(gui::TopBar::Elements::SIM, false);
        topBar->setActive(gui::TopBar::Elements::NETWORK_ACCESS_TECHNOLOGY, false);
        bottomBar->setActive(gui::BottomBar::Side::RIGHT, true);
        bottomBar->setActive(gui::BottomBar::Side::CENTER, true);
        bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::localize.get(style::strings::common::back));
        bottomBar->setText(gui::BottomBar::Side::CENTER, utils::localize.get(style::strings::common::save));

        setTitle(utils::localize.get("app_calendar_custom_repeat_title"));
        list = new gui::ListView(this,
                                 style::alarmClock::window::listView_x,
                                 style::alarmClock::window::listView_y,
                                 style::alarmClock::window::listView_w,
                                 style::alarmClock::window::listView_h,
                                 presenter->getItemProvider());
        setFocusItem(list);
    }

    void CustomRepeatWindow::onBeforeShow(gui::ShowMode mode, gui::SwitchData *data)
    {
        if (auto receivedData = dynamic_cast<WeekDaysRepeatData *>(data); receivedData != nullptr) {
            weekDaysOptData = *receivedData;
        }
        else {
            weekDaysOptData = WeekDaysRepeatData();
        }
        presenter->loadData(weekDaysOptData);
    }

    bool CustomRepeatWindow::onInput(const gui::InputEvent &inputEvent)
    {
        if (AppWindow::onInput(inputEvent)) {
            return true;
        }

        if (inputEvent.isShortPress() && inputEvent.is(gui::KeyCode::KEY_ENTER)) {
            weekDaysOptData = presenter->getWeekDaysRepeatData();
            application->switchWindow(style::alarmClock::window::name::newEditAlarm,
                                      gui::ShowMode::GUI_SHOW_RETURN,
                                      std::make_unique<WeekDaysRepeatData>(weekDaysOptData));
            return true;
        }
        return false;
    }
} // namespace app::alarmClock

A module-apps/application-alarm-clock/windows/CustomRepeatWindow.hpp => module-apps/application-alarm-clock/windows/CustomRepeatWindow.hpp +30 -0
@@ 0,0 1,30 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "application-alarm-clock/widgets/AlarmClockStyle.hpp"
#include "application-alarm-clock/presenter/CustomRepeatWindowPresenter.hpp"
#include "application-alarm-clock/data/AlarmsData.hpp"
#include "Application.hpp"
#include <InputEvent.hpp>
#include <ListView.hpp>

namespace app::alarmClock
{
    class CustomRepeatWindow : public gui::AppWindow, public CustomRepeatWindowContract::View
    {
        gui::ListView *list = nullptr;
        std::unique_ptr<CustomRepeatWindowContract::Presenter> presenter;
        WeekDaysRepeatData weekDaysOptData;

      public:
        CustomRepeatWindow(app::Application *app,
                           std::unique_ptr<CustomRepeatWindowContract::Presenter> &&windowPresenter);

        void onBeforeShow(gui::ShowMode mode, gui::SwitchData *data) override;
        bool onInput(const gui::InputEvent &inputEvent) override;
        void buildInterface() override;
    };

} // namespace app::alarmClock

M module-apps/application-alarm-clock/windows/NewEditAlarmWindow.cpp => module-apps/application-alarm-clock/windows/NewEditAlarmWindow.cpp +8 -0
@@ 3,6 3,7 @@

#include "NewEditAlarmWindow.hpp"
#include "application-alarm-clock/data/AlarmsData.hpp"
#include "application-calendar/data/OptionParser.hpp"
#include <module-db/Interface/AlarmsRecord.hpp>

namespace app::alarmClock


@@ 53,6 54,13 @@ namespace app::alarmClock
            }
            presenter->loadData(alarmRecord);
        }

        if (mode == gui::ShowMode::GUI_SHOW_RETURN) {
            if (auto receivedData = dynamic_cast<WeekDaysRepeatData *>(data); receivedData != nullptr) {
                presenter->updateRepeat(alarmRecord, *receivedData);
                presenter->loadRepeat(alarmRecord);
            }
        }
    }

    bool NewEditAlarmWindow::onInput(const gui::InputEvent &inputEvent)