~aleteoryx/muditaos

721fbbb1bd7fe9877faee2c60337ab7b43387c61 — Maciej Gibowicz 1 year, 11 months ago ac17db1
[BH-1896] Add opening the vertical lists on the currently set sound

After starting applications that use vertical lists of music, a list
will open on the appropriate page with the previously selected song.
Fix displaying vertical list before fileindexer ends.
M module-db/CMakeLists.txt => module-db/CMakeLists.txt +1 -0
@@ 100,6 100,7 @@ set(SOURCES
        queries/multimedia_files/QueryMultimediaFilesGetLimited.cpp
        queries/multimedia_files/QueryMultimediaFilesRemove.cpp
        queries/multimedia_files/QueryMultimediaFilesCount.cpp
        queries/multimedia_files/QueryMultimediaFilesGetOffset.cpp
        queries/notes/QueryNoteRemove.cpp
        queries/notes/QueryNotesGet.cpp
        queries/notes/QueryNotesGetByText.cpp

M module-db/Interface/MultimediaFilesRecord.cpp => module-db/Interface/MultimediaFilesRecord.cpp +15 -1
@@ 11,6 11,7 @@
#include <queries/multimedia_files/QueryMultimediaFilesGetLimited.hpp>
#include <queries/multimedia_files/QueryMultimediaFilesRemove.hpp>
#include <queries/multimedia_files/QueryMultimediaFilesCount.hpp>
#include <queries/multimedia_files/QueryMultimediaFilesGetOffset.hpp>

namespace db::multimedia_files
{


@@ 84,6 85,9 @@ namespace db::multimedia_files
        if (typeid(*query) == typeid(query::GetCountForPath)) {
            return runQueryImplGetCountForPath(std::static_pointer_cast<query::GetCountForPath>(query));
        }
        if (typeid(*query) == typeid(query::GetOffsetByPath)) {
            return runQueryImplGetOffsetByPath(std::static_pointer_cast<query::GetOffsetByPath>(query));
        }
        return nullptr;
    }



@@ 259,7 263,7 @@ namespace db::multimedia_files
    {
        const auto records =
            database->files.getLimitOffsetByPaths(query->paths, query->offset, query->limit, query->sorting);
        auto response      = std::make_unique<query::GetLimitedResult>(records, database->files.count(query->paths));
        auto response = std::make_unique<query::GetLimitedResult>(records, database->files.count(query->paths));
        response->setRequestQuery(query);

        return response;


@@ 282,4 286,14 @@ namespace db::multimedia_files
        response->setRequestQuery(query);
        return response;
    }

    std::unique_ptr<db::multimedia_files::query::GetOffsetResult> MultimediaFilesRecordInterface::
        runQueryImplGetOffsetByPath(const std::shared_ptr<db::multimedia_files::query::GetOffsetByPath> &query)
    {
        const auto ret =
            database->files.getOffsetOfSortedRecordByPath(query->folderPath, query->recordPath, query->sorting);
        auto response = std::make_unique<query::GetOffsetResult>(ret);
        response->setRequestQuery(query);
        return response;
    }
} // namespace db::multimedia_files

M module-db/Interface/MultimediaFilesRecord.hpp => module-db/Interface/MultimediaFilesRecord.hpp +6 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 45,6 45,9 @@ namespace db::multimedia_files::query
    class RemoveByPath;
    class RemoveResult;
    class GetCountForPath;
    class GetOffsetByPath;
    class GetOffsetResult;

} // namespace db::multimedia_files::query

namespace db::multimedia_files


@@ 102,6 105,8 @@ namespace db::multimedia_files
            const std::shared_ptr<db::multimedia_files::query::RemoveByPath> &query);
        std::unique_ptr<db::multimedia_files::query::GetCountResult> runQueryImplGetCountForPath(
            const std::shared_ptr<db::multimedia_files::query::GetCountForPath> &query);
        std::unique_ptr<db::multimedia_files::query::GetOffsetResult> runQueryImplGetOffsetByPath(
            const std::shared_ptr<db::multimedia_files::query::GetOffsetByPath> &query);

        MultimediaFilesDB *database = nullptr;
    };

