Add a setting for document matching method

This commit is contained in:
Justin Mitchell
2026-01-04 23:48:42 -05:00
parent ae34fc76e1
commit 77bb97339d
8 changed files with 133 additions and 26 deletions

View File

@@ -10,15 +10,26 @@
namespace {
// Time threshold for treating a long press as a page-up/page-down
constexpr int SKIP_PAGE_MS = 700;
// Sync option is shown at index 0 if credentials are configured
constexpr int SYNC_ITEM_INDEX = 0;
} // namespace
bool EpubReaderChapterSelectionActivity::hasSyncOption() const { return KOREADER_STORE.hasCredentials(); }
int EpubReaderChapterSelectionActivity::getTotalItems() const {
// Add 1 for sync option if credentials are configured
const int syncOffset = KOREADER_STORE.hasCredentials() ? 1 : 0;
return epub->getTocItemsCount() + syncOffset;
// Add 2 for sync options (top and bottom) if credentials are configured
const int syncCount = hasSyncOption() ? 2 : 0;
return epub->getTocItemsCount() + syncCount;
}
bool EpubReaderChapterSelectionActivity::isSyncItem(int index) const {
if (!hasSyncOption()) return false;
// First item and last item are sync options
return index == 0 || index == getTotalItems() - 1;
}
int EpubReaderChapterSelectionActivity::tocIndexFromItemIndex(int itemIndex) const {
// Account for the sync option at the top
const int offset = hasSyncOption() ? 1 : 0;
return itemIndex - offset;
}
int EpubReaderChapterSelectionActivity::getPageItems() const {
@@ -52,12 +63,12 @@ void EpubReaderChapterSelectionActivity::onEnter() {
renderingMutex = xSemaphoreCreateMutex();
// Account for sync option offset when finding current TOC index
const int syncOffset = KOREADER_STORE.hasCredentials() ? 1 : 0;
const int syncOffset = hasSyncOption() ? 1 : 0;
selectorIndex = epub->getTocIndexForSpineIndex(currentSpineIndex);
if (selectorIndex == -1) {
selectorIndex = 0;
}
selectorIndex += syncOffset; // Offset for sync option
selectorIndex += syncOffset; // Offset for top sync option
// Trigger first update
updateRequired = true;
@@ -114,17 +125,16 @@ void EpubReaderChapterSelectionActivity::loop() {
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
const int pageItems = getPageItems();
const int totalItems = getTotalItems();
const int syncOffset = KOREADER_STORE.hasCredentials() ? 1 : 0;
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
// Check if sync option is selected
if (syncOffset > 0 && selectorIndex == SYNC_ITEM_INDEX) {
// Check if sync option is selected (first or last item)
if (isSyncItem(selectorIndex)) {
launchSyncActivity();
return;
}
// Get TOC index (account for sync offset)
const int tocIndex = selectorIndex - syncOffset;
// Get TOC index (account for top sync offset)
const int tocIndex = tocIndexFromItemIndex(selectorIndex);
const auto newSpineIndex = epub->getSpineIndexForTocIndex(tocIndex);
if (newSpineIndex == -1) {
onGoBack();
@@ -168,7 +178,6 @@ void EpubReaderChapterSelectionActivity::renderScreen() {
const auto pageWidth = renderer.getScreenWidth();
const int pageItems = getPageItems();
const int totalItems = getTotalItems();
const int syncOffset = KOREADER_STORE.hasCredentials() ? 1 : 0;
const std::string title =
renderer.truncatedText(UI_12_FONT_ID, epub->getTitle().c_str(), pageWidth - 40, EpdFontFamily::BOLD);
@@ -181,12 +190,12 @@ void EpubReaderChapterSelectionActivity::renderScreen() {
const int displayY = 60 + (itemIndex % pageItems) * 30;
const bool isSelected = (itemIndex == selectorIndex);
if (syncOffset > 0 && itemIndex == SYNC_ITEM_INDEX) {
// Draw sync option
if (isSyncItem(itemIndex)) {
// Draw sync option (at top or bottom)
renderer.drawText(UI_10_FONT_ID, 20, displayY, ">> Sync Progress", !isSelected);
} else {
// Draw TOC item (account for sync offset)
const int tocIndex = itemIndex - syncOffset;
// Draw TOC item (account for top sync offset)
const int tocIndex = tocIndexFromItemIndex(itemIndex);
auto item = epub->getTocItem(tocIndex);
renderer.drawText(UI_10_FONT_ID, 20 + (item.level - 1) * 15, displayY, item.title.c_str(), !isSelected);
}

View File

@@ -26,9 +26,18 @@ class EpubReaderChapterSelectionActivity final : public ActivityWithSubactivity
// This adapts automatically when switching between portrait and landscape.
int getPageItems() const;
// Total items including the sync option
// Total items including sync options (top and bottom)
int getTotalItems() const;
// Check if sync option is available (credentials configured)
bool hasSyncOption() const;
// Check if given item index is a sync option (first or last)
bool isSyncItem(int index) const;
// Convert item index to TOC index (accounting for top sync option offset)
int tocIndexFromItemIndex(int itemIndex) const;
static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop();
void renderScreen();

View File

@@ -12,6 +12,11 @@
namespace {
void syncTimeWithNTP() {
// Stop SNTP if already running (can't reconfigure while running)
if (esp_sntp_enabled()) {
esp_sntp_stop();
}
// Configure SNTP
esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL);
esp_sntp_setservername(0, "pool.ntp.org");
@@ -67,8 +72,12 @@ void KOReaderSyncActivity::onWifiSelectionComplete(const bool success) {
}
void KOReaderSyncActivity::performSync() {
// Calculate document hash
documentHash = KOReaderDocumentId::calculate(epubPath);
// Calculate document hash based on user's preferred method
if (KOREADER_STORE.getMatchMethod() == DocumentMatchMethod::FILENAME) {
documentHash = KOReaderDocumentId::calculateFromFilename(epubPath);
} else {
documentHash = KOReaderDocumentId::calculate(epubPath);
}
if (documentHash.empty()) {
xSemaphoreTake(renderingMutex, portMAX_DELAY);
state = SYNC_FAILED;
@@ -413,7 +422,11 @@ void KOReaderSyncActivity::loop() {
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
// Calculate hash if not done yet
if (documentHash.empty()) {
documentHash = KOReaderDocumentId::calculate(epubPath);
if (KOREADER_STORE.getMatchMethod() == DocumentMatchMethod::FILENAME) {
documentHash = KOReaderDocumentId::calculateFromFilename(epubPath);
} else {
documentHash = KOReaderDocumentId::calculate(epubPath);
}
}
performUpload();
}

View File

@@ -11,8 +11,8 @@
#include "fontIds.h"
namespace {
constexpr int MENU_ITEMS = 4;
const char* menuNames[MENU_ITEMS] = {"Username", "Password", "Sync Server URL", "Authenticate"};
constexpr int MENU_ITEMS = 5;
const char* menuNames[MENU_ITEMS] = {"Username", "Password", "Sync Server URL", "Document Matching", "Authenticate"};
} // namespace
void KOReaderSettingsActivity::taskTrampoline(void* param) {
@@ -129,6 +129,14 @@ void KOReaderSettingsActivity::handleSelection() {
updateRequired = true;
}));
} else if (selectedIndex == 3) {
// Document Matching - toggle between Filename and Binary
const auto current = KOREADER_STORE.getMatchMethod();
const auto newMethod = (current == DocumentMatchMethod::FILENAME) ? DocumentMatchMethod::BINARY
: DocumentMatchMethod::FILENAME;
KOREADER_STORE.setMatchMethod(newMethod);
KOREADER_STORE.saveToFile();
updateRequired = true;
} else if (selectedIndex == 4) {
// Authenticate
if (!KOREADER_STORE.hasCredentials()) {
// Can't authenticate without credentials - just show message briefly
@@ -184,6 +192,8 @@ void KOReaderSettingsActivity::render() {
} else if (i == 2) {
status = KOREADER_STORE.getServerUrl().empty() ? "[Not Set]" : "[Set]";
} else if (i == 3) {
status = KOREADER_STORE.getMatchMethod() == DocumentMatchMethod::FILENAME ? "[Filename]" : "[Binary]";
} else if (i == 4) {
status = KOREADER_STORE.hasCredentials() ? "" : "[Set credentials first]";
}