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