M module-db/Tables/MultimediaFilesTable.cpp => module-db/Tables/MultimediaFilesTable.cpp +27 -0
@@ 386,4 386,31 @@ namespace db::multimedia_files

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

    auto MultimediaFilesTable::getOffsetOfSortedRecordByPath(const std::string &folderPath,
                                                             const std::string &recordPath,
                                                             SortingBy sorting) -> SortedRecord
    {
        const std::string query = "SELECT * FROM ("
                                  "      SELECT"
                                  "          ROW_NUMBER () OVER ("
                                  "              ORDER BY " +
                                  getSorting(sorting) +
                                  "          ) offset,"
                                  "          path"
                                  "      FROM"
                                  "          files"
                                  "      WHERE path LIKE '" +
                                  folderPath + "%%'" +
                                  "  )"
                                  "  WHERE"
                                  "      path='" +
                                  recordPath + "'";

        std::unique_ptr<QueryResult> retQuery = db->query(query.c_str());
        if ((retQuery == nullptr) || (retQuery->getRowCount() == 0)) {
            return SortedRecord{.path = recordPath, .offset = 0};
        }
        return SortedRecord{.path = (*retQuery)[1].getString(), .offset = (*retQuery)[0].getUInt32()};
    }
} // namespace db::multimedia_files

M module-db/Tables/MultimediaFilesTable.hpp => module-db/Tables/MultimediaFilesTable.hpp +10 -0
@@ 52,6 52,12 @@ namespace db::multimedia_files
        auto isValid() const -> bool;
    };

    struct SortedRecord
    {
        std::string path{};
        std::uint32_t offset{};
    };

    enum class TableFields
    {
        path,


@@ 117,6 123,10 @@ namespace db::multimedia_files
        /// @note entry.ID is skipped
        bool addOrUpdate(TableRow entry, std::string oldPath = "");

        auto getOffsetOfSortedRecordByPath(const std::string &folderPath,
                                           const std::string &recordPath,
                                           SortingBy sorting = SortingBy::TitleAscending) -> SortedRecord;

      private:
        auto getFieldName(TableFields field) -> std::string;
    };

A module-db/queries/multimedia_files/QueryMultimediaFilesGetOffset.cpp => module-db/queries/multimedia_files/QueryMultimediaFilesGetOffset.cpp +29 -0
@@ 0,0 1,29 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "QueryMultimediaFilesGetOffset.hpp"

namespace db::multimedia_files::query
{
    GetOffsetByPath::GetOffsetByPath(const std::string &folderPath, const std::string &recordPath, SortingBy sorting)
        : Query(Query::Type::Read), folderPath{folderPath}, recordPath{recordPath}, sorting{sorting}
    {}

    [[nodiscard]] auto GetOffsetByPath::debugInfo() const -> std::string
    {
        return std::string{"GetOffsetByPath"};
    }

    GetOffsetResult::GetOffsetResult(SortedRecord sortedRecord) : sortedRecord(std::move(sortedRecord))
    {}

    [[nodiscard]] auto GetOffsetResult::getResult() const -> SortedRecord
    {
        return sortedRecord;
    }

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

A module-db/queries/multimedia_files/QueryMultimediaFilesGetOffset.hpp => module-db/queries/multimedia_files/QueryMultimediaFilesGetOffset.hpp +35 -0
@@ 0,0 1,35 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "module-db/Interface/MultimediaFilesRecord.hpp"

#include <Common/Query.hpp>

namespace db::multimedia_files::query
{
    class GetOffsetByPath : public Query
    {
      public:
        GetOffsetByPath(const std::string &folderPath,
                        const std::string &recordPath,
                        SortingBy sorting = SortingBy::TitleAscending);
        [[nodiscard]] auto debugInfo() const -> std::string override;

