~aleteoryx/muditaos

ddcc09c4a943a27fc5208c9c7b3f6094ddba0f4c — Dawid Wojtas 1 year, 11 months ago 061fd40
[BH-1894] Create common ListViewWithLabels widget

Move RelaxationListView to common section.
Now the widget can be used by
other applications.
16 files changed, 191 insertions(+), 197 deletions(-)

M products/BellHybrid/apps/application-bell-relaxation/ApplicationBellRelaxation.cpp
M products/BellHybrid/apps/application-bell-relaxation/CMakeLists.txt
M products/BellHybrid/apps/application-bell-relaxation/data/RelaxationCommon.hpp
M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationMainWindowPresenter.cpp
M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationMainWindowPresenter.hpp
M products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationMainWindow.cpp
M products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationMainWindow.hpp
M products/BellHybrid/apps/common/CMakeLists.txt
R products/BellHybrid/apps/{application-bell-relaxation/model/RelaxationSongsModel => common/include/common/models/SongsModel}.hpp
R products/BellHybrid/apps/{application-bell-relaxation/widgets/RelaxationItem => common/include/common/widgets/LabelListItem}.hpp
R products/BellHybrid/apps/{application-bell-relaxation/widgets/RelaxationOption => common/include/common/widgets/LabelOption}.hpp
R products/BellHybrid/apps/{application-bell-relaxation/widgets/RelaxationListView => common/include/common/widgets/ListViewWithLabels}.hpp
R products/BellHybrid/apps/{application-bell-relaxation/model/RelaxationSongsModel => common/src/models/SongsModel}.cpp
R products/BellHybrid/apps/{application-bell-relaxation/widgets/RelaxationItem => common/src/widgets/LabelListItem}.cpp
R products/BellHybrid/apps/{application-bell-relaxation/widgets/RelaxationOption => common/src/widgets/LabelOption}.cpp
R products/BellHybrid/apps/{application-bell-relaxation/widgets/RelaxationListView => common/src/widgets/ListViewWithLabels}.cpp
M products/BellHybrid/apps/application-bell-relaxation/ApplicationBellRelaxation.cpp => products/BellHybrid/apps/application-bell-relaxation/ApplicationBellRelaxation.cpp +6 -6
@@ 26,6 26,7 @@
#include <common/models/TimeModel.hpp>
#include <common/models/BatteryModel.hpp>
#include <common/models/AudioModel.hpp>
#include <common/models/SongsModel.hpp>
#include <common/windows/AppsBatteryStatusWindow.hpp>
#include <service-db/DBNotificationMessage.hpp>
#include <system/messages/SentinelRegistrationMessage.hpp>


