mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-07 16:17:38 +03:00
feature: adding a default wifi option
This commit is contained in:
commit
e4bfcf0d77
19
.github/PULL_REQUEST_TEMPLATE.md
vendored
19
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,9 +1,22 @@
|
|||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
* **What is the goal of this PR?** (e.g., Fixes a bug in the user authentication module, Implements the new feature for
|
* **What is the goal of this PR?** (e.g., Implements the new feature for file uploading.)
|
||||||
file uploading.)
|
|
||||||
* **What changes are included?**
|
* **What changes are included?**
|
||||||
|
|
||||||
## Additional Context
|
## Additional Context
|
||||||
|
|
||||||
* Add any other information that might be helpful for the reviewer (e.g., performance implications, potential risks, specific areas to focus on).
|
* Add any other information that might be helpful for the reviewer (e.g., performance implications, potential risks,
|
||||||
|
specific areas to focus on).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### AI Usage
|
||||||
|
|
||||||
|
While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it
|
||||||
|
helps set the right context for reviewers.
|
||||||
|
|
||||||
|
Did you use AI tools to help write this code?
|
||||||
|
|
||||||
|
- [ ] Yes
|
||||||
|
- [ ] Partially
|
||||||
|
- [ ] No
|
||||||
|
|||||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -7,11 +7,11 @@ name: CI
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- uses: actions/setup-python@v6
|
- uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.14'
|
python-version: '3.14'
|
||||||
|
|||||||
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@ -7,17 +7,18 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build-release:
|
build-release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cache/pip
|
~/.cache/pip
|
||||||
~/.platformio/.cache
|
~/.platformio/.cache
|
||||||
key: ${{ runner.os }}-pio
|
key: ${{ runner.os }}-pio
|
||||||
|
|
||||||
- uses: actions/setup-python@v6
|
- uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.14'
|
python-version: '3.14'
|
||||||
|
|||||||
@ -67,6 +67,7 @@ The Settings screen allows you to configure the device's behavior. There are a f
|
|||||||
- "Light" - The same default sleep screen, on a white background
|
- "Light" - The same default sleep screen, on a white background
|
||||||
- "Custom" - Custom images from the SD card, see [Sleep Screen](#36-sleep-screen) below for more information
|
- "Custom" - Custom images from the SD card, see [Sleep Screen](#36-sleep-screen) below for more information
|
||||||
- "Cover" - The book cover image (Note: this is experimental and may not work as expected)
|
- "Cover" - The book cover image (Note: this is experimental and may not work as expected)
|
||||||
|
- "Blank" - A blank screen
|
||||||
- **Status Bar**: Configure the status bar displayed while reading:
|
- **Status Bar**: Configure the status bar displayed while reading:
|
||||||
- "None" - No status bar
|
- "None" - No status bar
|
||||||
- "No Progress" - Show status bar without reading progress
|
- "No Progress" - Show status bar without reading progress
|
||||||
|
|||||||
@ -345,11 +345,14 @@ const std::string& Epub::getAuthor() const {
|
|||||||
return bookMetadataCache->coreMetadata.author;
|
return bookMetadataCache->coreMetadata.author;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Epub::getCoverBmpPath() const { return cachePath + "/cover.bmp"; }
|
std::string Epub::getCoverBmpPath(bool cropped) const {
|
||||||
|
const auto coverFileName = "cover" + cropped ? "_crop" : "";
|
||||||
|
return cachePath + "/" + coverFileName + ".bmp";
|
||||||
|
}
|
||||||
|
|
||||||
bool Epub::generateCoverBmp() const {
|
bool Epub::generateCoverBmp(bool cropped) const {
|
||||||
// Already generated, return true
|
// Already generated, return true
|
||||||
if (SdMan.exists(getCoverBmpPath().c_str())) {
|
if (SdMan.exists(getCoverBmpPath(cropped).c_str())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,7 +384,7 @@ bool Epub::generateCoverBmp() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FsFile coverBmp;
|
FsFile coverBmp;
|
||||||
if (!SdMan.openFileForWrite("EBP", getCoverBmpPath(), coverBmp)) {
|
if (!SdMan.openFileForWrite("EBP", getCoverBmpPath(cropped), coverBmp)) {
|
||||||
coverJpg.close();
|
coverJpg.close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -392,7 +395,7 @@ bool Epub::generateCoverBmp() const {
|
|||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
Serial.printf("[%lu] [EBP] Failed to generate BMP from JPG cover image\n", millis());
|
Serial.printf("[%lu] [EBP] Failed to generate BMP from JPG cover image\n", millis());
|
||||||
SdMan.remove(getCoverBmpPath().c_str());
|
SdMan.remove(getCoverBmpPath(cropped).c_str());
|
||||||
}
|
}
|
||||||
Serial.printf("[%lu] [EBP] Generated BMP from JPG cover image, success: %s\n", millis(), success ? "yes" : "no");
|
Serial.printf("[%lu] [EBP] Generated BMP from JPG cover image, success: %s\n", millis(), success ? "yes" : "no");
|
||||||
return success;
|
return success;
|
||||||
|
|||||||
@ -44,8 +44,8 @@ class Epub {
|
|||||||
const std::string& getPath() const;
|
const std::string& getPath() const;
|
||||||
const std::string& getTitle() const;
|
const std::string& getTitle() const;
|
||||||
const std::string& getAuthor() const;
|
const std::string& getAuthor() const;
|
||||||
std::string getCoverBmpPath() const;
|
std::string getCoverBmpPath(bool cropped = false) const;
|
||||||
bool generateCoverBmp() const;
|
bool generateCoverBmp(bool cropped = false) const;
|
||||||
uint8_t* readItemContentsToBytes(const std::string& itemHref, size_t* size = nullptr,
|
uint8_t* readItemContentsToBytes(const std::string& itemHref, size_t* size = nullptr,
|
||||||
bool trailingNullByte = false) const;
|
bool trailingNullByte = false) const;
|
||||||
bool readItemContentsToStream(const std::string& itemHref, Print& out, size_t chunkSize) const;
|
bool readItemContentsToStream(const std::string& itemHref, Print& out, size_t chunkSize) const;
|
||||||
|
|||||||
@ -46,6 +46,7 @@ bool CrossPointSettings::saveToFile() const {
|
|||||||
serialization::writePod(outputFile, sleepScreenCoverMode);
|
serialization::writePod(outputFile, sleepScreenCoverMode);
|
||||||
serialization::writeString(outputFile, std::string(opdsServerUrl));
|
serialization::writeString(outputFile, std::string(opdsServerUrl));
|
||||||
serialization::writePod(outputFile, textAntiAliasing);
|
serialization::writePod(outputFile, textAntiAliasing);
|
||||||
|
serialization::writePod(outputFile, hideBatteryPercentage);
|
||||||
outputFile.close();
|
outputFile.close();
|
||||||
|
|
||||||
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
|
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
|
||||||
@ -110,6 +111,8 @@ bool CrossPointSettings::loadFromFile() {
|
|||||||
}
|
}
|
||||||
serialization::readPod(inputFile, textAntiAliasing);
|
serialization::readPod(inputFile, textAntiAliasing);
|
||||||
if (++settingsRead >= fileSettingsCount) break;
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
|
serialization::readPod(inputFile, hideBatteryPercentage);
|
||||||
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
} while (false);
|
} while (false);
|
||||||
|
|
||||||
inputFile.close();
|
inputFile.close();
|
||||||
|
|||||||
@ -52,6 +52,12 @@ class CrossPointSettings {
|
|||||||
// E-ink refresh frequency (pages between full refreshes)
|
// E-ink refresh frequency (pages between full refreshes)
|
||||||
enum REFRESH_FREQUENCY { REFRESH_1 = 0, REFRESH_5 = 1, REFRESH_10 = 2, REFRESH_15 = 3, REFRESH_30 = 4 };
|
enum REFRESH_FREQUENCY { REFRESH_1 = 0, REFRESH_5 = 1, REFRESH_10 = 2, REFRESH_15 = 3, REFRESH_30 = 4 };
|
||||||
|
|
||||||
|
// Short power button press actions
|
||||||
|
enum SHORT_PWRBTN { IGNORE = 0, SLEEP = 1, PAGE_TURN = 2 };
|
||||||
|
|
||||||
|
// Hide battery percentage
|
||||||
|
enum HIDE_BATTERY_PERCENTAGE { HIDE_NEVER = 0, HIDE_READER = 1, HIDE_ALWAYS = 2 };
|
||||||
|
|
||||||
// Sleep screen settings
|
// Sleep screen settings
|
||||||
uint8_t sleepScreen = DARK;
|
uint8_t sleepScreen = DARK;
|
||||||
// Sleep screen cover mode settings
|
// Sleep screen cover mode settings
|
||||||
@ -61,8 +67,8 @@ class CrossPointSettings {
|
|||||||
// Text rendering settings
|
// Text rendering settings
|
||||||
uint8_t extraParagraphSpacing = 1;
|
uint8_t extraParagraphSpacing = 1;
|
||||||
uint8_t textAntiAliasing = 1;
|
uint8_t textAntiAliasing = 1;
|
||||||
// Duration of the power button press
|
// Short power button click behaviour
|
||||||
uint8_t shortPwrBtn = 0;
|
uint8_t shortPwrBtn = IGNORE;
|
||||||
// EPUB reading orientation settings
|
// EPUB reading orientation settings
|
||||||
// 0 = portrait (default), 1 = landscape clockwise, 2 = inverted, 3 = landscape counter-clockwise
|
// 0 = portrait (default), 1 = landscape clockwise, 2 = inverted, 3 = landscape counter-clockwise
|
||||||
uint8_t orientation = PORTRAIT;
|
uint8_t orientation = PORTRAIT;
|
||||||
@ -82,13 +88,17 @@ class CrossPointSettings {
|
|||||||
uint8_t screenMargin = 5;
|
uint8_t screenMargin = 5;
|
||||||
// OPDS browser settings
|
// OPDS browser settings
|
||||||
char opdsServerUrl[128] = "";
|
char opdsServerUrl[128] = "";
|
||||||
|
// Hide battery percentage
|
||||||
|
uint8_t hideBatteryPercentage = HIDE_NEVER;
|
||||||
|
|
||||||
~CrossPointSettings() = default;
|
~CrossPointSettings() = default;
|
||||||
|
|
||||||
// Get singleton instance
|
// Get singleton instance
|
||||||
static CrossPointSettings& getInstance() { return instance; }
|
static CrossPointSettings& getInstance() { return instance; }
|
||||||
|
|
||||||
uint16_t getPowerButtonDuration() const { return shortPwrBtn ? 10 : 400; }
|
uint16_t getPowerButtonDuration() const {
|
||||||
|
return (shortPwrBtn == CrossPointSettings::SHORT_PWRBTN::SLEEP) ? 10 : 400;
|
||||||
|
}
|
||||||
int getReaderFontId() const;
|
int getReaderFontId() const;
|
||||||
|
|
||||||
bool saveToFile() const;
|
bool saveToFile() const;
|
||||||
|
|||||||
@ -8,10 +8,11 @@
|
|||||||
#include "Battery.h"
|
#include "Battery.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|
||||||
void ScreenComponents::drawBattery(const GfxRenderer& renderer, const int left, const int top) {
|
void ScreenComponents::drawBattery(const GfxRenderer& renderer, const int left, const int top,
|
||||||
|
const bool showPercentage) {
|
||||||
// Left aligned battery icon and percentage
|
// Left aligned battery icon and percentage
|
||||||
const uint16_t percentage = battery.readPercentage();
|
const uint16_t percentage = battery.readPercentage();
|
||||||
const auto percentageText = std::to_string(percentage) + "%";
|
const auto percentageText = showPercentage ? std::to_string(percentage) + "%" : "";
|
||||||
renderer.drawText(SMALL_FONT_ID, left + 20, top, percentageText.c_str());
|
renderer.drawText(SMALL_FONT_ID, left + 20, top, percentageText.c_str());
|
||||||
|
|
||||||
// 1 column on left, 2 columns on right, 5 columns of battery body
|
// 1 column on left, 2 columns on right, 5 columns of battery body
|
||||||
|
|||||||
@ -7,7 +7,7 @@ class GfxRenderer;
|
|||||||
|
|
||||||
class ScreenComponents {
|
class ScreenComponents {
|
||||||
public:
|
public:
|
||||||
static void drawBattery(const GfxRenderer& renderer, int left, int top);
|
static void drawBattery(const GfxRenderer& renderer, int left, int top, bool showPercentage = true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw a progress bar with percentage text.
|
* Draw a progress bar with percentage text.
|
||||||
|
|||||||
@ -199,6 +199,7 @@ void SleepActivity::renderCoverSleepScreen() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string coverBmpPath;
|
std::string coverBmpPath;
|
||||||
|
bool cropped = SETTINGS.sleepScreenCoverMode == CrossPointSettings::SLEEP_SCREEN_COVER_MODE::CROP;
|
||||||
|
|
||||||
if (StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".xtc") ||
|
if (StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".xtc") ||
|
||||||
StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".xtch")) {
|
StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".xtch")) {
|
||||||
@ -223,12 +224,12 @@ void SleepActivity::renderCoverSleepScreen() const {
|
|||||||
return renderDefaultSleepScreen();
|
return renderDefaultSleepScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lastEpub.generateCoverBmp()) {
|
if (!lastEpub.generateCoverBmp(cropped)) {
|
||||||
Serial.println("[SLP] Failed to generate cover bmp");
|
Serial.println("[SLP] Failed to generate cover bmp");
|
||||||
return renderDefaultSleepScreen();
|
return renderDefaultSleepScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
coverBmpPath = lastEpub.getCoverBmpPath();
|
coverBmpPath = lastEpub.getCoverBmpPath(cropped);
|
||||||
} else {
|
} else {
|
||||||
return renderDefaultSleepScreen();
|
return renderDefaultSleepScreen();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ void OpdsBookBrowserActivity::taskTrampoline(void* param) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OpdsBookBrowserActivity::onEnter() {
|
void OpdsBookBrowserActivity::onEnter() {
|
||||||
Activity::onEnter();
|
ActivityWithSubactivity::onEnter();
|
||||||
|
|
||||||
renderingMutex = xSemaphoreCreateMutex();
|
renderingMutex = xSemaphoreCreateMutex();
|
||||||
state = BrowserState::CHECK_WIFI;
|
state = BrowserState::CHECK_WIFI;
|
||||||
@ -50,7 +50,7 @@ void OpdsBookBrowserActivity::onEnter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OpdsBookBrowserActivity::onExit() {
|
void OpdsBookBrowserActivity::onExit() {
|
||||||
Activity::onExit();
|
ActivityWithSubactivity::onExit();
|
||||||
|
|
||||||
// Turn off WiFi when exiting
|
// Turn off WiFi when exiting
|
||||||
WiFi.mode(WIFI_OFF);
|
WiFi.mode(WIFI_OFF);
|
||||||
@ -67,18 +67,28 @@ void OpdsBookBrowserActivity::onExit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OpdsBookBrowserActivity::loop() {
|
void OpdsBookBrowserActivity::loop() {
|
||||||
if (subActivity) {
|
// Handle WiFi selection subactivity
|
||||||
subActivity->loop();
|
if (state == BrowserState::WIFI_SELECTION) {
|
||||||
|
ActivityWithSubactivity::loop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle error state - Confirm retries, Back goes back or home
|
// Handle error state - Confirm retries, Back goes back or home
|
||||||
if (state == BrowserState::ERROR) {
|
if (state == BrowserState::ERROR) {
|
||||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||||
state = BrowserState::LOADING;
|
// Check if WiFi is still connected
|
||||||
statusMessage = "Loading...";
|
if (WiFi.status() == WL_CONNECTED && WiFi.localIP() != IPAddress(0, 0, 0, 0)) {
|
||||||
updateRequired = true;
|
// WiFi connected - just retry fetching the feed
|
||||||
fetchFeed(currentPath);
|
Serial.printf("[%lu] [OPDS] Retry: WiFi connected, retrying fetch\n", millis());
|
||||||
|
state = BrowserState::LOADING;
|
||||||
|
statusMessage = "Loading...";
|
||||||
|
updateRequired = true;
|
||||||
|
fetchFeed(currentPath);
|
||||||
|
} else {
|
||||||
|
// WiFi not connected - launch WiFi selection
|
||||||
|
Serial.printf("[%lu] [OPDS] Retry: WiFi not connected, launching selection\n", millis());
|
||||||
|
launchWifiSelection();
|
||||||
|
}
|
||||||
} else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
} else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||||
navigateBack();
|
navigateBack();
|
||||||
}
|
}
|
||||||
@ -362,11 +372,20 @@ void OpdsBookBrowserActivity::downloadBook(const OpdsEntry& book) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OpdsBookBrowserActivity::checkAndConnectWifi() {
|
void OpdsBookBrowserActivity::checkAndConnectWifi() {
|
||||||
|
// Already connected? Verify connection is valid by checking IP
|
||||||
|
if (WiFi.status() == WL_CONNECTED && WiFi.localIP() != IPAddress(0, 0, 0, 0)) {
|
||||||
|
state = BrowserState::LOADING;
|
||||||
|
statusMessage = "Loading...";
|
||||||
|
updateRequired = true;
|
||||||
|
fetchFeed(currentPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to connect to default WiFi if available
|
||||||
WIFI_STORE.loadFromFile();
|
WIFI_STORE.loadFromFile();
|
||||||
const bool hasDefaultSSID = !WIFI_STORE.getDefaultSSID().empty();
|
const bool hasDefaultSSID = !WIFI_STORE.getDefaultSSID().empty();
|
||||||
const bool alreadyConnected = (WiFi.status() == WL_CONNECTED);
|
|
||||||
|
|
||||||
if (hasDefaultSSID && !alreadyConnected) {
|
if (hasDefaultSSID) {
|
||||||
statusMessage = "Connecting to WiFi...";
|
statusMessage = "Connecting to WiFi...";
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
}
|
}
|
||||||
@ -379,5 +398,37 @@ void OpdsBookBrowserActivity::checkAndConnectWifi() {
|
|||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
fetchFeed(currentPath);
|
fetchFeed(currentPath);
|
||||||
},
|
},
|
||||||
[this]() { onGoHome(); });
|
[this]() {
|
||||||
|
// User cancelled WiFi selection - go home
|
||||||
|
onGoHome();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpdsBookBrowserActivity::launchWifiSelection() {
|
||||||
|
state = BrowserState::WIFI_SELECTION;
|
||||||
|
updateRequired = true;
|
||||||
|
|
||||||
|
enterNewActivity(new WifiSelectionActivity(renderer, mappedInput,
|
||||||
|
[this](const bool connected) { onWifiSelectionComplete(connected); }));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpdsBookBrowserActivity::onWifiSelectionComplete(const bool connected) {
|
||||||
|
exitActivity();
|
||||||
|
|
||||||
|
if (connected) {
|
||||||
|
Serial.printf("[%lu] [OPDS] WiFi connected via selection, fetching feed\n", millis());
|
||||||
|
state = BrowserState::LOADING;
|
||||||
|
statusMessage = "Loading...";
|
||||||
|
updateRequired = true;
|
||||||
|
fetchFeed(currentPath);
|
||||||
|
} else {
|
||||||
|
Serial.printf("[%lu] [OPDS] WiFi selection cancelled/failed\n", millis());
|
||||||
|
// Force disconnect to ensure clean state for next retry
|
||||||
|
// This prevents stale connection status from interfering
|
||||||
|
WiFi.disconnect();
|
||||||
|
WiFi.mode(WIFI_OFF);
|
||||||
|
state = BrowserState::ERROR;
|
||||||
|
errorMessage = "WiFi connection failed";
|
||||||
|
updateRequired = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,15 +13,17 @@
|
|||||||
/**
|
/**
|
||||||
* Activity for browsing and downloading books from an OPDS server.
|
* Activity for browsing and downloading books from an OPDS server.
|
||||||
* Supports navigation through catalog hierarchy and downloading EPUBs.
|
* Supports navigation through catalog hierarchy and downloading EPUBs.
|
||||||
|
* When WiFi connection fails, launches WiFi selection to let user connect.
|
||||||
*/
|
*/
|
||||||
class OpdsBookBrowserActivity final : public ActivityWithSubactivity {
|
class OpdsBookBrowserActivity final : public ActivityWithSubactivity {
|
||||||
public:
|
public:
|
||||||
enum class BrowserState {
|
enum class BrowserState {
|
||||||
CHECK_WIFI, // Checking WiFi connection
|
CHECK_WIFI, // Checking WiFi connection
|
||||||
LOADING, // Fetching OPDS feed
|
WIFI_SELECTION, // WiFi selection subactivity is active
|
||||||
BROWSING, // Displaying entries (navigation or books)
|
LOADING, // Fetching OPDS feed
|
||||||
DOWNLOADING, // Downloading selected EPUB
|
BROWSING, // Displaying entries (navigation or books)
|
||||||
ERROR // Error state with message
|
DOWNLOADING, // Downloading selected EPUB
|
||||||
|
ERROR // Error state with message
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit OpdsBookBrowserActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
explicit OpdsBookBrowserActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
||||||
@ -54,6 +56,8 @@ class OpdsBookBrowserActivity final : public ActivityWithSubactivity {
|
|||||||
void render() const;
|
void render() const;
|
||||||
|
|
||||||
void checkAndConnectWifi();
|
void checkAndConnectWifi();
|
||||||
|
void launchWifiSelection();
|
||||||
|
void onWifiSelectionComplete(bool connected);
|
||||||
void fetchFeed(const std::string& path);
|
void fetchFeed(const std::string& path);
|
||||||
void navigateToEntry(const OpdsEntry& entry);
|
void navigateToEntry(const OpdsEntry& entry);
|
||||||
void navigateBack();
|
void navigateBack();
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Battery.h"
|
||||||
#include "CrossPointSettings.h"
|
#include "CrossPointSettings.h"
|
||||||
#include "CrossPointState.h"
|
#include "CrossPointState.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
@ -332,8 +333,13 @@ void HomeActivity::render() const {
|
|||||||
const auto labels = mappedInput.mapLabels("", "Confirm", "Up", "Down");
|
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 auto batteryX = pageWidth - 25 - renderer.getTextWidth(SMALL_FONT_ID, "100 %");
|
const bool showBatteryPercentage =
|
||||||
ScreenComponents::drawBattery(renderer, batteryX, 10);
|
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);
|
||||||
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -152,6 +152,8 @@ void EpubReaderActivity::loop() {
|
|||||||
const bool prevReleased = mappedInput.wasReleased(MappedInputManager::Button::PageBack) ||
|
const bool prevReleased = mappedInput.wasReleased(MappedInputManager::Button::PageBack) ||
|
||||||
mappedInput.wasReleased(MappedInputManager::Button::Left);
|
mappedInput.wasReleased(MappedInputManager::Button::Left);
|
||||||
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::PageForward) ||
|
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::PageForward) ||
|
||||||
|
(SETTINGS.shortPwrBtn == CrossPointSettings::SHORT_PWRBTN::PAGE_TURN &&
|
||||||
|
mappedInput.wasReleased(MappedInputManager::Button::Power)) ||
|
||||||
mappedInput.wasReleased(MappedInputManager::Button::Right);
|
mappedInput.wasReleased(MappedInputManager::Button::Right);
|
||||||
|
|
||||||
if (!prevReleased && !nextReleased) {
|
if (!prevReleased && !nextReleased) {
|
||||||
@ -417,6 +419,8 @@ void EpubReaderActivity::renderStatusBar(const int orientedMarginRight, const in
|
|||||||
SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL;
|
SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL;
|
||||||
const bool showChapterTitle = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::NO_PROGRESS ||
|
const bool showChapterTitle = SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::NO_PROGRESS ||
|
||||||
SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL;
|
SETTINGS.statusBar == CrossPointSettings::STATUS_BAR_MODE::FULL;
|
||||||
|
const bool showBatteryPercentage =
|
||||||
|
SETTINGS.hideBatteryPercentage == CrossPointSettings::HIDE_BATTERY_PERCENTAGE::HIDE_NEVER;
|
||||||
|
|
||||||
// Position status bar near the bottom of the logical screen, regardless of orientation
|
// Position status bar near the bottom of the logical screen, regardless of orientation
|
||||||
const auto screenHeight = renderer.getScreenHeight();
|
const auto screenHeight = renderer.getScreenHeight();
|
||||||
@ -437,7 +441,7 @@ void EpubReaderActivity::renderStatusBar(const int orientedMarginRight, const in
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (showBattery) {
|
if (showBattery) {
|
||||||
ScreenComponents::drawBattery(renderer, orientedMarginLeft + 1, textY);
|
ScreenComponents::drawBattery(renderer, orientedMarginLeft + 1, textY, showBatteryPercentage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showChapterTitle) {
|
if (showChapterTitle) {
|
||||||
|
|||||||
@ -16,7 +16,9 @@ int EpubReaderChapterSelectionActivity::getPageItems() const {
|
|||||||
constexpr int lineHeight = 30;
|
constexpr int lineHeight = 30;
|
||||||
|
|
||||||
const int screenHeight = renderer.getScreenHeight();
|
const int screenHeight = renderer.getScreenHeight();
|
||||||
const int availableHeight = screenHeight - startY;
|
const int endY = screenHeight - lineHeight;
|
||||||
|
|
||||||
|
const int availableHeight = endY - startY;
|
||||||
int items = availableHeight / lineHeight;
|
int items = availableHeight / lineHeight;
|
||||||
|
|
||||||
// Ensure we always have at least one item per page to avoid division by zero
|
// Ensure we always have at least one item per page to avoid division by zero
|
||||||
@ -134,5 +136,8 @@ void EpubReaderChapterSelectionActivity::renderScreen() {
|
|||||||
tocIndex != selectorIndex);
|
tocIndex != selectorIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto labels = mappedInput.mapLabels("« Back", "Select", "Up", "Down");
|
||||||
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -112,6 +112,8 @@ void XtcReaderActivity::loop() {
|
|||||||
const bool prevReleased = mappedInput.wasReleased(MappedInputManager::Button::PageBack) ||
|
const bool prevReleased = mappedInput.wasReleased(MappedInputManager::Button::PageBack) ||
|
||||||
mappedInput.wasReleased(MappedInputManager::Button::Left);
|
mappedInput.wasReleased(MappedInputManager::Button::Left);
|
||||||
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::PageForward) ||
|
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::PageForward) ||
|
||||||
|
(SETTINGS.shortPwrBtn == CrossPointSettings::SHORT_PWRBTN::PAGE_TURN &&
|
||||||
|
mappedInput.wasReleased(MappedInputManager::Button::Power)) ||
|
||||||
mappedInput.wasReleased(MappedInputManager::Button::Right);
|
mappedInput.wasReleased(MappedInputManager::Button::Right);
|
||||||
|
|
||||||
if (!prevReleased && !nextReleased) {
|
if (!prevReleased && !nextReleased) {
|
||||||
|
|||||||
@ -14,7 +14,9 @@ int XtcReaderChapterSelectionActivity::getPageItems() const {
|
|||||||
constexpr int lineHeight = 30;
|
constexpr int lineHeight = 30;
|
||||||
|
|
||||||
const int screenHeight = renderer.getScreenHeight();
|
const int screenHeight = renderer.getScreenHeight();
|
||||||
const int availableHeight = screenHeight - startY;
|
const int endY = screenHeight - lineHeight;
|
||||||
|
|
||||||
|
const int availableHeight = endY - startY;
|
||||||
int items = availableHeight / lineHeight;
|
int items = availableHeight / lineHeight;
|
||||||
if (items < 1) {
|
if (items < 1) {
|
||||||
items = 1;
|
items = 1;
|
||||||
@ -147,5 +149,8 @@ void XtcReaderChapterSelectionActivity::renderScreen() {
|
|||||||
renderer.drawText(UI_10_FONT_ID, 20, 60 + (i % pageItems) * 30, title, i != selectorIndex);
|
renderer.drawText(UI_10_FONT_ID, 20, 60 + (i % pageItems) * 30, title, i != selectorIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto labels = mappedInput.mapLabels("« Back", "Select", "Up", "Down");
|
||||||
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,15 +14,16 @@
|
|||||||
|
|
||||||
// Define the static settings list
|
// Define the static settings list
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int settingsCount = 19;
|
constexpr int settingsCount = 20;
|
||||||
const SettingInfo settingsList[settingsCount] = {
|
const SettingInfo settingsList[settingsCount] = {
|
||||||
// Should match with SLEEP_SCREEN_MODE
|
// Should match with SLEEP_SCREEN_MODE
|
||||||
SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover", "None"}),
|
SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover", "None"}),
|
||||||
SettingInfo::Enum("Sleep Screen Cover Mode", &CrossPointSettings::sleepScreenCoverMode, {"Fit", "Crop"}),
|
SettingInfo::Enum("Sleep Screen Cover Mode", &CrossPointSettings::sleepScreenCoverMode, {"Fit", "Crop"}),
|
||||||
SettingInfo::Enum("Status Bar", &CrossPointSettings::statusBar, {"None", "No Progress", "Full"}),
|
SettingInfo::Enum("Status Bar", &CrossPointSettings::statusBar, {"None", "No Progress", "Full"}),
|
||||||
|
SettingInfo::Enum("Hide Battery %", &CrossPointSettings::hideBatteryPercentage, {"Never", "In Reader", "Always"}),
|
||||||
SettingInfo::Toggle("Extra Paragraph Spacing", &CrossPointSettings::extraParagraphSpacing),
|
SettingInfo::Toggle("Extra Paragraph Spacing", &CrossPointSettings::extraParagraphSpacing),
|
||||||
SettingInfo::Toggle("Text Anti-Aliasing", &CrossPointSettings::textAntiAliasing),
|
SettingInfo::Toggle("Text Anti-Aliasing", &CrossPointSettings::textAntiAliasing),
|
||||||
SettingInfo::Toggle("Short Power Button Click", &CrossPointSettings::shortPwrBtn),
|
SettingInfo::Enum("Short Power Button Click", &CrossPointSettings::shortPwrBtn, {"Ignore", "Sleep", "Page Turn"}),
|
||||||
SettingInfo::Enum("Reading Orientation", &CrossPointSettings::orientation,
|
SettingInfo::Enum("Reading Orientation", &CrossPointSettings::orientation,
|
||||||
{"Portrait", "Landscape CW", "Inverted", "Landscape CCW"}),
|
{"Portrait", "Landscape CW", "Inverted", "Landscape CCW"}),
|
||||||
SettingInfo::Enum("Front Button Layout", &CrossPointSettings::frontButtonLayout,
|
SettingInfo::Enum("Front Button Layout", &CrossPointSettings::frontButtonLayout,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user