A fork of https://github.com/crosspoint-reader/crosspoint-reader
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: Wi-Fi Selection on Calibre Library launch (#313)

## Summary

* **What is the goal of this PR?**
Fixes the Wi-Fi connection issue when launching the Calibre Library
(OPDS browser). The previous implementation always attempted to connect
using the first saved WiFi credential, which caused connection failures
when users were in locations where only other saved networks (not the
first one) were available. Now, the activity launches a WiFi selection
screen allowing users to choose from available networks.

* **What changes are included?**

## Additional Context
**Bug Fixed**: Previously, the code used `credentials[0]` (always the
first saved WiFi), so users in areas with only their secondary/tertiary
saved networks available could never connect.

---------

Co-authored-by: danoooob <danoooob@example.com>

authored by

danoob
danoooob
and committed by
GitHub
66b100c6 41bda438

+54 -40
+42 -32
src/activities/browser/OpdsBookBrowserActivity.cpp
··· 7 7 #include "CrossPointSettings.h" 8 8 #include "MappedInputManager.h" 9 9 #include "ScreenComponents.h" 10 - #include "WifiCredentialStore.h" 10 + #include "activities/network/WifiSelectionActivity.h" 11 11 #include "fontIds.h" 12 12 #include "network/HttpDownloader.h" 13 13 #include "util/StringUtils.h" ··· 25 25 } 26 26 27 27 void OpdsBookBrowserActivity::onEnter() { 28 - Activity::onEnter(); 28 + ActivityWithSubactivity::onEnter(); 29 29 30 30 renderingMutex = xSemaphoreCreateMutex(); 31 31 state = BrowserState::CHECK_WIFI; ··· 49 49 } 50 50 51 51 void OpdsBookBrowserActivity::onExit() { 52 - Activity::onExit(); 52 + ActivityWithSubactivity::onExit(); 53 53 54 54 // Turn off WiFi when exiting 55 55 WiFi.mode(WIFI_OFF); ··· 66 66 } 67 67 68 68 void OpdsBookBrowserActivity::loop() { 69 + // Handle WiFi selection subactivity 70 + if (state == BrowserState::WIFI_SELECTION) { 71 + ActivityWithSubactivity::loop(); 72 + return; 73 + } 74 + 69 75 // Handle error state - Confirm retries, Back goes back or home 70 76 if (state == BrowserState::ERROR) { 71 77 if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { 72 - state = BrowserState::LOADING; 73 - statusMessage = "Loading..."; 74 - updateRequired = true; 75 - fetchFeed(currentPath); 78 + // Check if WiFi is still connected 79 + if (WiFi.status() == WL_CONNECTED && WiFi.localIP() != IPAddress(0, 0, 0, 0)) { 80 + // WiFi connected - just retry fetching the feed 81 + Serial.printf("[%lu] [OPDS] Retry: WiFi connected, retrying fetch\n", millis()); 82 + state = BrowserState::LOADING; 83 + statusMessage = "Loading..."; 84 + updateRequired = true; 85 + fetchFeed(currentPath); 86 + } else { 87 + // WiFi not connected - launch WiFi selection 88 + Serial.printf("[%lu] [OPDS] Retry: WiFi not connected, launching selection\n", millis()); 89 + launchWifiSelection(); 90 + } 76 91 } else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { 77 92 navigateBack(); 78 93 } ··· 350 365 } 351 366 352 367 void OpdsBookBrowserActivity::checkAndConnectWifi() { 353 - // Already connected? 354 - if (WiFi.status() == WL_CONNECTED) { 368 + // Already connected? Verify connection is valid by checking IP 369 + if (WiFi.status() == WL_CONNECTED && WiFi.localIP() != IPAddress(0, 0, 0, 0)) { 355 370 state = BrowserState::LOADING; 356 371 statusMessage = "Loading..."; 357 372 updateRequired = true; ··· 359 374 return; 360 375 } 361 376 362 - // Try to connect using saved credentials 363 - statusMessage = "Connecting to WiFi..."; 377 + // Not connected - launch WiFi selection screen directly 378 + launchWifiSelection(); 379 + } 380 + 381 + void OpdsBookBrowserActivity::launchWifiSelection() { 382 + state = BrowserState::WIFI_SELECTION; 364 383 updateRequired = true; 365 384 366 - WIFI_STORE.loadFromFile(); 367 - const auto& credentials = WIFI_STORE.getCredentials(); 368 - if (credentials.empty()) { 369 - state = BrowserState::ERROR; 370 - errorMessage = "No WiFi credentials saved"; 371 - updateRequired = true; 372 - return; 373 - } 385 + enterNewActivity(new WifiSelectionActivity(renderer, mappedInput, 386 + [this](const bool connected) { onWifiSelectionComplete(connected); })); 387 + } 374 388 375 - // Use the first saved credential 376 - const auto& cred = credentials[0]; 377 - WiFi.mode(WIFI_STA); 378 - WiFi.begin(cred.ssid.c_str(), cred.password.c_str()); 379 - 380 - // Wait for connection with timeout 381 - constexpr int WIFI_TIMEOUT_MS = 10000; 382 - const unsigned long startTime = millis(); 383 - while (WiFi.status() != WL_CONNECTED && millis() - startTime < WIFI_TIMEOUT_MS) { 384 - vTaskDelay(100 / portTICK_PERIOD_MS); 385 - } 389 + void OpdsBookBrowserActivity::onWifiSelectionComplete(const bool connected) { 390 + exitActivity(); 386 391 387 - if (WiFi.status() == WL_CONNECTED) { 388 - Serial.printf("[%lu] [OPDS] WiFi connected: %s\n", millis(), WiFi.localIP().toString().c_str()); 392 + if (connected) { 393 + Serial.printf("[%lu] [OPDS] WiFi connected via selection, fetching feed\n", millis()); 389 394 state = BrowserState::LOADING; 390 395 statusMessage = "Loading..."; 391 396 updateRequired = true; 392 397 fetchFeed(currentPath); 393 398 } else { 399 + Serial.printf("[%lu] [OPDS] WiFi selection cancelled/failed\n", millis()); 400 + // Force disconnect to ensure clean state for next retry 401 + // This prevents stale connection status from interfering 402 + WiFi.disconnect(); 403 + WiFi.mode(WIFI_OFF); 394 404 state = BrowserState::ERROR; 395 405 errorMessage = "WiFi connection failed"; 396 406 updateRequired = true;
+12 -8
src/activities/browser/OpdsBookBrowserActivity.h
··· 8 8 #include <string> 9 9 #include <vector> 10 10 11 - #include "../Activity.h" 11 + #include "../ActivityWithSubactivity.h" 12 12 13 13 /** 14 14 * Activity for browsing and downloading books from an OPDS server. 15 15 * Supports navigation through catalog hierarchy and downloading EPUBs. 16 + * When WiFi connection fails, launches WiFi selection to let user connect. 16 17 */ 17 - class OpdsBookBrowserActivity final : public Activity { 18 + class OpdsBookBrowserActivity final : public ActivityWithSubactivity { 18 19 public: 19 20 enum class BrowserState { 20 - CHECK_WIFI, // Checking WiFi connection 21 - LOADING, // Fetching OPDS feed 22 - BROWSING, // Displaying entries (navigation or books) 23 - DOWNLOADING, // Downloading selected EPUB 24 - ERROR // Error state with message 21 + CHECK_WIFI, // Checking WiFi connection 22 + WIFI_SELECTION, // WiFi selection subactivity is active 23 + LOADING, // Fetching OPDS feed 24 + BROWSING, // Displaying entries (navigation or books) 25 + DOWNLOADING, // Downloading selected EPUB 26 + ERROR // Error state with message 25 27 }; 26 28 27 29 explicit OpdsBookBrowserActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, 28 30 const std::function<void()>& onGoHome) 29 - : Activity("OpdsBookBrowser", renderer, mappedInput), onGoHome(onGoHome) {} 31 + : ActivityWithSubactivity("OpdsBookBrowser", renderer, mappedInput), onGoHome(onGoHome) {} 30 32 31 33 void onEnter() override; 32 34 void onExit() override; ··· 54 56 void render() const; 55 57 56 58 void checkAndConnectWifi(); 59 + void launchWifiSelection(); 60 + void onWifiSelectionComplete(bool connected); 57 61 void fetchFeed(const std::string& path); 58 62 void navigateToEntry(const OpdsEntry& entry); 59 63 void navigateBack();