Commit Graph

18 Commits

Author SHA1 Message Date
cottongin
0e2440aea8 fix: resolve end-of-book deadlock, long-press guards, archive UX, and home screen refresh
- Fix device freeze at end-of-book by deferring EndOfBookMenuActivity
  creation from render() to loop() (avoids RenderLock deadlock) in
  EpubReaderActivity and XtcReaderActivity
- Add initialSkipRelease to BookManageMenuActivity to prevent stale
  Confirm release from triggering actions when opened via long-press
- Add initialSkipRelease to MyLibraryActivity for long-press Browse
  Files -> archive navigation
- Thread skip-release through HomeActivity callback and main.cpp
- Fix HomeActivity stale cover buffer after archive/delete by fully
  resetting render state (freeCoverBuffer, firstRenderDone, etc.)
- Swap short/long-press actions in .archive context: short-press opens
  manage menu, long-press unarchives and opens the book
- Add deferred open pattern (pendingOpenPath) to wait for Confirm
  release before navigating to reader after unarchive
- Add BookManager::cleanupEmptyArchiveDirs() to remove empty parent
  directories after unarchive/delete inside .archive
- Add optional unarchivedPath output parameter to BookManager::unarchiveBook
- Restyle EndOfBookMenuActivity to standard list layout with proper
  header, margins, and button hints matching other screens
- Change EndOfBookMenuActivity back button hint to "« Back"
- Add Table of Contents option to EndOfBookMenuActivity

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-21 07:37:36 -05:00
cottongin
1c19899aa3 feat: add long-press on HomeActivity for book management and archive browsing
Long-press Confirm on a recent book opens the BookManageMenuActivity.
Long-press Confirm on Browse Files navigates directly to /.archive/.
Wires onMyLibraryOpenWithPath callback through main.cpp to HomeActivity.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-21 02:59:35 -05:00
cottongin
49471e36f1 refactor: change browse activities to ActivityWithSubactivity
Change HomeActivity, MyLibraryActivity, and RecentBooksActivity base
class from Activity to ActivityWithSubactivity. Adds subActivity
guard at top of each loop(). No new behavior, just enabling sub-activity
hosting for the upcoming BookManageMenuActivity integration.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-21 02:55:29 -05:00
cottongin
966fbef3d1 mod: add clock settings tab, timezone support, and clock size option
Fix clock persistence bug caused by stale legacy read in settings
deserialization. Add clock size setting (Small/Medium/Large) and
timezone selection with North American presets plus custom UTC offset.
Move all clock-related settings into a dedicated Clock tab, rename
"Home Screen Clock" to "Clock", and move minute-change detection to
main loop so the header clock updates on every screen.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 03:46:06 -05:00
cottongin
38a87298f3 mod: fix clock bugs, add NTP auto-sync, show clock in all headers
- Fix SetTimeActivity immediately dismissing by changing wasReleased to
  wasPressed for all button inputs (matching other subactivities)
- Extract NTP sync into shared TimeSync utility (startNtpSync,
  waitForNtpSync, stopNtpSync) and trigger non-blocking NTP sync on
  every WiFi connection
- Move clock rendering into drawHeader (BaseTheme + LyraTheme) so it
  appears on all screens with a header, positioned symmetrically with
  the battery icon (12px margin, same Y offset, SMALL_FONT_ID)
- Add per-minute auto-refresh on home screen so clock updates without
  button press
