diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index dc77545..ce873f8 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -282,7 +282,7 @@ void HomeActivity::render() { if (strlen(SETTINGS.pinnedListName) > 0) { listsLabel = std::string(SETTINGS.pinnedListName); } else { - listsLabel = "ReadingLists"; + listsLabel = "Reading Lists"; } // Build menu items dynamically (need count for layout calculation) diff --git a/src/activities/network/CrossPointWebServerActivity.cpp b/src/activities/network/CrossPointWebServerActivity.cpp index 24ecd84..c8c7d0f 100644 --- a/src/activities/network/CrossPointWebServerActivity.cpp +++ b/src/activities/network/CrossPointWebServerActivity.cpp @@ -72,7 +72,7 @@ void CrossPointWebServerActivity::onEnter() { connectedIP.clear(); connectedSSID.clear(); lastHandleClientTime = 0; - currentScreen = FileTransferScreen::WEB_BROWSER; + currentScreen = FileTransferScreen::COMPANION_APP_LIBRARY; lastStatsRefresh = 0; updateRequired = true; @@ -369,36 +369,20 @@ void CrossPointWebServerActivity::loop() { lastHandleClientTime = millis(); } - // Handle screen switching with arrow buttons - if (mappedInput.wasPressed(MappedInputManager::Button::PageForward)) { - // Cycle to next screen: WEB_BROWSER -> COMPANION_APP -> COMPANION_APP_LIBRARY -> WEB_BROWSER + // Handle screen switching with front arrow buttons (Left or Right cycles forward) + // Order: COMPANION_APP_LIBRARY -> WEB_BROWSER -> COMPANION_APP -> back to COMPANION_APP_LIBRARY + if (mappedInput.wasPressed(MappedInputManager::Button::Left) || + mappedInput.wasPressed(MappedInputManager::Button::Right)) { switch (currentScreen) { + case FileTransferScreen::COMPANION_APP_LIBRARY: + currentScreen = FileTransferScreen::WEB_BROWSER; + break; case FileTransferScreen::WEB_BROWSER: currentScreen = FileTransferScreen::COMPANION_APP; break; case FileTransferScreen::COMPANION_APP: currentScreen = FileTransferScreen::COMPANION_APP_LIBRARY; break; - case FileTransferScreen::COMPANION_APP_LIBRARY: - currentScreen = FileTransferScreen::WEB_BROWSER; - break; - } - updateRequired = true; - Serial.printf("[%lu] [WEBACT] Switched to screen: %d\n", millis(), static_cast(currentScreen)); - } - - if (mappedInput.wasPressed(MappedInputManager::Button::PageBack)) { - // Cycle to previous screen: WEB_BROWSER -> COMPANION_APP_LIBRARY -> COMPANION_APP -> WEB_BROWSER - switch (currentScreen) { - case FileTransferScreen::WEB_BROWSER: - currentScreen = FileTransferScreen::COMPANION_APP_LIBRARY; - break; - case FileTransferScreen::COMPANION_APP: - currentScreen = FileTransferScreen::WEB_BROWSER; - break; - case FileTransferScreen::COMPANION_APP_LIBRARY: - currentScreen = FileTransferScreen::COMPANION_APP; - break; } updateRequired = true; Serial.printf("[%lu] [WEBACT] Switched to screen: %d\n", millis(), static_cast(currentScreen)); @@ -446,26 +430,23 @@ void CrossPointWebServerActivity::render() const { } } -void drawQRCode(const GfxRenderer& renderer, const int x, const int y, const std::string& data) { - // Implementation of QR code calculation - // The structure to manage the QR code +// Draw QR code at specified position with configurable pixel size per module +// Returns the size of the QR code in pixels (width = height = size * pixelsPerModule) +int drawQRCode(const GfxRenderer& renderer, const int x, const int y, const std::string& data, + const uint8_t pixelsPerModule = 7) { QRCode qrcode; uint8_t qrcodeBytes[qrcode_getBufferSize(4)]; Serial.printf("[%lu] [WEBACT] QR Code (%lu): %s\n", millis(), data.length(), data.c_str()); qrcode_initText(&qrcode, qrcodeBytes, 4, ECC_LOW, data.c_str()); - const uint8_t px = 6; // pixels per module for (uint8_t cy = 0; cy < qrcode.size; cy++) { for (uint8_t cx = 0; cx < qrcode.size; cx++) { if (qrcode_getModule(&qrcode, cx, cy)) { - // Serial.print("**"); - renderer.fillRect(x + px * cx, y + px * cy, px, px, true); - } else { - // Serial.print(" "); + renderer.fillRect(x + pixelsPerModule * cx, y + pixelsPerModule * cy, pixelsPerModule, pixelsPerModule, true); } } - // Serial.print("\n"); } + return qrcode.size * pixelsPerModule; } // Helper to format bytes into human-readable sizes @@ -521,6 +502,10 @@ std::string CrossPointWebServerActivity::getCompanionAppLibraryUrl() const { } void CrossPointWebServerActivity::renderServerRunning() const { + // Switch to landscape orientation for the QR code screens (800x480) + const auto originalOrientation = renderer.getOrientation(); + renderer.setOrientation(GfxRenderer::Orientation::LandscapeCounterClockwise); + // Dispatch to the appropriate screen switch (currentScreen) { case FileTransferScreen::WEB_BROWSER: @@ -537,16 +522,20 @@ void CrossPointWebServerActivity::renderServerRunning() const { // Render stats bar at the bottom (above button hints) renderStats(); - // Draw button hints with arrow navigation - const auto labels = mappedInput.mapLabels("« Exit", "", "«", "»"); + // Restore orientation before drawing button hints (they handle their own orientation internally) + renderer.setOrientation(originalOrientation); + + // Draw button hints - Left/Right cycle screens, Back exits + const auto labels = mappedInput.mapLabels("« Exit", "", "", "Next »"); renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); } void CrossPointWebServerActivity::renderStats() const { if (!webServer) return; - const int screenHeight = renderer.getScreenHeight(); - const int statsY = screenHeight - 70; // Position above button hints + // In landscape (800x480), position stats near bottom but above button area + const int screenHeight = renderer.getScreenHeight(); // 480 in landscape + const int statsY = screenHeight - 30; // Get stats from web server const unsigned long uptime = webServer->getServerUptime(); @@ -566,144 +555,169 @@ void CrossPointWebServerActivity::renderStats() const { statsLine += " (" + std::to_string(filesDown) + ")"; } - renderer.drawCenteredText(SMALL_FONT_ID, statsY, statsLine.c_str()); + renderer.drawCenteredText(UI_10_FONT_ID, statsY, statsLine.c_str()); } void CrossPointWebServerActivity::renderWebBrowserScreen() const { - constexpr int LINE_SPACING = 28; + // Landscape layout (800x480): QR on left, text on right + constexpr int QR_X = 15; + constexpr int QR_Y = 15; + constexpr int QR_PX = 7; // pixels per QR module + constexpr int QR_SIZE = 33 * QR_PX; // QR version 4 = 33 modules = 231px + constexpr int TEXT_X = QR_X + QR_SIZE + 35; // Text starts after QR + margin + constexpr int LINE_SPACING = 32; - renderer.drawCenteredText(UI_12_FONT_ID, 15, "File Transfer - Web Browser", true, EpdFontFamily::BOLD); + // Draw title on right side + renderer.drawText(NOTOSANS_14_FONT_ID, TEXT_X, QR_Y, "File Transfer - Web Browser", true, EpdFontFamily::BOLD); + + int textY = QR_Y + LINE_SPACING + 10; if (isApMode) { - // AP mode display - center the content block - int startY = 55; - - renderer.drawCenteredText(UI_10_FONT_ID, startY, "Hotspot Mode", true, EpdFontFamily::BOLD); - - std::string ssidInfo = "Network: " + connectedSSID; - renderer.drawCenteredText(UI_10_FONT_ID, startY + LINE_SPACING, ssidInfo.c_str()); - - renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 2, "Connect your device to this WiFi network"); - - renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 3, - "or scan QR code with your phone to connect to Wifi."); - // Show QR code for WiFi config + // AP mode: Show WiFi QR code on left, connection info on right const std::string wifiConfig = std::string("WIFI:S:") + connectedSSID + ";;"; - drawQRCode(renderer, (480 - 6 * 33) / 2, startY + LINE_SPACING * 4, wifiConfig); - - startY += 6 * 29 + 3 * LINE_SPACING; - // Show primary URL (hostname) - std::string hostnameUrl = std::string("http://") + AP_HOSTNAME + ".local/"; - renderer.drawCenteredText(UI_10_FONT_ID, startY + LINE_SPACING * 3, hostnameUrl.c_str(), true, EpdFontFamily::BOLD); - - // Show IP address as fallback - std::string ipUrl = "or http://" + connectedIP + "/"; - renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 4, ipUrl.c_str()); - renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 5, "Open this URL in your browser"); - - // Show QR code for URL - renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 6, "or scan QR code with your phone:"); - drawQRCode(renderer, (480 - 6 * 33) / 2, startY + LINE_SPACING * 7, hostnameUrl); - } else { - // STA mode display - const int startY = 55; + drawQRCode(renderer, QR_X, QR_Y, wifiConfig, QR_PX); std::string ssidInfo = "Network: " + connectedSSID; - if (ssidInfo.length() > 28) { - ssidInfo.replace(25, ssidInfo.length() - 25, "..."); + renderer.drawText(NOTOSANS_12_FONT_ID, TEXT_X, textY, ssidInfo.c_str()); + textY += LINE_SPACING; + + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, "Scan QR code to connect to WiFi,"); + textY += LINE_SPACING - 4; + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, "then open the URL in your browser."); + textY += LINE_SPACING + 8; + + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, "Browser URL:", true, EpdFontFamily::BOLD); + textY += LINE_SPACING - 4; + + std::string webUrl = "http://" + connectedIP + "/"; + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, webUrl.c_str(), true, EpdFontFamily::BOLD); + textY += LINE_SPACING - 4; + + std::string hostnameUrl = std::string("or http://") + AP_HOSTNAME + ".local/"; + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, hostnameUrl.c_str()); + } else { + // STA mode: Show URL QR code on left, connection info on right + std::string webUrl = "http://" + connectedIP + "/"; + drawQRCode(renderer, QR_X, QR_Y, webUrl, QR_PX); + + std::string ssidInfo = "Network: " + connectedSSID; + if (ssidInfo.length() > 35) { + ssidInfo = ssidInfo.substr(0, 32) + "..."; } - renderer.drawCenteredText(UI_10_FONT_ID, startY, ssidInfo.c_str()); + renderer.drawText(NOTOSANS_12_FONT_ID, TEXT_X, textY, ssidInfo.c_str()); + textY += LINE_SPACING; std::string ipInfo = "IP Address: " + connectedIP; - renderer.drawCenteredText(UI_10_FONT_ID, startY + LINE_SPACING, ipInfo.c_str()); + renderer.drawText(NOTOSANS_12_FONT_ID, TEXT_X, textY, ipInfo.c_str()); + textY += LINE_SPACING + 8; - // Show web server URL prominently - std::string webInfo = "http://" + connectedIP + "/"; - renderer.drawCenteredText(UI_10_FONT_ID, startY + LINE_SPACING * 2, webInfo.c_str(), true, EpdFontFamily::BOLD); + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, webUrl.c_str(), true, EpdFontFamily::BOLD); + textY += LINE_SPACING - 4; - // Also show hostname URL std::string hostnameUrl = std::string("or http://") + AP_HOSTNAME + ".local/"; - renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 3, hostnameUrl.c_str()); + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, hostnameUrl.c_str()); + textY += LINE_SPACING + 4; - renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 4, "Open this URL in your browser"); - - renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 5, "or scan QR code with your phone:"); - - // Show QR code for URL - drawQRCode(renderer, (480 - 6 * 33) / 2, startY + LINE_SPACING * 6, webInfo); + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, "Open this URL in your browser"); + textY += LINE_SPACING - 4; + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, "or scan QR code with your phone:"); } } void CrossPointWebServerActivity::renderCompanionAppScreen() const { - constexpr int LINE_SPACING = 28; + // Landscape layout (800x480): QR on left, text on right + constexpr int QR_X = 15; + constexpr int QR_Y = 15; + constexpr int QR_PX = 7; // pixels per QR module + constexpr int QR_SIZE = 33 * QR_PX; // QR version 4 = 33 modules = 231px + constexpr int TEXT_X = QR_X + QR_SIZE + 35; // Text starts after QR + margin + constexpr int LINE_SPACING = 32; - renderer.drawCenteredText(UI_12_FONT_ID, 15, "File Transfer - Companion App", true, EpdFontFamily::BOLD); + // Draw title on right side + renderer.drawText(NOTOSANS_14_FONT_ID, TEXT_X, QR_Y, "Companion App - Files", true, EpdFontFamily::BOLD); - const int startY = 55; + int textY = QR_Y + LINE_SPACING + 10; // Show network info std::string ssidInfo = "Network: " + connectedSSID; - if (ssidInfo.length() > 28) { - ssidInfo.replace(25, ssidInfo.length() - 25, "..."); + if (ssidInfo.length() > 35) { + ssidInfo = ssidInfo.substr(0, 32) + "..."; } - renderer.drawCenteredText(UI_10_FONT_ID, startY, ssidInfo.c_str()); + renderer.drawText(NOTOSANS_12_FONT_ID, TEXT_X, textY, ssidInfo.c_str()); + textY += LINE_SPACING; + // Show scan instructions if (isApMode) { - renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING, - "Connect to this WiFi first, then scan QR code"); + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, "Connect to this WiFi first, then scan"); + textY += LINE_SPACING - 4; + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, "QR code with the Companion app."); } else { - renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING, "Scan QR code with the CrossPoint Companion app"); + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, "Scan the QR code with a phone that"); + textY += LINE_SPACING - 4; + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, "has the CrossPoint Companion app."); } + textY += LINE_SPACING + 8; - // Show companion app URL prominently + // Show alternative browser access + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, "Alternative Browser Access:", true, EpdFontFamily::BOLD); + textY += LINE_SPACING - 4; + std::string webUrl = "http://" + connectedIP + "/files"; + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, webUrl.c_str()); + + // Draw QR code on left const std::string appUrl = getCompanionAppUrl(); - renderer.drawCenteredText(UI_10_FONT_ID, startY + LINE_SPACING * 2, appUrl.c_str(), true, EpdFontFamily::BOLD); + drawQRCode(renderer, QR_X, QR_Y, appUrl, QR_PX); - renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 3, "or scan QR code:"); - - // Show QR code for app URL (centered) - drawQRCode(renderer, (480 - 6 * 33) / 2, startY + LINE_SPACING * 4, appUrl); - - // Show HTTP URL for reference (smaller) - const int refY = startY + LINE_SPACING * 4 + 6 * 33 + 15; // Below QR code - renderer.drawCenteredText(SMALL_FONT_ID, refY, "Opens to: Device file browser"); - std::string webUrl = "http://" + connectedIP + "/"; - renderer.drawCenteredText(SMALL_FONT_ID, refY + 20, webUrl.c_str()); + // Show deep link URL below QR code + const int urlY = QR_Y + QR_SIZE + 10; + renderer.drawText(UI_12_FONT_ID, QR_X, urlY, appUrl.c_str(), true, EpdFontFamily::BOLD); } void CrossPointWebServerActivity::renderCompanionAppLibraryScreen() const { - constexpr int LINE_SPACING = 28; + // Landscape layout (800x480): QR on left, text on right + constexpr int QR_X = 15; + constexpr int QR_Y = 15; + constexpr int QR_PX = 7; // pixels per QR module + constexpr int QR_SIZE = 33 * QR_PX; // QR version 4 = 33 modules = 231px + constexpr int TEXT_X = QR_X + QR_SIZE + 35; // Text starts after QR + margin + constexpr int LINE_SPACING = 32; - renderer.drawCenteredText(UI_12_FONT_ID, 15, "File Transfer - Library Link", true, EpdFontFamily::BOLD); + // Draw title on right side + renderer.drawText(NOTOSANS_14_FONT_ID, TEXT_X, QR_Y, "Companion App - Library", true, EpdFontFamily::BOLD); - const int startY = 55; + int textY = QR_Y + LINE_SPACING + 10; // Show network info std::string ssidInfo = "Network: " + connectedSSID; - if (ssidInfo.length() > 28) { - ssidInfo.replace(25, ssidInfo.length() - 25, "..."); + if (ssidInfo.length() > 35) { + ssidInfo = ssidInfo.substr(0, 32) + "..."; } - renderer.drawCenteredText(UI_10_FONT_ID, startY, ssidInfo.c_str()); + renderer.drawText(NOTOSANS_12_FONT_ID, TEXT_X, textY, ssidInfo.c_str()); + textY += LINE_SPACING; + // Show scan instructions if (isApMode) { - renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING, - "Connect to this WiFi first, then scan QR code"); + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, "Connect to this WiFi first, then scan"); + textY += LINE_SPACING - 4; + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, "QR code with the Companion app."); } else { - renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING, "Scan QR code with the CrossPoint Companion app"); + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, "Scan the QR code with a phone that"); + textY += LINE_SPACING - 4; + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, "has the CrossPoint Companion app."); } + textY += LINE_SPACING + 8; - // Show companion app URL prominently - const std::string appUrl = getCompanionAppLibraryUrl(); - renderer.drawCenteredText(UI_10_FONT_ID, startY + LINE_SPACING * 2, appUrl.c_str(), true, EpdFontFamily::BOLD); - - renderer.drawCenteredText(SMALL_FONT_ID, startY + LINE_SPACING * 3, "or scan QR code:"); - - // Show QR code for app URL (centered) - drawQRCode(renderer, (480 - 6 * 33) / 2, startY + LINE_SPACING * 4, appUrl); - - // Show what this link opens to - const int refY = startY + LINE_SPACING * 4 + 6 * 33 + 15; // Below QR code - renderer.drawCenteredText(SMALL_FONT_ID, refY, "Opens to: Your local Library"); + // Show alternative browser access + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, "Alternative Browser Access:", true, EpdFontFamily::BOLD); + textY += LINE_SPACING - 4; std::string webUrl = "http://" + connectedIP + "/"; - renderer.drawCenteredText(SMALL_FONT_ID, refY + 20, webUrl.c_str()); + renderer.drawText(UI_12_FONT_ID, TEXT_X, textY, webUrl.c_str()); + + // Draw QR code on left + const std::string appUrl = getCompanionAppLibraryUrl(); + drawQRCode(renderer, QR_X, QR_Y, appUrl, QR_PX); + + // Show deep link URL below QR code + const int urlY = QR_Y + QR_SIZE + 10; + renderer.drawText(UI_12_FONT_ID, QR_X, urlY, appUrl.c_str(), true, EpdFontFamily::BOLD); } diff --git a/src/activities/network/CrossPointWebServerActivity.h b/src/activities/network/CrossPointWebServerActivity.h index 03f9e63..e2ef979 100644 --- a/src/activities/network/CrossPointWebServerActivity.h +++ b/src/activities/network/CrossPointWebServerActivity.h @@ -20,11 +20,11 @@ enum class WebServerActivityState { SHUTTING_DOWN // Shutting down server and WiFi }; -// File transfer screen tabs +// File transfer screen tabs (cycle order: LIBRARY -> WEB_BROWSER -> FILES -> LIBRARY) enum class FileTransferScreen { - WEB_BROWSER, // Default screen with HTTP URL and QR code + WEB_BROWSER, // Screen with HTTP URL and QR code COMPANION_APP, // Screen with crosspoint://files deep link URL and QR code - COMPANION_APP_LIBRARY // Screen with crosspoint://library deep link URL and QR code + COMPANION_APP_LIBRARY // Default screen with crosspoint://library deep link URL and QR code }; /** @@ -58,8 +58,8 @@ class CrossPointWebServerActivity final : public ActivityWithSubactivity { // Performance monitoring unsigned long lastHandleClientTime = 0; - // Screen navigation - FileTransferScreen currentScreen = FileTransferScreen::WEB_BROWSER; + // Screen navigation - start on Companion App Library screen + FileTransferScreen currentScreen = FileTransferScreen::COMPANION_APP_LIBRARY; unsigned long lastStatsRefresh = 0; static void taskTrampoline(void* param);