~aleteoryx/muditaos

706a25df1d30ed69e073711c65d15071f547c436 — Maciej Janicki 4 years ago a9b111a
[BH-1125] Add file indexer

Add file indexer
52 files changed, 341 insertions(+), 194 deletions(-)

M module-apps/application-music-player/ApplicationMusicPlayer.cpp
M module-apps/application-music-player/CMakeLists.txt
M module-apps/application-music-player/include/application-music-player/ApplicationMusicPlayer.hpp
M module-apps/application-music-player/models/SongsModel.cpp
M module-apps/application-music-player/models/SongsModel.hpp
M module-apps/application-music-player/presenters/SongsPresenter.cpp
M module-apps/application-music-player/presenters/SongsPresenter.hpp
M module-apps/application-music-player/tests/MockSongsRepository.hpp
M module-apps/application-music-player/tests/MockTagsFetcher.hpp
M module-apps/application-music-player/tests/unittest_songsmodel.cpp
M module-apps/apps-common/CMakeLists.txt
R module-apps/{application-music-player => apps-common}/models/SongContext.cpp
R module-apps/{application-music-player => apps-common}/models/SongContext.hpp
R module-apps/{application-music-player => apps-common}/models/SongsModelInterface.hpp
R module-apps/{application-music-player => apps-common}/models/SongsRepository.cpp
R module-apps/{application-music-player => apps-common}/models/SongsRepository.hpp
M module-db/Interface/MultimediaFilesRecord.cpp
M module-db/Interface/MultimediaFilesRecord.hpp
M module-db/Tables/MultimediaFilesTable.cpp
M module-db/Tables/MultimediaFilesTable.hpp
M module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.cpp
M module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.hpp
M module-db/tests/MultimediaFilesTable_tests.cpp
M module-services/CMakeLists.txt
R {products/PurePhone/services => module-services}/service-fileindexer/CMakeLists.txt
R {products/PurePhone/services => module-services}/service-fileindexer/Common.hpp
R {products/PurePhone/services => module-services}/service-fileindexer/InotifyHandler.cpp
R {products/PurePhone/services => module-services}/service-fileindexer/ServiceFileIndexer.cpp
R {products/PurePhone/services => module-services}/service-fileindexer/StartupIndexer.cpp
R {products/PurePhone/services => module-services}/service-fileindexer/include/service-fileindexer/Constants.hpp
R {products/PurePhone/services => module-services}/service-fileindexer/include/service-fileindexer/InotifyHandler.hpp
R {products/PurePhone/services => module-services}/service-fileindexer/include/service-fileindexer/ServiceFileIndexer.hpp
R {products/PurePhone/services => module-services}/service-fileindexer/include/service-fileindexer/StartupIndexer.hpp
M products/BellHybrid/BellHybridMain.cpp
M products/BellHybrid/CMakeLists.txt
M products/BellHybrid/apps/application-bell-background-sounds/ApplicationBellBackgroundSounds.cpp
M products/BellHybrid/apps/application-bell-background-sounds/CMakeLists.txt
M products/BellHybrid/apps/application-bell-background-sounds/data/BGSoundsAudioData.hpp
M products/BellHybrid/apps/application-bell-background-sounds/include/application-bell-background-sounds/ApplicationBellBackgroundSounds.hpp
M products/BellHybrid/apps/application-bell-background-sounds/models/BGSoundsRepository.cpp
M products/BellHybrid/apps/application-bell-background-sounds/models/BGSoundsRepository.hpp
M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsMainWindowPresenter.cpp
M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsMainWindowPresenter.hpp
M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsProgressPresenter.cpp
M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsProgressPresenter.hpp
M products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsMainWindow.cpp
M products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsMainWindow.hpp
M products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsProgressWindow.cpp
M products/BellHybrid/services/db/ServiceDB.cpp
M products/BellHybrid/services/db/include/db/ServiceDB.hpp
M products/PurePhone/PurePhoneMain.cpp
M products/PurePhone/services/CMakeLists.txt
M module-apps/application-music-player/ApplicationMusicPlayer.cpp => module-apps/application-music-player/ApplicationMusicPlayer.cpp +9 -5
@@ 27,7 27,7 @@ namespace app
        class MusicPlayerPriv
        {
          public:
            std::shared_ptr<app::music_player::SongsModelInterface> songsModel;
            std::shared_ptr<app::music::SongsModelInterface> songsModel;
            std::shared_ptr<app::music_player::SongsContract::Presenter> songsPresenter;
        };
    } // namespace music_player::internal


@@ 46,15 46,19 @@ namespace app

        bus.channels.push_back(sys::BusChannel::ServiceAudioNotifications);

        auto tagsFetcher     = std::make_unique<app::music_player::ServiceAudioTagsFetcher>(this);
        auto songsRepository = std::make_unique<app::music_player::SongsRepository>(this, std::move(tagsFetcher));
        priv->songsModel     = std::make_unique<app::music_player::SongsModel>(this, std::move(songsRepository));
        auto tagsFetcher = std::make_unique<app::music::ServiceAudioTagsFetcher>(this);

        const auto musicPlayerFilesPath = purefs::createPath(purefs::dir::getUserDiskPath(), "music").string();
        auto songsRepository =
            std::make_unique<app::music::SongsRepository>(this, std::move(tagsFetcher), musicPlayerFilesPath);

        priv->songsModel     = std::make_unique<app::music::SongsModel>(this, std::move(songsRepository));
        auto audioOperations = std::make_unique<app::AsyncAudioOperations>(this);
        priv->songsPresenter =
            std::make_unique<app::music_player::SongsPresenter>(this, priv->songsModel, std::move(audioOperations));

        // callback used when playing state is changed
        using SongState                                 = app::music_player::SongState;
        using SongState                                 = app::music::SongState;
        std::function<void(SongState)> autolockCallback = [this](SongState isPlaying) {
            if (isPlaying == SongState::Playing) {
                LOG_DEBUG("Preventing autolock while playing track.");

M module-apps/application-music-player/CMakeLists.txt => module-apps/application-music-player/CMakeLists.txt +0 -5
@@ 14,9 14,7 @@ target_sources(application-music-player
    PRIVATE
        ApplicationMusicPlayer.cpp
        AudioNotificationsHandler.cpp
        models/SongContext.cpp
        models/SongsModel.cpp
        models/SongsRepository.cpp
        presenters/SongsPresenter.cpp
        widgets/SongItem.cpp
        windows/MusicPlayerMainWindow.cpp


@@ 24,10 22,7 @@ target_sources(application-music-player
    PRIVATE
        AudioNotificationsHandler.hpp
        data/MusicPlayerStyle.hpp
        models/SongContext.hpp
        models/SongsModel.hpp
        models/SongsRepository.hpp
        models/SongsModelInterface.hpp
        presenters/SongsPresenter.hpp
        widgets/SongItem.hpp
        windows/MusicPlayerMainWindow.cpp

M module-apps/application-music-player/include/application-music-player/ApplicationMusicPlayer.hpp => module-apps/application-music-player/include/application-music-player/ApplicationMusicPlayer.hpp +2 -1
@@ 5,6 5,7 @@

#include <Application.hpp>
#include <Audio/decoder/Decoder.hpp>
#include <purefs/filesystem_paths.hpp>
#include <atomic>

namespace gui


@@ 14,7 15,7 @@ namespace gui
        namespace window
        {
            inline constexpr auto track_info_window = "Track Info";
            inline constexpr auto all_songs_window = "All Songs";
            inline constexpr auto all_songs_window  = "All Songs";
        }; // namespace window
    };     // namespace name
};         // namespace gui

M module-apps/application-music-player/models/SongsModel.cpp => module-apps/application-music-player/models/SongsModel.cpp +7 -7
@@ 9,7 9,7 @@
#include <service-audio/AudioServiceAPI.hpp>
#include <time/time_conversion.hpp>

namespace app::music_player
namespace app::music
{

    SongsListItemProvider::SongsListItemProvider(ApplicationCommon *app) : DatabaseModel(app)


@@ 90,10 90,10 @@ namespace app::music_player
                                OnSetNavBarTemporaryCallback navBarTemporaryMode,
                                OnRestoreNavBarTemporaryCallback navBarRestoreFromTemporaryMode)
    {
        this->shortReleaseCallback              = shortReleaseCallback;
        this->longPressCallback                 = longPressCallback;
        this->navBarTemporaryMode               = navBarTemporaryMode;
        this->navBarRestoreFromTemporaryMode    = navBarRestoreFromTemporaryMode;
        this->shortReleaseCallback           = shortReleaseCallback;
        this->longPressCallback              = longPressCallback;
        this->navBarTemporaryMode            = navBarTemporaryMode;
        this->navBarRestoreFromTemporaryMode = navBarRestoreFromTemporaryMode;

        songsRepository->initCache();
    }


@@ 135,7 135,7 @@ namespace app::music_player

    void SongsModel::setCurrentSongContext(SongContext context)
    {
        songContext = context;
        songContext     = context;
        activatedRecord = songsRepository->getRecord(context.filePath);
    }



@@ 175,4 175,4 @@ namespace app::music_player
        songsRepository->updateRepository(filePath);
    }

} // namespace app::music_player
} // namespace app::music

