Skip to content

refactor: implement ActivityManager#1016

Merged
ngxson merged 47 commits intocrosspoint-reader:masterfrom
ngxson:xsn/act_man
Feb 27, 2026
Merged

refactor: implement ActivityManager#1016
ngxson merged 47 commits intocrosspoint-reader:masterfrom
ngxson:xsn/act_man

Conversation

@ngxson
Copy link
Contributor

@ngxson ngxson commented Feb 20, 2026

Summary

Ref comment: #1010 (review)

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:

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

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

  • Reconsider if the Intent is really necessary or it should be removed (note: it's inspired by Intent from Android API) ==> I decided to keep this pattern fr clarity
  • Verify if behavior is still correct (i.e. back from sub-activity)
  • Refactor the ActivityWithSubactivity to just simple Activity --> We are using a stack for keeping track of sub-activity now
  • Use single task for rendering --> avoid allocating 8KB stack per activity
  • Implement the idea of Activity 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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 20, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Centralizes activity lifecycle and rendering under a new ActivityManager with a render task and RAII RenderLock; removes ActivityWithSubactivity and per-activity render tasks; introduces ActivityResult / startActivityForResult / setResult / finish APIs; migrates activities to stack/result-driven navigation and unified render signatures.

Changes

Cohort / File(s) Summary
ActivityManager core
src/activities/ActivityManager.h, src/activities/ActivityManager.cpp
Adds ActivityManager, render task trampoline/loop, RenderLock implementation, activity stack and pending-action state machine, navigation helpers (goTo*, push/pop/replace), requestUpdate(), and central loop/render coordination.
Core Activity API & primitives
src/activities/Activity.h, src/activities/Activity.cpp, src/activities/ActivityResult.h, src/activities/RenderLock.h
Removes per-activity render-task scaffolding; adds ActivityResult types and handler alias, startActivityForResult/setResult/finish/onGoHome/onSelectBook APIs; requestUpdate delegates to ActivityManager; exposes RenderLock guard type.
Removal: subactivity abstraction
src/activities/ActivityWithSubactivity.h, src/activities/ActivityWithSubactivity.cpp
Deletes ActivityWithSubactivity and its sub-activity lifecycle, rendering delegation, and synchronization logic.
Main integration
src/main.cpp
Instantiates global ActivityManager, removes global currentActivity, and routes setup/loop/navigation through activityManager (begin(), loop(), goHome(), goToReader(), goToBoot(), goToSleep()).
Render signature migration
many src/activities/** (.h/.cpp) (examples: reader/*, network/*, home/*, settings/*, browser/*, util/*)
Unifies render signatures from Activity::RenderLock&&RenderLock&&; updates includes, constructors, and call sites across activities.
Activities — stack/result-driven refactor
src/activities/reader/*, src/activities/network/*, src/activities/home/*, src/activities/settings/*, src/activities/browser/*, src/activities/util/*
Change base classes from ActivityWithSubactivity → Activity, remove stored callback members, replace enterNewActivity/exitActivity with startActivityForResult + ActivityResult handlers, add onModeSelected/onComplete/onCancel helpers, and route navigation via activityManager.
New result types
src/activities/ActivityResult.h
Adds typed result structs (WifiResult, KeyboardResult, MenuResult, ChapterResult, PercentResult, PageResult, SyncResult, NetworkModeResult), ResultVariant, ActivityResult with isCancelled, and ActivityResultHandler alias.

Sequence Diagram(s)

sequenceDiagram
    participant App as Application
    participant AM as ActivityManager
    participant RT as RenderTask
    participant Mutex as RenderMutex
    participant Act as CurrentActivity

    App->>AM: begin()
    Note over AM,RT: create render task (trampoline -> renderTaskLoop)

    loop Main loop
        App->>AM: loop()
        alt Pending action (Push/Pop/Replace)
            AM->>AM: process pending action
            AM->>Act: onEnter()
        end
        Act->>AM: requestUpdate()
        AM->>RT: notify render task
    end

    RT->>Mutex: lock()
    RT->>AM: invoke renderTaskLoop
    AM->>Act: render(RenderLock&&)
    Act-->>AM: render complete
    RT->>Mutex: unlock()

    alt Activity finishes
        Act->>AM: setResult(result)
        Act->>AM: finish()
        AM->>Act: onExit()
        AM->>AM: invoke resultHandler(result) if present
    end
Loading
sequenceDiagram
    participant Parent as ParentActivity
    participant AM as ActivityManager
    participant Child as ChildActivity

    Parent->>AM: startActivityForResult(unique_ptr<Child>, handler)
    AM->>Child: pushActivity / onEnter()
    Child->>Child: user interaction
    Child->>Child: setResult(result)
    Child->>AM: finish()
    AM->>Parent: invoke handler(result)
    AM->>Child: onExit() / delete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • daveallie
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'refactor: implement ActivityManager' clearly and concisely summarizes the main change: introducing a new ActivityManager system.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the ActivityManager pattern, key differences from Android, and providing before/after code examples.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment on lines -121 to -141
// Pass input responsibility to sub activity if exists
if (subActivity) {
subActivity->loop();
// Deferred exit: process after subActivity->loop() returns to avoid use-after-free
if (pendingSubactivityExit) {
pendingSubactivityExit = false;
exitActivity();
requestUpdate();
skipNextButtonCheck = true; // Skip button processing to ignore stale events
}
// Deferred go home: process after subActivity->loop() returns to avoid race condition
if (pendingGoHome) {
pendingGoHome = false;
exitActivity();
if (onGoHome) {
onGoHome();
}
return; // Don't access 'this' after callback
}
return;
}
Copy link
Contributor Author

@ngxson ngxson Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A more practical example on how the ActivityManager can be useful is here. This whole logic is now removed and they are handled in a more generic way on ActivityManager

@ngxson
Copy link
Contributor Author

ngxson commented Feb 21, 2026

@daveallie Sorry for yet another big refactoring, but I believe this is quite necessary as it removes some dangerous patterns in the code base. Appreciate if we can prioritize this right after 1.1.0 release.

CC @crosspoint-reader/firmware-maintainers too, appreciate if someone can do testings here. I already did some testings on my side, so far so good.

@ngxson ngxson marked this pull request as ready for review February 21, 2026 14:04
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/activities/browser/OpdsBookBrowserActivity.cpp (1)

371-386: ⚠️ Potential issue | 🟡 Minor

Missing requestUpdate() on WiFi selection failure path.

When WiFi selection fails (line 378-386), the state is set to ERROR but requestUpdate() is not called, unlike the success path at line 376. This could leave the UI displaying stale content.

🐛 Proposed fix
   } else {
     LOG_DBG("OPDS", "WiFi selection cancelled/failed");
     // Force disconnect to ensure clean state for next retry
     // This prevents stale connection status from interfering
     WiFi.disconnect();
     WiFi.mode(WIFI_OFF);
     state = BrowserState::ERROR;
     errorMessage = tr(STR_WIFI_CONN_FAILED);
+    requestUpdate();
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/activities/browser/OpdsBookBrowserActivity.cpp` around lines 371 - 386,
In onWifiSelectionComplete (OpdsBookBrowserActivity::onWifiSelectionComplete)
the failure branch sets state = BrowserState::ERROR and updates errorMessage but
does not call requestUpdate(), leaving the UI potentially stale; add a call to
requestUpdate() after setting errorMessage (and after performing
WiFi.disconnect()/WiFi.mode(WIFI_OFF)) so the UI is refreshed when the error
state is entered.
🧹 Nitpick comments (7)
src/activities/network/NetworkModeSelectionActivity.h (1)

8-9: Remove stale commented-out enum stub.

🧹 Proposed cleanup
-// // Enum for network mode selection
-// enum class NetworkMode { JOIN_NETWORK, CONNECT_CALIBRE, CREATE_HOTSPOT };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/activities/network/NetworkModeSelectionActivity.h` around lines 8 - 9,
Remove the stale commented-out enum stub for NetworkMode: delete the commented
lines that declare "enum class NetworkMode { JOIN_NETWORK, CONNECT_CALIBRE,
CREATE_HOTSPOT }" so the header no longer contains dead/commented-out code; if
this enum is needed elsewhere, ensure the real definition (NetworkMode) resides
in the proper header or source and update includes instead of keeping the
commented stub.
src/activities/ActivityResult.h (1)

7-8: Track the TODO for NetworkMode placement.

Consider moving the enum to a dedicated network/activity header or linking this TODO to an issue.

If you want, I can draft an issue or propose the target header location.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/activities/ActivityResult.h` around lines 7 - 8, The TODO indicates
NetworkMode (enum class NetworkMode { JOIN_NETWORK, CONNECT_CALIBRE,
CREATE_HOTSPOT }) is in the wrong place; move it into a dedicated
network-related header (e.g., create a new NetworkMode.h or NetworkEnums.h) and
replace the enum in ActivityResult.h with an include of that new header, update
any files that reference NetworkMode to include the new header, and remove the
TODO; also link this change to an issue or add a brief comment referencing an
issue ID if you create one so the relocation is tracked.
src/activities/util/KeyboardEntryActivity.cpp (1)

321-336: Avoid std::move on a const ref (it won’t move).

Either pass by value and move, or just assign.

♻️ Minimal fix (keep const ref)
-  result.inputText = std::move(text);
+  result.inputText = text;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/activities/util/KeyboardEntryActivity.cpp` around lines 321 - 336, The
onComplete implementation incorrectly uses std::move on a const std::string&
(parameter text), which won’t move; either change the onComplete signature to
take text by value (std::string text) and keep result.inputText =
std::move(text) to enable move semantics, or keep the const std::string&
signature and assign without move (result.inputText = text); update the
onComplete function accordingly and leave onCancel unchanged (references:
KeyboardEntryActivity::onComplete, ActivityResult::inputText, setResult,
finish).
src/activities/reader/EpubReaderPercentSelectionActivity.cpp (1)

53-58: Direction mapping may be counter-intuitive for a slider.

Up increases percent while Down decreases it. For a horizontal slider UI, users might expect Up/Down to be swapped or to have no effect. However, this may be intentional for the device's button layout.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/activities/reader/EpubReaderPercentSelectionActivity.cpp` around lines 53
- 58, The Up/Down handlers currently call adjustPercent(kLargeStep) and
adjustPercent(-kLargeStep) which is counter-intuitive for a horizontal slider;
either swap the Up/Down mappings so Up calls adjustPercent(-kLargeStep) and Down
calls adjustPercent(kLargeStep), or remove/disable Up/Down handling if vertical
input should be ignored. Update the buttonNavigator.onPressAndContinuous calls
that reference MappedInputManager::Button::Up and
MappedInputManager::Button::Down and ensure adjustPercent, kLargeStep and any
inline comments reflect the chosen behavior.
src/activities/ActivityManager.h (1)

27-28: Redundant forward declarations.

Activity and RenderLock are already included via Activity.h (line 11) and RenderLock.h (line 14). These forward declarations are unnecessary.

🔧 Suggested removal
-class Activity;    // forward declaration
-class RenderLock;  // forward declaration
-
 /**
  * ActivityManager
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/activities/ActivityManager.h` around lines 27 - 28, Remove the redundant
forward declarations "class Activity;" and "class RenderLock;" from
ActivityManager.h because Activity.h and RenderLock.h are already included (so
the forward declarations are unnecessary); delete those two lines and rebuild to
confirm no remaining references require forward declarations elsewhere in this
header or switch to forward declarations only if you remove the corresponding
`#include` lines instead.
src/activities/browser/OpdsBookBrowserActivity.cpp (1)

49-54: Unnecessary base class call.

Activity::loop() is an empty virtual method (as seen in Activity.h: virtual void loop() {}). This call has no effect and could be removed for clarity. However, it's harmless.

♻️ Optional: Remove unnecessary base call
   // Handle WiFi selection subactivity
   if (state == BrowserState::WIFI_SELECTION) {
-    Activity::loop();
     return;
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/activities/browser/OpdsBookBrowserActivity.cpp` around lines 49 - 54, The
call to the empty base implementation Activity::loop() inside
OpdsBookBrowserActivity::loop when state == BrowserState::WIFI_SELECTION is
unnecessary; remove the Activity::loop() invocation and simply return after
handling the WIFI_SELECTION branch to simplify the method (locate the
conditional in OpdsBookBrowserActivity::loop and delete the Activity::loop()
call).
src/activities/home/HomeActivity.cpp (1)

262-266: Optional: reuse base Activity::onSelectBook to avoid duplication.

This method matches the base implementation exactly, so you could forward to the base to keep logic centralized.

♻️ Suggested tweak
 void HomeActivity::onSelectBook(const std::string& path) {
-  Intent intent;
-  intent.path = path;
-  activityManager.goToReader(std::move(intent));
+  Activity::onSelectBook(path);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/activities/home/HomeActivity.cpp` around lines 262 - 266,
HomeActivity::onSelectBook duplicates the base Activity::onSelectBook behavior;
replace the body to forward to the base implementation instead of duplicating
logic—call Activity::onSelectBook(path) (or the appropriate base class
qualifier) so Intent construction and the activityManager.goToReader call remain
centralized in Activity::onSelectBook.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e32d41a and ec2f24d.

📒 Files selected for processing (63)
  • src/activities/Activity.cpp
  • src/activities/Activity.h
  • src/activities/ActivityManager.cpp
  • src/activities/ActivityManager.h
  • src/activities/ActivityResult.h
  • src/activities/ActivityWithSubactivity.cpp
  • src/activities/ActivityWithSubactivity.h
  • src/activities/RenderLock.h
  • src/activities/browser/OpdsBookBrowserActivity.cpp
  • src/activities/browser/OpdsBookBrowserActivity.h
  • src/activities/home/HomeActivity.cpp
  • src/activities/home/HomeActivity.h
  • src/activities/home/MyLibraryActivity.cpp
  • src/activities/home/MyLibraryActivity.h
  • src/activities/home/RecentBooksActivity.cpp
  • src/activities/home/RecentBooksActivity.h
  • src/activities/network/CalibreConnectActivity.cpp
  • src/activities/network/CalibreConnectActivity.h
  • src/activities/network/CrossPointWebServerActivity.cpp
  • src/activities/network/CrossPointWebServerActivity.h
  • src/activities/network/NetworkModeSelectionActivity.cpp
  • src/activities/network/NetworkModeSelectionActivity.h
  • src/activities/network/WifiSelectionActivity.cpp
  • src/activities/network/WifiSelectionActivity.h
  • src/activities/reader/EpubReaderActivity.cpp
  • src/activities/reader/EpubReaderActivity.h
  • src/activities/reader/EpubReaderChapterSelectionActivity.cpp
  • src/activities/reader/EpubReaderChapterSelectionActivity.h
  • src/activities/reader/EpubReaderMenuActivity.cpp
  • src/activities/reader/EpubReaderMenuActivity.h
  • src/activities/reader/EpubReaderPercentSelectionActivity.cpp
  • src/activities/reader/EpubReaderPercentSelectionActivity.h
  • src/activities/reader/KOReaderSyncActivity.cpp
  • src/activities/reader/KOReaderSyncActivity.h
  • src/activities/reader/ReaderActivity.cpp
  • src/activities/reader/ReaderActivity.h
  • src/activities/reader/TxtReaderActivity.cpp
  • src/activities/reader/TxtReaderActivity.h
  • src/activities/reader/XtcReaderActivity.cpp
  • src/activities/reader/XtcReaderActivity.h
  • src/activities/reader/XtcReaderChapterSelectionActivity.cpp
  • src/activities/reader/XtcReaderChapterSelectionActivity.h
  • src/activities/settings/ButtonRemapActivity.cpp
  • src/activities/settings/ButtonRemapActivity.h
  • src/activities/settings/CalibreSettingsActivity.cpp
  • src/activities/settings/CalibreSettingsActivity.h
  • src/activities/settings/ClearCacheActivity.cpp
  • src/activities/settings/ClearCacheActivity.h
  • src/activities/settings/KOReaderAuthActivity.cpp
  • src/activities/settings/KOReaderAuthActivity.h
  • src/activities/settings/KOReaderSettingsActivity.cpp
  • src/activities/settings/KOReaderSettingsActivity.h
  • src/activities/settings/LanguageSelectActivity.cpp
  • src/activities/settings/LanguageSelectActivity.h
  • src/activities/settings/OtaUpdateActivity.cpp
  • src/activities/settings/OtaUpdateActivity.h
  • src/activities/settings/SettingsActivity.cpp
  • src/activities/settings/SettingsActivity.h
  • src/activities/util/BmpViewerActivity.cpp
  • src/activities/util/BmpViewerActivity.h
  • src/activities/util/KeyboardEntryActivity.cpp
  • src/activities/util/KeyboardEntryActivity.h
  • src/main.cpp
💤 Files with no reviewable changes (2)
  • src/activities/ActivityWithSubactivity.cpp
  • src/activities/ActivityWithSubactivity.h
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: GavinHigham
Repo: crosspoint-reader/crosspoint-reader PR: 966
File: src/activities/ActivityWithSubactivity.h:20-21
Timestamp: 2026-02-18T15:43:07.515Z
Learning: In the crosspoint-reader activity architecture, when using ActivityWithSubactivity, the currentActivity global variable remains at the top-level activity (e.g., ReaderActivity) even when nested subactivities are active. The supportsOrientation() delegation chain starts from the top-level activity and stops when it reaches a subactivity that overrides the method (e.g., EpubReaderActivity returns true). Deeper nested subactivities (like EpubReaderChapterSelectionActivity or EpubReaderPercentSelectionActivity) do not need to override supportsOrientation() because they never become the currentActivity and the delegation chain doesn't reach them.
Learnt from: Levrk
Repo: crosspoint-reader/crosspoint-reader PR: 909
File: src/activities/reader/EpubReaderActivity.cpp:461-461
Timestamp: 2026-02-16T20:43:19.339Z
Learning: In src/activities/reader/EpubReaderActivity.cpp, when using ConfirmationActivity with enterNewActivity(), it's not necessary to call exitActivity() beforehand. The ConfirmationActivity operates as a modal dialog and cleanup is handled via the pendingSubactivityExit flag in the callback lambda. This differs from other activities like EpubReaderChapterSelectionActivity or KOReaderSyncActivity which do require exitActivity() before enterNewActivity().
📚 Learning: 2026-02-16T20:43:19.339Z
Learnt from: Levrk
Repo: crosspoint-reader/crosspoint-reader PR: 909
File: src/activities/reader/EpubReaderActivity.cpp:461-461
Timestamp: 2026-02-16T20:43:19.339Z
Learning: In src/activities/reader/EpubReaderActivity.cpp, when using ConfirmationActivity with enterNewActivity(), it's not necessary to call exitActivity() beforehand. The ConfirmationActivity operates as a modal dialog and cleanup is handled via the pendingSubactivityExit flag in the callback lambda. This differs from other activities like EpubReaderChapterSelectionActivity or KOReaderSyncActivity which do require exitActivity() before enterNewActivity().

Applied to files:

  • src/activities/util/BmpViewerActivity.h
  • src/activities/home/MyLibraryActivity.cpp
  • src/activities/home/RecentBooksActivity.h
  • src/activities/browser/OpdsBookBrowserActivity.cpp
  • src/activities/settings/ClearCacheActivity.h
  • src/activities/reader/XtcReaderChapterSelectionActivity.h
  • src/activities/reader/EpubReaderPercentSelectionActivity.cpp
  • src/activities/settings/ButtonRemapActivity.h
  • src/activities/ActivityResult.h
  • src/activities/Activity.cpp
  • src/activities/reader/ReaderActivity.cpp
  • src/activities/network/CalibreConnectActivity.h
  • src/activities/util/BmpViewerActivity.cpp
  • src/activities/reader/KOReaderSyncActivity.cpp
  • src/activities/home/RecentBooksActivity.cpp
  • src/activities/util/KeyboardEntryActivity.cpp
  • src/activities/settings/KOReaderSettingsActivity.h
  • src/activities/home/HomeActivity.cpp
  • src/activities/settings/KOReaderAuthActivity.h
  • src/activities/settings/OtaUpdateActivity.cpp
  • src/activities/reader/ReaderActivity.h
  • src/activities/reader/KOReaderSyncActivity.h
  • src/activities/reader/EpubReaderMenuActivity.cpp
  • src/activities/settings/LanguageSelectActivity.h
  • src/activities/settings/CalibreSettingsActivity.cpp
  • src/main.cpp
  • src/activities/settings/SettingsActivity.cpp
  • src/activities/home/MyLibraryActivity.h
  • src/activities/home/HomeActivity.h
  • src/activities/settings/KOReaderSettingsActivity.cpp
  • src/activities/reader/EpubReaderActivity.h
  • src/activities/browser/OpdsBookBrowserActivity.h
  • src/activities/reader/EpubReaderChapterSelectionActivity.cpp
  • src/activities/network/NetworkModeSelectionActivity.h
  • src/activities/network/NetworkModeSelectionActivity.cpp
  • src/activities/network/CalibreConnectActivity.cpp
  • src/activities/reader/TxtReaderActivity.cpp
  • src/activities/reader/EpubReaderChapterSelectionActivity.h
  • src/activities/settings/ClearCacheActivity.cpp
  • src/activities/Activity.h
  • src/activities/settings/KOReaderAuthActivity.cpp
  • src/activities/reader/EpubReaderPercentSelectionActivity.h
  • src/activities/settings/CalibreSettingsActivity.h
  • src/activities/settings/SettingsActivity.h
  • src/activities/ActivityManager.cpp
  • src/activities/ActivityManager.h
  • src/activities/reader/EpubReaderActivity.cpp
  • src/activities/reader/XtcReaderChapterSelectionActivity.cpp
  • src/activities/reader/EpubReaderMenuActivity.h
  • src/activities/reader/XtcReaderActivity.h
  • src/activities/reader/XtcReaderActivity.cpp
  • src/activities/reader/TxtReaderActivity.h
  • src/activities/util/KeyboardEntryActivity.h
  • src/activities/network/CrossPointWebServerActivity.cpp
  • src/activities/network/WifiSelectionActivity.cpp
📚 Learning: 2026-02-19T17:46:26.871Z
Learnt from: Tritlo
Repo: crosspoint-reader/crosspoint-reader PR: 1003
File: src/activities/reader/EpubReaderActivity.cpp:657-674
Timestamp: 2026-02-19T17:46:26.871Z
Learning: In src/activities/reader/EpubReaderActivity.cpp's renderContents() method, when uncached images exist, Phase 1 intentionally calls displayBuffer(forceFullRefresh) to perform a HALF_REFRESH (if needed), while Phase 2 intentionally calls renderer.displayBuffer() directly without forceFullRefresh. This is by design: Phase 1's refresh clears the screen properly to prevent ghosting, so Phase 2 can use a faster refresh mode for better performance. The grayscale anti-aliasing is handled separately after renderContents() via displayGrayBuffer().

Applied to files:

  • src/activities/browser/OpdsBookBrowserActivity.cpp
  • src/activities/reader/EpubReaderPercentSelectionActivity.cpp
  • src/activities/Activity.cpp
  • src/activities/reader/ReaderActivity.cpp
  • src/activities/reader/KOReaderSyncActivity.cpp
  • src/activities/home/RecentBooksActivity.cpp
  • src/activities/reader/EpubReaderMenuActivity.cpp
  • src/main.cpp
  • src/activities/settings/KOReaderSettingsActivity.cpp
  • src/activities/reader/EpubReaderActivity.h
  • src/activities/reader/EpubReaderChapterSelectionActivity.cpp
  • src/activities/network/CalibreConnectActivity.cpp
  • src/activities/reader/TxtReaderActivity.cpp
  • src/activities/reader/EpubReaderChapterSelectionActivity.h
  • src/activities/settings/ClearCacheActivity.cpp
  • src/activities/settings/KOReaderAuthActivity.cpp
  • src/activities/reader/EpubReaderPercentSelectionActivity.h
  • src/activities/reader/XtcReaderChapterSelectionActivity.cpp
  • src/activities/reader/EpubReaderMenuActivity.h
  • src/activities/reader/XtcReaderActivity.h
  • src/activities/reader/XtcReaderActivity.cpp
📚 Learning: 2026-02-18T15:43:07.515Z
Learnt from: GavinHigham
Repo: crosspoint-reader/crosspoint-reader PR: 966
File: src/activities/ActivityWithSubactivity.h:20-21
Timestamp: 2026-02-18T15:43:07.515Z
Learning: In the crosspoint-reader activity architecture, when using ActivityWithSubactivity, the currentActivity global variable remains at the top-level activity (e.g., ReaderActivity) even when nested subactivities are active. The supportsOrientation() delegation chain starts from the top-level activity and stops when it reaches a subactivity that overrides the method (e.g., EpubReaderActivity returns true). Deeper nested subactivities (like EpubReaderChapterSelectionActivity or EpubReaderPercentSelectionActivity) do not need to override supportsOrientation() because they never become the currentActivity and the delegation chain doesn't reach them.

Applied to files:

  • src/activities/browser/OpdsBookBrowserActivity.cpp
  • src/activities/reader/XtcReaderChapterSelectionActivity.h
  • src/activities/reader/EpubReaderPercentSelectionActivity.cpp
  • src/activities/Activity.cpp
  • src/activities/reader/ReaderActivity.cpp
  • src/activities/reader/KOReaderSyncActivity.cpp
  • src/activities/settings/KOReaderSettingsActivity.h
  • src/activities/settings/KOReaderAuthActivity.h
  • src/activities/reader/ReaderActivity.h
  • src/activities/reader/KOReaderSyncActivity.h
  • src/activities/reader/EpubReaderMenuActivity.cpp
  • src/activities/settings/CalibreSettingsActivity.cpp
  • src/main.cpp
  • src/activities/settings/SettingsActivity.cpp
  • src/activities/settings/KOReaderSettingsActivity.cpp
  • src/activities/reader/EpubReaderActivity.h
  • src/activities/browser/OpdsBookBrowserActivity.h
  • src/activities/reader/EpubReaderChapterSelectionActivity.cpp
  • src/activities/reader/TxtReaderActivity.cpp
  • src/activities/reader/EpubReaderChapterSelectionActivity.h
  • src/activities/Activity.h
  • src/activities/settings/KOReaderAuthActivity.cpp
  • src/activities/reader/EpubReaderPercentSelectionActivity.h
  • src/activities/settings/CalibreSettingsActivity.h
  • src/activities/reader/EpubReaderActivity.cpp
  • src/activities/reader/XtcReaderChapterSelectionActivity.cpp
  • src/activities/reader/EpubReaderMenuActivity.h
  • src/activities/reader/XtcReaderActivity.h
  • src/activities/reader/XtcReaderActivity.cpp
  • src/activities/reader/TxtReaderActivity.h
📚 Learning: 2026-02-12T06:57:35.955Z
Learnt from: CaptainFrito
Repo: crosspoint-reader/crosspoint-reader PR: 725
File: src/activities/network/CalibreConnectActivity.cpp:218-264
Timestamp: 2026-02-12T06:57:35.955Z
Learning: In src/activities/network/CalibreConnectActivity.cpp, button hints (« Exit) are intentionally omitted from the SERVER_STARTING and ERROR states—only the SERVER_RUNNING state displays navigation hints. This is a deliberate design decision by the author.

Applied to files:

  • src/activities/browser/OpdsBookBrowserActivity.cpp
  • src/activities/ActivityResult.h
  • src/activities/reader/ReaderActivity.cpp
  • src/activities/network/CalibreConnectActivity.h
  • src/activities/reader/KOReaderSyncActivity.cpp
  • src/activities/settings/KOReaderSettingsActivity.h
  • src/activities/reader/EpubReaderMenuActivity.cpp
  • src/activities/settings/CalibreSettingsActivity.cpp
  • src/main.cpp
  • src/activities/settings/KOReaderSettingsActivity.cpp
  • src/activities/browser/OpdsBookBrowserActivity.h
  • src/activities/reader/EpubReaderChapterSelectionActivity.cpp
  • src/activities/network/NetworkModeSelectionActivity.h
  • src/activities/network/CalibreConnectActivity.cpp
  • src/activities/settings/KOReaderAuthActivity.cpp
  • src/activities/settings/CalibreSettingsActivity.h
  • src/activities/network/CrossPointWebServerActivity.h
  • src/activities/reader/XtcReaderActivity.cpp
  • src/activities/network/CrossPointWebServerActivity.cpp
  • src/activities/network/WifiSelectionActivity.cpp
📚 Learning: 2026-02-19T17:46:26.871Z
Learnt from: Tritlo
Repo: crosspoint-reader/crosspoint-reader PR: 1003
File: src/activities/reader/EpubReaderActivity.cpp:657-674
Timestamp: 2026-02-19T17:46:26.871Z
Learning: In EpubReaderActivity.cpp, renderContents() behavior is intentional: if uncached images exist, Phase 1 should call displayBuffer(forceFullRefresh) to perform a HALF_REFRESH and clear ghosting; Phase 2 should call renderer.displayBuffer() without forceFullRefresh for a faster refresh. Grayscale anti-aliasing is applied separately after renderContents() via displayGrayBuffer(). This guidance applies specifically to this file and situation.

Applied to files:

  • src/activities/reader/EpubReaderActivity.cpp
🧬 Code graph analysis (43)
src/activities/util/BmpViewerActivity.h (2)
src/activities/util/BmpViewerActivity.cpp (1)
  • BmpViewerActivity (11-12)
src/main.cpp (1)
  • renderer (30-30)
src/activities/home/MyLibraryActivity.cpp (7)
src/activities/settings/KOReaderSettingsActivity.cpp (2)
  • render (113-153)
  • render (113-113)
src/activities/network/WifiSelectionActivity.cpp (2)
  • render (457-507)
  • render (457-457)
src/activities/reader/XtcReaderActivity.cpp (2)
  • render (128-144)
  • render (128-128)
src/activities/network/NetworkModeSelectionActivity.cpp (2)
  • render (57-85)
  • render (57-57)
src/activities/home/HomeActivity.cpp (2)
  • render (214-260)
  • render (214-214)
src/activities/home/RecentBooksActivity.cpp (2)
  • render (86-113)
  • render (86-86)
src/activities/reader/TxtReaderActivity.cpp (2)
  • render (339-372)
  • render (339-339)
src/activities/home/RecentBooksActivity.h (2)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/browser/OpdsBookBrowserActivity.h (1)
  • onEnter (30-54)
src/activities/browser/OpdsBookBrowserActivity.cpp (3)
src/activities/reader/KOReaderSyncActivity.cpp (6)
  • onEnter (184-227)
  • onEnter (184-184)
  • render (239-351)
  • render (239-239)
  • onWifiSelectionComplete (44-73)
  • onWifiSelectionComplete (44-44)
src/activities/Activity.cpp (2)
  • startActivityForResult (24-27)
  • startActivityForResult (24-24)
src/activities/network/CrossPointWebServerActivity.cpp (2)
  • onWifiSelectionComplete (144-165)
  • onWifiSelectionComplete (144-144)
src/activities/settings/ClearCacheActivity.h (2)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/Activity.cpp (2)
  • finish (31-31)
  • finish (31-31)
src/activities/settings/ButtonRemapActivity.cpp (8)
src/activities/settings/KOReaderAuthActivity.cpp (2)
  • render (88-112)
  • render (88-88)
src/activities/settings/KOReaderSettingsActivity.cpp (2)
  • render (113-153)
  • render (113-113)
src/activities/network/WifiSelectionActivity.cpp (2)
  • render (457-507)
  • render (457-457)
src/activities/settings/CalibreSettingsActivity.cpp (2)
  • render (88-124)
  • render (88-88)
src/activities/settings/OtaUpdateActivity.cpp (2)
  • render (76-135)
  • render (76-76)
src/activities/network/CalibreConnectActivity.cpp (2)
  • render (161-220)
  • render (161-161)
src/activities/network/CrossPointWebServerActivity.cpp (2)
  • render (328-351)
  • render (328-328)
src/activities/settings/SettingsActivity.cpp (2)
  • render (196-245)
  • render (196-196)
src/activities/reader/XtcReaderChapterSelectionActivity.h (1)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/reader/EpubReaderPercentSelectionActivity.cpp (5)
src/activities/settings/CalibreSettingsActivity.cpp (6)
  • onEnter (19-24)
  • onEnter (19-19)
  • onExit (26-26)
  • onExit (26-26)
  • render (88-124)
  • render (88-88)
src/activities/network/NetworkModeSelectionActivity.cpp (6)
  • onEnter (14-22)
  • onEnter (14-14)
  • onExit (24-24)
  • onExit (24-24)
  • render (57-85)
  • render (57-57)
src/activities/reader/EpubReaderMenuActivity.cpp (6)
  • onEnter (10-13)
  • onEnter (10-10)
  • onExit (15-15)
  • onExit (15-15)
  • render (54-117)
  • render (54-54)
src/activities/Activity.cpp (6)
  • requestUpdate (9-9)
  • requestUpdate (9-9)
  • setResult (29-29)
  • setResult (29-29)
  • finish (31-31)
  • finish (31-31)
src/activities/ActivityManager.cpp (2)
  • requestUpdate (190-196)
  • requestUpdate (190-190)
src/activities/settings/ButtonRemapActivity.h (3)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/settings/ButtonRemapActivity.cpp (8)
  • onEnter (20-32)
  • onEnter (20-20)
  • onExit (34-34)
  • onExit (34-34)
  • loop (36-95)
  • loop (36-36)
  • render (97-150)
  • render (97-97)
src/activities/Activity.cpp (2)
  • finish (31-31)
  • finish (31-31)
src/activities/settings/LanguageSelectActivity.cpp (6)
src/activities/reader/EpubReaderActivity.cpp (2)
  • render (398-529)
  • render (398-398)
src/activities/settings/KOReaderAuthActivity.cpp (2)
  • render (88-112)
  • render (88-88)
src/activities/settings/KOReaderSettingsActivity.cpp (2)
  • render (113-153)
  • render (113-113)
src/activities/network/WifiSelectionActivity.cpp (2)
  • render (457-507)
  • render (457-457)
src/activities/settings/CalibreSettingsActivity.cpp (2)
  • render (88-124)
  • render (88-88)
src/activities/network/NetworkModeSelectionActivity.cpp (2)
  • render (57-85)
  • render (57-57)
src/activities/network/CalibreConnectActivity.h (4)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/network/CalibreConnectActivity.cpp (8)
  • onEnter (18-44)
  • onEnter (18-18)
  • onExit (46-57)
  • onExit (46-46)
  • loop (96-159)
  • loop (96-96)
  • render (161-220)
  • render (161-161)
src/activities/network/CrossPointWebServerActivity.cpp (8)
  • onEnter (35-53)
  • onEnter (35-35)
  • onExit (55-94)
  • onExit (55-55)
  • loop (254-326)
  • loop (254-254)
  • render (328-351)
  • render (328-328)
src/activities/network/NetworkModeSelectionActivity.cpp (8)
  • onEnter (14-22)
  • onEnter (14-14)
  • onExit (24-24)
  • onExit (24-24)
  • loop (26-55)
  • loop (26-26)
  • render (57-85)
  • render (57-57)
src/activities/reader/KOReaderSyncActivity.cpp (8)
src/activities/Activity.cpp (6)
  • setResult (29-29)
  • setResult (29-29)
  • finish (31-31)
  • finish (31-31)
  • startActivityForResult (24-27)
  • startActivityForResult (24-24)
src/activities/reader/EpubReaderActivity.cpp (6)
  • onEnter (61-106)
  • onEnter (61-61)
  • onExit (108-118)
  • onExit (108-108)
  • render (398-529)
  • render (398-398)
src/activities/settings/KOReaderSettingsActivity.cpp (6)
  • onEnter (21-26)
  • onEnter (21-21)
  • onExit (28-28)
  • onExit (28-28)
  • render (113-153)
  • render (113-113)
src/activities/browser/OpdsBookBrowserActivity.cpp (8)
  • onEnter (23-37)
  • onEnter (23-23)
  • onWifiSelectionComplete (371-387)
  • onWifiSelectionComplete (371-371)
  • onExit (39-47)
  • onExit (39-39)
  • render (139-225)
  • render (139-139)
src/activities/network/WifiSelectionActivity.cpp (6)
  • onEnter (16-71)
  • onEnter (16-16)
  • onExit (73-88)
  • onExit (73-73)
  • render (457-507)
  • render (457-457)
src/activities/settings/CalibreSettingsActivity.cpp (6)
  • onEnter (19-24)
  • onEnter (19-19)
  • onExit (26-26)
  • onExit (26-26)
  • render (88-124)
  • render (88-88)
src/activities/settings/OtaUpdateActivity.cpp (8)
  • onEnter (53-64)
  • onEnter (53-53)
  • onWifiSelectionComplete (13-51)
  • onWifiSelectionComplete (13-13)
  • onExit (66-74)
  • onExit (66-66)
  • render (76-135)
  • render (76-76)
src/activities/network/CrossPointWebServerActivity.cpp (2)
  • onWifiSelectionComplete (144-165)
  • onWifiSelectionComplete (144-144)
src/activities/home/RecentBooksActivity.cpp (2)
src/activities/reader/EpubReaderActivity.cpp (2)
  • render (398-529)
  • render (398-398)
src/activities/reader/XtcReaderActivity.cpp (2)
  • render (128-144)
  • render (128-128)
src/activities/util/KeyboardEntryActivity.cpp (4)
src/activities/network/WifiSelectionActivity.cpp (4)
  • onComplete (684-694)
  • onComplete (684-684)
  • render (457-507)
  • render (457-457)
src/activities/network/NetworkModeSelectionActivity.cpp (4)
  • onCancel (95-100)
  • onCancel (95-95)
  • render (57-85)
  • render (57-57)
src/activities/Activity.cpp (6)
  • requestUpdate (9-9)
  • requestUpdate (9-9)
  • setResult (29-29)
  • setResult (29-29)
  • finish (31-31)
  • finish (31-31)
src/activities/ActivityManager.cpp (2)
  • requestUpdate (190-196)
  • requestUpdate (190-190)
src/activities/settings/KOReaderSettingsActivity.h (1)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/home/HomeActivity.cpp (1)
src/activities/Activity.cpp (2)
  • onSelectBook (18-22)
  • onSelectBook (18-18)
src/activities/reader/ReaderActivity.h (2)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/reader/ReaderActivity.cpp (2)
  • onGoBack (145-147)
  • onGoBack (145-145)
src/activities/reader/EpubReaderMenuActivity.cpp (2)
src/activities/Activity.cpp (6)
  • requestUpdate (9-9)
  • requestUpdate (9-9)
  • setResult (29-29)
  • setResult (29-29)
  • finish (31-31)
  • finish (31-31)
src/activities/ActivityManager.cpp (2)
  • requestUpdate (190-196)
  • requestUpdate (190-190)
src/activities/settings/LanguageSelectActivity.h (2)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/settings/LanguageSelectActivity.cpp (2)
  • handleSelection (44-52)
  • handleSelection (44-44)
src/activities/settings/CalibreSettingsActivity.cpp (3)
src/activities/Activity.cpp (4)
  • requestUpdate (9-9)
  • requestUpdate (9-9)
  • startActivityForResult (24-27)
  • startActivityForResult (24-24)
src/activities/ActivityManager.cpp (2)
  • requestUpdate (190-196)
  • requestUpdate (190-190)
src/main.cpp (2)
  • activityManager (31-31)
  • renderer (30-30)
src/activities/network/WifiSelectionActivity.h (2)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/network/CrossPointWebServerActivity.h (1)
  • onEnter (58-62)
src/activities/home/MyLibraryActivity.h (1)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/settings/KOReaderSettingsActivity.cpp (3)
src/activities/network/WifiSelectionActivity.cpp (4)
  • loop (285-441)
  • loop (285-285)
  • render (457-507)
  • render (457-457)
src/activities/Activity.cpp (4)
  • requestUpdate (9-9)
  • requestUpdate (9-9)
  • startActivityForResult (24-27)
  • startActivityForResult (24-24)
src/activities/ActivityManager.cpp (2)
  • requestUpdate (190-196)
  • requestUpdate (190-190)
src/activities/reader/EpubReaderActivity.h (4)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/reader/EpubReaderActivity.cpp (8)
  • onEnter (61-106)
  • onEnter (61-61)
  • onExit (108-118)
  • onExit (108-108)
  • loop (120-233)
  • loop (120-120)
  • render (398-529)
  • render (398-398)
src/activities/reader/EpubReaderChapterSelectionActivity.cpp (8)
  • onEnter (28-42)
  • onEnter (28-28)
  • onExit (44-44)
  • onExit (44-44)
  • loop (46-89)
  • loop (46-46)
  • render (91-139)
  • render (91-91)
src/activities/reader/XtcReaderChapterSelectionActivity.cpp (8)
  • onEnter (41-51)
  • onEnter (41-41)
  • onExit (53-53)
  • onExit (53-53)
  • loop (55-93)
  • loop (55-55)
  • render (95-142)
  • render (95-95)
src/activities/browser/OpdsBookBrowserActivity.h (1)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/network/NetworkModeSelectionActivity.h (2)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/network/CrossPointWebServerActivity.cpp (8)
  • onEnter (35-53)
  • onEnter (35-35)
  • onExit (55-94)
  • onExit (55-55)
  • loop (254-326)
  • loop (254-254)
  • render (328-351)
  • render (328-328)
src/activities/network/NetworkModeSelectionActivity.cpp (2)
src/activities/Activity.cpp (4)
  • setResult (29-29)
  • setResult (29-29)
  • finish (31-31)
  • finish (31-31)
src/activities/util/KeyboardEntryActivity.cpp (2)
  • onCancel (330-336)
  • onCancel (330-330)
src/activities/reader/TxtReaderActivity.cpp (2)
src/activities/reader/ReaderActivity.cpp (2)
  • onEnter (110-143)
  • onEnter (110-110)
src/activities/reader/ReaderActivity.h (1)
  • onEnter (33-34)
src/activities/settings/ClearCacheActivity.cpp (5)
src/activities/settings/KOReaderAuthActivity.cpp (4)
  • onEnter (50-76)
  • onEnter (50-50)
  • render (88-112)
  • render (88-88)
src/activities/settings/KOReaderSettingsActivity.cpp (4)
  • onEnter (21-26)
  • onEnter (21-21)
  • render (113-153)
  • render (113-113)
src/activities/network/WifiSelectionActivity.cpp (4)
  • onEnter (16-71)
  • onEnter (16-16)
  • render (457-507)
  • render (457-457)
src/activities/Activity.cpp (2)
  • requestUpdate (9-9)
  • requestUpdate (9-9)
src/activities/ActivityManager.cpp (2)
  • requestUpdate (190-196)
  • requestUpdate (190-190)
src/activities/settings/KOReaderAuthActivity.cpp (3)
src/activities/settings/OtaUpdateActivity.cpp (6)
  • onEnter (53-64)
  • onEnter (53-53)
  • onWifiSelectionComplete (13-51)
  • onWifiSelectionComplete (13-13)
  • render (76-135)
  • render (76-76)
src/activities/reader/KOReaderSyncActivity.cpp (6)
  • onEnter (184-227)
  • onEnter (184-184)
  • onWifiSelectionComplete (44-73)
  • onWifiSelectionComplete (44-44)
  • render (239-351)
  • render (239-239)
src/activities/Activity.cpp (4)
  • startActivityForResult (24-27)
  • startActivityForResult (24-24)
  • finish (31-31)
  • finish (31-31)
src/activities/reader/EpubReaderPercentSelectionActivity.h (2)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/reader/EpubReaderPercentSelectionActivity.cpp (8)
  • onEnter (16-20)
  • onEnter (16-16)
  • onExit (22-22)
  • onExit (22-22)
  • loop (35-58)
  • loop (35-35)
  • render (60-96)
  • render (60-60)
src/activities/settings/CalibreSettingsActivity.h (2)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/browser/OpdsBookBrowserActivity.cpp (8)
  • onEnter (23-37)
  • onEnter (23-23)
  • onExit (39-47)
  • onExit (39-39)
  • loop (49-137)
  • loop (49-49)
  • render (139-225)
  • render (139-139)
src/activities/RenderLock.h (1)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/settings/SettingsActivity.h (1)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/ActivityManager.cpp (3)
src/activities/Activity.cpp (2)
  • requestUpdate (9-9)
  • requestUpdate (9-9)
src/activities/network/CrossPointWebServerActivity.h (1)
  • preventAutoSleep (63-63)
src/activities/settings/OtaUpdateActivity.h (1)
  • skipLoopDelay (35-35)
src/activities/ActivityManager.h (4)
src/activities/Activity.h (2)
  • Activity (14-53)
  • void (29-29)
src/activities/RenderLock.h (1)
  • RenderLock (6-16)
src/activities/ActivityManager.cpp (25)
  • RenderLock (200-203)
  • RenderLock (205-208)
  • RenderLock (210-215)
  • exitActivity (117-124)
  • exitActivity (117-117)
  • renderTaskTrampoline (26-29)
  • renderTaskTrampoline (26-26)
  • renderTaskLoop (31-40)
  • renderTaskLoop (31-31)
  • begin (16-24)
  • begin (16-16)
  • loop (42-115)
  • loop (42-42)
  • replaceActivity (126-138)
  • replaceActivity (126-126)
  • goHome (167-167)
  • goHome (167-167)
  • preventAutoSleep (184-184)
  • preventAutoSleep (184-184)
  • isReaderActivity (186-186)
  • isReaderActivity (186-186)
  • skipLoopDelay (188-188)
  • skipLoopDelay (188-188)
  • requestUpdate (190-196)
  • requestUpdate (190-190)
src/main.cpp (4)
  • renderer (30-30)
  • loop (307-387)
  • loop (307-307)
  • activityManager (31-31)
src/activities/reader/XtcReaderChapterSelectionActivity.cpp (2)
src/activities/Activity.cpp (4)
  • setResult (29-29)
  • setResult (29-29)
  • finish (31-31)
  • finish (31-31)
src/activities/reader/XtcReaderActivity.cpp (2)
  • render (128-144)
  • render (128-128)
src/activities/reader/EpubReaderMenuActivity.h (2)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/reader/EpubReaderActivity.cpp (8)
  • onEnter (61-106)
  • onEnter (61-61)
  • onExit (108-118)
  • onExit (108-108)
  • loop (120-233)
  • loop (120-120)
  • render (398-529)
  • render (398-398)
src/activities/network/CrossPointWebServerActivity.h (3)
src/activities/Activity.h (1)
  • Activity (14-53)
src/network/CrossPointWebServer.h (1)
  • CrossPointWebServer (20-108)
src/activities/network/CrossPointWebServerActivity.cpp (8)
  • onEnter (35-53)
  • onEnter (35-35)
  • onExit (55-94)
  • onExit (55-55)
  • loop (254-326)
  • loop (254-254)
  • render (328-351)
  • render (328-328)
src/activities/reader/XtcReaderActivity.cpp (3)
src/activities/reader/EpubReaderActivity.cpp (2)
  • render (398-529)
  • render (398-398)
src/activities/reader/TxtReaderActivity.cpp (2)
  • render (339-372)
  • render (339-339)
src/activities/Activity.cpp (2)
  • startActivityForResult (24-27)
  • startActivityForResult (24-24)
src/activities/settings/OtaUpdateActivity.h (3)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/settings/OtaUpdateActivity.cpp (8)
  • onEnter (53-64)
  • onEnter (53-53)
  • onExit (66-74)
  • onExit (66-66)
  • loop (137-195)
  • loop (137-137)
  • render (76-135)
  • render (76-76)
src/activities/Activity.cpp (4)
  • onEnter (5-5)
  • onEnter (5-5)
  • onExit (7-7)
  • onExit (7-7)
src/activities/network/CrossPointWebServerActivity.cpp (1)
src/activities/Activity.cpp (6)
  • startActivityForResult (24-27)
  • startActivityForResult (24-24)
  • onGoHome (16-16)
  • onGoHome (16-16)
  • requestUpdate (9-9)
  • requestUpdate (9-9)
src/activities/network/WifiSelectionActivity.cpp (2)
src/activities/Activity.cpp (6)
  • startActivityForResult (24-27)
  • startActivityForResult (24-24)
  • setResult (29-29)
  • setResult (29-29)
  • finish (31-31)
  • finish (31-31)
src/activities/util/KeyboardEntryActivity.cpp (2)
  • onComplete (321-328)
  • onComplete (321-321)
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/activities/ActivityManager.cpp`:
- Around line 42-115: In ActivityManager::loop() the Pop path holds a RenderLock
across the branch that calls goHome(), which can deadlock if goHome() triggers
replaceActivity()/onEnter() that re-acquires RenderLock; release the lock before
calling goHome(): ensure the RenderLock acquired earlier (e.g., the one created
in the Pop branch) is unlocked (or destroyed/scope ended) immediately after
exitActivity(lock) and pendingAction reset, then call goHome() without holding
the RenderLock so onEnter()/replaceActivity() can safely acquire their own lock.

In `@src/activities/network/CrossPointWebServerActivity.cpp`:
- Around line 51-52: The two lambdas passed to startActivityForResult that
currently call onNetworkModeSelected(result.selectedNetworkMode) must guard
against cancellation like the handler in the other block: check
result.isCancelled and return early if true, otherwise call
onNetworkModeSelected with result.selectedNetworkMode; update both occurrences
(the lambda handling NetworkModeSelectionActivity created with
NetworkModeSelectionActivity(renderer, mappedInput) and the second similar
lambda) to use the same pattern, referencing ActivityResult::isCancelled and
onNetworkModeSelected(...) to avoid using an undefined/placeholder
selectedNetworkMode when cancelled.

In `@src/activities/reader/EpubReaderActivity.cpp`:
- Around line 126-130: Move the epub null-check to the start of the loop()
function so all branches return early if epub is null rather than only the
Confirm path; remove the inner guard around the Confirm handling (the
LOG_ERR("ERS","No book loaded") and return inside that branch) after adding the
top-of-loop check to avoid duplicate checks and ensure no code in loop()
dereferences epub when it's null. Ensure you reference the epub pointer and the
loop() method during the change and keep behavior identical when epub is
present.

In `@src/activities/reader/ReaderActivity.cpp`:
- Around line 145-147: ReaderActivity::onGoBack() currently calls onGoHome()
unconditionally which loses the previous activity because ReaderActivity was
created with replaceActivity(); either make back navigate to the originating
activity by changing the creation to use pushActivity() so the activity stack is
preserved (so onGoBack() can pop to the previous activity), or if Home is indeed
the intended destination, remove the TODO and document that behavior; update the
code paths that create ReaderActivity (where replaceActivity() is used) to
pushActivity() when expecting normal back navigation (Library/Recents), and keep
onGoBack() calling onGoHome() only if you decide Home is the correct and
documented fallback.

---

Outside diff comments:
In `@src/activities/browser/OpdsBookBrowserActivity.cpp`:
- Around line 371-386: In onWifiSelectionComplete
(OpdsBookBrowserActivity::onWifiSelectionComplete) the failure branch sets state
= BrowserState::ERROR and updates errorMessage but does not call
requestUpdate(), leaving the UI potentially stale; add a call to requestUpdate()
after setting errorMessage (and after performing
WiFi.disconnect()/WiFi.mode(WIFI_OFF)) so the UI is refreshed when the error
state is entered.

---

Nitpick comments:
In `@src/activities/ActivityManager.h`:
- Around line 27-28: Remove the redundant forward declarations "class Activity;"
and "class RenderLock;" from ActivityManager.h because Activity.h and
RenderLock.h are already included (so the forward declarations are unnecessary);
delete those two lines and rebuild to confirm no remaining references require
forward declarations elsewhere in this header or switch to forward declarations
only if you remove the corresponding `#include` lines instead.

In `@src/activities/ActivityResult.h`:
- Around line 7-8: The TODO indicates NetworkMode (enum class NetworkMode {
JOIN_NETWORK, CONNECT_CALIBRE, CREATE_HOTSPOT }) is in the wrong place; move it
into a dedicated network-related header (e.g., create a new NetworkMode.h or
NetworkEnums.h) and replace the enum in ActivityResult.h with an include of that
new header, update any files that reference NetworkMode to include the new
header, and remove the TODO; also link this change to an issue or add a brief
comment referencing an issue ID if you create one so the relocation is tracked.

In `@src/activities/browser/OpdsBookBrowserActivity.cpp`:
- Around line 49-54: The call to the empty base implementation Activity::loop()
inside OpdsBookBrowserActivity::loop when state == BrowserState::WIFI_SELECTION
is unnecessary; remove the Activity::loop() invocation and simply return after
handling the WIFI_SELECTION branch to simplify the method (locate the
conditional in OpdsBookBrowserActivity::loop and delete the Activity::loop()
call).

In `@src/activities/home/HomeActivity.cpp`:
- Around line 262-266: HomeActivity::onSelectBook duplicates the base
Activity::onSelectBook behavior; replace the body to forward to the base
implementation instead of duplicating logic—call Activity::onSelectBook(path)
(or the appropriate base class qualifier) so Intent construction and the
activityManager.goToReader call remain centralized in Activity::onSelectBook.

In `@src/activities/network/NetworkModeSelectionActivity.h`:
- Around line 8-9: Remove the stale commented-out enum stub for NetworkMode:
delete the commented lines that declare "enum class NetworkMode { JOIN_NETWORK,
CONNECT_CALIBRE, CREATE_HOTSPOT }" so the header no longer contains
dead/commented-out code; if this enum is needed elsewhere, ensure the real
definition (NetworkMode) resides in the proper header or source and update
includes instead of keeping the commented stub.

In `@src/activities/reader/EpubReaderPercentSelectionActivity.cpp`:
- Around line 53-58: The Up/Down handlers currently call
adjustPercent(kLargeStep) and adjustPercent(-kLargeStep) which is
counter-intuitive for a horizontal slider; either swap the Up/Down mappings so
Up calls adjustPercent(-kLargeStep) and Down calls adjustPercent(kLargeStep), or
remove/disable Up/Down handling if vertical input should be ignored. Update the
buttonNavigator.onPressAndContinuous calls that reference
MappedInputManager::Button::Up and MappedInputManager::Button::Down and ensure
adjustPercent, kLargeStep and any inline comments reflect the chosen behavior.

In `@src/activities/util/KeyboardEntryActivity.cpp`:
- Around line 321-336: The onComplete implementation incorrectly uses std::move
on a const std::string& (parameter text), which won’t move; either change the
onComplete signature to take text by value (std::string text) and keep
result.inputText = std::move(text) to enable move semantics, or keep the const
std::string& signature and assign without move (result.inputText = text); update
the onComplete function accordingly and leave onCancel unchanged (references:
KeyboardEntryActivity::onComplete, ActivityResult::inputText, setResult,
finish).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/activities/ActivityManager.cpp (1)

191-197: Clarify notification coalescing behavior in comment.

The comment says "multiple rapid calls won't be lost," but ulTaskNotifyTake(pdTRUE, ...) in renderTaskLoop clears the notification value to 0 after waking, so multiple increments are coalesced into a single render. This is likely intentional (avoiding redundant renders), but the comment implies each notification triggers a separate render.

📝 Suggested comment update
 void ActivityManager::requestUpdate() {
-  // Using direct notification to signal the render task to update
-  // Increment counter so multiple rapid calls won't be lost
+  // Using direct notification to signal the render task to update.
+  // Multiple rapid calls are coalesced into a single render cycle.
   if (renderTaskHandle) {
     xTaskNotify(renderTaskHandle, 1, eIncrement);
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/activities/ActivityManager.cpp` around lines 191 - 197, The comment in
ActivityManager::requestUpdate is misleading about notifications — explain that
while xTaskNotify(renderTaskHandle, 1, eIncrement) increments the task
notification value, renderTaskLoop uses ulTaskNotifyTake(pdTRUE, ...) which
clears the value on wake so multiple rapid increments are coalesced into a
single render; update the comment in requestUpdate to state this intentional
coalescing behavior (i.e., redundant rapid calls collapse to one render) and
reference renderTaskLoop/ulTaskNotifyTake so future readers know why multiple
increments don't produce multiple renders.
src/activities/reader/EpubReaderActivity.cpp (2)

353-364: Missing RenderLock before state modification (same pattern as chapter selection).

Similar to the chapter selection callback, this modifies shared state and resets section without holding the lock.

🔒 Proposed fix
       startActivityForResult(new KOReaderSyncActivity(renderer, mappedInput, epub, epub->getPath(), currentSpineIndex,
                                                       currentPage, totalPages),
                              [this](const ActivityResult& result) {
                                if (!result.isCancelled) {
                                  if (currentSpineIndex != result.syncedSpineIndex ||
                                      (section && section->currentPage != result.syncedPage)) {
+                                   RenderLock lock(*this);
                                    currentSpineIndex = result.syncedSpineIndex;
                                    nextPageNumber = result.syncedPage;
                                    section.reset();
                                  }
                                }
                              });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/activities/reader/EpubReaderActivity.cpp` around lines 353 - 364, The
callback passed to startActivityForResult that handles KOReaderSyncActivity
results modifies shared state (currentSpineIndex, nextPageNumber, and resets
section) without acquiring the RenderLock; wrap the state-modifying block inside
the same RenderLock used elsewhere (e.g. chapter selection) so you lock before
checking/updating currentSpineIndex/section and release after (use the existing
RenderLock type or RAII guard used in this file to ensure exception-safe
unlocking).

304-311: Consider acquiring RenderLock before modifying shared state.

The callback modifies currentSpineIndex, nextPageNumber, and calls section.reset() without holding a RenderLock. Other mutation sites in this file (lines 192-196, 212-217, 224-230, 289-296) consistently acquire the lock before modifying section to prevent races with the render task.

🔒 Proposed fix
       startActivityForResult(new EpubReaderChapterSelectionActivity(renderer, mappedInput, epub, path, spineIdx),
                              [this](const ActivityResult& result) {
                                if (!result.isCancelled && currentSpineIndex != result.selectedSpineIndex) {
+                                 RenderLock lock(*this);
                                  currentSpineIndex = result.selectedSpineIndex;
                                  nextPageNumber = 0;
                                  section.reset();
                                }
                              });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/activities/reader/EpubReaderActivity.cpp` around lines 304 - 311, The
callback passed to startActivityForResult mutates shared state
(currentSpineIndex, nextPageNumber, and calls section.reset()) without holding
the RenderLock; wrap the mutation block in the same RenderLock acquisition
pattern used elsewhere so the render task cannot race with these changes.
Specifically, in the lambda given to startActivityForResult, acquire the
RenderLock (using the same RenderLock type/object used in this file) before
comparing/updating currentSpineIndex, setting nextPageNumber, and calling
section.reset(), then release the lock after the updates.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec2f24d and 847f344.

📒 Files selected for processing (3)
  • src/activities/ActivityManager.cpp
  • src/activities/reader/EpubReaderActivity.cpp
  • src/activities/reader/ReaderActivity.cpp
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: Levrk
Repo: crosspoint-reader/crosspoint-reader PR: 909
File: src/activities/reader/EpubReaderActivity.cpp:461-461
Timestamp: 2026-02-16T20:43:19.339Z
Learning: In src/activities/reader/EpubReaderActivity.cpp, when using ConfirmationActivity with enterNewActivity(), it's not necessary to call exitActivity() beforehand. The ConfirmationActivity operates as a modal dialog and cleanup is handled via the pendingSubactivityExit flag in the callback lambda. This differs from other activities like EpubReaderChapterSelectionActivity or KOReaderSyncActivity which do require exitActivity() before enterNewActivity().
📚 Learning: 2026-02-16T20:43:19.339Z
Learnt from: Levrk
Repo: crosspoint-reader/crosspoint-reader PR: 909
File: src/activities/reader/EpubReaderActivity.cpp:461-461
Timestamp: 2026-02-16T20:43:19.339Z
Learning: In src/activities/reader/EpubReaderActivity.cpp, when using ConfirmationActivity with enterNewActivity(), it's not necessary to call exitActivity() beforehand. The ConfirmationActivity operates as a modal dialog and cleanup is handled via the pendingSubactivityExit flag in the callback lambda. This differs from other activities like EpubReaderChapterSelectionActivity or KOReaderSyncActivity which do require exitActivity() before enterNewActivity().

Applied to files:

  • src/activities/ActivityManager.cpp
  • src/activities/reader/ReaderActivity.cpp
  • src/activities/reader/EpubReaderActivity.cpp
📚 Learning: 2026-02-18T15:43:07.515Z
Learnt from: GavinHigham
Repo: crosspoint-reader/crosspoint-reader PR: 966
File: src/activities/ActivityWithSubactivity.h:20-21
Timestamp: 2026-02-18T15:43:07.515Z
Learning: In the crosspoint-reader activity architecture, when using ActivityWithSubactivity, the currentActivity global variable remains at the top-level activity (e.g., ReaderActivity) even when nested subactivities are active. The supportsOrientation() delegation chain starts from the top-level activity and stops when it reaches a subactivity that overrides the method (e.g., EpubReaderActivity returns true). Deeper nested subactivities (like EpubReaderChapterSelectionActivity or EpubReaderPercentSelectionActivity) do not need to override supportsOrientation() because they never become the currentActivity and the delegation chain doesn't reach them.

Applied to files:

  • src/activities/reader/ReaderActivity.cpp
  • src/activities/reader/EpubReaderActivity.cpp
📚 Learning: 2026-02-19T17:46:26.871Z
Learnt from: Tritlo
Repo: crosspoint-reader/crosspoint-reader PR: 1003
File: src/activities/reader/EpubReaderActivity.cpp:657-674
Timestamp: 2026-02-19T17:46:26.871Z
Learning: In src/activities/reader/EpubReaderActivity.cpp's renderContents() method, when uncached images exist, Phase 1 intentionally calls displayBuffer(forceFullRefresh) to perform a HALF_REFRESH (if needed), while Phase 2 intentionally calls renderer.displayBuffer() directly without forceFullRefresh. This is by design: Phase 1's refresh clears the screen properly to prevent ghosting, so Phase 2 can use a faster refresh mode for better performance. The grayscale anti-aliasing is handled separately after renderContents() via displayGrayBuffer().

Applied to files:

  • src/activities/reader/ReaderActivity.cpp
📚 Learning: 2026-02-12T06:57:35.955Z
Learnt from: CaptainFrito
Repo: crosspoint-reader/crosspoint-reader PR: 725
File: src/activities/network/CalibreConnectActivity.cpp:218-264
Timestamp: 2026-02-12T06:57:35.955Z
Learning: In src/activities/network/CalibreConnectActivity.cpp, button hints (« Exit) are intentionally omitted from the SERVER_STARTING and ERROR states—only the SERVER_RUNNING state displays navigation hints. This is a deliberate design decision by the author.

Applied to files:

  • src/activities/reader/ReaderActivity.cpp
📚 Learning: 2026-02-19T17:46:26.871Z
Learnt from: Tritlo
Repo: crosspoint-reader/crosspoint-reader PR: 1003
File: src/activities/reader/EpubReaderActivity.cpp:657-674
Timestamp: 2026-02-19T17:46:26.871Z
Learning: In EpubReaderActivity.cpp, renderContents() behavior is intentional: if uncached images exist, Phase 1 should call displayBuffer(forceFullRefresh) to perform a HALF_REFRESH and clear ghosting; Phase 2 should call renderer.displayBuffer() without forceFullRefresh for a faster refresh. Grayscale anti-aliasing is applied separately after renderContents() via displayGrayBuffer(). This guidance applies specifically to this file and situation.

Applied to files:

  • src/activities/reader/EpubReaderActivity.cpp
🧬 Code graph analysis (2)
src/activities/reader/ReaderActivity.cpp (3)
src/main.cpp (2)
  • activityManager (31-31)
  • renderer (30-30)
src/activities/reader/ReaderActivity.h (1)
  • onEnter (33-34)
src/activities/Activity.cpp (2)
  • finish (31-31)
  • finish (31-31)
src/activities/reader/EpubReaderActivity.cpp (1)
src/activities/Activity.cpp (6)
  • finish (31-31)
  • finish (31-31)
  • startActivityForResult (24-27)
  • startActivityForResult (24-24)
  • onGoHome (16-16)
  • onGoHome (16-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (13)
src/activities/reader/ReaderActivity.cpp (3)

83-85: LGTM!

Clean migration to Intent-based navigation through ActivityManager.


88-108: LGTM!

The reader transitions now properly delegate to activityManager.replaceActivity(), removing the previous manual exit/enter patterns.


110-145: LGTM - back navigation behavior is now well-defined.

The onGoBack() implementation calling finish() properly delegates to popActivity(). When a book fails to load during onEnter(), the activity is popped; if the stack is empty (typical since goToReader() uses replaceActivity()), ActivityManager routes to Home. This is a clear improvement over the previous ambiguous behavior flagged in earlier reviews.

src/activities/ActivityManager.cpp (4)

16-24: LGTM!

Task creation with proper stack size and assert on failure is good defensive programming.


31-40: LGTM!

The render loop correctly acquires both power and render locks before rendering, preventing low-power mode interruption during display updates.


42-116: LGTM - careful lock management throughout.

The Pop path correctly unlocks before calling goHome() (line 59) and before invoking result handlers (line 74), addressing potential deadlock concerns. The result handler is moved before invocation (line 72) to safely handle nested startActivityForResult() calls. Stack cleanup during Replace (lines 95-99) properly calls onExit() before deletion.


201-223: LGTM!

The RenderLock RAII implementation correctly manages the mutex with proper double-release protection via isLocked. The RenderLock(Activity&) constructor preserves API compatibility for render method signatures while ignoring the parameter.

src/activities/reader/EpubReaderActivity.cpp (6)

61-62: LGTM!

Base class lifecycle calls correctly updated from ActivityWithSubactivity to Activity, aligning with the new ActivityManager architecture.

Also applies to: 108-109


120-125: LGTM! Null guard properly placed at loop entry.

The early finish() call when epub is null prevents null dereferences throughout the method and cleanly exits the activity.


137-144: LGTM!

The callback correctly:

  • Applies orientation changes unconditionally (intentional, as orientation selection persists regardless of menu action)
  • Only processes menu actions when not cancelled
  • Safely captures this since the parent activity remains on the stack when the callback executes

149-151: LGTM!

Navigation via activityManager.goToMyLibrary() is clean, and the ternary provides null-safety for the path.


321-326: LGTM!

The callback delegates to jumpToPercent(), which internally acquires RenderLock before modifying state.


329-348: LGTM!

The GO_HOME and DELETE_CACHE handlers are well-structured:

  • DELETE_CACHE properly acquires RenderLock before state modifications
  • Adding epub && at line 336 provides defensive null-safety
  • Both paths correctly use onGoHome() for navigation
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/activities/ActivityManager.cpp`:
- Around line 191-197: The comment in ActivityManager::requestUpdate is
misleading about notifications — explain that while
xTaskNotify(renderTaskHandle, 1, eIncrement) increments the task notification
value, renderTaskLoop uses ulTaskNotifyTake(pdTRUE, ...) which clears the value
on wake so multiple rapid increments are coalesced into a single render; update
the comment in requestUpdate to state this intentional coalescing behavior
(i.e., redundant rapid calls collapse to one render) and reference
renderTaskLoop/ulTaskNotifyTake so future readers know why multiple increments
don't produce multiple renders.

In `@src/activities/reader/EpubReaderActivity.cpp`:
- Around line 353-364: The callback passed to startActivityForResult that
handles KOReaderSyncActivity results modifies shared state (currentSpineIndex,
nextPageNumber, and resets section) without acquiring the RenderLock; wrap the
state-modifying block inside the same RenderLock used elsewhere (e.g. chapter
selection) so you lock before checking/updating currentSpineIndex/section and
release after (use the existing RenderLock type or RAII guard used in this file
to ensure exception-safe unlocking).
- Around line 304-311: The callback passed to startActivityForResult mutates
shared state (currentSpineIndex, nextPageNumber, and calls section.reset())
without holding the RenderLock; wrap the mutation block in the same RenderLock
acquisition pattern used elsewhere so the render task cannot race with these
changes. Specifically, in the lambda given to startActivityForResult, acquire
the RenderLock (using the same RenderLock type/object used in this file) before
comparing/updating currentSpineIndex, setting nextPageNumber, and calling
section.reset(), then release the lock after the updates.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 847f344 and 6bf5098.

📒 Files selected for processing (5)
  • src/activities/ActivityManager.cpp
  • src/activities/network/CrossPointWebServerActivity.cpp
  • src/activities/settings/KOReaderAuthActivity.cpp
  • src/activities/util/KeyboardEntryActivity.cpp
  • src/activities/util/KeyboardEntryActivity.h
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: GavinHigham
Repo: crosspoint-reader/crosspoint-reader PR: 966
File: src/activities/ActivityWithSubactivity.h:20-21
Timestamp: 2026-02-18T15:43:07.515Z
Learning: In the crosspoint-reader activity architecture, when using ActivityWithSubactivity, the currentActivity global variable remains at the top-level activity (e.g., ReaderActivity) even when nested subactivities are active. The supportsOrientation() delegation chain starts from the top-level activity and stops when it reaches a subactivity that overrides the method (e.g., EpubReaderActivity returns true). Deeper nested subactivities (like EpubReaderChapterSelectionActivity or EpubReaderPercentSelectionActivity) do not need to override supportsOrientation() because they never become the currentActivity and the delegation chain doesn't reach them.
📚 Learning: 2026-02-16T20:43:19.339Z
Learnt from: Levrk
Repo: crosspoint-reader/crosspoint-reader PR: 909
File: src/activities/reader/EpubReaderActivity.cpp:461-461
Timestamp: 2026-02-16T20:43:19.339Z
Learning: In src/activities/reader/EpubReaderActivity.cpp, when using ConfirmationActivity with enterNewActivity(), it's not necessary to call exitActivity() beforehand. The ConfirmationActivity operates as a modal dialog and cleanup is handled via the pendingSubactivityExit flag in the callback lambda. This differs from other activities like EpubReaderChapterSelectionActivity or KOReaderSyncActivity which do require exitActivity() before enterNewActivity().

Applied to files:

  • src/activities/util/KeyboardEntryActivity.cpp
  • src/activities/network/CrossPointWebServerActivity.cpp
  • src/activities/settings/KOReaderAuthActivity.cpp
  • src/activities/util/KeyboardEntryActivity.h
  • src/activities/ActivityManager.cpp
📚 Learning: 2026-02-12T06:57:35.955Z
Learnt from: CaptainFrito
Repo: crosspoint-reader/crosspoint-reader PR: 725
File: src/activities/network/CalibreConnectActivity.cpp:218-264
Timestamp: 2026-02-12T06:57:35.955Z
Learning: In src/activities/network/CalibreConnectActivity.cpp, button hints (« Exit) are intentionally omitted from the SERVER_STARTING and ERROR states—only the SERVER_RUNNING state displays navigation hints. This is a deliberate design decision by the author.

Applied to files:

  • src/activities/network/CrossPointWebServerActivity.cpp
  • src/activities/settings/KOReaderAuthActivity.cpp
📚 Learning: 2026-02-18T15:43:07.515Z
Learnt from: GavinHigham
Repo: crosspoint-reader/crosspoint-reader PR: 966
File: src/activities/ActivityWithSubactivity.h:20-21
Timestamp: 2026-02-18T15:43:07.515Z
Learning: In the crosspoint-reader activity architecture, when using ActivityWithSubactivity, the currentActivity global variable remains at the top-level activity (e.g., ReaderActivity) even when nested subactivities are active. The supportsOrientation() delegation chain starts from the top-level activity and stops when it reaches a subactivity that overrides the method (e.g., EpubReaderActivity returns true). Deeper nested subactivities (like EpubReaderChapterSelectionActivity or EpubReaderPercentSelectionActivity) do not need to override supportsOrientation() because they never become the currentActivity and the delegation chain doesn't reach them.

Applied to files:

  • src/activities/settings/KOReaderAuthActivity.cpp
📚 Learning: 2026-02-19T17:46:26.871Z
Learnt from: Tritlo
Repo: crosspoint-reader/crosspoint-reader PR: 1003
File: src/activities/reader/EpubReaderActivity.cpp:657-674
Timestamp: 2026-02-19T17:46:26.871Z
Learning: In src/activities/reader/EpubReaderActivity.cpp's renderContents() method, when uncached images exist, Phase 1 intentionally calls displayBuffer(forceFullRefresh) to perform a HALF_REFRESH (if needed), while Phase 2 intentionally calls renderer.displayBuffer() directly without forceFullRefresh. This is by design: Phase 1's refresh clears the screen properly to prevent ghosting, so Phase 2 can use a faster refresh mode for better performance. The grayscale anti-aliasing is handled separately after renderContents() via displayGrayBuffer().

Applied to files:

  • src/activities/settings/KOReaderAuthActivity.cpp
🧬 Code graph analysis (5)
src/activities/util/KeyboardEntryActivity.cpp (2)
src/activities/ActivityManager.cpp (2)
  • requestUpdate (199-205)
  • requestUpdate (199-199)
src/activities/Activity.cpp (6)
  • requestUpdate (9-9)
  • requestUpdate (9-9)
  • setResult (29-29)
  • setResult (29-29)
  • finish (31-31)
  • finish (31-31)
src/activities/network/CrossPointWebServerActivity.cpp (1)
src/activities/Activity.cpp (6)
  • startActivityForResult (24-27)
  • startActivityForResult (24-24)
  • onGoHome (16-16)
  • onGoHome (16-16)
  • requestUpdate (9-9)
  • requestUpdate (9-9)
src/activities/settings/KOReaderAuthActivity.cpp (1)
src/activities/Activity.cpp (2)
  • finish (31-31)
  • finish (31-31)
src/activities/util/KeyboardEntryActivity.h (4)
src/activities/Activity.h (1)
  • Activity (14-53)
src/activities/network/CrossPointWebServerActivity.cpp (8)
  • onEnter (35-58)
  • onEnter (35-35)
  • onExit (60-99)
  • onExit (60-60)
  • loop (265-337)
  • loop (265-265)
  • render (339-362)
  • render (339-339)
src/activities/util/KeyboardEntryActivity.cpp (12)
  • onEnter (22-27)
  • onEnter (22-22)
  • onExit (29-29)
  • onExit (29-29)
  • loop (107-187)
  • loop (107-107)
  • render (189-319)
  • render (189-189)
  • onComplete (321-328)
  • onComplete (321-321)
  • onCancel (330-336)
  • onCancel (330-330)
src/activities/ActivityManager.cpp (2)
  • loop (44-118)
  • loop (44-44)
src/activities/ActivityManager.cpp (4)
src/main.cpp (3)
  • loop (307-387)
  • loop (307-307)
  • activityManager (31-31)
src/activities/Activity.cpp (2)
  • requestUpdate (9-9)
  • requestUpdate (9-9)
src/activities/network/CrossPointWebServerActivity.h (1)
  • preventAutoSleep (63-63)
src/activities/settings/OtaUpdateActivity.h (1)
  • skipLoopDelay (35-35)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: cppcheck
🔇 Additional comments (35)
src/activities/util/KeyboardEntryActivity.h (2)

32-45: LGTM: constructor + render signature align with new ActivityManager flow.


60-62: LGTM: private handlers make the completion path explicit.

src/activities/util/KeyboardEntryActivity.cpp (5)

85-88: LGTM: direct onComplete call matches internal handler design.


183-185: LGTM: direct onCancel call is consistent with new result flow.


189-189: LGTM: render signature updated for RenderLock.


321-328: LGTM: onComplete correctly sets result and finishes.


330-335: LGTM: onCancel correctly signals cancellation and finishes.

src/activities/settings/KOReaderAuthActivity.cpp (4)

51-77: LGTM!

The migration from ActivityWithSubactivity::onEnter() to Activity::onEnter() and the adoption of startActivityForResult with the callback pattern correctly aligns with the new ActivityManager lifecycle model.


79-87: LGTM!

Base class call updated correctly. The WiFi cleanup logic is preserved as expected.


89-89: LGTM!

Render signature correctly updated to use the unified RenderLock type.


115-121: Setting a result before finish() would be inconsistent with actual usage; the parent callback deliberately ignores it.

KOReaderAuthActivity is started via startActivityForResult in KOReaderSettingsActivity (line 109), but the callback is an empty lambda [](const ActivityResult&) {} that explicitly ignores the result. The activity appears designed as a modal dialog for user feedback (displaying success/failure messages) rather than as a mechanism to communicate authentication outcomes back to its parent. Calling setResult() is not necessary here.

src/activities/ActivityManager.cpp (15)

16-24: Render task initialization looks solid.

Clear task setup with an assert to surface failures early.


26-29: Trampoline entry is minimal and correct.


31-42: Render loop locking and power lock ordering are sensible.

The TOCTOU-safe lock and power lock around render are well placed.


44-49: Loop delegation without a global lock is consistent with the design.


50-87: Pop flow cleanly handles results and avoids lock re-entrancy.

Unlocking before handlers/goHome keeps deadlocks at bay.


89-115: Push/replace state transitions look consistent and safe.


120-127: exitActivity() under lock is clear and correct.


129-141: Deferred replace avoids delete-this hazards.


143-158: Navigation helpers are straightforward and consistent.


164-170: Remaining navigation helpers look good.


172-191: Push/pop request wiring is tidy and defensive.


193-198: State query helpers are clean and clear.


199-205: Render notifications are coalesced correctly.


209-231: RenderLock RAII implementation is straightforward.


159-162: goToSleep() does not risk re-entrance.

The function is called from the main event loop (lines 348 and 354 in main.cpp) with return; statements, so activityManager.loop() is never invoked in that context. After goToSleep() renders the sleep screen via its nested loop() call, execution proceeds directly to display.deepSleep() and device deep sleep, never returning to the main event loop. The nested loop() call is safe and intentional.

src/activities/network/CrossPointWebServerActivity.cpp (9)

35-57: onEnter now cleanly boots into mode selection with cancellation handling.


60-62: Base onExit() delegation is in the right place.


113-125: Calibre connect result handoff back to mode selection is clear.


136-140: WiFi selection result plumbing looks good.


167-174: Cancellation path now routes cleanly to home or re-selection.


195-198: AP start failure now exits cleanly.


245-252: Immediate render update + error fallback are well handled.


323-324: Back button exits consistently via onGoHome().

Also applies to: 332-334


339-339: Render signature update matches the new RenderLock API.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/activities/util/KeyboardEntryActivity.h`:
- Around line 15-19: Update the usage comment for KeyboardEntryActivity to
reflect ActivityManager-driven lifecycle: replace steps that mention manual
onEnter() and loop() with instructions to start the activity via ActivityManager
(e.g., call startActivityForResult with a KeyboardEntryActivity instance) and
handle the returned ActivityResult in the result handler/callback; reference
KeyboardEntryActivity, ActivityManager, startActivityForResult, and
ActivityResult in the wording so callers know to use the manager and result
handler rather than calling onEnter()/loop() directly.

@znelson
Copy link
Contributor

znelson commented Feb 22, 2026

I'm in favor of moving in this direction.

After looking through the changes, I have a few thoughts that I put into patches for you to consider:

  1. Minor thing, prefer enum class for type safety and to avoid potential name collisions?
    xsn-crosspoint-fd3f6e9-enum class?.patch
  2. Use unique_ptr for activities instead of raw pointers, to better convey ownership semantics?
    xsn-crosspoint-6d6d6a8-Use unique_ptr?.patch
  3. Do we gain much from one catch-all Intent type, or would it be cleaner to just pass the appropriate parameters for each activity?
    xsn-crosspoint-a517480-Cleaner without Intent?.patch
  4. Similarly, the catch-all ActivityResult class starts to feel unwieldy carrying a bunch of unrelated data. Maybe a std::variant based result type? xsn-crosspoint-c317feb-Variant based ActivityResult?.patch

I'm seeing an occasional crash after bouncing into and out of a few epub books and using the chapter select activity. I'll try to make sense of the crash dump.

@ngxson
Copy link
Contributor Author

ngxson commented Feb 26, 2026

btw, do you have any public OPDS server that can be used for testing? I tested most activities except for OPDS, a bit struggle to setup one myself

osteotek
osteotek previously approved these changes Feb 26, 2026
@osteotek
Copy link
Member

btw, do you have any public OPDS server that can be used for testing? I tested most activities except for OPDS, a bit struggle to setup one myself

There is the Project Gutenberg OPDS - https://www.gutenberg.org/ebooks/search.opds/
works with empty credentials

@ngxson ngxson requested a review from znelson February 26, 2026 12:01
@ngxson
Copy link
Contributor Author

ngxson commented Feb 27, 2026

@znelson Would you mind having a look? Thanks!

@ngxson ngxson enabled auto-merge (squash) February 27, 2026 00:04
@znelson
Copy link
Contributor

znelson commented Feb 27, 2026

Looks good! Played around with it some this evening and didn't hit any problems.

A few possible small tweaks to consider:
crosspoint-reader-39d684f-ActivityManager tweaks.patch

@ngxson ngxson merged commit c4fc4ef into crosspoint-reader:master Feb 27, 2026
6 checks passed
@ngxson
Copy link
Contributor Author

ngxson commented Feb 27, 2026

@znelson the requestUpdateAndWait should be implemented using the ID number of task notification, I will implement it a bit later.

The rest of your patch looks ok, feel free to push a PR for it, thanks!

@drbourbon
Copy link
Contributor

This is quite invasive for existing PRs (or wanna be PRs) that implement new Activities the old way, expecially for developers not familiar with the Android framework. What about a migration guide?

(no access to Claude here, but an LLM generated migration guide would probably be enough)

@znelson
Copy link
Contributor

znelson commented Feb 27, 2026

@drbourbon That's a good suggestion. I'll ask Claude to put something together, and tag you in a PR for feedback.

ngxson pushed a commit that referenced this pull request Feb 27, 2026
## 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**_
ngxson pushed a commit that referenced this pull request Feb 28, 2026
## Summary

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

Added overview and migration guide for ActivityManager changes in #1016.
Thanks for the suggestion, @drbourbon!

---

### 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, fully written by
Claude 4.6**_
anthonydgallo pushed a commit to anthonydgallo/crosspoint-reader that referenced this pull request Feb 28, 2026
Synced with upstream daveallie/crosspoint-reader, integrating the major
ActivityManager refactor (PR crosspoint-reader#1016) and 30+ other commits.

Upstream features integrated:
- ActivityManager: stack-based activity lifecycle with startActivityForResult()
- Auto page turn for epub/txt readers
- File deletion via ConfirmationActivity in MyLibrary
- Thread-safe HalStorage (mutex-wrapped file operations)
- Footnotes support in epub reader
- QR code display for book sharing
- Status bar settings activity
- New translations: Dutch, Polish, Danish, Finnish, Belarusian, Italian
- Kerning and ligature support in fonts
- Improved hyphenation

Fork features preserved:
- App Store and Apps Menu with 20 preloaded SD card apps
- All app activities: Calculator, Minesweeper, Flashcard, Image Viewer,
  Text Editor, Text Viewer, Random Quote, Art Gallery, Rosary
- Enhanced file management (folder delete, rename, move)
- hideOpdsBrowser and keyboardStyle settings
- OTA updates pointing to anthonydgallo/crosspoint-reader
- FILE_BROWSER theme auto-navigation to MyLibrary

Fork adaptations for new architecture:
- Added goToAppsMenu(), goToAppStore(), goToOpenApp() to ActivityManager
- Migrated AppStoreActivity from ActivityWithSubactivity to Activity base
- Migrated TextEditorAppActivity from ActivityWithSubactivity to Activity base
- Rewrote RosaryActivity to use ActivityManager rendering (removed FreeRTOS task)
- Updated all app activities to use finish() instead of onGoHome callback
- Updated AppsMenuActivity to use activityManager.goToOpenApp()
- Removed all ActivityWithSubactivity references

https://claude.ai/code/session_018sxVXVzezGUiWJzrEkcT5J
lukestein pushed a commit to lukestein/crosspoint-reader that referenced this pull request Feb 28, 2026
## Summary

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

Added overview and migration guide for ActivityManager changes in crosspoint-reader#1016.
Thanks for the suggestion, @drbourbon!

---

### 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, fully written by
Claude 4.6**_
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants