diff --git a/src/activities/apps/AppsActivity.cpp b/src/activities/apps/AppsActivity.cpp index fb5cff73..8b82c4b5 100644 --- a/src/activities/apps/AppsActivity.cpp +++ b/src/activities/apps/AppsActivity.cpp @@ -86,9 +86,12 @@ void AppsActivity::launchApp() { CrossPoint::AppLoader loader; bool success = loader.flashApp(binPath, [this](size_t written, size_t total) { - flashProgress_ = static_cast((written * 100) / total); - needsUpdate_ = true; - renderProgress(); + const int nextProgress = (total > 0) ? static_cast((written * 100) / total) : 0; + if (nextProgress != flashProgress_) { + flashProgress_ = nextProgress; + needsUpdate_ = true; + renderProgress(); + } }); if (!success) { @@ -134,7 +137,8 @@ void AppsActivity::render() { int textWidth = renderer_.getTextWidth(UI_12_FONT_ID, buf); int x = (pageWidth - textWidth) / 2 - 10; renderer_.fillRect(x, y - 5, textWidth + 20, lineHeight - 5); - renderer_.drawText(UI_12_FONT_ID, x + 10, y, buf, true); // inverted + // Draw white text on black highlight. + renderer_.drawText(UI_12_FONT_ID, x + 10, y, buf, false); } else { renderer_.drawCenteredText(UI_10_FONT_ID, y, buf); } diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index 2ff3e743..27ee5f1d 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -506,7 +506,8 @@ void HomeActivity::render() { // --- Bottom menu tiles --- // Build menu items dynamically - std::vector menuItems = {"My Library", "File Transfer", "Settings"}; + // Keep this list in sync with getMenuItemCount() and loop() index mapping. + std::vector menuItems = {"My Library", "File Transfer", "Apps", "Settings"}; if (hasOpdsUrl) { // Insert OPDS Browser after My Library menuItems.insert(menuItems.begin() + 1, "OPDS Browser"); diff --git a/src/apps/hello-world/HelloWorldActivity.cpp b/src/apps/hello-world/HelloWorldActivity.cpp index 369bbf88..b0d3b6f2 100644 --- a/src/apps/hello-world/HelloWorldActivity.cpp +++ b/src/apps/hello-world/HelloWorldActivity.cpp @@ -19,7 +19,7 @@ EpdFont ui12BoldFont(&ubuntu_12_bold); EpdFontFamily ui12FontFamily(&ui12RegularFont, &ui12BoldFont); } -HelloWorldActivity::HelloWorldActivity(EInkDisplay& display, InputManager& input) +HelloWorldActivity::HelloWorldActivity(HalDisplay& display, HalGPIO& input) : display_(display), input_(input), needsUpdate_(true) {} void HelloWorldActivity::onEnter() { @@ -35,7 +35,7 @@ void HelloWorldActivity::onEnter() { } void HelloWorldActivity::loop() { - if (input_.wasPressed(InputManager::BTN_BACK)) { + if (input_.wasPressed(HalGPIO::BTN_BACK)) { returnToLauncher(); return; } diff --git a/src/apps/hello-world/HelloWorldActivity.h b/src/apps/hello-world/HelloWorldActivity.h index 12d396cd..861deb85 100644 --- a/src/apps/hello-world/HelloWorldActivity.h +++ b/src/apps/hello-world/HelloWorldActivity.h @@ -1,19 +1,19 @@ #pragma once -#include -#include +#include +#include class HelloWorldActivity { public: - HelloWorldActivity(EInkDisplay& display, InputManager& input); + HelloWorldActivity(HalDisplay& display, HalGPIO& input); void onEnter(); void loop(); void onExit(); private: - EInkDisplay& display_; - InputManager& input_; + HalDisplay& display_; + HalGPIO& input_; bool needsUpdate_; void render(); diff --git a/src/apps/hello-world/main.cpp b/src/apps/hello-world/main.cpp index b9d1b33a..1a32497a 100644 --- a/src/apps/hello-world/main.cpp +++ b/src/apps/hello-world/main.cpp @@ -1,32 +1,31 @@ #include -#include -#include +#include +#include + #include "HelloWorldActivity.h" -// Display SPI pins for Xteink X4 -#define EPD_SCLK 8 -#define EPD_MOSI 10 -#define EPD_CS 21 -#define EPD_DC 4 -#define EPD_RST 5 -#define EPD_BUSY 6 - -EInkDisplay display(EPD_SCLK, EPD_MOSI, EPD_CS, EPD_DC, EPD_RST, EPD_BUSY); -InputManager input; -HelloWorldActivity activity(display, input); +HalDisplay display; +HalGPIO gpio; +HelloWorldActivity activity(display, gpio); void setup() { - Serial.begin(115200); - Serial.println("[HelloWorld] Starting..."); - - input.begin(); + gpio.begin(); + + // Only start serial if USB connected + if (gpio.isUsbConnected()) { + Serial.begin(115200); + Serial.println("[HelloWorld] Starting..."); + } + activity.onEnter(); - - Serial.println("[HelloWorld] Activity started"); + + if (Serial) { + Serial.println("[HelloWorld] Activity started"); + } } void loop() { - input.update(); + gpio.update(); activity.loop(); delay(10); } diff --git a/src/extension/AppLoader.cpp b/src/extension/AppLoader.cpp index 72ff3999..ee7f1b04 100644 --- a/src/extension/AppLoader.cpp +++ b/src/extension/AppLoader.cpp @@ -102,8 +102,15 @@ AppManifest AppLoader::parseManifest(const String& path) { return manifest; } + // Handle UTF-8 BOM if the manifest was created by an editor that writes it. + const char* json = buffer.get(); + if (bytesRead >= 3 && static_cast(json[0]) == 0xEF && static_cast(json[1]) == 0xBB && + static_cast(json[2]) == 0xBF) { + json += 3; + } + JsonDocument doc; - const DeserializationError error = deserializeJson(doc, buffer.get()); + const DeserializationError error = deserializeJson(doc, json); if (error) { Serial.printf("[%lu] [AppLoader] JSON parse error in %s: %s\n", @@ -230,12 +237,16 @@ bool AppLoader::flashApp(const String& binPath, ProgressCallback callback) { } if (callback) { - callback(0, 100); + callback(0, fileSize); } size_t totalWritten = 0; - static constexpr size_t flashChunkSize = 1024; - uint8_t buffer[flashChunkSize]; + // Larger chunks reduce SD/OTA overhead significantly. + // 32KB is a good balance on ESP32-C3: faster writes without blowing RAM. + static constexpr size_t flashChunkSize = 32 * 1024; + static uint8_t buffer[flashChunkSize]; + + size_t lastNotifiedPercent = 0; while (totalWritten < fileSize) { const size_t remaining = fileSize - totalWritten; @@ -260,7 +271,11 @@ bool AppLoader::flashApp(const String& binPath, ProgressCallback callback) { if (callback) { const size_t percent = (totalWritten * 100) / fileSize; - callback(percent, 100); + // Throttle UI updates; each screen refresh is ~400ms. + if (percent >= lastNotifiedPercent + 10 || percent == 100) { + lastNotifiedPercent = percent; + callback(totalWritten, fileSize); + } } }