feat: add directory picker for OPDS downloads with per-server default path

When downloading a book via OPDS, a directory picker now lets the user
choose the save location instead of always saving to the SD root. Each
OPDS server has a configurable default download path (persisted in
opds.json) that the picker opens to. Falls back to "/" if the saved
path no longer exists on disk.

- Add DirectoryPickerActivity (browse-only directory view with "Save Here")
- Add PICKING_DIRECTORY state to OpdsBookBrowserActivity
- Add downloadPath field to OpdsServer with JSON serialization
- Add Download Path setting to OPDS server edit screen
- Extract sortFileList() to StringUtils for shared use
- Add i18n strings: STR_SAVE_HERE, STR_SELECT_FOLDER, STR_DOWNLOAD_PATH

Made-with: Cursor
This commit is contained in:
cottongin
2026-03-02 04:28:57 -05:00
parent 2aa13ea2de
commit 42011d5977
20 changed files with 363 additions and 72 deletions

View File

@@ -17,12 +17,13 @@
class OpdsBookBrowserActivity final : public ActivityWithSubactivity {
public:
enum class BrowserState {
CHECK_WIFI, // Checking WiFi connection
WIFI_SELECTION, // WiFi selection subactivity is active
LOADING, // Fetching OPDS feed
BROWSING, // Displaying entries (navigation or books)
DOWNLOADING, // Downloading selected EPUB
ERROR // Error state with message
CHECK_WIFI, // Checking WiFi connection
WIFI_SELECTION, // WiFi selection subactivity is active
LOADING, // Fetching OPDS feed
BROWSING, // Displaying entries (navigation or books)
PICKING_DIRECTORY, // Directory picker subactivity is active
DOWNLOADING, // Downloading selected EPUB
ERROR // Error state with message
};
explicit OpdsBookBrowserActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
@@ -55,6 +56,11 @@ class OpdsBookBrowserActivity final : public ActivityWithSubactivity {
void fetchFeed(const std::string& path);
void navigateToEntry(const OpdsEntry& entry);
void navigateBack();
void downloadBook(const OpdsEntry& book);
void launchDirectoryPicker(const OpdsEntry& book);
void onDirectorySelected(const std::string& directory);
void onDirectoryPickerCancelled();
void downloadBook(const OpdsEntry& book, const std::string& directory);
bool preventAutoSleep() override { return true; }
OpdsEntry pendingBook;
};