        const std::string folderPath;
        const std::string recordPath;
        const SortingBy sorting = SortingBy::TitleAscending;
    };

    class GetOffsetResult : public QueryResult
    {
      public:
        explicit GetOffsetResult(SortedRecord sortedRecord);
        [[nodiscard]] auto getResult() const -> SortedRecord;
        [[nodiscard]] auto debugInfo() const -> std::string override;

      private:
        const SortedRecord sortedRecord;
    };
} // namespace db::multimedia_files::query

M module-db/tests/MultimediaFilesTable_tests.cpp => module-db/tests/MultimediaFilesTable_tests.cpp +20 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <catch2/catch.hpp>


@@ 12,6 12,7 @@
#include <queries/multimedia_files/QueryMultimediaFilesGetLimited.hpp>
#include <queries/multimedia_files/QueryMultimediaFilesRemove.hpp>
#include <queries/multimedia_files/QueryMultimediaFilesCount.hpp>
#include <queries/multimedia_files/QueryMultimediaFilesGetOffset.hpp>

#include <algorithm>
using namespace db::multimedia_files;


@@ 455,6 456,16 @@ TEST_CASE("Multimedia DB tests")
            return result->getCount();
        };

        auto getOffsetQuery = [&](const std::string &folderPath, const std::string &recordPath, SortingBy sorting) {
            auto query =
                std::make_shared<db::multimedia_files::query::GetOffsetByPath>(folderPath, recordPath, sorting);
            auto ret    = multimediaFilesRecordInterface.runQuery(query);
            auto result = dynamic_cast<db::multimedia_files::query::GetOffsetResult *>(ret.get());
            REQUIRE(result != nullptr);
            auto record = result->getResult();
            return record;
        };

        SECTION("add, get, remove query")
        {
            const auto path = "audio/user";


@@ 665,5 676,13 @@ TEST_CASE("Multimedia DB tests")
                }
            }
        }

        SECTION("Get offset by path")
        {
            REQUIRE(getOffsetQuery("user/audio", "user/audio/file1.mp3", SortingBy::TrackIdAscending).offset == 1);
            REQUIRE(getOffsetQuery("user/audio", "user/audio/file4.mp3", SortingBy::TrackIdAscending).offset == 4);
            REQUIRE(getOffsetQuery("user", "user/audio/file7.mp3", SortingBy::TrackIdAscending).offset == 7);
            REQUIRE(getOffsetQuery("user", "user/file3.mp3", SortingBy::TrackIdAscending).offset == 10);
        }
    }
}

M products/BellHybrid/apps/application-bell-settings/widgets/SongsListViewItem.cpp => products/BellHybrid/apps/application-bell-settings/widgets/SongsListViewItem.cpp +8 -2
@@ 43,10 43,16 @@ namespace gui
            onValueChange(record.fileInfo.path);
            return true;
        };
        auto onOffsetUpdateCallback = [this](std::uint32_t offset) {
            list->rebuildList(listview::RebuildType::OnPageElement, offset);
            return true;
        };

        setValue();
        this->songsModel->createData(onListItemActivate, onListItemFocusAcquire);
        list->rebuildList(listview::RebuildType::Full);
        this->songsModel->createData(std::move(onListItemActivate),
                                     std::move(onListItemFocusAcquire),
                                     this->settingsModel.getValue(),
                                     std::move(onOffsetUpdateCallback));
    }

    auto SongsListViewItem::value() const -> UTF8

