Files
crosspoint-reader-mod/src/activities/ActivityManager.h
cottongin 9464df1727 mod: restore missing mod features from resync audit
Re-add KOReaderSyncActivity PUSH_ONLY mode (PR #1090):
- SyncMode enum with INTERACTIVE/PUSH_ONLY, deferFinish pattern
- Push & Sleep menu action in EpubReaderMenuActivity
- ActivityManager::requestSleep() for activity-initiated sleep
- main.cpp checks isSleepRequested() each loop iteration

Wire EndOfBookMenuActivity into EpubReaderActivity:
- pendingEndOfBookMenu deferred flag avoids render-lock deadlock
- Handles all 6 actions: ARCHIVE, DELETE, TABLE_OF_CONTENTS,
  BACK_TO_BEGINNING, CLOSE_BOOK, CLOSE_MENU

Add book management to reader menu:
- ARCHIVE_BOOK, DELETE_BOOK, REINDEX_BOOK actions with handlers

Port silent next-chapter pre-indexing:
- silentIndexNextChapterIfNeeded() proactively indexes next chapter
  when user is near end of current one, eliminating load screens

Add per-book letterbox fill toggle in reader menu:
- LETTERBOX_FILL cycles Default/Dithered/Solid/None
- Loads/saves per-book override via BookSettings
- bookCachePath constructor param added to EpubReaderMenuActivity

Made-with: Cursor
2026-03-07 16:53:17 -05:00

121 lines
4.4 KiB
C++

#pragma once
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#include <cassert>
#include <memory>
#include <string>
#include <vector>
#include "GfxRenderer.h"
#include "MappedInputManager.h"
#include "OpdsServerStore.h"
class Activity; // forward declaration
class RenderLock; // forward declaration
/**
* ActivityManager
*
* This 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.
*
* It also provides a stack mechanism to allow activities to launch sub-activities and get back the results when the
* sub-activity is done. For example, the WebServer activity can launch a WifiSelect activity to let the user choose a
* wifi network, and get back the selected network when the user is done.
*
* Main differences from Android's ActivityManager:
* - 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
*/
class ActivityManager {
friend class RenderLock;
protected:
GfxRenderer& renderer;
MappedInputManager& mappedInput;
std::vector<std::unique_ptr<Activity>> stackActivities;
std::unique_ptr<Activity> currentActivity;
void exitActivity(const RenderLock& lock);
// Pending activity to be launched on next loop iteration
std::unique_ptr<Activity> pendingActivity;
enum class PendingAction { None, Push, Pop, Replace };
PendingAction pendingAction = PendingAction::None;
// Task to render and display the activity
TaskHandle_t renderTaskHandle = nullptr;
static void renderTaskTrampoline(void* param);
[[noreturn]] virtual void renderTaskLoop();
// Set by requestUpdateAndWait(); read and cleared by the render task after render completes.
// Note: only one waiting task is supported at a time
TaskHandle_t waitingTaskHandle = nullptr;
// Mutex to protect rendering operations from race conditions
// Must only be used via RenderLock
SemaphoreHandle_t renderingMutex = nullptr;
// Whether to trigger a render after the current loop()
// This variable must only be set by the main loop, to avoid race conditions
bool requestedUpdate = false;
bool sleepRequested = false;
public:
explicit ActivityManager(GfxRenderer& renderer, MappedInputManager& mappedInput)
: renderer(renderer), mappedInput(mappedInput), renderingMutex(xSemaphoreCreateMutex()) {
assert(renderingMutex != nullptr && "Failed to create rendering mutex");
stackActivities.reserve(10);
}
~ActivityManager() { assert(false); /* should never be called */ };
void begin();
void loop();
// Will replace currentActivity and drop all activities on stack
void replaceActivity(std::unique_ptr<Activity>&& newActivity);
// goTo... functions are convenient wrapper for replaceActivity()
void goToFileTransfer();
void goToSettings();
void goToFileBrowser(std::string path = {});
void goToRecentBooks();
void goToBrowser();
void goToBrowser(const struct OpdsServer& server);
void goToReader(std::string path);
void goToSleep();
void goToBoot();
void goToFullScreenMessage(std::string message, EpdFontFamily::Style style = EpdFontFamily::REGULAR);
void goHome();
// This will move current activity to stack instead of deleting it
void pushActivity(std::unique_ptr<Activity>&& activity);
// Remove the currentActivity, returning the last one on stack
// Note: if popActivity() on last activity on the stack, we will goHome()
void popActivity();
bool preventAutoSleep() const;
bool isReaderActivity() const;
bool skipLoopDelay() const;
// Activities can request sleep (e.g. PUSH_AND_SLEEP). The main loop checks
// this flag after each loop() call and triggers enterDeepSleep() if set.
void requestSleep() { sleepRequested = true; }
bool isSleepRequested() const { return sleepRequested; }
// If immediate is true, the update will be triggered immediately.
// Otherwise, it will be deferred until the end of the current loop iteration.
void requestUpdate(bool immediate = false);
// Trigger a render and block until it completes.
// Must NOT be called from the render task or while holding a RenderLock.
void requestUpdateAndWait();
};
extern ActivityManager activityManager; // singleton, to be defined in main.cpp