Give activities name and log when entering and exiting them

This commit is contained in:
Dave Allie 2025-12-21 21:02:36 +11:00
parent fcfa10bb1f
commit 33fbcf6a8f
No known key found for this signature in database
GPG Key ID: F2FDDB3AD8D0276F
27 changed files with 123 additions and 104 deletions

View File

@ -1,19 +1,22 @@
#pragma once #pragma once
#include <InputManager.h> #include <InputManager.h>
#include <utility>
class GfxRenderer; class GfxRenderer;
class Activity { class Activity {
protected: protected:
std::string name;
GfxRenderer& renderer; GfxRenderer& renderer;
InputManager& inputManager; InputManager& inputManager;
public: public:
explicit Activity(GfxRenderer& renderer, InputManager& inputManager) explicit Activity(std::string name, GfxRenderer& renderer, InputManager& inputManager)
: renderer(renderer), inputManager(inputManager) {} : name(std::move(name)), renderer(renderer), inputManager(inputManager) {}
virtual ~Activity() = default; virtual ~Activity() = default;
virtual void onEnter() {} virtual void onEnter() { Serial.printf("[%lu] [ACT] Entering activity: %s\n", millis(), name.c_str()); }
virtual void onExit() {} virtual void onExit() { Serial.printf("[%lu] [ACT] Exiting activity: %s\n", millis(), name.c_str()); }
virtual void loop() {} virtual void loop() {}
virtual bool skipLoopDelay() { return false; } virtual bool skipLoopDelay() { return false; }
}; };

View File

@ -18,4 +18,7 @@ void ActivityWithSubactivity::loop() {
} }
} }
void ActivityWithSubactivity::onExit() { exitActivity(); } void ActivityWithSubactivity::onExit() {
Activity::onExit();
exitActivity();
}

View File

@ -10,8 +10,8 @@ class ActivityWithSubactivity : public Activity {
void enterNewActivity(Activity* activity); void enterNewActivity(Activity* activity);
public: public:
explicit ActivityWithSubactivity(GfxRenderer& renderer, InputManager& inputManager) explicit ActivityWithSubactivity(std::string name, GfxRenderer& renderer, InputManager& inputManager)
: Activity(renderer, inputManager) {} : Activity(std::move(name), renderer, inputManager) {}
void loop() override; void loop() override;
void onExit() override; void onExit() override;
}; };

View File

