From 0531cee32c27e45a6c5f8009d59db5af65bf0082 Mon Sep 17 00:00:00 2001 From: Eliz Kilic Date: Sun, 1 Feb 2026 20:09:33 +0000 Subject: [PATCH] auto connect last wifi --- src/CrossPointSettings.cpp | 3 +- src/CrossPointSettings.h | 1 + .../network/AutoConnectingActivity.cpp | 91 +++++++++++++++++++ .../network/AutoConnectingActivity.h | 29 ++++++ .../network/WifiSelectionActivity.cpp | 34 ++++--- .../settings/CategorySettingsActivity.cpp | 11 ++- src/activities/settings/SettingsActivity.cpp | 3 +- src/main.cpp | 42 ++++++++- 8 files changed, 193 insertions(+), 21 deletions(-) create mode 100644 src/activities/network/AutoConnectingActivity.cpp create mode 100644 src/activities/network/AutoConnectingActivity.h diff --git a/src/CrossPointSettings.cpp b/src/CrossPointSettings.cpp index 232c7c57..e032fab6 100644 --- a/src/CrossPointSettings.cpp +++ b/src/CrossPointSettings.cpp @@ -22,7 +22,7 @@ void readAndValidate(FsFile& file, uint8_t& member, const uint8_t maxValue) { namespace { constexpr uint8_t SETTINGS_FILE_VERSION = 1; // Increment this when adding new persisted settings fields -constexpr uint8_t SETTINGS_COUNT = 23; +constexpr uint8_t SETTINGS_COUNT = 24; constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin"; } // namespace @@ -60,6 +60,7 @@ bool CrossPointSettings::saveToFile() const { serialization::writeString(outputFile, std::string(opdsUsername)); serialization::writeString(outputFile, std::string(opdsPassword)); serialization::writePod(outputFile, sleepScreenCoverFilter); + serialization::writeString(outputFile, std::string(lastConnectedSSID)); // New fields added at end for backward compatibility outputFile.close(); diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index c450d348..7a4f9904 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -137,6 +137,7 @@ class CrossPointSettings { uint8_t hideBatteryPercentage = HIDE_NEVER; // Long-press chapter skip on side buttons uint8_t longPressChapterSkip = 1; + char lastConnectedSSID[33] = ""; ~CrossPointSettings() = default; diff --git a/src/activities/network/AutoConnectingActivity.cpp b/src/activities/network/AutoConnectingActivity.cpp new file mode 100644 index 00000000..3aad2d80 --- /dev/null +++ b/src/activities/network/AutoConnectingActivity.cpp @@ -0,0 +1,91 @@ +#include "AutoConnectingActivity.h" + +#include +#include +#include +#include "MappedInputManager.h" +#include "fontIds.h" +#include "CrossPointSettings.h" +#include "WifiCredentialStore.h" + +AutoConnectingActivity::AutoConnectingActivity( + GfxRenderer& renderer, + MappedInputManager& mappedInput, + const std::function& on_success, + const std::function& on_failure +) : Activity("AutoConnecting", renderer, mappedInput), + on_success(on_success), + on_failure(on_failure) {} + +void AutoConnectingActivity::onEnter() { + Activity::onEnter(); + render(); + attemptConnection(); +} + +void AutoConnectingActivity::onExit() { + Activity::onExit(); +} + +void AutoConnectingActivity::loop() { + if (mappedInput.wasPressed(MappedInputManager::Button::Back)) { + WiFi.disconnect(); + on_failure(); + } else if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { // Skip + WiFi.disconnect(); + on_failure(); + } + checkConnectionStatus(); +} + +void AutoConnectingActivity::attemptConnection() { + connectionStartTime = millis(); + std::string ssid = SETTINGS.lastConnectedSSID; + const auto* cred = WIFI_STORE.findCredential(ssid); + if (!cred) { + on_failure(); + return; + } + + WiFi.mode(WIFI_STA); + WiFi.begin(ssid.c_str(), cred->password.c_str()); +} + +void AutoConnectingActivity::checkConnectionStatus() { + const wl_status_t status = WiFi.status(); + + if (status == WL_CONNECTED) { + on_success(); + return; + } + + if (status == WL_CONNECT_FAILED || status == WL_NO_SSID_AVAIL) { + on_failure(); + return; + } + + if (millis() - connectionStartTime > CONNECTION_TIMEOUT_MS) { + WiFi.disconnect(); + on_failure(); + return; + } +} + +void AutoConnectingActivity::render() const { + renderer.clearScreen(); + const auto pageHeight = renderer.getScreenHeight(); + const auto height = renderer.getLineHeight(UI_10_FONT_ID); + const auto top = (pageHeight - height) / 2; + + renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Connecting...", true, EpdFontFamily::BOLD); + + std::string ssidInfo = "to " + std::string(SETTINGS.lastConnectedSSID); + if (ssidInfo.length() > 25) { + ssidInfo.replace(22, ssidInfo.length() - 22, "..."); + } + renderer.drawCenteredText(UI_10_FONT_ID, top, ssidInfo.c_str()); + + const auto labels = mappedInput.mapLabels("« Back", "Skip", "", ""); + renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); + renderer.displayBuffer(); +} diff --git a/src/activities/network/AutoConnectingActivity.h b/src/activities/network/AutoConnectingActivity.h new file mode 100644 index 00000000..696fbebf --- /dev/null +++ b/src/activities/network/AutoConnectingActivity.h @@ -0,0 +1,29 @@ +#pragma once + +#include "activities/Activity.h" +#include + +class AutoConnectingActivity final : public Activity { +public: + AutoConnectingActivity( + GfxRenderer& renderer, + MappedInputManager& mappedInput, + const std::function& on_success, + const std::function& on_failure + ); + + void onEnter() override; + void onExit() override; + void loop() override; + +private: + void render() const; + void attemptConnection(); + void checkConnectionStatus(); + + const std::function on_success; + const std::function on_failure; + + unsigned long connectionStartTime = 0; + static constexpr unsigned long CONNECTION_TIMEOUT_MS = 15000; +}; diff --git a/src/activities/network/WifiSelectionActivity.cpp b/src/activities/network/WifiSelectionActivity.cpp index 8bf83a93..71b3071c 100644 --- a/src/activities/network/WifiSelectionActivity.cpp +++ b/src/activities/network/WifiSelectionActivity.cpp @@ -251,6 +251,11 @@ void WifiSelectionActivity::checkConnectionStatus() { snprintf(ipStr, sizeof(ipStr), "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); connectedIP = ipStr; + // Save the last connected SSID + strncpy(SETTINGS.lastConnectedSSID, selectedSSID.c_str(), sizeof(SETTINGS.lastConnectedSSID) - 1); + SETTINGS.lastConnectedSSID[sizeof(SETTINGS.lastConnectedSSID) - 1] = '\0'; + SETTINGS.saveToFile(); + // If we entered a new password, ask if user wants to save it // Otherwise, immediately complete so parent can start web server if (!usedSavedPassword && !enteredPassword.empty()) { @@ -311,14 +316,12 @@ void WifiSelectionActivity::loop() { // Handle save prompt state if (state == WifiSelectionState::SAVE_PROMPT) { - if (mappedInput.wasPressed(MappedInputManager::Button::Up) || - mappedInput.wasPressed(MappedInputManager::Button::Left)) { + if (mappedInput.wasPressed(MappedInputManager::Button::Left)) { if (savePromptSelection > 0) { savePromptSelection--; updateRequired = true; } - } else if (mappedInput.wasPressed(MappedInputManager::Button::Down) || - mappedInput.wasPressed(MappedInputManager::Button::Right)) { + } else if (mappedInput.wasPressed(MappedInputManager::Button::Right)) { if (savePromptSelection < 1) { savePromptSelection++; updateRequired = true; @@ -341,14 +344,12 @@ void WifiSelectionActivity::loop() { // Handle forget prompt state (connection failed with saved credentials) if (state == WifiSelectionState::FORGET_PROMPT) { - if (mappedInput.wasPressed(MappedInputManager::Button::Up) || - mappedInput.wasPressed(MappedInputManager::Button::Left)) { + if (mappedInput.wasPressed(MappedInputManager::Button::Left)) { if (forgetPromptSelection > 0) { forgetPromptSelection--; updateRequired = true; } - } else if (mappedInput.wasPressed(MappedInputManager::Button::Down) || - mappedInput.wasPressed(MappedInputManager::Button::Right)) { + } else if (mappedInput.wasPressed(MappedInputManager::Button::Right)) { if (forgetPromptSelection < 1) { forgetPromptSelection++; updateRequired = true; @@ -420,18 +421,23 @@ void WifiSelectionActivity::loop() { } // Handle UP/DOWN navigation - if (mappedInput.wasPressed(MappedInputManager::Button::Up) || - mappedInput.wasPressed(MappedInputManager::Button::Left)) { + if (mappedInput.wasPressed(MappedInputManager::Button::Up)) { if (selectedNetworkIndex > 0) { selectedNetworkIndex--; updateRequired = true; } - } else if (mappedInput.wasPressed(MappedInputManager::Button::Down) || - mappedInput.wasPressed(MappedInputManager::Button::Right)) { + } else if (mappedInput.wasPressed(MappedInputManager::Button::Down)) { if (!networks.empty() && selectedNetworkIndex < static_cast(networks.size()) - 1) { selectedNetworkIndex++; updateRequired = true; } + } else if (mappedInput.wasPressed(MappedInputManager::Button::Left)) { + if (!networks.empty() && networks[selectedNetworkIndex].hasSavedPassword) { + selectedSSID = networks[selectedNetworkIndex].ssid; + state = WifiSelectionState::FORGET_PROMPT; + forgetPromptSelection = 0; // Default to "Cancel" + updateRequired = true; + } } } } @@ -585,7 +591,7 @@ void WifiSelectionActivity::renderNetworkList() const { // Draw help text renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 75, "* = Encrypted | + = Saved"); - const auto labels = mappedInput.mapLabels("« Back", "Connect", "", ""); + const auto labels = mappedInput.mapLabels("« Back", "Connect", "Forget", ""); renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); } @@ -689,7 +695,7 @@ void WifiSelectionActivity::renderForgetPrompt() const { const auto height = renderer.getLineHeight(UI_10_FONT_ID); const auto top = (pageHeight - height * 3) / 2; - renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Connection Failed", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_12_FONT_ID, top - 40, "Forget Network", true, EpdFontFamily::BOLD); std::string ssidInfo = "Network: " + selectedSSID; if (ssidInfo.length() > 28) { diff --git a/src/activities/settings/CategorySettingsActivity.cpp b/src/activities/settings/CategorySettingsActivity.cpp index 7fd5ef5f..1d035aa6 100644 --- a/src/activities/settings/CategorySettingsActivity.cpp +++ b/src/activities/settings/CategorySettingsActivity.cpp @@ -11,6 +11,7 @@ #include "KOReaderSettingsActivity.h" #include "MappedInputManager.h" #include "OtaUpdateActivity.h" +#include "WifiSelectionActivity.h" #include "fontIds.h" void CategorySettingsActivity::taskTrampoline(void* param) { @@ -95,7 +96,15 @@ void CategorySettingsActivity::toggleCurrentSetting() { SETTINGS.*(setting.valuePtr) = currentValue + setting.valueRange.step; } } else if (setting.type == SettingType::ACTION) { - if (strcmp(setting.name, "KOReader Sync") == 0) { + if (strcmp(setting.name, "Network") == 0) { + xSemaphoreTake(renderingMutex, portMAX_DELAY); + exitActivity(); + enterNewActivity(new WifiSelectionActivity(renderer, mappedInput, [this] { + exitActivity(); + updateRequired = true; + })); + xSemaphoreGive(renderingMutex); + } else if (strcmp(setting.name, "KOReader Sync") == 0) { xSemaphoreTake(renderingMutex, portMAX_DELAY); exitActivity(); enterNewActivity(new KOReaderSettingsActivity(renderer, mappedInput, [this] { diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index 7316db05..c14ca9ea 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -48,10 +48,11 @@ const SettingInfo controlsSettings[controlsSettingsCount] = { SettingInfo::Toggle("Long-press Chapter Skip", &CrossPointSettings::longPressChapterSkip), SettingInfo::Enum("Short Power Button Click", &CrossPointSettings::shortPwrBtn, {"Ignore", "Sleep", "Page Turn"})}; -constexpr int systemSettingsCount = 5; +constexpr int systemSettingsCount = 6; const SettingInfo systemSettings[systemSettingsCount] = { SettingInfo::Enum("Time to Sleep", &CrossPointSettings::sleepTimeout, {"1 min", "5 min", "10 min", "15 min", "30 min"}), + SettingInfo::Action("Network"), SettingInfo::Action("KOReader Sync"), SettingInfo::Action("OPDS Browser"), SettingInfo::Action("Clear Cache"), SettingInfo::Action("Check for updates")}; } // namespace diff --git a/src/main.cpp b/src/main.cpp index 89c4e13c..936c82a6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,7 +24,10 @@ #include "activities/reader/ReaderActivity.h" #include "activities/settings/SettingsActivity.h" #include "activities/util/FullScreenMessageActivity.h" +#include "activities/network/AutoConnectingActivity.h" +#include "activities/network/WifiSelectionActivity.h" #include "fontIds.h" +#include HalDisplay display; HalGPIO gpio; @@ -211,9 +214,38 @@ void onGoToReader(const std::string& initialEpubPath, MyLibraryActivity::Tab fro } void onContinueReading() { onGoToReader(APP_STATE.openEpubPath, MyLibraryActivity::Tab::Recent); } +void onGoHome(); // forward declaration + +void withWifi(std::function on_success) { + if (WiFi.isConnected()) { + on_success(); + return; + } + + auto on_failure = [on_success]() { + exitActivity(); + enterNewActivity(new WifiSelectionActivity(renderer, mappedInputManager, [on_success](bool connected) { + if (connected) { + on_success(); + } else { + onGoHome(); + } + })); + }; + + if (strlen(SETTINGS.lastConnectedSSID) > 0) { + exitActivity(); + enterNewActivity(new AutoConnectingActivity(renderer, mappedInputManager, on_success, on_failure)); + } else { + on_failure(); + } +} + void onGoToFileTransfer() { - exitActivity(); - enterNewActivity(new CrossPointWebServerActivity(renderer, mappedInputManager, onGoHome)); + withWifi([]() { + exitActivity(); + enterNewActivity(new CrossPointWebServerActivity(renderer, mappedInputManager, onGoHome)); + }); } void onGoToSettings() { @@ -232,8 +264,10 @@ void onGoToMyLibraryWithTab(const std::string& path, MyLibraryActivity::Tab tab) } void onGoToBrowser() { - exitActivity(); - enterNewActivity(new OpdsBookBrowserActivity(renderer, mappedInputManager, onGoHome)); + withWifi([]() { + exitActivity(); + enterNewActivity(new OpdsBookBrowserActivity(renderer, mappedInputManager, onGoHome)); + }); } void onGoHome() {