M module-apps/application-music-player/models/SongsModel.hpp => module-apps/application-music-player/models/SongsModel.hpp +5 -5
@@ 5,13 5,13 @@

#include "module-apps/application-music-player/data/MusicPlayerStyle.hpp"

#include "SongContext.hpp"
#include "SongsRepository.hpp"
#include "SongsModelInterface.hpp"
#include <apps-common/models/SongsRepository.hpp>
#include <apps-common/models/SongsModelInterface.hpp>

#include <apps-common/models/SongContext.hpp>
#include <Audio/decoder/Decoder.hpp>

namespace app::music_player
namespace app::music
{
    class SongsModel : public SongsModelInterface
    {


@@ 61,4 61,4 @@ namespace app::music_player

        std::shared_ptr<AbstractSongsRepository> songsRepository;
    };
} // namespace app::music_player
} // namespace app::music

M module-apps/application-music-player/presenters/SongsPresenter.cpp => module-apps/application-music-player/presenters/SongsPresenter.cpp +16 -15
@@ 10,7 10,7 @@
namespace app::music_player
{
    SongsPresenter::SongsPresenter(app::ApplicationCommon *app,
                                   std::shared_ptr<app::music_player::SongsModelInterface> songsModelInterface,
                                   std::shared_ptr<app::music::SongsModelInterface> songsModelInterface,
                                   std::unique_ptr<AbstractAudioOperations> &&audioOperations)
        : songsModelInterface{std::move(songsModelInterface)}, audioOperations{std::move(audioOperations)}
    {


@@ 19,7 19,7 @@ namespace app::music_player
            app, "MP Song Progres", tick, [this]([[maybe_unused]] sys::Timer &) { handleTrackProgressTick(); });
    }

    std::shared_ptr<app::music_player::SongsModelInterface> SongsPresenter::getMusicPlayerModelInterface() const
    std::shared_ptr<app::music::SongsModelInterface> SongsPresenter::getMusicPlayerModelInterface() const
    {
        return songsModelInterface;
    }


@@ 46,11 46,12 @@ namespace app::music_player
                return;
            }

            SongContext songContext{SongState::Playing, token, filePath, SongContext::StartPos};
            app::music::SongContext songContext{
                app::music::SongState::Playing, token, filePath, app::music::SongContext::StartPos};
            songsModelInterface->setCurrentSongContext(songContext);

            if (changePlayingStateCallback != nullptr) {
                changePlayingStateCallback(SongState::Playing);
                changePlayingStateCallback(app::music::SongState::Playing);
            }
            updateViewSongState();
            songsModelInterface->updateRepository(filePath);


@@ 76,9 77,9 @@ namespace app::music_player
                    LOG_ERROR("Pause audio operation failed, wrong token");
                    return;
                }
                songsModelInterface->setCurrentSongState(SongState::NotPlaying);
                songsModelInterface->setCurrentSongState(app::music::SongState::NotPlaying);
                if (changePlayingStateCallback != nullptr) {
                    changePlayingStateCallback(SongState::NotPlaying);
                    changePlayingStateCallback(app::music::SongState::NotPlaying);
                }
                updateViewSongState();
                songProgressTimer.stop();


@@ 106,9 107,9 @@ namespace app::music_player
                        LOG_ERROR("Resume audio operation failed, wrong token");
                        return;
                    }
                    songsModelInterface->setCurrentSongState(SongState::Playing);
                    songsModelInterface->setCurrentSongState(app::music::SongState::Playing);
                    if (changePlayingStateCallback != nullptr) {
                        changePlayingStateCallback(SongState::Playing);
                        changePlayingStateCallback(app::music::SongState::Playing);
                    }
                    updateViewSongState();
                    songProgressTimestamp = std::chrono::system_clock::now();


@@ 161,7 162,7 @@ namespace app::music_player
        updateViewSongState();
    }

    void SongsPresenter::setPlayingStateCallback(std::function<void(SongState)> cb)
    void SongsPresenter::setPlayingStateCallback(std::function<void(app::music::SongState)> cb)
    {
        changePlayingStateCallback = std::move(cb);
    }


