mod: restore missing mod features from resync audit
Re-add KOReaderSyncActivity PUSH_ONLY mode (PR #1090): - SyncMode enum with INTERACTIVE/PUSH_ONLY, deferFinish pattern - Push & Sleep menu action in EpubReaderMenuActivity - ActivityManager::requestSleep() for activity-initiated sleep - main.cpp checks isSleepRequested() each loop iteration Wire EndOfBookMenuActivity into EpubReaderActivity: - pendingEndOfBookMenu deferred flag avoids render-lock deadlock - Handles all 6 actions: ARCHIVE, DELETE, TABLE_OF_CONTENTS, BACK_TO_BEGINNING, CLOSE_BOOK, CLOSE_MENU Add book management to reader menu: - ARCHIVE_BOOK, DELETE_BOOK, REINDEX_BOOK actions with handlers Port silent next-chapter pre-indexing: - silentIndexNextChapterIfNeeded() proactively indexes next chapter when user is near end of current one, eliminating load screens Add per-book letterbox fill toggle in reader menu: - LETTERBOX_FILL cycles Default/Dithered/Solid/None - Loads/saves per-book override via BookSettings - bookCachePath constructor param added to EpubReaderMenuActivity Made-with: Cursor
This commit is contained in:
@@ -63,6 +63,8 @@ class ActivityManager {
|
||||
// This variable must only be set by the main loop, to avoid race conditions
|
||||
bool requestedUpdate = false;
|
||||
|
||||
bool sleepRequested = false;
|
||||
|
||||
public:
|
||||
explicit ActivityManager(GfxRenderer& renderer, MappedInputManager& mappedInput)
|
||||
: renderer(renderer), mappedInput(mappedInput), renderingMutex(xSemaphoreCreateMutex()) {
|
||||
@@ -101,6 +103,11 @@ class ActivityManager {
|
||||
bool isReaderActivity() const;
|
||||
bool skipLoopDelay() const;
|
||||
|
||||
// Activities can request sleep (e.g. PUSH_AND_SLEEP). The main loop checks
|
||||
// this flag after each loop() call and triggers enterDeepSleep() if set.
|
||||
void requestSleep() { sleepRequested = true; }
|
||||
bool isSleepRequested() const { return sleepRequested; }
|
||||
|
||||
// If immediate is true, the update will be triggered immediately.
|
||||
// Otherwise, it will be deferred until the end of the current loop iteration.
|
||||
void requestUpdate(bool immediate = false);
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
|
||||
#include "CrossPointSettings.h"
|
||||
#include "CrossPointState.h"
|
||||
#include "activities/ActivityManager.h"
|
||||
#include "DictionaryWordSelectActivity.h"
|
||||
#include "EndOfBookMenuActivity.h"
|
||||
#include "EpubReaderBookmarkSelectionActivity.h"
|
||||
#include "EpubReaderChapterSelectionActivity.h"
|
||||
#include "EpubReaderFootnotesActivity.h"
|
||||
@@ -23,6 +25,7 @@
|
||||
#include "RecentBooksStore.h"
|
||||
#include "components/UITheme.h"
|
||||
#include "fontIds.h"
|
||||
#include "util/BookManager.h"
|
||||
#include "util/BookmarkStore.h"
|
||||
#include "util/Dictionary.h"
|
||||
#include "util/ScreenshotUtil.h"
|
||||
@@ -133,6 +136,53 @@ void EpubReaderActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pendingEndOfBookMenu) {
|
||||
pendingEndOfBookMenu = false;
|
||||
endOfBookMenuOpened = true;
|
||||
startActivityForResult(
|
||||
std::make_unique<EndOfBookMenuActivity>(renderer, mappedInput, epub->getPath()),
|
||||
[this](const ActivityResult& result) {
|
||||
if (result.isCancelled) {
|
||||
return;
|
||||
}
|
||||
const auto action = static_cast<EndOfBookMenuActivity::Action>(std::get<MenuResult>(result.data).action);
|
||||
switch (action) {
|
||||
case EndOfBookMenuActivity::Action::ARCHIVE:
|
||||
BookManager::archiveBook(epub->getPath());
|
||||
activityManager.goHome();
|
||||
return;
|
||||
case EndOfBookMenuActivity::Action::DELETE:
|
||||
BookManager::deleteBook(epub->getPath());
|
||||
activityManager.goHome();
|
||||
return;
|
||||
case EndOfBookMenuActivity::Action::TABLE_OF_CONTENTS: {
|
||||
endOfBookMenuOpened = false;
|
||||
RenderLock lock(*this);
|
||||
currentSpineIndex = epub->getSpineItemsCount() - 1;
|
||||
nextPageNumber = UINT16_MAX;
|
||||
section.reset();
|
||||
break;
|
||||
}
|
||||
case EndOfBookMenuActivity::Action::BACK_TO_BEGINNING: {
|
||||
endOfBookMenuOpened = false;
|
||||
RenderLock lock(*this);
|
||||
currentSpineIndex = 0;
|
||||
nextPageNumber = 0;
|
||||
section.reset();
|
||||
break;
|
||||
}
|
||||
case EndOfBookMenuActivity::Action::CLOSE_BOOK:
|
||||
activityManager.goHome();
|
||||
return;
|
||||
case EndOfBookMenuActivity::Action::CLOSE_MENU:
|
||||
endOfBookMenuOpened = false;
|
||||
break;
|
||||
}
|
||||
requestUpdate();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (automaticPageTurnActive) {
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm) ||
|
||||
mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||
@@ -173,7 +223,8 @@ void EpubReaderActivity::loop() {
|
||||
section ? BookmarkStore::hasBookmark(epub->getCachePath(), currentSpineIndex, section->currentPage) : false;
|
||||
startActivityForResult(std::make_unique<EpubReaderMenuActivity>(
|
||||
renderer, mappedInput, epub->getTitle(), currentPage, totalPages, bookProgressPercent,
|
||||
SETTINGS.orientation, !currentPageFootnotes.empty(), isBookmarked, SETTINGS.fontSize),
|
||||
SETTINGS.orientation, !currentPageFootnotes.empty(), isBookmarked, SETTINGS.fontSize,
|
||||
epub->getCachePath()),
|
||||
[this](const ActivityResult& result) {
|
||||
// Always apply orientation and font size even if the menu was cancelled
|
||||
const auto& menu = std::get<MenuResult>(result.data);
|
||||
@@ -254,6 +305,8 @@ void EpubReaderActivity::loop() {
|
||||
} else {
|
||||
pageTurn(true);
|
||||
}
|
||||
|
||||
silentIndexNextChapterIfNeeded();
|
||||
}
|
||||
|
||||
// Translate an absolute percent into a spine index plus a normalized position
|
||||
@@ -441,6 +494,19 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EpubReaderMenuActivity::MenuAction::PUSH_AND_SLEEP: {
|
||||
const int cp = section ? section->currentPage : 0;
|
||||
const int tp = section ? section->pageCount : 0;
|
||||
if (KOREADER_STORE.hasCredentials()) {
|
||||
startActivityForResult(
|
||||
std::make_unique<KOReaderSyncActivity>(renderer, mappedInput, epub, epub->getPath(), currentSpineIndex, cp,
|
||||
tp, KOReaderSyncActivity::SyncMode::PUSH_ONLY),
|
||||
[](const ActivityResult&) { activityManager.requestSleep(); });
|
||||
} else {
|
||||
activityManager.requestSleep();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EpubReaderMenuActivity::MenuAction::CLOSE_BOOK:
|
||||
onGoHome();
|
||||
return;
|
||||
@@ -514,6 +580,23 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction
|
||||
requestUpdate();
|
||||
break;
|
||||
}
|
||||
case EpubReaderMenuActivity::MenuAction::ARCHIVE_BOOK: {
|
||||
if (epub) BookManager::archiveBook(epub->getPath());
|
||||
activityManager.goHome();
|
||||
return;
|
||||
}
|
||||
case EpubReaderMenuActivity::MenuAction::DELETE_BOOK: {
|
||||
if (epub) BookManager::deleteBook(epub->getPath());
|
||||
activityManager.goHome();
|
||||
return;
|
||||
}
|
||||
case EpubReaderMenuActivity::MenuAction::REINDEX_BOOK: {
|
||||
if (epub) BookManager::reindexBook(epub->getPath(), false);
|
||||
activityManager.goHome();
|
||||
return;
|
||||
}
|
||||
case EpubReaderMenuActivity::MenuAction::LETTERBOX_FILL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -618,6 +701,45 @@ void EpubReaderActivity::pageTurn(bool isForwardTurn) {
|
||||
requestUpdate();
|
||||
}
|
||||
|
||||
bool EpubReaderActivity::silentIndexNextChapterIfNeeded() {
|
||||
if (!epub || !section) return false;
|
||||
if (preIndexedNextSpine == currentSpineIndex + 1) return false;
|
||||
|
||||
const bool nearEnd = (section->pageCount == 1 && section->currentPage == 0) ||
|
||||
(section->pageCount >= 2 && section->currentPage == section->pageCount - 2);
|
||||
if (!nearEnd) return false;
|
||||
|
||||
const int nextSpine = currentSpineIndex + 1;
|
||||
if (nextSpine >= epub->getSpineItemsCount()) return false;
|
||||
|
||||
int marginTop, marginRight, marginBottom, marginLeft;
|
||||
renderer.getOrientedViewableTRBL(&marginTop, &marginRight, &marginBottom, &marginLeft);
|
||||
marginTop += SETTINGS.screenMargin;
|
||||
marginLeft += SETTINGS.screenMargin;
|
||||
marginRight += SETTINGS.screenMargin;
|
||||
marginBottom += std::max(SETTINGS.screenMargin, UITheme::getInstance().getStatusBarHeight());
|
||||
const uint16_t vpWidth = renderer.getScreenWidth() - marginLeft - marginRight;
|
||||
const uint16_t vpHeight = renderer.getScreenHeight() - marginTop - marginBottom;
|
||||
|
||||
Section nextSection(epub, nextSpine, renderer);
|
||||
if (nextSection.loadSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
|
||||
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, vpWidth, vpHeight,
|
||||
SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle, SETTINGS.imageRendering)) {
|
||||
preIndexedNextSpine = nextSpine;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DBG("ERS", "Silently indexing next chapter: %d", nextSpine);
|
||||
if (!nextSection.createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
|
||||
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, vpWidth, vpHeight,
|
||||
SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle, SETTINGS.imageRendering)) {
|
||||
LOG_ERR("ERS", "Failed silent indexing for chapter: %d", nextSpine);
|
||||
return false;
|
||||
}
|
||||
preIndexedNextSpine = nextSpine;
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Failure handling
|
||||
void EpubReaderActivity::render(RenderLock&& lock) {
|
||||
if (!epub) {
|
||||
@@ -633,12 +755,15 @@ void EpubReaderActivity::render(RenderLock&& lock) {
|
||||
currentSpineIndex = epub->getSpineItemsCount();
|
||||
}
|
||||
|
||||
// Show end of book screen
|
||||
// Show end of book screen (defer menu launch to loop() to avoid deadlock)
|
||||
if (currentSpineIndex == epub->getSpineItemsCount()) {
|
||||
renderer.clearScreen();
|
||||
renderer.drawCenteredText(UI_12_FONT_ID, 300, tr(STR_END_OF_BOOK), true, EpdFontFamily::BOLD);
|
||||
renderer.displayBuffer();
|
||||
automaticPageTurnActive = false;
|
||||
if (!endOfBookMenuOpened) {
|
||||
pendingEndOfBookMenu = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,13 @@ class EpubReaderActivity final : public Activity {
|
||||
bool pendingScreenshot = false;
|
||||
bool skipNextButtonCheck = false; // Skip button processing for one frame after subactivity exit
|
||||
bool automaticPageTurnActive = false;
|
||||
bool pendingEndOfBookMenu = false;
|
||||
bool endOfBookMenuOpened = false;
|
||||
|
||||
// Silent pre-indexing: proactively creates section files for the next chapter
|
||||
// when the user is near the end of the current one, eliminating load times.
|
||||
int preIndexedNextSpine = -1;
|
||||
bool silentIndexNextChapterIfNeeded();
|
||||
|
||||
// Footnote support
|
||||
std::vector<FootnoteEntry> currentPageFootnotes;
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
EpubReaderMenuActivity::EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
||||
const std::string& title, const int currentPage, const int totalPages,
|
||||
const int bookProgressPercent, const uint8_t currentOrientation,
|
||||
const bool hasFootnotes, bool isBookmarked, uint8_t currentFontSize)
|
||||
const bool hasFootnotes, bool isBookmarked, uint8_t currentFontSize,
|
||||
const std::string& bookCachePath)
|
||||
: Activity("EpubReaderMenu", renderer, mappedInput),
|
||||
menuItems(buildMenuItems(hasFootnotes, isBookmarked)),
|
||||
title(title),
|
||||
@@ -19,12 +20,18 @@ EpubReaderMenuActivity::EpubReaderMenuActivity(GfxRenderer& renderer, MappedInpu
|
||||
pendingFontSize(currentFontSize < CrossPointSettings::FONT_SIZE_COUNT ? currentFontSize : 0),
|
||||
currentPage(currentPage),
|
||||
totalPages(totalPages),
|
||||
bookProgressPercent(bookProgressPercent) {}
|
||||
bookProgressPercent(bookProgressPercent),
|
||||
bookCachePath(bookCachePath) {
|
||||
if (!bookCachePath.empty()) {
|
||||
auto bookSettings = BookSettings::load(bookCachePath);
|
||||
pendingLetterboxFill = bookSettings.letterboxFillOverride;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<EpubReaderMenuActivity::MenuItem> EpubReaderMenuActivity::buildMenuItems(bool hasFootnotes,
|
||||
bool isBookmarked) {
|
||||
std::vector<MenuItem> items;
|
||||
items.reserve(13);
|
||||
items.reserve(16);
|
||||
// Mod menu order
|
||||
if (isBookmarked) {
|
||||
items.push_back({MenuAction::REMOVE_BOOKMARK, StrId::STR_REMOVE_BOOKMARK});
|
||||
@@ -38,8 +45,13 @@ std::vector<EpubReaderMenuActivity::MenuItem> EpubReaderMenuActivity::buildMenuI
|
||||
items.push_back({MenuAction::GO_TO_PERCENT, StrId::STR_GO_TO_PERCENT});
|
||||
items.push_back({MenuAction::TOGGLE_ORIENTATION, StrId::STR_TOGGLE_ORIENTATION});
|
||||
items.push_back({MenuAction::TOGGLE_FONT_SIZE, StrId::STR_TOGGLE_FONT_SIZE});
|
||||
items.push_back({MenuAction::LETTERBOX_FILL, StrId::STR_OVERRIDE_LETTERBOX_FILL});
|
||||
items.push_back({MenuAction::SYNC, StrId::STR_SYNC_PROGRESS});
|
||||
items.push_back({MenuAction::PUSH_AND_SLEEP, StrId::STR_PUSH_AND_SLEEP});
|
||||
items.push_back({MenuAction::CLOSE_BOOK, StrId::STR_CLOSE_BOOK});
|
||||
items.push_back({MenuAction::ARCHIVE_BOOK, StrId::STR_ARCHIVE_BOOK});
|
||||
items.push_back({MenuAction::DELETE_BOOK, StrId::STR_DELETE_BOOK});
|
||||
items.push_back({MenuAction::REINDEX_BOOK, StrId::STR_REINDEX_BOOK});
|
||||
items.push_back({MenuAction::DELETE_CACHE, StrId::STR_DELETE_CACHE});
|
||||
items.push_back({MenuAction::DELETE_DICT_CACHE, StrId::STR_DELETE_DICT_CACHE});
|
||||
return items;
|
||||
@@ -81,6 +93,14 @@ void EpubReaderMenuActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedAction == MenuAction::LETTERBOX_FILL) {
|
||||
int idx = (letterboxFillToIndex() + 1) % LETTERBOX_FILL_OPTION_COUNT;
|
||||
pendingLetterboxFill = indexToLetterboxFill(idx);
|
||||
if (!bookCachePath.empty()) saveLetterboxFill();
|
||||
requestUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
setResult(MenuResult{static_cast<int>(selectedAction), pendingOrientation, selectedPageTurnOption, pendingFontSize});
|
||||
finish();
|
||||
return;
|
||||
@@ -155,6 +175,12 @@ void EpubReaderMenuActivity::render(RenderLock&&) {
|
||||
const auto width = renderer.getTextWidth(UI_10_FONT_ID, value);
|
||||
renderer.drawText(UI_10_FONT_ID, contentX + contentWidth - 20 - width, displayY, value, !isSelected);
|
||||
}
|
||||
|
||||
if (menuItems[i].action == MenuAction::LETTERBOX_FILL) {
|
||||
const char* value = I18N.get(letterboxFillLabels[letterboxFillToIndex()]);
|
||||
const auto width = renderer.getTextWidth(UI_10_FONT_ID, value);
|
||||
renderer.drawText(UI_10_FONT_ID, contentX + contentWidth - 20 - width, displayY, value, !isSelected);
|
||||
}
|
||||
}
|
||||
|
||||
// Footer / Hints
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "../Activity.h"
|
||||
#include "util/BookSettings.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
|
||||
class EpubReaderMenuActivity final : public Activity {
|
||||
@@ -21,6 +22,7 @@ class EpubReaderMenuActivity final : public Activity {
|
||||
DISPLAY_QR,
|
||||
GO_HOME,
|
||||
SYNC,
|
||||
PUSH_AND_SLEEP,
|
||||
DELETE_CACHE,
|
||||
// Mod-specific actions
|
||||
ADD_BOOKMARK,
|
||||
@@ -32,13 +34,18 @@ class EpubReaderMenuActivity final : public Activity {
|
||||
TOGGLE_ORIENTATION,
|
||||
TOGGLE_FONT_SIZE,
|
||||
CLOSE_BOOK,
|
||||
DELETE_DICT_CACHE
|
||||
DELETE_DICT_CACHE,
|
||||
ARCHIVE_BOOK,
|
||||
DELETE_BOOK,
|
||||
REINDEX_BOOK,
|
||||
LETTERBOX_FILL
|
||||
};
|
||||
|
||||
explicit EpubReaderMenuActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::string& title,
|
||||
const int currentPage, const int totalPages, const int bookProgressPercent,
|
||||
const uint8_t currentOrientation, const bool hasFootnotes,
|
||||
bool isBookmarked = false, uint8_t currentFontSize = 0);
|
||||
bool isBookmarked = false, uint8_t currentFontSize = 0,
|
||||
const std::string& bookCachePath = "");
|
||||
|
||||
void onEnter() override;
|
||||
void onExit() override;
|
||||
@@ -70,4 +77,26 @@ class EpubReaderMenuActivity final : public Activity {
|
||||
int currentPage = 0;
|
||||
int totalPages = 0;
|
||||
int bookProgressPercent = 0;
|
||||
|
||||
std::string bookCachePath;
|
||||
uint8_t pendingLetterboxFill = BookSettings::USE_GLOBAL;
|
||||
static constexpr int LETTERBOX_FILL_OPTION_COUNT = 4;
|
||||
const std::vector<StrId> letterboxFillLabels = {StrId::STR_DEFAULT_OPTION, StrId::STR_DITHERED, StrId::STR_SOLID,
|
||||
StrId::STR_NONE_OPT};
|
||||
|
||||
int letterboxFillToIndex() const {
|
||||
if (pendingLetterboxFill == BookSettings::USE_GLOBAL) return 0;
|
||||
return pendingLetterboxFill + 1;
|
||||
}
|
||||
|
||||
static uint8_t indexToLetterboxFill(int index) {
|
||||
if (index == 0) return BookSettings::USE_GLOBAL;
|
||||
return static_cast<uint8_t>(index - 1);
|
||||
}
|
||||
|
||||
void saveLetterboxFill() const {
|
||||
auto settings = BookSettings::load(bookCachePath);
|
||||
settings.letterboxFillOverride = pendingLetterboxFill;
|
||||
BookSettings::save(bookCachePath, settings);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -50,6 +50,12 @@ void wifiOff() {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void KOReaderSyncActivity::deferFinish(bool success) {
|
||||
RenderLock lock(*this);
|
||||
pendingFinishSuccess = success;
|
||||
pendingFinish = true;
|
||||
}
|
||||
|
||||
void KOReaderSyncActivity::onWifiSelectionComplete(const bool success) {
|
||||
if (!success) {
|
||||
LOG_DBG("KOSync", "WiFi connection failed, exiting");
|
||||
@@ -89,6 +95,10 @@ void KOReaderSyncActivity::performSync() {
|
||||
documentHash = KOReaderDocumentId::calculate(epubPath);
|
||||
}
|
||||
if (documentHash.empty()) {
|
||||
if (syncMode == SyncMode::PUSH_ONLY) {
|
||||
deferFinish(false);
|
||||
return;
|
||||
}
|
||||
{
|
||||
RenderLock lock(*this);
|
||||
state = SYNC_FAILED;
|
||||
@@ -106,11 +116,16 @@ void KOReaderSyncActivity::performSync() {
|
||||
}
|
||||
requestUpdateAndWait();
|
||||
|
||||
if (syncMode == SyncMode::PUSH_ONLY) {
|
||||
// Skip fetching remote progress entirely -- just upload local position
|
||||
performUpload();
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch remote progress
|
||||
const auto result = KOReaderSyncClient::getProgress(documentHash, remoteProgress);
|
||||
|
||||
if (result == KOReaderSyncClient::NOT_FOUND) {
|
||||
// No remote progress - offer to upload
|
||||
{
|
||||
RenderLock lock(*this);
|
||||
state = NO_REMOTE_PROGRESS;
|
||||
@@ -174,6 +189,11 @@ void KOReaderSyncActivity::performUpload() {
|
||||
|
||||
if (result != KOReaderSyncClient::OK) {
|
||||
wifiOff();
|
||||
if (syncMode == SyncMode::PUSH_ONLY) {
|
||||
LOG_DBG("KOSync", "PUSH_ONLY upload failed: %s", KOReaderSyncClient::errorString(result));
|
||||
deferFinish(false);
|
||||
return;
|
||||
}
|
||||
{
|
||||
RenderLock lock(*this);
|
||||
state = SYNC_FAILED;
|
||||
@@ -184,6 +204,11 @@ void KOReaderSyncActivity::performUpload() {
|
||||
}
|
||||
|
||||
wifiOff();
|
||||
if (syncMode == SyncMode::PUSH_ONLY) {
|
||||
LOG_DBG("KOSync", "PUSH_ONLY upload succeeded");
|
||||
deferFinish(true);
|
||||
return;
|
||||
}
|
||||
{
|
||||
RenderLock lock(*this);
|
||||
state = UPLOAD_COMPLETE;
|
||||
@@ -196,6 +221,11 @@ void KOReaderSyncActivity::onEnter() {
|
||||
|
||||
// Check for credentials first
|
||||
if (!KOREADER_STORE.hasCredentials()) {
|
||||
if (syncMode == SyncMode::PUSH_ONLY) {
|
||||
LOG_DBG("KOSync", "PUSH_ONLY: no credentials, finishing silently");
|
||||
deferFinish(false);
|
||||
return;
|
||||
}
|
||||
state = NO_CREDENTIALS;
|
||||
requestUpdate();
|
||||
return;
|
||||
@@ -335,6 +365,15 @@ void KOReaderSyncActivity::render(RenderLock&&) {
|
||||
}
|
||||
|
||||
void KOReaderSyncActivity::loop() {
|
||||
if (pendingFinish) {
|
||||
pendingFinish = false;
|
||||
ActivityResult result;
|
||||
result.isCancelled = !pendingFinishSuccess;
|
||||
setResult(std::move(result));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == NO_CREDENTIALS || state == SYNC_FAILED || state == UPLOAD_COMPLETE) {
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||
ActivityResult result;
|
||||
|
||||
@@ -11,18 +11,27 @@
|
||||
/**
|
||||
* Activity for syncing reading progress with KOReader sync server.
|
||||
*
|
||||
* Flow:
|
||||
* Flow (INTERACTIVE):
|
||||
* 1. Connect to WiFi (if not connected)
|
||||
* 2. Calculate document hash
|
||||
* 3. Fetch remote progress
|
||||
* 4. Show comparison and options (Apply/Upload)
|
||||
* 5. Apply or upload progress
|
||||
*
|
||||
* Flow (PUSH_ONLY):
|
||||
* 1. Connect to WiFi (if not connected)
|
||||
* 2. Calculate document hash
|
||||
* 3. Upload local progress (no UI interaction)
|
||||
* 4. Finish silently
|
||||
*/
|
||||
class KOReaderSyncActivity final : public Activity {
|
||||
public:
|
||||
enum class SyncMode { INTERACTIVE, PUSH_ONLY };
|
||||
|
||||
explicit KOReaderSyncActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
||||
const std::shared_ptr<Epub>& epub, const std::string& epubPath, int currentSpineIndex,
|
||||
int currentPage, int totalPagesInSpine)
|
||||
int currentPage, int totalPagesInSpine,
|
||||
SyncMode syncMode = SyncMode::INTERACTIVE)
|
||||
: Activity("KOReaderSync", renderer, mappedInput),
|
||||
epub(epub),
|
||||
epubPath(epubPath),
|
||||
@@ -31,7 +40,8 @@ class KOReaderSyncActivity final : public Activity {
|
||||
totalPagesInSpine(totalPagesInSpine),
|
||||
remoteProgress{},
|
||||
remotePosition{},
|
||||
localProgress{} {}
|
||||
localProgress{},
|
||||
syncMode(syncMode) {}
|
||||
|
||||
void onEnter() override;
|
||||
void onExit() override;
|
||||
@@ -73,6 +83,14 @@ class KOReaderSyncActivity final : public Activity {
|
||||
// Selection in result screen (0=Apply, 1=Upload)
|
||||
int selectedOption = 0;
|
||||
|
||||
SyncMode syncMode;
|
||||
|
||||
// Deferred finish for PUSH_ONLY (async upload completes on a blocking call,
|
||||
// so the actual finish() must happen in loop() where it's safe).
|
||||
bool pendingFinish = false;
|
||||
bool pendingFinishSuccess = false;
|
||||
|
||||
void deferFinish(bool success);
|
||||
void onWifiSelectionComplete(bool success);
|
||||
void performSync();
|
||||
void performUpload();
|
||||
|
||||
@@ -381,6 +381,12 @@ void loop() {
|
||||
screenshotButtonsReleased = true;
|
||||
}
|
||||
|
||||
if (activityManager.isSleepRequested()) {
|
||||
LOG_DBG("SLP", "Activity requested sleep (push-and-sleep)");
|
||||
enterDeepSleep();
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned long sleepTimeoutMs = SETTINGS.getSleepTimeoutMs();
|
||||
if (millis() - lastActivityTime >= sleepTimeoutMs) {
|
||||
LOG_DBG("SLP", "Auto-sleep triggered after %lu ms of inactivity", sleepTimeoutMs);
|
||||
|
||||
Reference in New Issue
Block a user