Merge branch 'master' of https://github.com/jpirnay/crosspoint-reader into feat-page-overlay

This commit is contained in:
jpirnay
2026-03-05 18:28:38 +01:00
33 changed files with 268 additions and 172 deletions
+8
View File
@@ -39,6 +39,14 @@ usability over "swiss-army-knife" functionality.
* **Complex Annotation:** No typed out notes. These features are better suited for devices with better input
capabilities and more powerful chips.
### In-scope — Technically Unsupported
*These features align with CrossPoint's goals but are impractical on the current hardware or produce poor UX.*
* **Clock Display:** The ESP32-C3's RTC drifts significantly during deep sleep; making the clock untrustworthy after any sleep cycle. NTP sync could help, but CrossPoint doesn't connect to the internet on every boot.
* **PDF Rendering:** PDFs are fixed-layout documents, so rendering them requires displaying pages as images rather than reflowable text — resulting in constant panning and zooming that makes for a poor reading experience on e-ink.
## 3. Idea Evaluation
While I appreciate the desire to add new and exciting features to CrossPoint Reader, CrossPoint Reader is designed to be
+22 -5
View File
@@ -308,13 +308,30 @@ If you use the HTTPS listener, use `https://<server-ip>:7200` (`curl -k` only fo
### 3.7 Sleep Screen
You can customize the sleep screen by placing custom images in specific locations on the SD card:
The **Sleep Screen** setting controls what is displayed when the device goes to sleep:
- **Single Image:** Place a file named `sleep.bmp` in the root directory.
- **Multiple Images:** Create a `sleep` directory in the root of the SD card and place any number of `.bmp` images inside. If images are found in this directory, they will take priority over the `sleep.bmp` file, and one will be randomly selected each time the device sleeps.
| Mode | Behavior |
|------|----------|
| **Dark** (default) | The CrossPoint logo on a dark background. |
| **Light** | The CrossPoint logo on a white background. |
| **Custom** | A custom image from the SD card (see below). Falls back to **Dark** if no custom image is found. |
| **Cover** | The cover of the currently open book. Falls back to **Dark** if no book is open. |
| **Cover + Custom** | The cover of the currently open book. Falls back to **Custom** behavior if no book is open. |
| **None** | A blank screen. |
> [!NOTE]
> You'll need to set the **Sleep Screen** setting to **Custom** in order to use these images.
#### Cover settings
When using **Cover** or **Cover + Custom**, two additional settings apply:
- **Sleep Screen Cover Mode**: **Fit** (scale to fit, white borders) or **Crop** (scale and crop to fill the screen).
- **Sleep Screen Cover Filter**: **None** (grayscale), **Contrast** (black & white), or **Inverted** (inverted black & white).
#### Custom images
To use custom sleep images, set the sleep screen mode to **Custom** or **Cover + Custom**, then place images on the SD card:
- **Multiple Images (recommended):** Create a `.sleep` directory in the root of the SD card and place any number of `.bmp` images inside. One will be randomly selected each time the device sleeps. (A directory named `sleep` is also accepted as a fallback.)
- **Single Image:** Place a file named `sleep.bmp` in the root directory. This is used as a fallback if no valid images are found in the `.sleep`/`sleep` directory.
> [!TIP]
> For best results:
+1
View File
@@ -35,6 +35,7 @@ If you'd like to add your name to this list, please open a PR adding yourself an
- [yeyeto2788](https://github.com/yeyeto2788)
- [Skrzakk](https://github.com/Skrzakk)
- [pablohc](https://github.com/pablohc)
- [DaniPhii](https://github.com/DaniPhii)
## Swedish
- [dawiik](https://github.com/dawiik)
+6 -4
View File
@@ -274,11 +274,13 @@ bool BookMetadataCache::buildBookBin(const std::string& epubPath, const BookMeta
}
bool BookMetadataCache::cleanupTmpFiles() const {
if (Storage.exists((cachePath + tmpSpineBinFile).c_str())) {
Storage.remove((cachePath + tmpSpineBinFile).c_str());
const auto spineBinFile = cachePath + tmpSpineBinFile;
if (Storage.exists(spineBinFile.c_str())) {
Storage.remove(spineBinFile.c_str());
}
if (Storage.exists((cachePath + tmpTocBinFile).c_str())) {
Storage.remove((cachePath + tmpTocBinFile).c_str());
const auto tocBinFile = cachePath + tmpTocBinFile;
if (Storage.exists(tocBinFile.c_str())) {
Storage.remove(tocBinFile.c_str());
}
return true;
}
+19 -9
View File
@@ -144,9 +144,12 @@ std::vector<size_t> ParsedText::computeLineBreaks(const GfxRenderer& renderer, c
return {};
}
// Calculate first line indent (only for left/justified text without extra paragraph spacing)
// Calculate first line indent (only for left/justified text).
// Positive text-indent (paragraph indent) is suppressed when extraParagraphSpacing is on.
// Negative text-indent (hanging indent, e.g. margin-left:3em; text-indent:-1em) always applies —
// it is structural (positions the bullet/marker), not decorative.
const int firstLineIndent =
blockStyle.textIndent > 0 && !extraParagraphSpacing &&
blockStyle.textIndentDefined && (blockStyle.textIndent < 0 || !extraParagraphSpacing) &&
(blockStyle.alignment == CssTextAlign::Justify || blockStyle.alignment == CssTextAlign::Left)
? blockStyle.textIndent
: 0;
@@ -275,9 +278,12 @@ std::vector<size_t> ParsedText::computeHyphenatedLineBreaks(const GfxRenderer& r
const int pageWidth, const int spaceWidth,
std::vector<uint16_t>& wordWidths,
std::vector<bool>& continuesVec) {
// Calculate first line indent (only for left/justified text without extra paragraph spacing)
// Calculate first line indent (only for left/justified text).
// Positive text-indent (paragraph indent) is suppressed when extraParagraphSpacing is on.
// Negative text-indent (hanging indent, e.g. margin-left:3em; text-indent:-1em) always applies —
// it is structural (positions the bullet/marker), not decorative.
const int firstLineIndent =
blockStyle.textIndent > 0 && !extraParagraphSpacing &&
blockStyle.textIndentDefined && (blockStyle.textIndent < 0 || !extraParagraphSpacing) &&
(blockStyle.alignment == CssTextAlign::Justify || blockStyle.alignment == CssTextAlign::Left)
? blockStyle.textIndent
: 0;
@@ -443,10 +449,13 @@ void ParsedText::extractLine(const size_t breakIndex, const int pageWidth, const
const size_t lastBreakAt = breakIndex > 0 ? lineBreakIndices[breakIndex - 1] : 0;
const size_t lineWordCount = lineBreak - lastBreakAt;
// Calculate first line indent (only for left/justified text without extra paragraph spacing)
// Calculate first line indent (only for left/justified text).
// Positive text-indent (paragraph indent) is suppressed when extraParagraphSpacing is on.
// Negative text-indent (hanging indent, e.g. margin-left:3em; text-indent:-1em) always applies —
// it is structural (positions the bullet/marker), not decorative.
const bool isFirstLine = breakIndex == 0;
const int firstLineIndent =
isFirstLine && blockStyle.textIndent > 0 && !extraParagraphSpacing &&
isFirstLine && blockStyle.textIndentDefined && (blockStyle.textIndent < 0 || !extraParagraphSpacing) &&
(blockStyle.alignment == CssTextAlign::Justify || blockStyle.alignment == CssTextAlign::Left)
? blockStyle.textIndent
: 0;
@@ -485,8 +494,9 @@ void ParsedText::extractLine(const size_t breakIndex, const int pageWidth, const
? spareSpace / static_cast<int>(actualGapCount)
: 0;
// Calculate initial x position (first line starts at indent for left/justified text)
auto xpos = static_cast<uint16_t>(firstLineIndent);
// Calculate initial x position (first line starts at indent for left/justified text;
// may be negative for hanging indents, e.g. margin-left:3em; text-indent:-1em).
auto xpos = static_cast<int16_t>(firstLineIndent);
if (blockStyle.alignment == CssTextAlign::Right) {
xpos = effectivePageWidth - lineWordWidthSum - totalNaturalGaps;
} else if (blockStyle.alignment == CssTextAlign::Center) {
@@ -495,7 +505,7 @@ void ParsedText::extractLine(const size_t breakIndex, const int pageWidth, const
// Pre-calculate X positions for words
// Continuation words attach to the previous word with no space before them
std::vector<uint16_t> lineXPos;
std::vector<int16_t> lineXPos;
lineXPos.reserve(lineWordCount);
for (size_t wordIdx = 0; wordIdx < lineWordCount; wordIdx++) {
+14 -9
View File
@@ -10,10 +10,10 @@
#include "parsers/ChapterHtmlSlimParser.h"
namespace {
constexpr uint8_t SECTION_FILE_VERSION = 14;
constexpr uint8_t SECTION_FILE_VERSION = 17;
constexpr uint32_t HEADER_SIZE = sizeof(uint8_t) + sizeof(int) + sizeof(float) + sizeof(bool) + sizeof(uint8_t) +
sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(bool) + sizeof(bool) +
sizeof(uint32_t);
sizeof(uint8_t) + sizeof(uint32_t);
} // namespace
uint32_t Section::onPageComplete(std::unique_ptr<Page> page) {
@@ -36,7 +36,7 @@ uint32_t Section::onPageComplete(std::unique_ptr<Page> page) {
void Section::writeSectionFileHeader(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
const uint16_t viewportHeight, const bool hyphenationEnabled,
const bool embeddedStyle) {
const bool embeddedStyle, const uint8_t imageRendering) {
if (!file) {
LOG_DBG("SCT", "File not open for writing header");
return;
@@ -44,7 +44,7 @@ void Section::writeSectionFileHeader(const int fontId, const float lineCompressi
static_assert(HEADER_SIZE == sizeof(SECTION_FILE_VERSION) + sizeof(fontId) + sizeof(lineCompression) +
sizeof(extraParagraphSpacing) + sizeof(paragraphAlignment) + sizeof(viewportWidth) +
sizeof(viewportHeight) + sizeof(pageCount) + sizeof(hyphenationEnabled) +
sizeof(embeddedStyle) + sizeof(uint32_t),
sizeof(embeddedStyle) + sizeof(imageRendering) + sizeof(uint32_t),
"Header size mismatch");
serialization::writePod(file, SECTION_FILE_VERSION);
serialization::writePod(file, fontId);
@@ -55,13 +55,15 @@ void Section::writeSectionFileHeader(const int fontId, const float lineCompressi
serialization::writePod(file, viewportHeight);
serialization::writePod(file, hyphenationEnabled);
serialization::writePod(file, embeddedStyle);
serialization::writePod(file, imageRendering);
serialization::writePod(file, pageCount); // Placeholder for page count (will be initially 0 when written)
serialization::writePod(file, static_cast<uint32_t>(0)); // Placeholder for LUT offset
}
bool Section::loadSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
const uint16_t viewportHeight, const bool hyphenationEnabled, const bool embeddedStyle) {
const uint16_t viewportHeight, const bool hyphenationEnabled, const bool embeddedStyle,
const uint8_t imageRendering) {
if (!Storage.openFileForRead("SCT", filePath, file)) {
return false;
}
@@ -84,6 +86,7 @@ bool Section::loadSectionFile(const int fontId, const float lineCompression, con
uint8_t fileParagraphAlignment;
bool fileHyphenationEnabled;
bool fileEmbeddedStyle;
uint8_t fileImageRendering;
serialization::readPod(file, fileFontId);
serialization::readPod(file, fileLineCompression);
serialization::readPod(file, fileExtraParagraphSpacing);
@@ -92,11 +95,13 @@ bool Section::loadSectionFile(const int fontId, const float lineCompression, con
serialization::readPod(file, fileViewportHeight);
serialization::readPod(file, fileHyphenationEnabled);
serialization::readPod(file, fileEmbeddedStyle);
serialization::readPod(file, fileImageRendering);
if (fontId != fileFontId || lineCompression != fileLineCompression ||
extraParagraphSpacing != fileExtraParagraphSpacing || paragraphAlignment != fileParagraphAlignment ||
viewportWidth != fileViewportWidth || viewportHeight != fileViewportHeight ||
hyphenationEnabled != fileHyphenationEnabled || embeddedStyle != fileEmbeddedStyle) {
hyphenationEnabled != fileHyphenationEnabled || embeddedStyle != fileEmbeddedStyle ||
imageRendering != fileImageRendering) {
file.close();
LOG_ERR("SCT", "Deserialization failed: Parameters do not match");
clearCache();
@@ -129,7 +134,7 @@ bool Section::clearCache() const {
bool Section::createSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
const uint16_t viewportHeight, const bool hyphenationEnabled, const bool embeddedStyle,
const std::function<void()>& popupFn) {
const uint8_t imageRendering, const std::function<void()>& popupFn) {
const auto localPath = epub->getSpineItem(spineIndex).href;
const auto tmpHtmlPath = epub->getCachePath() + "/.tmp_" + std::to_string(spineIndex) + ".html";
@@ -179,7 +184,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
return false;
}
writeSectionFileHeader(fontId, lineCompression, extraParagraphSpacing, paragraphAlignment, viewportWidth,
viewportHeight, hyphenationEnabled, embeddedStyle);
viewportHeight, hyphenationEnabled, embeddedStyle, imageRendering);
std::vector<uint32_t> lut = {};
// Derive the content base directory and image cache path prefix for the parser
@@ -201,7 +206,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
epub, tmpHtmlPath, renderer, fontId, lineCompression, extraParagraphSpacing, paragraphAlignment, viewportWidth,
viewportHeight, hyphenationEnabled,
[this, &lut](std::unique_ptr<Page> page) { lut.emplace_back(this->onPageComplete(std::move(page))); },
embeddedStyle, contentBase, imageBasePath, popupFn, cssParser);
embeddedStyle, contentBase, imageBasePath, imageRendering, popupFn, cssParser);
Hyphenator::setPreferredLanguage(epub->getLanguage());
success = visitor.parseAndBuildPages();
+4 -3
View File
@@ -16,7 +16,7 @@ class Section {
void writeSectionFileHeader(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment,
uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled,
bool embeddedStyle);
bool embeddedStyle, uint8_t imageRendering);
uint32_t onPageComplete(std::unique_ptr<Page> page);
public:
@@ -30,10 +30,11 @@ class Section {
filePath(epub->getCachePath() + "/sections/" + std::to_string(spineIndex) + ".bin") {}
~Section() = default;
bool loadSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment,
uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled, bool embeddedStyle);
uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled, bool embeddedStyle,
uint8_t imageRendering);
bool clearCache() const;
bool createSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, uint8_t paragraphAlignment,
uint16_t viewportWidth, uint16_t viewportHeight, bool hyphenationEnabled, bool embeddedStyle,
const std::function<void()>& popupFn = nullptr);
uint8_t imageRendering, const std::function<void()>& popupFn = nullptr);
std::unique_ptr<Page> loadPageFromSectionFile();
};
+1 -1
View File
@@ -74,7 +74,7 @@ bool TextBlock::serialize(FsFile& file) const {
std::unique_ptr<TextBlock> TextBlock::deserialize(FsFile& file) {
uint16_t wc;
std::vector<std::string> words;
std::vector<uint16_t> wordXpos;
std::vector<int16_t> wordXpos;
std::vector<EpdFontFamily::Style> wordStyles;
BlockStyle blockStyle;
+2 -2
View File
@@ -13,12 +13,12 @@
class TextBlock final : public Block {
private:
std::vector<std::string> words;
std::vector<uint16_t> wordXpos;
std::vector<int16_t> wordXpos;
std::vector<EpdFontFamily::Style> wordStyles;
BlockStyle blockStyle;
public:
explicit TextBlock(std::vector<std::string> words, std::vector<uint16_t> word_xpos,
explicit TextBlock(std::vector<std::string> words, std::vector<int16_t> word_xpos,
std::vector<EpdFontFamily::Style> word_styles, const BlockStyle& blockStyle = BlockStyle())
: words(std::move(words)),
wordXpos(std::move(word_xpos)),
@@ -243,7 +243,14 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
}
}
if (!src.empty()) {
// imageRendering: 0=display, 1=placeholder (alt text only), 2=suppress entirely
if (self->imageRendering == 2) {
self->skipUntilDepth = self->depth;
self->depth += 1;
return;
}
if (!src.empty() && self->imageRendering != 1) {
LOG_DBG("EHP", "Found image: src=%s", src.c_str());
{
@@ -278,8 +285,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
int displayWidth = 0;
int displayHeight = 0;
const float emSize =
static_cast<float>(self->renderer.getLineHeight(self->fontId)) * self->lineCompression;
const float emSize = static_cast<float>(self->renderer.getFontAscenderSize(self->fontId));
CssStyle imgStyle = self->cssParser ? self->cssParser->resolveStyle("img", classAttr) : CssStyle{};
// Merge inline style (e.g. style="height: 2em") so it overrides stylesheet rules
if (!styleAttr.empty()) {
@@ -505,7 +511,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
}
}
const float emSize = static_cast<float>(self->renderer.getLineHeight(self->fontId)) * self->lineCompression;
const float emSize = static_cast<float>(self->renderer.getFontAscenderSize(self->fontId));
const auto userAlignmentBlockStyle = BlockStyle::fromCssStyle(
cssStyle, emSize, static_cast<CssTextAlign>(self->paragraphAlignment), self->viewportWidth);
@@ -48,6 +48,7 @@ class ChapterHtmlSlimParser {
bool hyphenationEnabled;
const CssParser* cssParser;
bool embeddedStyle;
uint8_t imageRendering;
std::string contentBase;
std::string imageBasePath;
int imageCounter = 0;
@@ -94,8 +95,8 @@ class ChapterHtmlSlimParser {
const uint16_t viewportHeight, const bool hyphenationEnabled,
const std::function<void(std::unique_ptr<Page>)>& completePageFn,
const bool embeddedStyle, const std::string& contentBase,
const std::string& imageBasePath, const std::function<void()>& popupFn = nullptr,
const CssParser* cssParser = nullptr)
const std::string& imageBasePath, const uint8_t imageRendering = 0,
const std::function<void()>& popupFn = nullptr, const CssParser* cssParser = nullptr)
: epub(epub),
filepath(filepath),
@@ -111,6 +112,7 @@ class ChapterHtmlSlimParser {
popupFn(popupFn),
cssParser(cssParser),
embeddedStyle(embeddedStyle),
imageRendering(imageRendering),
contentBase(contentBase),
imageBasePath(imageBasePath) {}
+3 -5
View File
@@ -36,12 +36,10 @@ ContentOpfParser::~ContentOpfParser() {
if (tempItemStore) {
tempItemStore.close();
}
if (Storage.exists((cachePath + itemCacheFile).c_str())) {
Storage.remove((cachePath + itemCacheFile).c_str());
const auto itemCachePath = cachePath + itemCacheFile;
if (Storage.exists(itemCachePath.c_str())) {
Storage.remove(itemCachePath.c_str());
}
itemIndex.clear();
itemIndex.shrink_to_fit();
useItemIndex = false;
}
size_t ContentOpfParser::write(const uint8_t data) { return write(&data, 1); }
+4
View File
@@ -92,6 +92,10 @@ STR_STATUS_BAR: "Status Bar"
STR_HIDE_BATTERY: "Hide Battery %"
STR_EXTRA_SPACING: "Extra Paragraph Spacing"
STR_TEXT_AA: "Text Anti-Aliasing"
STR_IMAGES: "Images"
STR_IMAGES_DISPLAY: "Display"
STR_IMAGES_PLACEHOLDER: "Placeholder"
STR_IMAGES_SUPPRESS: "Suppress"
STR_SHORT_PWR_BTN: "Short Power Button Click"
STR_ORIENTATION: "Reading Orientation"
STR_FRONT_BTN_LAYOUT: "Front Button Layout"
+29 -6
View File
@@ -63,7 +63,7 @@ STR_MAC_ADDRESS: "MAC-Adresse:"
STR_CHECKING_WIFI: "WLAN prüfen…"
STR_ENTER_WIFI_PASSWORD: "WLAN-Passwort eingeben"
STR_ENTER_TEXT: "Text eingeben"
STR_TO_PREFIX: "bis"
STR_TO_PREFIX: "mit "
STR_CALIBRE_DISCOVERING: "Calibre finden..."
STR_CALIBRE_CONNECTING_TO: "Verbinden mit"
STR_CALIBRE_CONNECTED_TO: "Verbunden mit"
@@ -120,7 +120,7 @@ STR_SELECT_WALLPAPER: "Bildauswahl Standby"
STR_CLEAR_READING_CACHE: "Lese-Cache leeren"
STR_CALIBRE: "Calibre"
STR_USERNAME: "Benutzername"
STR_PASSWORD: "Passwort nötig"
STR_PASSWORD: "Passwort"
STR_SYNC_SERVER_URL: "Sync-Server-URL"
STR_DOCUMENT_MATCHING: "Dateizuordnung"
STR_AUTHENTICATE: "Authentifizieren"
@@ -169,7 +169,7 @@ STR_FRONT_LAYOUT_BCLR: "Zurück, Bst, L, R"
STR_FRONT_LAYOUT_LRBC: "L, R, Zurück, Bst"
STR_FRONT_LAYOUT_LBCR: "L, Zurück, Bst, R"
STR_PREV_NEXT: "Zurück/Weiter"
STR_NEXT_PREV: "Weiter/Zuürck"
STR_NEXT_PREV: "Weiter/Zurück"
STR_BOOKERLY: "Bookerly"
STR_NOTO_SANS: "Noto Sans"
STR_OPEN_DYSLEXIC: "Open Dyslexic"
@@ -236,6 +236,8 @@ STR_DOWNLOAD: "Herunterladen"
STR_RETRY: "Wiederh."
STR_YES: "Ja"
STR_NO: "Nein"
STR_SHOW: "Zeigen"
STR_HIDE: "Ausblenden"
STR_STATE_ON: "An"
STR_STATE_OFF: "Aus"
STR_NOT_SET: "Leer"
@@ -248,6 +250,21 @@ STR_CAPS_OFF: "umsch"
STR_OK_BUTTON: "OK"
STR_SLEEP_COVER_FILTER: "Standby-Coverfilter"
STR_FILTER_CONTRAST: "Kontrast"
STR_CUSTOMISE_STATUS_BAR: "Statusleiste anpassen"
STR_CHAPTER_PAGE_COUNT: "Kapitel-Seitenanzahl"
STR_BOOK_PROGRESS_PERCENTAGE: "Buchfortschritt in %"
STR_PROGRESS_BAR: "Fortschrittsbalken"
STR_PROGRESS_BAR_THICKNESS: "Balkenstärke"
STR_PROGRESS_BAR_THIN: "Dünn"
STR_PROGRESS_BAR_MEDIUM: "Mittel"
STR_PROGRESS_BAR_THICK: "Dick"
STR_BOOK: "Buch"
STR_CHAPTER: "Kapitel"
STR_EXAMPLE_CHAPTER: "Kapitel 21"
STR_EXAMPLE_BOOK: "Buchtitel"
STR_PREVIEW: "Vorschau"
STR_TITLE: "Titel"
STR_BATTERY: "Batterie"
STR_UI_THEME: "System-Design"
STR_THEME_CLASSIC: "Klassisch"
STR_THEME_LYRA: "Lyra"
@@ -280,6 +297,7 @@ STR_GO_TO_PERCENT: "Gehe zu %"
STR_GO_HOME_BUTTON: "Zum Anfang"
STR_SYNC_PROGRESS: "Fortschritt synchronisieren"
STR_DELETE_CACHE: "Buch-Cache leeren"
STR_DISPLAY_QR: "Seite als QR anzeigen"
STR_DELETE: "Löschen"
STR_CHAPTER_PREFIX: "Kapitel:"
STR_PAGES_SEPARATOR: " Seiten | "
@@ -289,7 +307,7 @@ STR_KBD_SHIFT_CAPS: "UMSCH"
STR_KBD_LOCK: "FESTST"
STR_CALIBRE_URL_HINT: "Calibre: URL um /opds ergänzen"
STR_PERCENT_STEP_HINT: "links/rechts: 1% hoch/runter: 10%"
STR_SYNCING_TIME: "Zeit synchonisieren…"
STR_SYNCING_TIME: "Zeit synchronisieren…"
STR_CALC_HASH: "Dokument-Hash berechnen…"
STR_HASH_FAILED: "Dokument-Hash fehlgeschlagen"
STR_FETCH_PROGRESS: "Externen Fortschritt abrufen..."
@@ -302,8 +320,8 @@ STR_LOCAL_LABEL: "Lokal:"
STR_PAGE_OVERALL_FORMAT: " Seite %d, %.2f%% insgesamt"
STR_PAGE_TOTAL_OVERALL_FORMAT: " Seite %d/%d, %.2f%% insgesamt"
STR_DEVICE_FROM_FORMAT: " Von: %s"
STR_APPLY_REMOTE: "Ext. Fortschritt übern."
STR_UPLOAD_LOCAL: "Lokalen Fortschritt hochl."
STR_APPLY_REMOTE: "Externen Fortschritt übernehmen"
STR_UPLOAD_LOCAL: "Lokalen Fortschritt hochladen"
STR_NO_REMOTE_MSG: "Kein externer Fortschritt"
STR_UPLOAD_PROMPT: "Aktuelle Position hochladen?"
STR_UPLOAD_SUCCESS: "Hochgeladen!"
@@ -314,3 +332,8 @@ STR_BOOK_S_STYLE: "Buch-Stil"
STR_EMBEDDED_STYLE: "Eingebetteter Stil"
STR_OPDS_SERVER_URL: "OPDS-Server-URL"
STR_SCREENSHOT_BUTTON: "Screenshot aufnehmen"
STR_FOOTNOTES: "Fußnoten"
STR_NO_FOOTNOTES: "Keine Fußnoten auf dieser Seite"
STR_LINK: "[Link]"
STR_AUTO_TURN_ENABLED: "Auto-Umblättern aktiv: "
STR_AUTO_TURN_PAGES_PER_MIN: "Auto-Umblättern (Seiten/Min.)"
+73 -49
View File
@@ -6,7 +6,7 @@ STR_CROSSPOINT: "CrossPoint"
STR_BOOTING: "Iniciando..."
STR_SLEEPING: "Suspendido"
STR_ENTERING_SLEEP: "Entrando en suspensión"
STR_BROWSE_FILES: "Explorador de Archivos"
STR_BROWSE_FILES: "Explorador de archivos"
STR_FILE_TRANSFER: "Transferir archivos"
STR_SETTINGS_TITLE: "Ajustes"
STR_CALIBRE_LIBRARY: "Biblioteca de Calibre"
@@ -46,11 +46,11 @@ STR_PRESS_ANY_CONTINUE: "Pulse cualquier botón para continuar"
STR_SELECT_HINT: "Izq./Der.: Seleccionar | OK: Confirmar"
STR_HOW_CONNECT: "¿Cómo desea conectarse?"
STR_JOIN_NETWORK: "Unirse a una red"
STR_CREATE_HOTSPOT: "Crear Punto de Acceso"
STR_CREATE_HOTSPOT: "Crear punto de acceso"
STR_JOIN_DESC: "Conectarse a una red Wi-Fi existente"
STR_HOTSPOT_DESC: "Conectarse a este dispositivo"
STR_STARTING_HOTSPOT: "Iniciando Punto de Acceso..."
STR_HOTSPOT_MODE: "Modo Punto de Acceso"
STR_STARTING_HOTSPOT: "Iniciando punto de acceso..."
STR_HOTSPOT_MODE: "Modo punto de acceso"
STR_CONNECT_WIFI_HINT: "Conecte su dispositivo a esta red Wi-Fi"
STR_OPEN_URL_HINT: "Abra esta dirección en su navegador"
STR_OR_HTTP_PREFIX: "o http://"
@@ -65,22 +65,22 @@ STR_ENTER_WIFI_PASSWORD: "Introduzca la contraseña del Wi-Fi"
STR_ENTER_TEXT: "Introduzca el texto"
STR_TO_PREFIX: "a "
STR_CALIBRE_DISCOVERING: "Buscando Calibre..."
STR_CALIBRE_CONNECTING_TO: "Conectándose a"
STR_CALIBRE_CONNECTING_TO: "Conectándose a "
STR_CALIBRE_CONNECTED_TO: "Conectado a "
STR_CALIBRE_WAITING_COMMANDS: "Esperando comandos..."
STR_CONNECTION_FAILED_RETRYING: "(Error de conexión, reintentando...)"
STR_CALIBRE_DISCONNECTED: "Calibre desconectado"
STR_CALIBRE_WAITING_TRANSFER: "Esperando transferencia..."
STR_CALIBRE_TRANSFER_HINT: "Si la transferencia falla, active \\n'Ignorar espacio libre' en la configuración del \\nPlugin Smart Device de Calibre."
STR_CALIBRE_TRANSFER_HINT: "Si la transferencia falla, active\\n\"Ignorar espacio libre\" en la configuración del\\ncomplemento SmartDevice de Calibre."
STR_CALIBRE_RECEIVING: "Recibiendo: "
STR_CALIBRE_RECEIVED: "Recibido: "
STR_CALIBRE_WAITING_MORE: "Esperando más..."
STR_CALIBRE_FAILED_CREATE_FILE: "Error al crear el archivo"
STR_CALIBRE_PASSWORD_REQUIRED: "Contraseña requerida"
STR_CALIBRE_TRANSFER_INTERRUPTED: "Transferencia interrumpida"
STR_CALIBRE_INSTRUCTION_1: "1) Instale el Plugin CrossPoint Reader"
STR_CALIBRE_INSTRUCTION_1: "1) Instale el complemento CrossPoint Reader"
STR_CALIBRE_INSTRUCTION_2: "2) Conéctese a la misma red Wi-Fi"
STR_CALIBRE_INSTRUCTION_3: "3) Desde Calibre seleccione: \"Enviar a dispositivo\""
STR_CALIBRE_INSTRUCTION_3: "3) Desde Calibre, seleccione \"Enviar a dispositivo\""
STR_CALIBRE_INSTRUCTION_4: "\"Permanezca en esta pantalla mientras se envía\""
STR_CAT_DISPLAY: "Pantalla"
STR_CAT_READER: "Lector"
@@ -92,15 +92,15 @@ STR_STATUS_BAR: "Barra de estado"
STR_HIDE_BATTERY: "Ocultar % de batería"
STR_EXTRA_SPACING: "Espaciado entre párrafos"
STR_TEXT_AA: "Suavizado de texto"
STR_SHORT_PWR_BTN: "Función especial botón Power"
STR_SHORT_PWR_BTN: "Toque corto botón encendido"
STR_ORIENTATION: "Orientación"
STR_FRONT_BTN_LAYOUT: "Diseño de los botones frontales"
STR_SIDE_BTN_LAYOUT: "Función botones laterales (Lector)"
STR_SIDE_BTN_LAYOUT: "Función botones laterales (lector)"
STR_LONG_PRESS_SKIP: "Saltar capítulo (pulsación larga)"
STR_FONT_FAMILY: "Tipografía"
STR_EXT_READER_FONT: "Tipografía externa"
STR_EXT_CHINESE_FONT: "Tipografía"
STR_EXT_UI_FONT: "Tipografía (Pantalla)"
STR_EXT_UI_FONT: "Tipografía (interfaz)"
STR_FONT_SIZE: "Tamaño"
STR_LINE_SPACING: "Interlineado"
STR_ASCII_LETTER_SPACING: "Espaciado entre letras ASCII"
@@ -126,7 +126,7 @@ STR_DOCUMENT_MATCHING: "Coincidencia de doc."
STR_AUTHENTICATE: "Autenticar"
STR_KOREADER_USERNAME: "Usuario de KOReader"
STR_KOREADER_PASSWORD: "Contraseña de KOReader"
STR_FILENAME: "Nombre del archivo"
STR_FILENAME: "Nombre de archivo"
STR_BINARY: "Binario"
STR_SET_CREDENTIALS_FIRST: "Configurar credenciales"
STR_WIFI_CONN_FAILED: "Fallo de conexión Wi-Fi"
@@ -142,8 +142,8 @@ STR_CLEAR_CACHE_WARNING_3: "Los libros deberán ser reindexados"
STR_CLEAR_CACHE_WARNING_4: "cuando se vuelvan a abrir."
STR_CLEARING_CACHE: "Borrando caché..."
STR_CACHE_CLEARED: "Caché borrada"
STR_ITEMS_REMOVED: "Elementos eliminados"
STR_FAILED_LOWER: "Error"
STR_ITEMS_REMOVED: "elementos eliminados"
STR_FAILED_LOWER: "falló"
STR_CLEAR_CACHE_FAILED: "No se pudo borrar la caché"
STR_CHECK_SERIAL_OUTPUT: "Consulte los registros del puerto serie"
STR_DARK: "Oscuro"
@@ -184,16 +184,16 @@ STR_JUSTIFY: "Justificado"
STR_ALIGN_LEFT: "Izquierda"
STR_CENTER: "Centro"
STR_ALIGN_RIGHT: "Derecha"
STR_MIN_1: "1 Minuto"
STR_MIN_5: "5 Minutos"
STR_MIN_10: "10 Minutos"
STR_MIN_15: "15 Minutos"
STR_MIN_30: "30 Minutos"
STR_PAGES_1: "1 Página"
STR_PAGES_5: "5 Páginas"
STR_PAGES_10: "10 Páginas"
STR_PAGES_15: "15 Páginas"
STR_PAGES_30: "30 Páginas"
STR_MIN_1: "1 min."
STR_MIN_5: "5 min."
STR_MIN_10: "10 min."
STR_MIN_15: "15 min."
STR_MIN_30: "30 min."
STR_PAGES_1: "1 pág."
STR_PAGES_5: "5 pág."
STR_PAGES_10: "10 pág."
STR_PAGES_15: "15 pág."
STR_PAGES_30: "30 pág."
STR_UPDATE: "Actualizar"
STR_CHECKING_UPDATE: "Verificando actualización..."
STR_NEW_UPDATE: "¡Nueva actualización disponible!"
@@ -203,20 +203,20 @@ STR_UPDATING: "Actualizando..."
STR_NO_UPDATE: "No hay actualizaciones disponibles"
STR_UPDATE_FAILED: "Fallo de actualización"
STR_UPDATE_COMPLETE: "Actualización completada"
STR_POWER_ON_HINT: "Pulse y mantenga presionado el botón de encendido para volver a encender"
STR_POWER_ON_HINT: "Reinicie manteniendo pulsado botón de encendido"
STR_EXTERNAL_FONT: "Fuente externa"
STR_BUILTIN_DISABLED: "Incorporado (Desactivado)"
STR_BUILTIN_DISABLED: "Incorporado (desactivado)"
STR_NO_ENTRIES: "No se encontraron elementos"
STR_DOWNLOADING: "Descargando..."
STR_DOWNLOAD_FAILED: "Fallo de descarga"
STR_ERROR_MSG: "Error"
STR_ERROR_MSG: "Error:"
STR_UNNAMED: "Sin nombre"
STR_NO_SERVER_URL: "No se ha configurado la URL del servidor"
STR_NO_SERVER_URL: "No se configuró URL de servidor"
STR_FETCH_FEED_FAILED: "Fallo al obtener el feed"
STR_PARSE_FEED_FAILED: "Fallo al procesar el feed"
STR_NETWORK_PREFIX: "Red: "
STR_IP_ADDRESS_PREFIX: "IP: "
STR_SCAN_QR_WIFI_HINT: "O escanee el código QR con su teléfono para conectarse a Wi-Fi."
STR_SCAN_QR_WIFI_HINT: "o lea el QR con su tfno. para conectarse al Wi-Fi."
STR_ERROR_GENERAL_FAILURE: "Error: Fallo general"
STR_ERROR_NETWORK_NOT_FOUND: "Error: Red no encontrada"
STR_ERROR_CONNECTION_TIMEOUT: "Error: Tiempo de conexión agotado"
@@ -225,7 +225,8 @@ STR_BACK: "« Atrás"
STR_EXIT: "« Salir"
STR_HOME: "« Inicio"
STR_SAVE: "« Guardar"
STR_SELECT: "Elegir"
STR_SELECT: "Selec."
STR_SELECTED: "Seleccionado"
STR_TOGGLE: "Cambiar"
STR_CONFIRM: "Confirmar"
STR_CANCEL: "Cancelar"
@@ -235,6 +236,8 @@ STR_DOWNLOAD: "Descargar"
STR_RETRY: "Reintentar"
STR_YES: "Sí"
STR_NO: "No"
STR_SHOW: "Mostrar"
STR_HIDE: "Ocultar"
STR_STATE_ON: "Activado"
STR_STATE_OFF: "Desactivado"
STR_NOT_SET: "No configurado"
@@ -247,6 +250,21 @@ STR_CAPS_OFF: "minúsculas"
STR_OK_BUTTON: "OK"
STR_SLEEP_COVER_FILTER: "Filtro de pantalla de suspensión"
STR_FILTER_CONTRAST: "Contraste"
STR_CUSTOMISE_STATUS_BAR: "Personalizar barra de estado"
STR_CHAPTER_PAGE_COUNT: "Contador pág. cap."
STR_BOOK_PROGRESS_PERCENTAGE: "Porcentaje progreso libro"
STR_PROGRESS_BAR: "Barra de progreso"
STR_PROGRESS_BAR_THICKNESS: "Grosor de barra de progreso"
STR_PROGRESS_BAR_THIN: "Fino"
STR_PROGRESS_BAR_MEDIUM: "Medio"
STR_PROGRESS_BAR_THICK: "Ancho"
STR_BOOK: "Libro"
STR_CHAPTER: "Capítulo"
STR_EXAMPLE_CHAPTER: "Capítulo 21"
STR_EXAMPLE_BOOK: "Título del libro"
STR_PREVIEW: "Previsualización"
STR_TITLE: "Título"
STR_BATTERY: "Batería"
STR_UI_THEME: "Interfaz"
STR_THEME_CLASSIC: "Clásico"
STR_THEME_LYRA: "Lyra"
@@ -269,37 +287,38 @@ STR_DEFAULT_VALUE: "Predeterminado"
STR_REMAP_PROMPT: "Pulse un botón frontal para cada función"
STR_UNASSIGNED: "Sin asignar"
STR_ALREADY_ASSIGNED: "Ya asignado"
STR_REMAP_RESET_HINT: "Botón lateral arriba: Restablecer configuración"
STR_REMAP_CANCEL_HINT: "Botón lateral abajo: Anular reconfiguración"
STR_HW_BACK_LABEL: "Atrás (Primer botón)"
STR_HW_CONFIRM_LABEL: "Confirmar (Segundo botón)"
STR_HW_LEFT_LABEL: "Izq. (Tercer botón)"
STR_HW_RIGHT_LABEL: "Der. (Cuarto botón)"
STR_REMAP_RESET_HINT: "Botón lateral arriba: restablecer configuración"
STR_REMAP_CANCEL_HINT: "Botón lateral abajo: anular reconfiguración"
STR_HW_BACK_LABEL: "Atrás (primer botón)"
STR_HW_CONFIRM_LABEL: "Confirmar (segundo botón)"
STR_HW_LEFT_LABEL: "Izq. (tercer botón)"
STR_HW_RIGHT_LABEL: "Der. (cuarto botón)"
STR_GO_TO_PERCENT: "Ir a %"
STR_GO_HOME_BUTTON: "Volver a inicio"
STR_DELETE: "Borrar"
STR_GO_HOME_BUTTON: "Volver al menú Inicio"
STR_SYNC_PROGRESS: "Sincronizar progreso de lectura"
STR_DELETE_CACHE: "Borrar caché del libro"
STR_CHAPTER_PREFIX: "Cap.:"
STR_PAGES_SEPARATOR: " Páginas |"
STR_BOOK_PREFIX: "Libro:"
STR_KBD_SHIFT: "shift"
STR_KBD_SHIFT_CAPS: "SHIFT"
STR_DELETE: "Borrar"
STR_DISPLAY_QR: "Mostrar página como QR"
STR_CHAPTER_PREFIX: "Cap.: "
STR_PAGES_SEPARATOR: " Págs. | "
STR_BOOK_PREFIX: "Libro: "
STR_KBD_SHIFT: "minús."
STR_KBD_SHIFT_CAPS: "MAYÚS."
STR_KBD_LOCK: "BLOQUEAR"
STR_CALIBRE_URL_HINT: "Para Calibre, agregue /opds a su URL"
STR_PERCENT_STEP_HINT: "Izq./Der.: 1% | Subir/Bajar: 10%"
STR_SYNCING_TIME: "Tiempo de sincronización..."
STR_CALC_HASH: "Calculando HASH del documento..."
STR_HASH_FAILED: "No se pudo calcular el HASH del documento"
STR_CALC_HASH: "Calculando hash del documento..."
STR_HASH_FAILED: "No se pudo calcular el hash del documento"
STR_FETCH_PROGRESS: "Recuperando progreso remoto..."
STR_UPLOAD_PROGRESS: "Subiendo progreso..."
STR_NO_CREDENTIALS_MSG: "No se han configurado credenciales"
STR_KOREADER_SETUP_HINT: "Configure una cuenta de KOReader en la configuración"
STR_PROGRESS_FOUND: "¡Progreso encontrado!"
STR_REMOTE_LABEL: "Remoto"
STR_LOCAL_LABEL: "Local"
STR_PAGE_OVERALL_FORMAT: "Página %d, %.2f%% Completada"
STR_PAGE_TOTAL_OVERALL_FORMAT: "Página %d / %d, %.2f%% Completada"
STR_REMOTE_LABEL: "Remoto:"
STR_LOCAL_LABEL: "Local:"
STR_PAGE_OVERALL_FORMAT: "Página %d, %.2f%% completado"
STR_PAGE_TOTAL_OVERALL_FORMAT: "Página %d / %d, %.2f%% completado"
STR_DEVICE_FROM_FORMAT: " De: %s"
STR_APPLY_REMOTE: "Aplicar progreso remoto"
STR_UPLOAD_LOCAL: "Subir progreso local"
@@ -312,4 +331,9 @@ STR_UPLOAD: "Subir"
STR_BOOK_S_STYLE: "Estilo del libro"
STR_EMBEDDED_STYLE: "Estilo integrado"
STR_OPDS_SERVER_URL: "URL del servidor OPDS"
STR_FOOTNOTES: "Pie de página"
STR_NO_FOOTNOTES: "No hay notas al pie de esta página"
STR_LINK: "[enlace]"
STR_SCREENSHOT_BUTTON: "Tomar captura de pantalla"
STR_AUTO_TURN_ENABLED: "Paso pág. automático: "
STR_AUTO_TURN_PAGES_PER_MIN: "Páginas por minuto"
+3
View File
@@ -297,6 +297,7 @@ STR_GO_TO_PERCENT: "Перейти до %"
STR_GO_HOME_BUTTON: "На головну"
STR_SYNC_PROGRESS: "Прогрес синхронізації"
STR_DELETE_CACHE: "Видалити кеш книги"
STR_DELETE: "Видалити"
STR_DISPLAY_QR: "Показати сторінку як QR-код"
STR_CHAPTER_PREFIX: "Розділ: "
STR_PAGES_SEPARATOR: " сторінок | "
@@ -334,3 +335,5 @@ STR_FOOTNOTES: "Зноски"
STR_NO_FOOTNOTES: "На цій сторінці немає зносок"
STR_LINK: "[посилання]"
STR_SCREENSHOT_BUTTON: "Знімок екрана"
STR_AUTO_TURN_ENABLED: "Автоперегортання увімк: "
STR_AUTO_TURN_PAGES_PER_MIN: "Автоперегортання (ст/хв)"
+1
View File
@@ -24,6 +24,7 @@ build_flags =
-DARDUINO_USB_CDC_ON_BOOT=1
-DEINK_DISPLAY_SINGLE_BUFFER_MODE=1
-DDISABLE_FS_H_WARNING=1
-DDESTRUCTOR_CLOSES_FILE=1
# https://libexpat.github.io/doc/api/latest/#XML_GE
-DXML_GE=0
-DXML_CONTEXT_BYTES=1024
+7
View File
@@ -135,6 +135,9 @@ class CrossPointSettings {
// UI Theme
enum UI_THEME { CLASSIC = 0, LYRA = 1, LYRA_3_COVERS = 2 };
// Image rendering in EPUB reader
enum IMAGE_RENDERING { IMAGES_DISPLAY = 0, IMAGES_PLACEHOLDER = 1, IMAGES_SUPPRESS = 2, IMAGE_RENDERING_COUNT };
// Sleep screen settings
uint8_t sleepScreen = DARK;
// Sleep screen cover mode settings
@@ -193,6 +196,10 @@ class CrossPointSettings {
uint8_t fadingFix = 0;
// Use book's embedded CSS styles for EPUB rendering (1 = enabled, 0 = disabled)
uint8_t embeddedStyle = 1;
// Show hidden files/directories (starting with '.') in the file browser (0 = hidden, 1 = show)
uint8_t showHiddenFiles = 0;
// Image rendering mode in EPUB reader
uint8_t imageRendering = IMAGES_DISPLAY;
~CrossPointSettings() = default;
+3
View File
@@ -62,6 +62,9 @@ inline const std::vector<SettingInfo>& getSettingsList() {
StrId::STR_CAT_READER),
SettingInfo::Toggle(StrId::STR_TEXT_AA, &CrossPointSettings::textAntiAliasing, "textAntiAliasing",
StrId::STR_CAT_READER),
SettingInfo::Enum(StrId::STR_IMAGES, &CrossPointSettings::imageRendering,
{StrId::STR_IMAGES_DISPLAY, StrId::STR_IMAGES_PLACEHOLDER, StrId::STR_IMAGES_SUPPRESS},
"imageRendering", StrId::STR_CAT_READER),
// --- Controls ---
SettingInfo::Enum(StrId::STR_SIDE_BTN_LAYOUT, &CrossPointSettings::sideButtonLayout,
{StrId::STR_PREV_NEXT, StrId::STR_NEXT_PREV}, "sideButtonLayout", StrId::STR_CAT_CONTROLS),
+3 -3
View File
@@ -5,8 +5,8 @@
#include "boot_sleep/BootActivity.h"
#include "boot_sleep/SleepActivity.h"
#include "browser/OpdsBookBrowserActivity.h"
#include "home/FileBrowserActivity.h"
#include "home/HomeActivity.h"
#include "home/MyLibraryActivity.h"
#include "home/RecentBooksActivity.h"
#include "network/CrossPointWebServerActivity.h"
#include "reader/ReaderActivity.h"
@@ -169,8 +169,8 @@ void ActivityManager::goToFileTransfer() {
void ActivityManager::goToSettings() { replaceActivity(std::make_unique<SettingsActivity>(renderer, mappedInput)); }
void ActivityManager::goToMyLibrary(std::string path) {
replaceActivity(std::make_unique<MyLibraryActivity>(renderer, mappedInput, std::move(path)));
void ActivityManager::goToFileBrowser(std::string path) {
replaceActivity(std::make_unique<FileBrowserActivity>(renderer, mappedInput, std::move(path)));
}
void ActivityManager::goToRecentBooks() {
+1 -1
View File
@@ -79,7 +79,7 @@ class ActivityManager {
// goTo... functions are convenient wrapper for replaceActivity()
void goToFileTransfer();
void goToSettings();
void goToMyLibrary(std::string path = {});
void goToFileBrowser(std::string path = {});
void goToRecentBooks();
void goToBrowser();
void goToReader(std::string path);
+15 -4
View File
@@ -182,9 +182,20 @@ void SleepActivity::onEnter() {
}
void SleepActivity::renderCustomSleepScreen() const {
// Check if we have a /sleep directory
auto dir = Storage.open("/sleep");
// Check if we have a /.sleep (preferred) or /sleep directory
const char* sleepDir = nullptr;
auto dir = Storage.open("/.sleep");
if (dir && dir.isDirectory()) {
sleepDir = "/.sleep";
} else {
if (dir) dir.close();
dir = Storage.open("/sleep");
if (dir && dir.isDirectory()) {
sleepDir = "/sleep";
}
}
if (sleepDir) {
std::vector<std::string> files;
char name[500];
// collect all valid BMP files
@@ -224,10 +235,10 @@ void SleepActivity::renderCustomSleepScreen() const {
}
APP_STATE.lastSleepImage = randomFileIndex;
APP_STATE.saveToFile();
const auto filename = "/sleep/" + files[randomFileIndex];
const auto filename = std::string(sleepDir) + "/" + files[randomFileIndex];
FsFile file;
if (Storage.openFileForRead("SLP", filename, file)) {
LOG_DBG("SLP", "Randomly loading: /sleep/%s", files[randomFileIndex].c_str());
LOG_DBG("SLP", "Randomly loading: %s/%s", sleepDir, files[randomFileIndex].c_str());
delay(100);
Bitmap bitmap(file, true);
if (bitmap.parseHeaders() == BmpReaderError::Ok) {
@@ -1,4 +1,4 @@
#include "MyLibraryActivity.h"
#include "FileBrowserActivity.h"
#include <Epub.h>
#include <GfxRenderer.h>
@@ -69,7 +69,7 @@ void sortFileList(std::vector<std::string>& strs) {
});
}
void MyLibraryActivity::loadFiles() {
void FileBrowserActivity::loadFiles() {
files.clear();
auto root = Storage.open(basepath.c_str());
@@ -104,7 +104,7 @@ void MyLibraryActivity::loadFiles() {
sortFileList(files);
}
void MyLibraryActivity::onEnter() {
void FileBrowserActivity::onEnter() {
Activity::onEnter();
loadFiles();
@@ -113,20 +113,20 @@ void MyLibraryActivity::onEnter() {
requestUpdate();
}
void MyLibraryActivity::onExit() {
void FileBrowserActivity::onExit() {
Activity::onExit();
files.clear();
}
void MyLibraryActivity::clearFileMetadata(const std::string& fullPath) {
void FileBrowserActivity::clearFileMetadata(const std::string& fullPath) {
// Only clear cache for .epub files
if (StringUtils::checkFileExtension(fullPath, ".epub")) {
Epub(fullPath, "/.crosspoint").clearCache();
LOG_DBG("MyLibrary", "Cleared metadata cache for: %s", fullPath.c_str());
LOG_DBG("FileBrowser", "Cleared metadata cache for: %s", fullPath.c_str());
}
}
void MyLibraryActivity::loop() {
void FileBrowserActivity::loop() {
// Long press BACK (1s+) goes to root folder
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= GO_HOME_MS &&
basepath != "/") {
@@ -152,10 +152,10 @@ void MyLibraryActivity::loop() {
auto handler = [this, fullPath](const ActivityResult& res) {
if (!res.isCancelled) {
LOG_DBG("MyLibrary", "Attempting to delete: %s", fullPath.c_str());
LOG_DBG("FileBrowser", "Attempting to delete: %s", fullPath.c_str());
clearFileMetadata(fullPath);
if (Storage.remove(fullPath.c_str())) {
LOG_DBG("MyLibrary", "Deleted successfully");
LOG_DBG("FileBrowser", "Deleted successfully");
loadFiles();
if (files.empty()) {
selectorIndex = 0;
@@ -166,10 +166,10 @@ void MyLibraryActivity::loop() {
requestUpdate(true);
} else {
LOG_ERR("MyLibrary", "Failed to delete file: %s", fullPath.c_str());
LOG_ERR("FileBrowser", "Failed to delete file: %s", fullPath.c_str());
}
} else {
LOG_DBG("MyLibrary", "Delete cancelled by user");
LOG_DBG("FileBrowser", "Delete cancelled by user");
}
};
@@ -244,7 +244,7 @@ std::string getFileName(std::string filename) {
return filename.substr(0, pos);
}
void MyLibraryActivity::render(RenderLock&&) {
void FileBrowserActivity::render(RenderLock&&) {
renderer.clearScreen();
const auto pageWidth = renderer.getScreenWidth();
@@ -274,7 +274,7 @@ void MyLibraryActivity::render(RenderLock&&) {
renderer.displayBuffer();
}
size_t MyLibraryActivity::findEntry(const std::string& name) const {
size_t FileBrowserActivity::findEntry(const std::string& name) const {
for (size_t i = 0; i < files.size(); i++)
if (files[i] == name) return i;
return 0;
@@ -8,7 +8,7 @@
#include "RecentBooksStore.h"
#include "util/ButtonNavigator.h"
class MyLibraryActivity final : public Activity {
class FileBrowserActivity final : public Activity {
private:
// Deletion
void clearFileMetadata(const std::string& fullPath);
@@ -26,8 +26,8 @@ class MyLibraryActivity final : public Activity {
size_t findEntry(const std::string& name) const;
public:
explicit MyLibraryActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string initialPath = "/")
: Activity("MyLibrary", renderer, mappedInput), basepath(initialPath.empty() ? "/" : std::move(initialPath)) {}
explicit FileBrowserActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, std::string initialPath = "/")
: Activity("FileBrowser", renderer, mappedInput), basepath(initialPath.empty() ? "/" : std::move(initialPath)) {}
void onEnter() override;
void onExit() override;
void loop() override;
+6 -6
View File
@@ -20,7 +20,7 @@
#include "util/StringUtils.h"
int HomeActivity::getMenuItemCount() const {
int count = 4; // My Library, Recents, File transfer, Settings
int count = 4; // File Browser, Recents, File transfer, Settings
if (!recentBooks.empty()) {
count += recentBooks.size();
}
@@ -189,7 +189,7 @@ void HomeActivity::loop() {
// Calculate dynamic indices based on which options are available
int idx = 0;
int menuSelectedIndex = selectorIndex - static_cast<int>(recentBooks.size());
const int myLibraryIdx = idx++;
const int fileBrowserIdx = idx++;
const int recentsIdx = idx++;
const int opdsLibraryIdx = hasOpdsUrl ? idx++ : -1;
const int fileTransferIdx = idx++;
@@ -197,8 +197,8 @@ void HomeActivity::loop() {
if (selectorIndex < recentBooks.size()) {
onSelectBook(recentBooks[selectorIndex].path);
} else if (menuSelectedIndex == myLibraryIdx) {
onMyLibraryOpen();
} else if (menuSelectedIndex == fileBrowserIdx) {
onFileBrowserOpen();
} else if (menuSelectedIndex == recentsIdx) {
onRecentsOpen();
} else if (menuSelectedIndex == opdsLibraryIdx) {
@@ -231,7 +231,7 @@ void HomeActivity::render(RenderLock&&) {
std::vector<UIIcon> menuIcons = {Folder, Recent, Transfer, Settings};
if (hasOpdsUrl) {
// Insert OPDS Browser after My Library
// Insert OPDS Browser after File Browser
menuItems.insert(menuItems.begin() + 2, tr(STR_OPDS_BROWSER));
menuIcons.insert(menuIcons.begin() + 2, Library);
}
@@ -261,7 +261,7 @@ void HomeActivity::render(RenderLock&&) {
void HomeActivity::onSelectBook(const std::string& path) { activityManager.goToReader(path); }
void HomeActivity::onMyLibraryOpen() { activityManager.goToMyLibrary(); }
void HomeActivity::onFileBrowserOpen() { activityManager.goToFileBrowser(); }
void HomeActivity::onRecentsOpen() { activityManager.goToRecentBooks(); }
+2 -2
View File
@@ -3,7 +3,7 @@
#include <vector>
#include "../Activity.h"
#include "./MyLibraryActivity.h"
#include "./FileBrowserActivity.h"
#include "util/ButtonNavigator.h"
struct RecentBook;
@@ -21,7 +21,7 @@ class HomeActivity final : public Activity {
uint8_t* coverBuffer = nullptr; // HomeActivity's own buffer for cover image
std::vector<RecentBook> recentBooks;
void onSelectBook(const std::string& path);
void onMyLibraryOpen();
void onFileBrowserOpen();
void onRecentsOpen();
void onSettingsOpen();
void onFileTransferOpen();
+5 -3
View File
@@ -182,7 +182,7 @@ void EpubReaderActivity::loop() {
// Long press BACK (1s+) goes to file selection
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= goHomeMs) {
activityManager.goToMyLibrary(epub ? epub->getPath() : "");
activityManager.goToFileBrowser(epub ? epub->getPath() : "");
return;
}
@@ -573,14 +573,16 @@ void EpubReaderActivity::render(RenderLock&& lock) {
if (!section->loadSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle)) {
viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle,
SETTINGS.imageRendering)) {
LOG_DBG("ERS", "Cache not found, building...");
const auto popupFn = [this]() { GUI.drawPopup(renderer, tr(STR_INDEXING)); };
if (!section->createSectionFile(SETTINGS.getReaderFontId(), SETTINGS.getReaderLineCompression(),
SETTINGS.extraParagraphSpacing, SETTINGS.paragraphAlignment, viewportWidth,
viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle, popupFn)) {
viewportHeight, SETTINGS.hyphenationEnabled, SETTINGS.embeddedStyle,
SETTINGS.imageRendering, popupFn)) {
LOG_ERR("ERS", "Failed to persist page data to SD");
section.reset();
return;
+1 -19
View File
@@ -204,25 +204,7 @@ void KOReaderSyncActivity::onEnter() {
// Check if already connected (e.g. from settings page auth)
if (WiFi.status() == WL_CONNECTED) {
LOG_DBG("KOSync", "Already connected to WiFi");
state = SYNCING;
statusMessage = tr(STR_SYNCING_TIME);
requestUpdate(true);
// Perform sync directly (will be handled in loop)
xTaskCreate(
[](void* param) {
auto* self = static_cast<KOReaderSyncActivity*>(param);
// Sync time first
syncTimeWithNTP();
{
RenderLock lock(*self);
self->statusMessage = tr(STR_CALC_HASH);
}
self->requestUpdate(true);
self->performSync();
vTaskDelete(nullptr);
},
"SyncTask", 4096, this, 1, nullptr);
onWifiSelectionComplete(true);
return;
}
+1 -1
View File
@@ -80,7 +80,7 @@ std::unique_ptr<Txt> ReaderActivity::loadTxt(const std::string& path) {
void ReaderActivity::goToLibrary(const std::string& fromBookPath) {
// If coming from a book, start in that book's folder; otherwise start from root
auto initialPath = fromBookPath.empty() ? "/" : extractFolderPath(fromBookPath);
activityManager.goToMyLibrary(std::move(initialPath));
activityManager.goToFileBrowser(std::move(initialPath));
}
void ReaderActivity::onGoToEpubReader(std::unique_ptr<Epub> epub) {
+1 -1
View File
@@ -2,7 +2,7 @@
#include <memory>
#include "../Activity.h"
#include "activities/home/MyLibraryActivity.h"
#include "activities/home/FileBrowserActivity.h"
class Epub;
class Xtc;
+1 -1
View File
@@ -135,7 +135,7 @@ void TxtReaderActivity::onExit() {
void TxtReaderActivity::loop() {
// Long press BACK (1s+) goes to file selection
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= goHomeMs) {
activityManager.goToMyLibrary(txt ? txt->getPath() : "");
activityManager.goToFileBrowser(txt ? txt->getPath() : "");
return;
}
+1 -1
View File
@@ -70,7 +70,7 @@ void XtcReaderActivity::loop() {
// Long press BACK (1s+) goes to file selection
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= goHomeMs) {
activityManager.goToMyLibrary(xtc ? xtc->getPath() : "");
activityManager.goToFileBrowser(xtc ? xtc->getPath() : "");
return;
}
@@ -51,23 +51,9 @@ void KOReaderAuthActivity::performAuthentication() {
void KOReaderAuthActivity::onEnter() {
Activity::onEnter();
// Turn on WiFi
WiFi.mode(WIFI_STA);
// Check if already connected
if (WiFi.status() == WL_CONNECTED) {
state = AUTHENTICATING;
statusMessage = tr(STR_AUTHENTICATING);
requestUpdate();
// Perform authentication in a separate task
xTaskCreate(
[](void* param) {
auto* self = static_cast<KOReaderAuthActivity*>(param);
self->performAuthentication();
vTaskDelete(nullptr);
},
"AuthTask", 4096, this, 1, nullptr);
onWifiSelectionComplete(true);
return;
}