From ee8e5234562c2b9ee1bb261785135abd4f718f83 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Wed, 4 Oct 2023 15:38:18 +1100 Subject: [PATCH] Add a basic database reindex screen --- src/database/database.cpp | 20 +++++++++---- src/database/file_gatherer.cpp | 12 ++++++-- src/database/include/db_events.hpp | 2 ++ src/ui/include/modal_progress.hpp | 7 ++++- src/ui/include/ui_events.hpp | 1 + src/ui/include/ui_fsm.hpp | 19 ++++++++++++ src/ui/modal_progress.cpp | 29 ++++++++++++++++--- src/ui/screen_settings.cpp | 6 +++- src/ui/ui_fsm.cpp | 46 ++++++++++++++++++++++++++++++ 9 files changed, 128 insertions(+), 14 deletions(-) diff --git a/src/database/database.cpp b/src/database/database.cpp index 1ecd72e0..ad81cfcf 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -136,14 +136,18 @@ auto Database::Update() -> std::future { // Stage 1: verify all existing tracks are still valid. ESP_LOGI(kTag, "verifying existing tracks"); - events::Ui().Dispatch(event::UpdateProgress{ - .stage = event::UpdateProgress::Stage::kVerifyingExistingTracks, - }); { + uint64_t num_processed = 0; std::unique_ptr it{db_->NewIterator(read_options)}; OwningSlice prefix = EncodeDataPrefix(); it->Seek(prefix.slice); while (it->Valid() && it->key().starts_with(prefix.slice)) { + num_processed++; + events::Ui().Dispatch(event::UpdateProgress{ + .stage = event::UpdateProgress::Stage::kVerifyingExistingTracks, + .val = num_processed, + }); + std::shared_ptr track = ParseDataValue(it->value()); if (!track) { // The value was malformed. Drop this record. @@ -195,10 +199,14 @@ auto Database::Update() -> std::future { // Stage 2: search for newly added files. ESP_LOGI(kTag, "scanning for new tracks"); - events::Ui().Dispatch(event::UpdateProgress{ - .stage = event::UpdateProgress::Stage::kScanningForNewTracks, - }); + uint64_t num_processed = 0; file_gatherer_.FindFiles("", [&](const std::pmr::string& path) { + num_processed++; + events::Ui().Dispatch(event::UpdateProgress{ + .stage = event::UpdateProgress::Stage::kScanningForNewTracks, + .val = num_processed, + }); + std::shared_ptr tags = tag_parser_.ReadAndParseTags(path); if (!tags || tags->encoding() == Container::kUnsupported) { // No parseable tags; skip this fiile. diff --git a/src/database/file_gatherer.cpp b/src/database/file_gatherer.cpp index d8c80b0d..b4e87acb 100644 --- a/src/database/file_gatherer.cpp +++ b/src/database/file_gatherer.cpp @@ -14,6 +14,7 @@ #include "ff.h" #include "memory_resource.hpp" +#include "spi.hpp" namespace database { @@ -30,7 +31,11 @@ auto FileGathererImpl::FindFiles( const TCHAR* next_path = static_cast(next_path_str.c_str()); FF_DIR dir; - FRESULT res = f_opendir(&dir, next_path); + FRESULT res; + { + auto lock = drivers::acquire_spi(); + res = f_opendir(&dir, next_path); + } if (res != FR_OK) { // TODO: log. continue; @@ -38,7 +43,10 @@ auto FileGathererImpl::FindFiles( for (;;) { FILINFO info; - res = f_readdir(&dir, &info); + { + auto lock = drivers::acquire_spi(); + res = f_readdir(&dir, &info); + } if (res != FR_OK || info.fname[0] == 0) { // No more files in the directory. break; diff --git a/src/database/include/db_events.hpp b/src/database/include/db_events.hpp index c071c6d4..a1aefc27 100644 --- a/src/database/include/db_events.hpp +++ b/src/database/include/db_events.hpp @@ -6,6 +6,7 @@ #pragma once +#include #include "tinyfsm.hpp" namespace database { @@ -21,6 +22,7 @@ struct UpdateProgress : tinyfsm::Event { kScanningForNewTracks, }; Stage stage; + uint64_t val; }; } // namespace event diff --git a/src/ui/include/modal_progress.hpp b/src/ui/include/modal_progress.hpp index f312d509..2ccb671a 100644 --- a/src/ui/include/modal_progress.hpp +++ b/src/ui/include/modal_progress.hpp @@ -19,10 +19,15 @@ namespace modals { class Progress : public Modal { public: - Progress(Screen*, std::pmr::string title); + Progress(Screen*, std::pmr::string title, std::pmr::string subtitle = ""); + + void title(const std::pmr::string&); + void subtitle(const std::pmr::string&); private: lv_obj_t* container_; + lv_obj_t* title_; + lv_obj_t* subtitle_; }; } // namespace modals diff --git a/src/ui/include/ui_events.hpp b/src/ui/include/ui_events.hpp index e08f8888..4549a51d 100644 --- a/src/ui/include/ui_events.hpp +++ b/src/ui/include/ui_events.hpp @@ -37,6 +37,7 @@ struct IndexSelected : tinyfsm::Event { }; struct ControlSchemeChanged : tinyfsm::Event {}; +struct ReindexDatabase : tinyfsm::Event {}; struct BackPressed : tinyfsm::Event {}; struct ShowNowPlaying : tinyfsm::Event {}; diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp index eef96b50..da63b77f 100644 --- a/src/ui/include/ui_fsm.hpp +++ b/src/ui/include/ui_fsm.hpp @@ -14,6 +14,7 @@ #include "audio_events.hpp" #include "battery.hpp" #include "bindey/property.h" +#include "db_events.hpp" #include "gpios.hpp" #include "lvgl_task.hpp" #include "model_playback.hpp" @@ -75,6 +76,11 @@ class UiState : public tinyfsm::Fsm { } virtual void react(const internal::OnboardingNavigate&) {} void react(const internal::ControlSchemeChanged&); + virtual void react(const internal::ReindexDatabase&){}; + + virtual void react(const database::event::UpdateStarted&){}; + virtual void react(const database::event::UpdateProgress&){}; + virtual void react(const database::event::UpdateFinished&){}; virtual void react(const system_fsm::DisplayReady&) {} virtual void react(const system_fsm::BootComplete&) {} @@ -130,6 +136,7 @@ class Browse : public UiState { void react(const internal::ShowNowPlaying&) override; void react(const internal::ShowSettingsPage&) override; + void react(const internal::ReindexDatabase&) override; void react(const system_fsm::StorageMounted&) override; void react(const system_fsm::BluetoothDevicesChanged&) override; @@ -150,6 +157,18 @@ class Playing : public UiState { using UiState::react; }; +class Indexing : public UiState { + public: + void entry() override; + void exit() override; + + void react(const database::event::UpdateStarted&) override; + void react(const database::event::UpdateProgress&) override; + void react(const database::event::UpdateFinished&) override; + + using UiState::react; +}; + class FatalError : public UiState {}; } // namespace states diff --git a/src/ui/modal_progress.cpp b/src/ui/modal_progress.cpp index 1213de7e..d3d3e9b9 100644 --- a/src/ui/modal_progress.cpp +++ b/src/ui/modal_progress.cpp @@ -28,18 +28,39 @@ namespace ui { namespace modals { -Progress::Progress(Screen* host, std::pmr::string title_text) : Modal(host) { +Progress::Progress(Screen* host, + std::pmr::string title_text, + std::pmr::string subtitle_text) + : Modal(host) { lv_obj_set_layout(root_, LV_LAYOUT_FLEX); lv_obj_set_flex_flow(root_, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_align(root_, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - lv_obj_t* title = lv_label_create(root_); - lv_label_set_text(title, title_text.c_str()); - lv_obj_set_size(title, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + title_ = lv_label_create(root_); + lv_obj_set_size(title_, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + + subtitle_ = lv_label_create(root_); + lv_obj_set_size(subtitle_, LV_SIZE_CONTENT, LV_SIZE_CONTENT); lv_obj_t* spinner = lv_spinner_create(root_, 3000, 45); lv_obj_set_size(spinner, 16, 16); + + title(title_text); + subtitle(subtitle_text); +} + +void Progress::title(const std::pmr::string& s) { + lv_label_set_text(title_, s.c_str()); +} + +void Progress::subtitle(const std::pmr::string& s) { + if (s.empty()) { + lv_obj_add_flag(subtitle_, LV_OBJ_FLAG_HIDDEN); + } else { + lv_obj_clear_flag(subtitle_, LV_OBJ_FLAG_HIDDEN); + lv_label_set_text(subtitle_, s.c_str()); + } } } // namespace modals diff --git a/src/ui/screen_settings.cpp b/src/ui/screen_settings.cpp index 6ee8405e..a661392f 100644 --- a/src/ui/screen_settings.cpp +++ b/src/ui/screen_settings.cpp @@ -469,8 +469,12 @@ Storage::Storage(models::TopBar& bar) : MenuScreen(bar, "Storage") { lv_obj_t* reset_btn = lv_btn_create(content_); lv_obj_t* reset_label = lv_label_create(reset_btn); - lv_label_set_text(reset_label, "Reset Database"); + lv_label_set_text(reset_label, "Update Database"); lv_group_add_obj(group_, reset_btn); + + lv_bind(reset_btn, LV_EVENT_CLICKED, [&](lv_obj_t*) { + events::Ui().Dispatch(internal::ReindexDatabase{}); + }); } FirmwareUpdate::FirmwareUpdate(models::TopBar& bar) diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp index f8c9d3f3..b5a0fa34 100644 --- a/src/ui/ui_fsm.cpp +++ b/src/ui/ui_fsm.cpp @@ -21,6 +21,7 @@ #include "lvgl_task.hpp" #include "modal_add_to_queue.hpp" #include "modal_confirm.hpp" +#include "modal_progress.hpp" #include "model_playback.hpp" #include "nvs.hpp" #include "relative_wheel.hpp" @@ -328,6 +329,10 @@ void Browse::react(const system_fsm::BluetoothDevicesChanged&) { } } +void Browse::react(const internal::ReindexDatabase& ev) { + transit(); +} + static std::shared_ptr sPlayingScreen; void Playing::entry() { @@ -347,6 +352,47 @@ void Playing::react(const internal::BackPressed& ev) { transit(); } +static std::shared_ptr sIndexProgress; + +void Indexing::entry() { + sIndexProgress.reset(new modals::Progress(sCurrentScreen.get(), "Indexing", + "Preparing database")); + sCurrentModal = sIndexProgress; + auto db = sServices->database().lock(); + if (!db) { + // TODO: Hmm. + return; + } + db->Update(); +} + +void Indexing::exit() { + sCurrentModal.reset(); + sIndexProgress.reset(); +} + +void Indexing::react(const database::event::UpdateStarted&) {} + +void Indexing::react(const database::event::UpdateProgress& ev) { + std::ostringstream str; + switch (ev.stage) { + case database::event::UpdateProgress::Stage::kVerifyingExistingTracks: + sIndexProgress->title("Verifying"); + str << "Tracks checked: " << ev.val; + sIndexProgress->subtitle(str.str().c_str()); + break; + case database::event::UpdateProgress::Stage::kScanningForNewTracks: + sIndexProgress->title("Scanning"); + str << "Files checked: " << ev.val; + sIndexProgress->subtitle(str.str().c_str()); + break; + } +} + +void Indexing::react(const database::event::UpdateFinished&) { + transit(); +} + } // namespace states } // namespace ui