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
121 lines
4.4 KiB
C++
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
|