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 = ""); };