@@ 171,7 172,7 @@ namespace app::music_player
        if (token == songsModelInterface->getCurrentFileToken()) {
            songsModelInterface->clearCurrentSongContext();
            if (changePlayingStateCallback != nullptr) {
                changePlayingStateCallback(SongState::NotPlaying);
                changePlayingStateCallback(app::music::SongState::NotPlaying);
            }
            if (!waitingToPlay) {
                updateViewSongState();


@@ 189,7 190,7 @@ namespace app::music_player
        if (token == currentSongContext.currentFileToken) {
            songsModelInterface->clearCurrentSongContext();
            if (changePlayingStateCallback != nullptr) {
                changePlayingStateCallback(SongState::NotPlaying);
                changePlayingStateCallback(app::music::SongState::NotPlaying);
            }
            auto nextSongToPlay = songsModelInterface->getNextFilePath(currentSongContext.filePath);
            if (!nextSongToPlay.empty()) {


@@ 207,9 208,9 @@ namespace app::music_player
    bool SongsPresenter::handleAudioPausedNotification(audio::Token token)
    {
        if (token == songsModelInterface->getCurrentFileToken()) {
            songsModelInterface->setCurrentSongState(SongState::NotPlaying);
            songsModelInterface->setCurrentSongState(app::music::SongState::NotPlaying);
            if (changePlayingStateCallback != nullptr) {
                changePlayingStateCallback(SongState::NotPlaying);
                changePlayingStateCallback(app::music::SongState::NotPlaying);
            }
            updateViewSongState();
            return true;


@@ 220,9 221,9 @@ namespace app::music_player
    bool SongsPresenter::handleAudioResumedNotification(audio::Token token)
    {
        if (token == songsModelInterface->getCurrentFileToken()) {
            songsModelInterface->setCurrentSongState(SongState::Playing);
            songsModelInterface->setCurrentSongState(app::music::SongState::Playing);
            if (changePlayingStateCallback != nullptr) {
                changePlayingStateCallback(SongState::Playing);
                changePlayingStateCallback(app::music::SongState::Playing);
            }
            updateViewSongState();
            return true;

M module-apps/application-music-player/presenters/SongsPresenter.hpp => module-apps/application-music-player/presenters/SongsPresenter.hpp +15 -15
@@ 6,7 6,7 @@
#include <apps-common/AudioOperations.hpp>
#include <apps-common/BasePresenter.hpp>

#include <module-apps/application-music-player/models/SongsModelInterface.hpp>
#include <apps-common/models/SongsModelInterface.hpp>

namespace app::music_player
{


@@ 23,24 23,24 @@ namespace app::music_player
                Stopped
            };

            virtual ~View() noexcept                                        = default;
            virtual ~View() noexcept                                     = default;
            virtual void updateSongsState(std::optional<db::multimedia_files::MultimediaFilesRecord> record,
                                          RecordState state)                = 0;
            virtual void updateSongProgress(float progress)                 = 0;
            virtual void refreshWindow()                                    = 0;
            virtual void setNavBarTemporaryMode(const std::string &text)    = 0;
            virtual void restoreFromNavBarTemporaryMode()                   = 0;
                                          RecordState state)             = 0;
            virtual void updateSongProgress(float progress)              = 0;
            virtual void refreshWindow()                                 = 0;
            virtual void setNavBarTemporaryMode(const std::string &text) = 0;
            virtual void restoreFromNavBarTemporaryMode()                = 0;
        };

        class Presenter : public BasePresenter<SongsContract::View>
        {
          public:
            using OnPlayingStateChangeCallback = std::function<void(SongState)>;
            using OnPlayingStateChangeCallback = std::function<void(app::music::SongState)>;

            virtual ~Presenter() noexcept = default;

            virtual std::shared_ptr<SongsModelInterface> getMusicPlayerModelInterface() const = 0;
            virtual void createData()                                                         = 0;
            virtual std::shared_ptr<app::music::SongsModelInterface> getMusicPlayerModelInterface() const = 0;
            virtual void createData()                                                                     = 0;

            virtual bool play(const std::string &filePath) = 0;
            virtual bool pause()                           = 0;


@@ 63,10 63,10 @@ namespace app::music_player
    {
      public:
        explicit SongsPresenter(app::ApplicationCommon *app,
                                std::shared_ptr<SongsModelInterface> songsListItemProvider,
                                std::shared_ptr<app::music::SongsModelInterface> songsListItemProvider,
                                std::unique_ptr<AbstractAudioOperations> &&audioOperations);

        std::shared_ptr<SongsModelInterface> getMusicPlayerModelInterface() const override;
        std::shared_ptr<app::music::SongsModelInterface> getMusicPlayerModelInterface() const override;

        void createData() override;



@@ 78,7 78,7 @@ namespace app::music_player
        bool playPrevious() override;

        void songsStateRequest() override;
        void setPlayingStateCallback(std::function<void(SongState)> cb) override;
        void setPlayingStateCallback(std::function<void(app::music::SongState)> cb) override;
        bool handleAudioStopNotifiaction(audio::Token token) override;
        bool handleAudioEofNotification(audio::Token token) override;
        bool handleAudioPausedNotification(audio::Token token) override;


@@ 100,9 100,9 @@ namespace app::music_player
        bool requestAudioOperation(const std::string &filePath = "");
        void setViewNavBarTemporaryMode(const std::string &text);
        void restoreViewNavBarFromTemporaryMode();
        std::shared_ptr<SongsModelInterface> songsModelInterface;
        std::shared_ptr<app::music::SongsModelInterface> songsModelInterface;
        std::unique_ptr<AbstractAudioOperations> audioOperations;
        std::function<void(SongState)> changePlayingStateCallback = nullptr;
        std::function<void(app::music::SongState)> changePlayingStateCallback = nullptr;

        sys::TimerHandle songProgressTimer;
        std::chrono::time_point<std::chrono::system_clock> songProgressTimestamp;

M module-apps/application-music-player/tests/MockSongsRepository.hpp => module-apps/application-music-player/tests/MockSongsRepository.hpp +3 -3
@@ 12,9 12,9 @@
#include <string>
#include <vector>

namespace testing::app::music_player
namespace testing::app::music
{
    class MockSongsRepository : public ::app::music_player::AbstractSongsRepository
    class MockSongsRepository : public ::app::music::AbstractSongsRepository
    {
      public:
        MOCK_METHOD(void,


@@ 31,4 31,4 @@ namespace testing::app::music_player
                    (const override));
        MOCK_METHOD(void, updateRepository, (const std::string &filePath), (override));
    };
}; // namespace testing::app::music_player
}; // namespace testing::app::music

M module-apps/application-music-player/tests/MockTagsFetcher.hpp => module-apps/application-music-player/tests/MockTagsFetcher.hpp +3 -3
@@ 10,11 10,11 @@

#include <optional>

namespace testing::app::music_player
namespace testing::app::music
{
    class MockTagsFetcher : public ::app::music_player::AbstractTagsFetcher
    class MockTagsFetcher : public ::app::music::AbstractTagsFetcher
    {
      public:
        MOCK_METHOD(std::optional<tags::fetcher::Tags>, getFileTags, (const std::string &filePath), (const override));
    };
}; // namespace testing::app::music_player
}; // namespace testing::app::music

M module-apps/application-music-player/tests/unittest_songsmodel.cpp => module-apps/application-music-player/tests/unittest_songsmodel.cpp +3 -3
@@ 10,9 10,9 @@
#include <memory>
#include <optional>

using ::app::music_player::SongsModel;
using ::app::music::SongsModel;
using ::testing::Return;
using ::testing::app::music_player::MockSongsRepository;
using ::testing::app::music::MockSongsRepository;

TEST(SongsModel, Init)
{


@@ 31,5 31,5 @@ TEST(SongsModel, EmptyContext)

    EXPECT_EQ(ctx.currentFileToken, std::nullopt);
    EXPECT_TRUE(ctx.filePath.empty());
    EXPECT_EQ(ctx.currentSongState, app::music_player::SongState::NotPlaying);
    EXPECT_EQ(ctx.currentSongState, app::music::SongState::NotPlaying);
}

M module-apps/apps-common/CMakeLists.txt => module-apps/apps-common/CMakeLists.txt +6 -0
@@ 15,6 15,8 @@ target_sources(apps-common
        StatusBarManager.cpp
        WindowsFactory.cpp
        audio/SoundsPlayer.cpp
        models/SongContext.cpp
        models/SongsRepository.cpp
        notifications/NotificationData.cpp
        notifications/NotificationListItem.cpp
        notifications/NotificationProvider.cpp


@@ 62,6 64,10 @@ target_sources(apps-common
        actions/AlarmRingingData.hpp
        actions/AlarmTriggeredAction.hpp

        models/SongContext.hpp
        models/SongsRepository.hpp
        models/SongsModelInterface.hpp

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

R module-apps/application-music-player/models/SongContext.cpp => module-apps/apps-common/models/SongContext.cpp +2 -2
@@ 4,7 4,7 @@
#include "SongContext.hpp"
#include <optional>

namespace app::music_player
namespace app::music
{
    void SongContext::clear()
    {


@@ 28,4 28,4 @@ namespace app::music_player
    {
        return isValid() && currentSongState == SongState::NotPlaying;
    }
} // namespace app::music_player
} // namespace app::music

R module-apps/application-music-player/models/SongContext.hpp => module-apps/apps-common/models/SongContext.hpp +3 -3
@@ 4,7 4,7 @@

#include <Audio/decoder/Decoder.hpp>

namespace app::music_player
namespace app::music
{

    enum class SongState


@@ 16,7 16,7 @@ namespace app::music_player
    struct SongContext
    {
        static constexpr uint32_t StartPos = 0;
        SongState currentSongState = SongState::NotPlaying;
        SongState currentSongState         = SongState::NotPlaying;
        std::optional<audio::Token> currentFileToken;
        std::string filePath;
        uint32_t currentPos = StartPos;


@@ 28,4 28,4 @@ namespace app::music_player
        bool isValid() const;
    };

} // namespace app::music_player
} // namespace app::music

R module-apps/application-music-player/models/SongsModelInterface.hpp => module-apps/apps-common/models/SongsModelInterface.hpp +17 -18
@@ 5,30 5,29 @@

#include "SongContext.hpp"
#include <string>
#include <widgets/SongItem.hpp>
#include <ListItemProvider.hpp>
#include <apps-common/ApplicationCommon.hpp>
#include <apps-common/DatabaseModel.hpp>
#include <module-db/Interface/MultimediaFilesRecord.hpp>

namespace app::music_player
namespace app::music
{
    class SongsListItemProvider : public app::DatabaseModel<db::multimedia_files::MultimediaFilesRecord>,
                                  public gui::ListItemProvider
    {
      public:
        using OnShortReleaseCallback              = std::function<bool(const std::string &fileName)>;
        using OnLongPressCallback                 = std::function<void()>;
        using OnSetNavBarTemporaryCallback        = std::function<void(const UTF8 &)>;
        using OnRestoreNavBarTemporaryCallback    = std::function<void()>;
        using OnShortReleaseCallback           = std::function<bool(const std::string &fileName)>;
        using OnLongPressCallback              = std::function<void()>;
        using OnSetNavBarTemporaryCallback     = std::function<void(const UTF8 &)>;
        using OnRestoreNavBarTemporaryCallback = std::function<void()>;
        explicit SongsListItemProvider(app::ApplicationCommon *app);
        virtual ~SongsListItemProvider() noexcept = default;

        virtual void createData(OnShortReleaseCallback shortReleaseCallback,
                                OnLongPressCallback longPressCallback,
                                OnSetNavBarTemporaryCallback navBarTemporaryMode,
                                OnRestoreNavBarTemporaryCallback navBarRestoreFromTemporaryMode)       = 0;
        virtual void clearData()                                                                       = 0;
                                OnRestoreNavBarTemporaryCallback navBarRestoreFromTemporaryMode) = 0;
        virtual void clearData()                                                                 = 0;
    };

    class SongsModelInterface : public SongsListItemProvider


@@ 37,15 36,15 @@ namespace app::music_player
        explicit SongsModelInterface(app::ApplicationCommon *app);
        virtual ~SongsModelInterface() noexcept = default;

        virtual bool isSongPlaying() const noexcept                              = 0;
        virtual void setCurrentSongState(SongState songState) noexcept           = 0;
        virtual std::optional<audio::Token> getCurrentFileToken() const noexcept = 0;
        virtual bool isSongPlaying() const noexcept                                                            = 0;
        virtual void setCurrentSongState(SongState songState) noexcept                                         = 0;
        virtual std::optional<audio::Token> getCurrentFileToken() const noexcept                               = 0;
        virtual std::optional<db::multimedia_files::MultimediaFilesRecord> getActivatedRecord() const noexcept = 0;
        virtual SongContext getCurrentSongContext() const noexcept               = 0;
        virtual void setCurrentSongContext(SongContext context)                  = 0;
        virtual void clearCurrentSongContext()                                   = 0;
        virtual std::string getNextFilePath(const std::string &filePath) const     = 0;
        virtual std::string getPreviousFilePath(const std::string &filePath) const = 0;
        virtual void updateRepository(const std::string &filePath)                 = 0;
        virtual SongContext getCurrentSongContext() const noexcept                                             = 0;
        virtual void setCurrentSongContext(SongContext context)                                                = 0;
        virtual void clearCurrentSongContext()                                                                 = 0;
        virtual std::string getNextFilePath(const std::string &filePath) const                                 = 0;
        virtual std::string getPreviousFilePath(const std::string &filePath) const                             = 0;
        virtual void updateRepository(const std::string &filePath)                                             = 0;
    };
} // namespace app::music_player
} // namespace app::music

R module-apps/application-music-player/models/SongsRepository.cpp => module-apps/apps-common/models/SongsRepository.cpp +9 -5
@@ 10,6 10,7 @@
#include <time/ScopedTime.hpp>
#include <service-audio/AudioMessage.hpp>
#include <module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.hpp>
#include <module-db/queries/multimedia_files/QueryMultimediaFilesGet.hpp>

#include <filesystem>



@@ 19,7 20,7 @@ namespace constants
    inline constexpr auto cacheThreshold{10};
} // namespace constants

namespace app::music_player
namespace app::music
{
    ServiceAudioTagsFetcher::ServiceAudioTagsFetcher(ApplicationCommon *application) : application(application)
    {}


@@ 29,8 30,11 @@ namespace app::music_player
        return tags::fetcher::fetchTags(filePath);
    }

    SongsRepository::SongsRepository(ApplicationCommon *application, std::unique_ptr<AbstractTagsFetcher> tagsFetcher)
        : app::AsyncCallbackReceiver{application}, application{application}, tagsFetcher(std::move(tagsFetcher))
    SongsRepository::SongsRepository(ApplicationCommon *application,
                                     std::unique_ptr<AbstractTagsFetcher> tagsFetcher,
                                     std::string pathPrefix)
        : app::AsyncCallbackReceiver{application}, application{application},
          tagsFetcher(std::move(tagsFetcher)), pathPrefix{pathPrefix}
    {}

    void SongsRepository::initCache()


@@ 55,7 59,7 @@ namespace app::music_player
                                            std::uint32_t limit,
                                            const OnGetMusicFilesListCallback &callback)
    {
        auto query = std::make_unique<db::multimedia_files::query::GetLimited>(offset, limit);
        auto query = std::make_unique<db::multimedia_files::query::GetLimitedByPath>(pathPrefix, offset, limit);
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::MultimediaFiles);

        task->setCallback([this, callback, offset](auto response) {


@@ 252,4 256,4 @@ namespace app::music_player
        return musicFilesModelCache.records[index];
    }

} // namespace app::music_player
} // namespace app::music

R module-apps/application-music-player/models/SongsRepository.hpp => module-apps/apps-common/models/SongsRepository.hpp +12 -10
@@ 14,7 14,7 @@

#include <cstddef>

namespace app::music_player
namespace app::music
{
    struct FilesCache
    {


@@ 54,22 54,22 @@ namespace app::music_player
        virtual void getMusicFilesList(std::uint32_t offset,
                                       std::uint32_t limit,
                                       const OnGetMusicFilesListCallback &callback) = 0;
        virtual std::string getNextFilePath(const std::string &filePath) const     = 0;
        virtual std::string getPreviousFilePath(const std::string &filePath) const = 0;
        virtual std::string getNextFilePath(const std::string &filePath) const      = 0;
        virtual std::string getPreviousFilePath(const std::string &filePath) const  = 0;
        virtual std::optional<db::multimedia_files::MultimediaFilesRecord> getRecord(
            const std::string &filePath) const                                      = 0;
        virtual void updateRepository(const std::string &filePath)                  = 0;
            const std::string &filePath) const                     = 0;
        virtual void updateRepository(const std::string &filePath) = 0;
    };

    class SongsRepository : public AbstractSongsRepository, public app::AsyncCallbackReceiver
    {
      public:
        explicit SongsRepository(ApplicationCommon *application, std::unique_ptr<AbstractTagsFetcher> tagsFetcher);
        explicit SongsRepository(ApplicationCommon *application,
                                 std::unique_ptr<AbstractTagsFetcher> tagsFetcher,
                                 std::string pathPrefix);

        void initCache();
        void getMusicFilesList(std::uint32_t offset,
                               std::uint32_t limit,
                               const OnGetMusicFilesListCallback &callback) override;
        void getMusicFilesList(std::uint32_t offset, std::uint32_t limit, const OnGetMusicFilesListCallback &callback);
        std::string getNextFilePath(const std::string &filePath) const override;
        std::string getPreviousFilePath(const std::string &filePath) const override;
        std::optional<db::multimedia_files::MultimediaFilesRecord> getRecord(


@@ 88,6 88,8 @@ namespace app::music_player

        std::unique_ptr<AbstractTagsFetcher> tagsFetcher;

        std::string pathPrefix;

        /// collection of music files displayed in the list view
        FilesCache musicFilesViewCache;
        /// collection of music files currently playing


@@ 119,4 121,4 @@ namespace app::music_player
                                  unsigned int repoRecordsCount,
                                  std::uint32_t offset);
    };
} // namespace app::music_player
} // namespace app::music

