This commit is contained in:
Kenneth 2026-01-13 14:13:37 -05:00
parent 5d81d7cac3
commit 5d04440407
3 changed files with 107 additions and 183 deletions

View File

@ -15,17 +15,15 @@
#include "fontIds.h"
#include "util/StringUtils.h"
void HomeActivity::taskTrampoline(void *param) {
auto *self = static_cast<HomeActivity *>(param);
void HomeActivity::taskTrampoline(void* param) {
auto* self = static_cast<HomeActivity*>(param);
self->displayTaskLoop();
}
int HomeActivity::getMenuItemCount() const {
int count = 3; // Base: My Library, File transfer, Settings
if (hasContinueReading)
count++;
if (hasOpdsUrl)
count++;
int count = 3; // My Library, File transfer, Settings
if (hasContinueReading) count++;
if (hasOpdsUrl) count++;
return count;
}
@ -35,8 +33,7 @@ void HomeActivity::onEnter() {
renderingMutex = xSemaphoreCreateMutex();
// Check if we have a book to continue reading
hasContinueReading = !APP_STATE.openEpubPath.empty() &&
SdMan.exists(APP_STATE.openEpubPath.c_str());
hasContinueReading = !APP_STATE.openEpubPath.empty() && SdMan.exists(APP_STATE.openEpubPath.c_str());
// Check if OPDS browser URL is configured
hasOpdsUrl = strlen(SETTINGS.opdsServerUrl) > 0;
@ -82,8 +79,7 @@ void HomeActivity::onEnter() {
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);
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
@ -94,11 +90,9 @@ void HomeActivity::onExit() {
}
void HomeActivity::loop() {
const bool prevPressed =
mappedInput.wasPressed(MappedInputManager::Button::Up) ||
const bool prevPressed = mappedInput.wasPressed(MappedInputManager::Button::Up) ||
mappedInput.wasPressed(MappedInputManager::Button::Left);
const bool nextPressed =
mappedInput.wasPressed(MappedInputManager::Button::Down) ||
const bool nextPressed = mappedInput.wasPressed(MappedInputManager::Button::Down) ||
mappedInput.wasPressed(MappedInputManager::Button::Right);
const int menuCount = getMenuItemCount();
@ -175,11 +169,9 @@ void HomeActivity::render() const {
constexpr int bookmarkY = bookY + 1;
// Main bookmark body (solid)
renderer.fillRect(bookmarkX, bookmarkY, bookmarkWidth, bookmarkHeight,
!bookSelected);
renderer.fillRect(bookmarkX, bookmarkY, bookmarkWidth, bookmarkHeight, !bookSelected);
// Carve out an inverted triangle notch at the bottom center to create
// angled points
// Carve out an inverted triangle notch at the bottom center to create angled points
const int notchHeight = bookmarkHeight / 2; // depth of the notch
for (int i = 0; i < notchHeight; ++i) {
const int y = bookmarkY + bookmarkHeight - 1 - i;
@ -218,16 +210,14 @@ void HomeActivity::render() const {
const int maxLineWidth = bookWidth - 40;
const int spaceWidth = renderer.getSpaceWidth(UI_12_FONT_ID);
for (auto &i : words) {
for (auto& i : words) {
// If we just hit the line limit (3), stop processing words
if (lines.size() >= 3) {
// Limit to 3 lines
// Still have words left, so add ellipsis to last line
lines.back().append("...");
while (!lines.back().empty() &&
renderer.getTextWidth(UI_12_FONT_ID, lines.back().c_str()) >
maxLineWidth) {
while (!lines.back().empty() && renderer.getTextWidth(UI_12_FONT_ID, lines.back().c_str()) > maxLineWidth) {
lines.back().resize(lines.back().size() - 5);
lines.back().append("...");
}
@ -242,8 +232,7 @@ void HomeActivity::render() const {
wordWidth = renderer.getTextWidth(UI_12_FONT_ID, i.c_str());
}
int newLineWidth =
renderer.getTextWidth(UI_12_FONT_ID, currentLine.c_str());
int newLineWidth = renderer.getTextWidth(UI_12_FONT_ID, currentLine.c_str());
if (newLineWidth > 0) {
newLineWidth += spaceWidth;
}
@ -264,8 +253,7 @@ void HomeActivity::render() const {
}
// Book title text
int totalTextHeight =
renderer.getLineHeight(UI_12_FONT_ID) * static_cast<int>(lines.size());
int totalTextHeight = renderer.getLineHeight(UI_12_FONT_ID) * static_cast<int>(lines.size());
if (!lastBookAuthor.empty()) {
totalTextHeight += renderer.getLineHeight(UI_10_FONT_ID) * 3 / 2;
}
@ -273,9 +261,8 @@ void HomeActivity::render() const {
// Vertically center the title block within the card
int titleYStart = bookY + (bookHeight - totalTextHeight) / 2;
for (const auto &line : lines) {
renderer.drawCenteredText(UI_12_FONT_ID, titleYStart, line.c_str(),
!bookSelected);
for (const auto& line : lines) {
renderer.drawCenteredText(UI_12_FONT_ID, titleYStart, line.c_str(), !bookSelected);
titleYStart += renderer.getLineHeight(UI_12_FONT_ID);
}
@ -283,35 +270,26 @@ void HomeActivity::render() const {
titleYStart += renderer.getLineHeight(UI_10_FONT_ID) / 2;
std::string trimmedAuthor = lastBookAuthor;
// Trim author if too long
while (renderer.getTextWidth(UI_10_FONT_ID, trimmedAuthor.c_str()) >
maxLineWidth &&
!trimmedAuthor.empty()) {
while (renderer.getTextWidth(UI_10_FONT_ID, trimmedAuthor.c_str()) > maxLineWidth && !trimmedAuthor.empty()) {
trimmedAuthor.resize(trimmedAuthor.size() - 5);
trimmedAuthor.append("...");
}
renderer.drawCenteredText(UI_10_FONT_ID, titleYStart,
trimmedAuthor.c_str(), !bookSelected);
renderer.drawCenteredText(UI_10_FONT_ID, titleYStart, trimmedAuthor.c_str(), !bookSelected);
}
renderer.drawCenteredText(UI_10_FONT_ID,
bookY + bookHeight -
renderer.getLineHeight(UI_10_FONT_ID) * 3 / 2,
renderer.drawCenteredText(UI_10_FONT_ID, bookY + bookHeight - renderer.getLineHeight(UI_10_FONT_ID) * 3 / 2,
"Continue Reading", !bookSelected);
} else {
// No book to continue reading
const int y = bookY + (bookHeight - renderer.getLineHeight(UI_12_FONT_ID) -
renderer.getLineHeight(UI_10_FONT_ID)) /
2;
const int y =
bookY + (bookHeight - renderer.getLineHeight(UI_12_FONT_ID) - renderer.getLineHeight(UI_10_FONT_ID)) / 2;
renderer.drawCenteredText(UI_12_FONT_ID, y, "No open book");
renderer.drawCenteredText(UI_10_FONT_ID,
y + renderer.getLineHeight(UI_12_FONT_ID),
"Start reading below");
renderer.drawCenteredText(UI_10_FONT_ID, y + renderer.getLineHeight(UI_12_FONT_ID), "Start reading below");
}
// --- Bottom menu tiles ---
// Build menu items dynamically
std::vector<const char *> menuItems = {"My Library", "File Transfer",
"Settings"};
std::vector<const char*> menuItems = {"My Library", "File Transfer", "Settings"};
if (hasOpdsUrl) {
// Insert Calibre Library after My Library
menuItems.insert(menuItems.begin() + 1, "Calibre Library");
@ -321,13 +299,11 @@ void HomeActivity::render() const {
constexpr int menuTileHeight = 45;
constexpr int menuSpacing = 8;
const int totalMenuHeight =
static_cast<int>(menuItems.size()) * menuTileHeight +
(static_cast<int>(menuItems.size()) - 1) * menuSpacing;
static_cast<int>(menuItems.size()) * menuTileHeight + (static_cast<int>(menuItems.size()) - 1) * menuSpacing;
int menuStartY = bookY + bookHeight + 15;
// Ensure we don't collide with the bottom button legend
const int maxMenuStartY =
pageHeight - bottomMargin - totalMenuHeight - margin;
const int maxMenuStartY = pageHeight - bottomMargin - totalMenuHeight - margin;
if (menuStartY > maxMenuStartY) {
menuStartY = maxMenuStartY;
}
@ -335,8 +311,7 @@ void HomeActivity::render() const {
for (size_t i = 0; i < menuItems.size(); ++i) {
const int overallIndex = static_cast<int>(i) + (hasContinueReading ? 1 : 0);
constexpr int tileX = margin;
const int tileY =
menuStartY + static_cast<int>(i) * (menuTileHeight + menuSpacing);
const int tileY = menuStartY + static_cast<int>(i) * (menuTileHeight + menuSpacing);
const bool selected = selectorIndex == overallIndex;
if (selected) {
@ -345,33 +320,25 @@ void HomeActivity::render() const {
renderer.drawRect(tileX, tileY, menuTileWidth, menuTileHeight);
}
const char *label = menuItems[i];
const char* label = menuItems[i];
const int textWidth = renderer.getTextWidth(UI_10_FONT_ID, label);
const int textX = tileX + (menuTileWidth - textWidth) / 2;
const int lineHeight = renderer.getLineHeight(UI_10_FONT_ID);
const int textY =
tileY + (menuTileHeight - lineHeight) /
2; // vertically centered assuming y is top of text
const int textY = tileY + (menuTileHeight - lineHeight) / 2; // vertically centered assuming y is top of text
// Invert text when the tile is selected, to contrast with the filled
// background
// Invert text when the tile is selected, to contrast with the filled background
renderer.drawText(UI_10_FONT_ID, textX, textY, label, !selected);
}
const auto labels = mappedInput.mapLabels("", "Confirm", "Up", "Down");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3,
labels.btn4);
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
const bool showBatteryPercentage =
SETTINGS.hideBatteryPercentage !=
CrossPointSettings::HIDE_BATTERY_PERCENTAGE::HIDE_ALWAYS;
SETTINGS.hideBatteryPercentage != CrossPointSettings::HIDE_BATTERY_PERCENTAGE::HIDE_ALWAYS;
// get percentage so we can align text properly
const uint16_t percentage = battery.readPercentage();
const auto percentageText =
showBatteryPercentage ? std::to_string(percentage) + "%" : "";
const auto batteryX =
pageWidth - 25 -
renderer.getTextWidth(SMALL_FONT_ID, percentageText.c_str());
const auto percentageText = showBatteryPercentage ? std::to_string(percentage) + "%" : "";
const auto batteryX = pageWidth - 25 - renderer.getTextWidth(SMALL_FONT_ID, percentageText.c_str());
ScreenComponents::drawBattery(renderer, batteryX, 10, showBatteryPercentage);
renderer.displayBuffer();

View File

@ -22,21 +22,21 @@ class HomeActivity final : public Activity {
const std::function<void()> onFileTransferOpen;
const std::function<void()> onOpdsBrowserOpen;
static void taskTrampoline(void *param);
static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop();
void render() const;
int getMenuItemCount() const;
public:
explicit HomeActivity(GfxRenderer &renderer, MappedInputManager &mappedInput,
const std::function<void()> &onContinueReading,
const std::function<void()> &onMyLibraryOpen,
const std::function<void()> &onSettingsOpen,
const std::function<void()> &onFileTransferOpen,
const std::function<void()> &onOpdsBrowserOpen)
public:
explicit HomeActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::function<void()>& onContinueReading, const std::function<void()>& onMyLibraryOpen,
const std::function<void()>& onSettingsOpen, const std::function<void()>& onFileTransferOpen,
const std::function<void()>& onOpdsBrowserOpen)
: Activity("Home", renderer, mappedInput),
onContinueReading(onContinueReading), onMyLibraryOpen(onMyLibraryOpen),
onSettingsOpen(onSettingsOpen), onFileTransferOpen(onFileTransferOpen),
onContinueReading(onContinueReading),
onMyLibraryOpen(onMyLibraryOpen),
onSettingsOpen(onSettingsOpen),
onFileTransferOpen(onFileTransferOpen),
onOpdsBrowserOpen(onOpdsBrowserOpen) {}
void onEnter() override;
void onExit() override;

View File

@ -42,98 +42,82 @@ EInkDisplay einkDisplay(EPD_SCLK, EPD_MOSI, EPD_CS, EPD_DC, EPD_RST, EPD_BUSY);
InputManager inputManager;
MappedInputManager mappedInputManager(inputManager);
GfxRenderer renderer(einkDisplay);
Activity *currentActivity;
Activity* currentActivity;
// Fonts
EpdFont bookerly12RegularFont(&bookerly_12_regular);
EpdFont bookerly12BoldFont(&bookerly_12_bold);
EpdFont bookerly12ItalicFont(&bookerly_12_italic);
EpdFont bookerly12BoldItalicFont(&bookerly_12_bolditalic);
EpdFontFamily bookerly12FontFamily(&bookerly12RegularFont, &bookerly12BoldFont,
&bookerly12ItalicFont,
EpdFontFamily bookerly12FontFamily(&bookerly12RegularFont, &bookerly12BoldFont, &bookerly12ItalicFont,
&bookerly12BoldItalicFont);
EpdFont bookerly14RegularFont(&bookerly_14_regular);
EpdFont bookerly14BoldFont(&bookerly_14_bold);
EpdFont bookerly14ItalicFont(&bookerly_14_italic);
EpdFont bookerly14BoldItalicFont(&bookerly_14_bolditalic);
EpdFontFamily bookerly14FontFamily(&bookerly14RegularFont, &bookerly14BoldFont,
&bookerly14ItalicFont,
EpdFontFamily bookerly14FontFamily(&bookerly14RegularFont, &bookerly14BoldFont, &bookerly14ItalicFont,
&bookerly14BoldItalicFont);
EpdFont bookerly16RegularFont(&bookerly_16_regular);
EpdFont bookerly16BoldFont(&bookerly_16_bold);
EpdFont bookerly16ItalicFont(&bookerly_16_italic);
EpdFont bookerly16BoldItalicFont(&bookerly_16_bolditalic);
EpdFontFamily bookerly16FontFamily(&bookerly16RegularFont, &bookerly16BoldFont,
&bookerly16ItalicFont,
EpdFontFamily bookerly16FontFamily(&bookerly16RegularFont, &bookerly16BoldFont, &bookerly16ItalicFont,
&bookerly16BoldItalicFont);
EpdFont bookerly18RegularFont(&bookerly_18_regular);
EpdFont bookerly18BoldFont(&bookerly_18_bold);
EpdFont bookerly18ItalicFont(&bookerly_18_italic);
EpdFont bookerly18BoldItalicFont(&bookerly_18_bolditalic);
EpdFontFamily bookerly18FontFamily(&bookerly18RegularFont, &bookerly18BoldFont,
&bookerly18ItalicFont,
EpdFontFamily bookerly18FontFamily(&bookerly18RegularFont, &bookerly18BoldFont, &bookerly18ItalicFont,
&bookerly18BoldItalicFont);
EpdFont notosans12RegularFont(&notosans_12_regular);
EpdFont notosans12BoldFont(&notosans_12_bold);
EpdFont notosans12ItalicFont(&notosans_12_italic);
EpdFont notosans12BoldItalicFont(&notosans_12_bolditalic);
EpdFontFamily notosans12FontFamily(&notosans12RegularFont, &notosans12BoldFont,
&notosans12ItalicFont,
EpdFontFamily notosans12FontFamily(&notosans12RegularFont, &notosans12BoldFont, &notosans12ItalicFont,
&notosans12BoldItalicFont);
EpdFont notosans14RegularFont(&notosans_14_regular);
EpdFont notosans14BoldFont(&notosans_14_bold);
EpdFont notosans14ItalicFont(&notosans_14_italic);
EpdFont notosans14BoldItalicFont(&notosans_14_bolditalic);
EpdFontFamily notosans14FontFamily(&notosans14RegularFont, &notosans14BoldFont,
&notosans14ItalicFont,
EpdFontFamily notosans14FontFamily(&notosans14RegularFont, &notosans14BoldFont, &notosans14ItalicFont,
&notosans14BoldItalicFont);
EpdFont notosans16RegularFont(&notosans_16_regular);
EpdFont notosans16BoldFont(&notosans_16_bold);
EpdFont notosans16ItalicFont(&notosans_16_italic);
EpdFont notosans16BoldItalicFont(&notosans_16_bolditalic);
EpdFontFamily notosans16FontFamily(&notosans16RegularFont, &notosans16BoldFont,
&notosans16ItalicFont,
EpdFontFamily notosans16FontFamily(&notosans16RegularFont, &notosans16BoldFont, &notosans16ItalicFont,
&notosans16BoldItalicFont);
EpdFont notosans18RegularFont(&notosans_18_regular);
EpdFont notosans18BoldFont(&notosans_18_bold);
EpdFont notosans18ItalicFont(&notosans_18_italic);
EpdFont notosans18BoldItalicFont(&notosans_18_bolditalic);
EpdFontFamily notosans18FontFamily(&notosans18RegularFont, &notosans18BoldFont,
&notosans18ItalicFont,
EpdFontFamily notosans18FontFamily(&notosans18RegularFont, &notosans18BoldFont, &notosans18ItalicFont,
&notosans18BoldItalicFont);
EpdFont opendyslexic8RegularFont(&opendyslexic_8_regular);
EpdFont opendyslexic8BoldFont(&opendyslexic_8_bold);
EpdFont opendyslexic8ItalicFont(&opendyslexic_8_italic);
EpdFont opendyslexic8BoldItalicFont(&opendyslexic_8_bolditalic);
EpdFontFamily opendyslexic8FontFamily(&opendyslexic8RegularFont,
&opendyslexic8BoldFont,
&opendyslexic8ItalicFont,
EpdFontFamily opendyslexic8FontFamily(&opendyslexic8RegularFont, &opendyslexic8BoldFont, &opendyslexic8ItalicFont,
&opendyslexic8BoldItalicFont);
EpdFont opendyslexic10RegularFont(&opendyslexic_10_regular);
EpdFont opendyslexic10BoldFont(&opendyslexic_10_bold);
EpdFont opendyslexic10ItalicFont(&opendyslexic_10_italic);
EpdFont opendyslexic10BoldItalicFont(&opendyslexic_10_bolditalic);
EpdFontFamily opendyslexic10FontFamily(&opendyslexic10RegularFont,
&opendyslexic10BoldFont,
&opendyslexic10ItalicFont,
EpdFontFamily opendyslexic10FontFamily(&opendyslexic10RegularFont, &opendyslexic10BoldFont, &opendyslexic10ItalicFont,
&opendyslexic10BoldItalicFont);
EpdFont opendyslexic12RegularFont(&opendyslexic_12_regular);
EpdFont opendyslexic12BoldFont(&opendyslexic_12_bold);
EpdFont opendyslexic12ItalicFont(&opendyslexic_12_italic);
EpdFont opendyslexic12BoldItalicFont(&opendyslexic_12_bolditalic);
EpdFontFamily opendyslexic12FontFamily(&opendyslexic12RegularFont,
&opendyslexic12BoldFont,
&opendyslexic12ItalicFont,
EpdFontFamily opendyslexic12FontFamily(&opendyslexic12RegularFont, &opendyslexic12BoldFont, &opendyslexic12ItalicFont,
&opendyslexic12BoldItalicFont);
EpdFont opendyslexic14RegularFont(&opendyslexic_14_regular);
EpdFont opendyslexic14BoldFont(&opendyslexic_14_bold);
EpdFont opendyslexic14ItalicFont(&opendyslexic_14_italic);
EpdFont opendyslexic14BoldItalicFont(&opendyslexic_14_bolditalic);
EpdFontFamily opendyslexic14FontFamily(&opendyslexic14RegularFont,
&opendyslexic14BoldFont,
&opendyslexic14ItalicFont,
EpdFontFamily opendyslexic14FontFamily(&opendyslexic14RegularFont, &opendyslexic14BoldFont, &opendyslexic14ItalicFont,
&opendyslexic14BoldItalicFont);
EpdFont smallFont(&notosans_8_regular);
@ -159,33 +143,27 @@ void exitActivity() {
}
}
void enterNewActivity(Activity *activity) {
void enterNewActivity(Activity* activity) {
currentActivity = activity;
currentActivity->onEnter();
}
// Verify long press on wake-up from deep sleep
void verifyWakeupLongPress() {
// Give the user up to 1000ms to start holding the power button, and must hold
// for SETTINGS.getPowerButtonDuration()
// Give the user up to 1000ms to start holding the power button, and must hold for SETTINGS.getPowerButtonDuration()
const auto start = millis();
bool abort = false;
// Subtract the current time, because inputManager only starts counting the
// HeldTime from the first update() This way, we remove the time we already
// took to reach here from the duration, assuming the button was held until
// now from millis()==0 (i.e. device start time).
// Subtract the current time, because inputManager only starts counting the HeldTime from the first update()
// This way, we remove the time we already took to reach here from the duration,
// assuming the button was held until now from millis()==0 (i.e. device start time).
const uint16_t calibration = start;
const uint16_t calibratedPressDuration =
(calibration < SETTINGS.getPowerButtonDuration())
? SETTINGS.getPowerButtonDuration() - calibration
: 1;
(calibration < SETTINGS.getPowerButtonDuration()) ? SETTINGS.getPowerButtonDuration() - calibration : 1;
inputManager.update();
// Verify the user has actually pressed
while (!inputManager.isPressed(InputManager::BTN_POWER) &&
millis() - start < 1000) {
delay(10); // only wait 10ms each iteration to not delay too much in case of
// short configured duration.
while (!inputManager.isPressed(InputManager::BTN_POWER) && millis() - start < 1000) {
delay(10); // only wait 10ms each iteration to not delay too much in case of short configured duration.
inputManager.update();
}
@ -194,8 +172,7 @@ void verifyWakeupLongPress() {
do {
delay(10);
inputManager.update();
} while (inputManager.isPressed(InputManager::BTN_POWER) &&
inputManager.getHeldTime() < calibratedPressDuration);
} while (inputManager.isPressed(InputManager::BTN_POWER) && inputManager.getHeldTime() < calibratedPressDuration);
abort = inputManager.getHeldTime() < calibratedPressDuration;
} else {
abort = true;
@ -204,8 +181,7 @@ void verifyWakeupLongPress() {
if (abort) {
// Button released too early. Returning to sleep.
// IMPORTANT: Re-arm the wakeup trigger before sleeping again
esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN,
ESP_GPIO_WAKEUP_GPIO_LOW);
esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN, ESP_GPIO_WAKEUP_GPIO_LOW);
esp_deep_sleep_start();
}
}
@ -224,55 +200,46 @@ void enterDeepSleep() {
enterNewActivity(new SleepActivity(renderer, mappedInputManager));
einkDisplay.deepSleep();
Serial.printf("[%lu] [ ] Power button press calibration value: %lu ms\n",
millis(), t2 - t1);
Serial.printf("[%lu] [ ] Power button press calibration value: %lu ms\n", millis(), t2 - t1);
Serial.printf("[%lu] [ ] Entering deep sleep.\n", millis());
esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN,
ESP_GPIO_WAKEUP_GPIO_LOW);
// Ensure that the power button has been released to avoid immediately turning
// back on if you're holding it
esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN, ESP_GPIO_WAKEUP_GPIO_LOW);
// Ensure that the power button has been released to avoid immediately turning back on if you're holding it
waitForPowerRelease();
// Enter Deep Sleep
esp_deep_sleep_start();
}
void onGoHome();
void onGoToReader(const std::string &initialEpubPath) {
void onGoToReader(const std::string& initialEpubPath) {
exitActivity();
enterNewActivity(new ReaderActivity(renderer, mappedInputManager,
initialEpubPath, onGoHome));
enterNewActivity(new ReaderActivity(renderer, mappedInputManager, initialEpubPath, onGoHome));
}
void onContinueReading() { onGoToReader(APP_STATE.openEpubPath); }
void onGoToFileTransfer() {
exitActivity();
enterNewActivity(
new CrossPointWebServerActivity(renderer, mappedInputManager, onGoHome));
enterNewActivity(new CrossPointWebServerActivity(renderer, mappedInputManager, onGoHome));
}
void onGoToSettings() {
exitActivity();
enterNewActivity(
new SettingsActivity(renderer, mappedInputManager, onGoHome));
enterNewActivity(new SettingsActivity(renderer, mappedInputManager, onGoHome));
}
void onGoToMyLibrary() {
exitActivity();
enterNewActivity(new MyLibraryActivity(renderer, mappedInputManager, onGoHome,
onGoToReader));
enterNewActivity(new MyLibraryActivity(renderer, mappedInputManager, onGoHome, onGoToReader));
}
void onGoToBrowser() {
exitActivity();
enterNewActivity(
new OpdsBookBrowserActivity(renderer, mappedInputManager, onGoHome));
enterNewActivity(new OpdsBookBrowserActivity(renderer, mappedInputManager, onGoHome));
}
void onGoHome() {
exitActivity();
enterNewActivity(new HomeActivity(
renderer, mappedInputManager, onContinueReading, onGoToMyLibrary,
onGoToSettings, onGoToFileTransfer, onGoToBrowser));
enterNewActivity(new HomeActivity(renderer, mappedInputManager, onContinueReading, onGoToMyLibrary, onGoToSettings,
onGoToFileTransfer, onGoToBrowser));
}
void setupDisplayAndFonts() {
@ -318,8 +285,7 @@ void setup() {
Serial.printf("[%lu] [ ] SD card initialization failed\n", millis());
setupDisplayAndFonts();
exitActivity();
enterNewActivity(new FullScreenMessageActivity(
renderer, mappedInputManager, "SD card error", EpdFontFamily::BOLD));
enterNewActivity(new FullScreenMessageActivity(renderer, mappedInputManager, "SD card error", EpdFontFamily::BOLD));
return;
}
@ -328,11 +294,8 @@ void setup() {
// verify power button press duration after we've read settings.
verifyWakeupLongPress();
// First serial output only here to avoid timing inconsistencies for power
// button press duration verification
Serial.printf("[%lu] [ ] Starting CrossPoint version " CROSSPOINT_VERSION
"\n",
millis());
// First serial output only here to avoid timing inconsistencies for power button press duration verification
Serial.printf("[%lu] [ ] Starting CrossPoint version " CROSSPOINT_VERSION "\n", millis());
setupDisplayAndFonts();
@ -345,8 +308,7 @@ void setup() {
if (APP_STATE.openEpubPath.empty()) {
onGoHome();
} else {
// Clear app state to avoid getting into a boot loop if the epub doesn't
// load
// Clear app state to avoid getting into a boot loop if the epub doesn't load
const auto path = APP_STATE.openEpubPath;
APP_STATE.openEpubPath = "";
APP_STATE.saveToFile();
@ -365,14 +327,12 @@ void loop() {
inputManager.update();
if (Serial && millis() - lastMemPrint >= 10000) {
Serial.printf(
"[%lu] [MEM] Free: %d bytes, Total: %d bytes, Min Free: %d bytes\n",
millis(), ESP.getFreeHeap(), ESP.getHeapSize(), ESP.getMinFreeHeap());
Serial.printf("[%lu] [MEM] Free: %d bytes, Total: %d bytes, Min Free: %d bytes\n", millis(), ESP.getFreeHeap(),
ESP.getHeapSize(), ESP.getMinFreeHeap());
lastMemPrint = millis();
}
// Check for any user activity (button press or release) or active background
// work
// Check for any user activity (button press or release) or active background work
static unsigned long lastActivityTime = millis();
if (inputManager.wasAnyPressed() || inputManager.wasAnyReleased() ||
(currentActivity && currentActivity->preventAutoSleep())) {
@ -381,9 +341,7 @@ void loop() {
const unsigned long sleepTimeoutMs = SETTINGS.getSleepTimeoutMs();
if (millis() - lastActivityTime >= sleepTimeoutMs) {
Serial.printf(
"[%lu] [SLP] Auto-sleep triggered after %lu ms of inactivity\n",
millis(), sleepTimeoutMs);
Serial.printf("[%lu] [SLP] Auto-sleep triggered after %lu ms of inactivity\n", millis(), sleepTimeoutMs);
enterDeepSleep();
// This should never be hit as `enterDeepSleep` calls esp_deep_sleep_start
return;
@ -406,15 +364,14 @@ void loop() {
if (loopDuration > maxLoopDuration) {
maxLoopDuration = loopDuration;
if (maxLoopDuration > 50) {
Serial.printf(
"[%lu] [LOOP] New max loop duration: %lu ms (activity: %lu ms)\n",
millis(), maxLoopDuration, activityDuration);
Serial.printf("[%lu] [LOOP] New max loop duration: %lu ms (activity: %lu ms)\n", millis(), maxLoopDuration,
activityDuration);
}
}
// Add delay at the end of the loop to prevent tight spinning
// When an activity requests skip loop delay (e.g., webserver running), use
// yield() for faster response Otherwise, use longer delay to save power
// When an activity requests skip loop delay (e.g., webserver running), use yield() for faster response
// Otherwise, use longer delay to save power
if (currentActivity && currentActivity->skipLoopDelay()) {
yield(); // Give FreeRTOS a chance to run tasks, but return immediately
} else {