@@ 86,10 87,10 @@ namespace app
    void ApplicationBellRelaxation::createUserInterface()
    {
        windowsFactory.attach(gui::name::window::main_window, [](ApplicationCommon *app, const std::string &name) {
            const auto pathsTypeMap = std::map<relaxation::MusicType, std::string>{
                {relaxation::MusicType::Relaxation, paths::audio::proprietary() / paths::audio::relaxation()},
                {relaxation::MusicType::ColorsOfNoise, paths::audio::proprietary() / paths::audio::colorOfNoises()},
                {relaxation::MusicType::User, paths::audio::userApp() / paths::audio::relaxation()}};
            const app::LabelsWithPaths labelsWithPaths{
                {"app_bell_relaxation_sounds", paths::audio::proprietary() / paths::audio::relaxation()},
                {"app_bell_relaxation_noises", paths::audio::proprietary() / paths::audio::colorOfNoises()},
                {"app_bell_relaxation_uploaded_sounds", paths::audio::userApp() / paths::audio::relaxation()}};
            const auto pathsSortingVector = std::vector<SoundsRepository::PathSorting>{
                {paths::audio::proprietary() / paths::audio::relaxation(), SoundsRepository::SortingBy::TitleAscending},
                {paths::audio::proprietary() / paths::audio::colorOfNoises(),


@@ 97,8 98,7 @@ namespace app
                {paths::audio::userApp() / paths::audio::relaxation(), SoundsRepository::SortingBy::TitleAscending}};

            auto soundsRepository = std::make_unique<SoundsRepository>(app, pathsSortingVector);
            auto songsModel =
                std::make_unique<relaxation::RelaxationSongsModel>(app, std::move(soundsRepository), pathsTypeMap);
            auto songsModel = std::make_unique<app::SongsModel>(app, std::move(soundsRepository), labelsWithPaths);
            auto presenter  = std::make_unique<relaxation::RelaxationMainWindowPresenter>(std::move(songsModel));
            return std::make_unique<gui::RelaxationMainWindow>(app, std::move(presenter));
        });

M products/BellHybrid/apps/application-bell-relaxation/CMakeLists.txt => products/BellHybrid/apps/application-bell-relaxation/CMakeLists.txt +0 -8
@@ 22,9 22,6 @@ target_sources(application-bell-relaxation
        presenter/RelaxationEndedPresenter.cpp
        presenter/RelaxationErrorPresenter.cpp
        widgets/RelaxationPlayer.cpp
        widgets/RelaxationListView.cpp
        widgets/RelaxationOption.cpp
        widgets/RelaxationItem.cpp
        windows/RelaxationMainWindow.cpp
        windows/RelaxationPausedWindow.cpp
        windows/RelaxationRunningProgressWindow.cpp


@@ 40,10 37,7 @@ target_sources(application-bell-relaxation
        data/RelaxationSwitchData.hpp
        data/RelaxationErrorData.hpp
        widgets/RelaxationPlayer.hpp
        widgets/RelaxationListView.hpp
        widgets/RelaxationOption.hpp
        windows/RelaxationMainWindow.hpp
        widgets/RelaxationItem.cpp
        presenter/RelaxationMainWindowPresenter.hpp
        presenter/RelaxationRunningProgressPresenter.hpp
        presenter/RelaxationRunningLoopPresenter.hpp


@@ 60,8 54,6 @@ target_sources(application-bell-relaxation
        windows/RelaxationVolumeWindow.hpp
        windows/RelaxationEndedWindow.hpp
        windows/RelaxationErrorWindow.hpp
        model/RelaxationSongsModel.hpp
        model/RelaxationSongsModel.cpp

        PUBLIC
        include/application-bell-relaxation/ApplicationBellRelaxation.hpp

M products/BellHybrid/apps/application-bell-relaxation/data/RelaxationCommon.hpp => products/BellHybrid/apps/application-bell-relaxation/data/RelaxationCommon.hpp +1 -9
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 6,12 6,4 @@
namespace app::relaxation
{
    constexpr auto timerValueDBRecordName = "RelaxationTimerValue";

    enum class MusicType
    {
        Relaxation,
        ColorsOfNoise,
        User,
        Undefined
    };
} // namespace app::relaxation

M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationMainWindowPresenter.cpp => products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationMainWindowPresenter.cpp +4 -6
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "RelaxationMainWindowPresenter.hpp"


@@ 6,11 6,11 @@

namespace app::relaxation
{
    RelaxationMainWindowPresenter::RelaxationMainWindowPresenter(std::unique_ptr<RelaxationSongsModel> songsModel)
    RelaxationMainWindowPresenter::RelaxationMainWindowPresenter(std::unique_ptr<SongsModel> songsModel)
        : songsModel{std::move(songsModel)}
    {}

    void RelaxationMainWindowPresenter::createData(RelaxationSongsModel::OnActivateCallback activateCallback)
    void RelaxationMainWindowPresenter::createData(SongsModel::OnActivateCallback activateCallback)
    {
        songsModel->createData(activateCallback);
        updateViewState();


@@ 22,13 22,11 @@ namespace app::relaxation
            view->updateViewState();
        }
    }

    void RelaxationMainWindowPresenter::updateRecordsCount()
    {
        songsModel->updateRecordsCount();
    }

    std::shared_ptr<RelaxationSongsModel> RelaxationMainWindowPresenter::getSongsModel()
    std::shared_ptr<SongsModel> RelaxationMainWindowPresenter::getSongsModel()
    {
        return songsModel;
    }

M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationMainWindowPresenter.hpp => products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationMainWindowPresenter.hpp +8 -10
@@ 1,12 1,10 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "model/RelaxationSongsModel.hpp"
#include <apps-common/BasePresenter.hpp>
#include <module-db/Interface/MultimediaFilesRecord.hpp>
#include <vector>
#include <common/models/SongsModel.hpp>

namespace app::music
{


@@ 31,24 29,24 @@ namespace app::relaxation
        {

          public:
            virtual void createData(RelaxationSongsModel::OnActivateCallback activateCallback) = 0;
            virtual void createData(SongsModel::OnActivateCallback activateCallback)           = 0;
            virtual void updateViewState()                                                     = 0;
            virtual void updateRecordsCount()                                                  = 0;
            virtual std::shared_ptr<RelaxationSongsModel> getSongsModel()                      = 0;
            virtual std::shared_ptr<SongsModel> getSongsModel()                                = 0;
        };
    };

    class RelaxationMainWindowPresenter : public RelaxationMainWindowContract::Presenter
    {
      private:
        std::shared_ptr<RelaxationSongsModel> songsModel;
        void createData(RelaxationSongsModel::OnActivateCallback activateCallback) override;
        std::shared_ptr<SongsModel> songsModel;
        void createData(SongsModel::OnActivateCallback activateCallback) override;
        void updateViewState() override;
        void updateRecordsCount() override;
        std::shared_ptr<RelaxationSongsModel> getSongsModel() override;
        std::shared_ptr<SongsModel> getSongsModel() override;

      public:
        explicit RelaxationMainWindowPresenter(std::unique_ptr<RelaxationSongsModel> songsModel);
        explicit RelaxationMainWindowPresenter(std::unique_ptr<SongsModel> songsModel);
    };

} // namespace app::relaxation

M products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationMainWindow.cpp => products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationMainWindow.cpp +7 -7
@@ 1,16 1,16 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "RelaxationMainWindow.hpp"
#include <data/RelaxationAudioData.hpp>
#include <data/RelaxationErrorData.hpp>
#include <ApplicationBellRelaxation.hpp>
#include "common/options/BellOptionsNavigation.hpp"

#include "data/RelaxationAudioData.hpp"
#include "data/RelaxationErrorData.hpp"

#include <ApplicationBellRelaxation.hpp>
#include <common/options/BellOptionsNavigation.hpp>
#include <common/options/OptionBellMenu.hpp>
#include <i18n/i18n.hpp>


namespace gui
{
    RelaxationMainWindow::RelaxationMainWindow(


@@ 30,7 30,7 @@ namespace gui
        header->setTitleVisibility(false);
        navBar->setVisible(false);

        songList = new gui::RelaxationListView(
        songList = new gui::ListViewWithLabels(
            this, 0, 0, style::window_width, style::window_height, presenter->getSongsModel());
        songList->applySizeRestrictions(style::bell_options_list::w,
                                        style::bell_options_list::h,

M products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationMainWindow.hpp => products/BellHybrid/apps/application-bell-relaxation/windows/RelaxationMainWindow.hpp +3 -3
@@ 1,11 1,11 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "presenter/RelaxationMainWindowPresenter.hpp"
#include "widgets/RelaxationListView.hpp"

#include <common/widgets/ListViewWithLabels.hpp>
#include <common/options/BellOptionWindow.hpp>

namespace gui


@@ 19,7 19,7 @@ namespace gui
      private:
        std::unique_ptr<app::relaxation::RelaxationMainWindowContract::Presenter> presenter;

        gui::RelaxationListView *songList = nullptr;
        gui::ListViewWithLabels *songList{nullptr};

        void buildInterface() override;
        void handleError() override;

M products/BellHybrid/apps/common/CMakeLists.txt => products/BellHybrid/apps/common/CMakeLists.txt +8 -0
@@ 39,6 39,7 @@ target_sources(application-bell-common
        src/models/SettingsModel.cpp
        src/models/BedtimeModel.cpp
        src/models/LowBatteryInfoModel.cpp
        src/models/SongsModel.cpp
        src/models/PreWakeUpModel.cpp
        src/popups/AlarmActivatedWindow.cpp
        src/popups/AlarmActivatedWindow.cpp


@@ 58,6 59,9 @@ target_sources(application-bell-common
        src/widgets/SnoozeTimer.cpp
        src/widgets/LayoutVertical.cpp
        src/widgets/ClockVertical.cpp
        src/widgets/ListViewWithLabels.cpp
        src/widgets/LabelListItem.cpp
        src/widgets/LabelOption.cpp

        src/options/BellOptionWindow.cpp
        src/options/BellShortOptionWindow.cpp


@@ 117,6 121,7 @@ target_sources(application-bell-common
        include/common/models/LayoutModel.hpp
        include/common/models/LowBatteryInfoModel.hpp
        include/common/models/QuoteModel.hpp
        include/common/models/SongsModel.hpp
        include/common/models/PreWakeUpModel.hpp
        include/common/popups/presenter/AlarmActivatedPresenter.hpp
        include/common/popups/AlarmActivatedWindow.hpp


@@ 136,6 141,9 @@ target_sources(application-bell-common
        include/common/widgets/ListItems.hpp
        include/common/widgets/LayoutVertical.hpp
        include/common/widgets/ClockVertical.hpp
        include/common/widgets/ListViewWithLabels.hpp
        include/common/widgets/LabelListItem.hpp
        include/common/widgets/LabelOption.hpp
        include/common/options/BellOptionWindow.hpp
        include/common/options/BellShortOptionWindow.hpp
        include/common/options/OptionBellMenu.hpp

R products/BellHybrid/apps/application-bell-relaxation/model/RelaxationSongsModel.hpp => products/BellHybrid/apps/common/include/common/models/SongsModel.hpp +15 -14
@@ 3,44 3,45 @@

#pragma once

#include "data/RelaxationCommon.hpp"
#include <common/SoundsRepository.hpp>
#include <common/widgets/LabelListItem.hpp>
#include <apps-common/models/SongsModelInterface.hpp>
#include <gui/widgets/ListItemProvider.hpp>

namespace app::relaxation
namespace app
{
    using LabelsWithPaths = std::map<std::string, std::string>;

    class RelaxationSongsProvider : public app::DatabaseModel<db::multimedia_files::MultimediaFilesRecord>,
                                    public gui::ListItemProvider
    class SongsProvider : public app::DatabaseModel<db::multimedia_files::MultimediaFilesRecord>,
                          public gui::ListItemProvider
    {
      public:
        virtual ~RelaxationSongsProvider() = default;
        virtual ~SongsProvider() = default;
        using OnActivateCallback =
            std::function<bool(const db::multimedia_files::MultimediaFilesRecord &selectedSound)>;
        explicit RelaxationSongsProvider(ApplicationCommon *application);
        explicit SongsProvider(ApplicationCommon *application);
        virtual void createData(OnActivateCallback activateCallback) = 0;
    };

    class RelaxationSongsModel : public RelaxationSongsProvider
    class SongsModel : public SongsProvider
    {
      private:
        ApplicationCommon *application;
        std::unique_ptr<AbstractSoundsRepository> songsRepository;
        std::map<MusicType, std::string> pathPrefixes;
        LabelsWithPaths pathPrefixes;
        OnActivateCallback activateCallback{nullptr};

        bool onMusicListRetrieved(const std::vector<db::multimedia_files::MultimediaFilesRecord> &records,
                                  unsigned int repoRecordsCount);
        [[nodiscard]] bool updateRecords(std::vector<db::multimedia_files::MultimediaFilesRecord> records) override;
        MusicType getTypeFromPath(const std::string &path);
        gui::ListLabel getLabelFromPath(const std::string &path);

      public:
        virtual ~RelaxationSongsModel() = default;
        virtual ~SongsModel() = default;

        RelaxationSongsModel(ApplicationCommon *application,
                             std::unique_ptr<AbstractSoundsRepository> soundsRepository,
                             const std::map<MusicType, std::string> &pathPrefixes);
        SongsModel(ApplicationCommon *application,
                   std::unique_ptr<AbstractSoundsRepository> soundsRepository,
                   const LabelsWithPaths &pathPrefixes);

        unsigned int requestRecordsCount() override;



@@ 54,4 55,4 @@ namespace app::relaxation
        void updateRecordsCount();
        bool nextRecordExist(gui::Order order);
    };
} // namespace app::relaxation
} // namespace app

R products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationItem.hpp => products/BellHybrid/apps/common/include/common/widgets/LabelListItem.hpp +11 -10
@@ 1,28 1,29 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "data/RelaxationCommon.hpp"
#include "ListItem.hpp"
#include "widgets/TextWithIconsWidget.hpp"
#include <common/widgets/ListViewWithLabels.hpp>

namespace gui
{
    class RelaxationItem : public ListItem
    class LabelListItem : public ListItem
    {
      private:
        app::relaxation::MusicType musicType{};
        ListLabel label{};

      public:
        explicit RelaxationItem(app::relaxation::MusicType musicType);
        virtual ~RelaxationItem() = default;
        app::relaxation::MusicType getMusicType();
        explicit LabelListItem(ListLabel label);
        virtual ~LabelListItem() = default;
        ListLabel getLabel();
    };

    class RelaxationMarkerItem : public ListItem
    class LabelMarkerItem : public ListItem
    {
      public:
        explicit RelaxationMarkerItem(const UTF8 &labelText);
        virtual ~RelaxationMarkerItem() = default;
        explicit LabelMarkerItem(const UTF8 &labelText);
        virtual ~LabelMarkerItem() = default;
    };
} // namespace gui

R products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationOption.hpp => products/BellHybrid/apps/common/include/common/widgets/LabelOption.hpp +11 -10
@@ 1,24 1,25 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "data/RelaxationCommon.hpp"
#include "RelaxationItem.hpp"
#include <common/widgets/LabelListItem.hpp>
#include <common/widgets/ListViewWithLabels.hpp>
#include <common/options/OptionBellMenu.hpp>

namespace gui::option
{
    class RelaxationOption : public OptionBellMenu
    class LabelOption : public OptionBellMenu
    {
      private:
        app::relaxation::MusicType musicType{app::relaxation::MusicType::Undefined};
        ListLabel label{};

      public:
        RelaxationOption(app::relaxation::MusicType musicType,
                         const UTF8 &text,
                         std::function<bool(Item &)> activatedCallback,
                         std::function<bool(Item &)> focusChangedCallback,
                         AppWindow *app);
        LabelOption(ListLabel label,
                    const UTF8 &text,
                    std::function<bool(Item &)> activatedCallback,
                    std::function<bool(Item &)> focusChangedCallback,
                    AppWindow *app);

        [[nodiscard]] auto build() const -> ListItem * override;
    };

R products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationListView.hpp => products/BellHybrid/apps/common/include/common/widgets/ListViewWithLabels.hpp +14 -11
@@ 1,22 1,24 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <apps-common/ApplicationCommon.hpp>
#include <ListViewWithArrows.hpp>
#include "data/RelaxationCommon.hpp"

#include <optional>

namespace gui
{
    class ListItemProvider;
    class LabelMarkerItem;

    class RelaxationListView : public ListViewWithArrows
    {
        using MusicType = app::relaxation::MusicType;
    using ListLabel = std::optional<std::string>;

    class ListViewWithLabels : public ListViewWithArrows
    {
      public:
        RelaxationListView(Item *parent,
        ListViewWithLabels(Item *parent,
                           unsigned int x,
                           unsigned int y,
                           unsigned int w,


@@ 26,14 28,15 @@ namespace gui
        void reset() override;

      private:
        std::size_t getSlotsLeft();
        void addItemsOnPage() override;
        void addLabelMarker(ListItem *item);
        std::size_t getSlotsLeft();
        void updateState(MusicType newMarker);
        void updateState(ListLabel newMarker);
        LabelMarkerItem *createMarkerItem(ListLabel label);

        MusicType currentType{MusicType::Undefined};
        MusicType previousType{MusicType::Undefined};
        MusicType currentMarker{MusicType::Undefined};
        ListLabel current{std::nullopt};
        ListLabel previous{std::nullopt};
        ListLabel currentMarker{std::nullopt};
        std::uint32_t itemsOnPage{0};
        bool labelAdded{false};
    };

R products/BellHybrid/apps/application-bell-relaxation/model/RelaxationSongsModel.cpp => products/BellHybrid/apps/common/src/models/SongsModel.cpp +42 -37
@@ 1,43 1,44 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "RelaxationSongsModel.hpp"
#include "common/options/OptionBellMenu.hpp"
#include "widgets/RelaxationOption.hpp"
#include <common/models/SongsModel.hpp>
#include <common/widgets/LabelOption.hpp>

namespace app::relaxation
namespace app
{
    RelaxationSongsProvider::RelaxationSongsProvider(ApplicationCommon *app) : DatabaseModel(app)
    SongsProvider::SongsProvider(ApplicationCommon *app) : DatabaseModel(app)
    {}

    RelaxationSongsModel::RelaxationSongsModel(ApplicationCommon *application,
                                               std::unique_ptr<AbstractSoundsRepository> soundsRepository,
                                               const std::map<MusicType, std::string> &pathPrefixes)
        : RelaxationSongsProvider(application),
    SongsModel::SongsModel(ApplicationCommon *application,
                           std::unique_ptr<AbstractSoundsRepository> soundsRepository,
                           const LabelsWithPaths &pathPrefixes)
        : SongsProvider(application),
          application(application), songsRepository{std::move(soundsRepository)}, pathPrefixes{pathPrefixes}
    {}

    void RelaxationSongsModel::createData(OnActivateCallback callback)
    void SongsModel::createData(OnActivateCallback callback)
    {
        activateCallback = callback;

        songsRepository->init();
    }

    void RelaxationSongsModel::updateRecordsCount()
    void SongsModel::updateRecordsCount()
    {
        songsRepository->updateFilesCount();
    }

    unsigned int RelaxationSongsModel::requestRecordsCount()
    unsigned int SongsModel::requestRecordsCount()
    {
        return songsRepository->getFilesCount();
    }
    unsigned int RelaxationSongsModel::getMinimalItemSpaceRequired() const

    unsigned int SongsModel::getMinimalItemSpaceRequired() const
    {
        return style::bell_options::h + 2 * style::bell_options::option_margin;
    }
    bool RelaxationSongsModel::nextRecordExist(gui::Order order)

    bool SongsModel::nextRecordExist(gui::Order order)
    {
        const auto getOppositeOrder = [order]() {
            return order == gui::Order::Next ? gui::Order::Previous : gui::Order::Next;


@@ 47,33 48,34 @@ namespace app::relaxation
        getRecord(getOppositeOrder());
        return exist;
    }
    gui::ListItem *RelaxationSongsModel::getItem(gui::Order order)

    gui::ListItem *SongsModel::getItem(gui::Order order)
    {
        const auto sound = getRecord(order);
        if (!sound) {
            return nullptr;
        }
        auto item = gui::option::RelaxationOption{getTypeFromPath(sound->fileInfo.path),
                                                  sound->tags.title,
                                                  [=]([[maybe_unused]] gui::Item &item) {
                                                      activateCallback(*sound);
                                                      return true;
                                                  },
                                                  []([[maybe_unused]] gui::Item &item) { return true; },
                                                  nullptr};
        auto item = gui::option::LabelOption{getLabelFromPath(sound->fileInfo.path),
                                             sound->tags.title,
                                             [=]([[maybe_unused]] gui::Item &item) {
                                                 activateCallback(*sound);
                                                 return true;
                                             },
                                             []([[maybe_unused]] gui::Item &item) { return true; },
                                             nullptr};

        return item.build();
    }
    void RelaxationSongsModel::requestRecords(std::uint32_t offset, std::uint32_t limit)

    void SongsModel::requestRecords(std::uint32_t offset, std::uint32_t limit)
    {
        songsRepository->getMusicFiles(
            offset,
            limit,
            [this](const std::vector<db::multimedia_files::MultimediaFilesRecord> &records,
                   unsigned int repoRecordsCount) { return onMusicListRetrieved(records, repoRecordsCount); });
        songsRepository->getMusicFiles(offset, limit, [this](const auto &records, const auto repoRecordsCount) {
            return onMusicListRetrieved(records, repoRecordsCount);
        });
    }
    bool RelaxationSongsModel::onMusicListRetrieved(
        const std::vector<db::multimedia_files::MultimediaFilesRecord> &records, unsigned int repoRecordsCount)

    bool SongsModel::onMusicListRetrieved(const std::vector<db::multimedia_files::MultimediaFilesRecord> &records,
                                          unsigned int repoRecordsCount)
    {
        if (list != nullptr && recordsCount != repoRecordsCount) {
            recordsCount = repoRecordsCount;


@@ 82,19 84,22 @@ namespace app::relaxation
        }
        return updateRecords(records);
    }
    bool RelaxationSongsModel::updateRecords(std::vector<db::multimedia_files::MultimediaFilesRecord> records)

    bool SongsModel::updateRecords(std::vector<db::multimedia_files::MultimediaFilesRecord> records)
    {
        DatabaseModel::updateRecords(std::move(records));
        list->onProviderDataUpdate();
        return true;
    }
    MusicType RelaxationSongsModel::getTypeFromPath(const std::string &path)

    gui::ListLabel SongsModel::getLabelFromPath(const std::string &path)
    {
        for (const auto &[type, pathPrefix] : pathPrefixes) {
        for (const auto &[label, pathPrefix] : pathPrefixes) {
            if (path.find(pathPrefix) != std::string::npos) {
                return type;
                return label;
            }
        }
        return MusicType::Relaxation;
        return std::nullopt;
    }
} // namespace app::relaxation

} // namespace app

R products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationItem.cpp => products/BellHybrid/apps/common/src/widgets/LabelListItem.cpp +6 -6
@@ 1,7 1,7 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "RelaxationItem.hpp"
#include <common/widgets/LabelListItem.hpp>
#include "common/options/OptionBellMenu.hpp"

namespace


@@ 11,15 11,15 @@ namespace

namespace gui
{
    RelaxationItem::RelaxationItem(app::relaxation::MusicType musicType) : musicType(musicType)
    LabelListItem::LabelListItem(ListLabel label) : label{label}
    {}

    app::relaxation::MusicType RelaxationItem::getMusicType()
    ListLabel LabelListItem::getLabel()
    {
        return musicType;
        return label;
    }

    RelaxationMarkerItem::RelaxationMarkerItem(const UTF8 &labelText)
    LabelMarkerItem::LabelMarkerItem(const UTF8 &labelText)
    {
        setMinimumSize(style::bell_options::default_text_width, style::bell_options::h);
        setMargins(gui::Margins(0, style::bell_options::option_margin, 0, style::bell_options::option_margin));

R products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationOption.cpp => products/BellHybrid/apps/common/src/widgets/LabelOption.cpp +13 -12
@@ 1,25 1,26 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "RelaxationOption.hpp"
#include <common/widgets/LabelOption.hpp>

#include <utility>

namespace gui::option
{

    RelaxationOption::RelaxationOption(app::relaxation::MusicType musicType,
                                       const UTF8 &text,
                                       std::function<bool(Item &)> activatedCallback,
                                       std::function<bool(Item &)> focusChangedCallback,
                                       gui::AppWindow *app)
        : OptionBellMenu(text, std::move(activatedCallback), std::move(focusChangedCallback), app), musicType(musicType)
    LabelOption::LabelOption(ListLabel label,
                             const UTF8 &text,
                             std::function<bool(Item &)> activatedCallback,
                             std::function<bool(Item &)> focusChangedCallback,
                             gui::AppWindow *app)
        : OptionBellMenu(text, std::move(activatedCallback), std::move(focusChangedCallback), app), label(label)
    {}

    auto RelaxationOption::build() const -> ListItem *
    auto LabelOption::build() const -> ListItem *
    {
        auto relaxationItem = new RelaxationItem(musicType);
        OptionBellMenu::prepareListItem(relaxationItem);
        return relaxationItem;
        auto labelItem = new LabelListItem(label);
        OptionBellMenu::prepareListItem(labelItem);
        return labelItem;
    }

} // namespace gui::option

R products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationListView.cpp => products/BellHybrid/apps/common/src/widgets/ListViewWithLabels.cpp +42 -48
@@ 1,33 1,18 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "RelaxationListView.hpp"
#include "widgets/RelaxationOption.hpp"
#include "model/RelaxationSongsModel.hpp"
#include "RelaxationItem.hpp"
#include <TextFixedSize.hpp>
#include <common/widgets/ListViewWithLabels.hpp>
#include <common/widgets/LabelListItem.hpp>
#include <common/models/SongsModel.hpp>

namespace
{

    constexpr auto maxItemDisplayed{4U};

    const std::map<app::relaxation::MusicType, std::string> typeToLabel{
        {app::relaxation::MusicType::Relaxation, "app_bell_relaxation_sounds"},
        {app::relaxation::MusicType::ColorsOfNoise, "app_bell_relaxation_noises"},
        {app::relaxation::MusicType::User, "app_bell_relaxation_uploaded_sounds"}};

    gui::RelaxationMarkerItem *createMarkerItem(app::relaxation::MusicType musicType)
    {
        const auto label = UTF8(utils::translate(typeToLabel.at(musicType)));
        return new gui::RelaxationMarkerItem(label);
    }

} // namespace

namespace gui
{
    RelaxationListView::RelaxationListView(Item *parent,
    ListViewWithLabels::ListViewWithLabels(Item *parent,
                                           unsigned int x,
                                           unsigned int y,
                                           unsigned int w,


@@ 38,7 23,7 @@ namespace gui
        body->dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool { return true; };
    }

    void RelaxationListView::addItemsOnPage()
    void ListViewWithLabels::addItemsOnPage()
    {
        currentPageSize = 0;
        itemsOnPage     = 0;


@@ 57,7 42,7 @@ namespace gui
                itemsOnPage++;
            }
            else {
                // Add invisible item to list to avoid memory leak
                /* Add invisible item to list to avoid memory leak */
                item->setVisible(false);
                body->addWidget(item);
                break;


@@ 72,39 57,48 @@ namespace gui
        recalculateStartIndex();

        if (!labelAdded) {
            currentMarker = MusicType::Undefined;
            currentMarker.reset();
        }
    }

    LabelMarkerItem *ListViewWithLabels::createMarkerItem(ListLabel label)
    {
        if (label.has_value()) {
            const auto labelString = UTF8(utils::translate(label.value()));
            return new LabelMarkerItem(labelString);
        }
        return new LabelMarkerItem(UTF8(""));
    }

    void RelaxationListView::addLabelMarker(ListItem *item)
    void ListViewWithLabels::addLabelMarker(ListItem *item)
    {
        const auto relaxationItem = dynamic_cast<gui::RelaxationItem *>(item);
        if (relaxationItem == nullptr) {
        const auto labelListItem = dynamic_cast<gui::LabelListItem *>(item);
        if (labelListItem == nullptr) {
            return;
        };
        previousType = currentType;
        currentType  = relaxationItem->getMusicType();
        previous = current;
        current  = labelListItem->getLabel();

        switch (direction) {
        case listview::Direction::Bottom:
            if (currentType != previousType && currentType != currentMarker) {
                body->addWidget(createMarkerItem(currentType));
                updateState(currentType);
            if (current != previous && current != currentMarker) {
                body->addWidget(createMarkerItem(*current));
                updateState(current);
            }
            break;

        case listview::Direction::Top:
            if (currentType != previousType && previousType != currentMarker) {
            if (current != previous && previous != currentMarker) {
                const auto initialSlotsLeft = getSlotsLeft();

                body->removeWidget(relaxationItem);
                body->addWidget(createMarkerItem(previousType));
                updateState(previousType);
                body->removeWidget(labelListItem);
                body->addWidget(createMarkerItem(*previous));
                updateState(previous);

                /* Add item to body even if it won't fit to avoid manual memory
                 * management for item, but apply correction to currentPageSize
                 * if it is not visible. */
                body->addWidget(relaxationItem);
                body->addWidget(labelListItem);

                if (initialSlotsLeft == 0) {
                    currentPageSize--;


@@ 112,21 106,21 @@ namespace gui
                }
            }
            else {
                const auto relaxationProvider = dynamic_cast<app::relaxation::RelaxationSongsModel *>(provider.get());
                if (relaxationProvider == nullptr) {
                const auto songsProvider = dynamic_cast<app::SongsModel *>(provider.get());
                if (songsProvider == nullptr) {
                    break;
                }
                const auto nextItemExist = relaxationProvider->nextRecordExist(getOrderFromDirection());
                const auto nextItemExist = songsProvider->nextRecordExist(getOrderFromDirection());
                if (!nextItemExist && getSlotsLeft() == 1) {
                    body->addWidget(createMarkerItem(currentType));
                    updateState(currentType);
                    body->addWidget(createMarkerItem(current));
                    updateState(current);
                }
            }
            break;
        }
    }

    std::size_t RelaxationListView::getSlotsLeft()
    std::size_t ListViewWithLabels::getSlotsLeft()
    {
        if (itemsOnPage > maxItemDisplayed) {
            return 0;


@@ 134,17 128,17 @@ namespace gui
        return maxItemDisplayed - itemsOnPage;
    }

    void RelaxationListView::reset()
    void ListViewWithLabels::reset()
    {
        currentMarker = MusicType::Undefined;
        previousType  = MusicType::Undefined;
        currentType   = MusicType::Undefined;
        currentMarker.reset();
        previous.reset();
        current.reset();
        ListViewEngine::reset();
    }

    void RelaxationListView::updateState(RelaxationListView::MusicType newMarker)
    void ListViewWithLabels::updateState(ListLabel marker)
    {
        currentMarker = newMarker;
        currentMarker = marker;
        itemsOnPage++;
        labelAdded = true;
    }