## Summary Make KOReader progress sync a one-click flow for common cases while keeping the existing manual mode available. ### Changes - Add a **Sync Behavior** setting: - **Smart sync** for new configurations - **Ask every time** for manual control - Preserve **Ask every time** when migrating an existing credential file that predates this setting. - In Smart mode: - upload local progress when no remote record exists; - show a short confirmation when already synced; - upload when local progress is further ahead; - apply remote progress when remote progress is further ahead. - Probe both KOReader document-matching hashes before making the Smart decision, while keeping uploads on the user's configured matching method. - Auto-return after successful Smart terminal states, with Back/Confirm still available. - Persist the setting alongside the current credential, matching-method, and send-metadata fields. - Document both behaviors in the user guide. ## Additional context This pairs well with #2189 (smart Wi-Fi auto-connect), but does not depend on it. It does not add background Wi-Fi or passive network detection; sync is still triggered by the user from the reader menu. Smart sync uses the furthest progress because CrossPoint does not currently persist local progress timestamps. Users who prefer explicit conflict resolution can select **Ask every time**. ## Verification - `git diff --check` - `platformio check --fail-on-defect low --fail-on-defect medium --fail-on-defect high` — no defects - `platformio run -e default` — firmware build successful - The original implementation was manually smoke-tested on an X4 device. --- ### AI Usage Did you use AI tools to help write this code? _**YES**_ AI tools were used to inspect the codebase, draft and review the implementation, resolve the rebase against current `develop`, and prepare the PR text. The resulting firmware was built locally. --------- Co-authored-by: Alexander Hoffer <git@alexanderhoffer.com> Co-authored-by: Alexander Hoffer <contact@alexanderhoffer.com>
83 lines
3.0 KiB
C++
83 lines
3.0 KiB
C++
#pragma once
|
|
#include <ArduinoJson.h>
|
|
#include <PersistableStore.h>
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
|
|
// Document matching method for KOReader sync
|
|
enum class DocumentMatchMethod : uint8_t {
|
|
FILENAME = 0, // Match by filename (simpler, works across different file sources)
|
|
BINARY = 1, // Match by partial MD5 of file content (more accurate, but files must be identical)
|
|
};
|
|
|
|
// How manual "Sync Progress" resolves differences after fetching remote progress.
|
|
enum class KOReaderSyncBehavior : uint8_t {
|
|
ASK_EVERY_TIME = 0, // Preserve legacy behavior: always show Apply/Upload choices.
|
|
SMART = 1, // Auto-resolve simple cases using furthest progress.
|
|
};
|
|
|
|
/**
|
|
* Singleton class for storing KOReader sync credentials on the SD card.
|
|
* Passwords are XOR-obfuscated with the device's unique hardware MAC address
|
|
* and base64-encoded before writing to JSON (not cryptographically secure,
|
|
* but prevents casual reading and ties credentials to the specific device).
|
|
*/
|
|
|
|
class KOReaderCredentialStore : public PersistableStore<KOReaderCredentialStore> {
|
|
private:
|
|
std::string username;
|
|
std::string password;
|
|
std::string serverUrl; // Custom sync server URL (empty = default)
|
|
DocumentMatchMethod matchMethod = DocumentMatchMethod::FILENAME; // Default to filename for compatibility
|
|
bool sendMetadata = false; // Send document metadata with progress sync
|
|
KOReaderSyncBehavior syncBehavior = KOReaderSyncBehavior::SMART;
|
|
|
|
// Private constructor for singleton
|
|
KOReaderCredentialStore() = default;
|
|
~KOReaderCredentialStore() = default;
|
|
|
|
friend class PersistableStore<KOReaderCredentialStore>;
|
|
|
|
public:
|
|
static const char* getFilePath() { return "/.crosspoint/koreader.json"; }
|
|
void toJson(JsonDocument& doc) const;
|
|
bool fromJson(JsonVariantConst doc);
|
|
|
|
// Credential management
|
|
void setCredentials(const std::string& user, const std::string& pass);
|
|
const std::string& getUsername() const { return username; }
|
|
const std::string& getPassword() const { return password; }
|
|
|
|
// Get MD5 hash of password for API authentication
|
|
std::string getMd5Password() const;
|
|
|
|
// Check if credentials are set
|
|
bool hasCredentials() const;
|
|
|
|
// Clear credentials
|
|
void clearCredentials();
|
|
|
|
// Server URL management
|
|
void setServerUrl(const std::string& url);
|
|
const std::string& getServerUrl() const { return serverUrl; }
|
|
|
|
// Get base URL for API calls (with http:// normalization if no protocol, falls back to default)
|
|
std::string getBaseUrl() const;
|
|
|
|
// Document matching method
|
|
void setMatchMethod(DocumentMatchMethod method);
|
|
DocumentMatchMethod getMatchMethod() const { return matchMethod; }
|
|
|
|
// Send metadata setting
|
|
void setSendMetadata(bool enabled);
|
|
bool getSendMetadata() const { return sendMetadata; }
|
|
|
|
// Sync behavior
|
|
void setSyncBehavior(KOReaderSyncBehavior behavior);
|
|
KOReaderSyncBehavior getSyncBehavior() const { return syncBehavior; }
|
|
};
|
|
|
|
// Helper macro to access credential store
|
|
#define KOREADER_STORE KOReaderCredentialStore::getInstance()
|