~aleteoryx/muditaos

150f1cfdef0032f3421cc1b383878ae58ee588f9 — Mateusz Piesta 3 years ago 4bd7b1a
[CP-1560] New user directory structure

* New user directory implemented.
* Generate user directory structure at compile
time instead of at runtime(Pure/Harmony).
* Changed MTP root path to /storage
* FileIndexer: Minor refactor
* FileIndexer: Fixed handling incorrect/non-existing
scan directories.
* Updated Repository module to correctly handle
many assets paths.
* MultimediaDB: Minor unit tests refactor and fixed
some issues when using simulator.
* MultimediaDB: Added new queries and unit tests
* Harmony/Relaxation: Updated to correctly
use audio assets from more than one source.
* Harmony/Relaxation: Updated model and list items
provider.
* Harmony/Relaxation: Fixed stack overflow in audio service
49 files changed, 718 insertions(+), 449 deletions(-)

M CMakeLists.txt
A cmake/modules/AddDirectories.cmake
A doc/directories.md
M module-apps/application-music-player/ApplicationMusicPlayer.cpp
M module-apps/apps-common/models/SongsRepository.cpp
M module-apps/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-db/tests/common.hpp
M module-platform/CMakeLists.txt
D module-platform/CommonPlatform.cpp
M module-platform/include/Platform.hpp
M module-platform/linux/src/LinuxPlatform.cpp
M module-platform/rt1051/src/RT1051Platform.cpp
M module-services/service-desktop/ServiceDesktop.cpp
M module-services/service-fileindexer/StartupIndexer.cpp
M module-services/service-fileindexer/include/service-fileindexer/StartupIndexer.hpp
M module-vfs/paths/filesystem_paths.cpp
M module-vfs/paths/include/purefs/filesystem_paths.hpp
M products/BellHybrid/BellHybridMain.cpp
M products/BellHybrid/CMakeLists.txt
M products/BellHybrid/alarms/CMakeLists.txt
D products/BellHybrid/alarms/include/AlarmSoundPaths.hpp
D products/BellHybrid/alarms/src/AlarmSoundPaths.cpp
M products/BellHybrid/alarms/src/actions/PlayAudioActions.cpp
A products/BellHybrid/apps/application-bell-background-sounds/ApplicationBellBackgroundSounds.cpp
M products/BellHybrid/apps/application-bell-meditation-timer/CMakeLists.txt
M products/BellHybrid/apps/application-bell-meditation-timer/data/MeditationCommon.hpp
M products/BellHybrid/apps/application-bell-powernap/ApplicationBellPowerNap.cpp
M products/BellHybrid/apps/application-bell-powernap/CMakeLists.txt
M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationMainWindowPresenter.cpp
M products/BellHybrid/apps/application-bell-settings/ApplicationBellSettings.cpp
M products/BellHybrid/apps/application-bell-settings/CMakeLists.txt
M products/BellHybrid/apps/common/CMakeLists.txt
A products/BellHybrid/apps/common/include/common/SoundsProvider.hpp
A products/BellHybrid/apps/common/src/SoundsProvider.cpp
M products/BellHybrid/assets/assets_common.json
A products/BellHybrid/paths/CMakeLists.txt
A products/BellHybrid/paths/Paths.cpp
A products/BellHybrid/paths/Paths.hpp
M products/BellHybrid/services/db/tests/MeditationStatisticsTable_tests.cpp
M products/PurePhone/CMakeLists.txt
M products/PurePhone/PurePhoneMain.cpp
M products/PurePhone/assets/assets_common.json
M CMakeLists.txt => CMakeLists.txt +7 -0
@@ 27,6 27,7 @@ include(ModuleUtils)
include(DiskImage)
include(AddPackage)
include(AutoModuleOption)
include(AddDirectories)

message("Selected product: ${PRODUCT}")
message("Selected board:   ${BOARD}")


@@ 167,6 168,12 @@ add_custom_target(
        "Generating version info"
    )

add_directories(
        TARGET user_directories_common
        PREFIX ${CMAKE_BINARY_DIR}/sysroot/sys/user
        DEPENDS assets
        DIRECTORIES logs crash_dumps backup tmp storage audio
)
add_library(version-header INTERFACE)
target_include_directories(version-header INTERFACE $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/source/include>)
add_dependencies(version-header version)

A cmake/modules/AddDirectories.cmake => cmake/modules/AddDirectories.cmake +31 -0
@@ 0,0 1,31 @@
#[[
Example of use:
add_directories(
        TARGET <target name>
        PREFIX <paths prefix>
        DEPENDS <dependencies list>
        DIRECTORIES <dir1 dir2 dir3 dir_n>
)
]]#
function(add_directories)
    cmake_parse_arguments(
            _ASSETS
            ""
            "TARGET;PREFIX"
            "DEPENDS;DIRECTORIES"
            ${ARGN}
    )
    set(command)
    foreach (entry ${_ASSETS_DIRECTORIES})
        list(APPEND command
                COMMAND mkdir -p ${_ASSETS_PREFIX}/${entry})
    endforeach ()

    add_custom_target(
            ${_ASSETS_TARGET}
            DEPENDS ${_ASSETS_DEPENDS}
            ${command}
            COMMENT
            "Adding directories: ${_ASSETS_DIRECTORIES}"
    )
endfunction()
\ No newline at end of file

A doc/directories.md => doc/directories.md +77 -0
@@ 0,0 1,77 @@
# PureOS directories structure

## Audio

Audio assets are split into two directories. Later, they are split further into subdirectories.
Each subdirectory is exclusive to an application that uses it hence it is relatively easy to have specific assets tied
to a specific application.

### Internal partition
Internal partition stores proprietary assets that can't be accessed/modified by a user. Its structure and contents are created
at compile time. For technical info, please check the [CMake section](#CMake).

Structure template:
```
assets/audio/
        ├── app_1
        ├── app_2
        ├── app_3
        └── app_n
```

For instance, Harmony directory:
```
assets/audio/
        └── relaxation
```

Each product can have its structure freely defined. There are no restrictions applied. For instance, for the time being, there is no
proprietary assets path in Pure as it is not required. It is because the Pure MusicPlayer application only loads assets from the user partition.

### User partition
User partition stores assets that may be accessed by using the Mudita Center. In contrast to the [Internal partition](#Internal partition),
its permissions are set to write/read. It follows the same structure as the internal partition, i.e. each application has
its subdirectory used exclusively. Its structure is also created at compile time, and its contents can be propagated with assets or not,
it depends on the product. For technical info, please check the [CMake section](#CMake).

Structure template:
```
user/
└── audio
     ├── app_1
     ├── app_2
     ├── app_3
     └── app_n
```
For instance, Harmony directory:
```
user/
└── audio
     └── relaxation
```
and PurePhone:
```
user/
└── audio
     └── music_player
```

## Storage
It is the directory accessible via MTP. Its purpose is to provide space for storing user files.

```
user/
└── storage
```

## CMake

Script used to create directories:
[AddDirectories](../cmake/modules/AddDirectories.cmake)

There are two targets created by using the script above,
`user_directories_common` and `user_directories`. The first one creates common directories which are required for PureOS.
The latter is used to create required product-related directories. They are both added as a dependency to both `BellHybrid-disk-img` and
`PurePhone-disk-img` targets hence they will be invoked automatically upon generating the product image.



M module-apps/application-music-player/ApplicationMusicPlayer.cpp => module-apps/application-music-player/ApplicationMusicPlayer.cpp +3 -5
@@ 46,11 46,9 @@ namespace app

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

        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);
        const auto paths     = std::vector<std::string>{purefs::dir::getUserAudioPath()};
        auto tagsFetcher     = std::make_unique<app::music::ServiceAudioTagsFetcher>(this);
        auto songsRepository = std::make_unique<app::music::SongsRepository>(this, std::move(tagsFetcher), paths);

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

M module-apps/apps-common/models/SongsRepository.cpp => module-apps/apps-common/models/SongsRepository.cpp +7 -6
@@ 32,15 32,16 @@ namespace app::music

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

    void SongsRepository::initCache()
    {
        auto query = std::make_unique<db::multimedia_files::query::GetLimited>(0, constants::cacheMaxSize);
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::MultimediaFiles);
        auto query =
            std::make_unique<db::multimedia_files::query::GetLimitedByPaths>(pathPrefixes, 0, constants::cacheMaxSize);
        auto task = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::MultimediaFiles);
        musicFilesModelCache.recordsOffset = 0;

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


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

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


@@ 187,7 188,7 @@ namespace app::music
                                               std::uint32_t limit,
                                               const OnUpdateMusicFilesListCallback &callback)
    {
        auto query = std::make_unique<db::multimedia_files::query::GetLimited>(offset, limit);
        auto query = std::make_unique<db::multimedia_files::query::GetLimitedByPaths>(pathPrefixes, offset, limit);
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::MultimediaFiles);

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

M module-apps/apps-common/models/SongsRepository.hpp => module-apps/apps-common/models/SongsRepository.hpp +4 -4
@@ 64,9 64,9 @@ namespace app::music
    class SongsRepository : public AbstractSongsRepository, public app::AsyncCallbackReceiver
    {
      public:
        explicit SongsRepository(ApplicationCommon *application,
                                 std::unique_ptr<AbstractTagsFetcher> tagsFetcher,
                                 std::string pathPrefix);
        SongsRepository(ApplicationCommon *application,
                        std::unique_ptr<AbstractTagsFetcher> tagsFetcher,
                        const std::vector<std::string> &pathPrefixes);

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


@@ 88,7 88,7 @@ namespace app::music

        std::unique_ptr<AbstractTagsFetcher> tagsFetcher;

        std::string pathPrefix;
        std::vector<std::string> pathPrefixes;

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

M module-db/Interface/MultimediaFilesRecord.cpp => module-db/Interface/MultimediaFilesRecord.cpp +5 -5
@@ 36,8 36,8 @@ 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::GetLimitedByPaths)) {
            return runQueryImplGetLimited(std::static_pointer_cast<query::GetLimitedByPaths>(query));
        }
        if (typeid(*query) == typeid(query::Remove)) {
            return runQueryImplRemove(std::static_pointer_cast<query::Remove>(query));


@@ 252,10 252,10 @@ namespace db::multimedia_files
    }

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

        return response;

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


@@ 72,7 72,7 @@ namespace db::multimedia_files
        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);
            const std::shared_ptr<db::multimedia_files::query::GetLimitedByPaths> &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 +67 -30
@@ 6,6 6,12 @@
#include <Database/QueryResult.hpp>
#include <Utils.hpp>
#include <magic_enum.hpp>
#include <inttypes.h>

#define u32_  "%" PRIu32
#define u32_c "%" PRIu32 ","
#define str_  "%Q"
#define str_c "%Q,"