M module-db/Interface/MultimediaFilesRecord.cpp => module-db/Interface/MultimediaFilesRecord.cpp +13 -1
@@ 36,6 36,9 @@ namespace db::multimedia_files
        if (typeid(*query) == typeid(query::GetLimited)) {
            return runQueryImplGetLimited(std::static_pointer_cast<query::GetLimited>(query));
        }
        if (typeid(*query) == typeid(query::GetLimitedByPath)) {
            return runQueryImplGetLimited(std::static_pointer_cast<query::GetLimitedByPath>(query));
        }
        if (typeid(*query) == typeid(query::Remove)) {
            return runQueryImplRemove(std::static_pointer_cast<query::Remove>(query));
        }


@@ 78,7 81,6 @@ namespace db::multimedia_files
        if (typeid(*query) == typeid(query::GetByPath)) {
            return runQueryImplGetByPath(std::static_pointer_cast<query::GetByPath>(query));
        }

        return nullptr;
    }



@@ 249,6 251,16 @@ namespace db::multimedia_files
        return response;
    }

    std::unique_ptr<db::multimedia_files::query::GetLimitedResult> MultimediaFilesRecordInterface::
        runQueryImplGetLimited(const std::shared_ptr<db::multimedia_files::query::GetLimitedByPath> &query)
    {
        const auto records = database->files.getLimitOffsetByPath(query->path, query->offset, query->limit);
        auto response      = std::make_unique<query::GetLimitedResult>(records, database->files.count());
        response->setRequestQuery(query);

        return response;
    }

    std::unique_ptr<db::multimedia_files::query::GetCountResult> MultimediaFilesRecordInterface::runQueryImplGetCount(
        const std::shared_ptr<db::multimedia_files::query::GetCountForAlbum> &query)
    {

M module-db/Interface/MultimediaFilesRecord.hpp => module-db/Interface/MultimediaFilesRecord.hpp +3 -0
@@ 34,6 34,7 @@ namespace db::multimedia_files::query
    class GetCountForArtist;
    class GetCountResult;
    class GetLimited;
    class GetLimitedByPath;
    class GetLimitedForAlbum;
    class GetLimitedForArtist;
    class GetLimitedResult;


@@ 70,6 71,8 @@ namespace db::multimedia_files
            const std::shared_ptr<db::multimedia_files::query::Get> &query);
        std::unique_ptr<db::multimedia_files::query::GetLimitedResult> runQueryImplGetLimited(
            const std::shared_ptr<db::multimedia_files::query::GetLimited> &query);
        std::unique_ptr<db::multimedia_files::query::GetLimitedResult> runQueryImplGetLimited(
            const std::shared_ptr<db::multimedia_files::query::GetLimitedByPath> &query);
        std::unique_ptr<db::multimedia_files::query::RemoveResult> runQueryImplRemove(
            const std::shared_ptr<db::multimedia_files::query::Remove> &query);
        std::unique_ptr<db::multimedia_files::query::RemoveResult> runQueryImplRemoveAll(

M module-db/Tables/MultimediaFilesTable.cpp => module-db/Tables/MultimediaFilesTable.cpp +9 -0
@@ 330,4 330,13 @@ namespace db::multimedia_files

        return (*queryRet)[0].getUInt32();
    }

    auto MultimediaFilesTable::getLimitOffsetByPath(const std::string &path, uint32_t offset, uint32_t limit)
        -> std::vector<TableRow>
    {
        std::string query = "SELECT * FROM files WHERE path LIKE '" + path + "%%' ORDER BY title ASC LIMIT " +
                            std::to_string(limit) + " OFFSET " + std::to_string(offset) + ";";
        std::unique_ptr<QueryResult> retQuery = db->query(query.c_str());
        return retQueryUnpack(std::move(retQuery));
    }
} // namespace db::multimedia_files