- Add RTC time debug log on boot to verify time persistence across
  deep sleep

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 02:13:10 -05:00
Xuan-Son Nguyen
ed8a0feac1 refactor: move render() to Activity super class, use freeRTOS notification (#774)
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.

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%)
```

---

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
-->

* **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>
2026-02-16 13:01:42 -05:00
Istiak Tridip
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 🙏


![crosspoint](https://github.com/user-attachments/assets/6a3c7482-f45e-4a77-b156-721bb3b679e6)

---

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>
2026-02-09 20:19:34 +11:00
CaptainFrito
bd8132a260 fix: Lag before displaying covers on home screen (#721)
## Summary

Reduce/fix the lag on the home screen before recent book covers are
rendered

## Additional Context

We were previously rendering the screen in two steps, delaying the
recent book covers render to avoid a lag before the screen loads.
In this PR, we are now doing that only if at least one book doesn't have
the cover thumbnail generated yet. If all thumbs are already generated,
we load and display them right away, with no lag.

---

### 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 **_
2026-02-06 18:58:32 +11:00
CaptainFrito
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

![IMG_7649
Medium](https://github.com/user-attachments/assets/b516f5a9-2636-4565-acff-91a25b93b39b)
![IMG_7746
Medium](https://github.com/user-attachments/assets/def41810-ab6e-4952-b40f-b9ce7d62bea8)
![IMG_7651
Medium](https://github.com/user-attachments/assets/518a9a6d-107a-4be3-9533-43a2b64b944b)



## 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>
2026-02-05 21:50:11 +11:00
Kenneth
e548bfc0e1 My Library: Tab bar w/ Recent Books + File Browser (#250)
# Summary

This PR introduces a reusable Tab Bar component and combines the Recent
Books and File Browser into a unified tabbed page called "My Library"
accessible from the Home screen.

## Features
### New Tab Bar Component
A flexible, reusable tab bar component added to `ScreenComponents` that
can be used throughout the application.

### New Scroll Indicator Component
A page position indicator for lists that span multiple pages.
**Features:**
- Up/down arrow indicators
- Current page fraction display (e.g., "1/3")
- Only renders when content spans multiple pages

### My Library Activity
A new unified view combining Recent Books and File Browser into a single
tabbed page.

**Tabs:**
- **Recent** - Shows recently opened books
- **Files** - Browse SD card directory structure

**Navigation:**
- Up/Down or Left/Right: Navigate through list items
- Left/Right (when first item selected): Switch between tabs
- Confirm: Open selected book or enter directory
- Back: Go up directory (Files tab) or return home
- Long press Back: Jump to root directory (Files tab)

**UI Elements:**
- Tab bar with selection indicator
- Scroll/page indicator on right side
- Side button hints (up/down arrows)
- Dynamic bottom button labels ("BACK" in subdirectories, "HOME" at
root)

## Tab Bar Usage
The tab bar component is designed to be reusable across different
activities. Here's how to use it:

### Basic Example
```cpp
#include "ScreenComponents.h"
void MyActivity::render() const {
  renderer.clearScreen();
  
  // Define tabs with labels and selection state
  std::vector<TabInfo> tabs = {
    {"Tab One", currentTab == 0},   // Selected when currentTab is 0
    {"Tab Two", currentTab == 1},   // Selected when currentTab is 1
    {"Tab Three", currentTab == 2}  // Selected when currentTab is 2
  };
  
  // Draw tab bar at Y position 15, returns height of the tab bar
  int tabBarHeight = ScreenComponents::drawTabBar(renderer, 15, tabs);
  
  // Position your content below the tab bar
  int contentStartY = 15 + tabBarHeight + 10; // Add some padding
  
  // Draw content based on selected tab
  if (currentTab == 0) {
    renderTabOneContent(contentStartY);
  } else if (currentTab == 1) {
    renderTabTwoContent(contentStartY);
  } else {
    renderTabThreeContent(contentStartY);
  }
  
  renderer.displayBuffer();
}
```
Video Demo: https://share.cleanshot.com/P6NBncFS

<img width="250"
src="https://github.com/user-attachments/assets/07de4418-968e-4a88-9b42-ac5f53d8a832"
/>
<img width="250"
src="https://github.com/user-attachments/assets/e40201ed-dcc8-4568-b008-cd2bf13ebb2a"
/>
<img width="250"
src="https://github.com/user-attachments/assets/73db269f-e629-4696-b8ca-0b8443451a05"
/>

---------

Co-authored-by: Dave Allie <dave@daveallie.com>
2026-01-21 11:38:38 +00:00
Eunchurn Park
fecd1849b9 Add cover image display in *Continue Reading* card with framebuffer caching (#200)
## Summary

* **What is the goal of this PR?** (e.g., Fixes a bug in the user
authentication module,

Display the book cover image in the **"Continue Reading"** card on the
home screen, with fast navigation using framebuffer caching.

* **What changes are included?**

- Display book cover image in the "Continue Reading" card on home screen
- Load cover from cached BMP (same as sleep screen cover)
- Add framebuffer store/restore functions (`copyStoredBwBuffer`,
`freeStoredBwBuffer`) for fast navigation after initial render
- Fix `drawBitmap` scaling bug: apply scale to offset only, not to base
coordinates
- Add white text boxes behind title/author/continue reading label for
readability on cover
- Support both EPUB and XTC file cover images
- Increase HomeActivity task stack size from 2048 to 4096 for cover
image rendering

## Additional Context

* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks, specific areas to
focus on).

- Performance: First render loads cover from SD card (~800ms),
subsequent navigation uses cached framebuffer (~instant)
- Memory: Framebuffer cache uses ~48KB (6 chunks × 8KB) while on home
screen, freed on exit
- Fallback: If cover image is not available, falls back to standard
text-only display
- The `drawBitmap` fix corrects a bug where screenY = (y + offset) scale
was incorrectly scaling the base coordinates. Now correctly uses screenY
= y + (offset scale)
2026-01-14 21:24:02 +11:00
Justin Mitchell
b792b792bf Calibre Web Epub Downloading + Calibre Wireless Device Syncing (#219)
## Summary

Adds support for browsing and downloading books from a Calibre-web
server via OPDS.
How it works
1. Configure server URL in Settings → Calibre Web URL (e.g.,
https://myserver.com:port I use Cloudflare tunnel to make my server
accessible anywhere fwiw)
2. "Calibre Library" will now show on the the home screen
3. Browse the catalog - navigate through categories like "By Newest",
"By Author", "By Series", etc.
4. Download books - select a book and press Confirm to download the EPUB
to your device
Navigation
- Up/Down - Move through entries
- Confirm - Open folder or download book
- Back - Go to parent catalog, or exit to home if at root
- Navigation entries show with > prefix, books show title and author
- Button hints update dynamically ("Open" for folders, "Download" for
books)
Technical details
- Fetches OPDS catalog from {server_url}/opds
- Parses both navigation feeds (catalog links) and acquisition feeds
(downloadable books)
- Maintains navigation history stack for back navigation
- Handles absolute paths in OPDS links correctly (e.g.,
/books/opds/navcatalog/...)
- Downloads EPUBs directly to the SD card root
Note
The server URL should be typed to include https:// if the server
requires it - HTTP→HTTPS redirects may cause SSL errors on ESP32.

## Additional Context

* I also changed the home titles to use uppercase for each word and
added a setting to change the size of the side margins

---------

Co-authored-by: Dave Allie <dave@daveallie.com>
2026-01-07 19:58:37 +11:00
Dave Allie
3abcd0d05d Redesign home screen (#166)
## Summary

* Redesigned home screen with big option to continue reading and
slightly nicer options to navigate to core sections
* Attempt to use the cached EPUB details (title, author) if they exist,
otherwise fall back to file name
* Adjusted button hints on home screen, removed Back option and changed
left/right to up/down

## Additional Context

* Core of this work comes from @ChandhokTannay in
1d36a86ef1
2025-12-30 23:18:10 +11:00
dangson
140d8749a6 Support swapping the functionality of the front buttons (#133)
## Summary

**What is the goal of this PR?** 

Adds a setting to swap the front buttons. The default functionality are:
Back/Confirm/Left/Right. When this setting is enabled they become:
Left/Right/Back/Confirm. This makes it more comfortable to use when
holding in your right hand since your thumb can more easily rest on the
next button. The original firmware has a similar setting.

**What changes are included?**

- Add the new setting.
- Create a mapper to dynamically switch the buttons based on the
setting.
- Use mapper on the various activity screens.
- Update the button hints to reflect the swapped buttons.

## Additional Context

Full disclosure: I used Codex CLI to put this PR together, but did
review it to make sure it makes sense.

Also tested on my device:
https://share.cleanshot.com/k76891NY
2025-12-29 14:59:14 +11:00
Eunchurn Park
b77af16caa Add Continue Reading menu and remember last book folder (#129)
## Summary

* **What is the goal of this PR?**

Add a "Continue Reading" feature to improve user experience when
returning to a previously opened book.

* **What changes are included?**

- Add dynamic "Continue: <book name>" menu item in Home screen when a
book was previously opened

- File browser now starts from the folder of the last opened book
instead of always starting from root directory
- Menu dynamically shows 3 or 4 items based on reading history:
  - Without history: `Browse`, `File transfer`, `Settings`
- With history: `Continue: <book>`, `Browse`, `File transfer`,
`Settings`

## Additional Context

* This feature leverages the existing `APP_STATE.openEpubPath` which
already persists the last opened book path
* The Continue Reading menu only appears if the book file still exists
on the SD card
* Book name in the menu is truncated to 25 characters with "..." suffix
if too long
* If the last book's folder was deleted, the file browser gracefully
falls back to root directory
* No new dependencies or significant memory overhead - reuses existing
state management
2025-12-26 11:55:23 +11:00
Dave Allie
77c655fcf5 Give activities names and log when entering and exiting them (#92)
## Summary

* Give activities name and log when entering and exiting them
* Clearer logs when attempting to debug, knowing where users are coming
from/going to helps
2025-12-21 21:17:00 +11:00
Brendan O'Leary
d41d539435 Add connect to Wifi and File Manager Webserver (#41)
## Summary

- **What is the goal of this PR?**  
Implements wireless EPUB file management via a built-in web server,
enabling users to upload, browse, organize, and delete EPUB files from
any device on the same WiFi network without needing a computer cable
connection.

- **What changes are included?**
- **New Web Server**
([`CrossPointWebServer.cpp`](src/CrossPointWebServer.cpp),
[`CrossPointWebServer.h`](src/CrossPointWebServer.h)):
    - HTTP server on port 80 with a responsive HTML/CSS interface
    - Home page showing device status (version, IP, free memory)
    - File Manager with folder navigation and breadcrumb support
    - EPUB file upload with progress tracking
    - Folder creation and file/folder deletion
    - XSS protection via HTML escaping
- Hidden system folders (`.` prefixed, "System Volume Information",
"XTCache")
  
- **WiFi Screen** ([`WifiScreen.cpp`](src/screens/WifiScreen.cpp),
[`WifiScreen.h`](src/screens/WifiScreen.h)):
    - Network scanning with signal strength indicators
    - Visual indicators for encrypted (`*`) and saved (`+`) networks
- State machine managing: scanning, network selection, password entry,
connecting, save/forget prompts
    - 15-second connection timeout handling
    - Integration with web server (starts on connect, stops on exit)
  
- **WiFi Credential Storage**
([`WifiCredentialStore.cpp`](src/WifiCredentialStore.cpp),
[`WifiCredentialStore.h`](src/WifiCredentialStore.h)):
    - Persistent storage in `/sd/.crosspoint/wifi.bin`
- XOR obfuscation for stored passwords (basic protection against casual
reading)
    - Up to 8 saved networks with add/remove/update operations
  
- **On-Screen Keyboard**
([`OnScreenKeyboard.cpp`](src/screens/OnScreenKeyboard.cpp),
[`OnScreenKeyboard.h`](src/screens/OnScreenKeyboard.h)):
    - Reusable QWERTY keyboard component with shift support
    - Special keys: Shift, Space, Backspace, Done
    - Support for password masking mode
  
- **Settings Screen Integration**
([`SettingsScreen.h`](src/screens/SettingsScreen.h)):
    - Added WiFi action to navigate to the new WiFi screen
  
  - **Documentation** ([`docs/webserver.md`](docs/webserver.md)):
- Comprehensive user guide covering WiFi setup, web interface usage,
file management, troubleshooting, and security notes
    - See this for more screenshots!
- Working "displays the right way in GitHub" on my repo:
https://github.com/olearycrew/crosspoint-reader/blob/feature/connect-to-wifi/docs/webserver.md

**Video demo**


https://github.com/user-attachments/assets/283e32dc-2d9f-4ae2-848e-01f41166a731

## Additional Context

- **Security considerations**: The web server has no
authentication—anyone on the same WiFi network can access files. This is
documented as a limitation, recommending use only on trusted private
networks. Password obfuscation in the credential store is XOR-based, not
cryptographically secure.

- **Memory implications**: The web server and WiFi stack consume
significant memory. The implementation properly cleans up (stops server,
disconnects WiFi, sets `WIFI_OFF` mode) when exiting the WiFi screen to
free resources.

- **Async operations**: Network scanning and connection use async
patterns with FreeRTOS tasks to prevent blocking the UI. The display
task handles rendering on a dedicated thread with mutex protection.

- **Browser compatibility**: The web interface uses standard
HTML5/CSS3/JavaScript and is tested to work with all modern browsers on
desktop and mobile.

---------

Co-authored-by: Dave Allie <dave@daveallie.com>
2025-12-20 01:05:43 +11:00
Dave Allie
d429966dd4 Rename Screens to Activities and restructure files (#44)
## Summary

* This PR drastically reshapes the structure of the codebase, moving
from the concept of "Screens" to "Activities", restructing the files and
setting up the concept of subactivities.
* This should help with keep the main file clean and containing all
functional logic in the relevant activity.
* CrossPointState is now also a global singleton which should help with
accessing it from within activities.

## Additional Context

* This is probably going to be a bit disruptive for people with open
PRs, sorry 😞
2025-12-17 23:32:18 +11:00