Files
Crosspoint/src/activities/network/WifiSelectionActivity.h
T
Xuan-Son NguyenandZach Nelson a94f6ca80b refactor: implement ActivityManager (#1016)
## Summary

Ref comment:
https://github.com/crosspoint-reader/crosspoint-reader/pull/1010#pullrequestreview-3828854640

This PR introduces `ActivityManager`, which mirrors the same concept of
Activity in Android, where an activity represents a single screen of the
UI. The manager is responsible for launching activities, and ensuring
that only one activity is active at a time.

Main differences from Android's ActivityManager:
- No concept of Bundle or Intent extras
- No onPause/onResume, since we don't have a concept of background
activities
- onActivityResult is implemented via a callback instead of a separate
method, for simplicity

## Key changes

- Single `renderTask` shared across all activities
- No more sub-activity, we manage them using a stack; Results can be
passed via `startActivityForResult` and `setResult`
- Activity can call `finish()` to destroy themself, but the actual
deletion will be handled by `ActivityManager` to avoid `delete this`
pattern

As a bonus: the manager will automatically call `requestUpdate()` when
returning from another activity

## Example usage

**BEFORE**:

```cpp
// caller
    enterNewActivity(new WifiSelectionActivity(renderer, mappedInput,
                                               [this](const bool connected) { onWifiSelectionComplete(connected); }));

// subactivity
  onComplete(true); // will eventually call exitActivity(), which deletes the caller instance (dangerous behavior)
``` 

**AFTER**: (mirrors the `startActivityForResult` and `setResult` from
android)

```cpp
// caller
  startActivityForResult(new NetworkModeSelectionActivity(renderer, mappedInput),
                         [this](const ActivityResult& result) { onNetworkModeSelected(result.selectedNetworkMode); });

// subactivity
  ActivityResult result;
  result.isCancelled = false;
  result.selectedNetworkMode = mode;
  setResult(result);
  finish(); // signals to ActivityManager to go back to last activity AFTER this function returns
```

TODO:
- [x] Reconsider if the `Intent` is really necessary or it should be
removed (note: it's inspired by
[Intent](https://developer.android.com/guide/components/intents-common)
from Android API) ==> I decided to keep this pattern fr clarity
- [x] Verify if behavior is still correct (i.e. back from sub-activity)
- [x] Refactor the `ActivityWithSubactivity` to just simple `Activity`
--> We are using a stack for keeping track of sub-activity now
- [x] Use single task for rendering --> avoid allocating 8KB stack per
activity
- [x] Implement the idea of [Activity
result](https://developer.android.com/training/basics/intents/result)
--> Allow sub-activity like Wifi to report back the status (connected,
failed, etc)

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? **PARTIALLY**, some
repetitive migrations are done by Claude, but I'm the one how ultimately
approve it

---------

Co-authored-by: Zach Nelson <zach@zdnelson.com>
2026-02-27 00:32:40 -06:00

108 lines
3.3 KiB
C++

#pragma once
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "activities/Activity.h"
#include "util/ButtonNavigator.h"
// Structure to hold WiFi network information
struct WifiNetworkInfo {
std::string ssid;
int32_t rssi;
bool isEncrypted;
bool hasSavedPassword; // Whether we have saved credentials for this network
std::string ipAddress; // Populated after connection for display
};
// WiFi selection states
enum class WifiSelectionState {
AUTO_CONNECTING, // Trying to connect to the last known network
SCANNING, // Scanning for networks
NETWORK_LIST, // Displaying available networks
PASSWORD_ENTRY, // Entering password for selected network
CONNECTING, // Attempting to connect
CONNECTED, // Successfully connected
SAVE_PROMPT, // Asking user if they want to save the password
CONNECTION_FAILED, // Connection failed
FORGET_PROMPT // Asking user if they want to forget the network
};
/**
* WifiSelectionActivity is responsible for scanning WiFi APs and connecting to them.
* It will:
* - Enter scanning mode on entry
* - List available WiFi networks
* - Allow selection and launch KeyboardEntryActivity for password if needed
* - Save the password if requested
* - Call onComplete callback when connected or cancelled
*
* The onComplete callback receives true if connected successfully, false if cancelled.
*/
class WifiSelectionActivity final : public Activity {
ButtonNavigator buttonNavigator;
WifiSelectionState state = WifiSelectionState::SCANNING;
size_t selectedNetworkIndex = 0;
std::vector<WifiNetworkInfo> networks;
// Selected network for connection
std::string selectedSSID;
bool selectedRequiresPassword = false;
// Connection result
std::string connectedIP;
std::string connectionError;
// Password to potentially save (from keyboard or saved credentials)
std::string enteredPassword;
// Cached MAC address string for display
std::string cachedMacAddress;
// Whether network was connected using a saved password (skip save prompt)
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)
int savePromptSelection = 0;
int forgetPromptSelection = 0;
// Connection timeout
static constexpr unsigned long CONNECTION_TIMEOUT_MS = 15000;
unsigned long connectionStartTime = 0;
void renderNetworkList() const;
void renderPasswordEntry() const;
void renderConnecting() const;
void renderConnected() const;
void renderSavePrompt() const;
void renderConnectionFailed() const;
void renderForgetPrompt() const;
void startWifiScan();
void processWifiScanResults();
void selectNetwork(int index);
void attemptConnection();
void checkConnectionStatus();
std::string getSignalStrengthIndicator(int32_t rssi) const;
void onComplete(bool connected);
public:
explicit WifiSelectionActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, bool autoConnect = true)
: Activity("WifiSelection", renderer, mappedInput), allowAutoConnect(autoConnect) {}
void onEnter() override;
void onExit() override;
void loop() override;
void render(RenderLock&&) override;
};