M products/BellHybrid/apps/common/include/common/SoundsRepository.hpp => products/BellHybrid/apps/common/include/common/SoundsRepository.hpp +6 -2
@@ 40,10 40,11 @@ class AbstractSoundsRepository
  public:
    using OnGetMusicFilesListCallback =
        std::function<bool(const std::vector<db::multimedia_files::MultimediaFilesRecord> &, std::uint32_t)>;
    using OnOffsetUpdateCallback = std::function<bool(std::uint32_t offset)>;

    virtual ~AbstractSoundsRepository() noexcept = default;

    virtual void init()                                                     = 0;
    virtual void init(const std::string &savedPath, OnOffsetUpdateCallback offsetUpdateCallback) = 0;
    virtual void getMusicFiles(std::uint32_t offset,
                               std::uint32_t limit,
                               const OnGetMusicFilesListCallback &callback) = 0;


@@ 68,7 69,7 @@ class SoundsRepository : public AbstractSoundsRepository, public app::AsyncCallb

    SoundsRepository(app::ApplicationCommon *application, const PathSorting &path);
    SoundsRepository(app::ApplicationCommon *application, const std::vector<PathSorting> &pathsVector);
    void init() override;
    void init(const std::string &savedPath = "", OnOffsetUpdateCallback offsetUpdateCallback = nullptr) override;
    void getMusicFiles(std::uint32_t offset,
                       std::uint32_t limit,
                       const OnGetMusicFilesListCallback &viewUpdateCallback) override;


@@ 96,4 97,7 @@ class SoundsRepository : public AbstractSoundsRepository, public app::AsyncCallb
                       const std::uint32_t offset,
                       const std::uint32_t limit,
                       const OnGetMusicFilesListCallback &viewUpdateCallback);
    std::optional<PathDetails> getPathDetails(const std::string &songPath);
    void updateOffsetFromSavedPath(const std::string &savedPath, OnOffsetUpdateCallback offsetUpdateCallback);
    std::optional<std::uint32_t> calculateOffsetFromDB(std::uint32_t offset, const std::string songPath);
};

M products/BellHybrid/apps/common/include/common/models/SongsModel.hpp => products/BellHybrid/apps/common/include/common/models/SongsModel.hpp +9 -4
@@ 20,12 20,15 @@ namespace app
            std::function<bool(const db::multimedia_files::MultimediaFilesRecord &selectedSound)>;
        using OnFocusAcquireCallback =
            std::function<bool(const db::multimedia_files::MultimediaFilesRecord &focusedSound)>;
        using OnOffsetUpdateCallback = AbstractSoundsRepository::OnOffsetUpdateCallback;

        explicit SongsProvider(ApplicationCommon *application);
        virtual ~SongsProvider() = default;

        virtual auto createData(OnActivateCallback activateCallback, OnFocusAcquireCallback focusChangeCallback)
            -> void = 0;
        virtual auto createData(OnActivateCallback activateCallback,
                                OnFocusAcquireCallback focusChangeCallback,
                                const std::string &savedPath,
                                OnOffsetUpdateCallback offsetUpdateCallback) -> void = 0;
    };

    class SongsModel : public SongsProvider


@@ 44,8 47,10 @@ namespace app

        auto requestRecords(std::uint32_t offset, std::uint32_t limit) -> void override;

        auto createData(OnActivateCallback activateCallback        = nullptr,
                        OnFocusAcquireCallback focusChangeCallback = nullptr) -> void override;
        auto createData(OnActivateCallback activateCallback         = nullptr,
                        OnFocusAcquireCallback focusChangeCallback  = nullptr,
                        const std::string &savedPath                = "",
                        OnOffsetUpdateCallback offsetUpdateCallback = nullptr) -> void override;
        auto updateRecordsCount() -> void;
        auto nextRecordExists(gui::Order order) -> bool;


M products/BellHybrid/apps/common/src/SoundsRepository.cpp => products/BellHybrid/apps/common/src/SoundsRepository.cpp +58 -1
@@ 5,6 5,7 @@

#include <module-db/queries/multimedia_files/QueryMultimediaFilesGetLimited.hpp>
#include <module-db/queries/multimedia_files/QueryMultimediaFilesCount.hpp>
#include <module-db/queries/multimedia_files/QueryMultimediaFilesGetOffset.hpp>
#include <AsyncTask.hpp>
#include <algorithm>



