home screen performance fix
This commit is contained in:
parent
2f21f55512
commit
7fce5b347d
@ -5,7 +5,7 @@
|
||||
#include <Serialization.h>
|
||||
|
||||
namespace {
|
||||
constexpr uint8_t STATE_FILE_VERSION = 2;
|
||||
constexpr uint8_t STATE_FILE_VERSION = 3;
|
||||
constexpr char STATE_FILE[] = "/.crosspoint/state.bin";
|
||||
} // namespace
|
||||
|
||||
@ -20,6 +20,9 @@ bool CrossPointState::saveToFile() const {
|
||||
serialization::writePod(outputFile, STATE_FILE_VERSION);
|
||||
serialization::writeString(outputFile, openEpubPath);
|
||||
serialization::writePod(outputFile, lastSleepImage);
|
||||
// Version 3: add cached book title and author
|
||||
serialization::writeString(outputFile, openBookTitle);
|
||||
serialization::writeString(outputFile, openBookAuthor);
|
||||
outputFile.close();
|
||||
return true;
|
||||
}
|
||||
@ -45,6 +48,15 @@ bool CrossPointState::loadFromFile() {
|
||||
lastSleepImage = 0;
|
||||
}
|
||||
|
||||
// Version 3: read cached book title and author
|
||||
if (version >= 3) {
|
||||
serialization::readString(inputFile, openBookTitle);
|
||||
serialization::readString(inputFile, openBookAuthor);
|
||||
} else {
|
||||
openBookTitle.clear();
|
||||
openBookAuthor.clear();
|
||||
}
|
||||
|
||||
inputFile.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -8,6 +8,8 @@ class CrossPointState {
|
||||
|
||||
public:
|
||||
std::string openEpubPath;
|
||||
std::string openBookTitle; // Cached title for the current book
|
||||
std::string openBookAuthor; // Cached author for the current book
|
||||
uint8_t lastSleepImage;
|
||||
~CrossPointState() = default;
|
||||
|
||||
|
||||
@ -42,6 +42,41 @@ void ScreenComponents::drawBattery(const GfxRenderer& renderer, const int left,
|
||||
renderer.fillRect(x + 2, y + 2, filledWidth, batteryHeight - 4);
|
||||
}
|
||||
|
||||
void ScreenComponents::drawBatteryLarge(const GfxRenderer& renderer, const int left, const int top,
|
||||
const bool showPercentage) {
|
||||
// Larger battery icon with UI_10 font for bottom button hint area
|
||||
const uint16_t percentage = battery.readPercentage();
|
||||
const auto percentageText = showPercentage ? std::to_string(percentage) + "%" : "";
|
||||
renderer.drawText(UI_10_FONT_ID, left + 28, top, percentageText.c_str());
|
||||
|
||||
// Scaled up battery dimensions (~33% larger)
|
||||
constexpr int batteryWidth = 20;
|
||||
constexpr int batteryHeight = 16;
|
||||
const int x = left;
|
||||
const int y = top + 6;
|
||||
|
||||
// Top line
|
||||
renderer.drawLine(x + 1, y, x + batteryWidth - 4, y);
|
||||
// Bottom line
|
||||
renderer.drawLine(x + 1, y + batteryHeight - 1, x + batteryWidth - 4, y + batteryHeight - 1);
|
||||
// Left line
|
||||
renderer.drawLine(x, y + 1, x, y + batteryHeight - 2);
|
||||
// Battery end (right side with nub)
|
||||
renderer.drawLine(x + batteryWidth - 3, y + 1, x + batteryWidth - 3, y + batteryHeight - 2);
|
||||
// Battery nub
|
||||
renderer.drawPixel(x + batteryWidth - 2, y + 4);
|
||||
renderer.drawPixel(x + batteryWidth - 2, y + batteryHeight - 5);
|
||||
renderer.drawLine(x + batteryWidth - 1, y + 5, x + batteryWidth - 1, y + batteryHeight - 6);
|
||||
|
||||
// The +1 is to round up, so that we always fill at least one pixel
|
||||
int filledWidth = percentage * (batteryWidth - 6) / 100 + 1;
|
||||
if (filledWidth > batteryWidth - 6) {
|
||||
filledWidth = batteryWidth - 6; // Ensure we don't overflow
|
||||
}
|
||||
|
||||
renderer.fillRect(x + 2, y + 2, filledWidth, batteryHeight - 4);
|
||||
}
|
||||
|
||||
int ScreenComponents::drawTabBar(const GfxRenderer& renderer, const int y, const std::vector<TabInfo>& tabs) {
|
||||
constexpr int tabPadding = 20; // Horizontal padding between tabs
|
||||
constexpr int leftMargin = 20; // Left margin for first tab
|
||||
|
||||
@ -15,6 +15,9 @@ class ScreenComponents {
|
||||
public:
|
||||
static void drawBattery(const GfxRenderer& renderer, int left, int top, bool showPercentage = true);
|
||||
|
||||
// Draw a larger battery icon suitable for bottom button hint area
|
||||
static void drawBatteryLarge(const GfxRenderer& renderer, int left, int top, bool showPercentage = true);
|
||||
|
||||
// Draw a horizontal tab bar with underline indicator for selected tab
|
||||
// Returns the height of the tab bar (for positioning content below)
|
||||
static int drawTabBar(const GfxRenderer& renderer, int y, const std::vector<TabInfo>& tabs);
|
||||
|
||||
@ -18,6 +18,12 @@
|
||||
#include "fontIds.h"
|
||||
#include "util/StringUtils.h"
|
||||
|
||||
// Static member definitions for persistent cover buffer
|
||||
bool HomeActivity::coverRendered = false;
|
||||
bool HomeActivity::coverBufferStored = false;
|
||||
uint8_t* HomeActivity::coverBuffer = nullptr;
|
||||
std::string HomeActivity::cachedCoverPath;
|
||||
|
||||
void HomeActivity::taskTrampoline(void* param) {
|
||||
auto* self = static_cast<HomeActivity*>(param);
|
||||
self->displayTaskLoop();
|
||||
@ -41,50 +47,94 @@ void HomeActivity::onEnter() {
|
||||
// Check if OPDS browser URL is configured
|
||||
hasOpdsUrl = strlen(SETTINGS.opdsServerUrl) > 0;
|
||||
|
||||
if (hasContinueReading) {
|
||||
// Extract filename from path for display
|
||||
lastBookTitle = APP_STATE.openEpubPath;
|
||||
const size_t lastSlash = lastBookTitle.find_last_of('/');
|
||||
if (!hasContinueReading) {
|
||||
// No book to continue reading - clear any stale cover cache
|
||||
if (coverBufferStored) {
|
||||
freeCoverBuffer();
|
||||
coverRendered = false;
|
||||
cachedCoverPath.clear();
|
||||
}
|
||||
} else {
|
||||
// Extract filename from path for display (fallback if no cached title)
|
||||
std::string filenameFromPath = APP_STATE.openEpubPath;
|
||||
const size_t lastSlash = filenameFromPath.find_last_of('/');
|
||||
if (lastSlash != std::string::npos) {
|
||||
lastBookTitle = lastBookTitle.substr(lastSlash + 1);
|
||||
filenameFromPath = filenameFromPath.substr(lastSlash + 1);
|
||||
}
|
||||
|
||||
// If epub, try to load the metadata for title/author and cover
|
||||
if (StringUtils::checkFileExtension(lastBookTitle, ".epub")) {
|
||||
// Check if we have cached title/author from CrossPointState
|
||||
const bool hasCachedMetadata = !APP_STATE.openBookTitle.empty();
|
||||
|
||||
if (hasCachedMetadata) {
|
||||
// Use cached title and author - no need to load the book file
|
||||
lastBookTitle = APP_STATE.openBookTitle;
|
||||
lastBookAuthor = APP_STATE.openBookAuthor;
|
||||
} else {
|
||||
// No cached metadata - use filename as fallback
|
||||
lastBookTitle = filenameFromPath;
|
||||
}
|
||||
|
||||
// If epub, try to get cover image (and populate cache if needed)
|
||||
if (StringUtils::checkFileExtension(filenameFromPath, ".epub")) {
|
||||
Epub epub(APP_STATE.openEpubPath, "/.crosspoint");
|
||||
epub.load(false);
|
||||
if (!epub.getTitle().empty()) {
|
||||
lastBookTitle = std::string(epub.getTitle());
|
||||
}
|
||||
if (!epub.getAuthor().empty()) {
|
||||
lastBookAuthor = std::string(epub.getAuthor());
|
||||
// Only load epub metadata if we don't have cached title/author
|
||||
if (!hasCachedMetadata) {
|
||||
epub.load(false);
|
||||
if (!epub.getTitle().empty()) {
|
||||
lastBookTitle = std::string(epub.getTitle());
|
||||
APP_STATE.openBookTitle = lastBookTitle;
|
||||
}
|
||||
if (!epub.getAuthor().empty()) {
|
||||
lastBookAuthor = std::string(epub.getAuthor());
|
||||
APP_STATE.openBookAuthor = lastBookAuthor;
|
||||
}
|
||||
// Save updated cache to file
|
||||
APP_STATE.saveToFile();
|
||||
}
|
||||
// Try to generate thumbnail image for Continue Reading card
|
||||
if (epub.generateThumbBmp()) {
|
||||
coverBmpPath = epub.getThumbBmpPath();
|
||||
hasCoverImage = true;
|
||||
}
|
||||
} else if (StringUtils::checkFileExtension(lastBookTitle, ".xtch") ||
|
||||
StringUtils::checkFileExtension(lastBookTitle, ".xtc")) {
|
||||
} else if (StringUtils::checkFileExtension(filenameFromPath, ".xtch") ||
|
||||
StringUtils::checkFileExtension(filenameFromPath, ".xtc")) {
|
||||
// Handle XTC file
|
||||
Xtc xtc(APP_STATE.openEpubPath, "/.crosspoint");
|
||||
if (xtc.load()) {
|
||||
if (!xtc.getTitle().empty()) {
|
||||
lastBookTitle = std::string(xtc.getTitle());
|
||||
if (!hasCachedMetadata) {
|
||||
if (xtc.load()) {
|
||||
if (!xtc.getTitle().empty()) {
|
||||
lastBookTitle = std::string(xtc.getTitle());
|
||||
APP_STATE.openBookTitle = lastBookTitle;
|
||||
APP_STATE.saveToFile();
|
||||
}
|
||||
}
|
||||
// Try to generate thumbnail image for Continue Reading card
|
||||
if (xtc.generateThumbBmp()) {
|
||||
coverBmpPath = xtc.getThumbBmpPath();
|
||||
hasCoverImage = true;
|
||||
// Remove extension from title if we don't have metadata
|
||||
if (StringUtils::checkFileExtension(lastBookTitle, ".xtch")) {
|
||||
lastBookTitle.resize(lastBookTitle.length() - 5);
|
||||
APP_STATE.openBookTitle = lastBookTitle;
|
||||
APP_STATE.saveToFile();
|
||||
} else if (StringUtils::checkFileExtension(lastBookTitle, ".xtc")) {
|
||||
lastBookTitle.resize(lastBookTitle.length() - 4);
|
||||
APP_STATE.openBookTitle = lastBookTitle;
|
||||
APP_STATE.saveToFile();
|
||||
}
|
||||
}
|
||||
// Remove extension from title if we don't have metadata
|
||||
if (StringUtils::checkFileExtension(lastBookTitle, ".xtch")) {
|
||||
lastBookTitle.resize(lastBookTitle.length() - 5);
|
||||
} else if (StringUtils::checkFileExtension(lastBookTitle, ".xtc")) {
|
||||
lastBookTitle.resize(lastBookTitle.length() - 4);
|
||||
// Try to generate thumbnail image for Continue Reading card
|
||||
if (xtc.generateThumbBmp()) {
|
||||
coverBmpPath = xtc.getThumbBmpPath();
|
||||
hasCoverImage = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if cached cover buffer is still valid (same book)
|
||||
if (hasCoverImage && coverBufferStored && cachedCoverPath == coverBmpPath) {
|
||||
// Cover buffer is still valid, mark as rendered to skip reload
|
||||
coverRendered = true;
|
||||
} else if (hasCoverImage && coverBufferStored && cachedCoverPath != coverBmpPath) {
|
||||
// Book changed, invalidate cached buffer
|
||||
freeCoverBuffer();
|
||||
coverRendered = false;
|
||||
}
|
||||
}
|
||||
|
||||
selectorIndex = 0;
|
||||
@ -112,8 +162,8 @@ void HomeActivity::onExit() {
|
||||
vSemaphoreDelete(renderingMutex);
|
||||
renderingMutex = nullptr;
|
||||
|
||||
// Free the stored cover buffer if any
|
||||
freeCoverBuffer();
|
||||
// NOTE: Do NOT free cover buffer here - keep it cached for fast re-entry
|
||||
// The buffer will be freed/replaced when the book changes
|
||||
}
|
||||
|
||||
bool HomeActivity::storeCoverBuffer() {
|
||||
@ -291,6 +341,9 @@ void HomeActivity::render() {
|
||||
|
||||
// Store the buffer with cover image for fast navigation
|
||||
coverBufferStored = storeCoverBuffer();
|
||||
if (coverBufferStored) {
|
||||
cachedCoverPath = coverBmpPath; // Remember which cover is cached
|
||||
}
|
||||
coverRendered = true;
|
||||
|
||||
// First render: if selected, draw selection indicators now
|
||||
@ -552,13 +605,12 @@ void HomeActivity::render() {
|
||||
const auto labels = mappedInput.mapLabels("", "Select", "Up", "Down");
|
||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||
|
||||
// Draw battery in bottom-left where the back button hint would normally be
|
||||
const bool showBatteryPercentage =
|
||||
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());
|
||||
ScreenComponents::drawBattery(renderer, batteryX, 10, showBatteryPercentage);
|
||||
constexpr int batteryX = 25; // Align with first button hint position
|
||||
const int batteryY = pageHeight - 34; // Vertically centered in button hint area
|
||||
ScreenComponents::drawBatteryLarge(renderer, batteryX, batteryY, showBatteryPercentage);
|
||||
|
||||
renderer.displayBuffer();
|
||||
}
|
||||
|
||||
@ -15,9 +15,13 @@ class HomeActivity final : public Activity {
|
||||
bool hasContinueReading = false;
|
||||
bool hasOpdsUrl = false;
|
||||
bool hasCoverImage = false;
|
||||
bool coverRendered = false; // Track if cover has been rendered once
|
||||
bool coverBufferStored = false; // Track if cover buffer is stored
|
||||
uint8_t* coverBuffer = nullptr; // HomeActivity's own buffer for cover image
|
||||
|
||||
// Static cover buffer - persists across activity changes to avoid reloading from SD
|
||||
static bool coverRendered; // Track if cover has been rendered once
|
||||
static bool coverBufferStored; // Track if cover buffer is stored
|
||||
static uint8_t* coverBuffer; // HomeActivity's own buffer for cover image
|
||||
static std::string cachedCoverPath; // Path of the cached cover (to detect book changes)
|
||||
|
||||
std::string lastBookTitle;
|
||||
std::string lastBookAuthor;
|
||||
std::string coverBmpPath;
|
||||
|
||||
@ -120,8 +120,10 @@ void EpubReaderActivity::onEnter() {
|
||||
}
|
||||
}
|
||||
|
||||
// Save current epub as last opened epub and add to recent books
|
||||
// Save current epub as last opened epub and cache title/author for home screen
|
||||
APP_STATE.openEpubPath = epub->getPath();
|
||||
APP_STATE.openBookTitle = epub->getTitle();
|
||||
APP_STATE.openBookAuthor = epub->getAuthor();
|
||||
APP_STATE.saveToFile();
|
||||
RECENT_BOOKS.addBook(epub->getPath(), epub->getTitle(), epub->getAuthor());
|
||||
|
||||
|
||||
@ -97,8 +97,10 @@ void TxtReaderActivity::onEnter() {
|
||||
});
|
||||
}
|
||||
|
||||
// Save current txt as last opened file
|
||||
// Save current txt as last opened file and cache title for home screen
|
||||
APP_STATE.openEpubPath = txt->getPath();
|
||||
APP_STATE.openBookTitle = txt->getTitle();
|
||||
APP_STATE.openBookAuthor.clear(); // TXT files don't have author metadata
|
||||
APP_STATE.saveToFile();
|
||||
|
||||
// Trigger first update
|
||||
|
||||
@ -83,8 +83,10 @@ void XtcReaderActivity::onEnter() {
|
||||
// Load saved progress
|
||||
loadProgress();
|
||||
|
||||
// Save current XTC as last opened book and add to recent books
|
||||
// Save current XTC as last opened book and cache title for home screen
|
||||
APP_STATE.openEpubPath = xtc->getPath();
|
||||
APP_STATE.openBookTitle = xtc->getTitle();
|
||||
APP_STATE.openBookAuthor.clear(); // XTC files don't have author metadata
|
||||
APP_STATE.saveToFile();
|
||||
RECENT_BOOKS.addBook(xtc->getPath(), xtc->getTitle(), "");
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user