From e5671275c31755514e9506a9d7e9ee44958d90c7 Mon Sep 17 00:00:00 2001 From: Kenneth Date: Wed, 14 Jan 2026 09:38:05 -0500 Subject: [PATCH] Remove FileSelectionActivity, Go back to Library, File extension updates --- src/activities/home/MyLibraryActivity.cpp | 25 ++- src/activities/home/MyLibraryActivity.h | 4 +- .../reader/FileSelectionActivity.cpp | 209 ------------------ src/activities/reader/FileSelectionActivity.h | 40 ---- src/activities/reader/ReaderActivity.cpp | 19 +- src/activities/reader/ReaderActivity.h | 9 +- src/main.cpp | 9 +- 7 files changed, 43 insertions(+), 272 deletions(-) delete mode 100644 src/activities/reader/FileSelectionActivity.cpp delete mode 100644 src/activities/reader/FileSelectionActivity.h diff --git a/src/activities/home/MyLibraryActivity.cpp b/src/activities/home/MyLibraryActivity.cpp index 60b7ad35..81ab1049 100644 --- a/src/activities/home/MyLibraryActivity.cpp +++ b/src/activities/home/MyLibraryActivity.cpp @@ -9,6 +9,7 @@ #include "RecentBooksStore.h" #include "ScreenComponents.h" #include "fontIds.h" +#include "util/StringUtils.h" namespace { // Layout constants @@ -106,7 +107,7 @@ void MyLibraryActivity::loadFiles() { root.rewindDirectory(); - char name[128]; + char name[500]; for (auto file = root.openNextFile(); file; file = root.openNextFile()) { file.getName(name, sizeof(name)); if (name[0] == '.' || strcmp(name, "System Volume Information") == 0) { @@ -118,9 +119,8 @@ void MyLibraryActivity::loadFiles() { files.emplace_back(std::string(name) + "/"); } else { auto filename = std::string(name); - std::string ext4 = filename.length() >= 4 ? filename.substr(filename.length() - 4) : ""; - std::string ext5 = filename.length() >= 5 ? filename.substr(filename.length() - 5) : ""; - if (ext5 == ".epub" || ext5 == ".xtch" || ext4 == ".xtc") { + if (StringUtils::checkFileExtension(filename, ".epub") || StringUtils::checkFileExtension(filename, ".xtch") || + StringUtils::checkFileExtension(filename, ".xtc") || StringUtils::checkFileExtension(filename, ".txt")) { files.emplace_back(filename); } } @@ -130,6 +130,13 @@ void MyLibraryActivity::loadFiles() { sortFileList(files); } +size_t MyLibraryActivity::findEntry(const std::string& name) const { + for (size_t i = 0; i < files.size(); i++) { + if (files[i] == name) return i; + } + return 0; +} + void MyLibraryActivity::taskTrampoline(void* param) { auto* self = static_cast(param); self->displayTaskLoop(); @@ -225,11 +232,17 @@ void MyLibraryActivity::loop() { if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { if (mappedInput.getHeldTime() < GO_HOME_MS) { if (currentTab == Tab::Files && basepath != "/") { - // Go up one directory + // Go up one directory, remembering the directory we came from + const std::string oldPath = basepath; basepath.replace(basepath.find_last_of('/'), std::string::npos, ""); if (basepath.empty()) basepath = "/"; loadFiles(); - selectorIndex = 0; + + // Select the directory we just came from + const auto pos = oldPath.find_last_of('/'); + const std::string dirName = oldPath.substr(pos + 1) + "/"; + selectorIndex = static_cast(findEntry(dirName)); + updateRequired = true; } else { // Go home diff --git a/src/activities/home/MyLibraryActivity.h b/src/activities/home/MyLibraryActivity.h index e6de28ca..896209b4 100644 --- a/src/activities/home/MyLibraryActivity.h +++ b/src/activities/home/MyLibraryActivity.h @@ -42,6 +42,7 @@ class MyLibraryActivity final : public Activity { // Data loading void loadRecentBooks(); void loadFiles(); + size_t findEntry(const std::string& name) const; // Rendering static void taskTrampoline(void* param); @@ -54,9 +55,10 @@ class MyLibraryActivity final : public Activity { explicit MyLibraryActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::function& onGoHome, const std::function& onSelectBook, - Tab initialTab = Tab::Recent) + Tab initialTab = Tab::Recent, std::string initialPath = "/") : Activity("MyLibrary", renderer, mappedInput), currentTab(initialTab), + basepath(initialPath.empty() ? "/" : std::move(initialPath)), onGoHome(onGoHome), onSelectBook(onSelectBook) {} void onEnter() override; diff --git a/src/activities/reader/FileSelectionActivity.cpp b/src/activities/reader/FileSelectionActivity.cpp deleted file mode 100644 index 33c2c3e4..00000000 --- a/src/activities/reader/FileSelectionActivity.cpp +++ /dev/null @@ -1,209 +0,0 @@ -#include "FileSelectionActivity.h" - -#include -#include - -#include "MappedInputManager.h" -#include "fontIds.h" -#include "util/StringUtils.h" - -namespace { -constexpr int PAGE_ITEMS = 23; -constexpr int SKIP_PAGE_MS = 700; -constexpr unsigned long GO_HOME_MS = 1000; -} // namespace - -void sortFileList(std::vector& strs) { - std::sort(begin(strs), end(strs), [](const std::string& str1, const std::string& str2) { - if (str1.back() == '/' && str2.back() != '/') return true; - if (str1.back() != '/' && str2.back() == '/') return false; - return lexicographical_compare( - begin(str1), end(str1), begin(str2), end(str2), - [](const char& char1, const char& char2) { return tolower(char1) < tolower(char2); }); - }); -} - -void FileSelectionActivity::taskTrampoline(void* param) { - auto* self = static_cast(param); - self->displayTaskLoop(); -} - -void FileSelectionActivity::loadFiles() { - files.clear(); - - auto root = SdMan.open(basepath.c_str()); - if (!root || !root.isDirectory()) { - if (root) root.close(); - return; - } - - root.rewindDirectory(); - - char name[500]; - for (auto file = root.openNextFile(); file; file = root.openNextFile()) { - file.getName(name, sizeof(name)); - if (name[0] == '.' || strcmp(name, "System Volume Information") == 0) { - file.close(); - continue; - } - - if (file.isDirectory()) { - files.emplace_back(std::string(name) + "/"); - } else { - auto filename = std::string(name); - if (StringUtils::checkFileExtension(filename, ".epub") || StringUtils::checkFileExtension(filename, ".xtch") || - StringUtils::checkFileExtension(filename, ".xtc")) { - files.emplace_back(filename); - } - } - file.close(); - } - root.close(); - sortFileList(files); -} - -void FileSelectionActivity::onEnter() { - Activity::onEnter(); - - renderingMutex = xSemaphoreCreateMutex(); - - // basepath is set via constructor parameter (defaults to "/" if not specified) - loadFiles(); - selectorIndex = 0; - - // Trigger first update - updateRequired = true; - - xTaskCreate(&FileSelectionActivity::taskTrampoline, "FileSelectionActivityTask", - 2048, // Stack size - this, // Parameters - 1, // Priority - &displayTaskHandle // Task handle - ); -} - -void FileSelectionActivity::onExit() { - Activity::onExit(); - - // Wait until not rendering to delete task to avoid killing mid-instruction to EPD - xSemaphoreTake(renderingMutex, portMAX_DELAY); - if (displayTaskHandle) { - vTaskDelete(displayTaskHandle); - displayTaskHandle = nullptr; - } - vSemaphoreDelete(renderingMutex); - renderingMutex = nullptr; - files.clear(); -} - -void FileSelectionActivity::loop() { - // Long press BACK (1s+) goes to root folder - if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= GO_HOME_MS) { - if (basepath != "/") { - basepath = "/"; - loadFiles(); - updateRequired = true; - } - return; - } - - const bool prevReleased = mappedInput.wasReleased(MappedInputManager::Button::Up) || - mappedInput.wasReleased(MappedInputManager::Button::Left); - const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::Down) || - mappedInput.wasReleased(MappedInputManager::Button::Right); - - const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS; - - if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { - if (files.empty()) { - return; - } - - if (basepath.back() != '/') basepath += "/"; - if (files[selectorIndex].back() == '/') { - basepath += files[selectorIndex].substr(0, files[selectorIndex].length() - 1); - loadFiles(); - selectorIndex = 0; - updateRequired = true; - } else { - onSelect(basepath + files[selectorIndex]); - } - } else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { - // Short press: go up one directory, or go home if at root - if (mappedInput.getHeldTime() < GO_HOME_MS) { - if (basepath != "/") { - const std::string oldPath = basepath; - - basepath.replace(basepath.find_last_of('/'), std::string::npos, ""); - if (basepath.empty()) basepath = "/"; - loadFiles(); - - const auto pos = oldPath.find_last_of('/'); - const std::string dirName = oldPath.substr(pos + 1) + "/"; - selectorIndex = findEntry(dirName); - - updateRequired = true; - } else { - onGoHome(); - } - } - } else if (prevReleased) { - if (skipPage) { - selectorIndex = ((selectorIndex / PAGE_ITEMS - 1) * PAGE_ITEMS + files.size()) % files.size(); - } else { - selectorIndex = (selectorIndex + files.size() - 1) % files.size(); - } - updateRequired = true; - } else if (nextReleased) { - if (skipPage) { - selectorIndex = ((selectorIndex / PAGE_ITEMS + 1) * PAGE_ITEMS) % files.size(); - } else { - selectorIndex = (selectorIndex + 1) % files.size(); - } - updateRequired = true; - } -} - -void FileSelectionActivity::displayTaskLoop() { - while (true) { - if (updateRequired) { - updateRequired = false; - xSemaphoreTake(renderingMutex, portMAX_DELAY); - render(); - xSemaphoreGive(renderingMutex); - } - vTaskDelay(10 / portTICK_PERIOD_MS); - } -} - -void FileSelectionActivity::render() const { - renderer.clearScreen(); - - const auto pageWidth = renderer.getScreenWidth(); - renderer.drawCenteredText(UI_12_FONT_ID, 15, "Books", true, EpdFontFamily::BOLD); - - // Help text - const auto labels = mappedInput.mapLabels("« Home", "Open", "", ""); - renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); - - if (files.empty()) { - renderer.drawText(UI_10_FONT_ID, 20, 60, "No books found"); - renderer.displayBuffer(); - return; - } - - const auto pageStartIndex = selectorIndex / PAGE_ITEMS * PAGE_ITEMS; - renderer.fillRect(0, 60 + (selectorIndex % PAGE_ITEMS) * 30 - 2, pageWidth - 1, 30); - for (size_t i = pageStartIndex; i < files.size() && i < pageStartIndex + PAGE_ITEMS; i++) { - auto item = renderer.truncatedText(UI_10_FONT_ID, files[i].c_str(), renderer.getScreenWidth() - 40); - renderer.drawText(UI_10_FONT_ID, 20, 60 + (i % PAGE_ITEMS) * 30, item.c_str(), i != selectorIndex); - } - - renderer.displayBuffer(); -} - -size_t FileSelectionActivity::findEntry(const std::string& name) const { - for (size_t i = 0; i < files.size(); i++) - if (files[i] == name) return i; - return 0; -} diff --git a/src/activities/reader/FileSelectionActivity.h b/src/activities/reader/FileSelectionActivity.h deleted file mode 100644 index 3c71968a..00000000 --- a/src/activities/reader/FileSelectionActivity.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once -#include -#include -#include - -#include -#include -#include - -#include "../Activity.h" - -class FileSelectionActivity final : public Activity { - TaskHandle_t displayTaskHandle = nullptr; - SemaphoreHandle_t renderingMutex = nullptr; - std::string basepath = "/"; - std::vector files; - size_t selectorIndex = 0; - bool updateRequired = false; - const std::function onSelect; - const std::function onGoHome; - - static void taskTrampoline(void* param); - [[noreturn]] void displayTaskLoop(); - void render() const; - void loadFiles(); - - size_t findEntry(const std::string& name) const; - - public: - explicit FileSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, - const std::function& onSelect, - const std::function& onGoHome, std::string initialPath = "/") - : Activity("FileSelection", renderer, mappedInput), - basepath(initialPath.empty() ? "/" : std::move(initialPath)), - onSelect(onSelect), - onGoHome(onGoHome) {} - void onEnter() override; - void onExit() override; - void loop() override; -}; diff --git a/src/activities/reader/ReaderActivity.cpp b/src/activities/reader/ReaderActivity.cpp index cb123e1c..4922a739 100644 --- a/src/activities/reader/ReaderActivity.cpp +++ b/src/activities/reader/ReaderActivity.cpp @@ -2,7 +2,6 @@ #include "Epub.h" #include "EpubReaderActivity.h" -#include "FileSelectionActivity.h" #include "Xtc.h" #include "XtcReaderActivity.h" #include "activities/util/FullScreenMessageActivity.h" @@ -65,7 +64,7 @@ void ReaderActivity::onSelectBookFile(const std::string& path) { enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Failed to load XTC", EpdFontFamily::REGULAR, EInkDisplay::HALF_REFRESH)); delay(2000); - onGoToFileSelection(); + goToLibrary(); } } else { // Load EPUB file @@ -77,17 +76,15 @@ void ReaderActivity::onSelectBookFile(const std::string& path) { enterNewActivity(new FullScreenMessageActivity(renderer, mappedInput, "Failed to load epub", EpdFontFamily::REGULAR, EInkDisplay::HALF_REFRESH)); delay(2000); - onGoToFileSelection(); + goToLibrary(); } } } -void ReaderActivity::onGoToFileSelection(const std::string& fromBookPath) { - exitActivity(); +void ReaderActivity::goToLibrary(const std::string& fromBookPath) { // If coming from a book, start in that book's folder; otherwise start from root const auto initialPath = fromBookPath.empty() ? "/" : extractFolderPath(fromBookPath); - enterNewActivity(new FileSelectionActivity( - renderer, mappedInput, [this](const std::string& path) { onSelectBookFile(path); }, onGoBack, initialPath)); + onGoToLibrary(initialPath); } void ReaderActivity::onGoToEpubReader(std::unique_ptr epub) { @@ -95,8 +92,7 @@ void ReaderActivity::onGoToEpubReader(std::unique_ptr epub) { currentBookPath = epubPath; exitActivity(); enterNewActivity(new EpubReaderActivity( - renderer, mappedInput, std::move(epub), [this, epubPath] { onGoToFileSelection(epubPath); }, - [this] { onGoBack(); })); + renderer, mappedInput, std::move(epub), [this, epubPath] { goToLibrary(epubPath); }, [this] { onGoBack(); })); } void ReaderActivity::onGoToXtcReader(std::unique_ptr xtc) { @@ -104,15 +100,14 @@ void ReaderActivity::onGoToXtcReader(std::unique_ptr xtc) { currentBookPath = xtcPath; exitActivity(); enterNewActivity(new XtcReaderActivity( - renderer, mappedInput, std::move(xtc), [this, xtcPath] { onGoToFileSelection(xtcPath); }, - [this] { onGoBack(); })); + renderer, mappedInput, std::move(xtc), [this, xtcPath] { goToLibrary(xtcPath); }, [this] { onGoBack(); })); } void ReaderActivity::onEnter() { ActivityWithSubactivity::onEnter(); if (initialBookPath.empty()) { - onGoToFileSelection(); // Start from root when entering via Browse + goToLibrary(); // Start from root when entering via Browse return; } diff --git a/src/activities/reader/ReaderActivity.h b/src/activities/reader/ReaderActivity.h index df44afe5..2aa557ba 100644 --- a/src/activities/reader/ReaderActivity.h +++ b/src/activities/reader/ReaderActivity.h @@ -10,21 +10,24 @@ class ReaderActivity final : public ActivityWithSubactivity { std::string initialBookPath; std::string currentBookPath; // Track current book path for navigation const std::function onGoBack; + const std::function onGoToLibrary; static std::unique_ptr loadEpub(const std::string& path); static std::unique_ptr loadXtc(const std::string& path); static bool isXtcFile(const std::string& path); static std::string extractFolderPath(const std::string& filePath); void onSelectBookFile(const std::string& path); - void onGoToFileSelection(const std::string& fromBookPath = ""); + void goToLibrary(const std::string& fromBookPath = ""); void onGoToEpubReader(std::unique_ptr epub); void onGoToXtcReader(std::unique_ptr xtc); public: explicit ReaderActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string initialBookPath, - const std::function& onGoBack) + const std::function& onGoBack, + const std::function& onGoToLibrary) : ActivityWithSubactivity("Reader", renderer, mappedInput), initialBookPath(std::move(initialBookPath)), - onGoBack(onGoBack) {} + onGoBack(onGoBack), + onGoToLibrary(onGoToLibrary) {} void onEnter() override; }; diff --git a/src/main.cpp b/src/main.cpp index 25f23512..05e41230 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -210,9 +210,10 @@ void enterDeepSleep() { } void onGoHome(); +void onGoToMyLibraryAtPath(const std::string& path); void onGoToReader(const std::string& initialEpubPath) { exitActivity(); - enterNewActivity(new ReaderActivity(renderer, mappedInputManager, initialEpubPath, onGoHome)); + enterNewActivity(new ReaderActivity(renderer, mappedInputManager, initialEpubPath, onGoHome, onGoToMyLibraryAtPath)); } void onContinueReading() { onGoToReader(APP_STATE.openEpubPath); } @@ -231,6 +232,12 @@ void onGoToMyLibrary() { enterNewActivity(new MyLibraryActivity(renderer, mappedInputManager, onGoHome, onGoToReader)); } +void onGoToMyLibraryAtPath(const std::string& path) { + exitActivity(); + enterNewActivity( + new MyLibraryActivity(renderer, mappedInputManager, onGoHome, onGoToReader, MyLibraryActivity::Tab::Files, path)); +} + void onGoToBrowser() { exitActivity(); enterNewActivity(new OpdsBookBrowserActivity(renderer, mappedInputManager, onGoHome));