From 2f342508bc4365490c56718274e8d1ba6fda38cb Mon Sep 17 00:00:00 2001 From: WuTofu <5987870+WuTofu@users.noreply.github.com> Date: Sat, 16 May 2026 10:25:48 +0800 Subject: [PATCH] fix: several QoL updates for SD font's UI (#1965) ## Summary * **What is the goal of this PR?** Improve the UI based on feedback from someone on discord > Downloading ALL fonts feature. > 1.1 Disable sleep when downloading, in my case went directly to sleep just right after downloading. > 1.2 It would be great to have and overall progress indicator as we only have the indication of each font family > 1.3 Any cancel or pause function might come in handy in case battery is running out and then resume or retry with pending fonts * **What changes are included?** - Now the UI can show overall progress across every file being downloaded in the batch, not just progress inside the current family. - Extended `HttpDownloader::downloadToFile()` to accept a cancel flag and abort the download. - Rendered a cancel button in the font download UI while a download is in progress. - `preventAutoSleep()` in `FontDownloadActivity.h` now returns true for `state_ == COMPLETE` and `state_ == ERROR` in addition to `LOADING_MANIFEST` and `DOWNLOADING` ## Additional Context Not very satisfied with how `HttpDownloader.cpp` is right now, might try to refactor it after v1.3.0 --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**PARTIALLY**_ --- .../browser/OpdsBookBrowserActivity.cpp | 2 +- .../settings/FontDownloadActivity.cpp | 56 +++++++++++++++---- .../settings/FontDownloadActivity.h | 9 ++- src/network/HttpDownloader.cpp | 20 +++++-- src/network/HttpDownloader.h | 4 +- 5 files changed, 72 insertions(+), 19 deletions(-) diff --git a/src/activities/browser/OpdsBookBrowserActivity.cpp b/src/activities/browser/OpdsBookBrowserActivity.cpp index b1d5a8d4..40eada07 100644 --- a/src/activities/browser/OpdsBookBrowserActivity.cpp +++ b/src/activities/browser/OpdsBookBrowserActivity.cpp @@ -280,7 +280,7 @@ void OpdsBookBrowserActivity::downloadBook(const OpdsEntry& book) { downloadTotal = total; requestUpdate(true); }, - server.username, server.password); + nullptr, server.username, server.password); if (result == HttpDownloader::OK) { Epub(filename, "/.crosspoint").clearCache(); diff --git a/src/activities/settings/FontDownloadActivity.cpp b/src/activities/settings/FontDownloadActivity.cpp index ab9bf50b..67f5cdf8 100644 --- a/src/activities/settings/FontDownloadActivity.cpp +++ b/src/activities/settings/FontDownloadActivity.cpp @@ -175,10 +175,11 @@ bool FontDownloadActivity::fetchAndParseManifest() { // --- Download --- void FontDownloadActivity::downloadAll() { + cancelRequested_ = false; for (size_t i = 0; i < families_.size(); i++) { if (families_[i].installed) continue; downloadFamily(families_[i]); - if (state_ == ERROR) return; + if (state_ == ERROR || cancelRequested_) return; } { @@ -188,10 +189,11 @@ void FontDownloadActivity::downloadAll() { } void FontDownloadActivity::updateAll() { + cancelRequested_ = false; for (size_t i = 0; i < families_.size(); i++) { if (!families_[i].hasUpdate) continue; downloadFamily(families_[i]); - if (state_ == ERROR) return; + if (state_ == ERROR || cancelRequested_) return; } { @@ -267,10 +269,9 @@ void FontDownloadActivity::downloadFamily(ManifestFamily& family) { RenderLock lock(*this); state_ = DOWNLOADING; downloadingFamilyIndex_ = static_cast(&family - families_.data()); - currentFileIndex_ = 0; - currentFileTotal_ = family.files.size(); fileProgress_ = 0; fileTotal_ = 0; + cancelRequested_ = false; } requestUpdateAndWait(); @@ -286,7 +287,6 @@ void FontDownloadActivity::downloadFamily(ManifestFamily& family) { { RenderLock lock(*this); - currentFileIndex_ = i; fileProgress_ = 0; fileTotal_ = file.size; } @@ -297,11 +297,30 @@ void FontDownloadActivity::downloadFamily(ManifestFamily& family) { std::string url = baseUrl_ + file.name; - auto result = HttpDownloader::downloadToFile(url, destPath, [this](size_t downloaded, size_t total) { - fileProgress_ = downloaded; - fileTotal_ = total; - requestUpdate(true); - }); + auto result = HttpDownloader::downloadToFile( + url, destPath, + [this](size_t downloaded, size_t total) { + fileProgress_ = downloaded; + fileTotal_ = total; + mappedInput.update(); + if (mappedInput.isPressed(MappedInputManager::Button::Back) || + mappedInput.wasPressed(MappedInputManager::Button::Back)) { + cancelRequested_ = true; + } + requestUpdate(true); + }, + &cancelRequested_); + + if (result == HttpDownloader::ABORTED) { + fontInstaller_.deleteFamily(family.name.c_str()); + family.installed = false; + family.hasUpdate = false; + { + RenderLock lock(*this); + state_ = FAMILY_LIST; + } + return; + } if (result != HttpDownloader::OK) { LOG_ERR("FONT", "Download failed: %s (%d)", file.name.c_str(), result); @@ -347,6 +366,7 @@ void FontDownloadActivity::downloadFamily(ManifestFamily& family) { errorMessage_ = "Invalid font file: " + file.name; return; } + currentFileIndex_++; } fontInstaller_.refreshRegistry(); @@ -435,12 +455,25 @@ void FontDownloadActivity::loop() { if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { if (!families_.empty()) { if (isDownloadAllRow(selectedIndex_)) { + currentFileIndex_ = 0; + currentFileTotal_ = 0; + for (const auto& f : families_) { + if (!f.installed) currentFileTotal_ += f.files.size(); + } + downloadAll(); } else if (isUpdateAllRow(selectedIndex_)) { + currentFileIndex_ = 0; + currentFileTotal_ = 0; + for (const auto& f : families_) { + if (f.hasUpdate) currentFileTotal_ += f.files.size(); + } updateAll(); } else { auto& family = families_[familyIndexFromList(selectedIndex_)]; if (!family.installed || family.hasUpdate) { + currentFileIndex_ = 0; + currentFileTotal_ = family.files.size(); downloadFamily(family); } else { promptDeleteSelectedFamily(); @@ -574,6 +607,9 @@ void FontDownloadActivity::render(RenderLock&&) { renderer, Rect{metrics.contentSidePadding, barY, pageWidth - metrics.contentSidePadding * 2, metrics.progressBarHeight}, static_cast(progress * 100), 100); + + const auto labels = mappedInput.mapLabels(tr(STR_CANCEL), "", "", ""); + GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); } else if (state_ == COMPLETE) { renderer.drawCenteredText(UI_10_FONT_ID, centerY, tr(STR_FONT_INSTALLED), true, EpdFontFamily::BOLD); const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", ""); diff --git a/src/activities/settings/FontDownloadActivity.h b/src/activities/settings/FontDownloadActivity.h index 7f76e7c6..90d488a8 100644 --- a/src/activities/settings/FontDownloadActivity.h +++ b/src/activities/settings/FontDownloadActivity.h @@ -34,7 +34,13 @@ class FontDownloadActivity : public Activity { void onExit() override; void loop() override; void render(RenderLock&&) override; - bool preventAutoSleep() override { return state_ == LOADING_MANIFEST || state_ == DOWNLOADING; } + bool preventAutoSleep() override { + return state_ == LOADING_MANIFEST || state_ == DOWNLOADING || + // This is added because HTTPClient is a synchronous/blocking function, + // and blocks the main loop until the download is complete. + // So `activityManager.preventAutoSleep()` is never called during downloading + state_ == COMPLETE || state_ == ERROR; + } bool skipLoopDelay() override { return true; } private: @@ -79,6 +85,7 @@ class FontDownloadActivity : public Activity { size_t fileTotal_ = 0; int downloadingFamilyIndex_ = 0; std::string errorMessage_; + bool cancelRequested_ = false; void onWifiSelectionComplete(bool success); bool fetchAndParseManifest(); diff --git a/src/network/HttpDownloader.cpp b/src/network/HttpDownloader.cpp index b881c859..bd26a5ac 100644 --- a/src/network/HttpDownloader.cpp +++ b/src/network/HttpDownloader.cpp @@ -16,13 +16,17 @@ namespace { class FileWriteStream final : public Stream { public: - FileWriteStream(FsFile& file, size_t total, HttpDownloader::ProgressCallback progress) - : file_(file), total_(total), progress_(std::move(progress)) {} + FileWriteStream(FsFile& file, size_t total, HttpDownloader::ProgressCallback progress, bool* cancelFlag) + : file_(file), total_(total), progress_(std::move(progress)), cancelFlag_(cancelFlag) {} size_t write(uint8_t byte) override { return write(&byte, 1); } size_t write(const uint8_t* buffer, size_t size) override { // Write-through stream for HTTPClient::writeToStream with progress tracking. + if (cancelFlag_ && *cancelFlag_) { + writeOk_ = false; + return 0; + } const size_t written = file_.write(buffer, size); if (written != size) { writeOk_ = false; @@ -48,6 +52,7 @@ class FileWriteStream final : public Stream { size_t downloaded_ = 0; bool writeOk_ = true; HttpDownloader::ProgressCallback progress_; + bool* cancelFlag_; }; } // namespace @@ -101,8 +106,8 @@ bool HttpDownloader::fetchUrl(const std::string& url, std::string& outContent, c } HttpDownloader::DownloadError HttpDownloader::downloadToFile(const std::string& url, const std::string& destPath, - ProgressCallback progress, const std::string& username, - const std::string& password) { + ProgressCallback progress, bool* cancelFlag, + const std::string& username, const std::string& password) { std::unique_ptr client; if (UrlUtils::isHttpsUrl(url)) { auto* secureClient = new NetworkClientSecure(); @@ -155,12 +160,17 @@ HttpDownloader::DownloadError HttpDownloader::downloadToFile(const std::string& } // Let HTTPClient handle chunked decoding and stream body bytes into the file. - FileWriteStream fileStream(file, contentLength, progress); + FileWriteStream fileStream(file, contentLength, progress, cancelFlag); const int writeResult = http.writeToStream(&fileStream); file.close(); http.end(); + if (cancelFlag && *cancelFlag) { + Storage.remove(destPath.c_str()); + return ABORTED; + } + if (writeResult < 0) { LOG_ERR("HTTP", "writeToStream error: %d", writeResult); Storage.remove(destPath.c_str()); diff --git a/src/network/HttpDownloader.h b/src/network/HttpDownloader.h index 216840a5..5913c895 100644 --- a/src/network/HttpDownloader.h +++ b/src/network/HttpDownloader.h @@ -32,6 +32,6 @@ class HttpDownloader { * Download a file to the SD card with optional credentials. */ static DownloadError downloadToFile(const std::string& url, const std::string& destPath, - ProgressCallback progress = nullptr, const std::string& username = "", - const std::string& password = ""); + ProgressCallback progress = nullptr, bool* cancelFlag = nullptr, + const std::string& username = "", const std::string& password = ""); };