@@ 13,6 14,7 @@ namespace fs = std::filesystem;
namespace
{
    constexpr auto allowedExtensions = {".wav", ".mp3", ".flac"};
    constexpr auto defaultOffset{0};

    constexpr std::uint32_t calculateOffset(std::uint32_t offset, std::uint32_t filesCount)
    {


@@ 107,9 109,10 @@ SoundsRepository::SoundsRepository(app::ApplicationCommon *application, const st
    }
}

void SoundsRepository::init()
void SoundsRepository::init(const std::string &savedPath, OnOffsetUpdateCallback offsetUpdateCallback)
{
    updateFilesCount();
    updateOffsetFromSavedPath(savedPath, std::move(offsetUpdateCallback));
}

void SoundsRepository::getMusicFiles(std::uint32_t offset,


@@ 212,3 215,57 @@ bool SoundsRepository::updateCountCallback(const std::uint32_t id, const std::ui
    }
    return false;
}

void SoundsRepository::updateOffsetFromSavedPath(const std::string &savedPath,
                                                 OnOffsetUpdateCallback offsetUpdateCallback)
{
    if (offsetUpdateCallback == nullptr) {
        return;
    }
    const auto path = getPathDetails(savedPath);
    if (!path.has_value()) {
        offsetUpdateCallback(defaultOffset);
        return;
    }

    auto query = std::make_unique<db::multimedia_files::query::GetOffsetByPath>(
        path->prefix, savedPath, transformSorting(path->sorting));
    auto task = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::MultimediaFiles);
    task->setCallback([this, offsetUpdateCallback, savedPath](auto response) {
        auto result = dynamic_cast<db::multimedia_files::query::GetOffsetResult *>(response);
        if (result == nullptr || result->getResult().path != savedPath) {
            offsetUpdateCallback(defaultOffset);
            return false;
        }
        const auto calculatedOffset = calculateOffsetFromDB(result->getResult().offset, savedPath);
        offsetUpdateCallback(calculatedOffset.has_value() ? calculatedOffset.value() : defaultOffset);
        return true;
    });
    task->execute(application, this);
}

std::optional<SoundsRepository::PathDetails> SoundsRepository::getPathDetails(const std::string &songPath)
{
    for (const auto &path : paths) {
        if (songPath.find(path.prefix) != std::string::npos) {
            return path;
        }
    }
    return std::nullopt;
}

std::optional<std::uint32_t> SoundsRepository::calculateOffsetFromDB(std::uint32_t offset, const std::string songPath)
{
    // the offset returned by the database is incremented from 1,
    // while the offset used in listView is from 0
    if (offset > 0) {
        offset--;
    }
    for (const auto &path : paths) {
        if (songPath.find(path.prefix) != std::string::npos) {
            return offset;
        }
        offset += path.count;
    }
    return std::nullopt;
}

M products/BellHybrid/apps/common/src/models/SongsModel.cpp => products/BellHybrid/apps/common/src/models/SongsModel.cpp +6 -3
@@ 63,11 63,14 @@ namespace app
        });
    }

    auto SongsModel::createData(OnActivateCallback onActivate, OnFocusAcquireCallback onFocusAcquire) -> void
    auto SongsModel::createData(OnActivateCallback onActivate,
                                OnFocusAcquireCallback onFocusAcquire,
                                const std::string &savedPath,
                                OnOffsetUpdateCallback offsetUpdateCallback) -> void
    {
        activateCallback     = std::move(onActivate);
        activateCallback    = std::move(onActivate);
        focusAcquireCallback = std::move(onFocusAcquire);
        songsRepository->init();
        songsRepository->init(savedPath, std::move(offsetUpdateCallback));
    }

    auto SongsModel::updateRecordsCount() -> void