@@ -7,6 +7,11 @@ enum class KOReaderSyncIntentState : uint8_t {
|
||||
COMPARE = 0,
|
||||
PULL_REMOTE = 1,
|
||||
PUSH_LOCAL = 2,
|
||||
// Auto variants compare progress before writing and skip silently when the other side
|
||||
// is already ahead. AUTO_PUSH fires from the reader-close auto-sync path; AUTO_PULL fires
|
||||
// when the user opens a book with long-press Confirm. Neither prompts the user.
|
||||
AUTO_PUSH = 3,
|
||||
AUTO_PULL = 4,
|
||||
};
|
||||
|
||||
enum class KOReaderSyncOutcomeState : uint8_t {
|
||||
@@ -47,6 +52,13 @@ struct KOReaderSyncSessionState {
|
||||
int resultPage = 0;
|
||||
uint16_t resultParagraphIndex = 0;
|
||||
bool resultHasParagraphIndex = false;
|
||||
// When true (auto-push-on-close), the sync activity goes to home instead of the reader on
|
||||
// completion. Without this, AUTO_PUSH would bounce back into the reader the user just left.
|
||||
bool exitToHomeAfterSync = false;
|
||||
// Set by RecentBooks / FileBrowser long-press to ask the reader to perform an AUTO_PULL
|
||||
// before rendering its first page. Consumed once on reader entry. Stored separately from
|
||||
// `intent` because the long-press path bypasses `launchKOReaderSync`'s reader-state capture.
|
||||
bool autoPullOnOpen = false;
|
||||
|
||||
void clear() {
|
||||
active = false;
|
||||
@@ -63,6 +75,8 @@ struct KOReaderSyncSessionState {
|
||||
resultPage = 0;
|
||||
resultParagraphIndex = 0;
|
||||
resultHasParagraphIndex = false;
|
||||
exitToHomeAfterSync = false;
|
||||
autoPullOnOpen = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -90,6 +90,8 @@ bool JsonSettingsIO::saveState(const CrossPointState& s, const char* path) {
|
||||
sync["resultPage"] = s.koReaderSyncSession.resultPage;
|
||||
sync["resultParagraphIndex"] = s.koReaderSyncSession.resultParagraphIndex;
|
||||
sync["resultHasParagraphIndex"] = s.koReaderSyncSession.resultHasParagraphIndex;
|
||||
sync["exitToHomeAfterSync"] = s.koReaderSyncSession.exitToHomeAfterSync;
|
||||
sync["autoPullOnOpen"] = s.koReaderSyncSession.autoPullOnOpen;
|
||||
// Information about a pending bookmark jump
|
||||
JsonObject jump = doc["pendingBookmarkJump"].to<JsonObject>();
|
||||
jump["active"] = s.pendingBookmarkJump.active;
|
||||
@@ -145,6 +147,8 @@ bool JsonSettingsIO::loadState(CrossPointState& s, const char* json) {
|
||||
s.koReaderSyncSession.resultPage = sync["resultPage"] | 0;
|
||||
s.koReaderSyncSession.resultParagraphIndex = sync["resultParagraphIndex"] | (uint16_t)0;
|
||||
s.koReaderSyncSession.resultHasParagraphIndex = sync["resultHasParagraphIndex"] | false;
|
||||
s.koReaderSyncSession.exitToHomeAfterSync = sync["exitToHomeAfterSync"] | false;
|
||||
s.koReaderSyncSession.autoPullOnOpen = sync["autoPullOnOpen"] | false;
|
||||
|
||||
JsonObject jump = doc["pendingBookmarkJump"].as<JsonObject>();
|
||||
s.pendingBookmarkJump.active = jump["active"] | false;
|
||||
|
||||
@@ -268,6 +268,8 @@ inline const std::vector<SettingInfo> list = {
|
||||
KOREADER_STORE.saveToFile();
|
||||
},
|
||||
"koMatchMethod", StrId::STR_KOREADER_SYNC),
|
||||
SettingInfo::Toggle(StrId::STR_KO_SYNC_ON_BOOK_CLOSE, &CrossPointSettings::koSyncOnBookClose, "koSyncOnBookClose",
|
||||
StrId::STR_KOREADER_SYNC),
|
||||
|
||||
// --- OPDS Browser (web-only, uses CrossPointSettings char arrays) ---
|
||||
SettingInfo::String(StrId::STR_OPDS_SERVER_URL, SETTINGS.opdsServerUrl, sizeof(SETTINGS.opdsServerUrl),
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include "../util/ConfirmationActivity.h"
|
||||
#include "BookInfoActivity.h"
|
||||
#include "CrossPointSettings.h"
|
||||
#include "CrossPointState.h"
|
||||
#include "KOReaderCredentialStore.h"
|
||||
#include "MappedInputManager.h"
|
||||
#include "components/UITheme.h"
|
||||
#include "fontIds.h"
|
||||
@@ -165,14 +167,17 @@ void FileBrowserActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Confirm short press opens selected entry; long press does nothing
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm) && mappedInput.getHeldTime() < GO_HOME_MS) {
|
||||
// Confirm short press opens selected entry; long press on a file opens it with KOReader sync.
|
||||
// Long press on a directory is ignored (no useful directory-level sync action).
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||
if (files.empty()) return;
|
||||
|
||||
const std::string& entry = files[selectorIndex];
|
||||
const bool isDirectory = (entry.back() == '/');
|
||||
const bool longPress = mappedInput.getHeldTime() >= GO_HOME_MS;
|
||||
|
||||
if (isDirectory) {
|
||||
if (longPress) return;
|
||||
if (basepath.back() != '/') basepath += "/";
|
||||
basepath += entry.substr(0, entry.length() - 1);
|
||||
loadFiles();
|
||||
@@ -182,6 +187,12 @@ void FileBrowserActivity::loop() {
|
||||
std::string fullPath = basepath;
|
||||
if (fullPath.back() != '/') fullPath += "/";
|
||||
fullPath += entry;
|
||||
if (longPress && KOREADER_STORE.hasCredentials()) {
|
||||
auto& sync = APP_STATE.koReaderSyncSession;
|
||||
sync.autoPullOnOpen = true;
|
||||
sync.exitToHomeAfterSync = false;
|
||||
APP_STATE.saveToFile();
|
||||
}
|
||||
ReturnHint hint;
|
||||
hint.target = ReturnTo::FileBrowser;
|
||||
hint.path = basepath;
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "../ActivityManager.h"
|
||||
#include "../reader/ReaderUtils.h"
|
||||
#include "../util/ConfirmationActivity.h"
|
||||
#include "BookInfoActivity.h"
|
||||
#include "CrossPointState.h"
|
||||
#include "KOReaderCredentialStore.h"
|
||||
#include "MappedInputManager.h"
|
||||
#include "RecentBooksStore.h"
|
||||
#include "components/UITheme.h"
|
||||
@@ -53,7 +56,16 @@ void RecentBooksActivity::loop() {
|
||||
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm) && !recentBooks.empty() &&
|
||||
selectorIndex < static_cast<int>(recentBooks.size())) {
|
||||
LOG_DBG("RBA", "Selected recent book: %s", recentBooks[selectorIndex].path.c_str());
|
||||
// Long-press Confirm signals "open with KOReader sync": the reader will perform an
|
||||
// AUTO_PULL before rendering its first page. Short-press is the unchanged direct open.
|
||||
const bool longPress = mappedInput.getHeldTime() >= ReaderUtils::GO_HOME_MS && KOREADER_STORE.hasCredentials();
|
||||
LOG_DBG("RBA", "Selected recent book: %s (sync=%d)", recentBooks[selectorIndex].path.c_str(), longPress ? 1 : 0);
|
||||
if (longPress) {
|
||||
auto& sync = APP_STATE.koReaderSyncSession;
|
||||
sync.autoPullOnOpen = true;
|
||||
sync.exitToHomeAfterSync = false;
|
||||
APP_STATE.saveToFile();
|
||||
}
|
||||
ReturnHint hint;
|
||||
hint.target = ReturnTo::RecentBooks;
|
||||
hint.selectIndex = static_cast<int>(selectorIndex);
|
||||
|
||||
@@ -294,6 +294,7 @@ void EpubReaderActivity::loop() {
|
||||
// Long press BACK (1s+) goes to home screen
|
||||
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= ReaderUtils::GO_HOME_MS) {
|
||||
ReaderUtils::enforceExitFullRefresh(renderer);
|
||||
if (tryAutoPushOnClose()) return;
|
||||
onGoHome();
|
||||
return;
|
||||
}
|
||||
@@ -306,6 +307,7 @@ void EpubReaderActivity::loop() {
|
||||
return;
|
||||
}
|
||||
ReaderUtils::enforceExitFullRefresh(renderer);
|
||||
if (tryAutoPushOnClose()) return;
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
@@ -318,6 +320,7 @@ void EpubReaderActivity::loop() {
|
||||
// At end of the book, forward button returns to caller and back button returns to last page
|
||||
if (currentSpineIndex > 0 && currentSpineIndex >= epub->getSpineItemsCount()) {
|
||||
if (nextTriggered) {
|
||||
if (tryAutoPushOnClose()) return;
|
||||
finish();
|
||||
} else {
|
||||
currentSpineIndex = epub->getSpineItemsCount() - 1;
|
||||
@@ -568,6 +571,7 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction
|
||||
break;
|
||||
}
|
||||
case EpubReaderMenuActivity::MenuAction::GO_HOME: {
|
||||
if (tryAutoPushOnClose()) return;
|
||||
onGoHome();
|
||||
return;
|
||||
}
|
||||
@@ -831,6 +835,8 @@ void EpubReaderActivity::launchKOReaderSync(const SyncLaunchMode mode) {
|
||||
syncIntent = KOReaderSyncIntentState::PULL_REMOTE;
|
||||
} else if (mode == SyncLaunchMode::PUSH_LOCAL) {
|
||||
syncIntent = KOReaderSyncIntentState::PUSH_LOCAL;
|
||||
} else if (mode == SyncLaunchMode::AUTO_PUSH) {
|
||||
syncIntent = KOReaderSyncIntentState::AUTO_PUSH;
|
||||
}
|
||||
|
||||
auto& sync = APP_STATE.koReaderSyncSession;
|
||||
@@ -865,6 +871,10 @@ void EpubReaderActivity::launchKOReaderSync(const SyncLaunchMode mode) {
|
||||
sync.resultPage = 0;
|
||||
sync.resultParagraphIndex = 0;
|
||||
sync.resultHasParagraphIndex = false;
|
||||
// Only auto-push-on-close should bypass the reader on resume; explicit syncs from the
|
||||
// reader menu always come back to the reader. Reset here so a stale flag from a prior
|
||||
// run cannot steal the user back to home.
|
||||
sync.exitToHomeAfterSync = (mode == SyncLaunchMode::AUTO_PUSH);
|
||||
APP_STATE.saveToFile();
|
||||
|
||||
LOG_DBG("ERS", "Standalone sync handoff: spine=%d page=%d/%d", currentSpineIndex, currentPage, totalPages);
|
||||
@@ -872,6 +882,27 @@ void EpubReaderActivity::launchKOReaderSync(const SyncLaunchMode mode) {
|
||||
activityManager.goToKOReaderSync();
|
||||
}
|
||||
|
||||
bool EpubReaderActivity::tryAutoPushOnClose() {
|
||||
// Three-page minimum filters out brief inspections — opening to check the cover or
|
||||
// skim the TOC shouldn't burn a network round-trip. Counter is per-activity-instance.
|
||||
constexpr int MIN_SESSION_PAGES = 3;
|
||||
if (!SETTINGS.koSyncOnBookClose) {
|
||||
return false;
|
||||
}
|
||||
if (!KOREADER_STORE.hasCredentials()) {
|
||||
return false;
|
||||
}
|
||||
if (sessionPagesAdvanced < MIN_SESSION_PAGES) {
|
||||
return false;
|
||||
}
|
||||
if (!epub) {
|
||||
return false;
|
||||
}
|
||||
// exitToHomeAfterSync flag is set inside launchKOReaderSync for AUTO_PUSH mode.
|
||||
launchKOReaderSync(SyncLaunchMode::AUTO_PUSH);
|
||||
return true;
|
||||
}
|
||||
|
||||
void EpubReaderActivity::applyPendingSyncSession() {
|
||||
auto& sync = APP_STATE.koReaderSyncSession;
|
||||
if (!sync.active || !epub || sync.epubPath != epub->getPath()) {
|
||||
@@ -891,6 +922,17 @@ void EpubReaderActivity::applyPendingSyncSession() {
|
||||
return;
|
||||
}
|
||||
|
||||
// AUTO_PULL handed off zeroed local state (the reader was not yet running when sync started),
|
||||
// so on cancel/fail we must NOT restore those zeros to progress.bin — they would clobber the
|
||||
// user's real local progress. Just clear the session and let the normal startup load progress.bin.
|
||||
if (sync.intent == KOReaderSyncIntentState::AUTO_PULL && sync.outcome != KOReaderSyncOutcomeState::APPLIED_REMOTE) {
|
||||
LOG_DBG("ERS", "AUTO_PULL non-success outcome=%d: leaving progress.bin untouched", static_cast<int>(sync.outcome));
|
||||
sync.clear();
|
||||
APP_STATE.saveToFile();
|
||||
logReaderMemSnapshot("after_apply_pending_sync_session");
|
||||
return;
|
||||
}
|
||||
|
||||
int restoreSpineIndex = sync.spineIndex;
|
||||
int restorePage = sync.page;
|
||||
pendingParagraphLookup = sync.hasParagraphIndex;
|
||||
@@ -1142,6 +1184,9 @@ void EpubReaderActivity::pageTurn(bool isForwardTurn) {
|
||||
if (!stepPageState(isForwardTurn)) {
|
||||
return;
|
||||
}
|
||||
// Track real progress within this session so auto-push-on-close can ignore brief
|
||||
// book inspections. Counts both directions — the user is engaging with the book either way.
|
||||
sessionPagesAdvanced++;
|
||||
requestUpdate();
|
||||
}
|
||||
|
||||
@@ -1888,6 +1933,7 @@ void EpubReaderActivity::onButtonAction(const CrossPointSettings::BUTTON_ACTION
|
||||
}
|
||||
case BA::BTN_EXIT_READER:
|
||||
ReaderUtils::enforceExitFullRefresh(renderer);
|
||||
if (tryAutoPushOnClose()) break;
|
||||
finish();
|
||||
break;
|
||||
case BA::BTN_READER_MENU:
|
||||
|
||||
@@ -11,15 +11,17 @@
|
||||
#include "activities/Activity.h"
|
||||
|
||||
class EpubReaderActivity final : public Activity {
|
||||
// Reader can launch sync in three UX modes:
|
||||
// Reader can launch sync in several UX modes:
|
||||
// - COMPARE: legacy chooser (apply/upload) for power users.
|
||||
// - PULL_REMOTE / PUSH_LOCAL: direct one-step actions from menu entries.
|
||||
// - AUTO_PUSH: silent push on reader exit; skips when remote is already ahead.
|
||||
// Keeping this split in the caller avoids branching on menu semantics deep
|
||||
// inside generic reader state handling.
|
||||
enum class SyncLaunchMode {
|
||||
COMPARE,
|
||||
PULL_REMOTE,
|
||||
PUSH_LOCAL,
|
||||
AUTO_PUSH,
|
||||
};
|
||||
|
||||
std::shared_ptr<Epub> epub;
|
||||
@@ -124,6 +126,10 @@ class EpubReaderActivity final : public Activity {
|
||||
bool skipNextButtonCheck = false; // Skip button processing for one frame after subactivity exit
|
||||
ReaderUtils::InputDrainGuard inputDrainGuard;
|
||||
bool automaticPageTurnActive = false;
|
||||
// Pages turned in the current reader session. Used to gate auto-push-on-close: a brief
|
||||
// inspection of a book should not trigger a network round-trip. Reset on every reader
|
||||
// entry; not persisted, since "session" means the lifetime of this activity instance.
|
||||
int sessionPagesAdvanced = 0;
|
||||
// -1 means use global SETTINGS value.
|
||||
int8_t bookEmbeddedStyleOverride = -1;
|
||||
int8_t bookImageRenderingOverride = -1;
|
||||
@@ -152,6 +158,11 @@ class EpubReaderActivity final : public Activity {
|
||||
void jumpToPercent(int percent);
|
||||
void onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction action);
|
||||
void launchKOReaderSync(SyncLaunchMode mode = SyncLaunchMode::COMPARE);
|
||||
// Reader-close auto-push gate. Returns true if AUTO_PUSH was launched (the caller
|
||||
// must not perform its own exit — the sync activity will route to home on completion).
|
||||
// Returns false when any of the gates fails (setting off, no credentials, < 3 pages),
|
||||
// letting the caller take its normal exit path.
|
||||
bool tryAutoPushOnClose();
|
||||
// Consume a persisted standalone KOReader sync session for this EPUB. Remote
|
||||
// apply writes the mapped reopen position into progress.bin before the normal
|
||||
// reader startup path reads it. Upload-complete leaves the existing local
|
||||
|
||||
@@ -115,8 +115,8 @@ void KOReaderSyncActivity::performSync() {
|
||||
LOG_DBG("KOSync", "Document hash: %s", documentHash.c_str());
|
||||
|
||||
// Local mapping is only needed for compare/upload paths.
|
||||
// Pull-only mode can skip this expensive step and go straight to remote fetch.
|
||||
if (syncIntent != KOReaderSyncIntentState::PULL_REMOTE) {
|
||||
// Pull-only modes can skip this expensive step and go straight to remote fetch.
|
||||
if (syncIntent != KOReaderSyncIntentState::PULL_REMOTE && syncIntent != KOReaderSyncIntentState::AUTO_PULL) {
|
||||
// Precompute local mapping before first network request so the expensive
|
||||
// inflate/index work happens before TLS. This avoids a second local mapping
|
||||
// pass later and keeps the upload path lightweight.
|
||||
@@ -141,7 +141,7 @@ void KOReaderSyncActivity::performSync() {
|
||||
|
||||
// Push intent skips comparison UI but still warms an HTTP/TLS session first
|
||||
// so PUT can reuse the connection instead of forcing a fresh handshake.
|
||||
if (syncIntent == KOReaderSyncIntentState::PUSH_LOCAL) {
|
||||
if (syncIntent == KOReaderSyncIntentState::PUSH_LOCAL || syncIntent == KOReaderSyncIntentState::AUTO_PUSH) {
|
||||
// Direct push previously started with no reusable HTTP/TLS session, forcing
|
||||
// a fresh handshake in updateProgress. Compare flow often succeeds because
|
||||
// upload reuses the GET session. Warm the session here so push can take the
|
||||
@@ -164,6 +164,22 @@ void KOReaderSyncActivity::performSync() {
|
||||
requestUpdate(true);
|
||||
return;
|
||||
}
|
||||
// Auto-push must not overwrite progress that is already further along on the server.
|
||||
// Compare percentages from the warmup GET; users opted into automatic sync, so a
|
||||
// remote-ahead state is treated as "nothing to do" and falls straight back to home.
|
||||
if (syncIntent == KOReaderSyncIntentState::AUTO_PUSH && warmupResult == KOReaderSyncClient::OK &&
|
||||
warmupProgress.percentage > localProgress.percentage) {
|
||||
LOG_DBG("KOSync", "AUTO_PUSH skipped: remote %.4f >= local %.4f", warmupProgress.percentage,
|
||||
localProgress.percentage);
|
||||
KOReaderSyncClient::endPersistentSession();
|
||||
HalClock::wifiOff(true);
|
||||
// Reuse UPLOAD_COMPLETE outcome so the resume path is identical to a successful push;
|
||||
// there is nothing to apply to the reader and progress.bin already reflects local state.
|
||||
APP_STATE.koReaderSyncSession.outcome = KOReaderSyncOutcomeState::UPLOAD_COMPLETE;
|
||||
APP_STATE.saveToFile();
|
||||
resumeReader(KOReaderSyncOutcomeState::UPLOAD_COMPLETE);
|
||||
return;
|
||||
}
|
||||
performUpload();
|
||||
return;
|
||||
}
|
||||
@@ -195,6 +211,15 @@ void KOReaderSyncActivity::performSync() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (syncIntent == KOReaderSyncIntentState::AUTO_PULL) {
|
||||
// Auto-pull at book open: nothing to apply, just open the book with local progress.
|
||||
// No user-visible failure since the user opted into "open with sync, if available".
|
||||
KOReaderSyncClient::endPersistentSession();
|
||||
HalClock::wifiOff(true);
|
||||
resumeReader(KOReaderSyncOutcomeState::CANCELLED);
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep session open so an immediate upload can reuse the same connection.
|
||||
// No remote progress - offer to upload
|
||||
{
|
||||
@@ -234,10 +259,16 @@ void KOReaderSyncActivity::performSync() {
|
||||
remotePosition.hasParagraphIndex = false;
|
||||
remoteChapterLabel.clear();
|
||||
|
||||
if (syncIntent == KOReaderSyncIntentState::PULL_REMOTE) {
|
||||
if (syncIntent == KOReaderSyncIntentState::PULL_REMOTE || syncIntent == KOReaderSyncIntentState::AUTO_PULL) {
|
||||
// Pull intent applies immediately and exits. We bypass chooser UI to keep
|
||||
// reader menu actions deterministic ("pull" always means apply remote).
|
||||
if (!ensureRemotePositionMapped()) {
|
||||
if (syncIntent == KOReaderSyncIntentState::AUTO_PULL) {
|
||||
// Auto-pull was best-effort. Fail silently and just open the book.
|
||||
HalClock::wifiOff(true);
|
||||
resumeReader(KOReaderSyncOutcomeState::CANCELLED);
|
||||
return;
|
||||
}
|
||||
{
|
||||
RenderLock lock(*this);
|
||||
state = SYNC_FAILED;
|
||||
@@ -256,6 +287,14 @@ void KOReaderSyncActivity::performSync() {
|
||||
sync.resultParagraphIndex = remotePosition.paragraphIndex;
|
||||
sync.resultHasParagraphIndex = remotePosition.hasParagraphIndex;
|
||||
APP_STATE.saveToFile();
|
||||
|
||||
if (syncIntent == KOReaderSyncIntentState::AUTO_PULL) {
|
||||
// Auto-pull skips the success-screen dwell — the reader will render the new
|
||||
// position immediately, which is the only visible feedback the user needs.
|
||||
HalClock::wifiOff(true);
|
||||
resumeReader(KOReaderSyncOutcomeState::APPLIED_REMOTE);
|
||||
return;
|
||||
}
|
||||
{
|
||||
RenderLock lock(*this);
|
||||
state = APPLY_COMPLETE;
|
||||
@@ -390,6 +429,12 @@ void KOReaderSyncActivity::performUpload() {
|
||||
HalClock::wifiOff(true);
|
||||
APP_STATE.koReaderSyncSession.outcome = KOReaderSyncOutcomeState::UPLOAD_COMPLETE;
|
||||
APP_STATE.saveToFile();
|
||||
if (syncIntent == KOReaderSyncIntentState::AUTO_PUSH) {
|
||||
// Auto-push doesn't need user acknowledgement on success; resume immediately
|
||||
// back to the calling activity (RecentBooks / FileBrowser via reader).
|
||||
resumeReader(KOReaderSyncOutcomeState::UPLOAD_COMPLETE);
|
||||
return;
|
||||
}
|
||||
{
|
||||
RenderLock lock(*this);
|
||||
state = UPLOAD_COMPLETE;
|
||||
@@ -464,8 +509,19 @@ void KOReaderSyncActivity::resumeReader(const KOReaderSyncOutcomeState outcome,
|
||||
sync.resultParagraphIndex = 0;
|
||||
sync.resultHasParagraphIndex = false;
|
||||
}
|
||||
// Honor exit-to-home flag set by reader-close auto-sync — bouncing back into the reader
|
||||
// the user just left would be jarring. The session state is consumed and cleared by the
|
||||
// home destination's normal flow (no reader to apply it to in this case).
|
||||
const bool exitToHome = sync.exitToHomeAfterSync;
|
||||
if (exitToHome) {
|
||||
sync.clear();
|
||||
}
|
||||
APP_STATE.saveToFile();
|
||||
logSyncMemSnapshot("before_resume_reader");
|
||||
if (exitToHome) {
|
||||
activityManager.goHome();
|
||||
return;
|
||||
}
|
||||
activityManager.goToReader(epubPath);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "CrossPointState.h"
|
||||
#include "Epub.h"
|
||||
#include "EpubReaderActivity.h"
|
||||
#include "KOReaderCredentialStore.h"
|
||||
#include "MdReaderActivity.h"
|
||||
#include "Txt.h"
|
||||
#include "TxtReaderActivity.h"
|
||||
@@ -110,6 +111,38 @@ void ReaderActivity::goToLibrary(const std::string& fromBookPath) {
|
||||
void ReaderActivity::onGoToEpubReader(std::unique_ptr<Epub> epub) {
|
||||
const auto epubPath = epub->getPath();
|
||||
currentBookPath = epubPath;
|
||||
|
||||
// Long-press Confirm on RecentBooks/FileBrowser sets autoPullOnOpen so the user can ask
|
||||
// for KOReader sync at open time. Route into the sync activity instead of creating the
|
||||
// reader; sync's resumeReader() will create the reader once the remote position is applied.
|
||||
// Pull-only mode does not need accurate local reader state, so we hand off zeros for spine/page.
|
||||
auto& sync = APP_STATE.koReaderSyncSession;
|
||||
if (sync.autoPullOnOpen && KOREADER_STORE.hasCredentials()) {
|
||||
LOG_DBG("READER", "AUTO_PULL on open: %s", epubPath.c_str());
|
||||
sync.autoPullOnOpen = false; // consume the flag
|
||||
sync.active = true;
|
||||
sync.epubPath = epubPath;
|
||||
sync.spineIndex = 0;
|
||||
sync.page = 0;
|
||||
sync.totalPagesInSpine = 0;
|
||||
sync.paragraphIndex = 0;
|
||||
sync.hasParagraphIndex = false;
|
||||
sync.xhtmlSeekHint = 0;
|
||||
sync.intent = KOReaderSyncIntentState::AUTO_PULL;
|
||||
sync.outcome = KOReaderSyncOutcomeState::PENDING;
|
||||
sync.resultSpineIndex = 0;
|
||||
sync.resultPage = 0;
|
||||
sync.resultParagraphIndex = 0;
|
||||
sync.resultHasParagraphIndex = false;
|
||||
sync.exitToHomeAfterSync = false;
|
||||
APP_STATE.saveToFile();
|
||||
// Drop the loaded Epub before TLS — sync activity will reload it for remote-position
|
||||
// mapping. Holding it here would needlessly inflate the heap during WiFi/TLS work.
|
||||
epub.reset();
|
||||
activityManager.goToKOReaderSync();
|
||||
return;
|
||||
}
|
||||
|
||||
logReaderLaunchMemSnapshot("before_replace_epub_reader");
|
||||
activityManager.replaceActivity(std::make_unique<EpubReaderActivity>(renderer, mappedInput, std::move(epub)));
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <GfxRenderer.h>
|
||||
#include <I18n.h>
|
||||
|
||||
#include "CrossPointSettings.h"
|
||||
#include "KOReaderAuthActivity.h"
|
||||
#include "KOReaderCredentialStore.h"
|
||||
#include "MappedInputManager.h"
|
||||
@@ -16,7 +17,7 @@ KOReaderSettingsActivity::KOReaderSettingsActivity(GfxRenderer& renderer, Mapped
|
||||
}
|
||||
|
||||
void KOReaderSettingsActivity::buildMenuItems() {
|
||||
menuItems.reserve(6);
|
||||
menuItems.reserve(7);
|
||||
// Username, Password, Server URL: ACTION items with custom value display
|
||||
menuItems.push_back(SettingInfo::Action(StrId::STR_SYNC_SERVER_URL, SettingAction::None)
|
||||
.withSubcategory(StrId::STR_MENU_KOSYNC_SERVER));
|
||||
@@ -24,14 +25,16 @@ void KOReaderSettingsActivity::buildMenuItems() {
|
||||
menuItems.push_back(SettingInfo::Action(StrId::STR_PASSWORD, SettingAction::None));
|
||||
|
||||
// Document matching: DynamicEnum toggling between Filename and Binary
|
||||
menuItems.push_back(SettingInfo::DynamicEnum(StrId::STR_DOCUMENT_MATCHING, {StrId::STR_FILENAME, StrId::STR_BINARY},
|
||||
static_cast<SettingInfo::ValueGetterFn>([](const void*) -> uint8_t {
|
||||
return static_cast<uint8_t>(KOREADER_STORE.getMatchMethod());
|
||||
}),
|
||||
[](void*, uint8_t v) {
|
||||
KOREADER_STORE.setMatchMethod(static_cast<DocumentMatchMethod>(v));
|
||||
KOREADER_STORE.saveToFile();
|
||||
}));
|
||||
menuItems.push_back(SettingInfo::DynamicEnum(
|
||||
StrId::STR_DOCUMENT_MATCHING, {StrId::STR_FILENAME, StrId::STR_BINARY},
|
||||
[](const void*) -> uint8_t { return static_cast<uint8_t>(KOREADER_STORE.getMatchMethod()); },
|
||||
[](void*, uint8_t v) {
|
||||
KOREADER_STORE.setMatchMethod(static_cast<DocumentMatchMethod>(v));
|
||||
KOREADER_STORE.saveToFile();
|
||||
})
|
||||
.withSubcategory(StrId::STR_MENU_KOSYNC_BEHAVIOR));
|
||||
menuItems.push_back(SettingInfo::Toggle(StrId::STR_KO_SYNC_ON_BOOK_CLOSE, &CrossPointSettings::koSyncOnBookClose,
|
||||
"koSyncOnBookClose"));
|
||||
|
||||
// Authenticate and Register: ACTION items
|
||||
menuItems.push_back(
|
||||
|
||||
Reference in New Issue
Block a user