Refactor ActivityManager calls
This commit is contained in:
@@ -12,8 +12,6 @@ void Activity::requestUpdateAndWait() { activityManager.requestUpdateAndWait();
|
||||
|
||||
void Activity::onGoHome() { activityManager.goHome(); }
|
||||
|
||||
void Activity::onSelectBook(const std::string& path) { activityManager.pushReader(path); }
|
||||
|
||||
void Activity::startActivityForResult(std::unique_ptr<Activity>&& activity, ActivityResultHandler resultHandler) {
|
||||
this->resultHandler = std::move(resultHandler);
|
||||
activityManager.pushActivity(std::move(activity));
|
||||
|
||||
@@ -58,5 +58,4 @@ class Activity {
|
||||
// Convenience method to facilitate API transition to ActivityManager
|
||||
// TODO: remove this in near future
|
||||
void onGoHome();
|
||||
void onSelectBook(const std::string& path);
|
||||
};
|
||||
|
||||
@@ -35,12 +35,8 @@ void ActivityManager::begin() {
|
||||
static void logActivityStackState(const char* stage, Activity* currentActivity, size_t stackSize) {
|
||||
const uint32_t freeHeap = esp_get_free_heap_size();
|
||||
const uint32_t contigHeap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_DEFAULT);
|
||||
LOG_DBG("ACT", "%s: current=%s stackSize=%zu free=%lu contig=%lu",
|
||||
stage,
|
||||
currentActivity ? currentActivity->getName().c_str() : "<none>",
|
||||
stackSize,
|
||||
freeHeap,
|
||||
contigHeap);
|
||||
LOG_DBG("ACT", "%s: current=%s stackSize=%zu free=%lu contig=%lu", stage,
|
||||
currentActivity ? currentActivity->getName().c_str() : "<none>", stackSize, freeHeap, contigHeap);
|
||||
}
|
||||
|
||||
void ActivityManager::renderTaskTrampoline(void* param) {
|
||||
@@ -125,10 +121,10 @@ void ActivityManager::loop() {
|
||||
pendingAction = PendingAction::None;
|
||||
|
||||
if (stackActivities.empty()) {
|
||||
LOG_DBG("ACT", "No more activities on stack, going home");
|
||||
lock.unlock(); // goHome may acquire its own lock
|
||||
goHome();
|
||||
continue; // Will launch goHome immediately
|
||||
LOG_DBG("ACT", "No more activities on stack, returning from child");
|
||||
lock.unlock(); // returnFromChild may acquire its own lock via replaceActivity
|
||||
returnFromChild();
|
||||
continue; // Will launch the target activity immediately
|
||||
|
||||
} else {
|
||||
currentActivity = std::move(stackActivities.back());
|
||||
@@ -219,8 +215,8 @@ void ActivityManager::replaceActivity(std::unique_ptr<Activity>&& newActivity) {
|
||||
if (currentActivity) {
|
||||
// Defer launch if we're currently in an activity, to avoid deleting the current activity
|
||||
// leading to the "delete this" problem
|
||||
LOG_DBG("ACT", "replaceActivity requested: current=%s stackSize=%zu",
|
||||
currentActivity->getName().c_str(), stackActivities.size());
|
||||
LOG_DBG("ACT", "replaceActivity requested: current=%s stackSize=%zu", currentActivity->getName().c_str(),
|
||||
stackActivities.size());
|
||||
pendingActivity = std::move(newActivity);
|
||||
pendingAction = PendingAction::Replace;
|
||||
} else {
|
||||
@@ -236,12 +232,14 @@ void ActivityManager::goToFileTransfer() {
|
||||
|
||||
void ActivityManager::goToSettings() { replaceActivity(std::make_unique<SettingsActivity>(renderer, mappedInput)); }
|
||||
|
||||
void ActivityManager::goToFileBrowser(std::string path) {
|
||||
replaceActivity(std::make_unique<FileBrowserActivity>(renderer, mappedInput, std::move(path)));
|
||||
void ActivityManager::goToFileBrowser(std::string path, std::string focusName) {
|
||||
hasReturnHint = false;
|
||||
replaceActivity(std::make_unique<FileBrowserActivity>(renderer, mappedInput, std::move(path), std::move(focusName)));
|
||||
}
|
||||
|
||||
void ActivityManager::goToRecentBooks() {
|
||||
replaceActivity(std::make_unique<RecentBooksActivity>(renderer, mappedInput));
|
||||
void ActivityManager::goToRecentBooks(int focusIndex) {
|
||||
hasReturnHint = false;
|
||||
replaceActivity(std::make_unique<RecentBooksActivity>(renderer, mappedInput, focusIndex));
|
||||
}
|
||||
|
||||
void ActivityManager::goToGlobalBookmarks() {
|
||||
@@ -269,8 +267,45 @@ void ActivityManager::goToKOReaderSync() {
|
||||
sync.hasParagraphIndex, sync.intent));
|
||||
}
|
||||
|
||||
void ActivityManager::pushReader(std::string path) {
|
||||
pushActivity(std::make_unique<ReaderActivity>(renderer, mappedInput, std::move(path)));
|
||||
void ActivityManager::replaceWithReader(std::string path, ReturnHint hint) {
|
||||
returnHint = std::move(hint);
|
||||
hasReturnHint = true;
|
||||
replaceActivity(std::make_unique<ReaderActivity>(renderer, mappedInput, std::move(path)));
|
||||
}
|
||||
|
||||
void ActivityManager::replaceWithFileBrowser(std::string path, ReturnHint hint, std::string focusName) {
|
||||
returnHint = std::move(hint);
|
||||
hasReturnHint = true;
|
||||
replaceActivity(std::make_unique<FileBrowserActivity>(renderer, mappedInput, std::move(path), std::move(focusName)));
|
||||
}
|
||||
|
||||
void ActivityManager::replaceWithRecentBooks(ReturnHint hint) {
|
||||
returnHint = std::move(hint);
|
||||
hasReturnHint = true;
|
||||
replaceActivity(std::make_unique<RecentBooksActivity>(renderer, mappedInput, -1));
|
||||
}
|
||||
|
||||
void ActivityManager::returnFromChild() {
|
||||
if (!hasReturnHint) {
|
||||
goHome();
|
||||
return;
|
||||
}
|
||||
ReturnHint hint = std::move(returnHint);
|
||||
returnHint = {};
|
||||
hasReturnHint = false;
|
||||
|
||||
switch (hint.target) {
|
||||
case ReturnTo::FileBrowser:
|
||||
goToFileBrowser(std::move(hint.path), std::move(hint.selectName));
|
||||
break;
|
||||
case ReturnTo::RecentBooks:
|
||||
goToRecentBooks(hint.selectIndex);
|
||||
break;
|
||||
case ReturnTo::Home:
|
||||
default:
|
||||
goHome(std::move(hint.selectName));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityManager::goToSleep() {
|
||||
@@ -286,7 +321,10 @@ void ActivityManager::goToFullScreenMessage(std::string message, EpdFontFamily::
|
||||
|
||||
void ActivityManager::goToWeather() { replaceActivity(std::make_unique<WeatherActivity>(renderer, mappedInput)); }
|
||||
|
||||
void ActivityManager::goHome() { replaceActivity(std::make_unique<HomeActivity>(renderer, mappedInput)); }
|
||||
void ActivityManager::goHome(std::string focusBookPath) {
|
||||
hasReturnHint = false;
|
||||
replaceActivity(std::make_unique<HomeActivity>(renderer, mappedInput, std::move(focusBookPath)));
|
||||
}
|
||||
|
||||
void ActivityManager::pushActivity(std::unique_ptr<Activity>&& activity) {
|
||||
if (pendingActivity) {
|
||||
|
||||
@@ -15,6 +15,21 @@
|
||||
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 };
|
||||
|
||||
// Minimal state the returning parent needs to restore its previous view (directory,
|
||||
// focused item, list index). Kept as a plain struct stored by value on the
|
||||
// ActivityManager — single instance, overwritten per transition, no heap churn
|
||||
// beyond the two small std::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
|
||||
};
|
||||
|
||||
/**
|
||||
* ActivityManager
|
||||
*
|
||||
@@ -68,6 +83,12 @@ class ActivityManager {
|
||||
// into the next one.
|
||||
bool drainInput = false;
|
||||
|
||||
// Where returnFromChild() should route to. Set by replaceWith*() helpers, cleared
|
||||
// in returnFromChild(). Cleared on any manual goHome()/goTo*() to avoid stale hints
|
||||
// outliving the flow they were recorded for.
|
||||
ReturnHint returnHint;
|
||||
bool hasReturnHint = false;
|
||||
|
||||
public:
|
||||
explicit ActivityManager(GfxRenderer& renderer, MappedInputManager& mappedInput)
|
||||
: renderer(renderer), mappedInput(mappedInput), renderingMutex(xSemaphoreCreateMutex()) {
|
||||
@@ -85,18 +106,29 @@ class ActivityManager {
|
||||
// goTo... functions are convenient wrapper for replaceActivity()
|
||||
void goToFileTransfer();
|
||||
void goToSettings();
|
||||
void goToFileBrowser(std::string path = {});
|
||||
void goToRecentBooks();
|
||||
void goToFileBrowser(std::string path = {}, std::string focusName = {});
|
||||
void goToRecentBooks(int focusIndex = -1);
|
||||
void goToGlobalBookmarks();
|
||||
void goToBrowser();
|
||||
void goToReader(std::string path);
|
||||
void goToKOReaderSync();
|
||||
void pushReader(std::string path);
|
||||
void goToSleep();
|
||||
void goToBoot();
|
||||
void goToFullScreenMessage(std::string message, EpdFontFamily::Style style = EpdFontFamily::REGULAR);
|
||||
void goToWeather();
|
||||
void goHome();
|
||||
void goHome(std::string focusBookPath = {});
|
||||
|
||||
// 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();
|
||||
|
||||
// This will move current activity to stack instead of deleting it
|
||||
void pushActivity(std::unique_ptr<Activity>&& activity);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "../ActivityManager.h"
|
||||
#include "../util/ConfirmationActivity.h"
|
||||
#include "BookInfoActivity.h"
|
||||
#include "CrossPointSettings.h"
|
||||
@@ -113,6 +114,14 @@ void FileBrowserActivity::onEnter() {
|
||||
loadFiles();
|
||||
selectorIndex = 0;
|
||||
|
||||
if (!focusName.empty()) {
|
||||
const size_t idx = findEntry(focusName);
|
||||
if (idx < files.size()) {
|
||||
selectorIndex = idx;
|
||||
}
|
||||
focusName.clear();
|
||||
}
|
||||
|
||||
requestUpdate();
|
||||
}
|
||||
|
||||
@@ -171,7 +180,12 @@ void FileBrowserActivity::loop() {
|
||||
} else {
|
||||
std::string fullPath = basepath;
|
||||
if (fullPath.back() != '/') fullPath += "/";
|
||||
onSelectBook(fullPath + entry);
|
||||
fullPath += entry;
|
||||
ReturnHint hint;
|
||||
hint.target = ReturnTo::FileBrowser;
|
||||
hint.path = basepath;
|
||||
hint.selectName = entry;
|
||||
activityManager.replaceWithReader(std::move(fullPath), std::move(hint));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ class FileBrowserActivity final : public Activity {
|
||||
|
||||
// Files state
|
||||
std::string basepath = "/";
|
||||
std::string focusName; // entry to select on first load (e.g. the file just returned from)
|
||||
std::vector<std::string> files;
|
||||
|
||||
// Data loading
|
||||
@@ -26,8 +27,11 @@ class FileBrowserActivity final : public Activity {
|
||||
size_t findEntry(const std::string& name) const;
|
||||
|
||||
public:
|
||||
explicit FileBrowserActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string initialPath = "/")
|
||||
: Activity("FileBrowser", renderer, mappedInput), basepath(initialPath.empty() ? "/" : std::move(initialPath)) {}
|
||||
explicit FileBrowserActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string initialPath = "/",
|
||||
std::string focusName = {})
|
||||
: Activity("FileBrowser", renderer, mappedInput),
|
||||
basepath(initialPath.empty() ? "/" : std::move(initialPath)),
|
||||
focusName(std::move(focusName)) {}
|
||||
void onEnter() override;
|
||||
void onExit() override;
|
||||
void loop() override;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
|
||||
#include "../ActivityManager.h"
|
||||
#include "BookmarkStore.h"
|
||||
#include "CrossPointState.h"
|
||||
#include "GlobalBookmarkIndex.h"
|
||||
@@ -124,7 +125,10 @@ void GlobalBookmarksActivity::openSelected() {
|
||||
APP_STATE.saveToFile();
|
||||
|
||||
LOG_DBG("GBA", "Jumping to bookmark in %s at %u/%u", entry.sourcePath.c_str(), bm.spineIndex, bm.pageNumber);
|
||||
onSelectBook(entry.sourcePath);
|
||||
ReturnHint hint;
|
||||
hint.target = ReturnTo::Home;
|
||||
hint.selectName = entry.sourcePath;
|
||||
activityManager.replaceWithReader(entry.sourcePath, std::move(hint));
|
||||
}
|
||||
|
||||
template <typename Op>
|
||||
|
||||
@@ -211,6 +211,16 @@ void HomeActivity::onEnter() {
|
||||
recentsLoaded = true;
|
||||
}
|
||||
|
||||
if (!focusBookPath.empty()) {
|
||||
for (size_t i = 0; i < recentBooks.size(); ++i) {
|
||||
if (recentBooks[i].path == focusBookPath) {
|
||||
selectorIndex = static_cast<int>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
focusBookPath.clear();
|
||||
}
|
||||
|
||||
// Trigger first update
|
||||
menuEntriesDirty = true;
|
||||
requestUpdate();
|
||||
@@ -348,7 +358,12 @@ void HomeActivity::render(RenderLock&&) {
|
||||
}
|
||||
}
|
||||
|
||||
void HomeActivity::onSelectBook(const std::string& path) { activityManager.pushReader(path); }
|
||||
void HomeActivity::onSelectBook(const std::string& path) {
|
||||
ReturnHint hint;
|
||||
hint.target = ReturnTo::Home;
|
||||
hint.selectName = path; // used to re-focus the book in the recents strip after return
|
||||
activityManager.replaceWithReader(path, std::move(hint));
|
||||
}
|
||||
|
||||
void HomeActivity::dispatchMenuAction(MenuAction action) {
|
||||
switch (action) {
|
||||
|
||||
@@ -44,6 +44,8 @@ class HomeActivity final : public Activity {
|
||||
std::vector<MenuEntry> menuEntries;
|
||||
bool menuEntriesDirty = true;
|
||||
|
||||
std::string focusBookPath; // book path to re-select on first render, if present in recents
|
||||
|
||||
void onSelectBook(const std::string& path);
|
||||
void dispatchMenuAction(MenuAction action);
|
||||
|
||||
@@ -55,8 +57,8 @@ class HomeActivity final : public Activity {
|
||||
void loadRecentCovers(int coverHeight);
|
||||
|
||||
public:
|
||||
explicit HomeActivity(GfxRenderer& renderer, MappedInputManager& mappedInput)
|
||||
: Activity("Home", renderer, mappedInput) {}
|
||||
explicit HomeActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string focusBookPath = {})
|
||||
: Activity("Home", renderer, mappedInput), focusBookPath(std::move(focusBookPath)) {}
|
||||
void onEnter() override;
|
||||
void onExit() override;
|
||||
void loop() override;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "../ActivityManager.h"
|
||||
#include "../util/ConfirmationActivity.h"
|
||||
#include "BookInfoActivity.h"
|
||||
#include "MappedInputManager.h"
|
||||
@@ -35,6 +36,10 @@ void RecentBooksActivity::onEnter() {
|
||||
loadRecentBooks();
|
||||
|
||||
selectorIndex = 0;
|
||||
if (initialFocusIndex >= 0 && static_cast<size_t>(initialFocusIndex) < recentBooks.size()) {
|
||||
selectorIndex = static_cast<size_t>(initialFocusIndex);
|
||||
}
|
||||
initialFocusIndex = -1;
|
||||
requestUpdate();
|
||||
}
|
||||
|
||||
@@ -49,7 +54,10 @@ void RecentBooksActivity::loop() {
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm) && !recentBooks.empty() &&
|
||||
selectorIndex < static_cast<int>(recentBooks.size())) {
|
||||
LOG_DBG("RBA", "Selected recent book: %s", recentBooks[selectorIndex].path.c_str());
|
||||
onSelectBook(recentBooks[selectorIndex].path);
|
||||
ReturnHint hint;
|
||||
hint.target = ReturnTo::RecentBooks;
|
||||
hint.selectIndex = static_cast<int>(selectorIndex);
|
||||
activityManager.replaceWithReader(recentBooks[selectorIndex].path, std::move(hint));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ class RecentBooksActivity final : public Activity {
|
||||
ButtonNavigator buttonNavigator;
|
||||
|
||||
size_t selectorIndex = 0;
|
||||
int initialFocusIndex = -1; // applied once in onEnter(), then cleared
|
||||
|
||||
// Recent tab state
|
||||
std::vector<RecentBook> recentBooks;
|
||||
@@ -22,8 +23,8 @@ class RecentBooksActivity final : public Activity {
|
||||
void loadRecentBooks();
|
||||
|
||||
public:
|
||||
explicit RecentBooksActivity(GfxRenderer& renderer, MappedInputManager& mappedInput)
|
||||
: Activity("RecentBooks", renderer, mappedInput) {}
|
||||
explicit RecentBooksActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, int focusIndex = -1)
|
||||
: Activity("RecentBooks", renderer, mappedInput), initialFocusIndex(focusIndex) {}
|
||||
void onEnter() override;
|
||||
void onExit() override;
|
||||
void loop() override;
|
||||
|
||||
@@ -102,28 +102,24 @@ void ReaderActivity::goToLibrary(const std::string& fromBookPath) {
|
||||
void ReaderActivity::onGoToEpubReader(std::unique_ptr<Epub> epub) {
|
||||
const auto epubPath = epub->getPath();
|
||||
currentBookPath = epubPath;
|
||||
logReaderLaunchMemSnapshot("before_push_epub_reader");
|
||||
startActivityForResult(std::make_unique<EpubReaderActivity>(renderer, mappedInput, std::move(epub)),
|
||||
[this](const ActivityResult&) { finish(); });
|
||||
logReaderLaunchMemSnapshot("before_replace_epub_reader");
|
||||
activityManager.replaceActivity(std::make_unique<EpubReaderActivity>(renderer, mappedInput, std::move(epub)));
|
||||
}
|
||||
|
||||
void ReaderActivity::onGoToBmpViewer(const std::string& path) {
|
||||
startActivityForResult(std::make_unique<BmpViewerActivity>(renderer, mappedInput, path),
|
||||
[this](const ActivityResult&) { finish(); });
|
||||
activityManager.replaceActivity(std::make_unique<BmpViewerActivity>(renderer, mappedInput, path));
|
||||
}
|
||||
|
||||
void ReaderActivity::onGoToXtcReader(std::unique_ptr<Xtc> xtc) {
|
||||
const auto xtcPath = xtc->getPath();
|
||||
currentBookPath = xtcPath;
|
||||
startActivityForResult(std::make_unique<XtcReaderActivity>(renderer, mappedInput, std::move(xtc)),
|
||||
[this](const ActivityResult&) { finish(); });
|
||||
activityManager.replaceActivity(std::make_unique<XtcReaderActivity>(renderer, mappedInput, std::move(xtc)));
|
||||
}
|
||||
|
||||
void ReaderActivity::onGoToTxtReader(std::unique_ptr<Txt> txt) {
|
||||
const auto txtPath = txt->getPath();
|
||||
currentBookPath = txtPath;
|
||||
startActivityForResult(std::make_unique<TxtReaderActivity>(renderer, mappedInput, std::move(txt)),
|
||||
[this](const ActivityResult&) { finish(); });
|
||||
activityManager.replaceActivity(std::make_unique<TxtReaderActivity>(renderer, mappedInput, std::move(txt)));
|
||||
}
|
||||
|
||||
void ReaderActivity::onEnter() {
|
||||
|
||||
Reference in New Issue
Block a user