mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-17 08:35:21 -04:00
feat(network): add saved WiFi state to settings (#2648)
This commit is contained in:
@@ -125,6 +125,8 @@ State updates are sent whenever network configuration changes:
|
|||||||
- `wifiConnected`: Whether associated with an access point
|
- `wifiConnected`: Whether associated with an access point
|
||||||
- `wifiSSID`: Currently connected network name
|
- `wifiSSID`: Currently connected network name
|
||||||
- `wifiIP`: Assigned IP address (empty until DHCP completes)
|
- `wifiIP`: Assigned IP address (empty until DHCP completes)
|
||||||
|
- `savedWifiNetworks` (API v26+): Saved WiFi profiles exposed at SSID granularity. If a backend has multiple profiles for the same SSID, DMS merges them into one SSID-level entry. Clients talking to older servers should derive saved visible networks from `wifiNetworks` entries where `saved` is true.
|
||||||
|
- `savedWifiNetworks[].outOfRange` (API v26+): Whether the saved profile is not currently visible in scan results. Fallback entries derived from `wifiNetworks` should be treated as visible (`outOfRange: false`).
|
||||||
- `lastError`: Error message from last failed connection attempt
|
- `lastError`: Error message from last failed connection attempt
|
||||||
|
|
||||||
### network.credentials Service Events
|
### network.credentials Service Events
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ type BackendState struct {
|
|||||||
WiFiBSSID string
|
WiFiBSSID string
|
||||||
WiFiSignal uint8
|
WiFiSignal uint8
|
||||||
WiFiNetworks []WiFiNetwork
|
WiFiNetworks []WiFiNetwork
|
||||||
|
SavedWiFiNetworks []WiFiNetwork
|
||||||
WiFiDevices []WiFiDevice
|
WiFiDevices []WiFiDevice
|
||||||
WiredConnections []WiredConnection
|
WiredConnections []WiredConnection
|
||||||
VPNProfiles []VPNProfile
|
VPNProfiles []VPNProfile
|
||||||
|
|||||||
@@ -27,6 +27,19 @@ func TestHybridIwdNetworkdBackend_GetCurrentState_MergesState(t *testing.T) {
|
|||||||
wifi.state.WiFiBSSID = "00:11:22:33:44:55"
|
wifi.state.WiFiBSSID = "00:11:22:33:44:55"
|
||||||
wifi.state.WiFiSignal = 75
|
wifi.state.WiFiSignal = 75
|
||||||
wifi.state.WiFiDevice = "wlan0"
|
wifi.state.WiFiDevice = "wlan0"
|
||||||
|
wifi.state.SavedWiFiNetworks = []WiFiNetwork{
|
||||||
|
{
|
||||||
|
SSID: "TestNetwork",
|
||||||
|
Saved: true,
|
||||||
|
Autoconnect: true,
|
||||||
|
Connected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SSID: "AwayNetwork",
|
||||||
|
Saved: true,
|
||||||
|
OutOfRange: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
l3.state.WiFiIP = "192.168.1.100"
|
l3.state.WiFiIP = "192.168.1.100"
|
||||||
l3.state.EthernetConnected = false
|
l3.state.EthernetConnected = false
|
||||||
@@ -42,6 +55,9 @@ func TestHybridIwdNetworkdBackend_GetCurrentState_MergesState(t *testing.T) {
|
|||||||
assert.True(t, state.WiFiConnected)
|
assert.True(t, state.WiFiConnected)
|
||||||
assert.False(t, state.EthernetConnected)
|
assert.False(t, state.EthernetConnected)
|
||||||
assert.Equal(t, StatusWiFi, state.NetworkStatus)
|
assert.Equal(t, StatusWiFi, state.NetworkStatus)
|
||||||
|
assert.Len(t, state.SavedWiFiNetworks, 2)
|
||||||
|
assert.Equal(t, "TestNetwork", state.SavedWiFiNetworks[0].SSID)
|
||||||
|
assert.True(t, state.SavedWiFiNetworks[1].OutOfRange)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHybridIwdNetworkdBackend_GetCurrentState_EthernetPriority(t *testing.T) {
|
func TestHybridIwdNetworkdBackend_GetCurrentState_EthernetPriority(t *testing.T) {
|
||||||
|
|||||||
@@ -80,6 +80,10 @@ func (b *IWDBackend) Initialize() error {
|
|||||||
return fmt.Errorf("failed to discover iwd devices: %w", err)
|
return fmt.Errorf("failed to discover iwd devices: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := b.updateSavedWiFiNetworks(); err != nil {
|
||||||
|
log.Warnf("Failed to get initial saved WiFi networks: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := b.updateState(); err != nil {
|
if err := b.updateState(); err != nil {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return fmt.Errorf("failed to get initial state: %w", err)
|
return fmt.Errorf("failed to get initial state: %w", err)
|
||||||
@@ -145,6 +149,7 @@ func (b *IWDBackend) GetCurrentState() (*BackendState, error) {
|
|||||||
|
|
||||||
state := *b.state
|
state := *b.state
|
||||||
state.WiFiNetworks = append([]WiFiNetwork(nil), b.state.WiFiNetworks...)
|
state.WiFiNetworks = append([]WiFiNetwork(nil), b.state.WiFiNetworks...)
|
||||||
|
state.SavedWiFiNetworks = append([]WiFiNetwork(nil), b.state.SavedWiFiNetworks...)
|
||||||
state.WiredConnections = append([]WiredConnection(nil), b.state.WiredConnections...)
|
state.WiredConnections = append([]WiredConnection(nil), b.state.WiredConnections...)
|
||||||
state.WiFiDevices = b.getWiFiDevicesLocked()
|
state.WiFiDevices = b.getWiFiDevicesLocked()
|
||||||
|
|
||||||
|
|||||||
@@ -45,12 +45,42 @@ func (b *IWDBackend) StartMonitoring(onStateChange func()) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := b.conn.AddMatchSignal(
|
||||||
|
dbus.WithMatchInterface(dbusPropertiesInterface),
|
||||||
|
dbus.WithMatchMember("PropertiesChanged"),
|
||||||
|
dbus.WithMatchArg(0, iwdKnownNetworkInterface),
|
||||||
|
); err != nil {
|
||||||
|
return fmt.Errorf("failed to add known network signal match: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := b.conn.AddMatchSignal(
|
||||||
|
dbus.WithMatchInterface(dbusObjectManager),
|
||||||
|
dbus.WithMatchMember("InterfacesAdded"),
|
||||||
|
); err != nil {
|
||||||
|
return fmt.Errorf("failed to add iwd interfaces-added signal match: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := b.conn.AddMatchSignal(
|
||||||
|
dbus.WithMatchInterface(dbusObjectManager),
|
||||||
|
dbus.WithMatchMember("InterfacesRemoved"),
|
||||||
|
); err != nil {
|
||||||
|
return fmt.Errorf("failed to add iwd interfaces-removed signal match: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
b.sigWG.Add(1)
|
b.sigWG.Add(1)
|
||||||
go b.signalHandler(sigChan)
|
go b.signalHandler(sigChan)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *IWDBackend) refreshWiFiNetworkState() bool {
|
||||||
|
_, err := b.updateWiFiNetworks()
|
||||||
|
if err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return b.updateSavedWiFiNetworks() == nil
|
||||||
|
}
|
||||||
|
|
||||||
func (b *IWDBackend) signalHandler(sigChan chan *dbus.Signal) {
|
func (b *IWDBackend) signalHandler(sigChan chan *dbus.Signal) {
|
||||||
defer b.sigWG.Done()
|
defer b.sigWG.Done()
|
||||||
|
|
||||||
@@ -66,11 +96,36 @@ func (b *IWDBackend) signalHandler(sigChan chan *dbus.Signal) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if sig.Name != dbusPropertiesInterface+".PropertiesChanged" {
|
if sig.Name == dbusObjectManager+".InterfacesAdded" {
|
||||||
|
if len(sig.Body) >= 2 {
|
||||||
|
if interfaces, ok := sig.Body[1].(map[string]map[string]dbus.Variant); ok {
|
||||||
|
if _, ok := interfaces[iwdKnownNetworkInterface]; ok {
|
||||||
|
if b.refreshWiFiNetworkState() && b.onStateChange != nil {
|
||||||
|
b.onStateChange()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(sig.Body) < 2 {
|
if sig.Name == dbusObjectManager+".InterfacesRemoved" {
|
||||||
|
if len(sig.Body) >= 2 {
|
||||||
|
if interfaces, ok := sig.Body[1].([]string); ok {
|
||||||
|
for _, iface := range interfaces {
|
||||||
|
if iface == iwdKnownNetworkInterface {
|
||||||
|
if b.refreshWiFiNetworkState() && b.onStateChange != nil {
|
||||||
|
b.onStateChange()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if sig.Name != dbusPropertiesInterface+".PropertiesChanged" || len(sig.Body) < 2 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,6 +142,9 @@ func (b *IWDBackend) signalHandler(sigChan chan *dbus.Signal) {
|
|||||||
stateChanged := false
|
stateChanged := false
|
||||||
|
|
||||||
switch iface {
|
switch iface {
|
||||||
|
case iwdKnownNetworkInterface:
|
||||||
|
stateChanged = b.refreshWiFiNetworkState()
|
||||||
|
|
||||||
case iwdDeviceInterface:
|
case iwdDeviceInterface:
|
||||||
if sig.Path == b.devicePath {
|
if sig.Path == b.devicePath {
|
||||||
if poweredVar, ok := changed["Powered"]; ok {
|
if poweredVar, ok := changed["Powered"]; ok {
|
||||||
@@ -105,13 +163,7 @@ func (b *IWDBackend) signalHandler(sigChan chan *dbus.Signal) {
|
|||||||
if sig.Path == b.stationPath {
|
if sig.Path == b.stationPath {
|
||||||
if scanningVar, ok := changed["Scanning"]; ok {
|
if scanningVar, ok := changed["Scanning"]; ok {
|
||||||
if scanning, ok := scanningVar.Value().(bool); ok && !scanning {
|
if scanning, ok := scanningVar.Value().(bool); ok && !scanning {
|
||||||
networks, err := b.updateWiFiNetworks()
|
stateChanged = b.refreshWiFiNetworkState() || stateChanged
|
||||||
if err == nil {
|
|
||||||
b.stateMutex.Lock()
|
|
||||||
b.state.WiFiNetworks = networks
|
|
||||||
b.stateMutex.Unlock()
|
|
||||||
stateChanged = true
|
|
||||||
}
|
|
||||||
|
|
||||||
b.stateMutex.RLock()
|
b.stateMutex.RLock()
|
||||||
wifiConnected := b.state.WiFiConnected
|
wifiConnected := b.state.WiFiConnected
|
||||||
@@ -236,6 +288,7 @@ func (b *IWDBackend) signalHandler(sigChan chan *dbus.Signal) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.refreshWiFiNetworkState()
|
||||||
stateChanged = true
|
stateChanged = true
|
||||||
|
|
||||||
if att != nil && isTarget {
|
if att != nil && isTarget {
|
||||||
@@ -282,6 +335,7 @@ func (b *IWDBackend) signalHandler(sigChan chan *dbus.Signal) {
|
|||||||
b.state.NetworkStatus = StatusDisconnected
|
b.state.NetworkStatus = StatusDisconnected
|
||||||
}
|
}
|
||||||
b.stateMutex.Unlock()
|
b.stateMutex.Unlock()
|
||||||
|
b.refreshWiFiNetworkState()
|
||||||
stateChanged = true
|
stateChanged = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -342,6 +396,7 @@ func (b *IWDBackend) signalHandler(sigChan chan *dbus.Signal) {
|
|||||||
stateChanged = true
|
stateChanged = true
|
||||||
}
|
}
|
||||||
b.stateMutex.Unlock()
|
b.stateMutex.Unlock()
|
||||||
|
b.refreshWiFiNetworkState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/godbus/dbus/v5"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -168,6 +169,92 @@ func TestIWDBackend_MapIwdDBusError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIWDSavedWiFiProfilesFromManagedObjects(t *testing.T) {
|
||||||
|
objects := map[dbus.ObjectPath]map[string]map[string]dbus.Variant{
|
||||||
|
"/net/connman/iwd/known_network/1": {
|
||||||
|
iwdKnownNetworkInterface: {
|
||||||
|
"Name": dbus.MakeVariant("Home"),
|
||||||
|
"AutoConnect": dbus.MakeVariant(false),
|
||||||
|
"Hidden": dbus.MakeVariant(true),
|
||||||
|
"Type": dbus.MakeVariant("psk"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"/net/connman/iwd/known_network/2": {
|
||||||
|
iwdKnownNetworkInterface: {
|
||||||
|
"Name": dbus.MakeVariant("Office"),
|
||||||
|
"Type": dbus.MakeVariant("8021x"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"/net/connman/iwd/known_network/3": {
|
||||||
|
iwdKnownNetworkInterface: {
|
||||||
|
"Name": dbus.MakeVariant("Cafe"),
|
||||||
|
"Type": dbus.MakeVariant("open"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"/net/connman/iwd/network/1": {
|
||||||
|
iwdNetworkInterface: {
|
||||||
|
"Name": dbus.MakeVariant("VisibleOnly"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
profiles := iwdSavedWiFiProfilesFromManagedObjects(objects)
|
||||||
|
|
||||||
|
assert.Len(t, profiles, 3)
|
||||||
|
assert.False(t, profiles["Home"].Autoconnect)
|
||||||
|
assert.True(t, profiles["Home"].Hidden)
|
||||||
|
assert.True(t, profiles["Home"].Secured)
|
||||||
|
assert.False(t, profiles["Home"].Enterprise)
|
||||||
|
|
||||||
|
assert.True(t, profiles["Office"].Autoconnect)
|
||||||
|
assert.True(t, profiles["Office"].Secured)
|
||||||
|
assert.True(t, profiles["Office"].Enterprise)
|
||||||
|
|
||||||
|
assert.True(t, profiles["Cafe"].Autoconnect)
|
||||||
|
assert.False(t, profiles["Cafe"].Secured)
|
||||||
|
assert.False(t, profiles["Cafe"].Enterprise)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIWDWiFiNetworksFromVisibleIncludesConnectedHiddenFallback(t *testing.T) {
|
||||||
|
profiles := map[string]savedWiFiProfile{
|
||||||
|
"Home": {
|
||||||
|
Autoconnect: true,
|
||||||
|
Secured: true,
|
||||||
|
Hidden: true,
|
||||||
|
Mode: "infrastructure",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
visible := []WiFiNetwork{
|
||||||
|
{
|
||||||
|
SSID: "Cafe",
|
||||||
|
Signal: 42,
|
||||||
|
Secured: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
networks := iwdWiFiNetworksFromVisible(visible, profiles, "Home", true, 68)
|
||||||
|
savedNetworks := savedWiFiNetworksFromProfiles(profiles, map[string]WiFiNetwork{
|
||||||
|
networks[0].SSID: networks[0],
|
||||||
|
networks[1].SSID: networks[1],
|
||||||
|
}, "Home", true)
|
||||||
|
|
||||||
|
assert.Len(t, networks, 2)
|
||||||
|
assert.Equal(t, "Cafe", networks[0].SSID)
|
||||||
|
assert.False(t, networks[0].Connected)
|
||||||
|
|
||||||
|
assert.Equal(t, "Home", networks[1].SSID)
|
||||||
|
assert.True(t, networks[1].Connected)
|
||||||
|
assert.True(t, networks[1].Hidden)
|
||||||
|
assert.True(t, networks[1].Saved)
|
||||||
|
assert.True(t, networks[1].Autoconnect)
|
||||||
|
assert.Equal(t, uint8(68), networks[1].Signal)
|
||||||
|
|
||||||
|
assert.Len(t, savedNetworks, 1)
|
||||||
|
assert.Equal(t, "Home", savedNetworks[0].SSID)
|
||||||
|
assert.True(t, savedNetworks[0].Connected)
|
||||||
|
assert.False(t, savedNetworks[0].OutOfRange)
|
||||||
|
}
|
||||||
|
|
||||||
func TestConnectAttempt_Finalization(t *testing.T) {
|
func TestConnectAttempt_Finalization(t *testing.T) {
|
||||||
backend, _ := NewIWDBackend()
|
backend, _ := NewIWDBackend()
|
||||||
backend.state = &BackendState{}
|
backend.state = &BackendState{}
|
||||||
|
|||||||
@@ -164,22 +164,18 @@ func (b *IWDBackend) updateWiFiNetworks() ([]WiFiNetwork, error) {
|
|||||||
return nil, fmt.Errorf("failed to get networks: %w", err)
|
return nil, fmt.Errorf("failed to get networks: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
knownNetworks, err := b.getKnownNetworks()
|
savedProfiles, err := b.getIWDSavedWiFiProfiles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
knownNetworks = make(map[string]bool)
|
savedProfiles = make(map[string]savedWiFiProfile)
|
||||||
}
|
|
||||||
|
|
||||||
autoconnectMap, err := b.getAutoconnectSettings()
|
|
||||||
if err != nil {
|
|
||||||
autoconnectMap = make(map[string]bool)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
b.stateMutex.RLock()
|
b.stateMutex.RLock()
|
||||||
currentSSID := b.state.WiFiSSID
|
currentSSID := b.state.WiFiSSID
|
||||||
wifiConnected := b.state.WiFiConnected
|
wifiConnected := b.state.WiFiConnected
|
||||||
|
wifiSignal := b.state.WiFiSignal
|
||||||
b.stateMutex.RUnlock()
|
b.stateMutex.RUnlock()
|
||||||
|
|
||||||
networks := make([]WiFiNetwork, 0, len(orderedNetworks))
|
visibleNetworks := make([]WiFiNetwork, 0, len(orderedNetworks))
|
||||||
for _, netData := range orderedNetworks {
|
for _, netData := range orderedNetworks {
|
||||||
if len(netData) < 2 {
|
if len(netData) < 2 {
|
||||||
continue
|
continue
|
||||||
@@ -225,23 +221,26 @@ func (b *IWDBackend) updateWiFiNetworks() ([]WiFiNetwork, error) {
|
|||||||
|
|
||||||
secured := netType != "open"
|
secured := netType != "open"
|
||||||
|
|
||||||
network := WiFiNetwork{
|
visibleNetworks = append(visibleNetworks, WiFiNetwork{
|
||||||
SSID: name,
|
SSID: name,
|
||||||
Signal: signal,
|
Signal: signal,
|
||||||
Secured: secured,
|
Secured: secured,
|
||||||
Connected: wifiConnected && name == currentSSID,
|
|
||||||
Saved: knownNetworks[name],
|
|
||||||
Autoconnect: autoconnectMap[name],
|
|
||||||
Enterprise: netType == "8021x",
|
Enterprise: netType == "8021x",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
networks = append(networks, network)
|
networks := iwdWiFiNetworksFromVisible(visibleNetworks, savedProfiles, currentSSID, wifiConnected, wifiSignal)
|
||||||
|
visibleNetworkMap := make(map[string]WiFiNetwork, len(networks))
|
||||||
|
for _, network := range networks {
|
||||||
|
visibleNetworkMap[network.SSID] = network
|
||||||
}
|
}
|
||||||
|
savedNetworks := savedWiFiNetworksFromProfiles(savedProfiles, visibleNetworkMap, currentSSID, wifiConnected)
|
||||||
|
|
||||||
sortWiFiNetworks(networks)
|
sortWiFiNetworks(networks)
|
||||||
|
|
||||||
b.stateMutex.Lock()
|
b.stateMutex.Lock()
|
||||||
b.state.WiFiNetworks = networks
|
b.state.WiFiNetworks = networks
|
||||||
|
b.state.SavedWiFiNetworks = savedNetworks
|
||||||
b.stateMutex.Unlock()
|
b.stateMutex.Unlock()
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
@@ -254,30 +253,129 @@ func (b *IWDBackend) updateWiFiNetworks() ([]WiFiNetwork, error) {
|
|||||||
return networks, nil
|
return networks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *IWDBackend) getKnownNetworks() (map[string]bool, error) {
|
func (b *IWDBackend) updateSavedWiFiNetworks() error {
|
||||||
obj := b.conn.Object(iwdBusName, iwdObjectPath)
|
savedProfiles, err := b.getIWDSavedWiFiProfiles()
|
||||||
|
|
||||||
var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant
|
|
||||||
err := obj.Call(dbusObjectManager+".GetManagedObjects", 0).Store(&objects)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
known := make(map[string]bool)
|
b.stateMutex.RLock()
|
||||||
for _, interfaces := range objects {
|
currentSSID := b.state.WiFiSSID
|
||||||
if knownProps, ok := interfaces[iwdKnownNetworkInterface]; ok {
|
wifiConnected := b.state.WiFiConnected
|
||||||
if nameVar, ok := knownProps["Name"]; ok {
|
wifiNetworks := append([]WiFiNetwork(nil), b.state.WiFiNetworks...)
|
||||||
if name, ok := nameVar.Value().(string); ok {
|
b.stateMutex.RUnlock()
|
||||||
known[name] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return known, nil
|
wifiNetworks, savedNetworks := refreshSavedWiFiState(wifiNetworks, savedProfiles, currentSSID, wifiConnected)
|
||||||
|
|
||||||
|
b.stateMutex.Lock()
|
||||||
|
b.state.WiFiNetworks = wifiNetworks
|
||||||
|
b.state.SavedWiFiNetworks = savedNetworks
|
||||||
|
b.stateMutex.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *IWDBackend) getAutoconnectSettings() (map[string]bool, error) {
|
func iwdWiFiNetworksFromVisible(visibleNetworks []WiFiNetwork, savedProfiles map[string]savedWiFiProfile, currentSSID string, wifiConnected bool, wifiSignal uint8) []WiFiNetwork {
|
||||||
|
networks := make([]WiFiNetwork, 0, len(visibleNetworks)+1)
|
||||||
|
seenSSIDs := make(map[string]struct{}, len(visibleNetworks)+1)
|
||||||
|
|
||||||
|
for _, network := range visibleNetworks {
|
||||||
|
profile, saved := savedProfiles[network.SSID]
|
||||||
|
network.Connected = wifiConnected && network.SSID == currentSSID
|
||||||
|
network.Saved = saved
|
||||||
|
network.Autoconnect = profile.Autoconnect
|
||||||
|
network.Hidden = network.Hidden || profile.Hidden
|
||||||
|
network.Secured = network.Secured || profile.Secured
|
||||||
|
network.Enterprise = network.Enterprise || profile.Enterprise
|
||||||
|
if network.Mode == "" {
|
||||||
|
network.Mode = profile.Mode
|
||||||
|
}
|
||||||
|
networks = append(networks, network)
|
||||||
|
seenSSIDs[network.SSID] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if wifiConnected && currentSSID != "" {
|
||||||
|
if _, exists := seenSSIDs[currentSSID]; !exists {
|
||||||
|
profile, saved := savedProfiles[currentSSID]
|
||||||
|
secured := profile.Secured
|
||||||
|
if !saved {
|
||||||
|
secured = true
|
||||||
|
}
|
||||||
|
mode := profile.Mode
|
||||||
|
if mode == "" {
|
||||||
|
mode = "infrastructure"
|
||||||
|
}
|
||||||
|
|
||||||
|
networks = append(networks, WiFiNetwork{
|
||||||
|
SSID: currentSSID,
|
||||||
|
Signal: wifiSignal,
|
||||||
|
Secured: secured,
|
||||||
|
Enterprise: profile.Enterprise,
|
||||||
|
Connected: true,
|
||||||
|
Saved: saved,
|
||||||
|
Autoconnect: profile.Autoconnect,
|
||||||
|
Hidden: true,
|
||||||
|
Mode: mode,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return networks
|
||||||
|
}
|
||||||
|
|
||||||
|
func iwdSavedWiFiProfilesFromManagedObjects(objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant) map[string]savedWiFiProfile {
|
||||||
|
profiles := make(map[string]savedWiFiProfile)
|
||||||
|
|
||||||
|
for _, interfaces := range objects {
|
||||||
|
knownProps, ok := interfaces[iwdKnownNetworkInterface]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
nameVar, ok := knownProps["Name"]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name, ok := nameVar.Value().(string)
|
||||||
|
if !ok || name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
profile := savedWiFiProfile{
|
||||||
|
Autoconnect: true,
|
||||||
|
Mode: "infrastructure",
|
||||||
|
}
|
||||||
|
if acVar, ok := knownProps["AutoConnect"]; ok {
|
||||||
|
if autoconnect, ok := acVar.Value().(bool); ok {
|
||||||
|
profile.Autoconnect = autoconnect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hiddenVar, ok := knownProps["Hidden"]; ok {
|
||||||
|
if hidden, ok := hiddenVar.Value().(bool); ok {
|
||||||
|
profile.Hidden = hidden
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if typeVar, ok := knownProps["Type"]; ok {
|
||||||
|
if networkType, ok := typeVar.Value().(string); ok {
|
||||||
|
profile.Secured = networkType != "" && networkType != "open"
|
||||||
|
profile.Enterprise = networkType == "8021x"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if existing, ok := profiles[name]; ok {
|
||||||
|
profile.Autoconnect = profile.Autoconnect || existing.Autoconnect
|
||||||
|
profile.Hidden = profile.Hidden || existing.Hidden
|
||||||
|
profile.Secured = profile.Secured || existing.Secured
|
||||||
|
profile.Enterprise = profile.Enterprise || existing.Enterprise
|
||||||
|
}
|
||||||
|
|
||||||
|
profiles[name] = profile
|
||||||
|
}
|
||||||
|
|
||||||
|
return profiles
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *IWDBackend) getIWDSavedWiFiProfiles() (map[string]savedWiFiProfile, error) {
|
||||||
obj := b.conn.Object(iwdBusName, iwdObjectPath)
|
obj := b.conn.Object(iwdBusName, iwdObjectPath)
|
||||||
|
|
||||||
var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant
|
var objects map[dbus.ObjectPath]map[string]map[string]dbus.Variant
|
||||||
@@ -286,24 +384,7 @@ func (b *IWDBackend) getAutoconnectSettings() (map[string]bool, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
autoconnectMap := make(map[string]bool)
|
return iwdSavedWiFiProfilesFromManagedObjects(objects), nil
|
||||||
for _, interfaces := range objects {
|
|
||||||
if knownProps, ok := interfaces[iwdKnownNetworkInterface]; ok {
|
|
||||||
if nameVar, ok := knownProps["Name"]; ok {
|
|
||||||
if name, ok := nameVar.Value().(string); ok {
|
|
||||||
autoconnect := true
|
|
||||||
if acVar, ok := knownProps["AutoConnect"]; ok {
|
|
||||||
if ac, ok := acVar.Value().(bool); ok {
|
|
||||||
autoconnect = ac
|
|
||||||
}
|
|
||||||
}
|
|
||||||
autoconnectMap[name] = autoconnect
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return autoconnectMap, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *IWDBackend) GetWiFiNetworkDetails(ssid string) (*NetworkInfoResponse, error) {
|
func (b *IWDBackend) GetWiFiNetworkDetails(ssid string) (*NetworkInfoResponse, error) {
|
||||||
@@ -614,6 +695,8 @@ func (b *IWDBackend) ForgetWiFiNetwork(ssid string) error {
|
|||||||
b.stateMutex.Unlock()
|
b.stateMutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, _ = b.updateWiFiNetworks()
|
||||||
|
|
||||||
if b.onStateChange != nil {
|
if b.onStateChange != nil {
|
||||||
b.onStateChange()
|
b.onStateChange()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,6 +222,10 @@ func (b *NetworkManagerBackend) Initialize() error {
|
|||||||
log.Warnf("Failed to update WiFi state: %v", err)
|
log.Warnf("Failed to update WiFi state: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := b.updateSavedWiFiNetworks(); err != nil {
|
||||||
|
log.Warnf("Failed to get initial saved WiFi networks: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if wifiEnabled {
|
if wifiEnabled {
|
||||||
if _, err := b.updateWiFiNetworks(); err != nil {
|
if _, err := b.updateWiFiNetworks(); err != nil {
|
||||||
log.Warnf("Failed to get initial networks: %v", err)
|
log.Warnf("Failed to get initial networks: %v", err)
|
||||||
@@ -261,6 +265,7 @@ func (b *NetworkManagerBackend) GetCurrentState() (*BackendState, error) {
|
|||||||
|
|
||||||
state := *b.state
|
state := *b.state
|
||||||
state.WiFiNetworks = append([]WiFiNetwork(nil), b.state.WiFiNetworks...)
|
state.WiFiNetworks = append([]WiFiNetwork(nil), b.state.WiFiNetworks...)
|
||||||
|
state.SavedWiFiNetworks = append([]WiFiNetwork(nil), b.state.SavedWiFiNetworks...)
|
||||||
state.WiFiDevices = append([]WiFiDevice(nil), b.state.WiFiDevices...)
|
state.WiFiDevices = append([]WiFiDevice(nil), b.state.WiFiDevices...)
|
||||||
state.WiredConnections = append([]WiredConnection(nil), b.state.WiredConnections...)
|
state.WiredConnections = append([]WiredConnection(nil), b.state.WiredConnections...)
|
||||||
state.EthernetDevices = append([]EthernetDevice(nil), b.state.EthernetDevices...)
|
state.EthernetDevices = append([]EthernetDevice(nil), b.state.EthernetDevices...)
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ import (
|
|||||||
"github.com/godbus/dbus/v5"
|
"github.com/godbus/dbus/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dbusNMSettingsPath = "/org/freedesktop/NetworkManager/Settings"
|
||||||
|
dbusNMSettingsInterface = "org.freedesktop.NetworkManager.Settings"
|
||||||
|
dbusNMSettingsConnectionInterface = "org.freedesktop.NetworkManager.Settings.Connection"
|
||||||
|
)
|
||||||
|
|
||||||
func (b *NetworkManagerBackend) startSignalPump() error {
|
func (b *NetworkManagerBackend) startSignalPump() error {
|
||||||
conn, err := dbus.ConnectSystemBus()
|
conn, err := dbus.ConnectSystemBus()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -27,8 +33,8 @@ func (b *NetworkManagerBackend) startSignalPump() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := conn.AddMatchSignal(
|
if err := conn.AddMatchSignal(
|
||||||
dbus.WithMatchObjectPath(dbus.ObjectPath("/org/freedesktop/NetworkManager/Settings")),
|
dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMSettingsPath)),
|
||||||
dbus.WithMatchInterface("org.freedesktop.NetworkManager.Settings"),
|
dbus.WithMatchInterface(dbusNMSettingsInterface),
|
||||||
dbus.WithMatchMember("NewConnection"),
|
dbus.WithMatchMember("NewConnection"),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
conn.RemoveMatchSignal(
|
conn.RemoveMatchSignal(
|
||||||
@@ -42,8 +48,8 @@ func (b *NetworkManagerBackend) startSignalPump() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := conn.AddMatchSignal(
|
if err := conn.AddMatchSignal(
|
||||||
dbus.WithMatchObjectPath(dbus.ObjectPath("/org/freedesktop/NetworkManager/Settings")),
|
dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMSettingsPath)),
|
||||||
dbus.WithMatchInterface("org.freedesktop.NetworkManager.Settings"),
|
dbus.WithMatchInterface(dbusNMSettingsInterface),
|
||||||
dbus.WithMatchMember("ConnectionRemoved"),
|
dbus.WithMatchMember("ConnectionRemoved"),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
conn.RemoveMatchSignal(
|
conn.RemoveMatchSignal(
|
||||||
@@ -52,8 +58,8 @@ func (b *NetworkManagerBackend) startSignalPump() error {
|
|||||||
dbus.WithMatchMember("PropertiesChanged"),
|
dbus.WithMatchMember("PropertiesChanged"),
|
||||||
)
|
)
|
||||||
conn.RemoveMatchSignal(
|
conn.RemoveMatchSignal(
|
||||||
dbus.WithMatchObjectPath(dbus.ObjectPath("/org/freedesktop/NetworkManager/Settings")),
|
dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMSettingsPath)),
|
||||||
dbus.WithMatchInterface("org.freedesktop.NetworkManager.Settings"),
|
dbus.WithMatchInterface(dbusNMSettingsInterface),
|
||||||
dbus.WithMatchMember("NewConnection"),
|
dbus.WithMatchMember("NewConnection"),
|
||||||
)
|
)
|
||||||
conn.RemoveSignal(signals)
|
conn.RemoveSignal(signals)
|
||||||
@@ -61,6 +67,31 @@ func (b *NetworkManagerBackend) startSignalPump() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := conn.AddMatchSignal(
|
||||||
|
dbus.WithMatchPathNamespace(dbus.ObjectPath(dbusNMSettingsPath)),
|
||||||
|
dbus.WithMatchInterface(dbusNMSettingsConnectionInterface),
|
||||||
|
dbus.WithMatchMember("Updated"),
|
||||||
|
); err != nil {
|
||||||
|
conn.RemoveMatchSignal(
|
||||||
|
dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMPath)),
|
||||||
|
dbus.WithMatchInterface(dbusPropsInterface),
|
||||||
|
dbus.WithMatchMember("PropertiesChanged"),
|
||||||
|
)
|
||||||
|
conn.RemoveMatchSignal(
|
||||||
|
dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMSettingsPath)),
|
||||||
|
dbus.WithMatchInterface(dbusNMSettingsInterface),
|
||||||
|
dbus.WithMatchMember("NewConnection"),
|
||||||
|
)
|
||||||
|
conn.RemoveMatchSignal(
|
||||||
|
dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMSettingsPath)),
|
||||||
|
dbus.WithMatchInterface(dbusNMSettingsInterface),
|
||||||
|
dbus.WithMatchMember("ConnectionRemoved"),
|
||||||
|
)
|
||||||
|
conn.RemoveSignal(signals)
|
||||||
|
conn.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := conn.AddMatchSignal(
|
if err := conn.AddMatchSignal(
|
||||||
dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMPath)),
|
dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMPath)),
|
||||||
dbus.WithMatchInterface(dbusNMInterface),
|
dbus.WithMatchInterface(dbusNMInterface),
|
||||||
@@ -137,6 +168,32 @@ func (b *NetworkManagerBackend) stopSignalPump() {
|
|||||||
dbus.WithMatchMember("PropertiesChanged"),
|
dbus.WithMatchMember("PropertiesChanged"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
b.dbusConn.RemoveMatchSignal(
|
||||||
|
dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMSettingsPath)),
|
||||||
|
dbus.WithMatchInterface(dbusNMSettingsInterface),
|
||||||
|
dbus.WithMatchMember("NewConnection"),
|
||||||
|
)
|
||||||
|
b.dbusConn.RemoveMatchSignal(
|
||||||
|
dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMSettingsPath)),
|
||||||
|
dbus.WithMatchInterface(dbusNMSettingsInterface),
|
||||||
|
dbus.WithMatchMember("ConnectionRemoved"),
|
||||||
|
)
|
||||||
|
b.dbusConn.RemoveMatchSignal(
|
||||||
|
dbus.WithMatchPathNamespace(dbus.ObjectPath(dbusNMSettingsPath)),
|
||||||
|
dbus.WithMatchInterface(dbusNMSettingsConnectionInterface),
|
||||||
|
dbus.WithMatchMember("Updated"),
|
||||||
|
)
|
||||||
|
b.dbusConn.RemoveMatchSignal(
|
||||||
|
dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMPath)),
|
||||||
|
dbus.WithMatchInterface(dbusNMInterface),
|
||||||
|
dbus.WithMatchMember("DeviceAdded"),
|
||||||
|
)
|
||||||
|
b.dbusConn.RemoveMatchSignal(
|
||||||
|
dbus.WithMatchObjectPath(dbus.ObjectPath(dbusNMPath)),
|
||||||
|
dbus.WithMatchInterface(dbusNMInterface),
|
||||||
|
dbus.WithMatchMember("DeviceRemoved"),
|
||||||
|
)
|
||||||
|
|
||||||
for _, info := range b.wifiDevices {
|
for _, info := range b.wifiDevices {
|
||||||
b.dbusConn.RemoveMatchSignal(
|
b.dbusConn.RemoveMatchSignal(
|
||||||
dbus.WithMatchObjectPath(dbus.ObjectPath(info.device.GetPath())),
|
dbus.WithMatchObjectPath(dbus.ObjectPath(info.device.GetPath())),
|
||||||
@@ -164,9 +221,13 @@ func (b *NetworkManagerBackend) stopSignalPump() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *NetworkManagerBackend) handleDBusSignal(sig *dbus.Signal) {
|
func (b *NetworkManagerBackend) handleDBusSignal(sig *dbus.Signal) {
|
||||||
if sig.Name == "org.freedesktop.NetworkManager.Settings.NewConnection" ||
|
if sig.Name == dbusNMSettingsInterface+".NewConnection" ||
|
||||||
sig.Name == "org.freedesktop.NetworkManager.Settings.ConnectionRemoved" {
|
sig.Name == dbusNMSettingsInterface+".ConnectionRemoved" ||
|
||||||
|
sig.Name == dbusNMSettingsConnectionInterface+".Updated" {
|
||||||
b.ListVPNProfiles()
|
b.ListVPNProfiles()
|
||||||
|
if err := b.updateSavedWiFiNetworks(); err != nil {
|
||||||
|
b.updateWiFiNetworks()
|
||||||
|
}
|
||||||
if b.onStateChange != nil {
|
if b.onStateChange != nil {
|
||||||
b.onStateChange()
|
b.onStateChange()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -225,24 +225,14 @@ func (b *NetworkManagerBackend) GetWiFiQRCodeContent(ssid string) (string, error
|
|||||||
return "", fmt.Errorf("failed to identify security type of network `%s`", ssid)
|
return "", fmt.Errorf("failed to identify security type of network `%s`", ssid)
|
||||||
}
|
}
|
||||||
|
|
||||||
var securityType string
|
|
||||||
switch keyMgmt {
|
switch keyMgmt {
|
||||||
case "none":
|
case "none":
|
||||||
authAlg, _ := secSettings["auth-alg"].(string)
|
return "", fmt.Errorf("QR code generation only supports WPA-PSK connections, `%s` is open or WEP", ssid)
|
||||||
switch authAlg {
|
|
||||||
case "open":
|
|
||||||
securityType = "nopass"
|
|
||||||
default:
|
|
||||||
securityType = "WEP"
|
|
||||||
}
|
|
||||||
case "ieee8021x":
|
case "ieee8021x":
|
||||||
securityType = "WEP"
|
return "", fmt.Errorf("QR code generation only supports WPA-PSK connections, `%s` is enterprise", ssid)
|
||||||
|
case "wpa-psk", "sae", "wpa-psk-sae":
|
||||||
default:
|
default:
|
||||||
securityType = "WPA"
|
return "", fmt.Errorf("QR code generation only supports WPA-PSK connections, `%s` uses %s", ssid, keyMgmt)
|
||||||
}
|
|
||||||
|
|
||||||
if securityType != "WPA" {
|
|
||||||
return "", fmt.Errorf("QR code generation only supports WPA connections, `%s` uses %s", ssid, securityType)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var psk string
|
var psk string
|
||||||
@@ -276,7 +266,7 @@ func (b *NetworkManagerBackend) GetWiFiQRCodeContent(ssid string) (string, error
|
|||||||
return "", fmt.Errorf("failed to retrieve password for `%s`", ssid)
|
return "", fmt.Errorf("failed to retrieve password for `%s`", ssid)
|
||||||
}
|
}
|
||||||
|
|
||||||
return FormatWiFiQRString(securityType, ssid, psk), nil
|
return FormatWiFiQRString("WPA", ssid, psk), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *NetworkManagerBackend) ConnectWiFi(req ConnectionRequest) error {
|
func (b *NetworkManagerBackend) ConnectWiFi(req ConnectionRequest) error {
|
||||||
@@ -405,6 +395,74 @@ func (b *NetworkManagerBackend) ForgetWiFiNetwork(ssid string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getSavedWiFiProfiles(connections []gonetworkmanager.Connection) map[string]savedWiFiProfile {
|
||||||
|
profiles := make(map[string]savedWiFiProfile)
|
||||||
|
|
||||||
|
for _, conn := range connections {
|
||||||
|
connSettings, err := conn.GetSettings()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
connMeta, ok := connSettings["connection"]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
connType, ok := connMeta["type"].(string)
|
||||||
|
if !ok || connType != "802-11-wireless" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
wifiSettings, ok := connSettings["802-11-wireless"]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ssidBytes, ok := wifiSettings["ssid"].([]byte)
|
||||||
|
if !ok || len(ssidBytes) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ssid := string(ssidBytes)
|
||||||
|
profile := savedWiFiProfile{
|
||||||
|
Autoconnect: true,
|
||||||
|
Mode: "infrastructure",
|
||||||
|
}
|
||||||
|
|
||||||
|
if ac, ok := connMeta["autoconnect"].(bool); ok {
|
||||||
|
profile.Autoconnect = ac
|
||||||
|
}
|
||||||
|
if hidden, ok := wifiSettings["hidden"].(bool); ok {
|
||||||
|
profile.Hidden = hidden
|
||||||
|
}
|
||||||
|
if mode, ok := wifiSettings["mode"].(string); ok && mode != "" {
|
||||||
|
profile.Mode = mode
|
||||||
|
}
|
||||||
|
if _, ok := connSettings["802-11-wireless-security"]; ok {
|
||||||
|
profile.Secured = true
|
||||||
|
}
|
||||||
|
if _, ok := connSettings["802-1x"]; ok {
|
||||||
|
profile.Enterprise = true
|
||||||
|
profile.Secured = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if existing, ok := profiles[ssid]; ok {
|
||||||
|
profile.Autoconnect = profile.Autoconnect || existing.Autoconnect
|
||||||
|
profile.Hidden = profile.Hidden || existing.Hidden
|
||||||
|
profile.Secured = profile.Secured || existing.Secured
|
||||||
|
profile.Enterprise = profile.Enterprise || existing.Enterprise
|
||||||
|
if profile.Mode == "" {
|
||||||
|
profile.Mode = existing.Mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
profiles[ssid] = profile
|
||||||
|
}
|
||||||
|
|
||||||
|
return profiles
|
||||||
|
}
|
||||||
|
|
||||||
func (b *NetworkManagerBackend) IsConnectingTo(ssid string) bool {
|
func (b *NetworkManagerBackend) IsConnectingTo(ssid string) bool {
|
||||||
b.stateMutex.RLock()
|
b.stateMutex.RLock()
|
||||||
defer b.stateMutex.RUnlock()
|
defer b.stateMutex.RUnlock()
|
||||||
@@ -442,47 +500,7 @@ func (b *NetworkManagerBackend) updateWiFiNetworks() ([]WiFiNetwork, error) {
|
|||||||
return nil, fmt.Errorf("failed to get connections: %w", err)
|
return nil, fmt.Errorf("failed to get connections: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
savedSSIDs := make(map[string]bool)
|
savedProfiles := getSavedWiFiProfiles(connections)
|
||||||
autoconnectMap := make(map[string]bool)
|
|
||||||
hiddenSSIDs := make(map[string]bool)
|
|
||||||
for _, conn := range connections {
|
|
||||||
connSettings, err := conn.GetSettings()
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
connMeta, ok := connSettings["connection"]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
connType, ok := connMeta["type"].(string)
|
|
||||||
if !ok || connType != "802-11-wireless" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
wifiSettings, ok := connSettings["802-11-wireless"]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ssidBytes, ok := wifiSettings["ssid"].([]byte)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ssid := string(ssidBytes)
|
|
||||||
savedSSIDs[ssid] = true
|
|
||||||
autoconnect := true
|
|
||||||
if ac, ok := connMeta["autoconnect"].(bool); ok {
|
|
||||||
autoconnect = ac
|
|
||||||
}
|
|
||||||
autoconnectMap[ssid] = autoconnect
|
|
||||||
|
|
||||||
if hidden, ok := wifiSettings["hidden"].(bool); ok && hidden {
|
|
||||||
hiddenSSIDs[ssid] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.stateMutex.RLock()
|
b.stateMutex.RLock()
|
||||||
currentSSID := b.state.WiFiSSID
|
currentSSID := b.state.WiFiSSID
|
||||||
@@ -491,8 +509,8 @@ func (b *NetworkManagerBackend) updateWiFiNetworks() ([]WiFiNetwork, error) {
|
|||||||
wifiBSSID := b.state.WiFiBSSID
|
wifiBSSID := b.state.WiFiBSSID
|
||||||
b.stateMutex.RUnlock()
|
b.stateMutex.RUnlock()
|
||||||
|
|
||||||
seenSSIDs := make(map[string]*WiFiNetwork)
|
seenSSIDs := make(map[string]int)
|
||||||
networks := []WiFiNetwork{}
|
networks := make([]WiFiNetwork, 0, len(apPaths)+1)
|
||||||
|
|
||||||
for _, ap := range apPaths {
|
for _, ap := range apPaths {
|
||||||
ssid, err := ap.GetPropertySSID()
|
ssid, err := ap.GetPropertySSID()
|
||||||
@@ -500,7 +518,8 @@ func (b *NetworkManagerBackend) updateWiFiNetworks() ([]WiFiNetwork, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if existing, exists := seenSSIDs[ssid]; exists {
|
if existingIndex, exists := seenSSIDs[ssid]; exists {
|
||||||
|
existing := &networks[existingIndex]
|
||||||
strength, _ := ap.GetPropertyStrength()
|
strength, _ := ap.GetPropertyStrength()
|
||||||
if strength > existing.Signal {
|
if strength > existing.Signal {
|
||||||
existing.Signal = strength
|
existing.Signal = strength
|
||||||
@@ -550,6 +569,7 @@ func (b *NetworkManagerBackend) updateWiFiNetworks() ([]WiFiNetwork, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
profile, saved := savedProfiles[ssid]
|
||||||
network := WiFiNetwork{
|
network := WiFiNetwork{
|
||||||
SSID: ssid,
|
SSID: ssid,
|
||||||
BSSID: bssid,
|
BSSID: bssid,
|
||||||
@@ -557,45 +577,86 @@ func (b *NetworkManagerBackend) updateWiFiNetworks() ([]WiFiNetwork, error) {
|
|||||||
Secured: secured,
|
Secured: secured,
|
||||||
Enterprise: enterprise,
|
Enterprise: enterprise,
|
||||||
Connected: isConnected,
|
Connected: isConnected,
|
||||||
Saved: savedSSIDs[ssid],
|
Saved: saved,
|
||||||
Autoconnect: autoconnectMap[ssid],
|
Autoconnect: profile.Autoconnect,
|
||||||
Hidden: hiddenSSIDs[ssid],
|
Hidden: profile.Hidden,
|
||||||
Frequency: freq,
|
Frequency: freq,
|
||||||
Mode: modeStr,
|
Mode: modeStr,
|
||||||
Rate: rate,
|
Rate: rate,
|
||||||
Channel: channel,
|
Channel: channel,
|
||||||
}
|
}
|
||||||
|
|
||||||
seenSSIDs[ssid] = &network
|
|
||||||
networks = append(networks, network)
|
networks = append(networks, network)
|
||||||
|
seenSSIDs[ssid] = len(networks) - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if wifiConnected && currentSSID != "" {
|
if wifiConnected && currentSSID != "" {
|
||||||
if _, exists := seenSSIDs[currentSSID]; !exists {
|
if _, exists := seenSSIDs[currentSSID]; !exists {
|
||||||
|
profile, saved := savedProfiles[currentSSID]
|
||||||
hiddenNetwork := WiFiNetwork{
|
hiddenNetwork := WiFiNetwork{
|
||||||
SSID: currentSSID,
|
SSID: currentSSID,
|
||||||
BSSID: wifiBSSID,
|
BSSID: wifiBSSID,
|
||||||
Signal: wifiSignal,
|
Signal: wifiSignal,
|
||||||
Secured: true,
|
Secured: true,
|
||||||
Connected: true,
|
Connected: true,
|
||||||
Saved: savedSSIDs[currentSSID],
|
Saved: saved,
|
||||||
Autoconnect: autoconnectMap[currentSSID],
|
Autoconnect: profile.Autoconnect,
|
||||||
Hidden: true,
|
Hidden: true,
|
||||||
Mode: "infrastructure",
|
Mode: "infrastructure",
|
||||||
}
|
}
|
||||||
networks = append(networks, hiddenNetwork)
|
networks = append(networks, hiddenNetwork)
|
||||||
|
seenSSIDs[currentSSID] = len(networks) - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
visibleNetworks := wiFiNetworksBySSID(networks, true)
|
||||||
|
savedNetworks := savedWiFiNetworksFromProfiles(savedProfiles, visibleNetworks, currentSSID, wifiConnected)
|
||||||
|
|
||||||
sortWiFiNetworks(networks)
|
sortWiFiNetworks(networks)
|
||||||
|
|
||||||
b.stateMutex.Lock()
|
b.stateMutex.Lock()
|
||||||
b.state.WiFiNetworks = networks
|
b.state.WiFiNetworks = networks
|
||||||
|
b.state.SavedWiFiNetworks = savedNetworks
|
||||||
b.stateMutex.Unlock()
|
b.stateMutex.Unlock()
|
||||||
|
|
||||||
return networks, nil
|
return networks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *NetworkManagerBackend) updateSavedWiFiNetworks() error {
|
||||||
|
s := b.settings
|
||||||
|
if s == nil {
|
||||||
|
var err error
|
||||||
|
s, err = gonetworkmanager.NewSettings()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get settings: %w", err)
|
||||||
|
}
|
||||||
|
b.settings = s
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsMgr := s.(gonetworkmanager.Settings)
|
||||||
|
connections, err := settingsMgr.ListConnections()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get connections: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
savedProfiles := getSavedWiFiProfiles(connections)
|
||||||
|
|
||||||
|
b.stateMutex.RLock()
|
||||||
|
currentSSID := b.state.WiFiSSID
|
||||||
|
wifiConnected := b.state.WiFiConnected
|
||||||
|
wifiNetworks := append([]WiFiNetwork(nil), b.state.WiFiNetworks...)
|
||||||
|
b.stateMutex.RUnlock()
|
||||||
|
|
||||||
|
wifiNetworks, savedNetworks := refreshSavedWiFiState(wifiNetworks, savedProfiles, currentSSID, wifiConnected)
|
||||||
|
|
||||||
|
b.stateMutex.Lock()
|
||||||
|
b.state.WiFiNetworks = wifiNetworks
|
||||||
|
b.state.SavedWiFiNetworks = savedNetworks
|
||||||
|
b.stateMutex.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (b *NetworkManagerBackend) findConnection(ssid string) (gonetworkmanager.Connection, error) {
|
func (b *NetworkManagerBackend) findConnection(ssid string) (gonetworkmanager.Connection, error) {
|
||||||
s := b.settings
|
s := b.settings
|
||||||
if s == nil {
|
if s == nil {
|
||||||
@@ -975,49 +1036,14 @@ func (b *NetworkManagerBackend) updateAllWiFiDevices() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
savedSSIDs := make(map[string]bool)
|
savedProfiles := getSavedWiFiProfiles(connections)
|
||||||
autoconnectMap := make(map[string]bool)
|
|
||||||
hiddenSSIDs := make(map[string]bool)
|
|
||||||
for _, conn := range connections {
|
|
||||||
connSettings, err := conn.GetSettings()
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
connMeta, ok := connSettings["connection"]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
connType, ok := connMeta["type"].(string)
|
|
||||||
if !ok || connType != "802-11-wireless" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
wifiSettings, ok := connSettings["802-11-wireless"]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ssidBytes, ok := wifiSettings["ssid"].([]byte)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ssid := string(ssidBytes)
|
|
||||||
savedSSIDs[ssid] = true
|
|
||||||
autoconnect := true
|
|
||||||
if ac, ok := connMeta["autoconnect"].(bool); ok {
|
|
||||||
autoconnect = ac
|
|
||||||
}
|
|
||||||
autoconnectMap[ssid] = autoconnect
|
|
||||||
|
|
||||||
if hidden, ok := wifiSettings["hidden"].(bool); ok && hidden {
|
|
||||||
hiddenSSIDs[ssid] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var devices []WiFiDevice
|
var devices []WiFiDevice
|
||||||
|
visibleNetworks := make(map[string]WiFiNetwork)
|
||||||
|
b.stateMutex.RLock()
|
||||||
|
currentSSID := b.state.WiFiSSID
|
||||||
|
wifiConnected := b.state.WiFiConnected
|
||||||
|
b.stateMutex.RUnlock()
|
||||||
|
|
||||||
for name, devInfo := range b.wifiDevices {
|
for name, devInfo := range b.wifiDevices {
|
||||||
state, _ := devInfo.device.GetPropertyState()
|
state, _ := devInfo.device.GetPropertyState()
|
||||||
@@ -1050,14 +1076,16 @@ func (b *NetworkManagerBackend) updateAllWiFiDevices() {
|
|||||||
apPaths, err := devInfo.wireless.GetAccessPoints()
|
apPaths, err := devInfo.wireless.GetAccessPoints()
|
||||||
var networks []WiFiNetwork
|
var networks []WiFiNetwork
|
||||||
if err == nil {
|
if err == nil {
|
||||||
seenSSIDs := make(map[string]*WiFiNetwork)
|
seenSSIDs := make(map[string]int)
|
||||||
|
networks = make([]WiFiNetwork, 0, len(apPaths)+1)
|
||||||
for _, ap := range apPaths {
|
for _, ap := range apPaths {
|
||||||
apSSID, err := ap.GetPropertySSID()
|
apSSID, err := ap.GetPropertySSID()
|
||||||
if err != nil || apSSID == "" {
|
if err != nil || apSSID == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if existing, exists := seenSSIDs[apSSID]; exists {
|
if existingIndex, exists := seenSSIDs[apSSID]; exists {
|
||||||
|
existing := &networks[existingIndex]
|
||||||
strength, _ := ap.GetPropertyStrength()
|
strength, _ := ap.GetPropertyStrength()
|
||||||
if strength > existing.Signal {
|
if strength > existing.Signal {
|
||||||
existing.Signal = strength
|
existing.Signal = strength
|
||||||
@@ -1107,6 +1135,7 @@ func (b *NetworkManagerBackend) updateAllWiFiDevices() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
profile, saved := savedProfiles[apSSID]
|
||||||
network := WiFiNetwork{
|
network := WiFiNetwork{
|
||||||
SSID: apSSID,
|
SSID: apSSID,
|
||||||
BSSID: apBSSID,
|
BSSID: apBSSID,
|
||||||
@@ -1114,9 +1143,9 @@ func (b *NetworkManagerBackend) updateAllWiFiDevices() {
|
|||||||
Secured: secured,
|
Secured: secured,
|
||||||
Enterprise: enterprise,
|
Enterprise: enterprise,
|
||||||
Connected: isConnected,
|
Connected: isConnected,
|
||||||
Saved: savedSSIDs[apSSID],
|
Saved: saved,
|
||||||
Autoconnect: autoconnectMap[apSSID],
|
Autoconnect: profile.Autoconnect,
|
||||||
Hidden: hiddenSSIDs[apSSID],
|
Hidden: profile.Hidden,
|
||||||
Frequency: freq,
|
Frequency: freq,
|
||||||
Mode: modeStr,
|
Mode: modeStr,
|
||||||
Rate: rate,
|
Rate: rate,
|
||||||
@@ -1124,25 +1153,31 @@ func (b *NetworkManagerBackend) updateAllWiFiDevices() {
|
|||||||
Device: name,
|
Device: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
seenSSIDs[apSSID] = &network
|
|
||||||
networks = append(networks, network)
|
networks = append(networks, network)
|
||||||
|
seenSSIDs[apSSID] = len(networks) - 1
|
||||||
|
if existing, ok := visibleNetworks[apSSID]; !ok || network.Signal > existing.Signal {
|
||||||
|
visibleNetworks[apSSID] = network
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if connected && ssid != "" {
|
if connected && ssid != "" {
|
||||||
if _, exists := seenSSIDs[ssid]; !exists {
|
if _, exists := seenSSIDs[ssid]; !exists {
|
||||||
|
profile, saved := savedProfiles[ssid]
|
||||||
hiddenNetwork := WiFiNetwork{
|
hiddenNetwork := WiFiNetwork{
|
||||||
SSID: ssid,
|
SSID: ssid,
|
||||||
BSSID: bssid,
|
BSSID: bssid,
|
||||||
Signal: signal,
|
Signal: signal,
|
||||||
Secured: true,
|
Secured: true,
|
||||||
Connected: true,
|
Connected: true,
|
||||||
Saved: savedSSIDs[ssid],
|
Saved: saved,
|
||||||
Autoconnect: autoconnectMap[ssid],
|
Autoconnect: profile.Autoconnect,
|
||||||
Hidden: true,
|
Hidden: true,
|
||||||
Mode: "infrastructure",
|
Mode: "infrastructure",
|
||||||
Device: name,
|
Device: name,
|
||||||
}
|
}
|
||||||
networks = append(networks, hiddenNetwork)
|
networks = append(networks, hiddenNetwork)
|
||||||
|
seenSSIDs[ssid] = len(networks) - 1
|
||||||
|
visibleNetworks[ssid] = hiddenNetwork
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1168,6 +1203,7 @@ func (b *NetworkManagerBackend) updateAllWiFiDevices() {
|
|||||||
|
|
||||||
b.stateMutex.Lock()
|
b.stateMutex.Lock()
|
||||||
b.state.WiFiDevices = devices
|
b.state.WiFiDevices = devices
|
||||||
|
b.state.SavedWiFiNetworks = savedWiFiNetworksFromProfiles(savedProfiles, visibleNetworks, currentSSID, wifiConnected)
|
||||||
b.stateMutex.Unlock()
|
b.stateMutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
mock_gonetworkmanager "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2"
|
mock_gonetworkmanager "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/github.com/Wifx/gonetworkmanager/v2"
|
||||||
|
"github.com/Wifx/gonetworkmanager/v2"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -176,6 +177,54 @@ func TestNetworkManagerBackend_UpdateWiFiNetworks_NoDevice(t *testing.T) {
|
|||||||
assert.Contains(t, err.Error(), "no WiFi device available")
|
assert.Contains(t, err.Error(), "no WiFi device available")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNetworkManagerBackend_UpdateSavedWiFiNetworksPreservesVisibleSavedNetworks(t *testing.T) {
|
||||||
|
mockNM := mock_gonetworkmanager.NewMockNetworkManager(t)
|
||||||
|
mockSettings := mock_gonetworkmanager.NewMockSettings(t)
|
||||||
|
mockConn := mock_gonetworkmanager.NewMockConnection(t)
|
||||||
|
|
||||||
|
backend, err := NewNetworkManagerBackend(mockNM)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
backend.settings = mockSettings
|
||||||
|
|
||||||
|
backend.stateMutex.Lock()
|
||||||
|
backend.state.WiFiNetworks = []WiFiNetwork{
|
||||||
|
{
|
||||||
|
SSID: "Home",
|
||||||
|
Signal: 76,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
backend.stateMutex.Unlock()
|
||||||
|
|
||||||
|
settings := gonetworkmanager.ConnectionSettings{
|
||||||
|
"connection": {
|
||||||
|
"type": "802-11-wireless",
|
||||||
|
"autoconnect": true,
|
||||||
|
},
|
||||||
|
"802-11-wireless": {
|
||||||
|
"ssid": []byte("Home"),
|
||||||
|
},
|
||||||
|
"802-11-wireless-security": {},
|
||||||
|
}
|
||||||
|
mockSettings.EXPECT().ListConnections().Return([]gonetworkmanager.Connection{mockConn}, nil)
|
||||||
|
mockConn.EXPECT().GetSettings().Return(settings, nil)
|
||||||
|
|
||||||
|
err = backend.updateSavedWiFiNetworks()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
backend.stateMutex.RLock()
|
||||||
|
savedNetworks := append([]WiFiNetwork(nil), backend.state.SavedWiFiNetworks...)
|
||||||
|
wifiNetworks := append([]WiFiNetwork(nil), backend.state.WiFiNetworks...)
|
||||||
|
backend.stateMutex.RUnlock()
|
||||||
|
|
||||||
|
assert.Len(t, wifiNetworks, 1)
|
||||||
|
assert.True(t, wifiNetworks[0].Saved)
|
||||||
|
assert.Len(t, savedNetworks, 1)
|
||||||
|
assert.Equal(t, "Home", savedNetworks[0].SSID)
|
||||||
|
assert.True(t, savedNetworks[0].Saved)
|
||||||
|
assert.False(t, savedNetworks[0].OutOfRange)
|
||||||
|
assert.Equal(t, uint8(76), savedNetworks[0].Signal)
|
||||||
|
}
|
||||||
|
|
||||||
func TestNetworkManagerBackend_FindConnection_NoSettings(t *testing.T) {
|
func TestNetworkManagerBackend_FindConnection_NoSettings(t *testing.T) {
|
||||||
mockNM := mock_gonetworkmanager.NewMockNetworkManager(t)
|
mockNM := mock_gonetworkmanager.NewMockNetworkManager(t)
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ func NewManager() (*Manager, error) {
|
|||||||
NetworkStatus: StatusDisconnected,
|
NetworkStatus: StatusDisconnected,
|
||||||
Preference: PreferenceAuto,
|
Preference: PreferenceAuto,
|
||||||
WiFiNetworks: []WiFiNetwork{},
|
WiFiNetworks: []WiFiNetwork{},
|
||||||
|
SavedWiFiNetworks: []WiFiNetwork{},
|
||||||
},
|
},
|
||||||
stateMutex: sync.RWMutex{},
|
stateMutex: sync.RWMutex{},
|
||||||
|
|
||||||
@@ -120,6 +121,7 @@ func (m *Manager) syncStateFromBackend() error {
|
|||||||
m.state.WiFiBSSID = backendState.WiFiBSSID
|
m.state.WiFiBSSID = backendState.WiFiBSSID
|
||||||
m.state.WiFiSignal = backendState.WiFiSignal
|
m.state.WiFiSignal = backendState.WiFiSignal
|
||||||
m.state.WiFiNetworks = backendState.WiFiNetworks
|
m.state.WiFiNetworks = backendState.WiFiNetworks
|
||||||
|
m.state.SavedWiFiNetworks = backendState.SavedWiFiNetworks
|
||||||
m.state.WiFiDevices = backendState.WiFiDevices
|
m.state.WiFiDevices = backendState.WiFiDevices
|
||||||
m.state.WiredConnections = backendState.WiredConnections
|
m.state.WiredConnections = backendState.WiredConnections
|
||||||
m.state.VPNProfiles = backendState.VPNProfiles
|
m.state.VPNProfiles = backendState.VPNProfiles
|
||||||
@@ -156,6 +158,7 @@ func (m *Manager) snapshotState() NetworkState {
|
|||||||
defer m.stateMutex.RUnlock()
|
defer m.stateMutex.RUnlock()
|
||||||
s := *m.state
|
s := *m.state
|
||||||
s.WiFiNetworks = append([]WiFiNetwork(nil), m.state.WiFiNetworks...)
|
s.WiFiNetworks = append([]WiFiNetwork(nil), m.state.WiFiNetworks...)
|
||||||
|
s.SavedWiFiNetworks = append([]WiFiNetwork(nil), m.state.SavedWiFiNetworks...)
|
||||||
s.WiFiDevices = append([]WiFiDevice(nil), m.state.WiFiDevices...)
|
s.WiFiDevices = append([]WiFiDevice(nil), m.state.WiFiDevices...)
|
||||||
s.WiredConnections = append([]WiredConnection(nil), m.state.WiredConnections...)
|
s.WiredConnections = append([]WiredConnection(nil), m.state.WiredConnections...)
|
||||||
s.EthernetDevices = append([]EthernetDevice(nil), m.state.EthernetDevices...)
|
s.EthernetDevices = append([]EthernetDevice(nil), m.state.EthernetDevices...)
|
||||||
@@ -211,6 +214,9 @@ func stateChangedMeaningfully(old, new *NetworkState) bool {
|
|||||||
if len(old.WiFiNetworks) != len(new.WiFiNetworks) {
|
if len(old.WiFiNetworks) != len(new.WiFiNetworks) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if len(old.SavedWiFiNetworks) != len(new.SavedWiFiNetworks) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
if len(old.WiFiDevices) != len(new.WiFiDevices) {
|
if len(old.WiFiDevices) != len(new.WiFiDevices) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -238,6 +244,23 @@ func stateChangedMeaningfully(old, new *NetworkState) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i := range old.SavedWiFiNetworks {
|
||||||
|
oldNet := &old.SavedWiFiNetworks[i]
|
||||||
|
newNet := &new.SavedWiFiNetworks[i]
|
||||||
|
if oldNet.SSID != newNet.SSID {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if oldNet.Connected != newNet.Connected {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if oldNet.Autoconnect != newNet.Autoconnect {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if oldNet.OutOfRange != newNet.OutOfRange {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for i := range old.WiredConnections {
|
for i := range old.WiredConnections {
|
||||||
oldNet := &old.WiredConnections[i]
|
oldNet := &old.WiredConnections[i]
|
||||||
newNet := &new.WiredConnections[i]
|
newNet := &new.WiredConnections[i]
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ type WiFiNetwork struct {
|
|||||||
Saved bool `json:"saved"`
|
Saved bool `json:"saved"`
|
||||||
Autoconnect bool `json:"autoconnect"`
|
Autoconnect bool `json:"autoconnect"`
|
||||||
Hidden bool `json:"hidden"`
|
Hidden bool `json:"hidden"`
|
||||||
|
OutOfRange bool `json:"outOfRange"`
|
||||||
Frequency uint32 `json:"frequency"`
|
Frequency uint32 `json:"frequency"`
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
Rate uint32 `json:"rate"`
|
Rate uint32 `json:"rate"`
|
||||||
@@ -111,6 +112,7 @@ type NetworkState struct {
|
|||||||
WiFiBSSID string `json:"wifiBSSID"`
|
WiFiBSSID string `json:"wifiBSSID"`
|
||||||
WiFiSignal uint8 `json:"wifiSignal"`
|
WiFiSignal uint8 `json:"wifiSignal"`
|
||||||
WiFiNetworks []WiFiNetwork `json:"wifiNetworks"`
|
WiFiNetworks []WiFiNetwork `json:"wifiNetworks"`
|
||||||
|
SavedWiFiNetworks []WiFiNetwork `json:"savedWifiNetworks"`
|
||||||
WiFiDevices []WiFiDevice `json:"wifiDevices"`
|
WiFiDevices []WiFiDevice `json:"wifiDevices"`
|
||||||
WiredConnections []WiredConnection `json:"wiredConnections"`
|
WiredConnections []WiredConnection `json:"wiredConnections"`
|
||||||
VPNProfiles []VPNProfile `json:"vpnProfiles"`
|
VPNProfiles []VPNProfile `json:"vpnProfiles"`
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package network
|
||||||
|
|
||||||
|
import "sort"
|
||||||
|
|
||||||
|
type savedWiFiProfile struct {
|
||||||
|
Autoconnect bool
|
||||||
|
Hidden bool
|
||||||
|
Secured bool
|
||||||
|
Enterprise bool
|
||||||
|
Mode string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Saved WiFi state is keyed by SSID because the UI/API accepts SSID actions.
|
||||||
|
// Multiple backend profiles for the same SSID are intentionally collapsed here.
|
||||||
|
func mergeSavedProfilesIntoWiFiNetworks(networks []WiFiNetwork, profiles map[string]savedWiFiProfile, currentSSID string, wifiConnected bool) []WiFiNetwork {
|
||||||
|
merged := make([]WiFiNetwork, len(networks))
|
||||||
|
for i, network := range networks {
|
||||||
|
profile, saved := profiles[network.SSID]
|
||||||
|
network.Connected = wifiConnected && network.SSID == currentSSID
|
||||||
|
network.Saved = saved
|
||||||
|
if saved {
|
||||||
|
network.Autoconnect = profile.Autoconnect
|
||||||
|
network.Hidden = network.Hidden || profile.Hidden
|
||||||
|
network.Secured = network.Secured || profile.Secured
|
||||||
|
network.Enterprise = network.Enterprise || profile.Enterprise
|
||||||
|
if network.Mode == "" {
|
||||||
|
network.Mode = profile.Mode
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
network.Autoconnect = false
|
||||||
|
}
|
||||||
|
merged[i] = network
|
||||||
|
}
|
||||||
|
return merged
|
||||||
|
}
|
||||||
|
|
||||||
|
func wiFiNetworksBySSID(networks []WiFiNetwork, visibleOnly bool) map[string]WiFiNetwork {
|
||||||
|
visible := make(map[string]WiFiNetwork, len(networks))
|
||||||
|
for _, network := range networks {
|
||||||
|
if visibleOnly && network.OutOfRange {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
visible[network.SSID] = network
|
||||||
|
}
|
||||||
|
return visible
|
||||||
|
}
|
||||||
|
|
||||||
|
func refreshSavedWiFiState(networks []WiFiNetwork, profiles map[string]savedWiFiProfile, currentSSID string, wifiConnected bool) ([]WiFiNetwork, []WiFiNetwork) {
|
||||||
|
mergedNetworks := mergeSavedProfilesIntoWiFiNetworks(networks, profiles, currentSSID, wifiConnected)
|
||||||
|
visibleNetworks := wiFiNetworksBySSID(mergedNetworks, true)
|
||||||
|
savedNetworks := savedWiFiNetworksFromProfiles(profiles, visibleNetworks, currentSSID, wifiConnected)
|
||||||
|
return mergedNetworks, savedNetworks
|
||||||
|
}
|
||||||
|
|
||||||
|
func savedWiFiNetworksFromProfiles(profiles map[string]savedWiFiProfile, visible map[string]WiFiNetwork, currentSSID string, wifiConnected bool) []WiFiNetwork {
|
||||||
|
networks := make([]WiFiNetwork, 0, len(profiles))
|
||||||
|
for ssid, profile := range profiles {
|
||||||
|
if network, ok := visible[ssid]; ok {
|
||||||
|
network.Saved = true
|
||||||
|
network.Autoconnect = profile.Autoconnect
|
||||||
|
network.Hidden = network.Hidden || profile.Hidden
|
||||||
|
network.Secured = network.Secured || profile.Secured
|
||||||
|
network.Enterprise = network.Enterprise || profile.Enterprise
|
||||||
|
network.OutOfRange = false
|
||||||
|
if network.Mode == "" {
|
||||||
|
network.Mode = profile.Mode
|
||||||
|
}
|
||||||
|
networks = append(networks, network)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
isConnected := wifiConnected && ssid == currentSSID
|
||||||
|
networks = append(networks, WiFiNetwork{
|
||||||
|
SSID: ssid,
|
||||||
|
Secured: profile.Secured,
|
||||||
|
Enterprise: profile.Enterprise,
|
||||||
|
Connected: isConnected,
|
||||||
|
Saved: true,
|
||||||
|
Autoconnect: profile.Autoconnect,
|
||||||
|
Hidden: profile.Hidden,
|
||||||
|
OutOfRange: !isConnected,
|
||||||
|
Mode: profile.Mode,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(networks, func(i, j int) bool {
|
||||||
|
if networks[i].Connected && !networks[j].Connected {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !networks[i].Connected && networks[j].Connected {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if networks[i].OutOfRange != networks[j].OutOfRange {
|
||||||
|
return !networks[i].OutOfRange
|
||||||
|
}
|
||||||
|
if networks[i].Signal != networks[j].Signal {
|
||||||
|
return networks[i].Signal > networks[j].Signal
|
||||||
|
}
|
||||||
|
return networks[i].SSID < networks[j].SSID
|
||||||
|
})
|
||||||
|
|
||||||
|
return networks
|
||||||
|
}
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMergeSavedProfilesIntoWiFiNetworks(t *testing.T) {
|
||||||
|
networks := []WiFiNetwork{
|
||||||
|
{
|
||||||
|
SSID: "Home",
|
||||||
|
Signal: 80,
|
||||||
|
Secured: false,
|
||||||
|
Autoconnect: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SSID: "Cafe",
|
||||||
|
Signal: 50,
|
||||||
|
Secured: false,
|
||||||
|
Autoconnect: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
profiles := map[string]savedWiFiProfile{
|
||||||
|
"Home": {
|
||||||
|
Autoconnect: true,
|
||||||
|
Hidden: true,
|
||||||
|
Secured: true,
|
||||||
|
Mode: "infrastructure",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
merged := mergeSavedProfilesIntoWiFiNetworks(networks, profiles, "Home", true)
|
||||||
|
|
||||||
|
assert.Len(t, merged, 2)
|
||||||
|
assert.Equal(t, "Home", merged[0].SSID)
|
||||||
|
assert.True(t, merged[0].Connected)
|
||||||
|
assert.True(t, merged[0].Saved)
|
||||||
|
assert.True(t, merged[0].Autoconnect)
|
||||||
|
assert.True(t, merged[0].Hidden)
|
||||||
|
assert.True(t, merged[0].Secured)
|
||||||
|
assert.Equal(t, "infrastructure", merged[0].Mode)
|
||||||
|
|
||||||
|
assert.Equal(t, "Cafe", merged[1].SSID)
|
||||||
|
assert.False(t, merged[1].Saved)
|
||||||
|
assert.False(t, merged[1].Autoconnect)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSavedWiFiNetworksFromProfilesOutOfRangeWithoutVisibleNetworks(t *testing.T) {
|
||||||
|
profiles := map[string]savedWiFiProfile{
|
||||||
|
"Home": {
|
||||||
|
Autoconnect: true,
|
||||||
|
Secured: true,
|
||||||
|
Mode: "infrastructure",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
networks := savedWiFiNetworksFromProfiles(profiles, nil, "", false)
|
||||||
|
|
||||||
|
assert.Len(t, networks, 1)
|
||||||
|
assert.Equal(t, "Home", networks[0].SSID)
|
||||||
|
assert.True(t, networks[0].Saved)
|
||||||
|
assert.True(t, networks[0].OutOfRange)
|
||||||
|
assert.Equal(t, uint8(0), networks[0].Signal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSavedWiFiNetworksFromProfilesKeepsConnectedCurrentNetworkInRange(t *testing.T) {
|
||||||
|
profiles := map[string]savedWiFiProfile{
|
||||||
|
"Home": {
|
||||||
|
Autoconnect: true,
|
||||||
|
Secured: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
networks := savedWiFiNetworksFromProfiles(profiles, nil, "Home", true)
|
||||||
|
|
||||||
|
assert.Len(t, networks, 1)
|
||||||
|
assert.Equal(t, "Home", networks[0].SSID)
|
||||||
|
assert.True(t, networks[0].Connected)
|
||||||
|
assert.False(t, networks[0].OutOfRange)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSavedWiFiNetworksFromProfilesIncludesOutOfRange(t *testing.T) {
|
||||||
|
profiles := map[string]savedWiFiProfile{
|
||||||
|
"Home": {
|
||||||
|
Autoconnect: true,
|
||||||
|
Hidden: true,
|
||||||
|
Secured: true,
|
||||||
|
Mode: "infrastructure",
|
||||||
|
},
|
||||||
|
"Office": {
|
||||||
|
Autoconnect: false,
|
||||||
|
Secured: true,
|
||||||
|
Enterprise: true,
|
||||||
|
Mode: "infrastructure",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
visible := map[string]WiFiNetwork{
|
||||||
|
"Home": {
|
||||||
|
SSID: "Home",
|
||||||
|
Signal: 72,
|
||||||
|
Secured: true,
|
||||||
|
Connected: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
networks := savedWiFiNetworksFromProfiles(profiles, visible, "Home", true)
|
||||||
|
|
||||||
|
assert.Len(t, networks, 2)
|
||||||
|
assert.Equal(t, "Home", networks[0].SSID)
|
||||||
|
assert.True(t, networks[0].Saved)
|
||||||
|
assert.True(t, networks[0].Connected)
|
||||||
|
assert.False(t, networks[0].OutOfRange)
|
||||||
|
assert.True(t, networks[0].Hidden)
|
||||||
|
assert.Equal(t, uint8(72), networks[0].Signal)
|
||||||
|
|
||||||
|
assert.Equal(t, "Office", networks[1].SSID)
|
||||||
|
assert.True(t, networks[1].Saved)
|
||||||
|
assert.False(t, networks[1].Autoconnect)
|
||||||
|
assert.True(t, networks[1].Enterprise)
|
||||||
|
assert.True(t, networks[1].OutOfRange)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWiFiNetworksBySSIDVisibleOnlySkipsOutOfRange(t *testing.T) {
|
||||||
|
visible := wiFiNetworksBySSID([]WiFiNetwork{
|
||||||
|
{SSID: "Home", Signal: 70},
|
||||||
|
{SSID: "Office", Signal: 0, OutOfRange: true},
|
||||||
|
}, true)
|
||||||
|
|
||||||
|
assert.Contains(t, visible, "Home")
|
||||||
|
assert.NotContains(t, visible, "Office")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRefreshSavedWiFiStatePreservesVisibleSavedNetworks(t *testing.T) {
|
||||||
|
networks := []WiFiNetwork{
|
||||||
|
{
|
||||||
|
SSID: "Home",
|
||||||
|
Signal: 82,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
profiles := map[string]savedWiFiProfile{
|
||||||
|
"Home": {
|
||||||
|
Autoconnect: true,
|
||||||
|
Secured: true,
|
||||||
|
Mode: "infrastructure",
|
||||||
|
},
|
||||||
|
"Office": {
|
||||||
|
Autoconnect: false,
|
||||||
|
Secured: true,
|
||||||
|
Mode: "infrastructure",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mergedNetworks, savedNetworks := refreshSavedWiFiState(networks, profiles, "", false)
|
||||||
|
|
||||||
|
assert.Len(t, mergedNetworks, 1)
|
||||||
|
assert.Equal(t, "Home", mergedNetworks[0].SSID)
|
||||||
|
assert.True(t, mergedNetworks[0].Saved)
|
||||||
|
assert.True(t, mergedNetworks[0].Autoconnect)
|
||||||
|
|
||||||
|
assert.Len(t, savedNetworks, 2)
|
||||||
|
assert.Equal(t, "Home", savedNetworks[0].SSID)
|
||||||
|
assert.True(t, savedNetworks[0].Saved)
|
||||||
|
assert.False(t, savedNetworks[0].OutOfRange)
|
||||||
|
assert.Equal(t, uint8(82), savedNetworks[0].Signal)
|
||||||
|
|
||||||
|
assert.Equal(t, "Office", savedNetworks[1].SSID)
|
||||||
|
assert.True(t, savedNetworks[1].Saved)
|
||||||
|
assert.True(t, savedNetworks[1].OutOfRange)
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@ import (
|
|||||||
"github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
|
"github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
|
||||||
)
|
)
|
||||||
|
|
||||||
const APIVersion = 25
|
const APIVersion = 26
|
||||||
|
|
||||||
var CLIVersion = "dev"
|
var CLIVersion = "dev"
|
||||||
|
|
||||||
|
|||||||
@@ -721,7 +721,7 @@ Rectangle {
|
|||||||
|
|
||||||
DankActionButton {
|
DankActionButton {
|
||||||
id: qrCodeButton
|
id: qrCodeButton
|
||||||
visible: modelData.secured && modelData.saved
|
visible: modelData.secured && modelData.saved && !(modelData.enterprise || false)
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: optionsButton.width + pinWifiRow.width + 3 * Theme.spacingM + Theme.spacingS
|
anchors.rightMargin: optionsButton.width + pinWifiRow.width + 3 * Theme.spacingM + Theme.spacingS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modules.Settings.Widgets
|
import qs.Modules.Settings.Widgets
|
||||||
@@ -16,6 +17,7 @@ Item {
|
|||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
NetworkService.addRef();
|
NetworkService.addRef();
|
||||||
|
Qt.callLater(() => NetworkService.refreshSavedWifiNetworks());
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onDestruction: {
|
Component.onDestruction: {
|
||||||
@@ -40,6 +42,7 @@ Item {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string expandedWifiSsid: ""
|
property string expandedWifiSsid: ""
|
||||||
|
property string expandedSavedWifiSsid: ""
|
||||||
property int maxPinnedWifiNetworks: 3
|
property int maxPinnedWifiNetworks: 3
|
||||||
|
|
||||||
function normalizePinList(value) {
|
function normalizePinList(value) {
|
||||||
@@ -84,6 +87,79 @@ Item {
|
|||||||
settingKey: "networkWifi"
|
settingKey: "networkWifi"
|
||||||
tags: ["wifi", "wi-fi", "wireless", "network", "ssid", "adapter", "radio"]
|
tags: ["wifi", "wi-fi", "wireless", "network", "ssid", "adapter", "radio"]
|
||||||
|
|
||||||
|
function visibleWifiBySsid(ssid) {
|
||||||
|
const networks = NetworkService.wifiNetworks || [];
|
||||||
|
return networks.find(network => network.ssid === ssid) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergedSavedWifiNetworks() {
|
||||||
|
const saved = NetworkService.savedWifiNetworks || [];
|
||||||
|
const supportsSavedWifiState = DMSService.apiVersion >= NetworkService.savedWifiStateApiVersion;
|
||||||
|
const result = [];
|
||||||
|
const seen = new Set();
|
||||||
|
|
||||||
|
for (const network of saved) {
|
||||||
|
if (!network?.ssid || seen.has(network.ssid))
|
||||||
|
continue;
|
||||||
|
const isOutOfRange = supportsSavedWifiState ? network.outOfRange === true : false;
|
||||||
|
const visibleNetwork = !isOutOfRange ? visibleWifiBySsid(network.ssid) : null;
|
||||||
|
if (visibleNetwork) {
|
||||||
|
result.push(Object.assign({}, network, visibleNetwork, {
|
||||||
|
saved: true,
|
||||||
|
autoconnect: network.autoconnect ?? visibleNetwork.autoconnect,
|
||||||
|
hidden: (network.hidden || false) || (visibleNetwork.hidden || false),
|
||||||
|
outOfRange: false
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
result.push(Object.assign({}, network, {
|
||||||
|
saved: true,
|
||||||
|
outOfRange: isOutOfRange
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
seen.add(network.ssid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortedSavedWifiNetworks() {
|
||||||
|
const ssid = NetworkService.currentWifiSSID;
|
||||||
|
const pinnedList = root.getPinnedWifiNetworks();
|
||||||
|
let sorted = root.mergedSavedWifiNetworks();
|
||||||
|
|
||||||
|
sorted.sort((a, b) => {
|
||||||
|
const aPinnedIndex = pinnedList.indexOf(a.ssid);
|
||||||
|
const bPinnedIndex = pinnedList.indexOf(b.ssid);
|
||||||
|
if (aPinnedIndex !== -1 || bPinnedIndex !== -1) {
|
||||||
|
if (aPinnedIndex === -1)
|
||||||
|
return 1;
|
||||||
|
if (bPinnedIndex === -1)
|
||||||
|
return -1;
|
||||||
|
return aPinnedIndex - bPinnedIndex;
|
||||||
|
}
|
||||||
|
if (a.ssid === ssid)
|
||||||
|
return -1;
|
||||||
|
if (b.ssid === ssid)
|
||||||
|
return 1;
|
||||||
|
if ((a.outOfRange || false) !== (b.outOfRange || false))
|
||||||
|
return (a.outOfRange || false) ? 1 : -1;
|
||||||
|
if ((a.signal || 0) !== (b.signal || 0))
|
||||||
|
return (b.signal || 0) - (a.signal || 0);
|
||||||
|
return (a.ssid || "").localeCompare(b.ssid || "");
|
||||||
|
});
|
||||||
|
return sorted;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showForgetNetworkConfirm(ssid) {
|
||||||
|
forgetNetworkConfirm.showWithOptions({
|
||||||
|
title: I18n.tr("Forget Network"),
|
||||||
|
message: I18n.tr("Forget \"%1\"?").arg(ssid),
|
||||||
|
confirmText: I18n.tr("Forget"),
|
||||||
|
confirmColor: Theme.error,
|
||||||
|
onConfirm: () => NetworkService.forgetWifiNetwork(ssid)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: wifiSection
|
id: wifiSection
|
||||||
|
|
||||||
@@ -563,7 +639,7 @@ Item {
|
|||||||
DankActionButton {
|
DankActionButton {
|
||||||
iconName: "qr_code"
|
iconName: "qr_code"
|
||||||
buttonSize: 28
|
buttonSize: 28
|
||||||
visible: modelData.secured && modelData.saved
|
visible: modelData.secured && modelData.saved && !(modelData.enterprise || false)
|
||||||
onClicked: {
|
onClicked: {
|
||||||
PopoutService.showWifiQRCodeModal(modelData.ssid);
|
PopoutService.showWifiQRCodeModal(modelData.ssid);
|
||||||
}
|
}
|
||||||
@@ -584,13 +660,7 @@ Item {
|
|||||||
iconColor: Theme.error
|
iconColor: Theme.error
|
||||||
visible: modelData.saved || isConnected
|
visible: modelData.saved || isConnected
|
||||||
onClicked: {
|
onClicked: {
|
||||||
forgetNetworkConfirm.showWithOptions({
|
root.showForgetNetworkConfirm(modelData.ssid);
|
||||||
title: I18n.tr("Forget Network"),
|
|
||||||
message: I18n.tr("Forget \"%1\"?").arg(modelData.ssid),
|
|
||||||
confirmText: I18n.tr("Forget"),
|
|
||||||
confirmColor: Theme.error,
|
|
||||||
onConfirm: () => NetworkService.forgetWifiNetwork(modelData.ssid)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -756,6 +826,421 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SettingsCard {
|
||||||
|
id: savedWifiCard
|
||||||
|
|
||||||
|
readonly property var savedNetworks: root.sortedSavedWifiNetworks()
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
title: I18n.tr("Saved Networks")
|
||||||
|
iconName: "bookmark"
|
||||||
|
settingKey: "networkSavedWifi"
|
||||||
|
tags: ["wifi", "wi-fi", "wireless", "network", "saved", "known", "ssid", "autoconnect", "forget"]
|
||||||
|
collapsible: true
|
||||||
|
expanded: false
|
||||||
|
visible: savedNetworks.length > 0
|
||||||
|
|
||||||
|
headerActions: [
|
||||||
|
StyledText {
|
||||||
|
text: savedWifiCard.savedNetworks.length
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: savedWifiCard.expanded ? savedWifiCard.savedNetworks : []
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
id: savedWifiDelegate
|
||||||
|
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
readonly property bool isConnected: modelData.ssid === NetworkService.currentWifiSSID
|
||||||
|
readonly property bool isPinned: root.getPinnedWifiNetworks().includes(modelData.ssid)
|
||||||
|
readonly property bool isOutOfRange: modelData.outOfRange || false
|
||||||
|
readonly property bool isExpanded: !isOutOfRange && root.expandedSavedWifiSsid === modelData.ssid
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: isExpanded ? 56 + savedWifiExpandedContent.height : 56
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: savedWifiMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceLight
|
||||||
|
border.width: isConnected ? 2 : 0
|
||||||
|
border.color: Theme.primary
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
easing.type: Easing.OutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: 56
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: savedWifiActions.left
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: {
|
||||||
|
if (isOutOfRange)
|
||||||
|
return "wifi_off";
|
||||||
|
const s = modelData.signal || 0;
|
||||||
|
if (s >= 50)
|
||||||
|
return "wifi";
|
||||||
|
if (s >= 25)
|
||||||
|
return "wifi_2_bar";
|
||||||
|
return "wifi_1_bar";
|
||||||
|
}
|
||||||
|
size: 20
|
||||||
|
color: isConnected ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 2
|
||||||
|
width: parent.width - 20 - Theme.spacingS
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.ssid || I18n.tr("Unknown")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: isConnected ? Theme.primary : Theme.surfaceText
|
||||||
|
font.weight: isConnected ? Font.Medium : Font.Normal
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: Math.max(0, parent.width - (savedWifiHiddenIcon.visible ? savedWifiHiddenIcon.width + Theme.spacingXS : 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
id: savedWifiHiddenIcon
|
||||||
|
name: "visibility_off"
|
||||||
|
size: 14
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
visible: modelData.hidden || false
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
const parts = [isConnected ? I18n.tr("Connected") : (modelData.secured ? I18n.tr("Secured") : I18n.tr("Open"))];
|
||||||
|
parts.push(isOutOfRange ? I18n.tr("Unavailable") : (modelData.signal || 0) + "%");
|
||||||
|
if (modelData.hidden || false)
|
||||||
|
parts.push(I18n.tr("Hidden"));
|
||||||
|
return parts.join(" • ");
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: isConnected ? Theme.primary : Theme.surfaceVariantText
|
||||||
|
width: parent.width
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: savedWifiActions
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 28
|
||||||
|
height: 28
|
||||||
|
radius: 14
|
||||||
|
color: savedWifiExpandBtn.containsMouse ? Theme.surfacePressed : "transparent"
|
||||||
|
visible: !isOutOfRange
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: isExpanded ? "expand_less" : "expand_more"
|
||||||
|
size: 18
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: savedWifiExpandBtn
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (isExpanded) {
|
||||||
|
root.expandedSavedWifiSsid = "";
|
||||||
|
} else {
|
||||||
|
root.expandedSavedWifiSsid = modelData.ssid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
iconName: "qr_code"
|
||||||
|
buttonSize: 28
|
||||||
|
visible: modelData.secured && !(modelData.enterprise || false)
|
||||||
|
onClicked: {
|
||||||
|
PopoutService.showWifiQRCodeModal(modelData.ssid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
iconName: "push_pin"
|
||||||
|
buttonSize: 28
|
||||||
|
iconColor: isPinned ? Theme.primary : Theme.surfaceVariantText
|
||||||
|
onClicked: {
|
||||||
|
root.toggleWifiPin(modelData.ssid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
id: savedWifiMoreButton
|
||||||
|
iconName: "more_horiz"
|
||||||
|
buttonSize: 28
|
||||||
|
onClicked: {
|
||||||
|
if (savedWifiMenu.visible) {
|
||||||
|
savedWifiMenu.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
savedWifiMenu.popup(savedWifiMoreButton, -savedWifiMenu.width + savedWifiMoreButton.width, savedWifiMoreButton.height + Theme.spacingXS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: savedWifiMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.rightMargin: savedWifiActions.width + Theme.spacingM
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: isOutOfRange ? Qt.ArrowCursor : Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (isOutOfRange)
|
||||||
|
return;
|
||||||
|
if (isExpanded) {
|
||||||
|
root.expandedSavedWifiSsid = "";
|
||||||
|
} else {
|
||||||
|
root.expandedSavedWifiSsid = modelData.ssid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: savedWifiExpandedContent
|
||||||
|
width: parent.width
|
||||||
|
visible: isExpanded
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width - Theme.spacingM * 2
|
||||||
|
height: 1
|
||||||
|
x: Theme.spacingM
|
||||||
|
color: Theme.outlineLight
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: savedWifiDetailsColumn.implicitHeight + Theme.spacingM * 2
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: savedWifiDetailsColumn
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Flow {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: {
|
||||||
|
const fields = [];
|
||||||
|
const net = modelData;
|
||||||
|
if (!net)
|
||||||
|
return fields;
|
||||||
|
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Signal"),
|
||||||
|
value: (net.signal || 0) + "%"
|
||||||
|
});
|
||||||
|
if (net.frequency)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Frequency"),
|
||||||
|
value: (net.frequency / 1000).toFixed(1) + " GHz"
|
||||||
|
});
|
||||||
|
if (net.channel)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Channel"),
|
||||||
|
value: String(net.channel)
|
||||||
|
});
|
||||||
|
if (net.rate)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Rate"),
|
||||||
|
value: net.rate + " Mbps"
|
||||||
|
});
|
||||||
|
if (net.mode)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Mode"),
|
||||||
|
value: net.mode
|
||||||
|
});
|
||||||
|
if (net.bssid)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("BSSID"),
|
||||||
|
value: net.bssid
|
||||||
|
});
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Security"),
|
||||||
|
value: net.secured ? (net.enterprise ? I18n.tr("Enterprise") : I18n.tr("WPA/WPA2")) : I18n.tr("Open")
|
||||||
|
});
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
width: savedWifiFieldContent.width + Theme.spacingM * 2
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius - 2
|
||||||
|
color: Theme.surfaceContainerHigh
|
||||||
|
border.width: 1
|
||||||
|
border.color: Theme.outlineLight
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: savedWifiFieldContent
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.label + ":"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.value
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu {
|
||||||
|
id: savedWifiMenu
|
||||||
|
width: 170
|
||||||
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
border.width: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: isConnected ? I18n.tr("Disconnect") : I18n.tr("Connect")
|
||||||
|
height: isOutOfRange ? 0 : 32
|
||||||
|
visible: !isOutOfRange
|
||||||
|
|
||||||
|
contentItem: StyledText {
|
||||||
|
text: parent.text
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
leftPadding: Theme.spacingS
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||||
|
radius: Theme.cornerRadius / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
if (isConnected) {
|
||||||
|
NetworkService.disconnectWifi();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NetworkService.connectToWifi(modelData.ssid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: modelData.autoconnect ? I18n.tr("Disable Autoconnect") : I18n.tr("Enable Autoconnect")
|
||||||
|
height: DMSService.apiVersion > 13 ? 32 : 0
|
||||||
|
visible: DMSService.apiVersion > 13
|
||||||
|
|
||||||
|
contentItem: StyledText {
|
||||||
|
text: parent.text
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
leftPadding: Theme.spacingS
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||||
|
radius: Theme.cornerRadius / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
NetworkService.setWifiAutoconnect(modelData.ssid, !(modelData.autoconnect || false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: I18n.tr("Forget Network")
|
||||||
|
height: 32
|
||||||
|
|
||||||
|
contentItem: StyledText {
|
||||||
|
text: parent.text
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.error
|
||||||
|
leftPadding: Theme.spacingS
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: parent.hovered ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.08) : "transparent"
|
||||||
|
radius: Theme.cornerRadius / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
root.showForgetNetworkConfirm(modelData.ssid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ Singleton {
|
|||||||
property bool changingPreference: false
|
property bool changingPreference: false
|
||||||
property string targetPreference: ""
|
property string targetPreference: ""
|
||||||
property var savedWifiNetworks: []
|
property var savedWifiNetworks: []
|
||||||
|
readonly property int savedWifiStateApiVersion: 26
|
||||||
property string connectionStatus: ""
|
property string connectionStatus: ""
|
||||||
property string lastConnectionError: ""
|
property string lastConnectionError: ""
|
||||||
property bool passwordDialogShouldReopen: false
|
property bool passwordDialogShouldReopen: false
|
||||||
@@ -309,18 +310,22 @@ Singleton {
|
|||||||
|
|
||||||
if (state.wifiNetworks) {
|
if (state.wifiNetworks) {
|
||||||
wifiNetworks = state.wifiNetworks;
|
wifiNetworks = state.wifiNetworks;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.wifiNetworks || state.savedWifiNetworks) {
|
||||||
|
const hasSavedWifiState = DMSService.apiVersion >= savedWifiStateApiVersion && Array.isArray(state.savedWifiNetworks);
|
||||||
|
const sourceSavedNetworks = hasSavedWifiState ? state.savedWifiNetworks : (state.wifiNetworks || []).filter(network => network.saved);
|
||||||
const saved = [];
|
const saved = [];
|
||||||
const mapping = {};
|
const mapping = {};
|
||||||
for (const network of state.wifiNetworks) {
|
for (const network of sourceSavedNetworks) {
|
||||||
if (network.saved) {
|
const normalized = Object.assign({}, network, {
|
||||||
saved.push({
|
saved: true,
|
||||||
ssid: network.ssid,
|
outOfRange: hasSavedWifiState ? network.outOfRange === true : false
|
||||||
saved: true
|
|
||||||
});
|
});
|
||||||
|
saved.push(normalized);
|
||||||
|
if (network?.ssid)
|
||||||
mapping[network.ssid] = network.ssid;
|
mapping[network.ssid] = network.ssid;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
savedConnections = saved;
|
savedConnections = saved;
|
||||||
savedWifiNetworks = saved;
|
savedWifiNetworks = saved;
|
||||||
ssidToConnectionName = mapping;
|
ssidToConnectionName = mapping;
|
||||||
@@ -596,6 +601,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
wifiNetworks = updated;
|
wifiNetworks = updated;
|
||||||
networksUpdated();
|
networksUpdated();
|
||||||
|
Qt.callLater(() => refreshSavedWifiNetworks());
|
||||||
}
|
}
|
||||||
forgetSSID = "";
|
forgetSSID = "";
|
||||||
});
|
});
|
||||||
@@ -985,4 +991,11 @@ Singleton {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function refreshSavedWifiNetworks() {
|
||||||
|
if (!networkAvailable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
getState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,9 +142,11 @@ Singleton {
|
|||||||
|
|
||||||
readonly property var savedConnections: wifiNetworks.filter(n => n.saved).map(n => ({
|
readonly property var savedConnections: wifiNetworks.filter(n => n.saved).map(n => ({
|
||||||
"ssid": n.ssid,
|
"ssid": n.ssid,
|
||||||
"saved": true
|
"saved": true,
|
||||||
|
"outOfRange": false
|
||||||
}))
|
}))
|
||||||
readonly property var savedWifiNetworks: savedConnections
|
readonly property var savedWifiNetworks: savedConnections
|
||||||
|
readonly property int savedWifiStateApiVersion: 26
|
||||||
readonly property var ssidToConnectionName: {
|
readonly property var ssidToConnectionName: {
|
||||||
const map = {};
|
const map = {};
|
||||||
for (const n of wifiNetworks) {
|
for (const n of wifiNetworks) {
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ Singleton {
|
|||||||
property bool changingPreference: activeService?.changingPreference ?? false
|
property bool changingPreference: activeService?.changingPreference ?? false
|
||||||
property string targetPreference: activeService?.targetPreference ?? ""
|
property string targetPreference: activeService?.targetPreference ?? ""
|
||||||
property var savedWifiNetworks: activeService?.savedWifiNetworks ?? []
|
property var savedWifiNetworks: activeService?.savedWifiNetworks ?? []
|
||||||
|
readonly property int savedWifiStateApiVersion: activeService?.savedWifiStateApiVersion ?? 26
|
||||||
property string connectionStatus: activeService?.connectionStatus ?? ""
|
property string connectionStatus: activeService?.connectionStatus ?? ""
|
||||||
property string lastConnectionError: activeService?.lastConnectionError ?? ""
|
property string lastConnectionError: activeService?.lastConnectionError ?? ""
|
||||||
property bool passwordDialogShouldReopen: activeService?.passwordDialogShouldReopen ?? false
|
property bool passwordDialogShouldReopen: activeService?.passwordDialogShouldReopen ?? false
|
||||||
@@ -180,6 +181,12 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function refreshSavedWifiNetworks() {
|
||||||
|
if (activeService && activeService.refreshSavedWifiNetworks) {
|
||||||
|
activeService.refreshSavedWifiNetworks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function connectToWifi(ssid, password = "", username = "", anonymousIdentity = "", domainSuffixMatch = "") {
|
function connectToWifi(ssid, password = "", username = "", anonymousIdentity = "", domainSuffixMatch = "") {
|
||||||
if (activeService && activeService.connectToWifi) {
|
if (activeService && activeService.connectToWifi) {
|
||||||
activeService.connectToWifi(ssid, password, username, anonymousIdentity, domainSuffixMatch);
|
activeService.connectToWifi(ssid, password, username, anonymousIdentity, domainSuffixMatch);
|
||||||
|
|||||||
@@ -8882,6 +8882,30 @@
|
|||||||
],
|
],
|
||||||
"icon": "settings_ethernet"
|
"icon": "settings_ethernet"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "networkSavedWifi",
|
||||||
|
"label": "Saved Networks",
|
||||||
|
"tabIndex": 40,
|
||||||
|
"category": "Network",
|
||||||
|
"keywords": [
|
||||||
|
"autoconnect",
|
||||||
|
"connection",
|
||||||
|
"connectivity",
|
||||||
|
"ethernet",
|
||||||
|
"forget",
|
||||||
|
"internet",
|
||||||
|
"known",
|
||||||
|
"network",
|
||||||
|
"networks",
|
||||||
|
"online",
|
||||||
|
"saved",
|
||||||
|
"ssid",
|
||||||
|
"wi-fi",
|
||||||
|
"wifi",
|
||||||
|
"wireless"
|
||||||
|
],
|
||||||
|
"icon": "bookmark"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "networkWifi",
|
"section": "networkWifi",
|
||||||
"label": "WiFi",
|
"label": "WiFi",
|
||||||
|
|||||||
Reference in New Issue
Block a user