4fb785af82578474358c69f2e5861be39eca4bf7
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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> |
||
|
|
6ec5fc5603 |
feat: lower CPU freq on idle, add HalPowerManager (#852)
## Summary Continue my experiment from https://github.com/crosspoint-reader/crosspoint-reader/pull/801 This PR add the ability to lower the CPU frequency on extended idle period (currently set to 3 seconds). By default, the esp32c3 CPU is set to 160MHz, and now on idle, we can reduce it to just 10MHz. Note that while this functionality is already provided by [esp power management](https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32c3/api-reference/system/power_management.html), the current Arduino build lacks of this, and enabling it is just too complicated (not worth the effort compared to this PR) Update: more info in https://github.com/crosspoint-reader/crosspoint-reader/pull/852#issuecomment-3904562827 ## Testing Pre-condition for each test case: the battery is charged to 100%, and is left plugged in after fully charged for an extra 1 hour. The table below shows how much battery is **used** for a given duration: | case / duration | 6 hrs | 12 hrs | | --- | --- | --- | | `delay(10)` | 26% | 48% | | `delay(50)`, PR https://github.com/crosspoint-reader/crosspoint-reader/pull/801 | 20% | Not tested | | `delay(50)` + low CPU freq (This PR) | Not tested | 25% | | `delay(10)` + low CPU freq (1) | Not tested | Not tested | (1) I decided not to test this case because it may not make sense. The problem is that CPU frequency vs power consumption do not follow a linear relationship, see [this](https://www.arrow.com/en/research-and-events/articles/esp32-power-consumption-can-be-reduced-with-sleep-modes) as an example. So, tight loop (10ms) + lower CPU freq significantly impact battery life, because the active CPU time is now much higher compared to the wall time. **So in conclusion, this PR improves ~150% to ~200% battery use time per charge.** The projected battery life is now: ~36-48 hrs of reading time (normal reading, no wifi) --- ### 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** |
||
|
|
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> |
||
|
|
d399afb53d |
feat: invalidate cache on web uploads and opds downloads and add Clear Cache action (#393)
## Summary When uploading or downloading an updated ebook from SD/WebUI/OPDS with same the filename the `.crosspoint` cache is not cleared. This can lead to issues with the Table of Contents and hangs when switching between chapters. I encountered this issue in two places: - When I need to do further ePub cleaning using Calibre after I load an ePub and find that some of its formatting should be cleaned up. When I reprocess the same book and want to place it back in the same location I need a way to invalidate the cache. - When syncing RSS feed generated epubs. I generate news ePubs with filenames like `news-outlet.epub` and so every day when I fetch new news the crosspoint cache needs to be cleared to load that file. This change offers the following features: - On web uploads, if the file already exists, the cache for that file is cleared - On OPDS downloads, if the file already exists, the cache for that file is cleared - There's now an action for `Clear Cache` in the Settings page which can clear the cache for all books Addresses https://github.com/crosspoint-reader/crosspoint-reader/issues/281 --- ### 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: Dave Allie <dave@daveallie.com> |