Files
Crosspoint/src/activities/ActivityManager.h
T

184 lines
7.6 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 "ButtonEventManager.h"
#include "CrossPointSettings.h"
#include "GfxRenderer.h"
#include "MappedInputManager.h"
class Activity; // forward declaration
class RenderLock; // forward declaration
// Where a "child" activity (launched via one of the replaceWith* helpers) should route
// control when it exits successfully. See ActivityManager::returnFromChild().
enum class ReturnTo : uint8_t { Home, FileBrowser, RecentBooks, GlobalBookmarks };
// Minimal state the returning parent needs to restore its previous view (directory,
// focused item, list index, or bookmark selection). Kept as a plain struct stored by
// value on the ActivityManager — single instance, overwritten per transition, no heap
// churn beyond the small strings.
struct ReturnHint {
ReturnTo target = ReturnTo::Home;
std::string path; // FileBrowser directory to restore
std::string selectName; // item to re-focus in a list (file name, book title)
int selectIndex = -1; // e.g. Recents index
std::string selectionContext; // optional activity-specific restore key
int selectBookmarkIndex = -1; // optional bookmark index for GlobalBookmarks
};
/**
* 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;
ButtonEventManager& buttonEvents;
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
volatile bool requestedUpdate = false;
// When true, input events are consumed (discarded) until all buttons are released
// and no press/release events remain. Armed automatically on activity transitions
// (push / pop / replace) so that the button used to leave one activity cannot bleed
// into the next one.
bool drainInput = false;
// Where returnFromChild() should route to. Set by replaceWith*() helpers and
// preserved across plain goTo*() chains so a chained navigation flow can still
// restore its original parent state. Cleared only by returnFromChild() or by
// explicit goHome()/replaceWith*() calls, not by ordinary goTo*() transitions.
// Relevant symbols: ReturnHint, returnHint, hasReturnHint, returnFromChild(),
// goHome(), goTo*(), replaceWith*().
ReturnHint returnHint;
bool hasReturnHint = false;
public:
explicit ActivityManager(GfxRenderer& renderer, MappedInputManager& mappedInput)
: renderer(renderer),
mappedInput(mappedInput),
buttonEvents(globalButtonEvents()),
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 = {}, std::string focusName = {});
void goToRecentBooks(int focusIndex = -1);
void goToGlobalBookmarks();
void goToGlobalBookmarks(ReturnHint hint);
void goToBrowser();
void goToReader(std::string path);
void goToKOReaderSync();
void goToSleep();
void goToBoot();
void goToFullScreenMessage(std::string message, EpdFontFamily::Style style = EpdFontFamily::REGULAR);
void goToWeather();
void goHome(std::string focusBookPath = {}, int focusSelectorIndex = -1);
// Replace-with-hint helpers: destroy the current activity before launching the new
// one (freeing its memory) and record where to route control when the new activity
// exits. Consumed by returnFromChild().
void replaceWithReader(std::string path, ReturnHint hint);
void replaceWithFileBrowser(std::string path, ReturnHint hint, std::string focusName = {});
void replaceWithRecentBooks(ReturnHint hint);
// Called by a "child" activity on successful exit. Consults the stored ReturnHint,
// clears it, and dispatches to the corresponding parent with restoration args. If
// no hint is set, defaults to goHome().
void returnFromChild();
// Record a ReturnHint before calling any plain goTo*() helper. Allows an activity
// (e.g. Home) to declare "when this flow ends, come back here with this state" for
// transitions where we don't want a dedicated replaceWith*() wrapper.
// Cleared by returnFromChild() or by an explicit goHome()/replaceWith*() call.
void setReturnHint(ReturnHint hint) {
returnHint = std::move(hint);
hasReturnHint = true;
}
void clearReturnHint() {
returnHint = {};
hasReturnHint = false;
}
// 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;
// Dispatch a globally-configured button action to the current activity.
// Reader-specific actions (page navigation, TOC, bookmarks, footnotes) are forwarded
// only when the current activity is a reader; others are no-ops in other contexts.
void dispatchButtonAction(CrossPointSettings::BUTTON_ACTION action);
// 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