M module-db/Tables/MultimediaFilesTable.hpp => module-db/Tables/MultimediaFilesTable.hpp +1 -0
@@ 101,6 101,7 @@ namespace db::multimedia_files
        auto getLimitOffset(const Album &album, uint32_t offset, uint32_t limit) -> std::vector<TableRow>;
        auto count(const Album &album) -> uint32_t;

        auto getLimitOffsetByPath(const std::string &path, uint32_t offset, uint32_t limit) -> std::vector<TableRow>;
        TableRow getByPath(std::string path);

        /// @note entry.ID is skipped

M module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.cpp => module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.cpp +9 -0
@@ 105,4 105,13 @@ namespace db::multimedia_files::query
    {
        return std::string{"GetAlbumsLimitedResult"};
    }

    GetLimitedByPath::GetLimitedByPath(std::string path, uint32_t offset, uint32_t limit)
        : Query(Query::Type::Read), path{path}, offset(offset), limit(limit)
    {}

    auto GetLimitedByPath::debugInfo() const -> std::string
    {
        return std::string{"GetLimitedByPath"};
    }
} // namespace db::multimedia_files::query

M module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.hpp => module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.hpp +11 -0
@@ 100,4 100,15 @@ namespace db::multimedia_files::query
        [[nodiscard]] auto getCount() const noexcept -> unsigned int;
        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class GetLimitedByPath : public Query
    {
      public:
        GetLimitedByPath(std::string path, uint32_t offset, uint32_t limit);
        [[nodiscard]] auto debugInfo() const -> std::string override;

        const std::string path;
        const uint32_t offset = 0;
        const uint32_t limit  = 0;
    };
} // namespace db::multimedia_files::query

