d853a5caad693fdd154e9779955c9f10fcd6cb46
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9fa2ff2aa0 | Add signal strength widget to calibre sync too | ||
|
|
a94f6ca80b |
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> |
||
|
|
b9cd4b4c9e |
fix: prevent infinite render loop in Calibre Wireless after file transfer (#1070)
## Summary * **What is the goal of this PR?** Fix an infinite render loop bug in CalibreConnectActivity that caused the e-ink display to refresh continuously every ~421ms after a file transfer completed. * **What changes are included?** - Added lastProcessedCompleteAt member variable to track which server completion timestamp has already been processed - Modified the completion status update logic to only accept new values from the server, preventing re-processing of old timestamps - Added clarifying comments explaining the fix ## Problem Description After receiving a file via Calibre Wireless, the activity displays "Received: [filename]" for 6 seconds, then clears the message. However, the web server's wsLastCompleteAt timestamp persists indefinitely and is never cleared. This created a race condition: After 6 seconds, lastCompleteAt is set to 0 (timeout) In the next loop iteration, status.lastCompleteAt (still has the old timestamp) ≠ lastCompleteAt (0) The code restores lastCompleteAt from the server value Immediately, the 6-second timeout condition is met again This creates an infinite cycle causing unnecessary e-ink refreshes ## Solution The fix introduces lastProcessedCompleteAt to track which server timestamp value has already been processed: Only accept a new status.lastCompleteAt if it differs from lastProcessedCompleteAt Update lastProcessedCompleteAt when processing a new value Do NOT reset lastProcessedCompleteAt when the 6-second timeout clears lastCompleteAt This prevents re-processing the same old server value after the timeout. ## Testing Tested on device with multiple file transfer scenarios: ✅ File received message appears correctly after transfer ✅ Message clears after 6 seconds as expected ✅ No infinite render loop after timeout ✅ Multiple consecutive transfers work correctly ✅ Exiting and re-entering Calibre Wireless works as expected ## Performance Impact Before: Infinite refreshes every ~421ms after timeout (high battery drain, display wear) After: 2-3 refreshes after timeout, then stops (normal behavior) ## Additional Context This is a targeted fix that only affects the Calibre Wireless file transfer screen. The root cause is the architectural difference between the persistent web server state (wsLastCompleteAt) and the per-activity display state (lastCompleteAt). An alternative fix would be to clear wsLastCompleteAt in the web server after some timeout, but that would affect all consumers of the web server status. The chosen solution keeps the fix localized to CalibreConnectActivity. --- ### AI Usage Did you use AI tools to help write this code? _**< YES >**_ |
||
|
|
d501d31e02 |
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> |
||
|
|
a58a93cc11 |
Refactors Calibre Wireless Device & Calibre Library (#404)
Our esp32 consistently dropped the last few packets of the TCP transfer in the old implementation. Only about 1/5 transfers would complete. I've refactored that entire system into an actual Calibre Device Plugin that basically uses the exact same system as the web server's file transfer protocol. I kept them separate so that we don't muddy up the existing file transfer stuff even if it's basically the same at the end of the day I didn't want to limit our ability to change it later. I've also added basic auth to OPDS and renamed that feature to OPDS Browser to just disassociate it from Calibre. --------- Co-authored-by: Arthur Tazhitdinov <lisnake@gmail.com> Co-authored-by: Dave Allie <dave@daveallie.com> |