@ -6,6 +6,8 @@
#include "images/CrossLarge.h" #include "images/CrossLarge.h"
void BootActivity::onEnter() { void BootActivity::onEnter() {
Activity::onEnter();
const auto pageWidth = GfxRenderer::getScreenWidth(); const auto pageWidth = GfxRenderer::getScreenWidth();
const auto pageHeight = GfxRenderer::getScreenHeight(); const auto pageHeight = GfxRenderer::getScreenHeight();

View File

@ -3,6 +3,6 @@
class BootActivity final : public Activity { class BootActivity final : public Activity {
public: public:
explicit BootActivity(GfxRenderer& renderer, InputManager& inputManager) : Activity(renderer, inputManager) {} explicit BootActivity(GfxRenderer& renderer, InputManager& inputManager) : Activity("Boot", renderer, inputManager) {}
void onEnter() override; void onEnter() override;
}; };

View File

@ -12,6 +12,7 @@
#include "images/CrossLarge.h" #include "images/CrossLarge.h"
void SleepActivity::onEnter() { void SleepActivity::onEnter() {
Activity::onEnter();
renderPopup("Entering Sleep..."); renderPopup("Entering Sleep...");
if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::CUSTOM) { if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::CUSTOM) {
@ -170,11 +171,16 @@ void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap) const {
} }
void SleepActivity::renderCoverSleepScreen() const { void SleepActivity::renderCoverSleepScreen() const {
if (APP_STATE.openEpubPath.empty()) {
return renderDefaultSleepScreen();
}
Epub lastEpub(APP_STATE.openEpubPath, "/.crosspoint"); Epub lastEpub(APP_STATE.openEpubPath, "/.crosspoint");
if (!lastEpub.load()) { if (!lastEpub.load()) {
Serial.println("[SLP] Failed to load last epub"); Serial.println("[SLP] Failed to load last epub");
return renderDefaultSleepScreen(); return renderDefaultSleepScreen();
} }
if (!lastEpub.generateCoverBmp()) { if (!lastEpub.generateCoverBmp()) {
Serial.println("[SLP] Failed to generate cover bmp"); Serial.println("[SLP] Failed to generate cover bmp");
return renderDefaultSleepScreen(); return renderDefaultSleepScreen();

View File

@ -5,7 +5,8 @@ class Bitmap;
class SleepActivity final : public Activity { class SleepActivity final : public Activity {
public: public:
explicit SleepActivity(GfxRenderer& renderer, InputManager& inputManager) : Activity(renderer, inputManager) {} explicit SleepActivity(GfxRenderer& renderer, InputManager& inputManager)
: Activity("Sleep", renderer, inputManager) {}
void onEnter() override; void onEnter() override;
private: private:

View File

@ -15,6 +15,8 @@ void HomeActivity::taskTrampoline(void* param) {
} }
void HomeActivity::onEnter() { void HomeActivity::onEnter() {
Activity::onEnter();
renderingMutex = xSemaphoreCreateMutex(); renderingMutex = xSemaphoreCreateMutex();
selectorIndex = 0; selectorIndex = 0;
@ -31,6 +33,8 @@ void HomeActivity::onEnter() {
} }
void HomeActivity::onExit() { void HomeActivity::onExit() {
Activity::onExit();
// Wait until not rendering to delete task to avoid killing mid-instruction to EPD // Wait until not rendering to delete task to avoid killing mid-instruction to EPD
xSemaphoreTake(renderingMutex, portMAX_DELAY); xSemaphoreTake(renderingMutex, portMAX_DELAY);
if (displayTaskHandle) { if (displayTaskHandle) {

View File

@ -23,7 +23,7 @@ class HomeActivity final : public Activity {
public: public:
explicit HomeActivity(GfxRenderer& renderer, InputManager& inputManager, const std::function<void()>& onReaderOpen, explicit HomeActivity(GfxRenderer& renderer, InputManager& inputManager, const std::function<void()>& onReaderOpen,
const std::function<void()>& onSettingsOpen, const std::function<void()>& onFileTransferOpen) const std::function<void()>& onSettingsOpen, const std::function<void()>& onFileTransferOpen)
: Activity(renderer, inputManager), : Activity("Home", renderer, inputManager),
onReaderOpen(onReaderOpen), onReaderOpen(onReaderOpen),
onSettingsOpen(onSettingsOpen), onSettingsOpen(onSettingsOpen),
onFileTransferOpen(onFileTransferOpen) {} onFileTransferOpen(onFileTransferOpen) {}

View File

@ -11,7 +11,8 @@ void CrossPointWebServerActivity::taskTrampoline(void* param) {
} }
void CrossPointWebServerActivity::onEnter() { void CrossPointWebServerActivity::onEnter() {
Serial.printf("[%lu] [WEBACT] ========== CrossPointWebServerActivity onEnter ==========\n", millis()); ActivityWithSubactivity::onEnter();
Serial.printf("[%lu] [WEBACT] [MEM] Free heap at onEnter: %d bytes\n", millis(), ESP.getFreeHeap()); Serial.printf("[%lu] [WEBACT] [MEM] Free heap at onEnter: %d bytes\n", millis(), ESP.getFreeHeap());
renderingMutex = xSemaphoreCreateMutex(); renderingMutex = xSemaphoreCreateMutex();
@ -36,13 +37,13 @@ void CrossPointWebServerActivity::onEnter() {
// Launch WiFi selection subactivity // Launch WiFi selection subactivity
Serial.printf("[%lu] [WEBACT] Launching WifiSelectionActivity...\n", millis()); Serial.printf("[%lu] [WEBACT] Launching WifiSelectionActivity...\n", millis());
wifiSelection.reset(new WifiSelectionActivity(renderer, inputManager, enterNewActivity(new WifiSelectionActivity(renderer, inputManager,
[this](bool connected) { onWifiSelectionComplete(connected); })); [this](const bool connected) { onWifiSelectionComplete(connected); }));
wifiSelection->onEnter();
} }
void CrossPointWebServerActivity::onExit() { void CrossPointWebServerActivity::onExit() {
Serial.printf("[%lu] [WEBACT] ========== CrossPointWebServerActivity onExit START ==========\n", millis()); ActivityWithSubactivity::onExit();
Serial.printf("[%lu] [WEBACT] [MEM] Free heap at onExit start: %d bytes\n", millis(), ESP.getFreeHeap()); Serial.printf("[%lu] [WEBACT] [MEM] Free heap at onExit start: %d bytes\n", millis(), ESP.getFreeHeap());
state = WebServerActivityState::SHUTTING_DOWN; state = WebServerActivityState::SHUTTING_DOWN;
@ -50,14 +51,6 @@ void CrossPointWebServerActivity::onExit() {
// Stop the web server first (before disconnecting WiFi) // Stop the web server first (before disconnecting WiFi)
stopWebServer(); stopWebServer();
// Exit WiFi selection subactivity if still active
if (wifiSelection) {
Serial.printf("[%lu] [WEBACT] Exiting WifiSelectionActivity...\n", millis());
wifiSelection->onExit();
wifiSelection.reset();
Serial.printf("[%lu] [WEBACT] WifiSelectionActivity exited\n", millis());
}
// CRITICAL: Wait for LWIP stack to flush any pending packets // CRITICAL: Wait for LWIP stack to flush any pending packets
Serial.printf("[%lu] [WEBACT] Waiting 500ms for network stack to flush pending packets...\n", millis()); Serial.printf("[%lu] [WEBACT] Waiting 500ms for network stack to flush pending packets...\n", millis());
delay(500); delay(500);
@ -92,20 +85,17 @@ void CrossPointWebServerActivity::onExit() {
Serial.printf("[%lu] [WEBACT] Mutex deleted\n", millis()); Serial.printf("[%lu] [WEBACT] Mutex deleted\n", millis());
Serial.printf("[%lu] [WEBACT] [MEM] Free heap at onExit end: %d bytes\n", millis(), ESP.getFreeHeap()); Serial.printf("[%lu] [WEBACT] [MEM] Free heap at onExit end: %d bytes\n", millis(), ESP.getFreeHeap());
Serial.printf("[%lu] [WEBACT] ========== CrossPointWebServerActivity onExit COMPLETE ==========\n", millis());
} }
void CrossPointWebServerActivity::onWifiSelectionComplete(bool connected) { void CrossPointWebServerActivity::onWifiSelectionComplete(const bool connected) {
Serial.printf("[%lu] [WEBACT] WifiSelectionActivity completed, connected=%d\n", millis(), connected); Serial.printf("[%lu] [WEBACT] WifiSelectionActivity completed, connected=%d\n", millis(), connected);
if (connected) { if (connected) {
// Get connection info before exiting subactivity // Get connection info before exiting subactivity
connectedIP = wifiSelection->getConnectedIP(); connectedIP = static_cast<WifiSelectionActivity*>(subActivity.get())->getConnectedIP();
connectedSSID = WiFi.SSID().c_str(); connectedSSID = WiFi.SSID().c_str();
// Exit the wifi selection subactivity exitActivity();
wifiSelection->onExit();
wifiSelection.reset();
// Start the web server // Start the web server
startWebServer(); startWebServer();
@ -150,47 +140,40 @@ void CrossPointWebServerActivity::stopWebServer() {
} }
void CrossPointWebServerActivity::loop() { void CrossPointWebServerActivity::loop() {
if (subActivity) {
// Forward loop to subactivity
subActivity->loop();
return;
}
// Handle different states // Handle different states
switch (state) { if (state == WebServerActivityState::SERVER_RUNNING) {
case WebServerActivityState::WIFI_SELECTION: // Handle web server requests - call handleClient multiple times per loop
// Forward loop to WiFi selection subactivity // to improve responsiveness and upload throughput
if (wifiSelection) { if (webServer && webServer->isRunning()) {
wifiSelection->loop(); const unsigned long timeSinceLastHandleClient = millis() - lastHandleClientTime;
}
break;
case WebServerActivityState::SERVER_RUNNING: // Log if there's a significant gap between handleClient calls (>100ms)
// Handle web server requests - call handleClient multiple times per loop if (lastHandleClientTime > 0 && timeSinceLastHandleClient > 100) {
// to improve responsiveness and upload throughput Serial.printf("[%lu] [WEBACT] WARNING: %lu ms gap since last handleClient\n", millis(),
if (webServer && webServer->isRunning()) { timeSinceLastHandleClient);
unsigned long timeSinceLastHandleClient = millis() - lastHandleClientTime;
// Log if there's a significant gap between handleClient calls (>100ms)
if (lastHandleClientTime > 0 && timeSinceLastHandleClient > 100) {
Serial.printf("[%lu] [WEBACT] WARNING: %lu ms gap since last handleClient\n", millis(),
timeSinceLastHandleClient);
}
// Call handleClient multiple times to process pending requests faster
// This is critical for upload performance - HTTP file uploads send data
// in chunks and each handleClient() call processes incoming data
constexpr int HANDLE_CLIENT_ITERATIONS = 10;
for (int i = 0; i < HANDLE_CLIENT_ITERATIONS && webServer->isRunning(); i++) {
webServer->handleClient();
}
lastHandleClientTime = millis();
} }
// Handle exit on Back button // Call handleClient multiple times to process pending requests faster
if (inputManager.wasPressed(InputManager::BTN_BACK)) { // This is critical for upload performance - HTTP file uploads send data
onGoBack(); // in chunks and each handleClient() call processes incoming data
return; constexpr int HANDLE_CLIENT_ITERATIONS = 10;
for (int i = 0; i < HANDLE_CLIENT_ITERATIONS && webServer->isRunning(); i++) {
webServer->handleClient();
} }
break; lastHandleClientTime = millis();
}
case WebServerActivityState::SHUTTING_DOWN: // Handle exit on Back button
// Do nothing - waiting for cleanup if (inputManager.wasPressed(InputManager::BTN_BACK)) {
break; onGoBack();
return;
}
} }
} }

View File

@ -9,6 +9,7 @@
#include "../Activity.h" #include "../Activity.h"
#include "WifiSelectionActivity.h" #include "WifiSelectionActivity.h"
#include "activities/ActivityWithSubactivity.h"
#include "server/CrossPointWebServer.h" #include "server/CrossPointWebServer.h"
// Web server activity states // Web server activity states
@ -26,16 +27,13 @@ enum class WebServerActivityState {
* - Handles client requests in its loop() function * - Handles client requests in its loop() function
* - Cleans up the server and shuts down WiFi on exit * - Cleans up the server and shuts down WiFi on exit
*/ */
class CrossPointWebServerActivity final : public Activity { class CrossPointWebServerActivity final : public ActivityWithSubactivity {
TaskHandle_t displayTaskHandle = nullptr; TaskHandle_t displayTaskHandle = nullptr;
SemaphoreHandle_t renderingMutex = nullptr; SemaphoreHandle_t renderingMutex = nullptr;
bool updateRequired = false; bool updateRequired = false;
WebServerActivityState state = WebServerActivityState::WIFI_SELECTION; WebServerActivityState state = WebServerActivityState::WIFI_SELECTION;
const std::function<void()> onGoBack; const std::function<void()> onGoBack;
// WiFi selection subactivity
std::unique_ptr<WifiSelectionActivity> wifiSelection;
// Web server - owned by this activity // Web server - owned by this activity
std::unique_ptr<CrossPointWebServer> webServer; std::unique_ptr<CrossPointWebServer> webServer;
@ -58,7 +56,7 @@ class CrossPointWebServerActivity final : public Activity {
public: public:
explicit CrossPointWebServerActivity(GfxRenderer& renderer, InputManager& inputManager, explicit CrossPointWebServerActivity(GfxRenderer& renderer, InputManager& inputManager,
const std::function<void()>& onGoBack) const std::function<void()>& onGoBack)
: Activity(renderer, inputManager), onGoBack(onGoBack) {} : ActivityWithSubactivity("CrossPointWebServer", renderer, inputManager), onGoBack(onGoBack) {}
void onEnter() override; void onEnter() override;
void onExit() override; void onExit() override;
void loop() override; void loop() override;

View File

@ -14,6 +14,8 @@ void WifiSelectionActivity::taskTrampoline(void* param) {
} }
void WifiSelectionActivity::onEnter() { void WifiSelectionActivity::onEnter() {
Activity::onEnter();
renderingMutex = xSemaphoreCreateMutex(); renderingMutex = xSemaphoreCreateMutex();
// Load saved WiFi credentials // Load saved WiFi credentials
@ -47,7 +49,8 @@ void WifiSelectionActivity::onEnter() {
} }
void WifiSelectionActivity::onExit() { void WifiSelectionActivity::onExit() {
Serial.printf("[%lu] [WIFI] ========== WifiSelectionActivity onExit START ==========\n", millis()); Activity::onExit();
Serial.printf("[%lu] [WIFI] [MEM] Free heap at onExit start: %d bytes\n", millis(), ESP.getFreeHeap()); Serial.printf("[%lu] [WIFI] [MEM] Free heap at onExit start: %d bytes\n", millis(), ESP.getFreeHeap());
// Stop any ongoing WiFi scan // Stop any ongoing WiFi scan
@ -78,7 +81,6 @@ void WifiSelectionActivity::onExit() {
Serial.printf("[%lu] [WIFI] Mutex deleted\n", millis()); Serial.printf("[%lu] [WIFI] Mutex deleted\n", millis());
Serial.printf("[%lu] [WIFI] [MEM] Free heap at onExit end: %d bytes\n", millis(), ESP.getFreeHeap()); Serial.printf("[%lu] [WIFI] [MEM] Free heap at onExit end: %d bytes\n", millis(), ESP.getFreeHeap());
Serial.printf("[%lu] [WIFI] ========== WifiSelectionActivity onExit COMPLETE ==========\n", millis());
} }
void WifiSelectionActivity::startWifiScan() { void WifiSelectionActivity::startWifiScan() {

View File

@ -98,7 +98,7 @@ class WifiSelectionActivity final : public Activity {
public: public:
explicit WifiSelectionActivity(GfxRenderer& renderer, InputManager& inputManager, explicit WifiSelectionActivity(GfxRenderer& renderer, InputManager& inputManager,
const std::function<void(bool connected)>& onComplete) const std::function<void(bool connected)>& onComplete)
: Activity(renderer, inputManager), onComplete(onComplete) {} : Activity("WifiSelection", renderer, inputManager), onComplete(onComplete) {}
void onEnter() override; void onEnter() override;
void onExit() override; void onExit() override;
void loop() override; void loop() override;

View File

@ -26,6 +26,8 @@ void EpubReaderActivity::taskTrampoline(void* param) {
} }
void EpubReaderActivity::onEnter() { void EpubReaderActivity::onEnter() {
ActivityWithSubactivity::onEnter();
if (!epub) { if (!epub) {
return; return;
} }
@ -61,6 +63,8 @@ void EpubReaderActivity::onEnter() {
} }
void EpubReaderActivity::onExit() { void EpubReaderActivity::onExit() {
ActivityWithSubactivity::onExit();
// Wait until not rendering to delete task to avoid killing mid-instruction to EPD // Wait until not rendering to delete task to avoid killing mid-instruction to EPD
xSemaphoreTake(renderingMutex, portMAX_DELAY); xSemaphoreTake(renderingMutex, portMAX_DELAY);
if (displayTaskHandle) { if (displayTaskHandle) {
@ -75,8 +79,8 @@ void EpubReaderActivity::onExit() {
void EpubReaderActivity::loop() { void EpubReaderActivity::loop() {
// Pass input responsibility to sub activity if exists // Pass input responsibility to sub activity if exists
if (subAcitivity) { if (subActivity) {
subAcitivity->loop(); subActivity->loop();
return; return;
} }
@ -84,11 +88,11 @@ void EpubReaderActivity::loop() {
if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) { if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) {
// Don't start activity transition while rendering // Don't start activity transition while rendering
xSemaphoreTake(renderingMutex, portMAX_DELAY); xSemaphoreTake(renderingMutex, portMAX_DELAY);
subAcitivity.reset(new EpubReaderChapterSelectionActivity( exitActivity();
enterNewActivity(new EpubReaderChapterSelectionActivity(
this->renderer, this->inputManager, epub, currentSpineIndex, this->renderer, this->inputManager, epub, currentSpineIndex,
[this] { [this] {
subAcitivity->onExit(); exitActivity();
subAcitivity.reset();
updateRequired = true; updateRequired = true;
}, },
[this](const int newSpineIndex) { [this](const int newSpineIndex) {
@ -97,11 +101,9 @@ void EpubReaderActivity::loop() {
nextPageNumber = 0; nextPageNumber = 0;
section.reset(); section.reset();
} }
subAcitivity->onExit(); exitActivity();
subAcitivity.reset();
updateRequired = true; updateRequired = true;
})); }));
subAcitivity->onEnter();
xSemaphoreGive(renderingMutex); xSemaphoreGive(renderingMutex);
} }
@ -330,8 +332,8 @@ void EpubReaderActivity::renderStatusBar() const {
constexpr auto textY = 776; constexpr auto textY = 776;
// Calculate progress in book // Calculate progress in book
float sectionChapterProg = static_cast<float>(section->currentPage) / section->pageCount; const float sectionChapterProg = static_cast<float>(section->currentPage) / section->pageCount;
uint8_t bookProgress = epub->calculateProgress(currentSpineIndex, sectionChapterProg); const uint8_t bookProgress = epub->calculateProgress(currentSpineIndex, sectionChapterProg);
// Right aligned text for progress counter // Right aligned text for progress counter
const std::string progress = std::to_string(section->currentPage + 1) + "/" + std::to_string(section->pageCount) + const std::string progress = std::to_string(section->currentPage + 1) + "/" + std::to_string(section->pageCount) +

View File

@ -5,14 +5,13 @@
#include <freertos/semphr.h> #include <freertos/semphr.h>
#include <freertos/task.h> #include <freertos/task.h>
#include "../Activity.h" #include "activities/ActivityWithSubactivity.h"
class EpubReaderActivity final : public Activity { class EpubReaderActivity final : public ActivityWithSubactivity {
std::shared_ptr<Epub> epub; std::shared_ptr<Epub> epub;
std::unique_ptr<Section> section = nullptr; std::unique_ptr<Section> section = nullptr;
TaskHandle_t displayTaskHandle = nullptr; TaskHandle_t displayTaskHandle = nullptr;
SemaphoreHandle_t renderingMutex = nullptr; SemaphoreHandle_t renderingMutex = nullptr;
std::unique_ptr<Activity> subAcitivity = nullptr;
int currentSpineIndex = 0; int currentSpineIndex = 0;
int nextPageNumber = 0; int nextPageNumber = 0;
int pagesUntilFullRefresh = 0; int pagesUntilFullRefresh = 0;
@ -28,7 +27,7 @@ class EpubReaderActivity final : public Activity {
public: public:
explicit EpubReaderActivity(GfxRenderer& renderer, InputManager& inputManager, std::unique_ptr<Epub> epub, explicit EpubReaderActivity(GfxRenderer& renderer, InputManager& inputManager, std::unique_ptr<Epub> epub,
const std::function<void()>& onGoBack) const std::function<void()>& onGoBack)
: Activity(renderer, inputManager), epub(std::move(epub)), onGoBack(onGoBack) {} : ActivityWithSubactivity("EpubReader", renderer, inputManager), epub(std::move(epub)), onGoBack(onGoBack) {}
void onEnter() override; void onEnter() override;
void onExit() override; void onExit() override;
void loop() override; void loop() override;

View File

@ -16,6 +16,8 @@ void EpubReaderChapterSelectionActivity::taskTrampoline(void* param) {
} }
void EpubReaderChapterSelectionActivity::onEnter() { void EpubReaderChapterSelectionActivity::onEnter() {
Activity::onEnter();
if (!epub) { if (!epub) {
return; return;
} }
@ -34,6 +36,8 @@ void EpubReaderChapterSelectionActivity::onEnter() {
} }
void EpubReaderChapterSelectionActivity::onExit() { void EpubReaderChapterSelectionActivity::onExit() {
Activity::onExit();
// Wait until not rendering to delete task to avoid killing mid-instruction to EPD // Wait until not rendering to delete task to avoid killing mid-instruction to EPD
xSemaphoreTake(renderingMutex, portMAX_DELAY); xSemaphoreTake(renderingMutex, portMAX_DELAY);
if (displayTaskHandle) { if (displayTaskHandle) {

View File

@ -27,7 +27,7 @@ class EpubReaderChapterSelectionActivity final : public Activity {
const std::shared_ptr<Epub>& epub, const int currentSpineIndex, const std::shared_ptr<Epub>& epub, const int currentSpineIndex,
const std::function<void()>& onGoBack, const std::function<void()>& onGoBack,
const std::function<void(int newSpineIndex)>& onSelectSpineIndex) const std::function<void(int newSpineIndex)>& onSelectSpineIndex)
: Activity(renderer, inputManager), : Activity("EpubReaderChapterSelection", renderer, inputManager),
epub(epub), epub(epub),
currentSpineIndex(currentSpineIndex), currentSpineIndex(currentSpineIndex),
onGoBack(onGoBack), onGoBack(onGoBack),

View File

@ -48,6 +48,8 @@ void FileSelectionActivity::loadFiles() {
} }
void FileSelectionActivity::onEnter() { void FileSelectionActivity::onEnter() {
Activity::onEnter();
renderingMutex = xSemaphoreCreateMutex(); renderingMutex = xSemaphoreCreateMutex();
basepath = "/"; basepath = "/";
@ -66,6 +68,8 @@ void FileSelectionActivity::onEnter() {
} }
void FileSelectionActivity::onExit() { void FileSelectionActivity::onExit() {
Activity::onExit();
// Wait until not rendering to delete task to avoid killing mid-instruction to EPD // Wait until not rendering to delete task to avoid killing mid-instruction to EPD
xSemaphoreTake(renderingMutex, portMAX_DELAY); xSemaphoreTake(renderingMutex, portMAX_DELAY);
if (displayTaskHandle) { if (displayTaskHandle) {

View File

@ -28,7 +28,7 @@ class FileSelectionActivity final : public Activity {
explicit FileSelectionActivity(GfxRenderer& renderer, InputManager& inputManager, explicit FileSelectionActivity(GfxRenderer& renderer, InputManager& inputManager,
const std::function<void(const std::string&)>& onSelect, const std::function<void(const std::string&)>& onSelect,
const std::function<void()>& onGoHome) const std::function<void()>& onGoHome)
: Activity(renderer, inputManager), onSelect(onSelect), onGoHome(onGoHome) {} : Activity("FileSelection", renderer, inputManager), onSelect(onSelect), onGoHome(onGoHome) {}
void onEnter() override; void onEnter() override;
void onExit() override; void onExit() override;
void loop() override; void loop() override;

View File

@ -50,6 +50,8 @@ void ReaderActivity::onGoToEpubReader(std::unique_ptr<Epub> epub) {
} }
void ReaderActivity::onEnter() { void ReaderActivity::onEnter() {
ActivityWithSubactivity::onEnter();
if (initialEpubPath.empty()) { if (initialEpubPath.empty()) {
onGoToFileSelection(); onGoToFileSelection();
return; return;

View File

@ -17,7 +17,7 @@ class ReaderActivity final : public ActivityWithSubactivity {
public: public:
explicit ReaderActivity(GfxRenderer& renderer, InputManager& inputManager, std::string initialEpubPath, explicit ReaderActivity(GfxRenderer& renderer, InputManager& inputManager, std::string initialEpubPath,
const std::function<void()>& onGoBack) const std::function<void()>& onGoBack)
: ActivityWithSubactivity(renderer, inputManager), : ActivityWithSubactivity("Reader", renderer, inputManager),
initialEpubPath(std::move(initialEpubPath)), initialEpubPath(std::move(initialEpubPath)),
onGoBack(onGoBack) {} onGoBack(onGoBack) {}
void onEnter() override; void onEnter() override;

View File

@ -21,6 +21,8 @@ void SettingsActivity::taskTrampoline(void* param) {
} }
void SettingsActivity::onEnter() { void SettingsActivity::onEnter() {
Activity::onEnter();
renderingMutex = xSemaphoreCreateMutex(); renderingMutex = xSemaphoreCreateMutex();
// Reset selection to first item // Reset selection to first item
@ -38,6 +40,8 @@ void SettingsActivity::onEnter() {
} }
void SettingsActivity::onExit() { void SettingsActivity::onExit() {
Activity::onExit();
// Wait until not rendering to delete task to avoid killing mid-instruction to EPD // Wait until not rendering to delete task to avoid killing mid-instruction to EPD
xSemaphoreTake(renderingMutex, portMAX_DELAY); xSemaphoreTake(renderingMutex, portMAX_DELAY);
if (displayTaskHandle) { if (displayTaskHandle) {
@ -76,7 +80,7 @@ void SettingsActivity::loop() {
} }
} }
void SettingsActivity::toggleCurrentSetting() { void SettingsActivity::toggleCurrentSetting() const {
// Validate index // Validate index
if (selectedSettingIndex < 0 || selectedSettingIndex >= settingsCount) { if (selectedSettingIndex < 0 || selectedSettingIndex >= settingsCount) {
return; return;

View File

@ -32,11 +32,11 @@ class SettingsActivity final : public Activity {
static void taskTrampoline(void* param); static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop(); [[noreturn]] void displayTaskLoop();
void render() const; void render() const;
void toggleCurrentSetting(); void toggleCurrentSetting() const;
public: public:
explicit SettingsActivity(GfxRenderer& renderer, InputManager& inputManager, const std::function<void()>& onGoHome) explicit SettingsActivity(GfxRenderer& renderer, InputManager& inputManager, const std::function<void()>& onGoHome)
: Activity(renderer, inputManager), onGoHome(onGoHome) {} : Activity("Settings", renderer, inputManager), onGoHome(onGoHome) {}
void onEnter() override; void onEnter() override;
void onExit() override; void onExit() override;
void loop() override; void loop() override;

View File

@ -5,6 +5,8 @@
#include "config.h" #include "config.h"
void FullScreenMessageActivity::onEnter() { void FullScreenMessageActivity::onEnter() {
Activity::onEnter();
const auto height = renderer.getLineHeight(UI_FONT_ID); const auto height = renderer.getLineHeight(UI_FONT_ID);
const auto top = (GfxRenderer::getScreenHeight() - height) / 2; const auto top = (GfxRenderer::getScreenHeight() - height) / 2;

View File

@ -16,6 +16,9 @@ class FullScreenMessageActivity final : public Activity {
explicit FullScreenMessageActivity(GfxRenderer& renderer, InputManager& inputManager, std::string text, explicit FullScreenMessageActivity(GfxRenderer& renderer, InputManager& inputManager, std::string text,
const EpdFontStyle style = REGULAR, const EpdFontStyle style = REGULAR,
const EInkDisplay::RefreshMode refreshMode = EInkDisplay::FAST_REFRESH) const EInkDisplay::RefreshMode refreshMode = EInkDisplay::FAST_REFRESH)
: Activity(renderer, inputManager), text(std::move(text)), style(style), refreshMode(refreshMode) {} : Activity("FullScreenMessage", renderer, inputManager),
text(std::move(text)),
style(style),
refreshMode(refreshMode) {}
void onEnter() override; void onEnter() override;
}; };

View File

@ -12,11 +12,6 @@ const char* const KeyboardEntryActivity::keyboard[NUM_ROWS] = {
const char* const KeyboardEntryActivity::keyboardShift[NUM_ROWS] = {"~!@#$%^&*()_+", "QWERTYUIOP{}|", "ASDFGHJKL:\"", const char* const KeyboardEntryActivity::keyboardShift[NUM_ROWS] = {"~!@#$%^&*()_+", "QWERTYUIOP{}|", "ASDFGHJKL:\"",
"ZXCVBNM<>?", "^ _____<OK"}; "ZXCVBNM<>?", "^ _____<OK"};
KeyboardEntryActivity::KeyboardEntryActivity(GfxRenderer& renderer, InputManager& inputManager,
const std::string& title, const std::string& initialText, size_t maxLength,
bool isPassword)
: Activity(renderer, inputManager), title(title), text(initialText), maxLength(maxLength), isPassword(isPassword) {}
void KeyboardEntryActivity::setText(const std::string& newText) { void KeyboardEntryActivity::setText(const std::string& newText) {
text = newText; text = newText;
if (maxLength > 0 && text.length() > maxLength) { if (maxLength > 0 && text.length() > maxLength) {
@ -37,15 +32,13 @@ void KeyboardEntryActivity::reset(const std::string& newTitle, const std::string
} }
void KeyboardEntryActivity::onEnter() { void KeyboardEntryActivity::onEnter() {
Activity::onEnter();
// Reset state when entering the activity // Reset state when entering the activity
complete = false; complete = false;
cancelled = false; cancelled = false;
} }
void KeyboardEntryActivity::onExit() {
// Clean up if needed
}
void KeyboardEntryActivity::loop() { void KeyboardEntryActivity::loop() {
handleInput(); handleInput();
render(10); render(10);

View File

@ -34,7 +34,12 @@ class KeyboardEntryActivity : public Activity {
* @param isPassword If true, display asterisks instead of actual characters * @param isPassword If true, display asterisks instead of actual characters
*/ */
KeyboardEntryActivity(GfxRenderer& renderer, InputManager& inputManager, const std::string& title = "Enter Text", KeyboardEntryActivity(GfxRenderer& renderer, InputManager& inputManager, const std::string& title = "Enter Text",
const std::string& initialText = "", size_t maxLength = 0, bool isPassword = false); const std::string& initialText = "", const size_t maxLength = 0, const bool isPassword = false)
: Activity("KeyboardEntry", renderer, inputManager),
title(title),
text(initialText),
maxLength(maxLength),
isPassword(isPassword) {}
/** /**
* Handle button input. Call this in your main loop. * Handle button input. Call this in your main loop.
@ -85,7 +90,6 @@ class KeyboardEntryActivity : public Activity {
// Activity overrides // Activity overrides
void onEnter() override; void onEnter() override;
void onExit() override;
void loop() override; void loop() override;
private: private: