diff --git a/src/CrossPointState.cpp b/src/CrossPointState.cpp index b0df9d9..897447e 100644 --- a/src/CrossPointState.cpp +++ b/src/CrossPointState.cpp @@ -9,6 +9,8 @@ constexpr uint8_t STATE_FILE_VERSION = 1; constexpr char STATE_FILE[] = "/sd/.crosspoint/state.bin"; +CrossPointState CrossPointState::instance; + bool CrossPointState::saveToFile() const { std::ofstream outputFile(STATE_FILE); serialization::writePod(outputFile, STATE_FILE_VERSION); diff --git a/src/CrossPointState.h b/src/CrossPointState.h index 35b928d..f060a0c 100644 --- a/src/CrossPointState.h +++ b/src/CrossPointState.h @@ -3,11 +3,20 @@ #include class CrossPointState { + // Static instance + static CrossPointState instance; + public: std::string openEpubPath; ~CrossPointState() = default; + // Get singleton instance + static CrossPointState& getInstance() { return instance; } + bool saveToFile() const; bool loadFromFile(); }; + +// Helper macro to access settings +#define APP_STATE CrossPointState::getInstance() diff --git a/src/activities/ActivityWithSubactivity.cpp b/src/activities/ActivityWithSubactivity.cpp new file mode 100644 index 0000000..56dccd9 --- /dev/null +++ b/src/activities/ActivityWithSubactivity.cpp @@ -0,0 +1,21 @@ +#include "ActivityWithSubactivity.h" + +void ActivityWithSubactivity::exitActivity() { + if (subActivity) { + subActivity->onExit(); + subActivity.reset(); + } +} + +void ActivityWithSubactivity::enterNewActivity(Activity* activity) { + subActivity.reset(activity); + subActivity->onEnter(); +} + +void ActivityWithSubactivity::loop() { + if (subActivity) { + subActivity->loop(); + } +} + +void ActivityWithSubactivity::onExit() { exitActivity(); } diff --git a/src/activities/ActivityWithSubactivity.h b/src/activities/ActivityWithSubactivity.h new file mode 100644 index 0000000..b3a6873 --- /dev/null +++ b/src/activities/ActivityWithSubactivity.h @@ -0,0 +1,17 @@ +#pragma once +#include + +#include "Activity.h" + +class ActivityWithSubactivity : public Activity { + protected: + std::unique_ptr subActivity = nullptr; + void exitActivity(); + void enterNewActivity(Activity* activity); + + public: + explicit ActivityWithSubactivity(GfxRenderer& renderer, InputManager& inputManager) + : Activity(renderer, inputManager) {} + void loop() override; + void onExit() override; +}; diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index 604c72c..cc5712a 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -45,7 +45,7 @@ void HomeActivity::loop() { if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) { if (selectorIndex == 0) { - onFileSelectionOpen(); + onReaderOpen(); } else if (selectorIndex == 1) { onSettingsOpen(); } diff --git a/src/activities/home/HomeActivity.h b/src/activities/home/HomeActivity.h index a3ad324..48c3aa8 100644 --- a/src/activities/home/HomeActivity.h +++ b/src/activities/home/HomeActivity.h @@ -12,7 +12,7 @@ class HomeActivity final : public Activity { SemaphoreHandle_t renderingMutex = nullptr; int selectorIndex = 0; bool updateRequired = false; - const std::function onFileSelectionOpen; + const std::function onReaderOpen; const std::function onSettingsOpen; static constexpr int menuItemCount = 2; @@ -22,9 +22,9 @@ class HomeActivity final : public Activity { void render() const; public: - explicit HomeActivity(GfxRenderer& renderer, InputManager& inputManager, - const std::function& onFileSelectionOpen, const std::function& onSettingsOpen) - : Activity(renderer, inputManager), onFileSelectionOpen(onFileSelectionOpen), onSettingsOpen(onSettingsOpen) {} + explicit HomeActivity(GfxRenderer& renderer, InputManager& inputManager, const std::function& onReaderOpen, + const std::function& onSettingsOpen) + : Activity(renderer, inputManager), onReaderOpen(onReaderOpen), onSettingsOpen(onSettingsOpen) {} void onEnter() override; void onExit() override; void loop() override; diff --git a/src/activities/reader/ReaderActivity.cpp b/src/activities/reader/ReaderActivity.cpp new file mode 100644 index 0000000..099d7e2 --- /dev/null +++ b/src/activities/reader/ReaderActivity.cpp @@ -0,0 +1,68 @@ +#include "ReaderActivity.h" + +#include + +#include "CrossPointState.h" +#include "Epub.h" +#include "EpubReaderActivity.h" +#include "FileSelectionActivity.h" +#include "activities/util/FullScreenMessageActivity.h" + +std::unique_ptr ReaderActivity::loadEpub(const std::string& path) { + if (!SD.exists(path.c_str())) { + Serial.printf("[%lu] [ ] File does not exist: %s\n", millis(), path.c_str()); + return nullptr; + } + + auto epub = std::unique_ptr(new Epub(path, "/.crosspoint")); + if (epub->load()) { + return epub; + } + + Serial.printf("[%lu] [ ] Failed to load epub\n", millis()); + return nullptr; +} + +void ReaderActivity::onSelectEpubFile(const std::string& path) { + exitActivity(); + enterNewActivity(new FullScreenMessageActivity(renderer, inputManager, "Loading...")); + + auto epub = loadEpub(path); + if (epub) { + APP_STATE.openEpubPath = path; + APP_STATE.saveToFile(); + onGoToEpubReader(std::move(epub)); + } else { + exitActivity(); + enterNewActivity(new FullScreenMessageActivity(renderer, inputManager, "Failed to load epub", REGULAR, + EInkDisplay::HALF_REFRESH)); + delay(2000); + onGoToFileSelection(); + } +} + +void ReaderActivity::onGoToFileSelection() { + exitActivity(); + enterNewActivity(new FileSelectionActivity( + renderer, inputManager, [this](const std::string& path) { onSelectEpubFile(path); }, onGoBack)); +} + +void ReaderActivity::onGoToEpubReader(std::unique_ptr epub) { + exitActivity(); + enterNewActivity(new EpubReaderActivity(renderer, inputManager, std::move(epub), [this] { onGoToFileSelection(); })); +} + +void ReaderActivity::onEnter() { + if (initialEpubPath.empty()) { + onGoToFileSelection(); + return; + } + + auto epub = loadEpub(initialEpubPath); + if (!epub) { + onGoBack(); + return; + } + + onGoToEpubReader(std::move(epub)); +} diff --git a/src/activities/reader/ReaderActivity.h b/src/activities/reader/ReaderActivity.h new file mode 100644 index 0000000..a68cd89 --- /dev/null +++ b/src/activities/reader/ReaderActivity.h @@ -0,0 +1,24 @@ +#pragma once +#include + +#include "../ActivityWithSubactivity.h" + +class Epub; + +class ReaderActivity final : public ActivityWithSubactivity { + std::string initialEpubPath; + const std::function onGoBack; + static std::unique_ptr loadEpub(const std::string& path); + + void onSelectEpubFile(const std::string& path); + void onGoToFileSelection(); + void onGoToEpubReader(std::unique_ptr epub); + + public: + explicit ReaderActivity(GfxRenderer& renderer, InputManager& inputManager, std::string initialEpubPath, + const std::function& onGoBack) + : ActivityWithSubactivity(renderer, inputManager), + initialEpubPath(std::move(initialEpubPath)), + onGoBack(onGoBack) {} + void onEnter() override; +}; diff --git a/src/main.cpp b/src/main.cpp index 597f645..39c5bd0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,8 +19,7 @@ #include "activities/boot_sleep/BootActivity.h" #include "activities/boot_sleep/SleepActivity.h" #include "activities/home/HomeActivity.h" -#include "activities/reader/EpubReaderActivity.h" -#include "activities/reader/FileSelectionActivity.h" +#include "activities/reader/ReaderActivity.h" #include "activities/settings/SettingsActivity.h" #include "activities/util/FullScreenMessageActivity.h" #include "config.h" @@ -43,7 +42,6 @@ EInkDisplay einkDisplay(EPD_SCLK, EPD_MOSI, EPD_CS, EPD_DC, EPD_RST, EPD_BUSY); InputManager inputManager; GfxRenderer renderer(einkDisplay); Activity* currentActivity; -CrossPointState appState; // Fonts EpdFont bookerlyFont(&bookerly_2b); @@ -67,21 +65,6 @@ constexpr unsigned long POWER_BUTTON_SLEEP_MS = 500; // Auto-sleep timeout (10 minutes of inactivity) constexpr unsigned long AUTO_SLEEP_TIMEOUT_MS = 10 * 60 * 1000; -std::unique_ptr loadEpub(const std::string& path) { - if (!SD.exists(path.c_str())) { - Serial.printf("[%lu] [ ] File does not exist: %s\n", millis(), path.c_str()); - return nullptr; - } - - auto epub = std::unique_ptr(new Epub(path, "/.crosspoint")); - if (epub->load()) { - return epub; - } - - Serial.printf("[%lu] [ ] Failed to load epub\n", millis()); - return nullptr; -} - void exitActivity() { if (currentActivity) { currentActivity->onExit(); @@ -151,30 +134,11 @@ void enterDeepSleep() { } void onGoHome(); -void onGoToFileSelection(); -void onSelectEpubFile(const std::string& path) { +void onGoToReader(const std::string& initialEpubPath) { exitActivity(); - enterNewActivity(new FullScreenMessageActivity(renderer, inputManager, "Loading...")); - - auto epub = loadEpub(path); - if (epub) { - appState.openEpubPath = path; - appState.saveToFile(); - exitActivity(); - enterNewActivity(new EpubReaderActivity(renderer, inputManager, std::move(epub), onGoToFileSelection)); - } else { - exitActivity(); - enterNewActivity(new FullScreenMessageActivity(renderer, inputManager, "Failed to load epub", REGULAR, - EInkDisplay::HALF_REFRESH)); - delay(2000); - onGoToFileSelection(); - } -} - -void onGoToFileSelection() { - exitActivity(); - enterNewActivity(new FileSelectionActivity(renderer, inputManager, onSelectEpubFile, onGoHome)); + enterNewActivity(new ReaderActivity(renderer, inputManager, initialEpubPath, onGoHome)); } +void onGoToReaderHome() { onGoToReader(std::string()); } void onGoToSettings() { exitActivity(); @@ -183,7 +147,7 @@ void onGoToSettings() { void onGoHome() { exitActivity(); - enterNewActivity(new HomeActivity(renderer, inputManager, onGoToFileSelection, onGoToSettings)); + enterNewActivity(new HomeActivity(renderer, inputManager, onGoToReaderHome, onGoToSettings)); } void setup() { @@ -216,20 +180,13 @@ void setup() { SD.begin(SD_SPI_CS, SPI, SPI_FQ); SETTINGS.loadFromFile(); - appState.loadFromFile(); - if (!appState.openEpubPath.empty()) { - auto epub = loadEpub(appState.openEpubPath); - if (epub) { - exitActivity(); - enterNewActivity(new EpubReaderActivity(renderer, inputManager, std::move(epub), onGoHome)); - // Ensure we're not still holding the power button before leaving setup - waitForPowerRelease(); - return; - } + APP_STATE.loadFromFile(); + if (APP_STATE.openEpubPath.empty()) { + onGoHome(); + } else { + onGoToReader(APP_STATE.openEpubPath); } - onGoHome(); - // Ensure we're not still holding the power button before leaving setup waitForPowerRelease(); }