c15b5b9bc5fafd8241f7c3689fbc9494327f37e5
13
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
77b2c31635 |
refactor: redesign on-screen keyboard (#1644)
# Refactor: Redesign On-Screen Keyboard ## Summary Complete redesign of the on-screen keyboard (used for WiFi password, KOReader, Calibre URLs) with improved layout, navigation, visual style, and new input features: **cursor mode** for text navigation, **password mode** with visibility toggle, and **URL mode** with pre-defined snippets. ## Screenshots ### Base Theme |**master** | **PR #1644** | |----------|-------------| | <img width="480" height="800" alt="image" src="https://github.com/user-attachments/assets/49125857-12d0-4020-b872-05d0ddbf1d94" /> | <img width="480" height="800" alt="image" src="https://github.com/user-attachments/assets/ad16656b-d66e-43dd-8697-2b85f709d7f8" /> | ### Lyra Theme | **master** | **PR #1644** | |----------|-------------| | <img width="480" height="800" alt="image" src="https://github.com/user-attachments/assets/d9901251-9154-48d2-83b4-376d3223d132" /> | <img width="480" height="800" alt="image" src="https://github.com/user-attachments/assets/84b45949-ed61-4924-af1b-d570c4c61e13" /> | ### Keyboard States | ABC Mode | Symbol Mode | URL Mode | |----------|-------------|----------| | <img width="480" height="280" alt="image" src="https://github.com/user-attachments/assets/6397a82e-50b8-4d03-92e0-f707ed7c9054" /> | <img width="480" height="280" alt="image" src="https://github.com/user-attachments/assets/205d34fe-0413-49e9-9db2-30569c297ba0" /> | <img width="480" height="280" alt="image" src="https://github.com/user-attachments/assets/801ddeab-e082-4a22-a9df-c66adb1afc16" /> | | Cursor Mode | Password Toggle | |-------------|-----------------| | <img width="480" height="800" alt="image" src="https://github.com/user-attachments/assets/841773e1-7a3c-45e5-aa89-e5787255608c" /> | <img width="480" height="800" alt="image" src="https://github.com/user-attachments/assets/9487a0b3-3f47-41ed-9cd3-b24e56e342a8" /> | ## Changes ### Layout (10-column uniform grid) - Reduced from 13/11/10 columns per row to **10 uniform columns** across all rows - Keyboard now uses **90% of screen width** (was ~66%) - Row 0: Numbers `1-9, 0` with secondary symbols (`!@#$%^&*()`) - Rows 1-3: Standard QWERTY letters - Bottom row: `shift` | `#@!` | `___` | `←` | `OK` ### New Symbol Mode (#@!) - New mode toggle key `#@!` / `abc` switches between letter and symbol layouts - Symbol layout: 4 rows (numbers, inverted symbols, paired symbols, loose symbols) - Covers all 95 printable ASCII characters - **No secondary hints, no long-press** in symbol mode (simple and direct) - SHIFT key remains visible but **disabled** in symbol mode ### URL Mode - In `InputType::Url`, the Space key becomes a **URL toggle** button - Activating URL mode replaces the 4 content rows with a **3×3 grid of URL snippets**: - Col 0 (protocols): `https://`, `http://`, `/opds` - Col 1 (hosts/ports): `www.`, `192.168.`, `:8080` - Col 2 (domains): `.com`, `.org`, `.net` - Snippets are inserted as full strings at the cursor position - URL mode **persists** after inserting a snippet (does not auto-deactivate) - Column alignment: col 0 over ABC, col 1 over URL, col 2 over Del - Up/Down navigation maps `bottomCol - 1` / `urlCol + 1` - SHIFT disabled in URL mode - SpecMode (`abc`) exits URL mode back to ABC - SpecSpace (`URL`) toggles URL mode on/off; selection always stays on the URL button - Button styled with `KeyboardKeyType::Mode` for consistent outline ### Cursor Mode - **Enter**: Long-press Up (500ms) while in keyboard mode - **Exit**: Short-press Down while in cursor mode (resets `passwordVisible`, clears toggle position) - **Navigate**: Left/Right move cursor position within text (one position per press, no continuous repeat) - **Visual**: - Keyboard mode: underline cursor (2px line + serifs) - Cursor mode: inverted block cursor (black fill + white character) - Block width adapts to the actual character width under cursor (minimum 6px for narrow chars like space) - Block position includes inter-character kerning offset for correct alignment (calculated via string-difference: `getTextWidth(before+char) - getTextWidth(before) - getTextWidth(char)`) - End-of-text: thin 6px block - Password hidden: 3-part drawing (Part 1 + block + Part 3) to prevent block overflow onto `*` characters - Toggle position: caret ("I") cursor at saved position, `[abc]`/`[***]` label with inverted selection - **Inactive key styling**: - BaseTheme: 2px outline rectangle - LyraTheme: gray filled rounded rectangle (`Color::LightGray`) - **Password toggle position**: in cursor mode (Password only), Hold Right (500ms) enters toggle — caret cursor shows saved position, `[abc]`/`[***]` label becomes selected. Press Confirm to toggle `passwordVisible`. Press Left to restore cursor to saved position. Right from toggle is a no-op. Down from toggle exits to keyboard. - `cursorPos` persists between keyboard and cursor modes ### Password Mode - `InputType::Password` enum replaces `bool isPassword` parameter - Text is masked with `*` except for one revealed character: - Keyboard mode: reveals character at `cursorPos - 1` - Cursor mode: no reveal in display text (block cursor draws actual char directly) - **Toggle `[abc]`/`[***]`**: accessible via cursor mode — Hold Right (500ms) enters toggle position, Confirm toggles visibility, Left exits back to cursor. Caret ("I") shown at saved position while in toggle. - `passwordVisible` resets to `false` when exiting cursor mode - **Long-press Del (1.5s)**: clears all text and resets cursor to 0 ### InputType Enum - Replaced `bool isPassword` constructor parameter with `enum class InputType { Text, Password, Url }` - Callers updated: `WifiSelectionActivity`, `KOReaderSettingsActivity`, `CalibreSettingsActivity` ### Contextual Tips - `"Tips:"` header followed by context-sensitive hints, centered between text field underline and keyboard as a block - ABC mode: `"Hold SELECT for UPPERCASE or secondary char"` (shift ON: `"lowercase"` variant) + `"Hold DEL to clear all text"` (only if text not empty) - ABC + `InputType::Url`: same + `"Press URL for snippets"` - Symbol mode: `"Hold DEL to clear all text"` (only if text not empty) - URL mode: `"Press ABC to exit URL mode"` + `"Hold DEL to clear all text"` (only if text not empty) - Cursor mode: `"Press DOWN to return to keyboard"` ### Hint Phases (cursor mode, Password only) - **Phase 1**: `"Hold UP to edit entry"` — shown after 2× DEL press, auto-hides after 4s, positioned below underline - **Phase 2**: `"Press < or > to move cursor"` + dynamic password toggle hint — shown when entering cursor mode, positioned below underline, visible until exit - When `!passwordVisible`: `"Hold > then press [abc] to show password"` - When `passwordVisible`: `"Hold > then press [***] to hide password"` - When in toggle position: `"Press < to return to cursor position"` ### Long-Press Alternative Character - Holding Confirm (>500ms) inserts the **alternative character** instead of the primary - Letters: long-press inserts opposite case (e.g., `a`→`A`, `A`→`a`) - Numbers/symbols (row 0): long-press inserts secondary (e.g., `0`→`)`, `)`→`0`) - Only active in ABC mode; disabled in Symbol mode and URL mode - **`InputType::Url`**: Hold SELECT on ABC rows 1+ (letters) returns primary character only (same as short press). Row 0 (symbols) still returns secondary character on Hold SELECT. ### Shift (2 sticky states) - Reduced from 3 states (shift/SHIFT/LOCK) to **2 sticky states** (shift/SHIFT) - Shift stays active after typing until manually toggled off - Label: `shift` (off) / `SHIFT` (on) ### SpecialKeyType Enum - `enum class SpecialKeyType { Shift, Mode, Space, Del, Ok }` replaces plain `enum` (`SpecShift`, `SpecMode`, etc.) for type safety - All switch cases updated to `SpecialKeyType::*` with `static_cast<int>()` for array indexing - `onExit()` reverted to simple `Activity::onExit()` call (half-refresh removed) - **Bottom row column mapping**: navigating up/down between content rows and bottom row uses `col/2` and `col*2` formulas for consistent positioning (10 cols ↔ 5 cols) - **URL mode column mapping**: `bottomCol - 1` / `urlCol + 1` (3 cols ↔ 5 cols) - **Wrap-around**: row 0 → up → bottom row and bottom row → down → row 0 both apply correct column mapping ### Visual Improvements (both Base and Lyra themes) - **Space key**: underscore-style horizontal line (60% of key width, 3px thick) - **Delete key**: arrow `←` drawn with lines (3px thick) instead of "DEL" text - **Secondary label** (ABC row 0): small hint in top-right corner with separation from primary number - **BaseTheme**: selection uses **inverted fill** (black rect + white text) instead of `[bracket]` markers - **BaseTheme**: text field brackets drawn as **stretchable lines** that adapt to multi-line input (1px normal, 3px cursor mode) - **LyraTheme**: text field uses **fixed-width underline** (16px margins, 8px each side) instead of stretchable line (2px normal, 3px cursor mode) - **Both themes**: special keys (shift, mode, space, del, OK) have bordered/bordered-rounded rectangles - **Font size**: keyboard uses `UI_12_FONT_ID` in both themes (was `UI_10` in Base) - **Key height**: 40px in all themes for better proportions - **Layout unification**: text and password toggle are left-aligned in all themes (`keyboardCenteredText = false` for Lyra/Lyra3Covers) - **`primaryOffset` removed**: dead code eliminated from BaseTheme and LyraTheme `drawKeyboardKey` ### New Theme Metrics - `keyboardVerticalOffset`: per-theme vertical adjustment of keyboard position - Base: `-13`, Lyra: `-7` - `keyboardBottomKeySpacing`: independent spacing for bottom row keys - Base: `5`, Lyra: `5` - Bottom-aligned keyboard in both themes for consistent vertical positioning - Bottom row total width calculated to match content rows width (10-col based, consistent across modes) - 4px extra gap between content rows and bottom row when `bkSpacing > 0` - `keyboardCenteredText`: `false` for all themes (unified left-aligned text) ### Defensive Improvements - **State reset on re-entry**: `onEnter()` resets all mutable state (`symMode`, `urlMode`, `cursorMode`, `togglePos`, `passwordVisible`, `shiftState`, `selectedRow`, `selectedCol`, `rightHeld`, `rightLongHandled`, `savedCursorPos`, `rightStartCursorPos`, `delPressCount`, `hintVisible`, `hintShowTime`) — prevents stale state when re-entering the keyboard - **Bounds checking**: `insertChar`/`insertString` clamp `cursorPos` to `text.length()` before inserting - **Empty string guard**: `insertString` returns early on empty string - **`std::string::npos`**: used instead of `SIZE_MAX` for size_t sentinel (proper C++ idiom) - **`<algorithm>` header**: included for `std::max` ## Files Modified | File | Changes | |------|---------| | `src/activities/util/KeyboardEntryActivity.h` | `InputType` enum, `KeyDef` struct, 10-col layouts, cursor/password/URL/toggle state, hints (`delPressCount`, `hintVisible`, `hintShowTime`), held vars (`rightHeld`, `rightLongHandled`, `savedCursorPos`, `rightStartCursorPos`), `mapColContentBottom` helper | | `src/activities/util/KeyboardEntryActivity.cpp` | Complete rewrite: layout rendering, symbol/cursor/password/URL modes, toggle position, long-press, contextual tips, hint phases, block cursor kerning alignment, defensive bounds checks, state reset | | `src/components/themes/BaseTheme.h` | `KeyboardKeyType` enum, new `drawTextField`/`drawKeyboardKey` signatures, `keyboardVerticalOffset`, `keyboardBottomKeySpacing` metrics | | `src/components/themes/BaseTheme.cpp` | Redesigned `drawTextField` (stretchable brackets), `drawKeyboardKey` (inverted selection, space/delete graphics, secondary label, inactive selection), removed `primaryOffset` dead code | | `src/components/themes/lyra/LyraTheme.h` | Override signatures, `keyboardVerticalOffset`, `keyboardBottomKeySpacing`, `keyboardKeyHeight` adjustments | | `src/components/themes/lyra/LyraTheme.cpp` | `drawTextField` (fixed underline), `drawKeyboardKey` (rounded rects for special keys, space/delete graphics, secondary label, inactive selection), removed `primaryOffset` dead code | | `src/components/themes/lyra/Lyra3CoversTheme.h` | `keyboardCenteredText = false`, `keyboardVerticalOffset = -7`, inherits Lyra overrides | | `src/activities/network/WifiSelectionActivity.cpp` | `bool isPassword` → `InputType::Password` | | `src/activities/settings/KOReaderSettingsActivity.cpp` | `bool isPassword` → `InputType::Text`/`InputType::Password`/`InputType::Url` | | `src/activities/settings/CalibreSettingsActivity.cpp` | `bool isPassword` → `InputType::Text`/`InputType::Password`/`InputType::Url` | ## Backward Compatibility - **API change**: Constructor parameter changed from `bool isPassword` to `InputType inputType` (default `InputType::Text`) - **All callers updated**: WiFi, KOReader, and Calibre integrations migrated to new `InputType` enum ## Testing ### Input & Text Handling - [x] Empty input → press OK (submit empty string) - [x] Back button → cancel (no text returned) - [x] Pre-filled initial text (e.g., editing existing WiFi password) - [x] Password mode: text masked with `*` characters, one character revealed - [x] Delete on empty text (no crash) - [x] Very long text near maxLength limit - [x] URL with path and port (~60 chars) - [x] Multi-line text wrapping in input field - [x] Space insert in middle of text (cursor mode) - [x] Delete last character repeatedly - [ ] Type all 95 printable ASCII characters ### Mode Switching - [x] ABC → #@! preserves typed text and cursor position - [x] #@! → ABC preserves typed text and cursor position - [x] Shift state preserved when switching modes - [x] Switch modes multiple times rapidly ### Shift Behavior - [x] Shift OFF → type letter → inserts lowercase, shift stays OFF - [x] Shift ON → type letter → inserts uppercase, shift stays ON - [x] Shift ON → type number → inserts symbol, shift stays ON - [x] Shift ON → navigate rows → shift stays ON - [x] Shift ON → switch to #@! → shift shows "shift" (disabled) - [x] Shift ON → switch to ABC → shift state preserved - [x] Shift ON → switch to URL → shift shows "shift" (disabled) - [x] Shift disabled in URL mode: pressing shift does nothing ### Long-Press - [x] Long-press letter with shift OFF → inserts uppercase - [x] Long-press letter with shift ON → inserts lowercase - [x] Long-press number → inserts secondary symbol - [x] Long-press symbol (row 0) → inserts opposite (number) - [x] Long-press key without secondary (e.g., `-`, `=` in rows 2-3) → inserts primary character on release - [x] Long-press on special keys (shift, mode, space, del, ok) → no alternative inserted - [x] Long-press in #@! mode → no effect (disabled) - [x] Long-press in URL mode → no effect (disabled) - [x] Long-press number in row 0 with InputType::Url → inserts secondary symbol (same as non-URL) - [x] Short press after cancelled long-press → normal behavior - [x] Long-press at maxLength → no character inserted - [x] Long-press Del (1.5s) → clears all text ### Cursor Mode - [x] Long-press Up → enters cursor mode - [x] Short-press Down → exits cursor mode (resets passwordVisible) - [x] Left/Right navigate within text - [x] Left at position 0 → no movement - [x] Right at end of text → no movement in Text mode, enters toggle in Password mode (Hold Right) - [x] Block cursor visual: correct width for character, thin block at end - [x] Underline cursor visual (keyboard mode): correct position with serifs - [x] Inactive key styling: outline (Base) or gray fill (Lyra) on selected key - [x] Typing with cursor mid-text → inserts at cursor position - [x] Deleting with cursor mid-text → deletes character before cursor - [x] Exit cursor mode → type at cursor position (inserts mid-text, not at end) - [x] Exit cursor mode from toggle → cursor at saved position (not end of text) ### Password Mode - [x] Masked text with one revealed character at `cursorPos - 1` - [x] Cursor mode: block shows actual character, display text all `*` - [x] Toggle `[abc]`/`[***]`: Hold Right (500ms) in cursor mode enters toggle, Confirm toggles visibility, Left exits back to cursor - [x] Exiting cursor mode resets `passwordVisible` to false - [x] Long-press Del clears all text ### URL Mode - [x] URL toggle activates/deactivates URL mode - [x] URL button stays selected after toggle (both on and off) - [x] Deactivating URL mode returns to ABC (not SYM) - [x] 3×3 snippet grid displays correctly - [x] Column alignment: col 0 over ABC, col 1 over URL, col 2 over Del - [x] Snippet insertion: inserts full string at cursor position - [x] URL mode persists after snippet insertion - [x] Shift disabled in InputType::Url - [x] SpecMode (`abc`) exits URL mode to ABC - [x] Up/Down navigation between URL grid and bottom row ### Re-entry State Reset - [x] Enter keyboard → activate URL mode → exit → re-enter → URL mode OFF - [x] Enter keyboard → switch to SYM → exit → re-enter → ABC mode - [x] Enter keyboard → enter cursor mode → exit → re-enter → keyboard mode - [x] Enter keyboard → enter toggle pos → exit → re-enter → togglePos OFF - [x] Enter keyboard → activate shift → exit → re-enter → shift OFF - [x] Enter password keyboard → toggle password visible → exit → re-enter → password hidden ### Navigation - [x] Left/right wrap-around within content rows - [x] Left/right wrap-around within bottom row - [x] Up from row 0 → bottom row (correct column mapping) - [x] Down from bottom row → row 0 (correct column mapping) - [x] Up from bottom row → last content row (correct column) - [x] Down from last content row → bottom row (correct column) - [x] Navigate horizontally in bottom row, then up → correct content column - [x] Navigate horizontally in bottom row, then down (wrap) → correct content column ### Visual (both themes) - [x] Secondary hints only on ABC row 0 - [x] No secondary hints in #@! mode or URL mode - [x] No secondary hints on letter rows (1-3) - [x] Space bar: horizontal line centered, not touching edges - [x] Delete: arrow `←` drawn correctly - [x] Selected key: inverted colors (black fill, white text) - [x] All special keys have border rectangles - [x] Fixed underline in text field (Both themes) - [x] Mode key label: `#@!` in ABC mode, `abc` in symbol mode, `abc` in URL mode - [x] URL key label: `URL` (only in InputType::Url), styled same as other bottom keys - [x] Shift label: `shift` when OFF, `SHIFT` when ON, `shift` when disabled (SYM/URL) - [x] Both themes: bottom row total width matches content rows width - [x] URL snippet grid centered over ABC/URL/Del buttons ### Device & Theme Coverage - [ ] Base Theme on X3 - [x] Base Theme on X4 - [ ] Lyra Theme on X3 - [x] Lyra Theme on X4 - [ ] Lyra Extended Theme on X3 - [x] Lyra Extended Theme on X4 ### Toggle Position - [x] Hold Right > 500ms in cursor mode (Password) → enters toggle, caret visible at saved position - [x] Short-press Right in cursor mode (Password) → advances cursor 1 position, does not jump to toggle - [x] Short-press Left in cursor mode (Password) → moves cursor left 1 position, from toggle returns to saved position - [x] Confirm in toggle → toggles `passwordVisible` - [x] Left from toggle → returns to saved position, caret disappears, block cursor appears - [x] Right from toggle → no-op - [x] Down from toggle → exits to keyboard, cursor at saved position - [x] Hold Right in cursor mode (InputType::Text) → no effect - [x] Hold Right in cursor mode (InputType::Url) → no effect - [x] Hold Right < 500ms released in cursor mode (Password) → short press, advances cursor 1 - [x] No continuous repeat when holding Left or Right in cursor mode ### Caret Visual in Toggle - [x] In toggle: caret "I" visible at saved cursor position - [x] Character under cursor visible (no gap) in password not-visible mode - [x] Character under cursor visible in password visible mode - [x] `[abc]`/`[***]` label with inverted selection in toggle ### Contextual Tips - [x] `"Tips:"` header centered above contextual hints - [x] Single tip → `"Tips:"` + one line - [x] Multiple tips → `"Tips:"` + multiple lines, all centered as block - [x] No tips shown when not applicable (e.g., ABC with empty text and non-URL) - [x] `"UPPERCASE"` shown when shift OFF - [x] `"lowercase"` shown when shift ON - [x] `"secondary char"` shown for InputType::Url ### Hint Phases - [x] 2× DEL → Phase 1 appears ("Hold UP to edit entry") - [x] Phase 1 auto-hides after 4s - [x] Phase 2 appears when entering cursor mode ("Press < or > to move cursor") - [x] Phase 2 shows "Hold > then press [abc] to show password" when `!passwordVisible` - [x] Phase 2 shows "Hold > then press [***] to hide password" when `passwordVisible` - [x] Phase 2 shows "Press < to return to cursor position" when in toggle - [x] Phase 2 disappears when exiting cursor mode ### Long-Press `InputType::Url` Behavior - [x] Hold SELECT on letter rows (rows 1+) with InputType::Url → same character as short press - [x] Hold SELECT on row 0 with InputType::Url → secondary character works normally ### Number Row Reorder - [x] Number row order: 1-9, 0 left to right - [x] `(` and `)` are adjacent (positions 8 and 9) via secondary labels - [x] Long-press on row 0 returns correct secondary symbols in new order - [x] SYM row 1: `(` and `)` also adjacent (positions 8 and 9) ### Block Cursor Alignment - [x] Block cursor correctly positioned for consecutive spaces (kerning offset applied) - [x] Block cursor correctly positioned for mixed characters (letters, numbers, symbols) - [x] Block width minimum 6px for narrow characters (space) — visible as block, not thin line - [x] Password hidden: 3-part drawing prevents block overflow onto `*` characters - [x] Password visible: block post-loop draws correctly on continuous text (no 3-part needed) - [x] End-of-text block: thin 6px block at correct position ### Integration - [x] WiFi password entry (connect to network) - [ ] KOReader username, password, and sync server URL - [ ] Calibre OPDS URL, username, and password - [ ] Calibre OPDS URL: empty → opens with "https://" prefilled - [ ] Calibre OPDS URL: type "http://" or "https://" only → saved as empty - [ ] Calibre OPDS URL: type full URL → saved correctly - [ ] Calibre OPDS URL: existing URL → opens with existing URL (not "https://" prefill) |
||
|
|
050a3bd1b6 |
fix: ActivityManager tweaks (#1220)
## Summary **What is the goal of this PR?** Small tweaks to #1016: - Only Activity and ActivityManager can access activityResultHandler and activityResult - `[[maybe_unused]]` in RenderLock constructor - Only ActivityManager and RenderLock can access renderingMutex - Missing renderUpdate after failed wifi selection - Standardize on activities calling finish instead of activityManager.popActivity - Hold RenderLock while mutating state in EpubReaderActivity result handlers --- ### 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**_ |
||
|
|
c4fc4effbd |
refactor: implement ActivityManager (#1016)
## Summary Ref comment: https://github.com/crosspoint-reader/crosspoint-reader/pull/1010#pullrequestreview-3828854640 This PR introduces `ActivityManager`, which mirrors the same concept of Activity in Android, where an activity represents a single screen of the UI. The manager is responsible for launching activities, and ensuring that only one activity is active at a time. Main differences from Android's ActivityManager: - No concept of Bundle or Intent extras - No onPause/onResume, since we don't have a concept of background activities - onActivityResult is implemented via a callback instead of a separate method, for simplicity ## Key changes - Single `renderTask` shared across all activities - No more sub-activity, we manage them using a stack; Results can be passed via `startActivityForResult` and `setResult` - Activity can call `finish()` to destroy themself, but the actual deletion will be handled by `ActivityManager` to avoid `delete this` pattern As a bonus: the manager will automatically call `requestUpdate()` when returning from another activity ## Example usage **BEFORE**: ```cpp // caller enterNewActivity(new WifiSelectionActivity(renderer, mappedInput, [this](const bool connected) { onWifiSelectionComplete(connected); })); // subactivity onComplete(true); // will eventually call exitActivity(), which deletes the caller instance (dangerous behavior) ``` **AFTER**: (mirrors the `startActivityForResult` and `setResult` from android) ```cpp // caller startActivityForResult(new NetworkModeSelectionActivity(renderer, mappedInput), [this](const ActivityResult& result) { onNetworkModeSelected(result.selectedNetworkMode); }); // subactivity ActivityResult result; result.isCancelled = false; result.selectedNetworkMode = mode; setResult(result); finish(); // signals to ActivityManager to go back to last activity AFTER this function returns ``` TODO: - [x] Reconsider if the `Intent` is really necessary or it should be removed (note: it's inspired by [Intent](https://developer.android.com/guide/components/intents-common) from Android API) ==> I decided to keep this pattern fr clarity - [x] Verify if behavior is still correct (i.e. back from sub-activity) - [x] Refactor the `ActivityWithSubactivity` to just simple `Activity` --> We are using a stack for keeping track of sub-activity now - [x] Use single task for rendering --> avoid allocating 8KB stack per activity - [x] Implement the idea of [Activity result](https://developer.android.com/training/basics/intents/result) --> Allow sub-activity like Wifi to report back the status (connected, failed, etc) --- ### 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**, some repetitive migrations are done by Claude, but I'm the one how ultimately approve it --------- Co-authored-by: Zach Nelson <zach@zdnelson.com> |
||
|
|
410c70ab89 |
perf: UITheme::getMetrics const and const-ref usage (#1094)
## Summary **What is the goal of this PR?** Small cleanup to make getTheme and getMetrics methods on UITheme const. They return const refs, so updated call sites to use `const auto&`. Realistically this won't make much performance difference, but it better conveys the nature of theme metrics being shared const state. --- ### 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**_ |
||
|
|
e7ee6ff05e |
feat: Lyra screens (#732)
## Summary Implements Lyra theme for some more Crosspoint screens:       ## Additional Context - A bit of refactoring for list scrolling logic --- ### 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**_ --------- Co-authored-by: Dave Allie <dave@daveallie.com> |
||
|
|
d02e21a48f |
fix: Added missing up/down button labels (#935)
## Summary **What is the goal of this PR?** In some places, button labels are omitted intentionally because the button has no purpose in the activity. I noticed a few obvious cases, like Home > File Transfer and Settings > System > Language, where the up and down button labels were missing. This change fixes those and all similar instances I could find. --- ### 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**_ |
||
|
|
7ba5978848 |
feat: User-Interface I18n System (#728)
## Summary **What is the goal of this PR?** This PR introduces Internationalization (i18n) support, enabling users to switch the UI language dynamically. **What changes are included?** - Core Logic: Added I18n class (`lib/I18n/I18n.h/cpp`) to manage language state and string retrieval. - Data Structures: - `lib/I18n/I18nStrings.h/cpp`: Static string arrays for each supported language. - `lib/I18n/I18nKeys.h`: Enum definitions for type-safe string access. - `lib/I18n/translations.csv`: single source of truth. - Documentation: Added `docs/i18n.md` detailing the workflow for developers and translators. - New Settings activity: `src/activities/settings/LanguageSelectActivity.h/cpp` ## Additional Context This implementation (building on concepts from #505) prioritizes performance and memory efficiency. The core approach is to store all localized strings for each language in dedicated arrays and access them via enums. This provides O(1) access with zero runtime overhead, and avoids the heap allocations, hashing, and collision handling required by `std::map` or `std::unordered_map`. The main trade-off is that enums and string arrays must remain perfectly synchronized—any mismatch would result in incorrect strings being displayed in the UI. To eliminate this risk, I added a Python script that automatically generates `I18nStrings.h/.cpp` and `I18nKeys.h` from a CSV file, which will serve as the single source of truth for all translations. The full design and workflow are documented in `docs/i18n.md`. ### Next Steps - [x] Python script `generate_i18n.py` to auto-generate C++ files from CSV - [x] Populate translations.csv with initial translations. Currently available translations: English, Español, Français, Deutsch, Čeština, Português (Brasil), Русский, Svenska. Thanks, community! **Status:** EDIT: ready to be merged. As a proof of concept, the SPANISH strings currently mirror the English ones, but are fully uppercased. --- ### AI Usage Did you use AI tools to help write this code? _**< PARTIALLY >**_ I used AI for the black work of replacing strings with I18n references across the project, and for generating the documentation. EDIT: also some help with merging changes from master. --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: yeyeto2788 <juanernestobiondi@gmail.com> |
||
|
|
3d47c081f2 |
fix: use RAII render lock everywhere (#916)
## Summary Follow-up to https://github.com/crosspoint-reader/crosspoint-reader/pull/774 --- ### 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** <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Refactor** * Modernized internal synchronization mechanisms across multiple components to improve code reliability and maintainability. All functionality remains unchanged. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
a616f42cb4 |
refactor: move render() to Activity super class, use freeRTOS notification (#774)
## Summary Currently, each activity has to manage their own `displayTaskLoop` which adds redundant boilerplate code. The loop is a wait loop which is also not the best practice, as the `updateRequested` boolean is not protected by a mutex. In this PR: - Move `displayTaskLoop` to the super `Activity` class - Replace `updateRequested` with freeRTOS's [direct to task notification](https://www.freertos.org/Documentation/02-Kernel/02-Kernel-features/03-Direct-to-task-notifications/01-Task-notifications) - For `ActivityWithSubactivity`, whenever a sub-activity is present, the parent's `render()` automatically goes inactive With this change, activities now only need to expose `render()` function, and anywhere in the code base can call `requestUpdate()` to request a new rendering pass. ## Additional Context In theory, this change may also make the battery life a bit better, since one wait loop is removed. Although the equipment in my home lab wasn't been able to verify it (the electric current is too noisy and small). Would appreciate if anyone has any insights on this subject. Update: I managed to hack [a small piece of code](https://github.com/ngxson/crosspoint-reader/tree/xsn/measure_cpu_usage) that allow tracking CPU idle time. The CPU load does decrease a bit (1.47% down to 1.39%), which make sense, because the display task is now sleeping most of the time unless notified. This should translate to a slightly increase in battery life in the long run. ``` PR: [40012] [MEM] Free: 185856 bytes, Total: 231004 bytes, Min Free: 123316 bytes [40012] [IDLE] Idle time: 98.61% (CPU load: 1.39%) [50017] [MEM] Free: 185856 bytes, Total: 231004 bytes, Min Free: 123316 bytes [50017] [IDLE] Idle time: 98.61% (CPU load: 1.39%) [60022] [MEM] Free: 185856 bytes, Total: 231004 bytes, Min Free: 123316 bytes [60022] [IDLE] Idle time: 98.61% (CPU load: 1.39%) master: [20012] [MEM] Free: 195016 bytes, Total: 231532 bytes, Min Free: 132460 bytes [20012] [IDLE] Idle time: 98.53% (CPU load: 1.47%) [30017] [MEM] Free: 195016 bytes, Total: 231532 bytes, Min Free: 132460 bytes [30017] [IDLE] Idle time: 98.53% (CPU load: 1.47%) [40022] [MEM] Free: 195016 bytes, Total: 231532 bytes, Min Free: 132460 bytes [40022] [IDLE] Idle time: 98.53% (CPU load: 1.47%) ``` --- ### 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** <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Streamlined rendering architecture by consolidating update mechanisms across all activities, improving efficiency and consistency. * Modernized synchronization patterns for display updates to ensure reliable, conflict-free rendering. * **Bug Fixes** * Enhanced rendering stability through improved locking mechanisms and explicit update requests. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: znelson <znelson@users.noreply.github.com> |
||
|
|
64d161e88b |
feat: unify navigation handling with system-wide continuous navigation (#600)
This PR unifies navigation handling & adds system-wide support for continuous navigation. ## Summary Holding down a navigation button now continuously advances through items until the button is released. This removes the need for repeated press-and-release actions and makes navigation faster and smoother, especially in long menus or documents. When page-based navigation is available, it will navigate through pages. If not, it will progress through menu items or similar list-based UI elements. Additionally, this PR fixes inconsistencies in wrap-around behavior and navigation index calculations. Places where the navigation system was updated: - Home Page - Settings Pages - My Library Page - WiFi Selection Page - OPDS Browser Page - Keyboard - File Transfer Page - XTC Chapter Selector Page - EPUB Chapter Selector Page I’ve tested this on the device as much as possible and tried to match the existing behavior. Please let me know if I missed anything. Thanks 🙏  --- Following the request from @osteotek and @daveallie for system-wide support, the old PR (#379) has been closed in favor of this consolidated, system-wide implementation. --- ### AI Usage Did you use AI tools to help write this code? _**PARTIALLY**_ --------- Co-authored-by: Dave Allie <dave@daveallie.com> |
||
|
|
bf87a7dc60 |
feat: UI themes, Lyra (#528)
## Summary ### What is the goal of this PR? - Visual UI overhaul - UI theme selection ### What changes are included? - Added a setting "UI Theme": Classic, Lyra - The classic theme is the current Crosspoint theme - The Lyra theme implements these mockups: https://www.figma.com/design/UhxoV4DgUnfrDQgMPPTXog/Lyra-Theme?node-id=2003-7596&t=4CSOZqf0n9uQMxDt-0 by Discord users yagofarias, ruby and gan_shu - New functions in GFXRenderer to render rounded rectangles, greyscale fills (using dithering) and thick lines - Basic UI components are factored into BaseTheme methods which can be overridden by each additional theme. Methods that are not overridden will fallback to BaseTheme behavior. This means any new features/components in CrossPoint only need to be developed for the "Classic" BaseTheme. - Additional themes can easily be developed by the community using this foundation    ## Additional Context - Only the Home, Library and main Settings screens have been implemented so far, this will be extended to the transfer screens and chapter selection screen later on, but we need to get the ball rolling somehow :) - Loading extra covers on the home screen in the Lyra theme takes a little more time (about 2 seconds), I added a loading bar popup (reusing the Indexing progress bar from the reader view, factored into a neat UI component) but the popup adds ~400ms to the loading time. - ~~Home screen thumbnails will need to be generated separately for each theme, because they are displayed in different sizes. Because we're using dithering, displaying a thumb with the wrong size causes the picture to look janky or dark as it does on the screenshots above. No worries this will be fixed in a future PR.~~ Thumbs are now generated with a size parameter - UI Icons will need to be implemented in a future PR. --- ### 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**_ This is not a vibe coded PR. Copilot was used for autocompletion to save time but I reviewed, understood and edited all generated code. --------- Co-authored-by: Dave Allie <dave@daveallie.com> |
||
|
|
5d369df6be |
fix: Chapter Selection UI bugs when koreader sync is enabled, and clarify default kosync URL (#501)
## Summary * Fixes #475 * Fixes #477 * Closes #428 ## Additional Context * Updates to `src/activities/reader/EpubReaderChapterSelectionActivity.cpp` are copied verbatim from #433 (thanks to @jonasdiemer) * Update to `src/activities/settings/KOReaderSettingsActivity.cpp` per discussion with @itsthisjustin at #428 Tested on my device with several books and koreader sync turned on and off. --- ### AI Usage Did you use AI tools to help write this code? _NO_ |
||
|
|
f69cddf2cc |
Adds KOReader Sync support (#232)
## Summary - Adds KOReader progress sync integration, allowing CrossPoint to sync reading positions with other KOReader-compatible devices - Stores credentials securely with XOR obfuscation - Uses KOReader's partial MD5 document hashing for cross-device book matching - Syncs position via percentage with estimated XPath for compatibility # Features - Settings: KOReader Username, Password, and Authenticate options - Sync from chapters menu: "Sync Progress" option appears when credentials are configured - Bidirectional sync: Can apply remote progress or upload local progress --------- Co-authored-by: Dave Allie <dave@daveallie.com> |