M module-db/tests/MultimediaFilesTable_tests.cpp => module-db/tests/MultimediaFilesTable_tests.cpp +24 -0
@@ 345,6 345,15 @@ TEST_CASE("Multimedia DB tests")
            return record;
        };

        auto getLimitedByPathQuery = [&](const std::string &pathPrefix, const uint32_t offset, const uint32_t limit) {
            auto query  = std::make_shared<db::multimedia_files::query::GetLimitedByPath>(pathPrefix, offset, limit);
            auto ret    = multimediaFilesRecordInterface.runQuery(query);
            auto result = dynamic_cast<db::multimedia_files::query::GetLimitedResult *>(ret.get());
            REQUIRE(result != nullptr);
            auto record = result->getResult();
            return record;
        };

        auto updateQuery = [&](const MultimediaFilesRecord &record) {
            const auto query  = std::make_shared<db::multimedia_files::query::Edit>(record);
            const auto ret    = multimediaFilesRecordInterface.runQuery(query);


@@ 564,6 573,21 @@ TEST_CASE("Multimedia DB tests")
            REQUIRE(getLimitedQuery(size - 3, size).size() == 3);
        }

        SECTION("getLimitOffsetByPath")
        {
            auto rootPrefix   = "user/";
            auto musicPrefix  = "user/music/";
            auto falsePrefix1 = "user/music1/";
            auto flasePrefix2 = "user/music2";
            auto falsePrefix3 = "abc/";
            auto size         = records.size();
            REQUIRE(getLimitedByPathQuery(rootPrefix, 0, size).size() == size);
            REQUIRE(getLimitedByPathQuery(musicPrefix, 0, size).size() == (size - 3));
            REQUIRE(getLimitedByPathQuery(falsePrefix1, 0, size).size() == 0);
            REQUIRE(getLimitedByPathQuery(flasePrefix2, 0, size).size() == 0);
            REQUIRE(getLimitedByPathQuery(falsePrefix3, 0, size).size() == 0);
        }

        SECTION("Artists")
        {
            REQUIRE(getCountArtistsQuery() == artists.size());

M module-services/CMakeLists.txt => module-services/CMakeLists.txt +1 -0
@@ 14,6 14,7 @@ add_subdirectory( service-db )
add_subdirectory( service-desktop )
add_subdirectory( service-eink )
add_subdirectory( service-evtmgr )
add_subdirectory( service-fileindexer )
add_subdirectory( service-gui )
add_subdirectory( service-time )


R products/PurePhone/services/service-fileindexer/CMakeLists.txt => module-services/service-fileindexer/CMakeLists.txt +0 -0
R products/PurePhone/services/service-fileindexer/Common.hpp => module-services/service-fileindexer/Common.hpp +0 -0
R products/PurePhone/services/service-fileindexer/InotifyHandler.cpp => module-services/service-fileindexer/InotifyHandler.cpp +0 -0
R products/PurePhone/services/service-fileindexer/ServiceFileIndexer.cpp => module-services/service-fileindexer/ServiceFileIndexer.cpp +3 -2
@@ 19,9 19,10 @@ namespace
namespace service
{

    ServiceFileIndexer::ServiceFileIndexer(const std::string_view name) : sys::Service(std::string(name))
    ServiceFileIndexer::ServiceFileIndexer(const std::vector<std::string> &paths)
        : sys::Service{service::name::file_indexer}, mStartupIndexer{paths}
    {
        LOG_DEBUG("[%s] Initializing", std::string(name).c_str());
        LOG_DEBUG("[%s] Initializing", service::name::file_indexer);
    }

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

R products/PurePhone/services/service-fileindexer/StartupIndexer.cpp => module-services/service-fileindexer/StartupIndexer.cpp +3 -3
@@ 18,9 18,6 @@ namespace service::detail
    namespace
    {
        using namespace std::string_literals;

        // List of initial dirs for scan
        const std::vector<std::string> start_dirs{purefs::dir::getUserDiskPath() / "music"};
        // Lock file name
        const auto lock_file_name = purefs::dir::getUserDiskPath() / ".directory_is_indexed";
        // Time for indexing first unit


@@ 29,6 26,9 @@ namespace service::detail
        constexpr auto timer_run_delay = 10000;
    } // namespace

    StartupIndexer::StartupIndexer(const std::vector<std::string> &paths) : start_dirs{paths}
    {}

    // Process single entry
    auto StartupIndexer::processEntry(std::shared_ptr<sys::Service> svc,
                                      const std::filesystem::recursive_directory_iterator::value_type &entry) -> void

R products/PurePhone/services/service-fileindexer/include/service-fileindexer/Constants.hpp => module-services/service-fileindexer/include/service-fileindexer/Constants.hpp +0 -0
R products/PurePhone/services/service-fileindexer/include/service-fileindexer/InotifyHandler.hpp => module-services/service-fileindexer/include/service-fileindexer/InotifyHandler.hpp +0 -0
R products/PurePhone/services/service-fileindexer/include/service-fileindexer/ServiceFileIndexer.hpp => module-services/service-fileindexer/include/service-fileindexer/ServiceFileIndexer.hpp +1 -1
@@ 15,7 15,7 @@ namespace service
    class ServiceFileIndexer final : public sys::Service
    {
      public:
        ServiceFileIndexer(const std::string_view name = service::name::file_indexer);
        ServiceFileIndexer(const std::vector<std::string> &paths);
        virtual ~ServiceFileIndexer()                  = default;
        ServiceFileIndexer(const ServiceFileIndexer &) = delete;
        ServiceFileIndexer &operator=(const ServiceFileIndexer &) = delete;

R products/PurePhone/services/service-fileindexer/include/service-fileindexer/StartupIndexer.hpp => module-services/service-fileindexer/include/service-fileindexer/StartupIndexer.hpp +4 -1
@@ 13,7 13,7 @@ namespace service::detail
    {

      public:
        StartupIndexer()                       = default;
        explicit StartupIndexer(const std::vector<std::string> &paths);
        ~StartupIndexer()                      = default;
        StartupIndexer(const StartupIndexer &) = delete;
        StartupIndexer &operator=(StartupIndexer) = delete;


@@ 41,5 41,8 @@ namespace service::detail
        sys::TimerHandle mIdxTimer;
        bool mStarted{};
        bool mForceStop{};

        // List of initial dirs for scan
        const std::vector<std::string> start_dirs;
    };
} // namespace service::detail

M products/BellHybrid/BellHybridMain.cpp => products/BellHybrid/BellHybridMain.cpp +7 -0
@@ 15,7 15,9 @@

// modules
#include <module-db/Databases/EventsDB.hpp>
#include <module-db/Databases/MultimediaFilesDB.hpp>
#include <module-db/Interface/AlarmEventRecord.hpp>
#include <module-db/Interface/MultimediaFilesRecord.hpp>

// services
#include <appmgr/ApplicationManager.hpp>


@@ 28,6 30,7 @@
#include <service-eink/ServiceEink.hpp>
#include <service-gui/ServiceGUI.hpp>
#include <service-time/ServiceTime.hpp>
#include <service-fileindexer/ServiceFileIndexer.hpp>

#include <Application.hpp>
#include <ApplicationLauncher.hpp>


@@ 47,6 50,9 @@ int main()
{
    constexpr auto ApplicationName = "BellHybrid";

    const std::vector<std::string> fileIndexerAudioPaths = {
        {purefs::dir::getCurrentOSPath() / "assets/audio/bell/bg_sounds"}};

#if SYSTEM_VIEW_ENABLED
    SEGGER_SYSVIEW_Conf();
    SEGGER_SYSVIEW_DisableEvents(SYSVIEW_EVTMASK_ISR_ENTER);


@@ 68,6 74,7 @@ int main()

    std::vector<std::unique_ptr<sys::BaseServiceCreator>> systemServices;
    systemServices.emplace_back(sys::CreatorFor<EventManager>([]() { return dumpLogs(); }));
    systemServices.emplace_back(sys::CreatorFor<service::ServiceFileIndexer>(std::move(fileIndexerAudioPaths)));
    systemServices.emplace_back(sys::CreatorFor<ServiceDB>());
    systemServices.emplace_back(sys::CreatorFor<service::Audio>());
    systemServices.emplace_back(sys::CreatorFor<ServiceDesktop>());

M products/BellHybrid/CMakeLists.txt => products/BellHybrid/CMakeLists.txt +1 -0
@@ 58,6 58,7 @@ target_link_libraries(BellHybrid
        module-vfs
        service-desktop
        service-time
        service-fileindexer
        sys
        bell::time
        platform

M products/BellHybrid/apps/application-bell-background-sounds/ApplicationBellBackgroundSounds.cpp => products/BellHybrid/apps/application-bell-background-sounds/ApplicationBellBackgroundSounds.cpp +11 -3
@@ 2,7 2,6 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ApplicationBellBackgroundSounds.hpp"
#include "models/BGSoundsRepository.hpp"
#include "presenter/BGSoundsMainWindowPresenter.hpp"
#include "presenter/BGSoundsTimerSelectPresenter.hpp"
#include "presenter/BGSoundsProgressPresenter.hpp"


@@ 14,11 13,19 @@
#include "windows/BGSoundsVolumeWindow.hpp"
#include "widgets/BGSoundsPlayer.hpp"
#include <apps-common/messages/AppMessage.hpp>
#include <apps-common/models/SongsRepository.hpp>
#include <common/models/TimeModel.hpp>
#include <common/models/AudioModel.hpp>
#include <audio/AudioMessage.hpp>

#include <log/log.hpp>

namespace
{
    const auto backgroundSoundsFilesPath =
        purefs::createPath(purefs::dir::getCurrentOSPath(), "assets/audio/bell/bg_sounds").string();
}

namespace app
{
    ApplicationBellBackgroundSounds::ApplicationBellBackgroundSounds(std::string name,


@@ 48,8 55,9 @@ namespace app
    void ApplicationBellBackgroundSounds::createUserInterface()
    {
        windowsFactory.attach(gui::name::window::main_window, [this](ApplicationCommon *app, const std::string &name) {
            auto tagsFetcher      = std::make_unique<bgSounds::BGSoundsTagsFetcher>(app);
            auto soundsRepository = std::make_shared<bgSounds::BGSoundsRepository>(std::move(tagsFetcher));
            auto tagsFetcher = std::make_unique<app::music::ServiceAudioTagsFetcher>(app);
            auto soundsRepository =
                std::make_unique<app::music::SongsRepository>(app, std::move(tagsFetcher), backgroundSoundsFilesPath);
            auto presenter = std::make_unique<bgSounds::BGSoundsMainWindowPresenter>(std::move(soundsRepository));
            return std::make_unique<gui::BGSoundsMainWindow>(app, std::move(presenter));
        });

M products/BellHybrid/apps/application-bell-background-sounds/CMakeLists.txt => products/BellHybrid/apps/application-bell-background-sounds/CMakeLists.txt +0 -2
@@ 13,7 13,6 @@ target_include_directories(application-bell-background-sounds
target_sources(application-bell-background-sounds
    PRIVATE
        ApplicationBellBackgroundSounds.cpp
        models/BGSoundsRepository.cpp
        presenter/BGSoundsMainWindowPresenter.cpp
        presenter/BGSoundsProgressPresenter.cpp
        presenter/BGSoundsTimerSelectPresenter.cpp


@@ 28,7 27,6 @@ target_sources(application-bell-background-sounds
        data/BGSoundsCommon.hpp
        data/BGSoundsStyle.hpp
        data/BGSoundsAudioData.hpp
        models/BGSoundsRepository.hpp
        widgets/BGSoundsPlayer.hpp
        windows/BGSoundsMainWindow.hpp
        presenter/BGSoundsMainWindowPresenter.hpp

M products/BellHybrid/apps/application-bell-background-sounds/data/BGSoundsAudioData.hpp => products/BellHybrid/apps/application-bell-background-sounds/data/BGSoundsAudioData.hpp +5 -4
@@ 3,6 3,7 @@

#pragma once
#include <SwitchData.hpp>
#include <module-db/Interface/MultimediaFilesRecord.hpp>
#include <tags_fetcher/TagsFetcher.hpp>

#include <chrono>


@@ 13,14 14,14 @@ namespace gui
{
    class BGSoundsAudioContext
    {
        tags::fetcher::Tags tags;
        db::multimedia_files::MultimediaFilesRecord sound;

      public:
        explicit BGSoundsAudioContext(const tags::fetcher::Tags &tags) : tags{tags}
        explicit BGSoundsAudioContext(const db::multimedia_files::MultimediaFilesRecord &sound) : sound{sound}
        {}
        [[nodiscard]] const tags::fetcher::Tags &getTags() const noexcept
        [[nodiscard]] const db::multimedia_files::MultimediaFilesRecord &getSound() const noexcept
        {
            return tags;
            return sound;
        }
    };


M products/BellHybrid/apps/application-bell-background-sounds/include/application-bell-background-sounds/ApplicationBellBackgroundSounds.hpp => products/BellHybrid/apps/application-bell-background-sounds/include/application-bell-background-sounds/ApplicationBellBackgroundSounds.hpp +1 -0
@@ 5,6 5,7 @@

#include <Application.hpp>
#include <common/models/AbstractAudioModel.hpp>
#include <purefs/filesystem_paths.hpp>

namespace gui::window::name
{

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

#include "BGSoundsRepository.hpp"

#include <algorithm>
#include <log/log.hpp>
#include <time/ScopedTime.hpp>
#include <tags_fetcher/TagsFetcher.hpp>
#include <apps-common/models/SongsRepository.hpp>
#include <module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.hpp>

#include <filesystem>



@@ 20,41 20,39 @@ namespace app::bgSounds
        return tags::fetcher::fetchTags(filePath);
    }

    BGSoundsRepository::BGSoundsRepository(std::unique_ptr<AbstractTagsFetcher> tagsFetcher,
    BGSoundsRepository::BGSoundsRepository(ApplicationCommon *application,
                                           std::unique_ptr<AbstractTagsFetcher> tagsFetcher,
                                           std::string musicFolderName)
        : tagsFetcher(std::move(tagsFetcher)), musicFolderName(std::move(musicFolderName))
        : app::AsyncCallbackReceiver{application}, application{application}, tagsFetcher(std::move(tagsFetcher)),
          musicFolderName(std::move(musicFolderName))
    {}

    void BGSoundsRepository::scanMusicFilesList()
    void BGSoundsRepository::getMusicFilesList(std::uint32_t offset,
                                               std::uint32_t limit,
                                               const OnGetMusicFilesListCallback &callback)
    {
        musicFiles.clear();

        LOG_INFO("Scanning music folder: %s", musicFolderName.c_str());
        {
            auto time = utils::time::Scoped("fetch tags time");
            for (const auto &entry : std::filesystem::directory_iterator(musicFolderName)) {
                if (!std::filesystem::is_directory(entry)) {
                    const auto &filePath = entry.path();
                    const auto fileTags  = tagsFetcher->getFileTags(filePath);
                    if (fileTags) {
                        musicFiles.push_back(*fileTags);
                    }
                    else {
                        LOG_ERROR("Scanned not an audio file, skipped");
                    }
                }
        auto query = std::make_unique<db::multimedia_files::query::GetLimited>(offset, limit);
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::MultimediaFiles);

        task->setCallback([this, callback, offset](auto response) {
            auto result = dynamic_cast<db::multimedia_files::query::GetLimitedResult *>(response);
            musicFilesViewCache.records.clear();

            if (result == nullptr) {
                return false;
            }
            std::sort(
                musicFiles.begin(), musicFiles.end(), [](const tags::fetcher::Tags &t1, const tags::fetcher::Tags &t2) {
                    return t1.filePath < t2.filePath;
                });
        }
        LOG_INFO("Total number of music files found: %u", static_cast<unsigned int>(musicFiles.size()));
    }

    std::vector<tags::fetcher::Tags> BGSoundsRepository::getMusicFilesList() const
    {
        return musicFiles;
    }
            for (auto &record : result->getResult()) {
                musicFilesViewCache.records.push_back(record);
            }
            musicFilesViewCache.recordsOffset = offset;
            musicFilesViewCache.recordsCount  = result->getCount();

            if (callback) {
                callback(musicFilesViewCache.records, musicFilesViewCache.recordsCount);
            }
            return true;
        });
        task->execute(application, this);
    }
} // namespace app::bgSounds

M products/BellHybrid/apps/application-bell-background-sounds/models/BGSoundsRepository.hpp => products/BellHybrid/apps/application-bell-background-sounds/models/BGSoundsRepository.hpp +17 -7
@@ 16,6 16,13 @@

namespace app::bgSounds
{
    struct FilesCache
    {
        std::vector<db::multimedia_files::MultimediaFilesRecord> records{};
        std::uint32_t recordsOffset{0};
        std::uint32_t recordsCount{0};
    };

    class AbstractTagsFetcher
    {
      public:


@@ 40,25 47,28 @@ namespace app::bgSounds
      public:
        virtual ~AbstractSoundsRepository() noexcept = default;

        virtual void scanMusicFilesList()                                  = 0;
        virtual std::vector<tags::fetcher::Tags> getMusicFilesList() const = 0;
    };

    class BGSoundsRepository : public AbstractSoundsRepository
    class BGSoundsRepository : public AbstractSoundsRepository, public app::AsyncCallbackReceiver
    {
        static constexpr auto bgsoundsSubfolderPath = "assets/audio/bell/bg_sounds";

      public:
        explicit BGSoundsRepository(std::unique_ptr<AbstractTagsFetcher> tagsFetcher,
                                    std::string musicFolderName = purefs::dir::getCurrentOSPath() /
                                                                  bgsoundsSubfolderPath);
        using OnGetMusicFilesListCallback =
            std::function<bool(const std::vector<db::multimedia_files::MultimediaFilesRecord> &, unsigned int)>;

        void scanMusicFilesList() override;
        std::vector<tags::fetcher::Tags> getMusicFilesList() const override;
        BGSoundsRepository(ApplicationCommon *application,
                           std::unique_ptr<AbstractTagsFetcher> tagsFetcher,
                           std::string musicFolderName = purefs::dir::getCurrentOSPath() / bgsoundsSubfolderPath);

        void getMusicFilesList(std::uint32_t offset, std::uint32_t limit, const OnGetMusicFilesListCallback &callback);

      private:
        ApplicationCommon *application = nullptr;
        std::unique_ptr<AbstractTagsFetcher> tagsFetcher;
        std::string musicFolderName;
        FilesCache musicFilesViewCache;
        std::vector<tags::fetcher::Tags> musicFiles;
    };
} // namespace app::bgSounds

M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsMainWindowPresenter.cpp => products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsMainWindowPresenter.cpp +21 -7
@@ 2,18 2,32 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "BGSoundsMainWindowPresenter.hpp"
#include "models/BGSoundsRepository.hpp"
#include <application-music-player/models/SongsRepository.hpp>
#include <apps-common/models/SongsRepository.hpp>
#include <purefs/filesystem_paths.hpp>

namespace
{
    constexpr auto soundsRepoOffset = 0;
    constexpr auto soundsRepoLimit  = 100;
} // namespace

namespace app::bgSounds
{
    BGSoundsMainWindowPresenter::BGSoundsMainWindowPresenter(std::shared_ptr<AbstractSoundsRepository> soundsRepository)
    auto bgSoundsPath = purefs::dir::getCurrentOSPath() / "assets" / "audio" / "bell" / "bg_sounds";

    BGSoundsMainWindowPresenter::BGSoundsMainWindowPresenter(
        std::unique_ptr<app::music::AbstractSongsRepository> soundsRepository)
        : soundsRepository{std::move(soundsRepository)}
    {
        this->soundsRepository->scanMusicFilesList();
    }
    {}
    void BGSoundsMainWindowPresenter::loadAudioRecords()
    {
        getView()->setSoundsList(soundsRepository->getMusicFilesList());
        soundsRepository->getMusicFilesList(
            soundsRepoOffset,
            soundsRepoLimit,
            [this](const std::vector<db::multimedia_files::MultimediaFilesRecord> &records,
                   unsigned int repoRecordsCount) {
                getView()->setSoundsList(records);
                return true;
            });
    }
} // namespace app::bgSounds

M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsMainWindowPresenter.hpp => products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsMainWindowPresenter.hpp +8 -4
@@ 4,11 4,16 @@
#pragma once

#include <apps-common/BasePresenter.hpp>
#include <module-db/Interface/MultimediaFilesRecord.hpp>
#include <tags_fetcher/TagsFetcher.hpp>
#include <memory>
#include <string>
#include <vector>

namespace app::music
{
    class AbstractSongsRepository;
}
namespace app::bgSounds
{
    class BGSoundsMainWindowContract


@@ 19,7 24,7 @@ namespace app::bgSounds
          public:
            virtual ~View() = default;

            virtual void setSoundsList(std::vector<tags::fetcher::Tags> soundsTags) = 0;
            virtual void setSoundsList(std::vector<db::multimedia_files::MultimediaFilesRecord> songs) = 0;
        };

        class Presenter : public BasePresenter<BGSoundsMainWindowContract::View>


@@ 29,14 34,13 @@ namespace app::bgSounds
        };
    };

    class AbstractSoundsRepository;
    class BGSoundsMainWindowPresenter : public BGSoundsMainWindowContract::Presenter
    {
        std::shared_ptr<AbstractSoundsRepository> soundsRepository;
        std::unique_ptr<app::music::AbstractSongsRepository> soundsRepository;
        void loadAudioRecords() override;

      public:
        explicit BGSoundsMainWindowPresenter(std::shared_ptr<AbstractSoundsRepository> soundsRepository);
        explicit BGSoundsMainWindowPresenter(std::unique_ptr<app::music::AbstractSongsRepository> soundsRepository);
    };

} // namespace app::bgSounds

M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsProgressPresenter.cpp => products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsProgressPresenter.cpp +3 -3
@@ 27,7 27,7 @@ namespace app::bgSounds
        timer->registerOnFinishedCallback([this]() { onFinished(); });
    }

    void BGSoundsProgressPresenter::activate(const tags::fetcher::Tags &tags)
    void BGSoundsProgressPresenter::activate(const db::multimedia_files::MultimediaFilesRecord &song)
    {
        Expects(timer != nullptr);
        AbstractBGSoundsPlayer::PlaybackMode mode;


@@ 37,7 37,7 @@ namespace app::bgSounds
            mode = AbstractBGSoundsPlayer::PlaybackMode::Looped;
        }
        else {
            timer->reset(std::chrono::seconds{tags.total_duration_s});
            timer->reset(std::chrono::seconds{song.audioProperties.songLength});
            mode = AbstractBGSoundsPlayer::PlaybackMode::SingleShot;
        }
        auto onStartCallback = [this](audio::RetCode retCode) {


@@ 45,7 45,7 @@ namespace app::bgSounds
                timer->start();
            }
        };
        player.start(tags.filePath, mode, std::move(onStartCallback));
        player.start(song.fileInfo.path, mode, std::move(onStartCallback));
    }
    void BGSoundsProgressPresenter::stop()
    {

M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsProgressPresenter.hpp => products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsProgressPresenter.hpp +10 -9
@@ 5,6 5,7 @@

#include <apps-common/BasePresenter.hpp>
#include <apps-common/widgets/TimerWithCallbacks.hpp>
#include <module-db/Interface/MultimediaFilesRecord.hpp>
#include <tags_fetcher/TagsFetcher.hpp>
#include <time/time_locale.hpp>
#include <memory>


@@ 40,14 41,14 @@ namespace app::bgSounds
        class Presenter : public BasePresenter<BGSoundsProgressContract::View>
        {
          public:
            virtual void activate(const tags::fetcher::Tags &tags)                  = 0;
            virtual void stop()                                                     = 0;
            virtual void pause()                                                    = 0;
            virtual void resume()                                                   = 0;
            virtual void setTimer(std::unique_ptr<app::TimerWithCallbacks> &&timer) = 0;
            virtual void handleUpdateTimeEvent()                                    = 0;
            virtual bool isPaused()                                                 = 0;
            virtual void onBeforeShow()                                             = 0;
            virtual void activate(const db::multimedia_files::MultimediaFilesRecord &tags) = 0;
            virtual void stop()                                                            = 0;
            virtual void pause()                                                           = 0;
            virtual void resume()                                                          = 0;
            virtual void setTimer(std::unique_ptr<app::TimerWithCallbacks> &&timer)        = 0;
            virtual void handleUpdateTimeEvent()                                           = 0;
            virtual bool isPaused()                                                        = 0;
            virtual void onBeforeShow()                                                    = 0;
        };
    };



@@ 60,7 61,7 @@ namespace app::bgSounds
        std::unique_ptr<app::TimerWithCallbacks> timer;
        std::unique_ptr<AbstractTimeModel> timeModel;

        void activate(const tags::fetcher::Tags &tags) override;
        void activate(const db::multimedia_files::MultimediaFilesRecord &tags) override;
        void stop() override;
        void pause() override;
        void resume() override;

M products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsMainWindow.cpp => products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsMainWindow.cpp +8 -8
@@ 19,21 19,21 @@ namespace gui
        buildInterface();
        setListTitle(utils::translate("app_bellmain_background_sounds"));
    }
    void BGSoundsMainWindow::setSoundsList(std::vector<tags::fetcher::Tags> soundsTags)
    void BGSoundsMainWindow::setSoundsList(std::vector<db::multimedia_files::MultimediaFilesRecord> sounds)
    {
        std::list<gui::Option> menuOptionList;
        auto addRecord = [&](const tags::fetcher::Tags &soundTags) {
        auto addRecord = [&](const db::multimedia_files::MultimediaFilesRecord &sound) {
            menuOptionList.emplace_back(std::make_unique<gui::option::OptionBellMenu>(
                soundTags.title,
                sound.tags.title,
                [=](gui::Item &item) {
                    onActivated(soundTags);
                    onActivated(sound);
                    return true;
                },
                [=](gui::Item &item) { return true; },
                this));
        };
        for (const auto &tag : soundsTags) {
            addRecord(tag);
        for (const auto &sound : sounds) {
            addRecord(sound);
        }

        addOptions(std::move(menuOptionList));


@@ 44,9 44,9 @@ namespace gui
        presenter->loadAudioRecords();
    }

    void BGSoundsMainWindow::onActivated(const tags::fetcher::Tags &selectedSoundTags)
    void BGSoundsMainWindow::onActivated(const db::multimedia_files::MultimediaFilesRecord &selectedSound)
    {
        auto audioContext = std::make_unique<BGSoundsAudioContext>(selectedSoundTags);
        auto audioContext = std::make_unique<BGSoundsAudioContext>(selectedSound);
        auto switchData   = std::make_unique<BGSoundsSwitchData>(std::move(audioContext));
        application->switchWindow(gui::window::name::bgSoundsTimerSelect, std::move(switchData));
    }

M products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsMainWindow.hpp => products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsMainWindow.hpp +2 -2
@@ 11,10 11,10 @@ namespace gui
    {
        std::unique_ptr<app::bgSounds::BGSoundsMainWindowContract::Presenter> presenter;

        void setSoundsList(std::vector<tags::fetcher::Tags> soundsTags);
        void setSoundsList(std::vector<db::multimedia_files::MultimediaFilesRecord> soundsTags);
        void buildInterface() override;

        void onActivated(const tags::fetcher::Tags &selectedSoundTags);
        void onActivated(const db::multimedia_files::MultimediaFilesRecord &selectedSound);

      public:
        BGSoundsMainWindow(app::ApplicationCommon *app,

M products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsProgressWindow.cpp => products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsProgressWindow.cpp +2 -2
@@ 86,8 86,8 @@ namespace gui
        if (data && typeid(*data) == typeid(BGSoundsSwitchData)) {
            auto *audioSwitchData = static_cast<BGSoundsSwitchData *>(data);
            audioContext          = audioSwitchData->getAudioContext();
            title->setText(audioContext->getTags().title);
            presenter->activate(audioContext->getTags());
            title->setText(audioContext->getSound().tags.title);
            presenter->activate(audioContext->getSound());
        }
    }


M products/BellHybrid/services/db/ServiceDB.cpp => products/BellHybrid/services/db/ServiceDB.cpp +10 -1
@@ 4,7 4,10 @@
#include <db/ServiceDB.hpp>

#include <module-db/Databases/EventsDB.hpp>
#include <module-db/Databases/MultimediaFilesDB.hpp>

#include <module-db/Interface/AlarmEventRecord.hpp>
#include <module-db/Interface/MultimediaFilesRecord.hpp>

#include <service-db/DBServiceMessage.hpp>
#include <service-db/agents/settings/SettingsAgent.hpp>


@@ 25,6 28,8 @@ db::Interface *ServiceDB::getInterface(db::Interface::Name interface)
    switch (interface) {
    case db::Interface::Name::AlarmEvents:
        return alarmEventRecordInterface.get();
    case db::Interface::Name::MultimediaFiles:
        return multimediaFilesRecordInterface.get();
    default:
        LOG_INFO("Not supported interface");
    }


@@ 67,10 72,14 @@ sys::ReturnCodes ServiceDB::InitHandler()
    }

    // Create databases
    eventsDB = std::make_unique<EventsDB>((purefs::dir::getUserDiskPath() / "events.db").c_str());
    eventsDB          = std::make_unique<EventsDB>((purefs::dir::getUserDiskPath() / "events.db").c_str());
    multimediaFilesDB = std::make_unique<db::multimedia_files::MultimediaFilesDB>(
        (purefs::dir::getUserDiskPath() / "multimedia.db").c_str());

    // Create record interfaces
    alarmEventRecordInterface = std::make_unique<AlarmEventRecordInterface>(eventsDB.get());
    multimediaFilesRecordInterface =
        std::make_unique<db::multimedia_files::MultimediaFilesRecordInterface>(multimediaFilesDB.get());

    databaseAgents.emplace(std::make_unique<SettingsAgent>(this, "settings_bell.db"));


M products/BellHybrid/services/db/include/db/ServiceDB.hpp => products/BellHybrid/services/db/include/db/ServiceDB.hpp +8 -0
@@ 10,6 10,11 @@ class AlarmEventRecordInterface;
class EventsDB;

class ThreadRecordInterface;
namespace db::multimedia_files
{
    class MultimediaFilesDB;
    class MultimediaFilesRecordInterface;
} // namespace db::multimedia_files

class ServiceDB : public ServiceDBCommon
{


@@ 20,7 25,10 @@ class ServiceDB : public ServiceDBCommon

  private:
    std::unique_ptr<EventsDB> eventsDB;
    std::unique_ptr<db::multimedia_files::MultimediaFilesDB> multimediaFilesDB;

    std::unique_ptr<AlarmEventRecordInterface> alarmEventRecordInterface;
    std::unique_ptr<db::multimedia_files::MultimediaFilesRecordInterface> multimediaFilesRecordInterface;

    db::Interface *getInterface(db::Interface::Name interface) override;
    sys::MessagePointer DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp) override;

M products/PurePhone/PurePhoneMain.cpp => products/PurePhone/PurePhoneMain.cpp +3 -1
@@ 75,6 75,8 @@ int main()
{
    constexpr auto ApplicationName = "PurePhone";

    const std::vector<std::string> fileIndexerAudioPaths = {{purefs::dir::getUserDiskPath() / "music"}};

#if SYSTEM_VIEW_ENABLED
    SEGGER_SYSVIEW_Conf();
    SEGGER_SYSVIEW_DisableEvents(SYSVIEW_EVTMASK_ISR_ENTER);


@@ 96,7 98,7 @@ int main()

    std::vector<std::unique_ptr<sys::BaseServiceCreator>> systemServices;
    systemServices.emplace_back(sys::CreatorFor<EventManager>([]() { return dumpLogs(); }));
    systemServices.emplace_back(sys::CreatorFor<service::ServiceFileIndexer>());
    systemServices.emplace_back(sys::CreatorFor<service::ServiceFileIndexer>(std::move(fileIndexerAudioPaths)));
    systemServices.emplace_back(sys::CreatorFor<ServiceDB>());
#if ENABLE_GSM == 0
    // For now disable permanently Service cellular when there is no GSM configured

M products/PurePhone/services/CMakeLists.txt => products/PurePhone/services/CMakeLists.txt +0 -1
@@ 2,5 2,4 @@ add_subdirectory(appmgr)
add_subdirectory(evtmgr)
add_subdirectory(db)
add_subdirectory(desktop)
add_subdirectory(service-fileindexer)
add_subdirectory(time)