feat: Connect to last wifi by default (#752)
## Summary * **What is the goal of this PR?** Use last connected network as default * **What changes are included?** - Refactor how an action type of Settings are handled - Add a new System Settings option → Network - Add the ability to forget a network in the Network Selection Screen - Add the ability to Refresh network list - Save the last connected network SSID - Use the last connection whenever network is needed (OPDS, Koreader sync, update etc) ## Additional Context * Add any other information that might be helpful for the reviewer (e.g., performance implications, potential risks, specific areas to focus on).   https://github.com/user-attachments/assets/95bf34a8-44ce-4279-8cd8-f78524ce745b --- ### AI Usage Did you use AI tools to help write this code? _** PARTIALLY: I wrote most of it but I also used Gemini as assist. --------- Co-authored-by: Eliz Kilic <elizk@google.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -9,7 +9,7 @@ WifiCredentialStore WifiCredentialStore::instance;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// File format version
|
// File format version
|
||||||
constexpr uint8_t WIFI_FILE_VERSION = 1;
|
constexpr uint8_t WIFI_FILE_VERSION = 2; // Increased version
|
||||||
|
|
||||||
// WiFi credentials file path
|
// WiFi credentials file path
|
||||||
constexpr char WIFI_FILE[] = "/.crosspoint/wifi.bin";
|
constexpr char WIFI_FILE[] = "/.crosspoint/wifi.bin";
|
||||||
@@ -38,6 +38,7 @@ bool WifiCredentialStore::saveToFile() const {
|
|||||||
|
|
||||||
// Write header
|
// Write header
|
||||||
serialization::writePod(file, WIFI_FILE_VERSION);
|
serialization::writePod(file, WIFI_FILE_VERSION);
|
||||||
|
serialization::writeString(file, lastConnectedSsid); // Save last connected SSID
|
||||||
serialization::writePod(file, static_cast<uint8_t>(credentials.size()));
|
serialization::writePod(file, static_cast<uint8_t>(credentials.size()));
|
||||||
|
|
||||||
// Write each credential
|
// Write each credential
|
||||||
@@ -67,12 +68,18 @@ bool WifiCredentialStore::loadFromFile() {
|
|||||||
// Read and verify version
|
// Read and verify version
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
serialization::readPod(file, version);
|
serialization::readPod(file, version);
|
||||||
if (version != WIFI_FILE_VERSION) {
|
if (version > WIFI_FILE_VERSION) {
|
||||||
Serial.printf("[%lu] [WCS] Unknown file version: %u\n", millis(), version);
|
Serial.printf("[%lu] [WCS] Unknown file version: %u\n", millis(), version);
|
||||||
file.close();
|
file.close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version >= 2) {
|
||||||
|
serialization::readString(file, lastConnectedSsid);
|
||||||
|
} else {
|
||||||
|
lastConnectedSsid.clear();
|
||||||
|
}
|
||||||
|
|
||||||
// Read credential count
|
// Read credential count
|
||||||
uint8_t count;
|
uint8_t count;
|
||||||
serialization::readPod(file, count);
|
serialization::readPod(file, count);
|
||||||
@@ -128,6 +135,9 @@ bool WifiCredentialStore::removeCredential(const std::string& ssid) {
|
|||||||
if (cred != credentials.end()) {
|
if (cred != credentials.end()) {
|
||||||
credentials.erase(cred);
|
credentials.erase(cred);
|
||||||
Serial.printf("[%lu] [WCS] Removed credentials for: %s\n", millis(), ssid.c_str());
|
Serial.printf("[%lu] [WCS] Removed credentials for: %s\n", millis(), ssid.c_str());
|
||||||
|
if (ssid == lastConnectedSsid) {
|
||||||
|
clearLastConnectedSsid();
|
||||||
|
}
|
||||||
return saveToFile();
|
return saveToFile();
|
||||||
}
|
}
|
||||||
return false; // Not found
|
return false; // Not found
|
||||||
@@ -146,8 +156,25 @@ const WifiCredential* WifiCredentialStore::findCredential(const std::string& ssi
|
|||||||
|
|
||||||
bool WifiCredentialStore::hasSavedCredential(const std::string& ssid) const { return findCredential(ssid) != nullptr; }
|
bool WifiCredentialStore::hasSavedCredential(const std::string& ssid) const { return findCredential(ssid) != nullptr; }
|
||||||
|
|
||||||
|
void WifiCredentialStore::setLastConnectedSsid(const std::string& ssid) {
|
||||||
|
if (lastConnectedSsid != ssid) {
|
||||||
|
lastConnectedSsid = ssid;
|
||||||
|
saveToFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& WifiCredentialStore::getLastConnectedSsid() const { return lastConnectedSsid; }
|
||||||
|
|
||||||
|
void WifiCredentialStore::clearLastConnectedSsid() {
|
||||||
|
if (!lastConnectedSsid.empty()) {
|
||||||
|
lastConnectedSsid.clear();
|
||||||
|
saveToFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void WifiCredentialStore::clearAll() {
|
void WifiCredentialStore::clearAll() {
|
||||||
credentials.clear();
|
credentials.clear();
|
||||||
|
lastConnectedSsid.clear();
|
||||||
saveToFile();
|
saveToFile();
|
||||||
Serial.printf("[%lu] [WCS] Cleared all WiFi credentials\n", millis());
|
Serial.printf("[%lu] [WCS] Cleared all WiFi credentials\n", millis());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class WifiCredentialStore {
|
|||||||
private:
|
private:
|
||||||
static WifiCredentialStore instance;
|
static WifiCredentialStore instance;
|
||||||
std::vector<WifiCredential> credentials;
|
std::vector<WifiCredential> credentials;
|
||||||
|
std::string lastConnectedSsid;
|
||||||
|
|
||||||
static constexpr size_t MAX_NETWORKS = 8;
|
static constexpr size_t MAX_NETWORKS = 8;
|
||||||
|
|
||||||
@@ -48,6 +49,11 @@ class WifiCredentialStore {
|
|||||||
// Check if a network is saved
|
// Check if a network is saved
|
||||||
bool hasSavedCredential(const std::string& ssid) const;
|
bool hasSavedCredential(const std::string& ssid) const;
|
||||||
|
|
||||||
|
// Last connected network
|
||||||
|
void setLastConnectedSsid(const std::string& ssid);
|
||||||
|
const std::string& getLastConnectedSsid() const;
|
||||||
|
void clearLastConnectedSsid();
|
||||||
|
|
||||||
// Clear all credentials
|
// Clear all credentials
|
||||||
void clearAll();
|
void clearAll();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ void WifiSelectionActivity::onEnter() {
|
|||||||
|
|
||||||
renderingMutex = xSemaphoreCreateMutex();
|
renderingMutex = xSemaphoreCreateMutex();
|
||||||
|
|
||||||
// Load saved WiFi credentials - SD card operations need lock as we use SPI for both
|
// Load saved WiFi credentials - SD card operations need lock as we use SPI
|
||||||
|
// for both
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
WIFI_STORE.loadFromFile();
|
WIFI_STORE.loadFromFile();
|
||||||
xSemaphoreGive(renderingMutex);
|
xSemaphoreGive(renderingMutex);
|
||||||
@@ -37,6 +38,7 @@ void WifiSelectionActivity::onEnter() {
|
|||||||
usedSavedPassword = false;
|
usedSavedPassword = false;
|
||||||
savePromptSelection = 0;
|
savePromptSelection = 0;
|
||||||
forgetPromptSelection = 0;
|
forgetPromptSelection = 0;
|
||||||
|
autoConnecting = false;
|
||||||
|
|
||||||
// Cache MAC address for display
|
// Cache MAC address for display
|
||||||
uint8_t mac[6];
|
uint8_t mac[6];
|
||||||
@@ -46,9 +48,7 @@ void WifiSelectionActivity::onEnter() {
|
|||||||
mac[5]);
|
mac[5]);
|
||||||
cachedMacAddress = std::string(macStr);
|
cachedMacAddress = std::string(macStr);
|
||||||
|
|
||||||
// Trigger first update to show scanning message
|
// Task creation
|
||||||
updateRequired = true;
|
|
||||||
|
|
||||||
xTaskCreate(&WifiSelectionActivity::taskTrampoline, "WifiSelectionTask",
|
xTaskCreate(&WifiSelectionActivity::taskTrampoline, "WifiSelectionTask",
|
||||||
4096, // Stack size (larger for WiFi operations)
|
4096, // Stack size (larger for WiFi operations)
|
||||||
this, // Parameters
|
this, // Parameters
|
||||||
@@ -56,7 +56,26 @@ void WifiSelectionActivity::onEnter() {
|
|||||||
&displayTaskHandle // Task handle
|
&displayTaskHandle // Task handle
|
||||||
);
|
);
|
||||||
|
|
||||||
// Start WiFi scan
|
// Attempt to auto-connect to the last network
|
||||||
|
if (allowAutoConnect) {
|
||||||
|
const std::string lastSsid = WIFI_STORE.getLastConnectedSsid();
|
||||||
|
if (!lastSsid.empty()) {
|
||||||
|
const auto* cred = WIFI_STORE.findCredential(lastSsid);
|
||||||
|
if (cred) {
|
||||||
|
Serial.printf("[%lu] [WIFI] Attempting to auto-connect to %s\n", millis(), lastSsid.c_str());
|
||||||
|
selectedSSID = cred->ssid;
|
||||||
|
enteredPassword = cred->password;
|
||||||
|
selectedRequiresPassword = !cred->password.empty();
|
||||||
|
usedSavedPassword = true;
|
||||||
|
autoConnecting = true;
|
||||||
|
attemptConnection();
|
||||||
|
updateRequired = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to scanning
|
||||||
startWifiScan();
|
startWifiScan();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,15 +89,17 @@ void WifiSelectionActivity::onExit() {
|
|||||||
WiFi.scanDelete();
|
WiFi.scanDelete();
|
||||||
Serial.printf("[%lu] [WIFI] [MEM] Free heap after scanDelete: %d bytes\n", millis(), ESP.getFreeHeap());
|
Serial.printf("[%lu] [WIFI] [MEM] Free heap after scanDelete: %d bytes\n", millis(), ESP.getFreeHeap());
|
||||||
|
|
||||||
// Note: We do NOT disconnect WiFi here - the parent activity (CrossPointWebServerActivity)
|
// Note: We do NOT disconnect WiFi here - the parent activity
|
||||||
// manages WiFi connection state. We just clean up the scan and task.
|
// (CrossPointWebServerActivity) manages WiFi connection state. We just clean
|
||||||
|
// up the scan and task.
|
||||||
|
|
||||||
// Acquire mutex before deleting task to ensure task isn't using it
|
// Acquire mutex before deleting task to ensure task isn't using it
|
||||||
// This prevents hangs/crashes if the task holds the mutex when deleted
|
// This prevents hangs/crashes if the task holds the mutex when deleted
|
||||||
Serial.printf("[%lu] [WIFI] Acquiring rendering mutex before task deletion...\n", millis());
|
Serial.printf("[%lu] [WIFI] Acquiring rendering mutex before task deletion...\n", millis());
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
|
|
||||||
// Delete the display task (we now hold the mutex, so task is blocked if it needs it)
|
// Delete the display task (we now hold the mutex, so task is blocked if it
|
||||||
|
// needs it)
|
||||||
Serial.printf("[%lu] [WIFI] Deleting display task...\n", millis());
|
Serial.printf("[%lu] [WIFI] Deleting display task...\n", millis());
|
||||||
if (displayTaskHandle) {
|
if (displayTaskHandle) {
|
||||||
vTaskDelete(displayTaskHandle);
|
vTaskDelete(displayTaskHandle);
|
||||||
@@ -96,6 +117,7 @@ void WifiSelectionActivity::onExit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WifiSelectionActivity::startWifiScan() {
|
void WifiSelectionActivity::startWifiScan() {
|
||||||
|
autoConnecting = false;
|
||||||
state = WifiSelectionState::SCANNING;
|
state = WifiSelectionState::SCANNING;
|
||||||
networks.clear();
|
networks.clear();
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
@@ -181,6 +203,7 @@ void WifiSelectionActivity::selectNetwork(const int index) {
|
|||||||
selectedRequiresPassword = network.isEncrypted;
|
selectedRequiresPassword = network.isEncrypted;
|
||||||
usedSavedPassword = false;
|
usedSavedPassword = false;
|
||||||
enteredPassword.clear();
|
enteredPassword.clear();
|
||||||
|
autoConnecting = false;
|
||||||
|
|
||||||
// Check if we have saved credentials for this network
|
// Check if we have saved credentials for this network
|
||||||
const auto* savedCred = WIFI_STORE.findCredential(selectedSSID);
|
const auto* savedCred = WIFI_STORE.findCredential(selectedSSID);
|
||||||
@@ -223,7 +246,7 @@ void WifiSelectionActivity::selectNetwork(const int index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WifiSelectionActivity::attemptConnection() {
|
void WifiSelectionActivity::attemptConnection() {
|
||||||
state = WifiSelectionState::CONNECTING;
|
state = autoConnecting ? WifiSelectionState::AUTO_CONNECTING : WifiSelectionState::CONNECTING;
|
||||||
connectionStartTime = millis();
|
connectionStartTime = millis();
|
||||||
connectedIP.clear();
|
connectedIP.clear();
|
||||||
connectionError.clear();
|
connectionError.clear();
|
||||||
@@ -239,7 +262,7 @@ void WifiSelectionActivity::attemptConnection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WifiSelectionActivity::checkConnectionStatus() {
|
void WifiSelectionActivity::checkConnectionStatus() {
|
||||||
if (state != WifiSelectionState::CONNECTING) {
|
if (state != WifiSelectionState::CONNECTING && state != WifiSelectionState::AUTO_CONNECTING) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,6 +274,13 @@ void WifiSelectionActivity::checkConnectionStatus() {
|
|||||||
char ipStr[16];
|
char ipStr[16];
|
||||||
snprintf(ipStr, sizeof(ipStr), "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
|
snprintf(ipStr, sizeof(ipStr), "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
|
||||||
connectedIP = ipStr;
|
connectedIP = ipStr;
|
||||||
|
autoConnecting = false;
|
||||||
|
|
||||||
|
// Save this as the last connected network - SD card operations need lock as
|
||||||
|
// we use SPI for both
|
||||||
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
|
WIFI_STORE.setLastConnectedSsid(selectedSSID);
|
||||||
|
xSemaphoreGive(renderingMutex);
|
||||||
|
|
||||||
// If we entered a new password, ask if user wants to save it
|
// If we entered a new password, ask if user wants to save it
|
||||||
// Otherwise, immediately complete so parent can start web server
|
// Otherwise, immediately complete so parent can start web server
|
||||||
@@ -260,7 +290,10 @@ void WifiSelectionActivity::checkConnectionStatus() {
|
|||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
} else {
|
} else {
|
||||||
// Using saved password or open network - complete immediately
|
// Using saved password or open network - complete immediately
|
||||||
Serial.printf("[%lu] [WIFI] Connected with saved/open credentials, completing immediately\n", millis());
|
Serial.printf(
|
||||||
|
"[%lu] [WIFI] Connected with saved/open credentials, "
|
||||||
|
"completing immediately\n",
|
||||||
|
millis());
|
||||||
onComplete(true);
|
onComplete(true);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -299,7 +332,7 @@ void WifiSelectionActivity::loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check connection progress
|
// Check connection progress
|
||||||
if (state == WifiSelectionState::CONNECTING) {
|
if (state == WifiSelectionState::CONNECTING || state == WifiSelectionState::AUTO_CONNECTING) {
|
||||||
checkConnectionStatus();
|
checkConnectionStatus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -368,17 +401,16 @@ void WifiSelectionActivity::loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Go back to network list (whether Cancel or Forget network was selected)
|
// Go back to network list (whether Cancel or Forget network was selected)
|
||||||
state = WifiSelectionState::NETWORK_LIST;
|
startWifiScan();
|
||||||
updateRequired = true;
|
|
||||||
} else if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
|
} else if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
|
||||||
// Skip forgetting, go back to network list
|
// Skip forgetting, go back to network list
|
||||||
state = WifiSelectionState::NETWORK_LIST;
|
startWifiScan();
|
||||||
updateRequired = true;
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle connected state (should not normally be reached - connection completes immediately)
|
// Handle connected state (should not normally be reached - connection
|
||||||
|
// completes immediately)
|
||||||
if (state == WifiSelectionState::CONNECTED) {
|
if (state == WifiSelectionState::CONNECTED) {
|
||||||
// Safety fallback - immediately complete
|
// Safety fallback - immediately complete
|
||||||
onComplete(true);
|
onComplete(true);
|
||||||
@@ -389,12 +421,14 @@ void WifiSelectionActivity::loop() {
|
|||||||
if (state == WifiSelectionState::CONNECTION_FAILED) {
|
if (state == WifiSelectionState::CONNECTION_FAILED) {
|
||||||
if (mappedInput.wasPressed(MappedInputManager::Button::Back) ||
|
if (mappedInput.wasPressed(MappedInputManager::Button::Back) ||
|
||||||
mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
|
mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
|
||||||
// If we used saved credentials, offer to forget the network
|
// If we were auto-connecting or using a saved credential, offer to forget
|
||||||
if (usedSavedPassword) {
|
// the network
|
||||||
|
if (autoConnecting || usedSavedPassword) {
|
||||||
|
autoConnecting = false;
|
||||||
state = WifiSelectionState::FORGET_PROMPT;
|
state = WifiSelectionState::FORGET_PROMPT;
|
||||||
forgetPromptSelection = 0; // Default to "Cancel"
|
forgetPromptSelection = 0; // Default to "Cancel"
|
||||||
} else {
|
} else {
|
||||||
// Go back to network list on failure
|
// Go back to network list on failure for non-saved credentials
|
||||||
state = WifiSelectionState::NETWORK_LIST;
|
state = WifiSelectionState::NETWORK_LIST;
|
||||||
}
|
}
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
@@ -420,6 +454,23 @@ void WifiSelectionActivity::loop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mappedInput.wasPressed(MappedInputManager::Button::Right)) {
|
||||||
|
startWifiScan();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool leftPressed = mappedInput.wasPressed(MappedInputManager::Button::Left);
|
||||||
|
if (leftPressed) {
|
||||||
|
const bool hasSavedPassword = !networks.empty() && networks[selectedNetworkIndex].hasSavedPassword;
|
||||||
|
if (hasSavedPassword) {
|
||||||
|
selectedSSID = networks[selectedNetworkIndex].ssid;
|
||||||
|
state = WifiSelectionState::FORGET_PROMPT;
|
||||||
|
forgetPromptSelection = 0; // Default to "Cancel"
|
||||||
|
updateRequired = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle navigation
|
// Handle navigation
|
||||||
buttonNavigator.onNext([this] {
|
buttonNavigator.onNext([this] {
|
||||||
selectedNetworkIndex = ButtonNavigator::nextIndex(selectedNetworkIndex, networks.size());
|
selectedNetworkIndex = ButtonNavigator::nextIndex(selectedNetworkIndex, networks.size());
|
||||||
@@ -479,6 +530,9 @@ void WifiSelectionActivity::render() const {
|
|||||||
renderer.clearScreen();
|
renderer.clearScreen();
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
|
case WifiSelectionState::AUTO_CONNECTING:
|
||||||
|
renderConnecting();
|
||||||
|
break;
|
||||||
case WifiSelectionState::SCANNING:
|
case WifiSelectionState::SCANNING:
|
||||||
renderConnecting(); // Reuse connecting screen with different message
|
renderConnecting(); // Reuse connecting screen with different message
|
||||||
break;
|
break;
|
||||||
@@ -582,7 +636,11 @@ void WifiSelectionActivity::renderNetworkList() const {
|
|||||||
|
|
||||||
// Draw help text
|
// Draw help text
|
||||||
renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 75, "* = Encrypted | + = Saved");
|
renderer.drawText(SMALL_FONT_ID, 20, pageHeight - 75, "* = Encrypted | + = Saved");
|
||||||
const auto labels = mappedInput.mapLabels("« Back", "Connect", "", "");
|
|
||||||
|
const bool hasSavedPassword = !networks.empty() && networks[selectedNetworkIndex].hasSavedPassword;
|
||||||
|
const char* forgetLabel = hasSavedPassword ? "Forget" : "";
|
||||||
|
|
||||||
|
const auto labels = mappedInput.mapLabels("« Back", "Connect", forgetLabel, "Refresh");
|
||||||
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,8 +744,7 @@ void WifiSelectionActivity::renderForgetPrompt() const {
|
|||||||
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
|
const auto height = renderer.getLineHeight(UI_10_FONT_ID);
|
||||||
const auto top = (pageHeight - height * 3) / 2;
|
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;
|
std::string ssidInfo = "Network: " + selectedSSID;
|
||||||
if (ssidInfo.length() > 28) {
|
if (ssidInfo.length() > 28) {
|
||||||
ssidInfo.replace(25, ssidInfo.length() - 25, "...");
|
ssidInfo.replace(25, ssidInfo.length() - 25, "...");
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ struct WifiNetworkInfo {
|
|||||||
|
|
||||||
// WiFi selection states
|
// WiFi selection states
|
||||||
enum class WifiSelectionState {
|
enum class WifiSelectionState {
|
||||||
|
AUTO_CONNECTING, // Trying to connect to the last known network
|
||||||
SCANNING, // Scanning for networks
|
SCANNING, // Scanning for networks
|
||||||
NETWORK_LIST, // Displaying available networks
|
NETWORK_LIST, // Displaying available networks
|
||||||
PASSWORD_ENTRY, // Entering password for selected network
|
PASSWORD_ENTRY, // Entering password for selected network
|
||||||
@@ -70,6 +71,12 @@ class WifiSelectionActivity final : public ActivityWithSubactivity {
|
|||||||
// Whether network was connected using a saved password (skip save prompt)
|
// Whether network was connected using a saved password (skip save prompt)
|
||||||
bool usedSavedPassword = false;
|
bool usedSavedPassword = false;
|
||||||
|
|
||||||
|
// Whether to attempt auto-connect on entry
|
||||||
|
const bool allowAutoConnect;
|
||||||
|
|
||||||
|
// Whether we are attempting to auto-connect
|
||||||
|
bool autoConnecting = false;
|
||||||
|
|
||||||
// Save/forget prompt selection (0 = Yes, 1 = No)
|
// Save/forget prompt selection (0 = Yes, 1 = No)
|
||||||
int savePromptSelection = 0;
|
int savePromptSelection = 0;
|
||||||
int forgetPromptSelection = 0;
|
int forgetPromptSelection = 0;
|
||||||
@@ -98,8 +105,10 @@ class WifiSelectionActivity final : public ActivityWithSubactivity {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit WifiSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
explicit WifiSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
||||||
const std::function<void(bool connected)>& onComplete)
|
const std::function<void(bool connected)>& onComplete, bool autoConnect = true)
|
||||||
: ActivityWithSubactivity("WifiSelection", renderer, mappedInput), onComplete(onComplete) {}
|
: ActivityWithSubactivity("WifiSelection", renderer, mappedInput),
|
||||||
|
onComplete(onComplete),
|
||||||
|
allowAutoConnect(autoConnect) {}
|
||||||
void onEnter() override;
|
void onEnter() override;
|
||||||
void onExit() override;
|
void onExit() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "OtaUpdateActivity.h"
|
#include "OtaUpdateActivity.h"
|
||||||
#include "SettingsList.h"
|
#include "SettingsList.h"
|
||||||
|
#include "activities/network/WifiSelectionActivity.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|
||||||
@@ -46,11 +47,13 @@ void SettingsActivity::onEnter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Append device-only ACTION items
|
// Append device-only ACTION items
|
||||||
controlsSettings.insert(controlsSettings.begin(), SettingInfo::Action("Remap Front Buttons"));
|
controlsSettings.insert(controlsSettings.begin(),
|
||||||
systemSettings.push_back(SettingInfo::Action("KOReader Sync"));
|
SettingInfo::Action("Remap Front Buttons", SettingAction::RemapFrontButtons));
|
||||||
systemSettings.push_back(SettingInfo::Action("OPDS Browser"));
|
systemSettings.push_back(SettingInfo::Action("Network", SettingAction::Network));
|
||||||
systemSettings.push_back(SettingInfo::Action("Clear Cache"));
|
systemSettings.push_back(SettingInfo::Action("KOReader Sync", SettingAction::KOReaderSync));
|
||||||
systemSettings.push_back(SettingInfo::Action("Check for updates"));
|
systemSettings.push_back(SettingInfo::Action("OPDS Browser", SettingAction::OPDSBrowser));
|
||||||
|
systemSettings.push_back(SettingInfo::Action("Clear Cache", SettingAction::ClearCache));
|
||||||
|
systemSettings.push_back(SettingInfo::Action("Check for updates", SettingAction::CheckForUpdates));
|
||||||
|
|
||||||
// Reset selection to first category
|
// Reset selection to first category
|
||||||
selectedCategoryIndex = 0;
|
selectedCategoryIndex = 0;
|
||||||
@@ -178,46 +181,45 @@ void SettingsActivity::toggleCurrentSetting() {
|
|||||||
SETTINGS.*(setting.valuePtr) = currentValue + setting.valueRange.step;
|
SETTINGS.*(setting.valuePtr) = currentValue + setting.valueRange.step;
|
||||||
}
|
}
|
||||||
} else if (setting.type == SettingType::ACTION) {
|
} else if (setting.type == SettingType::ACTION) {
|
||||||
if (strcmp(setting.name, "Remap Front Buttons") == 0) {
|
auto enterSubActivity = [this](Activity* activity) {
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new ButtonRemapActivity(renderer, mappedInput, [this] {
|
enterNewActivity(activity);
|
||||||
exitActivity();
|
|
||||||
updateRequired = true;
|
|
||||||
}));
|
|
||||||
xSemaphoreGive(renderingMutex);
|
xSemaphoreGive(renderingMutex);
|
||||||
} else if (strcmp(setting.name, "KOReader Sync") == 0) {
|
};
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
||||||
|
auto onComplete = [this] {
|
||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new KOReaderSettingsActivity(renderer, mappedInput, [this] {
|
updateRequired = true;
|
||||||
exitActivity();
|
};
|
||||||
updateRequired = true;
|
|
||||||
}));
|
auto onCompleteBool = [this](bool) {
|
||||||
xSemaphoreGive(renderingMutex);
|
|
||||||
} else if (strcmp(setting.name, "OPDS Browser") == 0) {
|
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
|
||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new CalibreSettingsActivity(renderer, mappedInput, [this] {
|
updateRequired = true;
|
||||||
exitActivity();
|
};
|
||||||
updateRequired = true;
|
|
||||||
}));
|
switch (setting.action) {
|
||||||
xSemaphoreGive(renderingMutex);
|
case SettingAction::RemapFrontButtons:
|
||||||
} else if (strcmp(setting.name, "Clear Cache") == 0) {
|
enterSubActivity(new ButtonRemapActivity(renderer, mappedInput, onComplete));
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
break;
|
||||||
exitActivity();
|
case SettingAction::KOReaderSync:
|
||||||
enterNewActivity(new ClearCacheActivity(renderer, mappedInput, [this] {
|
enterSubActivity(new KOReaderSettingsActivity(renderer, mappedInput, onComplete));
|
||||||
exitActivity();
|
break;
|
||||||
updateRequired = true;
|
case SettingAction::OPDSBrowser:
|
||||||
}));
|
enterSubActivity(new CalibreSettingsActivity(renderer, mappedInput, onComplete));
|
||||||
xSemaphoreGive(renderingMutex);
|
break;
|
||||||
} else if (strcmp(setting.name, "Check for updates") == 0) {
|
case SettingAction::Network:
|
||||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
enterSubActivity(new WifiSelectionActivity(renderer, mappedInput, onCompleteBool, false));
|
||||||
exitActivity();
|
break;
|
||||||
enterNewActivity(new OtaUpdateActivity(renderer, mappedInput, [this] {
|
case SettingAction::ClearCache:
|
||||||
exitActivity();
|
enterSubActivity(new ClearCacheActivity(renderer, mappedInput, onComplete));
|
||||||
updateRequired = true;
|
break;
|
||||||
}));
|
case SettingAction::CheckForUpdates:
|
||||||
xSemaphoreGive(renderingMutex);
|
enterSubActivity(new OtaUpdateActivity(renderer, mappedInput, onComplete));
|
||||||
|
break;
|
||||||
|
case SettingAction::None:
|
||||||
|
// Do nothing
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -14,11 +14,22 @@ class CrossPointSettings;
|
|||||||
|
|
||||||
enum class SettingType { TOGGLE, ENUM, ACTION, VALUE, STRING };
|
enum class SettingType { TOGGLE, ENUM, ACTION, VALUE, STRING };
|
||||||
|
|
||||||
|
enum class SettingAction {
|
||||||
|
None,
|
||||||
|
RemapFrontButtons,
|
||||||
|
KOReaderSync,
|
||||||
|
OPDSBrowser,
|
||||||
|
Network,
|
||||||
|
ClearCache,
|
||||||
|
CheckForUpdates,
|
||||||
|
};
|
||||||
|
|
||||||
struct SettingInfo {
|
struct SettingInfo {
|
||||||
const char* name;
|
const char* name;
|
||||||
SettingType type;
|
SettingType type;
|
||||||
uint8_t CrossPointSettings::* valuePtr = nullptr;
|
uint8_t CrossPointSettings::* valuePtr = nullptr;
|
||||||
std::vector<std::string> enumValues;
|
std::vector<std::string> enumValues;
|
||||||
|
SettingAction action = SettingAction::None;
|
||||||
|
|
||||||
struct ValueRange {
|
struct ValueRange {
|
||||||
uint8_t min;
|
uint8_t min;
|
||||||
@@ -63,10 +74,11 @@ struct SettingInfo {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SettingInfo Action(const char* name) {
|
static SettingInfo Action(const char* name, SettingAction action) {
|
||||||
SettingInfo s;
|
SettingInfo s;
|
||||||
s.name = name;
|
s.name = name;
|
||||||
s.type = SettingType::ACTION;
|
s.type = SettingType::ACTION;
|
||||||
|
s.action = action;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user