namespace db::multimedia_files
{


@@ 39,6 45,17 @@ namespace db::multimedia_files
        return (!fileInfo.path.empty() && Record::isValid());
    }

    auto constructMatchPattern = [](const std::vector<std::string> &paths) -> std::string {
        std::string ret;
        for (auto e = paths.begin(); e != paths.end(); e++) {
            ret += "path LIKE '" + *e + "%%'";
            if (std::next(e) != paths.end()) {
                ret += " or ";
            }
        }
        return ret;
    };

    MultimediaFilesTable::MultimediaFilesTable(Database *db) : Table(db)
    {
        createTableRow = CreateTableRow;


@@ 53,7 70,8 @@ namespace db::multimedia_files
    {
        return db->execute("INSERT INTO files (path, media_type, size, title, artist, album, "
                           "comment, genre, year, track, song_length, bitrate, sample_rate, channels) "
                           "VALUES('%q', '%q', %lu, '%q', '%q', '%q', '%q', '%q', %lu, %lu, %lu, %lu, %lu, %lu) "
                           "VALUES(" str_c str_c u32_c str_c str_c str_c str_c str_c u32_c u32_c u32_c u32_c u32_c u32_
                           ") "
                           "ON CONFLICT(path) DO UPDATE SET "
                           "path = excluded.path, "
                           "media_type = excluded.media_type, "


@@ 98,7 116,7 @@ namespace db::multimedia_files
            return false;
        }

        return db->execute("DELETE FROM files WHERE %q = '%q';", fieldName.c_str(), str);
        return db->execute("DELETE FROM files WHERE %q = %Q;", fieldName.c_str(), str);
    }

    bool MultimediaFilesTable::removeAll()


@@ 108,9 126,10 @@ namespace db::multimedia_files

    bool MultimediaFilesTable::update(TableRow entry)
    {
        return db->execute("UPDATE files SET path = '%q', media_type = '%q', size = %lu, title = '%q', artist = '%q',"
                           "album = '%q', comment = '%q', genre = '%q', year = %lu, track = %lu, song_length = %lu,"
                           "bitrate = %lu, sample_rate = %lu, channels = %lu WHERE _id = %lu;",
        return db->execute("UPDATE files SET path=" str_c "media_type=" str_c "size=" u32_c "title=" str_c
                           "artist=" str_c "album=" str_c "comment=" str_c "genre=" str_c "year=" u32_c "track=" u32_c
                           "song_length=" u32_c "bitrate=" u32_c "sample_rate=" u32_c "channels=" u32_
                           " WHERE _id=" u32_ ";",
                           entry.fileInfo.path.c_str(),
                           entry.fileInfo.mediaType.c_str(),
                           entry.fileInfo.size,


@@ 133,10 152,11 @@ namespace db::multimedia_files
        auto path = oldPath.empty() ? entry.fileInfo.path : oldPath;

        return db->execute("BEGIN TRANSACTION; "
                           "INSERT OR IGNORE INTO files (path) VALUES ('%q'); "
                           "UPDATE files SET path = '%q', media_type = '%q', size = %lu, title = '%q', artist = '%q', "
                           "album = '%q', comment = '%q', genre = '%q', year = %lu, track = %lu, song_length = %lu, "
                           "bitrate = %lu, sample_rate = %lu, channels = %lu WHERE path = '%q'; "
                           "INSERT OR IGNORE INTO files (path) VALUES (" str_ "); "
                           "UPDATE files SET path=" str_c "media_type=" str_c "size=" u32_c "title=" str_c
                           "artist=" str_c "album=" str_c "comment=" str_c "genre=" str_c "year=" u32_c "track=" u32_c
                           "song_length=" u32_c "bitrate=" u32_c "sample_rate=" u32_c "channels=" u32_
                           " WHERE path=" str_ "; "
                           "COMMIT;",
                           path.c_str(),
                           entry.fileInfo.path.c_str(),


@@ 158,7 178,7 @@ namespace db::multimedia_files

    TableRow MultimediaFilesTable::getById(uint32_t id)
    {
        auto retQuery = db->query("SELECT * FROM files WHERE _id = %lu;", id);
        auto retQuery = db->query("SELECT * FROM files WHERE _id=" u32_ ";", id);

        if ((retQuery == nullptr) || (retQuery->getRowCount() == 0)) {
            return TableRow();


@@ 169,7 189,7 @@ namespace db::multimedia_files

    TableRow MultimediaFilesTable::getByPath(std::string path)
    {
        auto retQuery = db->query("SELECT * FROM files WHERE path = '%q';", path.c_str());
        auto retQuery = db->query("SELECT * FROM files WHERE path=" str_ ";", path.c_str());

        if ((retQuery == nullptr) || (retQuery->getRowCount() == 0)) {
            return TableRow();


@@ 180,15 200,16 @@ namespace db::multimedia_files

    std::vector<TableRow> MultimediaFilesTable::getLimitOffset(uint32_t offset, uint32_t limit)
    {
        auto retQuery = db->query("SELECT * from files ORDER BY title ASC LIMIT %lu OFFSET %lu;", limit, offset);
        auto retQuery =
            db->query("SELECT * from files ORDER BY title ASC LIMIT " u32_ " OFFSET " u32_ ";", limit, offset);

        return retQueryUnpack(std::move(retQuery));
    }

    auto MultimediaFilesTable::getArtistsLimitOffset(uint32_t offset, uint32_t limit) -> std::vector<Artist>
    {
        auto retQuery =
            db->query("SELECT DISTINCT artist from files ORDER BY artist ASC LIMIT %lu OFFSET %lu;", limit, offset);
        auto retQuery = db->query(
            "SELECT DISTINCT artist from files ORDER BY artist ASC LIMIT " u32_ " OFFSET " u32_ ";", limit, offset);

        if ((retQuery == nullptr) || (retQuery->getRowCount() == 0)) {
            return {};


@@ 214,7 235,7 @@ namespace db::multimedia_files
            return {};
        }

        retQuery = db->query("SELECT * FROM files WHERE %q = '%q' ORDER BY title ASC LIMIT %lu OFFSET %lu;",
        retQuery = db->query("SELECT * FROM files WHERE %q=" str_ " ORDER BY title ASC LIMIT " u32_ " OFFSET " u32_ ";",
                             fieldName.c_str(),
                             str,
                             limit,


@@ 245,8 266,10 @@ namespace db::multimedia_files

    auto MultimediaFilesTable::getAlbumsLimitOffset(uint32_t offset, uint32_t limit) -> std::vector<Album>
    {
        auto retQuery = db->query(
            "SELECT DISTINCT artist,album from files ORDER BY album ASC LIMIT %lu OFFSET %lu;", limit, offset);
        auto retQuery =
            db->query("SELECT DISTINCT artist,album from files ORDER BY album ASC LIMIT " u32_ " OFFSET " u32_ ";",
                      limit,
                      offset);

        if ((retQuery == nullptr) || (retQuery->getRowCount() < 2)) {
            return {};


@@ 277,7 300,7 @@ namespace db::multimedia_files
            return 0;
        }

        auto queryRet = db->query("SELECT COUNT(*) FROM files WHERE %q=%lu;", field, id);
        auto queryRet = db->query("SELECT COUNT(*) FROM files WHERE " str_ "=" u32_ ";", field, id);
        if ((queryRet == nullptr) || (queryRet->getRowCount() == 0)) {
            return 0;
        }


@@ 298,7 321,7 @@ namespace db::multimedia_files

    auto MultimediaFilesTable::count(const Artist &artist) -> uint32_t
    {
        auto queryRet = db->query("SELECT COUNT(*) FROM files WHERE artist='%q';", artist.c_str());
        auto queryRet = db->query("SELECT COUNT(*) FROM files WHERE artist=" str_ " ;", artist.c_str());
        if ((queryRet == nullptr) || (queryRet->getRowCount() == 0)) {
            return 0;
        }


@@ 309,19 332,19 @@ namespace db::multimedia_files
    auto MultimediaFilesTable::getLimitOffset(const Album &album, uint32_t offset, uint32_t limit)
        -> std::vector<TableRow>
    {
        std::unique_ptr<QueryResult> retQuery = db->query(
            "SELECT * FROM files WHERE artist = '%q' AND album = '%q' ORDER BY title ASC LIMIT %lu OFFSET %lu;",
            album.artist.c_str(),
            album.title.c_str(),
            limit,
            offset);
        std::unique_ptr<QueryResult> retQuery = db->query("SELECT * FROM files WHERE artist=" str_ " AND album=" str_
                                                          " ORDER BY title ASC LIMIT " u32_ " OFFSET " u32_ ";",
                                                          album.artist.c_str(),
                                                          album.title.c_str(),
                                                          limit,
                                                          offset);

        return retQueryUnpack(std::move(retQuery));
    }

    auto MultimediaFilesTable::count(const Album &album) -> uint32_t
    {
        auto queryRet = db->query("SELECT COUNT(*) FROM files WHERE artist='%q' AND album = '%q';",
        auto queryRet = db->query("SELECT COUNT(*) FROM files WHERE artist=" str_ " AND album=" str_ ";",
                                  album.artist.c_str(),
                                  album.title.c_str());
        if ((queryRet == nullptr) || (queryRet->getRowCount() == 0)) {


@@ 331,12 354,26 @@ namespace db::multimedia_files
        return (*queryRet)[0].getUInt32();
    }

    auto MultimediaFilesTable::getLimitOffsetByPath(const std::string &path, uint32_t offset, uint32_t limit)
        -> std::vector<TableRow>
    auto MultimediaFilesTable::getLimitOffsetByPaths(const std::vector<std::string> &paths,
                                                     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) + ";";
        const std::string query = "SELECT * FROM files WHERE " + constructMatchPattern(paths) +
                                  " 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));
    }

    auto MultimediaFilesTable::count(const std::vector<std::string> &paths) -> uint32_t
    {
        const std::string query = "SELECT COUNT(*) FROM files WHERE " + constructMatchPattern(paths) + ";";
        const auto retQuery     = db->query(query.c_str());

        if ((retQuery == nullptr) || (retQuery->getRowCount() == 0)) {
            return 0;
        }

        return (*retQuery)[0].getUInt32();
    }
} // namespace db::multimedia_files

M module-db/Tables/MultimediaFilesTable.hpp => module-db/Tables/MultimediaFilesTable.hpp +9 -7
@@ 24,16 24,16 @@ namespace db::multimedia_files
        Album album{};
        std::string comment{};
        std::string genre{};
        unsigned year{};
        unsigned track{};
        std::uint32_t year{};
        std::uint32_t track{};
    };

    struct AudioProperties
    {
        unsigned songLength{}; /// in seconds
        unsigned bitrate{};    /// in kb/s
        unsigned sampleRate{}; /// in Hz
        unsigned channels{};   /// 1 - mono, 2 - stereo
        std::uint32_t songLength{}; /// in seconds
        std::uint32_t bitrate{};    /// in kb/s
        std::uint32_t sampleRate{}; /// in Hz
        std::uint32_t channels{};   /// 1 - mono, 2 - stereo
    };

    struct FileInfo


@@ 101,7 101,9 @@ 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>;
        auto getLimitOffsetByPaths(const std::vector<std::string> &paths, uint32_t offset, uint32_t limit)
            -> std::vector<TableRow>;
        auto count(const std::vector<std::string> &paths) -> uint32_t;
        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 +4 -4
@@ 106,12 106,12 @@ 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)
    GetLimitedByPaths::GetLimitedByPaths(const std::vector<std::string> &paths, uint32_t offset, uint32_t limit)
        : Query(Query::Type::Read), paths{paths}, offset(offset), limit(limit)
    {}

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

M module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.hpp => module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.hpp +3 -3
@@ 101,13 101,13 @@ namespace db::multimedia_files::query
        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

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

        const std::string path;
        const std::vector<std::string> paths;
        const uint32_t offset = 0;
        const uint32_t limit  = 0;
    };

M module-db/tests/MultimediaFilesTable_tests.cpp => module-db/tests/MultimediaFilesTable_tests.cpp +98 -93
@@ 3,6 3,7 @@

#include <catch2/catch.hpp>

#include "common.hpp"
#include <Databases/MultimediaFilesDB.hpp>
#include <Interface/MultimediaFilesRecord.hpp>
#include <queries/multimedia_files/QueryMultimediaFilesAdd.hpp>


@@ 36,31 37,31 @@ const std::vector<Album> albums = {{.artist = artists[0], .title = {}},

const std::vector<TableRow> records = {
    {Record{DB_ID_NONE},
     .fileInfo        = {.path = "user/music/file1.mp3", .mediaType = "audio/mp3", .size = 100},
     .fileInfo        = {.path = "user/audio/file1.mp3", .mediaType = "audio/mp3", .size = 100},
     .tags            = {.title = song1, .album = albums[3], .comment = "", .genre = "", .year = 2011, .track = 1},
     .audioProperties = {.songLength = 300, .bitrate = 320, .sampleRate = 44100, .channels = 1}},
    {Record{DB_ID_NONE},
     .fileInfo        = {.path = "user/music/file2.mp3", .mediaType = "audio/mp3", .size = 100},
     .fileInfo        = {.path = "user/audio/file2.mp3", .mediaType = "audio/mp3", .size = 100},
     .tags            = {.title = song2, .album = albums[3], .comment = "", .genre = "", .year = 2011, .track = 1},
     .audioProperties = {.songLength = 300, .bitrate = 320, .sampleRate = 44100, .channels = 1}},
    {Record{DB_ID_NONE},
     .fileInfo        = {.path = "user/music/file3.mp3", .mediaType = "audio/mp3", .size = 100},
     .fileInfo        = {.path = "user/audio/file3.mp3", .mediaType = "audio/mp3", .size = 100},
     .tags            = {.title = song2, .album = albums[4], .comment = "", .genre = "", .year = 2011, .track = 1},
     .audioProperties = {.songLength = 300, .bitrate = 320, .sampleRate = 44100, .channels = 1}},
    {Record{DB_ID_NONE},
     .fileInfo        = {.path = "user/music/file4.mp3", .mediaType = "audio/mp3", .size = 100},
     .fileInfo        = {.path = "user/audio/file4.mp3", .mediaType = "audio/mp3", .size = 100},
     .tags            = {.title = song2, .album = albums[5], .comment = "", .genre = "", .year = 2011, .track = 1},
     .audioProperties = {.songLength = 300, .bitrate = 320, .sampleRate = 44100, .channels = 1}},
    {Record{DB_ID_NONE},
     .fileInfo        = {.path = "user/music/file5.mp3", .mediaType = "audio/mp3", .size = 100},
     .fileInfo        = {.path = "user/audio/file5.mp3", .mediaType = "audio/mp3", .size = 100},
     .tags            = {.title = song3, .album = albums[0], .comment = "", .genre = "", .year = 2011, .track = 1},
     .audioProperties = {.songLength = 300, .bitrate = 320, .sampleRate = 44100, .channels = 1}},
    {Record{DB_ID_NONE},
     .fileInfo        = {.path = "user/music/file6.mp3", .mediaType = "audio/mp3", .size = 100},
     .fileInfo        = {.path = "user/audio/file6.mp3", .mediaType = "audio/mp3", .size = 100},
     .tags            = {.title = song3, .album = albums[7], .comment = "", .genre = "", .year = 2011, .track = 1},
     .audioProperties = {.songLength = 300, .bitrate = 320, .sampleRate = 44100, .channels = 1}},
    {Record{DB_ID_NONE},
     .fileInfo        = {.path = "user/music/file7.mp3", .mediaType = "audio/mp3", .size = 100},
     .fileInfo        = {.path = "user/audio/file7.mp3", .mediaType = "audio/mp3", .size = 100},
     .tags            = {.title = song3, .album = albums[5], .comment = "", .genre = "", .year = 2011, .track = 1},
     .audioProperties = {.songLength = 300, .bitrate = 320, .sampleRate = 44100, .channels = 1}},
    {Record{DB_ID_NONE},


@@ 78,17 79,8 @@ const std::vector<TableRow> records = {

TEST_CASE("Multimedia DB tests")
{
    REQUIRE(Database::initialize());

    const auto path = (std::filesystem::path{"sys/user"} / "multimedia.db");
    if (std::filesystem::exists(path)) {
        REQUIRE(std::filesystem::remove(path));
    }

    MultimediaFilesDB db(path.c_str());
    MultimediaFilesRecordInterface multimediaFilesRecordInterface(&db);

    REQUIRE(db.isInitialized());
    DatabaseUnderTest<MultimediaFilesDB> db{"multimedia.db"};
    MultimediaFilesRecordInterface multimediaFilesRecordInterface(&db.get());

    constexpr auto PageSize = 8;



@@ 100,95 92,95 @@ TEST_CASE("Multimedia DB tests")
            REQUIRE(!record.isValid());

            record.ID            = 1;
            record.fileInfo.path = "music";
            record.fileInfo.path = "audio";

            REQUIRE(record.isValid());
        }

        SECTION("Empty")
        {
            REQUIRE(db.files.count() == 0);
            REQUIRE(db.files.getLimitOffset(0, PageSize).empty());
            REQUIRE(db.get().files.count() == 0);
            REQUIRE(db.get().files.getLimitOffset(0, PageSize).empty());
        }

        SECTION("Add, get and remove")
        {
            const auto path = "music/user";
            const auto path = "audio/user";
            TableRow record;
            record.fileInfo.path = path;
            REQUIRE(!record.isValid());
            REQUIRE(db.files.add(record));
            REQUIRE(db.get().files.add(record));

            REQUIRE(db.files.count() == 1);
            auto result = db.files.getById(1);
            REQUIRE(db.get().files.count() == 1);
            auto result = db.get().files.getById(1);
            REQUIRE(result.isValid());

            SECTION("Remove by ID")
            {
                REQUIRE(db.files.removeById(1));
                REQUIRE(db.files.count() == 0);
                auto result = db.files.getById(1);
                REQUIRE(db.get().files.removeById(1));
                REQUIRE(db.get().files.count() == 0);
                auto result = db.get().files.getById(1);
                REQUIRE(!result.isValid());
            }
            SECTION("Remove by field")
            {
                REQUIRE(db.files.removeByField(TableFields::path, path));
                REQUIRE(db.files.count() == 0);
                auto result = db.files.getById(1);
                REQUIRE(db.get().files.removeByField(TableFields::path, path));
                REQUIRE(db.get().files.count() == 0);
                auto result = db.get().files.getById(1);
                REQUIRE(!result.isValid());
            }
        }

        for (const auto &record : records) {
            REQUIRE(db.files.add(record));
            REQUIRE(db.get().files.add(record));
        }

        SECTION("Remove all")
        {
            REQUIRE(db.files.count() == records.size());
            REQUIRE(db.files.removeAll());
            REQUIRE(db.files.count() == 0);
            REQUIRE(db.get().files.count() == records.size());
            REQUIRE(db.get().files.removeAll());
            REQUIRE(db.get().files.count() == 0);
        }

        SECTION("Add for existing path")
        {
            auto resultPre               = db.files.getById(2);
            auto resultPre               = db.get().files.getById(2);
            resultPre.fileInfo.mediaType = "bla bla";
            REQUIRE(db.files.add(resultPre));
            auto resultPost = db.files.getById(2);
            REQUIRE(db.get().files.add(resultPre));
            auto resultPost = db.get().files.getById(2);
            REQUIRE((resultPre.ID == resultPost.ID && resultPre.fileInfo.mediaType == resultPost.fileInfo.mediaType));
        }

        SECTION("Update")
        {
            auto resultPre               = db.files.getById(2);
            auto resultPre               = db.get().files.getById(2);
            resultPre.fileInfo.mediaType = "bla bla";
            REQUIRE(db.files.update(resultPre));
            auto resultPost = db.files.getById(2);
            REQUIRE(db.get().files.update(resultPre));
            auto resultPost = db.get().files.getById(2);
            REQUIRE((resultPre.ID == resultPost.ID && resultPre.fileInfo.mediaType == resultPost.fileInfo.mediaType));
        }

        SECTION("Add or Update")
        {
            REQUIRE(db.files.removeAll());
            REQUIRE(db.files.count() == 0);
            REQUIRE(db.get().files.removeAll());
            REQUIRE(db.get().files.count() == 0);

            for (const auto &record : records) {
                REQUIRE(db.files.addOrUpdate(record));
                REQUIRE(db.get().files.addOrUpdate(record));
            }

            REQUIRE(db.files.count() == records.size());
            REQUIRE(db.get().files.count() == records.size());

            auto oldPath           = records[1].fileInfo.path;
            auto resultPre         = db.files.getByPath(oldPath);
            auto resultPre         = db.get().files.getByPath(oldPath);
            const auto referenceID = resultPre.ID;
            resultPre.ID           = 10;

            SECTION("No changes")
            {
                REQUIRE(db.files.addOrUpdate(resultPre));
                REQUIRE(db.files.count() == records.size());
                auto resultPost = db.files.getByPath(oldPath);
                REQUIRE(db.get().files.addOrUpdate(resultPre));
                REQUIRE(db.get().files.count() == records.size());
                auto resultPost = db.get().files.getByPath(oldPath);
                REQUIRE(resultPost.isValid());
                REQUIRE((resultPost.ID == referenceID && resultPre.fileInfo.path == resultPost.fileInfo.path));
            }


@@ 196,19 188,19 @@ TEST_CASE("Multimedia DB tests")
            {
                auto newPath            = "user/newPath";
                resultPre.fileInfo.path = newPath;
                REQUIRE(db.files.addOrUpdate(resultPre, oldPath));
                REQUIRE(db.files.count() == records.size());
                auto notExistingEntry = db.files.getByPath(oldPath);
                REQUIRE(db.get().files.addOrUpdate(resultPre, oldPath));
                REQUIRE(db.get().files.count() == records.size());
                auto notExistingEntry = db.get().files.getByPath(oldPath);
                REQUIRE(!notExistingEntry.isValid());
                auto resultPost = db.files.getByPath(newPath);
                auto resultPost = db.get().files.getByPath(newPath);
                REQUIRE(resultPost.isValid());
                REQUIRE((resultPost.ID == referenceID && resultPre.fileInfo.path == resultPost.fileInfo.path));
            }
            SECTION("change any field")
            {
                resultPre.fileInfo.mediaType = "bla bla";
                REQUIRE(db.files.addOrUpdate(resultPre));
                auto resultPost = db.files.getByPath(oldPath);
                REQUIRE(db.get().files.addOrUpdate(resultPre));
                auto resultPost = db.get().files.getByPath(oldPath);
                REQUIRE(resultPost.isValid());
                REQUIRE((resultPost.ID == referenceID && resultPre.fileInfo.path == resultPost.fileInfo.path &&
                         resultPre.fileInfo.mediaType == resultPost.fileInfo.mediaType));


@@ 218,20 210,20 @@ TEST_CASE("Multimedia DB tests")
        SECTION("getLimitOffset")
        {
            auto size = records.size();
            REQUIRE(db.files.getLimitOffset(0, size - 3).size() == size - 3);
            REQUIRE(db.files.getLimitOffset(size - 3, size).size() == 3);
            REQUIRE(db.get().files.getLimitOffset(0, size - 3).size() == size - 3);
            REQUIRE(db.get().files.getLimitOffset(size - 3, size).size() == 3);
        }

        SECTION("getLimitOffsetByField")
        {
            auto size = records.size();
            REQUIRE(db.files.getLimitOffsetByField(0, size, TableFields::artist, artists[1].c_str()).size() == 4);
            REQUIRE(db.get().files.getLimitOffsetByField(0, size, TableFields::artist, artists[1].c_str()).size() == 4);
        }

        SECTION("Artists")
        {
            REQUIRE(db.files.countArtists() == artists.size());
            auto artistsList = db.files.getArtistsLimitOffset(0, artists.size());
            REQUIRE(db.get().files.countArtists() == artists.size());
            auto artistsList = db.get().files.getArtistsLimitOffset(0, artists.size());
            REQUIRE(artistsList.size() == artists.size());
            for (size_t i = 0; i < artists.size(); i++) {
                REQUIRE(artistsList[i] == artists[i]);


@@ 242,16 234,16 @@ TEST_CASE("Multimedia DB tests")
                const auto size         = artists.size();
                constexpr auto reminder = 2;
                const auto pageSize     = size - reminder;
                REQUIRE(db.files.getLimitOffset(0, pageSize).size() == pageSize);
                REQUIRE(db.files.getLimitOffset(pageSize, pageSize).size() == reminder);
                REQUIRE(db.get().files.getLimitOffset(0, pageSize).size() == pageSize);
                REQUIRE(db.get().files.getLimitOffset(pageSize, pageSize).size() == reminder);
            }
        }

        const auto numberOfAlbums = albums.size();
        SECTION("Albums")
        {
            REQUIRE(db.files.countAlbums() == numberOfAlbums);
            auto albumsList = db.files.getAlbumsLimitOffset(0, numberOfAlbums);
            REQUIRE(db.get().files.countAlbums() == numberOfAlbums);
            auto albumsList = db.get().files.getAlbumsLimitOffset(0, numberOfAlbums);
            REQUIRE(albumsList.size() == numberOfAlbums);
            for (const auto &album : albumsList) {
                auto it = std::find_if(albums.begin(), albums.end(), [album](const Album &albumRef) {


@@ 265,8 257,8 @@ TEST_CASE("Multimedia DB tests")
                const auto size         = numberOfAlbums;
                constexpr auto reminder = 2;
                const auto pageSize     = size - reminder;
                REQUIRE(db.files.getAlbumsLimitOffset(0, pageSize).size() == pageSize);
                REQUIRE(db.files.getAlbumsLimitOffset(pageSize, pageSize).size() == reminder);
                REQUIRE(db.get().files.getAlbumsLimitOffset(0, pageSize).size() == pageSize);
                REQUIRE(db.get().files.getAlbumsLimitOffset(pageSize, pageSize).size() == reminder);
            }
        }



@@ 276,8 268,8 @@ TEST_CASE("Multimedia DB tests")
                size_t size = std::count_if(records.begin(), records.end(), [artist](const TableRow &record) {
                    return record.tags.album.artist == artist;
                });
                REQUIRE(db.files.count(artist) == size);
                auto artistsList = db.files.getLimitOffset(artist, 0, records.size());
                REQUIRE(db.get().files.count(artist) == size);
                auto artistsList = db.get().files.getLimitOffset(artist, 0, records.size());
                REQUIRE(artistsList.size() == size);
                for (size_t i = 0; i < artistsList.size(); i++) {
                    REQUIRE(artistsList[i].tags.album.artist == artist);


@@ 291,8 283,8 @@ TEST_CASE("Multimedia DB tests")
                size_t size = std::count_if(records.begin(), records.end(), [album](const TableRow &record) {
                    return record.tags.album.artist == album.artist && record.tags.album.title == album.title;
                });
                REQUIRE(db.files.count(album) == size);
                auto artistsList = db.files.getLimitOffset(album, 0, records.size());
                REQUIRE(db.get().files.count(album) == size);
                auto artistsList = db.get().files.getLimitOffset(album, 0, records.size());
                REQUIRE(artistsList.size() == size);
                for (size_t i = 0; i < artistsList.size(); i++) {
                    REQUIRE((artistsList[i].tags.album.artist == album.artist &&


@@ 345,14 337,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 getLimitedByPathsQuery =
            [&](const std::vector<std::string> &paths, const uint32_t offset, const uint32_t limit) {
                auto query  = std::make_shared<db::multimedia_files::query::GetLimitedByPaths>(paths, 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);


@@ 464,7 457,7 @@ TEST_CASE("Multimedia DB tests")

        SECTION("add, get, remove query")
        {
            const auto path = "music/user";
            const auto path = "audio/user";
            MultimediaFilesRecord record;
            record.fileInfo.path = path;
            REQUIRE(!record.isValid());


@@ 573,19 566,31 @@ TEST_CASE("Multimedia DB tests")
            REQUIRE(getLimitedQuery(size - 3, size).size() == 3);
        }

        SECTION("getLimitOffsetByPath")
        SECTION("getLimitOffsetByPaths")
        {
            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);
            MultimediaFilesRecord entry;
            const auto user_count       = records.size();
            const auto user_audio_count = user_count - 3;

            REQUIRE(getLimitedByPathsQuery({"user/"}, 0, UINT32_MAX).size() == user_count);
            REQUIRE(getLimitedByPathsQuery({"user/audio/"}, 0, UINT32_MAX).size() == user_audio_count);
            REQUIRE(getLimitedByPathsQuery({"user/audio1/"}, 0, UINT32_MAX).size() == 0);
            REQUIRE(getLimitedByPathsQuery({"user/audio2"}, 0, UINT32_MAX).size() == 0);
            REQUIRE(getLimitedByPathsQuery({"abc/"}, 0, UINT32_MAX).size() == 0);

            entry.fileInfo.path = "assets/audio/file30.mp3";
            addQuery(entry);
            entry.fileInfo.path = "assets/audio/file31.mp3";
            addQuery(entry);
            REQUIRE(getLimitedByPathsQuery({"user/audio", "assets/audio"}, 0, UINT32_MAX).size() ==
                    user_audio_count + 2);
            REQUIRE(getLimitedByPathsQuery({"user/audio", "assets/audio", "non/existing/path"}, 0, UINT32_MAX).size() ==
                    user_audio_count + 2);

            entry.fileInfo.path = "assets/audio/app/file32.mp3";
            addQuery(entry);
            REQUIRE(getLimitedByPathsQuery({"user/audio", "assets/audio/app"}, 0, UINT32_MAX).size() ==
                    user_audio_count + 1);
        }

        SECTION("Artists")


@@ 625,8 630,8 @@ TEST_CASE("Multimedia DB tests")
                const auto size         = numberOfAlbums;
                constexpr auto reminder = 2;
                const auto pageSize     = size - reminder;
                REQUIRE(db.files.getAlbumsLimitOffset(0, pageSize).size() == pageSize);
                REQUIRE(db.files.getAlbumsLimitOffset(pageSize, pageSize).size() == reminder);
                REQUIRE(db.get().files.getAlbumsLimitOffset(0, pageSize).size() == pageSize);
                REQUIRE(db.get().files.getAlbumsLimitOffset(pageSize, pageSize).size() == reminder);
            }
        }


M module-db/tests/common.hpp => module-db/tests/common.hpp +37 -0
@@ 3,6 3,43 @@

#pragma once

#include "Database/Database.hpp"
#include <catch2/catch.hpp>
#include <string>
#include <filesystem>

void RemoveDbFiles(const std::string &dbName);

template <typename Db>
class DatabaseUnderTest
{
  public:
    explicit DatabaseUnderTest(std::filesystem::path name)
    {
        Database::initialize();

        if (std::filesystem::exists(name)) {
            std::filesystem::remove(name);
        }

        db = std::make_unique<Db>(name.c_str());

        if (not db->isInitialized()) {
            throw std::runtime_error("Could not initialize database");
        }
    }

    ~DatabaseUnderTest()
    {
        Database::deinitialize();
    }

    Db &get()
    {
        return *db;
    }

  private:
    std::filesystem::path name;
    std::unique_ptr<Db> db;
};

M module-platform/CMakeLists.txt => module-platform/CMakeLists.txt +0 -1
@@ 14,7 14,6 @@ target_sources(

    PUBLIC
        include/Platform.hpp
        CommonPlatform.cpp
)

target_link_libraries(

D module-platform/CommonPlatform.cpp => module-platform/CommonPlatform.cpp +0 -31
@@ 1,31 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <Platform.hpp>

#include <purefs/vfs_subsystem.hpp>
#include <purefs/filesystem_paths.hpp>
#include <filesystem>

namespace platform
{

    void Platform::initCommonUserFolders()
    {
        if (!std::filesystem::exists(purefs::dir::getLogsPath())) {
            std::filesystem::create_directories(purefs::dir::getLogsPath());
        }

        if (!std::filesystem::exists(purefs::dir::getCrashDumpsPath())) {
            std::filesystem::create_directories(purefs::dir::getCrashDumpsPath());
        }

        if (!std::filesystem::exists(purefs::dir::getBackupOSPath())) {
            std::filesystem::create_directories(purefs::dir::getBackupOSPath());
        }

        if (!std::filesystem::exists(purefs::dir::getTemporaryPath())) {
            std::filesystem::create_directories(purefs::dir::getTemporaryPath());
        }
    }
} // namespace platform

M module-platform/include/Platform.hpp => module-platform/include/Platform.hpp +0 -1
@@ 10,7 10,6 @@ namespace platform
      public:
        virtual ~Platform() = default;
        virtual void init() = 0;
        virtual void initCommonUserFolders();
        virtual void deinit() = 0;
    };
} // namespace platform

M module-platform/linux/src/LinuxPlatform.cpp => module-platform/linux/src/LinuxPlatform.cpp +0 -1
@@ 29,7 29,6 @@ LinuxPlatform::~LinuxPlatform()
void LinuxPlatform::init()
{
    initFilesystem();
    ::platform::Platform::initCommonUserFolders();
}

void LinuxPlatform::initFilesystem()

M module-platform/rt1051/src/RT1051Platform.cpp => module-platform/rt1051/src/RT1051Platform.cpp +0 -1
@@ 22,7 22,6 @@ RT1051Platform::RT1051Platform()
void RT1051Platform::init()
{
    initFilesystem();
    ::platform::Platform::initCommonUserFolders();
}

void RT1051Platform::initFilesystem()

M module-services/service-desktop/ServiceDesktop.cpp => module-services/service-desktop/ServiceDesktop.cpp +1 -1
@@ 201,7 201,7 @@ auto ServiceDesktop::usbWorkerInit() -> sys::ReturnCodes
                                                    *usbSecurityModel,
                                                    serialNumber,
                                                    caseColour,
                                                    purefs::dir::getUserDiskPath() / "music");
                                                    purefs::dir::getUserStoragePath());

    initialized =
        desktopWorker->init({{sdesktop::RECEIVE_QUEUE_BUFFER_NAME, sizeof(std::string *), sdesktop::cdc_queue_len},

M module-services/service-fileindexer/StartupIndexer.cpp => module-services/service-fileindexer/StartupIndexer.cpp +60 -50
@@ 12,29 12,66 @@

#include <filesystem>
#include <fstream>
#include <optional>

namespace service::detail
{
    namespace fs = std::filesystem;
    namespace
    using namespace std::literals;
    using namespace std::chrono_literals;

    const auto lock_file_name        = purefs::dir::getUserDiskPath() / ".directory_is_indexed";
    constexpr auto indexing_interval = 50ms;
    constexpr auto start_delay       = 10000ms;

    bool isDirectoryFullyTraversed(const std::filesystem::recursive_directory_iterator &directory)
    {
        return directory == std::filesystem::recursive_directory_iterator();
    }

    bool createLockFile()
    {
        std::ofstream ofs(lock_file_name);
        ofs << time(nullptr);
        return ofs.good();
    }

    bool hasLockFile()
    {
        std::error_code ec;
        return fs::is_regular_file(lock_file_name, ec);
    }

    bool removeLockFile()
    {
        if (hasLockFile()) {
            std::error_code ec;
            if (!remove(lock_file_name, ec)) {
                LOG_ERROR("Failed to remove lock file, error: %d", ec.value());
                return false;
            }
        }
        return true;
    }

    std::optional<std::filesystem::recursive_directory_iterator> scanPath(std::vector<std::string>::const_iterator path)
    {
        using namespace std::string_literals;
        // Lock file name
        const auto lock_file_name = purefs::dir::getUserDiskPath() / ".directory_is_indexed";
        // Time for indexing first unit
        constexpr auto timer_indexing_delay = 400;
        // Time for initial delay after start
        constexpr auto timer_run_delay = 10000;
    } // namespace

    StartupIndexer::StartupIndexer(const std::vector<std::string> &paths) : start_dirs{paths}
        std::error_code err;
        const auto mSubDirIterator = fs::recursive_directory_iterator(*path, err);
        if (err) {
            LOG_WARN("Directory '%s' not indexed, it does not exist", path->c_str());
            return std::nullopt;
        }
        return mSubDirIterator;
    }

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

    // Process single entry
    auto StartupIndexer::processEntry(std::shared_ptr<sys::Service> svc,
                                      const std::filesystem::recursive_directory_iterator::value_type &entry) -> void
    {
        using namespace std::string_view_literals;
        if (fs::is_regular_file(entry)) {
            auto ext = fs::path(entry).extension();
            if (!isExtSupported(ext)) {


@@ 49,24 86,22 @@ namespace service::detail
        }
    }

    // On timer timeout
    auto StartupIndexer::onTimerTimeout(std::shared_ptr<sys::Service> svc) -> void
    {
        if (mForceStop) {
            return;
        }
        if (!mStarted) {
            mIdxTimer.restart(std::chrono::milliseconds{timer_indexing_delay});
            mStarted = true;
        }
        if (mSubDirIterator == std::filesystem::recursive_directory_iterator()) {
            if (mTopDirIterator == std::cend(start_dirs)) {
        if (isDirectoryFullyTraversed(mSubDirIterator)) {
            if (mTopDirIterator == std::cend(directoriesToScan)) {
                createLockFile();
                LOG_INFO("Initial startup indexer - Finished ...");
                mIdxTimer.stop();
                return;
            }
            else {
                mSubDirIterator = fs::recursive_directory_iterator(*mTopDirIterator);
                if (auto result = scanPath(mTopDirIterator)) {
                    mSubDirIterator = *result;
                }
                mTopDirIterator++;
            }
        }


@@ 74,15 109,15 @@ namespace service::detail
            processEntry(svc, *mSubDirIterator);
            mSubDirIterator++;
        }

        mIdxTimer.restart(indexing_interval);
    }

    // Setup timers for notification
    auto StartupIndexer::setupTimers(std::shared_ptr<sys::Service> svc, std::string_view svc_name) -> void
    {
        mIdxTimer = sys::TimerFactory::createPeriodicTimer(svc.get(),
                                                           "file_indexing",
                                                           std::chrono::milliseconds{timer_run_delay},
                                                           [this, svc](sys::Timer &) { onTimerTimeout(svc); });
        mIdxTimer = sys::TimerFactory::createSingleShotTimer(
            svc.get(), svc_name.data(), start_delay, [this, svc](auto &) { onTimerTimeout(svc); });
        mIdxTimer.start();
    }



@@ 91,7 126,7 @@ namespace service::detail
    {
        if (!hasLockFile()) {
            LOG_INFO("Initial startup indexer - Started...");
            mTopDirIterator = std::begin(start_dirs);
            mTopDirIterator = std::begin(directoriesToScan);
            setupTimers(svc, svc_name);
            mForceStop = false;
        }


@@ 111,29 146,4 @@ namespace service::detail
        stop();
        removeLockFile();
    }

    auto StartupIndexer::createLockFile() -> bool
    {
        std::ofstream ofs(lock_file_name);
        ofs << time(nullptr);
        return ofs.good();
    }

    auto StartupIndexer::hasLockFile() -> bool
    {
        std::error_code ec;
        return fs::is_regular_file(lock_file_name, ec);
    }

    auto StartupIndexer::removeLockFile() -> bool
    {
        if (hasLockFile()) {
            std::error_code ec;
            if (!remove(lock_file_name, ec)) {
                LOG_ERROR("Failed to remove lock file, error: %d", ec.value());
                return false;
            }
        }
        return true;
    }
} // namespace service::detail

M module-services/service-fileindexer/include/service-fileindexer/StartupIndexer.hpp => module-services/service-fileindexer/include/service-fileindexer/StartupIndexer.hpp +3 -15
@@ 22,28 22,16 @@ namespace service::detail
        void stop();

      private:
        // Process single entry
        static auto processEntry(std::shared_ptr<sys::Service> svc,
                                 const std::filesystem::recursive_directory_iterator::value_type &entry) -> void;
        // Setup timers for notification
        auto processEntry(std::shared_ptr<sys::Service> svc,
                          const std::filesystem::recursive_directory_iterator::value_type &entry) -> void;
        auto setupTimers(std::shared_ptr<sys::Service> svc, std::string_view svc_name) -> void;
        // On timer timeout
        auto onTimerTimeout(std::shared_ptr<sys::Service> svc) -> void;
        // Create lock file
        static auto createLockFile() -> bool;
        //  Check if lock file exists
        static auto hasLockFile() -> bool;
        //  remove lock file exists
        static auto removeLockFile() -> bool;

      private:
        std::vector<std::string>::const_iterator mTopDirIterator;
        std::filesystem::recursive_directory_iterator mSubDirIterator;
        sys::TimerHandle mIdxTimer;
        bool mStarted{};
        bool mForceStop{};

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

M module-vfs/paths/filesystem_paths.cpp => module-vfs/paths/filesystem_paths.cpp +22 -12
@@ 5,19 5,21 @@

namespace
{
    constexpr inline auto PATH_SYS         = "/sys";
    constexpr inline auto PATH_CONF        = "/mfgconf";
    constexpr inline auto PATH_SYS          = "/sys";
    constexpr inline auto PATH_CONF         = "/mfgconf";
    constexpr inline auto PATH_USER         = "user";
    constexpr inline auto PATH_OS          = "os";
    constexpr inline auto PATH_USER        = "user";
    constexpr inline auto PATH_CURRENT     = "current";
    constexpr inline auto PATH_PREVIOUS    = "previous";
    constexpr inline auto PATH_UPDATES     = "updates";
    constexpr inline auto PATH_TMP         = "tmp";
    constexpr inline auto PATH_BACKUP      = "backup";
    constexpr inline auto PATH_FACTORY     = "factory";
    constexpr inline auto PATH_LOGS        = "logs";
    constexpr inline auto PATH_CRASH_DUMPS = "crash_dumps";
    constexpr inline auto eMMC_disk        = PATH_SYS;
    constexpr inline auto PATH_CURRENT      = "current";
    constexpr inline auto PATH_PREVIOUS     = "previous";
    constexpr inline auto PATH_UPDATES      = "updates";
    constexpr inline auto PATH_TMP          = "tmp";
    constexpr inline auto PATH_BACKUP       = "backup";
    constexpr inline auto PATH_FACTORY      = "factory";
    constexpr inline auto PATH_LOGS         = "logs";
    constexpr inline auto PATH_CRASH_DUMPS  = "crash_dumps";
    constexpr inline auto PATH_USER_AUDIO   = "audio";
    constexpr inline auto PATH_USER_STORAGE = "storage";
    constexpr inline auto eMMC_disk         = PATH_SYS;
} // namespace

namespace purefs


@@ 83,5 85,13 @@ namespace purefs
        {
            return getUserDiskPath() / PATH_CRASH_DUMPS;
        }
        std::filesystem::path getUserAudioPath() noexcept
        {
            return getUserDiskPath() / PATH_USER_AUDIO;
        }
        std::filesystem::path getUserStoragePath() noexcept
        {
            return getUserDiskPath() / PATH_USER_STORAGE;
        }
    } // namespace dir
} // namespace purefs

M module-vfs/paths/include/purefs/filesystem_paths.hpp => module-vfs/paths/include/purefs/filesystem_paths.hpp +2 -0
@@ 22,6 22,8 @@ namespace purefs
        std::filesystem::path getFactoryOSPath() noexcept;
        std::filesystem::path getLogsPath() noexcept;
        std::filesystem::path getCrashDumpsPath() noexcept;
        std::filesystem::path getUserAudioPath() noexcept;
        std::filesystem::path getUserStoragePath() noexcept;
    } // namespace dir

    namespace file

M products/BellHybrid/BellHybridMain.cpp => products/BellHybrid/BellHybridMain.cpp +2 -18
@@ 41,7 41,7 @@
#include <SystemWatchdog/SystemWatchdog.hpp>
#include <thread.hpp>
#include <time/AlarmOperations.hpp>
#include "alarms/include/AlarmSoundPaths.hpp"
#include <Paths.hpp>

#include <memory>
#include <vector>


@@ 50,24 50,11 @@
#include <SEGGER/SEGGER_SYSVIEW.h>
#endif

namespace
{
    void validate_assets_path()
    {
        if (const auto ret = alarms::paths::validate(); not ret.empty()) {
            for (const auto &e : ret) {
                LOG_FATAL("%s path missing", e.c_str());
            }
            abort();
        }
    }
} // namespace

int main()
{
    constexpr auto ApplicationName = "BellHybrid";

    std::vector<std::string> fileIndexerAudioPaths = {alarms::paths::getBackgroundSoundsDir()};
    std::vector<std::string> fileIndexerAudioPaths = {paths::audio::proprietary(), paths::audio::user()};

#if SYSTEM_VIEW_ENABLED
    SEGGER_SYSVIEW_Conf();


@@ 117,9 104,6 @@ int main()
            /// otherwise we would end up with an init race and PhonenumberUtil could
            /// be initiated in a task with stack not big enough to handle it
            i18n::phonenumbers::PhoneNumberUtil::GetInstance();

            validate_assets_path();

            return true;
        },
        [sysmgr]() {

M products/BellHybrid/CMakeLists.txt => products/BellHybrid/CMakeLists.txt +11 -2
@@ 52,6 52,7 @@ target_link_libraries(BellHybrid
        bell::db
        bell::audio
        bell::evtmgr
        bell::paths
        log
        logdump
        messagetype


@@ 80,6 81,13 @@ strip_executable(BellHybrid)
include(BinaryAssetsVersions.cmake)
include(AddVersionJson)

add_directories(
        TARGET user_directories
        PREFIX ${CMAKE_BINARY_DIR}/sysroot/sys/user/audio
        DEPENDS user_directories_common
        DIRECTORIES relaxation
)

if (${PROJECT_TARGET} STREQUAL "TARGET_RT1051")
    include(AddBootBin)
    add_boot_bin(BellHybrid)


@@ 87,7 95,7 @@ if (${PROJECT_TARGET} STREQUAL "TARGET_RT1051")
        PRODUCT BellHybrid
        SYSROOT sysroot
        LUTS Luts.bin
        DEPENDS assets updater.bin-target ecoboot.bin-target BellHybrid-boot.bin BellHybrid-version.json-target
        DEPENDS user_directories assets updater.bin-target ecoboot.bin-target BellHybrid-boot.bin BellHybrid-version.json-target
    )
    add_version_rt1051_json(BellHybrid)
else()


@@ 95,7 103,7 @@ else()
        PRODUCT BellHybrid
        SYSROOT sysroot
        LUTS ""
        DEPENDS assets BellHybrid-version.json-target
        DEPENDS user_directories assets BellHybrid-version.json-target
    )
    add_version_linux_json(BellHybrid)
endif()


@@ 144,6 152,7 @@ add_subdirectory(keymap)
add_subdirectory(services)
add_subdirectory(sys)
add_subdirectory(serial-number-reader)
add_subdirectory(paths)

option(CONFIG_ENABLE_TEMP "Enable displaying temperature" OFF)
option(CONFIG_SHOW_MEMORY_INFO "Enable displaying memory info" OFF)

M products/BellHybrid/alarms/CMakeLists.txt => products/BellHybrid/alarms/CMakeLists.txt +1 -3
@@ 4,7 4,6 @@ add_library(bell::alarms ALIAS alarms)
target_sources(alarms
    PRIVATE
        BellAlarmHandler.cpp
        src/AlarmSoundPaths.cpp
        src/actions/PlayAudioActions.cpp
        src/actions/NotifyGUIAction.cpp
        src/actions/NotifyGUIBedtimeReminderAction.cpp


@@ 17,7 16,6 @@ target_sources(alarms
        src/actions/NotifyGUIAction.hpp
        src/actions/NotifyGUIBedtimeReminderAction.hpp
    PUBLIC
        include/AlarmSoundPaths.hpp
        include/popups/AlarmActivatedPopupRequestParams.hpp
        include/popups/AlarmDeactivatedPopupRequestParams.hpp
        include/popups/BedtimeReminderPopupRequestParams.hpp


@@ 33,12 31,12 @@ target_include_directories(alarms

target_link_libraries(alarms
   PRIVATE
        module-vfs
        bell::db
        bell::appmgr
        bell::app-common
        bell::evtmgr
        bell::audio
        bell::paths
        apps-common
   PUBLIC
        module-db

D products/BellHybrid/alarms/include/AlarmSoundPaths.hpp => products/BellHybrid/alarms/include/AlarmSoundPaths.hpp +0 -21
@@ 1,21 0,0 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <filesystem>
#include <vector>

namespace alarms::paths
{
    std::filesystem::path getAlarmDir() noexcept;
    std::filesystem::path getMusicDir() noexcept;
    std::filesystem::path getPreWakeUpChimesDir() noexcept;
    std::filesystem::path getSnoozeChimesDir() noexcept;
    std::filesystem::path getBedtimeReminderChimesDir() noexcept;
    std::filesystem::path getBackgroundSoundsDir() noexcept;
    std::filesystem::path getMeditationSoundsDir() noexcept;

    /// Check if system paths exist. In case of error, returns vector of missing entries.
    std::vector<std::filesystem::path> validate() noexcept;
} // namespace alarms::paths

D products/BellHybrid/alarms/src/AlarmSoundPaths.cpp => products/BellHybrid/alarms/src/AlarmSoundPaths.cpp +0 -71
@@ 1,71 0,0 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "AlarmSoundPaths.hpp"

#include <purefs/filesystem_paths.hpp>

namespace alarms::paths
{
    std::filesystem::path getAlarmDir() noexcept
    {
        return purefs::dir::getCurrentOSPath() / "assets/audio/alarm";
    }

    std::filesystem::path getMusicDir() noexcept
    {
        return purefs::dir::getUserDiskPath() / "music";
    }

    std::filesystem::path getPreWakeUpChimesDir() noexcept
    {
        return purefs::dir::getCurrentOSPath() / "assets/audio/prewakeup";
    }

    std::filesystem::path getSnoozeChimesDir() noexcept
    {
        return purefs::dir::getCurrentOSPath() / "assets/audio/chimes";
    }

    std::filesystem::path getBedtimeReminderChimesDir() noexcept
    {
        return purefs::dir::getCurrentOSPath() / "assets/audio/evening_reminder";
    }

    std::filesystem::path getBackgroundSoundsDir() noexcept
    {
        return purefs::dir::getCurrentOSPath() / "assets/audio/bg_sounds";
    }

    std::filesystem::path getMeditationSoundsDir() noexcept
    {
        return purefs::dir::getCurrentOSPath() / "assets/audio/meditation";
    }

    std::vector<std::filesystem::path> validate() noexcept
    {
        std::vector<std::filesystem::path> ret;
        if (not std::filesystem::exists(getAlarmDir())) {
            ret.push_back(getAlarmDir());
        }
        if (not std::filesystem::exists(getMusicDir())) {
            ret.push_back(getMusicDir());
        }
        if (not std::filesystem::exists(getPreWakeUpChimesDir())) {
            ret.push_back(getPreWakeUpChimesDir());
        }
        if (not std::filesystem::exists(getSnoozeChimesDir())) {
            ret.push_back(getSnoozeChimesDir());
        }
        if (not std::filesystem::exists(getBedtimeReminderChimesDir())) {
            ret.push_back(getBedtimeReminderChimesDir());
        }
        if (not std::filesystem::exists(getBackgroundSoundsDir())) {
            ret.push_back(getBackgroundSoundsDir());
        }
        if (not std::filesystem::exists(getMeditationSoundsDir())) {
            ret.push_back(getMeditationSoundsDir());
        }
        return ret;
    }
} // namespace alarms::paths

M products/BellHybrid/alarms/src/actions/PlayAudioActions.cpp => products/BellHybrid/alarms/src/actions/PlayAudioActions.cpp +11 -7
@@ 1,9 1,9 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "AlarmSoundPaths.hpp"
#include "PlayAudioActions.hpp"

#include <Paths.hpp>
#include <audio/AudioMessage.hpp>
#include <db/SystemSettings.hpp>
#include <Timers/TimerFactory.hpp>


@@ 69,26 69,30 @@ namespace alarms
        std::unique_ptr<PlayAudioAction> createPreWakeUpChimeAction(sys::Service &service)
        {
            return std::make_unique<PlayAudioAction>(service,
                                                     paths::getPreWakeUpChimesDir(),
                                                     paths::audio::proprietary() / paths::audio::preWakeup(),
                                                     bell::settings::PrewakeUp::tone,
                                                     audio::PlaybackType::PreWakeUp);
        }

        std::unique_ptr<PlayAudioAction> createSnoozeChimeAction(sys::Service &service)
        {
            return std::make_unique<PlayAudioAction>(
                service, paths::getSnoozeChimesDir(), bell::settings::Snooze::tone, audio::PlaybackType::Snooze);
            return std::make_unique<PlayAudioAction>(service,
                                                     paths::audio::proprietary() / paths::audio::snooze(),
                                                     bell::settings::Snooze::tone,
                                                     audio::PlaybackType::Snooze);
        }
        std::unique_ptr<PlayAudioAction> createAlarmToneAction(sys::Service &service)
        {
            /// Alarm duration is controlled from the main application's state machine
            return std::make_unique<PlayAudioAction>(
                service, paths::getAlarmDir(), bell::settings::Alarm::tone, audio::PlaybackType::Alarm);
            return std::make_unique<PlayAudioAction>(service,
                                                     paths::audio::proprietary() / paths::audio::alarm(),
                                                     bell::settings::Alarm::tone,
                                                     audio::PlaybackType::Alarm);
        }
        std::unique_ptr<PlayAudioAction> createBedtimeChimeAction(sys::Service &service)
        {
            return std::make_unique<PlayAudioAction>(service,
                                                     paths::getBedtimeReminderChimesDir(),
                                                     paths::audio::proprietary() / paths::audio::bedtimeReminder(),
                                                     bell::settings::Bedtime::tone,
                                                     audio::PlaybackType::Bedtime);
        }

A products/BellHybrid/apps/application-bell-background-sounds/ApplicationBellBackgroundSounds.cpp => products/BellHybrid/apps/application-bell-background-sounds/ApplicationBellBackgroundSounds.cpp +0 -0
M products/BellHybrid/apps/application-bell-meditation-timer/CMakeLists.txt => products/BellHybrid/apps/application-bell-meditation-timer/CMakeLists.txt +1 -1
@@ 48,7 48,7 @@ target_link_libraries(application-bell-meditation-timer
        bell::audio
        bell::app-common
        bell::app-main
        bell::alarms
        bell::paths
        service-appmgr
        service-time
    PUBLIC

M products/BellHybrid/apps/application-bell-meditation-timer/data/MeditationCommon.hpp => products/BellHybrid/apps/application-bell-meditation-timer/data/MeditationCommon.hpp +3 -6
@@ 3,8 3,7 @@

#pragma once

#include <AlarmSoundPaths.hpp>

#include <Paths.hpp>
#include <filesystem>
#include <string>



@@ 14,15 13,13 @@ namespace app::meditation
    {
        static constexpr auto meditationCountdown = "MeditationCountdown";
        static constexpr auto meditationProgress  = "MeditationProgress";
        static constexpr auto sessionEnded        = "MeditationSessionEnded";
    }; // namespace windows

    constexpr auto meditationDBRecordName          = "MeditationTimer";
    constexpr auto meditationCountdownDBRecordName = "start_delay";
    constexpr auto meditationDBRecordName = "MeditationTimer";

    inline std::filesystem::path getMeditationAudioPath()
    {
        return alarms::paths::getMeditationSoundsDir() / "Meditation_Gong.mp3";
        return paths::audio::proprietary() / "Meditation_Gong.mp3";
    }

} // namespace app::meditation

M products/BellHybrid/apps/application-bell-powernap/ApplicationBellPowerNap.cpp => products/BellHybrid/apps/application-bell-powernap/ApplicationBellPowerNap.cpp +6 -5
@@ 9,7 9,7 @@
#include "windows/PowerNapProgressWindow.hpp"
#include "windows/PowerNapSessionEndedWindow.hpp"
#include <common/models/TimeModel.hpp>
#include <AlarmSoundPaths.hpp>
#include <Paths.hpp>
#include <common/windows/SessionPausedWindow.hpp>

namespace app


@@ 46,10 46,11 @@ namespace app
        });
        windowsFactory.attach(
            gui::window::name::powernapProgress, [this](ApplicationCommon *app, const std::string &name) {
                auto timeModel        = std::make_unique<app::TimeModel>();
                auto alarmLightOnOff  = std::make_unique<bell_settings::AlarmLightOnOffModel>(this);
                auto soundsRepository = std::make_unique<SoundsRepository>(alarms::paths::getAlarmDir());
                auto presenter        = std::make_unique<powernap::PowerNapProgressPresenter>(app,
                auto timeModel       = std::make_unique<app::TimeModel>();
                auto alarmLightOnOff = std::make_unique<bell_settings::AlarmLightOnOffModel>(this);
                auto soundsRepository =
                    std::make_unique<SoundsRepository>(paths::audio::proprietary() / paths::audio::alarm());
                auto presenter = std::make_unique<powernap::PowerNapProgressPresenter>(app,
                                                                                       settings.get(),
                                                                                       std::move(soundsRepository),
                                                                                       *audioModel,

M products/BellHybrid/apps/application-bell-powernap/CMakeLists.txt => products/BellHybrid/apps/application-bell-powernap/CMakeLists.txt +1 -1
@@ 46,7 46,7 @@ target_link_libraries(application-bell-powernap
        bell::app-common
        bell::app-main
        bell::keymap
        bell::alarms
        bell::paths
        bell::db
        Microsoft.GSL::GSL


M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationMainWindowPresenter.cpp => products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationMainWindowPresenter.cpp +3 -1
@@ 2,7 2,9 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "RelaxationMainWindowPresenter.hpp"

#include "data/BGSoundsAudioData.hpp"
#include "widgets/SoundListItem.hpp"
#include "ApplicationBellBackgroundSounds.hpp"
#include <apps-common/models/SongsRepository.hpp>

namespace

M products/BellHybrid/apps/application-bell-settings/ApplicationBellSettings.cpp => products/BellHybrid/apps/application-bell-settings/ApplicationBellSettings.cpp +7 -7
@@ 30,9 30,8 @@
#include "windows/BellSettingsWindow.hpp"
#include "widgets/DialogYesNo.hpp"

#include <AlarmSoundPaths.hpp>
#include <Paths.hpp>
#include <apps-common/windows/Dialog.hpp>
#include <common/layouts/BaseHomeScreenLayoutProvider.hpp>
#include <common/BellPowerOffPresenter.hpp>
#include <common/models/BedtimeModel.hpp>
#include <common/models/LayoutModel.hpp>


@@ 125,7 124,7 @@ namespace app
            gui::window::name::bellSettingsBedtimeTone, [this](ApplicationCommon *app, const std::string &name) {
                auto bedtimeModel = std::make_shared<bell_bedtime::BedtimeModel>(app, *audioModel);
                auto soundsRepository =
                    std::make_unique<SoundsRepository>(alarms::paths::getBedtimeReminderChimesDir());
                    std::make_unique<SoundsRepository>(paths::audio::proprietary() / paths::audio::bedtimeReminder());
                auto provider = std::make_shared<bell_settings::BedtimeSettingsListItemProvider>(
                    bedtimeModel, soundsRepository->getSongTitles());
                auto presenter = std::make_unique<bell_settings::SettingsPresenter>(


@@ 166,7 165,7 @@ namespace app
                                                                            std::move(prewakeUpChimeVolumeModel),
                                                                            std::move(prewakeUpLightDurationModel),
                                                                            std::move(prewakeUpFrontlightModel));
                auto soundsRepository = std::make_unique<SoundsRepository>(alarms::paths::getPreWakeUpChimesDir());
                auto soundsRepository = std::make_unique<SoundsRepository>(paths::audio::proprietary() / paths::audio::preWakeup());
                auto provider         = std::make_unique<bell_settings::PrewakeUpListItemProvider>(
                    *prewakeUpSettingsModel, soundsRepository->getSongTitles());
                auto frontlightModel = std::make_unique<bell_settings::FrontlightModel>(app);


@@ 197,8 196,9 @@ namespace app
                                                                         std::move(snoozeChimeIntervalModel),
                                                                         std::move(snoozeChimeToneModel),
                                                                         std::move(snoozeChimeVolumeModel));
                auto soundsRepository = std::make_unique<SoundsRepository>(alarms::paths::getSnoozeChimesDir());
                auto provider         = std::make_shared<bell_settings::SnoozeListItemProvider>(
                auto soundsRepository =
                    std::make_unique<SoundsRepository>(paths::audio::proprietary() / paths::audio::snooze());
                auto provider = std::make_shared<bell_settings::SnoozeListItemProvider>(
                    *snoozeSettingsModel, soundsRepository->getSongTitles());
                auto presenter = std::make_unique<bell_settings::SnoozePresenter>(
                    provider, std::move(snoozeSettingsModel), *audioModel, std::move(soundsRepository));


@@ 215,7 215,7 @@ namespace app
                                                                        std::move(alarmVolumeModel),
                                                                        std::move(alarmLightOnOffModel),
                                                                        std::move(alarmFrontlightModel));
                auto soundsRepository = std::make_unique<SoundsRepository>(alarms::paths::getAlarmDir());
                auto soundsRepository = std::make_unique<SoundsRepository>(paths::audio::proprietary() / paths::audio::alarm());
                auto frontlightModel  = std::make_unique<bell_settings::FrontlightModel>(app);
                auto provider         = std::make_unique<bell_settings::AlarmSettingsListItemProvider>(
                    *alarmSettingsModel, soundsRepository->getSongTitles());

M products/BellHybrid/apps/application-bell-settings/CMakeLists.txt => products/BellHybrid/apps/application-bell-settings/CMakeLists.txt +1 -1
@@ 123,7 123,7 @@ target_link_libraries(application-bell-settings
        bell::audio
        bellgui
        bell::db
        bell::alarms
        bell::paths
        bell::app-main
        bell::appmgr
        service-appmgr

M products/BellHybrid/apps/common/CMakeLists.txt => products/BellHybrid/apps/common/CMakeLists.txt +1 -0
@@ 20,6 20,7 @@ target_sources(application-bell-common
        src/BatteryModel.cpp
        src/SoundsRepository.cpp
        src/BellListItemProvider.cpp
        src/SoundsProvider.cpp
        src/windows/BellFactoryReset.cpp
        src/windows/BellFinishedWindow.cpp
        src/windows/BellTurnOffWindow.cpp

A products/BellHybrid/apps/common/include/common/SoundsProvider.hpp => products/BellHybrid/apps/common/include/common/SoundsProvider.hpp +53 -0
@@ 0,0 1,53 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "DatabaseModel.hpp"
#include "module-gui/gui/widgets/ListItemProvider.hpp"
#include "module-db/Interface/MultimediaFilesRecord.hpp"

namespace app
{
    class ApplicationCommon;
}
namespace app::music
{
    class AbstractSongsRepository;
}
namespace app::bgSounds
{
    class SoundsProvider : public app::DatabaseModel<db::multimedia_files::MultimediaFilesRecord>,
                           public gui::ListItemProvider
    {
      public:
        using ItemBuilder = std::function<gui::ListItem *(const db::multimedia_files::MultimediaFilesRecord &)>;
        using OnSoundItemActivatedCallback =
            std::function<void(const db::multimedia_files::MultimediaFilesRecord &sound)>;

        SoundsProvider(app::ApplicationCommon *app,
                       ItemBuilder &&itemBuilder,
                       std::unique_ptr<app::music::AbstractSongsRepository> soundsRepository,
                       std::uint32_t itemSize);

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

        void setOnSoundItemActivatedCallback(OnSoundItemActivatedCallback &&callback);

      private:
        app::ApplicationCommon *app{nullptr};
        std::unique_ptr<app::music::AbstractSongsRepository> soundsRepository;
        ItemBuilder itemBuilder;
        std::uint32_t itemSize;

        OnSoundItemActivatedCallback onSoundItemActivatedCallback;

        [[nodiscard]] bool updateRecords(std::vector<db::multimedia_files::MultimediaFilesRecord> records) override;

        bool onAudioListRetrieved(const std::vector<db::multimedia_files::MultimediaFilesRecord> &records,
                                  std::uint32_t repoRecordsCount);
    };
} // namespace app::bgSounds

A products/BellHybrid/apps/common/src/SoundsProvider.cpp => products/BellHybrid/apps/common/src/SoundsProvider.cpp +71 -0
@@ 0,0 1,71 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "products/BellHybrid/apps/common/include/common/SoundsProvider.hpp"
#include "module-gui/gui/widgets/ListViewEngine.hpp"
#include "module-apps/apps-common/models/SongsRepository.hpp"

namespace app::bgSounds
{
    app::bgSounds::SoundsProvider::SoundsProvider(app::ApplicationCommon *app,
                                                  ItemBuilder &&itemBuilder,
                                                  std::unique_ptr<app::music::AbstractSongsRepository> soundsRepository,
                                                  std::uint32_t itemSize)
        : DatabaseModel(app),
          soundsRepository(std::move(soundsRepository)), itemBuilder{std::move(itemBuilder)}, itemSize{itemSize}
    {}

    auto SoundsProvider::requestRecordsCount() -> unsigned int
    {
        return recordsCount;
    }
    auto SoundsProvider::getMinimalItemSpaceRequired() const -> unsigned int
    {
        return itemSize;
    }
    auto SoundsProvider::getItem(gui::Order order) -> gui::ListItem *
    {
        auto sound = getRecord(order);
        if (!sound) {
            return nullptr;
        }

        auto item               = itemBuilder(*sound);
        item->activatedCallback = [this, sound](auto &) {
            if (onSoundItemActivatedCallback) {
                onSoundItemActivatedCallback(*sound);
            }
            return true;
        };

        return item;
    }
    void SoundsProvider::requestRecords(uint32_t offset, uint32_t limit)
    {
        soundsRepository->getMusicFilesList(
            offset,
            limit,
            [this](const std::vector<db::multimedia_files::MultimediaFilesRecord> &records,
                   const auto repoRecordsCount) { return onAudioListRetrieved(records, repoRecordsCount); });
    }
    bool SoundsProvider::onAudioListRetrieved(const std::vector<db::multimedia_files::MultimediaFilesRecord> &records,
                                              const std::uint32_t repoRecordsCount)
    {
        if (list != nullptr && recordsCount != repoRecordsCount) {
            recordsCount = repoRecordsCount;
            list->reSendLastRebuildRequest();
            return false;
        }
        return updateRecords(records);
    }
    bool SoundsProvider::updateRecords(std::vector<db::multimedia_files::MultimediaFilesRecord> records)
    {
        DatabaseModel::updateRecords(std::move(records));
        list->onProviderDataUpdate();
        return true;
    }
    void SoundsProvider::setOnSoundItemActivatedCallback(SoundsProvider::OnSoundItemActivatedCallback &&callback)
    {
        onSoundItemActivatedCallback = std::move(callback);
    }
} // namespace app::bgSounds

M products/BellHybrid/assets/assets_common.json => products/BellHybrid/assets/assets_common.json +13 -15
@@ 65,18 65,18 @@
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/chimes/Gentle_Chime.mp3", "output": "assets/audio/chimes/Gentle_Chime.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/chimes/Rise_&_Shine.mp3", "output": "assets/audio/chimes/Rise_&_Shine.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/chimes/Twinkle_Chime.mp3", "output": "assets/audio/chimes/Twinkle_Chime.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Ancient_Greek.mp3", "output": "assets/audio/bg_sounds/Ancient_Greek.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Autumnal_Sea.mp3", "output": "assets/audio/bg_sounds/Autumnal_Sea.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Bubbling_Brook.mp3", "output": "assets/audio/bg_sounds/Bubbling_Brook.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Charming_Bells.mp3", "output": "assets/audio/bg_sounds/Charming_Bells.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Forest_Creek.mp3", "output": "assets/audio/bg_sounds/Forest_Creek.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Mountain_Lagoon.mp3", "output": "assets/audio/bg_sounds/Mountain_Lagoon.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Mystic_Nature.mp3", "output": "assets/audio/bg_sounds/Mystic_Nature.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Natures_Harmony.mp3", "output": "assets/audio/bg_sounds/Natures_Harmony.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Rhythmic_Lullaby.mp3", "output": "assets/audio/bg_sounds/Rhythmic_Lullaby.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Seaside_Symphony.mp3", "output": "assets/audio/bg_sounds/Seaside_Symphony.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Under_the_Water.mp3", "output": "assets/audio/bg_sounds/Under_the_Water.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Woodland_Ambiance.mp3", "output": "assets/audio/bg_sounds/Woodland_Ambiance.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Ancient_Greek.mp3", "output": "assets/audio/relaxation/Ancient_Greek.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Autumnal_Sea.mp3", "output": "assets/audio/relaxation/Autumnal_Sea.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Bubbling_Brook.mp3", "output": "assets/audio/relaxation/Bubbling_Brook.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Charming_Bells.mp3", "output": "assets/audio/relaxation/Charming_Bells.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Forest_Creek.mp3", "output": "assets/audio/relaxation/Forest_Creek.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Mountain_Lagoon.mp3", "output": "assets/audio/relaxation/Mountain_Lagoon.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Mystic_Nature.mp3", "output": "assets/audio/relaxation/Mystic_Nature.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Natures_Harmony.mp3", "output": "assets/audio/relaxation/Natures_Harmony.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Rhythmic_Lullaby.mp3", "output": "assets/audio/relaxation/Rhythmic_Lullaby.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Seaside_Symphony.mp3", "output": "assets/audio/relaxation/Seaside_Symphony.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Under_the_Water.mp3", "output": "assets/audio/relaxation/Under_the_Water.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/bg_sounds/Woodland_Ambiance.mp3", "output": "assets/audio/relaxation/Woodland_Ambiance.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/meditation/Meditation_Gong.mp3", "output": "assets/audio/meditation/Meditation_Gong.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/prewakeup/Joyful_Awakening.mp3", "output": "assets/audio/prewakeup/Joyful_Awakening.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/prewakeup/Morning_Spirit.mp3", "output": "assets/audio/prewakeup/Morning_Spirit.mp3"},


@@ 102,8 102,6 @@
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/evening_reminder/Evening_Horizon.mp3", "output": "assets/audio/evening_reminder/Evening_Horizon.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/evening_reminder/Evolving_Dusk.mp3", "output": "assets/audio/evening_reminder/Evolving_Dusk.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/evening_reminder/Melodic_Mirth.mp3", "output": "assets/audio/evening_reminder/Melodic_Mirth.mp3"},
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/evening_reminder/Twilight_Gleam.mp3", "output": "assets/audio/evening_reminder/Twilight_Gleam.mp3"},

        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/bell/evening_reminder/Twilight_Gleam.mp3", "output": "../user/music/Twilight_Gleam.mp3"}
        {"name": "release_audio.tgz", "tarfile" :"./image/assets/audio/bell/evening_reminder/Twilight_Gleam.mp3", "output": "assets/audio/evening_reminder/Twilight_Gleam.mp3"}
    ]
}

A products/BellHybrid/paths/CMakeLists.txt => products/BellHybrid/paths/CMakeLists.txt +6 -0
@@ 0,0 1,6 @@
add_library(paths Paths.cpp)
add_library(bell::paths ALIAS paths)

target_include_directories(paths PUBLIC .)

target_link_libraries(paths PRIVATE module-vfs)
\ No newline at end of file

A products/BellHybrid/paths/Paths.cpp => products/BellHybrid/paths/Paths.cpp +38 -0
@@ 0,0 1,38 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "Paths.hpp"
#include <purefs/filesystem_paths.hpp>

std::filesystem::path paths::audio::user() noexcept
{
    return purefs::dir::getUserDiskPath() / "audio";
}
std::filesystem::path paths::audio::proprietary() noexcept
{
    return purefs::dir::getCurrentOSPath() / "assets/audio";
}
std::filesystem::path paths::audio::alarm() noexcept
{
    return "alarm";
}
std::filesystem::path paths::audio::preWakeup() noexcept
{
    return "prewakeup";
}
std::filesystem::path paths::audio::snooze() noexcept
{
    return "chimes";
}
std::filesystem::path paths::audio::bedtimeReminder() noexcept
{
    return "evening_reminder";
}
std::filesystem::path paths::audio::relaxation() noexcept
{
    return "relaxation";
}
std::filesystem::path paths::audio::meditation() noexcept
{
    return "meditation";
}

A products/BellHybrid/paths/Paths.hpp => products/BellHybrid/paths/Paths.hpp +22 -0
@@ 0,0 1,22 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <filesystem>

namespace paths
{
    namespace audio
    {
        std::filesystem::path user() noexcept;
        std::filesystem::path proprietary() noexcept;
        std::filesystem::path alarm() noexcept;
        std::filesystem::path preWakeup() noexcept;
        std::filesystem::path snooze() noexcept;
        std::filesystem::path bedtimeReminder() noexcept;
        std::filesystem::path relaxation() noexcept;
        std::filesystem::path meditation() noexcept;
    } // namespace audio

} // namespace paths

M products/BellHybrid/services/db/tests/MeditationStatisticsTable_tests.cpp => products/BellHybrid/services/db/tests/MeditationStatisticsTable_tests.cpp +1 -1
@@ 34,7 34,7 @@ namespace
                REQUIRE(std::filesystem::remove(name));
            }

            db = std::make_unique<MeditationStatisticsDB>(name.c_str());
            db = std::make_unique<Db>(name.c_str());

            if (not db->isInitialized()) {
                throw std::runtime_error("Could not initialize database");

M products/PurePhone/CMakeLists.txt => products/PurePhone/CMakeLists.txt +9 -2
@@ 106,6 106,13 @@ message_serial_status()
include(BinaryAssetsVersions.cmake)
include(AddVersionJson)

add_directories(
        TARGET user_directories
        PREFIX ${CMAKE_BINARY_DIR}/sysroot/sys/user/audio
        DEPENDS user_directories_common
        DIRECTORIES music_player
)

if (${PROJECT_TARGET} STREQUAL "TARGET_RT1051")
    include(AddBootBin)
    add_boot_bin(PurePhone)


@@ 113,7 120,7 @@ if (${PROJECT_TARGET} STREQUAL "TARGET_RT1051")
        PRODUCT PurePhone
        SYSROOT sysroot
        LUTS Luts.bin
        DEPENDS assets updater.bin-target ecoboot.bin-target PurePhone-boot.bin PurePhone-version.json-target
        DEPENDS user_directories assets updater.bin-target ecoboot.bin-target PurePhone-boot.bin PurePhone-version.json-target
    )
    add_version_rt1051_json(PurePhone)
else()


@@ 121,7 128,7 @@ else()
        PRODUCT PurePhone
        SYSROOT sysroot
        LUTS ""
        DEPENDS assets PurePhone-version.json-target
        DEPENDS user_directories assets PurePhone-version.json-target
    )
    add_version_linux_json(PurePhone)
endif()

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

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

    prof::init();


M products/PurePhone/assets/assets_common.json => products/PurePhone/assets/assets_common.json +14 -14
@@ 220,19 220,19 @@
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/ringtone/ringtone_drum_2.mp3", "output": "assets/audio/ringtone/ringtone_drum_2.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/ringtone/ringtone_drum.mp3", "output": "assets/audio/ringtone/ringtone_drum.mp3"},

        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_Love_Peace_Harmony.mp3", "output": "../user/music/Nick_Lewis_-_Love_Peace_Harmony.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/lazy_nature_reserve-devel.mp3", "output": "../user/music/lazy_nature_reserve-devel.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_Que_Bien_Me_Dijo_Mi_Madre.mp3", "output": "../user/music/Nick_Lewis_-_Que_Bien_Me_Dijo_Mi_Madre.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_Mahamrityunjaya_Mantra.mp3", "output": "../user/music/Nick_Lewis_-_Mahamrityunjaya_Mantra.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/autumn_in_jelitkowo-devel.mp3", "output": "../user/music/autumn_in_jelitkowo-devel.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_Love_Peace_And_Forgiveness.mp3", "output": "../user/music/Nick_Lewis_-_Love_Peace_And_Forgiveness.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_Bring_The_Light.mp3", "output": "../user/music/Nick_Lewis_-_Bring_The_Light.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_Kristies_Elephant.mp3", "output": "../user/music/Nick_Lewis_-_Kristies_Elephant.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/secret_river-devel.mp3", "output": "../user/music/secret_river-devel.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/beka_nature_reserve-devel.mp3", "output": "../user/music/beka_nature_reserve-devel.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_Always_With_You.mp3", "output": "../user/music/Nick_Lewis_-_Always_With_You.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/baltic_seagulls-devel.mp3", "output": "../user/music/baltic_seagulls-devel.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_You_Are_Holy.mp3", "output": "../user/music/Nick_Lewis_-_You_Are_Holy.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_Pure_Ocean.mp3", "output": "../user/music/Nick_Lewis_-_Pure_Ocean.mp3"}
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_Love_Peace_Harmony.mp3", "output": "../user/audio/music_player/Nick_Lewis_-_Love_Peace_Harmony.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/lazy_nature_reserve-devel.mp3", "output": "../user/audio/music_player/lazy_nature_reserve-devel.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_Que_Bien_Me_Dijo_Mi_Madre.mp3", "output": "../user/audio/music_player/Nick_Lewis_-_Que_Bien_Me_Dijo_Mi_Madre.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_Mahamrityunjaya_Mantra.mp3", "output": "../user/audio/music_player/Nick_Lewis_-_Mahamrityunjaya_Mantra.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/autumn_in_jelitkowo-devel.mp3", "output": "../user/audio/music_player/autumn_in_jelitkowo-devel.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_Love_Peace_And_Forgiveness.mp3", "output": "../user/audio/music_player/Nick_Lewis_-_Love_Peace_And_Forgiveness.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_Bring_The_Light.mp3", "output": "../user/audio/music_player/Nick_Lewis_-_Bring_The_Light.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_Kristies_Elephant.mp3", "output": "../user/audio/music_player/Nick_Lewis_-_Kristies_Elephant.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/secret_river-devel.mp3", "output": "../user/audio/music_player/secret_river-devel.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/beka_nature_reserve-devel.mp3", "output": "../user/audio/music_player/beka_nature_reserve-devel.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_Always_With_You.mp3", "output": "../user/audio/music_player/Nick_Lewis_-_Always_With_You.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/baltic_seagulls-devel.mp3", "output": "../user/audio/music_player/baltic_seagulls-devel.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_You_Are_Holy.mp3", "output": "../user/audio/music_player/Nick_Lewis_-_You_Are_Holy.mp3"},
        {"name" : "release_audio.tgz", "tarfile": "./image/assets/audio/pure/music/Nick_Lewis_-_Pure_Ocean.mp3", "output": "../user/audio/music_player/Nick_Lewis_-_Pure_Ocean.mp3"}
    ]
}