ebf0413fe145fc5c8e863ffc6a42f27957aec2d2
14
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ebf0413fe1 |
feat: add custom sleep timer picker (#2206)
## Summary * **What is the goal of this PR?** Mainly fixes #2196, but also allows users to set a sleep time of 1~30 min or to never sleep. * **What changes are included?** 1. Revert changes from #1948 and #2137 (Sorry @Uri-Tauber) 2. Cherrypick changes from https://github.com/uxjulia/CrossInk/commit/0cb91c3dd1c63f0559d4392fadc4a8340af5bdde and https://github.com/uxjulia/CrossInk/commit/4e6bd634b6ede51ff1a7107ba3e8efcd152bc6ee for a timeout interval picker UI, the custom sleep time feature, and migrate the existing setting in v1.3.0, `sleepTimeout`, to `sleepTimeoutMinutes` (Thanks! @uxjulia) 3. Add a "Never" at the far right of the timeout interval picker --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**PARTIALLY**_ --------- Co-authored-by: Julia <julia@uxj.io> |
||
|
|
36a3a0cc3a |
feat: epub bookmarks (#1337)
Co-authored-by: vedi0boy <nate@origin8publishing.com> Co-authored-by: Uri Tauber <uritaube@gmail.com> |
||
|
|
f39ba7037f |
fix(settings): preserve quick resume timeout preference (#2101)
## Summary ### **What is the goal of this PR?** This fixes an unintended settings side effect when cycling the `Sleep Screen` option through `Quick Resume`. Previously, selecting `Sleep Screen = Quick Resume` globally forced `Quick Resume on Timeout = ON` and left it enabled even after the user toggled `Sleep Screen` to another option within the same settings session. Now the auto-enable behavior is scoped to the Settings screen session: - If `Quick Resume on Timeout` was already `ON` when entering Settings, it stays `ON`. - If it was `OFF`, selecting `Sleep Screen = Quick Resume` temporarily turns it `ON`. - If the user then switches away from `Quick Resume`, it turns back `OFF`. ### **What changes are included?** - Removes the global logic that permanently forced `Quick Resume on Timeout` to `ON` whenever `Sleep Screen` was set to `Quick Resume`, even if it was just due to toggling through the options. - Adds Settings-screen session tracking so `Quick Resume on Timeout` is only auto-enabled while the user has `Sleep Screen = Quick Resume`. - Restores `Quick Resume on Timeout` back to `OFF` when the user switches away, but only if it was `OFF` when they entered Settings. - Preserves existing `ON` timeout preferences. - Same behavior applies to the web settings ## Additional Context - Tested this on device and via the settings UI --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**< YES >**_ |
||
|
|
c44555007b |
feat(settings): modify "Page as Sleep Screen" to "Quick Resume" options (#2089)
## Summary **What is the goal of this PR?** Adds a clearer Quick Resume sleep-screen flow. The previous “Page as Sleep Screen” behavior is now exposed as a dedicated `Sleep Screen > Quick Resume `option, with the timeout-only behavior controlled by a renamed `Quick Resume on Timeout `setting. **What changes are included?** - Adds `Quick Resume` as a new `Sleep Screen` option. - Renames the old `Page as Sleep Screen` setting to `Quick Resume on Timeout`. - Changes that setting’s choices from `Never / After Timeout / Always` to `OFF / ON`. - Makes `Quick Resume on Timeout = ON` equivalent to the old `After Timeout` behavior. - Makes `Sleep Screen > Quick Resume` equivalent to the old `Always` behavior. - Automatically forces `Quick Resume on Timeout` to `ON` when `Sleep Screen` is set to `Quick Resume`. - Renames internal setting references from `seamlessSleepScreen` to `quickResumeSleepScreen`. - Updates translations for the renamed setting label. **Additional Context** - This is mostly a settings/labeling restructure around existing behavior, not a new rendering path. - The runtime quick-resume behavior still uses the existing saved framebuffer / last-screen sleep flow. - Review focus areas: - Sleep entry behavior from manual sleep vs timeout sleep. - The automatic dependency where selecting `Sleep Screen > Quick Resume` sets `Quick Resume on Timeout` to `ON`. --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**< YES >**_ --- **New `Quick Resume` option for `Sleep Screen` will automatically set `Quick Resume on Timeout` to `ON`**: <img width="480" height="800" alt="quick resume" src="https://github.com/user-attachments/assets/94c553fd-a122-47a8-add9-f29694f55566" /> **Example where a different sleep screen setting like `Cover` can be used in combination with the `Quick Resume on Timeout` setting**: <img width="480" height="800" alt="cover + quick resume" src="https://github.com/user-attachments/assets/dd18ce18-230b-4b78-808b-ac85f5e7d5d8" /> |
||
|
|
41e6e15229 |
feat: Seamless sleep/wake screens for displaying book pages during deep sleep (#2064)
<img width="605" height="454" alt="image" src="https://github.com/user-attachments/assets/bfd84afe-3b58-436e-9a5d-539af3ec3d4e" /> Actualized "Last" sleep screen setting from previous PRs, rebranded as a ~~`Seamless Sleep`~~ `Page as Sleep Screen` option with more improvements. https://github.com/user-attachments/assets/59029ba6-007e-4841-abfa-f680d7e98b79 --- New option: `Page as Sleep Screen` - `Never (default)`, `After Timeout`, `Always` When enabled, it seamlessly sleeps on timeout or power off, making a fast refresh for the moon icon. When waking up, we still show the last page, instead of the boot screen, making it fully seamless. I tried different icons such as "refresh arrow" and others, but they looked not as nice as 3 simple dots. With this mode, the device turns off 4 seconds faster. And has 6 seconds less delay when turning back on. Much more responsive. Previously, even a 10-minute timeout sometimes wasn't enough, and I'd worry about seeing the book cover. It's now easier to use a shorter sleep timeout: if I get distracted during a reading session but don't want to stop, the new screen is much more inviting to come back to. --- Did you use AI tools to help write this code? _**PARTIALLY**_. --- Test v1.3.0 firmware.bin file [download](https://github.com/user-attachments/files/28015193/firmware.zip) Based on PRs #410 and #495 Closes #400, #1649 --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
6f7f4c592a |
refactor: Eliminated relative path includes (#1961)
## Summary Relative includes can hide inappropriate dependency relationships. In this case, I found that lib/KOReaderSync/KOReaderCredentialStore.cpp was dependent on src/JsonSettingsIO.h -- a lib -> app dependency, the opposite direction dependencies should flow in this project. This change replaces all relative includes with root-relative includes, and corrects the KOReaderCredentialStore dependency by moving its JSON settings serialization local to the KOReaderSync library. --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**PARTIALLY**_ |
||
|
|
7993b2bb97 |
feat: add SD card font support with on-device download and web management
Add a complete SD card font subsystem that enables users to install and use custom fonts beyond the three built-in families. This combines the back-end firmware support (#1327) with the font configuration, build pipeline, CI distribution, and user-facing management UI (#1392). Core font system: - Custom .cpfont binary format (v4) with multi-style support (regular, bold, italic, bold-italic) packed into a single file per size - On-demand glyph loading from SD card with two-pass prewarm rendering to bulk-read glyphs per page, achieving near-flash performance for Latin text (~697ms vs ~681ms) and viable CJK rendering (~32% slower) - Persistent advance cache for layout measurement without SD I/O - Overflow ring buffer for glyph cache misses during rendering - Memory-conscious design: only advance tables kept in RAM; glyph bitmaps, kern tables, and ligatures loaded on demand from SD Font management: - On-device WiFi download from GitHub Releases with manifest-based discovery, install/update detection, and progress UI - Web interface font upload, listing, and deletion via /fonts page - Manual SD card copy to /fonts/ or /.fonts/ directories - Font selection integrated into Settings > Reader > Font Family Build pipeline: - Declarative YAML config (sd-fonts.yaml) as single source of truth for the 17-family font library (serif, sans, mono, accessibility) - Python converter (fontconvert_sdcard.py) for TTF/OTF to .cpfont with FreeType rasterization, class-based kerning, and ligature extraction - Parallel build orchestrator with variable font instance extraction - CI workflow publishing versioned + stable releases to a dedicated crosspoint-fonts repository with auto-incrementing revision tags - Centralized version constants (cpfont_version.py) shared across build tooling and CI, with firmware headers as manual sync points Additional fixes: - CJK characters no longer get hyphens inserted at line breaks - Advance table eliminates 30+ second stalls during CJK section indexing for paragraphs with >512 unique codepoints Closes #930 Co-authored-by: Zach Nelson <zach@zdnelson.com> Co-authored-by: Justin <itsthisjustin@users.noreply.github.com> Co-authored-by: jpirnay <jens@pirnay.com> Co-authored-by: mcrosson <kemonine@kemonine.info> |
||
|
|
b25389b43e |
refactor: Move language setting into JSON settings (#1796)
## Summary
Another follow up to #1408. That PR revised the language.bin settings
file to version 2. After merging, it occurred to me that it would make
more sense to have the language inside the overall settings.json file,
easy for users to see and change directly.
This change adds migration from language.bin to settings.json. It only
migrates from the pre-#1408 v1 language.bin format, because v2 has only
been in for a couple of commits and I don't think we need long-term
maintenance code to handle it.
#1408 had a potential long-term maintenance issue for migration. It
assumed that the enum order of languages never changes, only grows. So,
e.g. "Portuguese (Portugal)" could never be added next to "Portuguese
(Brazil)", only appended. This change includes a hardcoded frozen
ordering of the language indices as they existed at
|
||
|
|
1cf2239742 |
feat: Support for multiple OPDS servers (#1209)
## Summary * Add support for configuring and using multiple OPDS servers, replacing the previous single-server limitation. Closes https://github.com/crosspoint-reader/crosspoint-reader/issues/1178 * New OpdsServerStore singleton (modeled after WifiCredentialStore) that persists up to 8 OPDS servers to /.crosspoint/opds.json with MAC-based password obfuscation. * One-time migration from legacy single-server fields in CrossPointSettings to the new store on first boot. * New OpdsServerListActivity for the device UI — works in two modes: a settings list (add/edit/delete servers) and a picker (select which server to browse). When only one server is configured, the picker is skipped automatically. * Renamed CalibreSettingsActivity → OpdsSettingsActivity for clarity. It now edits individual OpdsServer entries (name, URL, username, password, delete). * OpdsBookBrowserActivity now receives an OpdsServer at construction and uses its credentials for all fetches/downloads, and shows the server name in the header. * HttpDownloader::fetchUrl and downloadToFile accept optional per-call username/password parameters instead of reading from global settings. * REST API endpoints on CrossPointWebServer: GET /api/opds, POST /api/opds, POST /api/opds/delete — passwords are never exposed over the API (only a hasPassword flag), and omitting the password field on update preserves the existing one. * Web UI (SettingsPage.html) with dynamic OPDS server management cards — add, edit, save, and delete servers from the browser. <img width="932" height="906" alt="SCR-20260416-stvu" src="https://github.com/user-attachments/assets/a8f18d84-4204-46a0-bb31-b73d24b3255f" /> ## Additional Context * The OpdsServerStore JSON format and obfuscation scheme are identical to WifiCredentialStore, so the same JsonSettingsIO infrastructure handles both. * The web API uses POST /api/opds/delete instead of DELETE /api/opds because the ESP32 WebServer doesn't support the DELETE method with a request body. * Existing single-server configurations are migrated automatically — no user action required. After migration the legacy CrossPointSettings fields are cleared so it only runs once. * The HttpDownloader changes are backward-compatible: the credential parameters default to empty strings, so existing callers are unaffected. --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**< YES >**_ --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> |
||
|
|
c4f5c8e931 |
fix: prevent wallpaper clustering with 16-entry recency buffer (#1606)
## Problem Custom sleep wallpapers feel repetitive — the same image appearing multiple times in a short session. Only the single most-recently-shown index was stored (`uint8_t lastSleepImage`), so on collections of 3–5 images, two-pick cycles were common. On larger collections, any image could reappear within the next few picks. ## Solution Add a 16-entry circular recency buffer to `CrossPointState` that excludes recently shown wallpapers from selection. **Recency buffer** (`recentSleepImages[16]`, `recentSleepPos`, `recentSleepFill` — 34 bytes DRAM): - Tracks the last 16 shown wallpaper indices - On each pick, rerolls up to 20 times if the candidate was recently shown - Window auto-shrinks to `numFiles - 1` for small collections (guarantees a non-repeat is always possible) - `isRecentSleep()` clamps to `recentSleepFill` to avoid false positives on unwritten buffer slots - State persisted to `state.json` so the buffer survives sleep/wake cycles **Migration**: - Binary (`state.bin`): old `lastSleepImage` field seeded into the new buffer if valid - JSON (`state.json`): legacy `lastSleepImage` key detected and seeded into the buffer when upgrading from older firmware ## Index type fix `randomFileIndex` upgraded from `uint8_t` to `uint16_t` — silently truncated for collections larger than 255 wallpapers. ## Repeat probability: before vs after Chance of seeing a recently-shown image on the next pick. After: `(numFiles - 16) / numFiles` once collection exceeds the window; 0% while ≤17. | Collection | Before | After | |---|---|---| | 3 | 50% | 0% | | 5 | 75% | 0% | | 10 | 89% | 0% | | 17 | 94% | 0% | | 18 | 94% | 6% | | 20 | 95% | 20% | | 25 | 96% | 36% | | 30 | 97% | 47% | | 50 | 98% | 68% | | 100 | 99% | 84% | ## Memory impact | Addition | Size | |---|---| | `recentSleepImages[16]` | 32 bytes DRAM | | `recentSleepPos` + `recentSleepFill` | 2 bytes DRAM | | **Total** | **34 bytes DRAM** | ## Files changed - `src/CrossPointState.h` — recency buffer fields + `isRecentSleep()` / `pushRecentSleep()` declarations - `src/CrossPointState.cpp` — `isRecentSleep()` / `pushRecentSleep()` implementations + binary migration path - `src/JsonSettingsIO.cpp` — JSON serialisation of buffer state + JSON migration - `src/activities/boot_sleep/SleepActivity.cpp` — retry loop with recency check Co-authored-by: Patryk Radtke <patryk@Patryks-MacBook-Pro.local> |
||
|
|
79b54b3a75 |
fix: Init lastSleepImage (edge case) (#1360)
## Summary * **What is the goal of this PR?** fix edge case for definition ## Additional Context If loadFromFile() returns false (no state file exists — first boot, or SD missing), lastSleepImage is never set and contains garbage. [SleepActivity.cpp:83] then uses it in a while comparison to avoid repeating the same image. The JSON path (doc["lastSleepImage"] | (uint8_t)0) handles it, but only if the file exists. --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _** NO **_ |
||
|
|
04242fa221 |
refactor: Simplify new setting introduction (#1086)
## Summary
* **What is the goal of this PR?** Eliminate the 3-file / 4-location
overhead for adding a new setting. Previously, every new setting
required manually editing JsonSettingsIO.cpp in two places (save +
load), duplicating knowledge already present in SettingsList.h. After
this PR, JsonSettingsIO.cpp never needs to be touched again for standard
settings.
* **What changes are included?**
* `SettingInfo` (in `SettingsActivity.h`) gains one new field: `bool
obfuscated` (base64 save/load for passwords), with a fluent builder
method `.withObfuscated()`. The previously proposed
`defaultValue`/`withDefault()` approach was dropped in favour of reading
the struct field's own initializer value as the fallback (see below).
* `SettingsList.h` entries are annotated with `.withObfuscated()` on the
OPDS password entry. The list is now returned as a `static const`
singleton (`const std::vector<SettingInfo>&`), so it is constructed
exactly once. A missing `key`/`category` on the
`statusBarProgressBarThickness` entry was also fixed — it was previously
skipped by the generic save loop, so changes were silently lost on
restart.
* `JsonSettingsIO::saveSettings` and `loadSettings` replace their ~90
lines of manual per-field code with a single generic loop over
`getSettingsList()`. The loop uses `info.key`,
`info.valuePtr`/`info.stringOffset`+`info.stringMaxLen` (for char-array
string fields), `info.enumValues.size()` (for enum clamping), and
`info.obfuscated`.
* **Default values**: instead of a duplicated `defaultValue` field in
`SettingInfo`, `loadSettings` reads `s.*(info.valuePtr)` *before*
overwriting it. Because `CrossPointSettings` is default-constructed
before `loadSettings` is called, this captures each field's struct
initializer value as the JSON-absent fallback. The single source of
truth for defaults is `CrossPointSettings.h`.
* One post-loop special case remains explicitly: the four `frontButton*`
remap fields (managed by the RemapFrontButtons sub-activity, not in
SettingsList) and `validateFrontButtonMapping()`.
* One pre-loop migration guard handles legacy settings files that
predate the status bar refactor: if `statusBarChapterPageCount` is
absent from the JSON, `applyLegacyStatusBarSettings()` is called first
so the generic loop picks up the migrated values as defaults and applies
its normal clamping.
* OPDS password backward-compat migration (plain `opdsPassword` →
obfuscated `opdsPassword_obf`) is preserved inside the generic
obfuscated-string path.
## Additional Context
Say we want to add a new `bookmarkStyle` enum setting with options
`DOT`, `LINE`, `NONE` and a default of `DOT`:
1. `src/CrossPointSettings.h` — add enum and member:
```cpp
enum BOOKMARK_STYLE { BOOKMARK_DOT = 0, BOOKMARK_LINE = 1, BOOKMARK_NONE = 2 };
uint8_t bookmarkStyle = BOOKMARK_DOT;
```
2. `lib/I18n/translations/english.yaml` — add display strings:
```yaml
STR_BOOKMARK_STYLE: "Bookmark Style"
STR_BOOKMARK_DOT: "Dot"
STR_BOOKMARK_LINE: "Line"
```
(Other language files will fall back to English if not translated. Run
`gen_i18n.py` to regenerate `I18nKeys.h`.)
3. `src/SettingsList.h` — add one entry in the appropriate category:
```cpp
SettingInfo::Enum(StrId::STR_BOOKMARK_STYLE, &CrossPointSettings::bookmarkStyle,
{StrId::STR_BOOKMARK_DOT, StrId::STR_BOOKMARK_LINE, StrId::STR_NONE_OPT},
"bookmarkStyle", StrId::STR_CAT_READER),
```
That's it — no default annotation needed anywhere, because
`bookmarkStyle = BOOKMARK_DOT` in the struct already provides the
fallback. The setting will automatically persist to JSON on save, load
with clamping on boot, appear in the device settings UI under the Reader
category, and be exposed via the web API — all with no further changes.
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**< PARTIALLY>**_
|
||
|
|
2d49c7b7b4 |
feat: split status bar setting (#733)
## Summary This PR aims to reduce the complexity of the status bar by splitting the setting into 5: - Chapter Page Count - Book Progress % - Progress Bar - Chapter Title - Battery Indicator These are located within the new StausBarSettings activity, which also shows a preview of the bar the user has created <img width="513" height="806" alt="image" src="https://github.com/user-attachments/assets/cdf852fb-15d8-4da2-a74f-fd69294d7b05" /> <img width="483" height="797" alt="image" src="https://github.com/user-attachments/assets/66fc0c0d-ee51-4d31-b70d-e2bc043205d1" /> When updating from a previous version, the user's past settings are honoured. ## Additional Context The PR aims to remove any duplication of status bar code where possible, and extracts the status bar rendering into a new component - StatusBar It also adds a new (optional) padding option to the progress bar to allow the status bar to be shifted upwards - this is only intended for use in the settings. --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? No - although did help to decode some C++ errors --------- Co-authored-by: Arthur Tazhitdinov <lisnake@gmail.com> |
||
|
|
6e4d0e534d |
feat: Migrate binary settings to json (#920)
## Summary
* This PR introduces a migration from binary file storage to JSON-based
storage for application settings, state, and various credential stores.
This improves readability, maintainability, and allows for easier manual
configuration editing.
* Benefits:
- Settings files are now JSON and can be easily read/edited manually
- Easier to inspect application state and settings during development
- JSON structure is more flexible for future changes
* Drawback: around 15k of additional flash usage
* Compatibility: Seamless migration preserves existing user data
## Additional Context
1. New JSON I/O Infrastructure files:
- JsonSettingsIO: Core JSON serialization/deserialization logic using
ArduinoJson library
- ObfuscationUtils: XOR-based password obfuscation for sensitive data
2. Migrated Components (now use JSON storage with automatic binary
migration):
- CrossPointSettings (settings.json): Main application settings
- CrossPointState (state.json): Application state (open book, sleep
mode, etc.)
- WifiCredentialStore (wifi.json): WiFi network credentials (Password
Obfuscation: Sensitive data like WiFi passwords, uses XOR encryption
with fixed keys. Note: This is obfuscation, not cryptographic security -
passwords can be recovered with the key)
- KOReaderCredentialStore (koreader.json): KOReader sync credentials
- RecentBooksStore (recent.json): Recently opened books list
3. Migration Logic
- Forward Compatibility: New installations use JSON format
- Backward Compatibility: Existing binary files are automatically
migrated to JSON on first load
- Backup Safety: Original binary files are renamed with .bak extension
after successful migration
- Fallback Handling: If JSON parsing fails, system falls back to binary
loading
4. Infrastructure Updates
- HalStorage: Added rename() method for backup operations
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _** YES**_
---------
Co-authored-by: Dave Allie <dave@daveallie.com>
|