1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

net: allow overriding wifi device

This commit is contained in:
bbedward
2025-11-24 21:27:18 -05:00
parent 5288d042ca
commit df940124b1
18 changed files with 1587 additions and 948 deletions

View File

@@ -28,7 +28,7 @@ packages:
outpkg: mocks_brightness outpkg: mocks_brightness
interfaces: interfaces:
DBusConn: DBusConn:
github.com/AvengeMedia/danklinux/internal/server/network: github.com/AvengeMedia/DankMaterialShell/core/internal/server/network:
config: config:
dir: "internal/mocks/network" dir: "internal/mocks/network"
outpkg: mocks_network outpkg: mocks_network

View File

@@ -509,6 +509,52 @@ func (_c *MockBackend_DisconnectWiFi_Call) RunAndReturn(run func() error) *MockB
return _c return _c
} }
// DisconnectWiFiDevice provides a mock function with given fields: device
func (_m *MockBackend) DisconnectWiFiDevice(device string) error {
ret := _m.Called(device)
if len(ret) == 0 {
panic("no return value specified for DisconnectWiFiDevice")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(device)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockBackend_DisconnectWiFiDevice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DisconnectWiFiDevice'
type MockBackend_DisconnectWiFiDevice_Call struct {
*mock.Call
}
// DisconnectWiFiDevice is a helper method to define mock.On call
// - device string
func (_e *MockBackend_Expecter) DisconnectWiFiDevice(device interface{}) *MockBackend_DisconnectWiFiDevice_Call {
return &MockBackend_DisconnectWiFiDevice_Call{Call: _e.mock.On("DisconnectWiFiDevice", device)}
}
func (_c *MockBackend_DisconnectWiFiDevice_Call) Run(run func(device string)) *MockBackend_DisconnectWiFiDevice_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockBackend_DisconnectWiFiDevice_Call) Return(_a0 error) *MockBackend_DisconnectWiFiDevice_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockBackend_DisconnectWiFiDevice_Call) RunAndReturn(run func(string) error) *MockBackend_DisconnectWiFiDevice_Call {
_c.Call.Return(run)
return _c
}
// ForgetWiFiNetwork provides a mock function with given fields: ssid // ForgetWiFiNetwork provides a mock function with given fields: ssid
func (_m *MockBackend) ForgetWiFiNetwork(ssid string) error { func (_m *MockBackend) ForgetWiFiNetwork(ssid string) error {
ret := _m.Called(ssid) ret := _m.Called(ssid)
@@ -659,6 +705,53 @@ func (_c *MockBackend_GetPromptBroker_Call) RunAndReturn(run func() network.Prom
return _c return _c
} }
// GetWiFiDevices provides a mock function with no fields
func (_m *MockBackend) GetWiFiDevices() []network.WiFiDevice {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for GetWiFiDevices")
}
var r0 []network.WiFiDevice
if rf, ok := ret.Get(0).(func() []network.WiFiDevice); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]network.WiFiDevice)
}
}
return r0
}
// MockBackend_GetWiFiDevices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetWiFiDevices'
type MockBackend_GetWiFiDevices_Call struct {
*mock.Call
}
// GetWiFiDevices is a helper method to define mock.On call
func (_e *MockBackend_Expecter) GetWiFiDevices() *MockBackend_GetWiFiDevices_Call {
return &MockBackend_GetWiFiDevices_Call{Call: _e.mock.On("GetWiFiDevices")}
}
func (_c *MockBackend_GetWiFiDevices_Call) Run(run func()) *MockBackend_GetWiFiDevices_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockBackend_GetWiFiDevices_Call) Return(_a0 []network.WiFiDevice) *MockBackend_GetWiFiDevices_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockBackend_GetWiFiDevices_Call) RunAndReturn(run func() []network.WiFiDevice) *MockBackend_GetWiFiDevices_Call {
_c.Call.Return(run)
return _c
}
// GetWiFiEnabled provides a mock function with no fields // GetWiFiEnabled provides a mock function with no fields
func (_m *MockBackend) GetWiFiEnabled() (bool, error) { func (_m *MockBackend) GetWiFiEnabled() (bool, error) {
ret := _m.Called() ret := _m.Called()
@@ -1091,6 +1184,52 @@ func (_c *MockBackend_ScanWiFi_Call) RunAndReturn(run func() error) *MockBackend
return _c return _c
} }
// ScanWiFiDevice provides a mock function with given fields: device
func (_m *MockBackend) ScanWiFiDevice(device string) error {
ret := _m.Called(device)
if len(ret) == 0 {
panic("no return value specified for ScanWiFiDevice")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(device)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockBackend_ScanWiFiDevice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ScanWiFiDevice'
type MockBackend_ScanWiFiDevice_Call struct {
*mock.Call
}
// ScanWiFiDevice is a helper method to define mock.On call
// - device string
func (_e *MockBackend_Expecter) ScanWiFiDevice(device interface{}) *MockBackend_ScanWiFiDevice_Call {
return &MockBackend_ScanWiFiDevice_Call{Call: _e.mock.On("ScanWiFiDevice", device)}
}
func (_c *MockBackend_ScanWiFiDevice_Call) Run(run func(device string)) *MockBackend_ScanWiFiDevice_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *MockBackend_ScanWiFiDevice_Call) Return(_a0 error) *MockBackend_ScanWiFiDevice_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockBackend_ScanWiFiDevice_Call) RunAndReturn(run func(string) error) *MockBackend_ScanWiFiDevice_Call {
_c.Call.Return(run)
return _c
}
// SetPromptBroker provides a mock function with given fields: broker // SetPromptBroker provides a mock function with given fields: broker
func (_m *MockBackend) SetPromptBroker(broker network.PromptBroker) error { func (_m *MockBackend) SetPromptBroker(broker network.PromptBroker) error {
ret := _m.Called(broker) ret := _m.Called(broker)

View File

@@ -8,10 +8,13 @@ type Backend interface {
SetWiFiEnabled(enabled bool) error SetWiFiEnabled(enabled bool) error
ScanWiFi() error ScanWiFi() error
ScanWiFiDevice(device string) error
GetWiFiNetworkDetails(ssid string) (*NetworkInfoResponse, error) GetWiFiNetworkDetails(ssid string) (*NetworkInfoResponse, error)
GetWiFiDevices() []WiFiDevice
ConnectWiFi(req ConnectionRequest) error ConnectWiFi(req ConnectionRequest) error
DisconnectWiFi() error DisconnectWiFi() error
DisconnectWiFiDevice(device string) error
ForgetWiFiNetwork(ssid string) error ForgetWiFiNetwork(ssid string) error
SetWiFiAutoconnect(ssid string, autoconnect bool) error SetWiFiAutoconnect(ssid string, autoconnect bool) error
@@ -54,11 +57,13 @@ type BackendState struct {
WiFiBSSID string WiFiBSSID string
WiFiSignal uint8 WiFiSignal uint8
WiFiNetworks []WiFiNetwork WiFiNetworks []WiFiNetwork
WiFiDevices []WiFiDevice
WiredConnections []WiredConnection WiredConnections []WiredConnection
VPNProfiles []VPNProfile VPNProfiles []VPNProfile
VPNActive []VPNActive VPNActive []VPNActive
IsConnecting bool IsConnecting bool
ConnectingSSID string ConnectingSSID string
ConnectingDevice string
IsConnectingVPN bool IsConnectingVPN bool
ConnectingVPNUUID string ConnectingVPNUUID string
LastError string LastError string

View File

@@ -196,3 +196,15 @@ func (b *HybridIwdNetworkdBackend) CancelCredentials(token string) error {
func (b *HybridIwdNetworkdBackend) SetWiFiAutoconnect(ssid string, autoconnect bool) error { func (b *HybridIwdNetworkdBackend) SetWiFiAutoconnect(ssid string, autoconnect bool) error {
return b.wifi.SetWiFiAutoconnect(ssid, autoconnect) return b.wifi.SetWiFiAutoconnect(ssid, autoconnect)
} }
func (b *HybridIwdNetworkdBackend) ScanWiFiDevice(device string) error {
return b.wifi.ScanWiFiDevice(device)
}
func (b *HybridIwdNetworkdBackend) DisconnectWiFiDevice(device string) error {
return b.wifi.DisconnectWiFiDevice(device)
}
func (b *HybridIwdNetworkdBackend) GetWiFiDevices() []WiFiDevice {
return b.wifi.GetWiFiDevices()
}

View File

@@ -139,9 +139,13 @@ func (b *IWDBackend) discoverDevices() error {
} }
func (b *IWDBackend) GetCurrentState() (*BackendState, error) { func (b *IWDBackend) GetCurrentState() (*BackendState, error) {
b.stateMutex.RLock()
defer b.stateMutex.RUnlock()
state := *b.state state := *b.state
state.WiFiNetworks = append([]WiFiNetwork(nil), b.state.WiFiNetworks...) state.WiFiNetworks = append([]WiFiNetwork(nil), b.state.WiFiNetworks...)
state.WiredConnections = append([]WiredConnection(nil), b.state.WiredConnections...) state.WiredConnections = append([]WiredConnection(nil), b.state.WiredConnections...)
state.WiFiDevices = b.getWiFiDevicesLocked()
return &state, nil return &state, nil
} }

View File

@@ -45,3 +45,38 @@ func (b *IWDBackend) DisconnectAllVPN() error {
func (b *IWDBackend) ClearVPNCredentials(uuidOrName string) error { func (b *IWDBackend) ClearVPNCredentials(uuidOrName string) error {
return fmt.Errorf("VPN not supported by iwd backend") return fmt.Errorf("VPN not supported by iwd backend")
} }
func (b *IWDBackend) ScanWiFiDevice(device string) error {
return b.ScanWiFi()
}
func (b *IWDBackend) DisconnectWiFiDevice(device string) error {
return b.DisconnectWiFi()
}
func (b *IWDBackend) GetWiFiDevices() []WiFiDevice {
b.stateMutex.RLock()
defer b.stateMutex.RUnlock()
return b.getWiFiDevicesLocked()
}
func (b *IWDBackend) getWiFiDevicesLocked() []WiFiDevice {
if b.state.WiFiDevice == "" {
return nil
}
stateStr := "disconnected"
if b.state.WiFiConnected {
stateStr = "connected"
}
return []WiFiDevice{{
Name: b.state.WiFiDevice,
State: stateStr,
Connected: b.state.WiFiConnected,
SSID: b.state.WiFiSSID,
Signal: b.state.WiFiSignal,
IP: b.state.WiFiIP,
Networks: b.state.WiFiNetworks,
}}
}

View File

@@ -57,3 +57,15 @@ func (b *SystemdNetworkdBackend) ClearVPNCredentials(uuidOrName string) error {
func (b *SystemdNetworkdBackend) SetWiFiAutoconnect(ssid string, autoconnect bool) error { func (b *SystemdNetworkdBackend) SetWiFiAutoconnect(ssid string, autoconnect bool) error {
return fmt.Errorf("WiFi autoconnect not supported by networkd backend") return fmt.Errorf("WiFi autoconnect not supported by networkd backend")
} }
func (b *SystemdNetworkdBackend) ScanWiFiDevice(device string) error {
return fmt.Errorf("WiFi scan not supported by networkd backend")
}
func (b *SystemdNetworkdBackend) DisconnectWiFiDevice(device string) error {
return fmt.Errorf("WiFi disconnect not supported by networkd backend")
}
func (b *SystemdNetworkdBackend) GetWiFiDevices() []WiFiDevice {
return nil
}

View File

@@ -30,12 +30,20 @@ const (
NmDeviceStateReasonNewActivation = 60 NmDeviceStateReasonNewActivation = 60
) )
type wifiDeviceInfo struct {
device gonetworkmanager.Device
wireless gonetworkmanager.DeviceWireless
name string
hwAddress string
}
type NetworkManagerBackend struct { type NetworkManagerBackend struct {
nmConn interface{} nmConn interface{}
ethernetDevice interface{} ethernetDevice interface{}
wifiDevice interface{} wifiDevice interface{}
settings interface{} settings interface{}
wifiDev interface{} wifiDev interface{}
wifiDevices map[string]*wifiDeviceInfo
dbusConn *dbus.Conn dbusConn *dbus.Conn
signals chan *dbus.Signal signals chan *dbus.Signal
@@ -71,8 +79,9 @@ func NewNetworkManagerBackend(nmConn ...gonetworkmanager.NetworkManager) (*Netwo
} }
backend := &NetworkManagerBackend{ backend := &NetworkManagerBackend{
nmConn: nm, nmConn: nm,
stopChan: make(chan struct{}), stopChan: make(chan struct{}),
wifiDevices: make(map[string]*wifiDeviceInfo),
state: &BackendState{ state: &BackendState{
Backend: "networkmanager", Backend: "networkmanager",
}, },
@@ -114,27 +123,48 @@ func (b *NetworkManagerBackend) Initialize() error {
} }
case gonetworkmanager.NmDeviceTypeWifi: case gonetworkmanager.NmDeviceTypeWifi:
b.wifiDevice = dev iface, err := dev.GetPropertyInterface()
if w, err := gonetworkmanager.NewDeviceWireless(dev.GetPath()); err == nil { if err != nil {
b.wifiDev = w
}
wifiEnabled, err := nm.GetPropertyWirelessEnabled()
if err == nil {
b.stateMutex.Lock()
b.state.WiFiEnabled = wifiEnabled
b.stateMutex.Unlock()
}
if err := b.updateWiFiState(); err != nil {
continue continue
} }
if wifiEnabled { w, err := gonetworkmanager.NewDeviceWireless(dev.GetPath())
if _, err := b.updateWiFiNetworks(); err != nil { if err != nil {
log.Warnf("Failed to get initial networks: %v", err) continue
} }
hwAddr, _ := w.GetPropertyHwAddress()
b.wifiDevices[iface] = &wifiDeviceInfo{
device: dev,
wireless: w,
name: iface,
hwAddress: hwAddr,
}
if b.wifiDevice == nil {
b.wifiDevice = dev
b.wifiDev = w
} }
} }
} }
wifiEnabled, err := nm.GetPropertyWirelessEnabled()
if err == nil {
b.stateMutex.Lock()
b.state.WiFiEnabled = wifiEnabled
b.stateMutex.Unlock()
}
if err := b.updateWiFiState(); err != nil {
log.Warnf("Failed to update WiFi state: %v", err)
}
if wifiEnabled {
if _, err := b.updateWiFiNetworks(); err != nil {
log.Warnf("Failed to get initial networks: %v", err)
}
b.updateAllWiFiDevices()
}
if err := b.updatePrimaryConnection(); err != nil { if err := b.updatePrimaryConnection(); err != nil {
return err return err
} }
@@ -165,6 +195,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.WiFiDevices = append([]WiFiDevice(nil), b.state.WiFiDevices...)
state.WiredConnections = append([]WiredConnection(nil), b.state.WiredConnections...) state.WiredConnections = append([]WiredConnection(nil), b.state.WiredConnections...)
state.VPNProfiles = append([]VPNProfile(nil), b.state.VPNProfiles...) state.VPNProfiles = append([]VPNProfile(nil), b.state.VPNProfiles...)
state.VPNActive = append([]VPNActive(nil), b.state.VPNActive...) state.VPNActive = append([]VPNActive(nil), b.state.VPNActive...)

View File

@@ -197,21 +197,23 @@ func (b *NetworkManagerBackend) GetWiFiNetworkDetails(ssid string) (*NetworkInfo
} }
func (b *NetworkManagerBackend) ConnectWiFi(req ConnectionRequest) error { func (b *NetworkManagerBackend) ConnectWiFi(req ConnectionRequest) error {
if b.wifiDevice == nil { devInfo, err := b.getWifiDeviceForConnection(req.Device)
return fmt.Errorf("no WiFi device available") if err != nil {
return err
} }
b.stateMutex.RLock() b.stateMutex.RLock()
alreadyConnected := b.state.WiFiConnected && b.state.WiFiSSID == req.SSID alreadyConnected := b.state.WiFiConnected && b.state.WiFiSSID == req.SSID
b.stateMutex.RUnlock() b.stateMutex.RUnlock()
if alreadyConnected && !req.Interactive { if alreadyConnected && !req.Interactive && req.Device == "" {
return nil return nil
} }
b.stateMutex.Lock() b.stateMutex.Lock()
b.state.IsConnecting = true b.state.IsConnecting = true
b.state.ConnectingSSID = req.SSID b.state.ConnectingSSID = req.SSID
b.state.ConnectingDevice = req.Device
b.state.LastError = "" b.state.LastError = ""
b.stateMutex.Unlock() b.stateMutex.Unlock()
@@ -223,14 +225,13 @@ func (b *NetworkManagerBackend) ConnectWiFi(req ConnectionRequest) error {
existingConn, err := b.findConnection(req.SSID) existingConn, err := b.findConnection(req.SSID)
if err == nil && existingConn != nil { if err == nil && existingConn != nil {
dev := b.wifiDevice.(gonetworkmanager.Device) _, err := nm.ActivateConnection(existingConn, devInfo.device, nil)
_, err := nm.ActivateConnection(existingConn, dev, nil)
if err != nil { if err != nil {
log.Warnf("[ConnectWiFi] Failed to activate existing connection: %v", err) log.Warnf("[ConnectWiFi] Failed to activate existing connection: %v", err)
b.stateMutex.Lock() b.stateMutex.Lock()
b.state.IsConnecting = false b.state.IsConnecting = false
b.state.ConnectingSSID = "" b.state.ConnectingSSID = ""
b.state.ConnectingDevice = ""
b.state.LastError = fmt.Sprintf("failed to activate connection: %v", err) b.state.LastError = fmt.Sprintf("failed to activate connection: %v", err)
b.stateMutex.Unlock() b.stateMutex.Unlock()
if b.onStateChange != nil { if b.onStateChange != nil {
@@ -242,11 +243,12 @@ func (b *NetworkManagerBackend) ConnectWiFi(req ConnectionRequest) error {
return nil return nil
} }
if err := b.createAndConnectWiFi(req); err != nil { if err := b.createAndConnectWiFiOnDevice(req, devInfo); err != nil {
log.Warnf("[ConnectWiFi] Failed to create and connect: %v", err) log.Warnf("[ConnectWiFi] Failed to create and connect: %v", err)
b.stateMutex.Lock() b.stateMutex.Lock()
b.state.IsConnecting = false b.state.IsConnecting = false
b.state.ConnectingSSID = "" b.state.ConnectingSSID = ""
b.state.ConnectingDevice = ""
b.state.LastError = err.Error() b.state.LastError = err.Error()
b.stateMutex.Unlock() b.stateMutex.Unlock()
if b.onStateChange != nil { if b.onStateChange != nil {
@@ -502,19 +504,17 @@ func (b *NetworkManagerBackend) findConnection(ssid string) (gonetworkmanager.Co
} }
func (b *NetworkManagerBackend) createAndConnectWiFi(req ConnectionRequest) error { func (b *NetworkManagerBackend) createAndConnectWiFi(req ConnectionRequest) error {
if b.wifiDevice == nil { devInfo, err := b.getWifiDeviceForConnection(req.Device)
return fmt.Errorf("no WiFi device available") if err != nil {
}
nm := b.nmConn.(gonetworkmanager.NetworkManager)
dev := b.wifiDevice.(gonetworkmanager.Device)
if err := b.ensureWiFiDevice(); err != nil {
return err return err
} }
wifiDev := b.wifiDev return b.createAndConnectWiFiOnDevice(req, devInfo)
}
w := wifiDev.(gonetworkmanager.DeviceWireless) func (b *NetworkManagerBackend) createAndConnectWiFiOnDevice(req ConnectionRequest, devInfo *wifiDeviceInfo) error {
nm := b.nmConn.(gonetworkmanager.NetworkManager)
dev := devInfo.device
w := devInfo.wireless
apPaths, err := w.GetAccessPoints() apPaths, err := w.GetAccessPoints()
if err != nil { if err != nil {
return fmt.Errorf("failed to get access points: %w", err) return fmt.Errorf("failed to get access points: %w", err)
@@ -716,3 +716,254 @@ func (b *NetworkManagerBackend) SetWiFiAutoconnect(ssid string, autoconnect bool
return nil return nil
} }
func (b *NetworkManagerBackend) ScanWiFiDevice(device string) error {
devInfo, ok := b.wifiDevices[device]
if !ok {
return fmt.Errorf("WiFi device not found: %s", device)
}
b.stateMutex.RLock()
enabled := b.state.WiFiEnabled
b.stateMutex.RUnlock()
if !enabled {
return fmt.Errorf("WiFi is disabled")
}
if err := devInfo.wireless.RequestScan(); err != nil {
return fmt.Errorf("scan request failed: %w", err)
}
b.updateAllWiFiDevices()
return nil
}
func (b *NetworkManagerBackend) DisconnectWiFiDevice(device string) error {
devInfo, ok := b.wifiDevices[device]
if !ok {
return fmt.Errorf("WiFi device not found: %s", device)
}
if err := devInfo.device.Disconnect(); err != nil {
return fmt.Errorf("failed to disconnect: %w", err)
}
b.updateWiFiState()
b.updateAllWiFiDevices()
b.updatePrimaryConnection()
if b.onStateChange != nil {
b.onStateChange()
}
return nil
}
func (b *NetworkManagerBackend) GetWiFiDevices() []WiFiDevice {
b.stateMutex.RLock()
defer b.stateMutex.RUnlock()
return append([]WiFiDevice(nil), b.state.WiFiDevices...)
}
func (b *NetworkManagerBackend) updateAllWiFiDevices() {
s := b.settings
if s == nil {
var err error
s, err = gonetworkmanager.NewSettings()
if err != nil {
return
}
b.settings = s
}
settingsMgr := s.(gonetworkmanager.Settings)
connections, err := settingsMgr.ListConnections()
if err != nil {
return
}
savedSSIDs := make(map[string]bool)
autoconnectMap := 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
}
var devices []WiFiDevice
for name, devInfo := range b.wifiDevices {
state, _ := devInfo.device.GetPropertyState()
connected := state == gonetworkmanager.NmDeviceStateActivated
var ssid, bssid, ip string
var signal uint8
if connected {
if activeAP, err := devInfo.wireless.GetPropertyActiveAccessPoint(); err == nil && activeAP != nil && activeAP.GetPath() != "/" {
ssid, _ = activeAP.GetPropertySSID()
signal, _ = activeAP.GetPropertyStrength()
bssid, _ = activeAP.GetPropertyHWAddress()
}
ip = b.getDeviceIP(devInfo.device)
}
stateStr := "disconnected"
switch state {
case gonetworkmanager.NmDeviceStateActivated:
stateStr = "connected"
case gonetworkmanager.NmDeviceStateConfig, gonetworkmanager.NmDeviceStateIpConfig:
stateStr = "connecting"
case gonetworkmanager.NmDeviceStatePrepare:
stateStr = "preparing"
case gonetworkmanager.NmDeviceStateDeactivating:
stateStr = "disconnecting"
}
apPaths, err := devInfo.wireless.GetAccessPoints()
var networks []WiFiNetwork
if err == nil {
seenSSIDs := make(map[string]*WiFiNetwork)
for _, ap := range apPaths {
apSSID, err := ap.GetPropertySSID()
if err != nil || apSSID == "" {
continue
}
if existing, exists := seenSSIDs[apSSID]; exists {
strength, _ := ap.GetPropertyStrength()
if strength > existing.Signal {
existing.Signal = strength
freq, _ := ap.GetPropertyFrequency()
existing.Frequency = freq
apBSSID, _ := ap.GetPropertyHWAddress()
existing.BSSID = apBSSID
}
continue
}
strength, _ := ap.GetPropertyStrength()
flags, _ := ap.GetPropertyFlags()
wpaFlags, _ := ap.GetPropertyWPAFlags()
rsnFlags, _ := ap.GetPropertyRSNFlags()
freq, _ := ap.GetPropertyFrequency()
maxBitrate, _ := ap.GetPropertyMaxBitrate()
apBSSID, _ := ap.GetPropertyHWAddress()
mode, _ := ap.GetPropertyMode()
secured := flags != uint32(gonetworkmanager.Nm80211APFlagsNone) ||
wpaFlags != uint32(gonetworkmanager.Nm80211APSecNone) ||
rsnFlags != uint32(gonetworkmanager.Nm80211APSecNone)
enterprise := (rsnFlags&uint32(gonetworkmanager.Nm80211APSecKeyMgmt8021X) != 0) ||
(wpaFlags&uint32(gonetworkmanager.Nm80211APSecKeyMgmt8021X) != 0)
var modeStr string
switch mode {
case gonetworkmanager.Nm80211ModeAdhoc:
modeStr = "adhoc"
case gonetworkmanager.Nm80211ModeInfra:
modeStr = "infrastructure"
case gonetworkmanager.Nm80211ModeAp:
modeStr = "ap"
default:
modeStr = "unknown"
}
channel := frequencyToChannel(freq)
network := WiFiNetwork{
SSID: apSSID,
BSSID: apBSSID,
Signal: strength,
Secured: secured,
Enterprise: enterprise,
Connected: connected && apSSID == ssid,
Saved: savedSSIDs[apSSID],
Autoconnect: autoconnectMap[apSSID],
Frequency: freq,
Mode: modeStr,
Rate: maxBitrate / 1000,
Channel: channel,
Device: name,
}
seenSSIDs[apSSID] = &network
networks = append(networks, network)
}
sortWiFiNetworks(networks)
}
devices = append(devices, WiFiDevice{
Name: name,
HwAddress: devInfo.hwAddress,
State: stateStr,
Connected: connected,
SSID: ssid,
BSSID: bssid,
Signal: signal,
IP: ip,
Networks: networks,
})
}
sort.Slice(devices, func(i, j int) bool {
return devices[i].Name < devices[j].Name
})
b.stateMutex.Lock()
b.state.WiFiDevices = devices
b.stateMutex.Unlock()
}
func (b *NetworkManagerBackend) getWifiDeviceForConnection(deviceName string) (*wifiDeviceInfo, error) {
if deviceName != "" {
devInfo, ok := b.wifiDevices[deviceName]
if !ok {
return nil, fmt.Errorf("WiFi device not found: %s", deviceName)
}
return devInfo, nil
}
if b.wifiDevice == nil {
return nil, fmt.Errorf("no WiFi device available")
}
dev := b.wifiDevice.(gonetworkmanager.Device)
iface, _ := dev.GetPropertyInterface()
if devInfo, ok := b.wifiDevices[iface]; ok {
return devInfo, nil
}
return nil, fmt.Errorf("no WiFi device available")
}

View File

@@ -101,10 +101,21 @@ func TestNetworkManagerBackend_ConnectWiFi_AlreadyConnected(t *testing.T) {
backend.wifiDevice = mockDeviceWireless backend.wifiDevice = mockDeviceWireless
backend.wifiDev = mockDeviceWireless backend.wifiDev = mockDeviceWireless
backend.wifiDevices = map[string]*wifiDeviceInfo{
"wlan0": {
device: nil,
wireless: mockDeviceWireless,
name: "wlan0",
hwAddress: "00:11:22:33:44:55",
},
}
mockDeviceWireless.EXPECT().GetPropertyInterface().Return("wlan0", nil)
backend.stateMutex.Lock() backend.stateMutex.Lock()
backend.state.WiFiConnected = true backend.state.WiFiConnected = true
backend.state.WiFiSSID = "TestNetwork" backend.state.WiFiSSID = "TestNetwork"
backend.state.WiFiDevice = "wlan0"
backend.stateMutex.Unlock() backend.stateMutex.Unlock()
req := ConnectionRequest{SSID: "TestNetwork", Password: "password"} req := ConnectionRequest{SSID: "TestNetwork", Password: "password"}

View File

@@ -135,7 +135,14 @@ func handleGetState(conn net.Conn, req Request, manager *Manager) {
} }
func handleScanWiFi(conn net.Conn, req Request, manager *Manager) { func handleScanWiFi(conn net.Conn, req Request, manager *Manager) {
if err := manager.ScanWiFi(); err != nil { device, _ := req.Params["device"].(string)
var err error
if device != "" {
err = manager.ScanWiFiDevice(device)
} else {
err = manager.ScanWiFi()
}
if err != nil {
models.RespondError(conn, req.ID, err.Error()) models.RespondError(conn, req.ID, err.Error())
return return
} }
@@ -163,6 +170,9 @@ func handleConnectWiFi(conn net.Conn, req Request, manager *Manager) {
if username, ok := req.Params["username"].(string); ok { if username, ok := req.Params["username"].(string); ok {
connReq.Username = username connReq.Username = username
} }
if device, ok := req.Params["device"].(string); ok {
connReq.Device = device
}
if interactive, ok := req.Params["interactive"].(bool); ok { if interactive, ok := req.Params["interactive"].(bool); ok {
connReq.Interactive = interactive connReq.Interactive = interactive
@@ -170,7 +180,7 @@ func handleConnectWiFi(conn net.Conn, req Request, manager *Manager) {
state := manager.GetState() state := manager.GetState()
alreadyConnected := state.WiFiConnected && state.WiFiSSID == ssid alreadyConnected := state.WiFiConnected && state.WiFiSSID == ssid
if alreadyConnected { if alreadyConnected && connReq.Device == "" {
connReq.Interactive = false connReq.Interactive = false
} else { } else {
networkInfo, err := manager.GetNetworkInfo(ssid) networkInfo, err := manager.GetNetworkInfo(ssid)
@@ -200,7 +210,14 @@ func handleConnectWiFi(conn net.Conn, req Request, manager *Manager) {
} }
func handleDisconnectWiFi(conn net.Conn, req Request, manager *Manager) { func handleDisconnectWiFi(conn net.Conn, req Request, manager *Manager) {
if err := manager.DisconnectWiFi(); err != nil { device, _ := req.Params["device"].(string)
var err error
if device != "" {
err = manager.DisconnectWiFiDevice(device)
} else {
err = manager.DisconnectWiFi()
}
if err != nil {
models.RespondError(conn, req.ID, err.Error()) models.RespondError(conn, req.ID, err.Error())
return return
} }

View File

@@ -117,11 +117,13 @@ 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.WiFiDevices = backendState.WiFiDevices
m.state.WiredConnections = backendState.WiredConnections m.state.WiredConnections = backendState.WiredConnections
m.state.VPNProfiles = backendState.VPNProfiles m.state.VPNProfiles = backendState.VPNProfiles
m.state.VPNActive = backendState.VPNActive m.state.VPNActive = backendState.VPNActive
m.state.IsConnecting = backendState.IsConnecting m.state.IsConnecting = backendState.IsConnecting
m.state.ConnectingSSID = backendState.ConnectingSSID m.state.ConnectingSSID = backendState.ConnectingSSID
m.state.ConnectingDevice = backendState.ConnectingDevice
m.state.LastError = backendState.LastError m.state.LastError = backendState.LastError
m.stateMutex.Unlock() m.stateMutex.Unlock()
@@ -151,6 +153,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.WiFiDevices = append([]WiFiDevice(nil), m.state.WiFiDevices...)
s.WiredConnections = append([]WiredConnection(nil), m.state.WiredConnections...) s.WiredConnections = append([]WiredConnection(nil), m.state.WiredConnections...)
s.VPNProfiles = append([]VPNProfile(nil), m.state.VPNProfiles...) s.VPNProfiles = append([]VPNProfile(nil), m.state.VPNProfiles...)
s.VPNActive = append([]VPNActive(nil), m.state.VPNActive...) s.VPNActive = append([]VPNActive(nil), m.state.VPNActive...)
@@ -204,6 +207,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.WiFiDevices) != len(new.WiFiDevices) {
return true
}
if len(old.WiredConnections) != len(new.WiredConnections) { if len(old.WiredConnections) != len(new.WiredConnections) {
return true return true
} }
@@ -505,3 +511,19 @@ func (m *Manager) ClearVPNCredentials(uuidOrName string) error {
func (m *Manager) SetWiFiAutoconnect(ssid string, autoconnect bool) error { func (m *Manager) SetWiFiAutoconnect(ssid string, autoconnect bool) error {
return m.backend.SetWiFiAutoconnect(ssid, autoconnect) return m.backend.SetWiFiAutoconnect(ssid, autoconnect)
} }
func (m *Manager) GetWiFiDevices() []WiFiDevice {
m.stateMutex.RLock()
defer m.stateMutex.RUnlock()
devices := make([]WiFiDevice, len(m.state.WiFiDevices))
copy(devices, m.state.WiFiDevices)
return devices
}
func (m *Manager) ScanWiFiDevice(device string) error {
return m.backend.ScanWiFiDevice(device)
}
func (m *Manager) DisconnectWiFiDevice(device string) error {
return m.backend.DisconnectWiFiDevice(device)
}

View File

@@ -37,6 +37,19 @@ type WiFiNetwork struct {
Mode string `json:"mode"` Mode string `json:"mode"`
Rate uint32 `json:"rate"` Rate uint32 `json:"rate"`
Channel uint32 `json:"channel"` Channel uint32 `json:"channel"`
Device string `json:"device,omitempty"`
}
type WiFiDevice struct {
Name string `json:"name"`
HwAddress string `json:"hwAddress"`
State string `json:"state"`
Connected bool `json:"connected"`
SSID string `json:"ssid,omitempty"`
BSSID string `json:"bssid,omitempty"`
Signal uint8 `json:"signal,omitempty"`
IP string `json:"ip,omitempty"`
Networks []WiFiNetwork `json:"networks"`
} }
type VPNProfile struct { type VPNProfile struct {
@@ -76,11 +89,13 @@ 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"`
WiFiDevices []WiFiDevice `json:"wifiDevices"`
WiredConnections []WiredConnection `json:"wiredConnections"` WiredConnections []WiredConnection `json:"wiredConnections"`
VPNProfiles []VPNProfile `json:"vpnProfiles"` VPNProfiles []VPNProfile `json:"vpnProfiles"`
VPNActive []VPNActive `json:"vpnActive"` VPNActive []VPNActive `json:"vpnActive"`
IsConnecting bool `json:"isConnecting"` IsConnecting bool `json:"isConnecting"`
ConnectingSSID string `json:"connectingSSID"` ConnectingSSID string `json:"connectingSSID"`
ConnectingDevice string `json:"connectingDevice,omitempty"`
LastError string `json:"lastError"` LastError string `json:"lastError"`
} }
@@ -91,6 +106,7 @@ type ConnectionRequest struct {
AnonymousIdentity string `json:"anonymousIdentity,omitempty"` AnonymousIdentity string `json:"anonymousIdentity,omitempty"`
DomainSuffixMatch string `json:"domainSuffixMatch,omitempty"` DomainSuffixMatch string `json:"domainSuffixMatch,omitempty"`
Interactive bool `json:"interactive,omitempty"` Interactive bool `json:"interactive,omitempty"`
Device string `json:"device,omitempty"`
} }
type WiredConnection struct { type WiredConnection struct {

View File

@@ -31,7 +31,7 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
) )
const APIVersion = 19 const APIVersion = 20
type Capabilities struct { type Capabilities struct {
Capabilities []string `json:"capabilities"` Capabilities []string `json:"capabilities"`
@@ -1071,10 +1071,10 @@ func Start(printDocs bool) error {
log.Info(" plugins.search - Search plugins (params: query, category?, compositor?, capability?)") log.Info(" plugins.search - Search plugins (params: query, category?, compositor?, capability?)")
log.Info("Network:") log.Info("Network:")
log.Info(" network.getState - Get current network state") log.Info(" network.getState - Get current network state")
log.Info(" network.wifi.scan - Scan for WiFi networks") log.Info(" network.wifi.scan - Scan for WiFi networks (params: device?)")
log.Info(" network.wifi.networks - Get WiFi network list") log.Info(" network.wifi.networks - Get WiFi network list")
log.Info(" network.wifi.connect - Connect to WiFi (params: ssid, password?, username?)") log.Info(" network.wifi.connect - Connect to WiFi (params: ssid, password?, username?, device?)")
log.Info(" network.wifi.disconnect - Disconnect WiFi") log.Info(" network.wifi.disconnect - Disconnect WiFi (params: device?)")
log.Info(" network.wifi.forget - Forget network (params: ssid)") log.Info(" network.wifi.forget - Forget network (params: ssid)")
log.Info(" network.wifi.toggle - Toggle WiFi radio") log.Info(" network.wifi.toggle - Toggle WiFi radio")
log.Info(" network.wifi.enable - Enable WiFi") log.Info(" network.wifi.enable - Enable WiFi")

File diff suppressed because it is too large Load Diff

View File

@@ -11,15 +11,15 @@ Rectangle {
implicitHeight: { implicitHeight: {
if (height > 0) { if (height > 0) {
return height return height;
} }
if (NetworkService.wifiToggling) { if (NetworkService.wifiToggling) {
return headerRow.height + wifiToggleContent.height + Theme.spacingM return headerRow.height + wifiToggleContent.height + Theme.spacingM;
} }
if (NetworkService.wifiEnabled) { if (NetworkService.wifiEnabled) {
return headerRow.height + wifiContent.height + Theme.spacingM return headerRow.height + wifiContent.height + Theme.spacingM;
} }
return headerRow.height + wifiOffContent.height + Theme.spacingM return headerRow.height + wifiOffContent.height + Theme.spacingM;
} }
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
@@ -27,35 +27,35 @@ Rectangle {
border.width: 0 border.width: 0
Component.onCompleted: { Component.onCompleted: {
NetworkService.addRef() NetworkService.addRef();
} }
Component.onDestruction: { Component.onDestruction: {
NetworkService.removeRef() NetworkService.removeRef();
} }
property int currentPreferenceIndex: { property int currentPreferenceIndex: {
if (DMSService.apiVersion < 5) { if (DMSService.apiVersion < 5) {
return 1 return 1;
} }
if (NetworkService.backend !== "networkmanager" || DMSService.apiVersion <= 10) { if (NetworkService.backend !== "networkmanager" || DMSService.apiVersion <= 10) {
return 1 return 1;
} }
const pref = NetworkService.userPreference const pref = NetworkService.userPreference;
const status = NetworkService.networkStatus const status = NetworkService.networkStatus;
let index = 1 let index = 1;
if (pref === "ethernet") { if (pref === "ethernet") {
index = 0 index = 0;
} else if (pref === "wifi") { } else if (pref === "wifi") {
index = 1 index = 1;
} else { } else {
index = status === "ethernet" ? 0 : 1 index = status === "ethernet" ? 0 : 1;
} }
return index return index;
} }
Row { Row {
@@ -78,28 +78,56 @@ Rectangle {
} }
Item { Item {
width: Math.max(0, parent.width - headerText.implicitWidth - preferenceControls.width - Theme.spacingM) height: 1
height: parent.height width: parent.width - headerText.width - rightControls.width
} }
DankButtonGroup { Row {
id: preferenceControls id: rightControls
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: NetworkService.backend === "networkmanager" && DMSService.apiVersion > 10 spacing: Theme.spacingS
model: ["Ethernet", "WiFi"] DankDropdown {
currentIndex: currentPreferenceIndex id: wifiDeviceDropdown
selectionMode: "single" anchors.verticalCenter: parent.verticalCenter
onSelectionChanged: (index, selected) => { visible: currentPreferenceIndex === 1 && (NetworkService.wifiDevices?.length ?? 0) > 1
if (!selected) return compactMode: true
console.log("NetworkDetail: Setting preference to", index === 0 ? "ethernet" : "wifi") dropdownWidth: 120
NetworkService.setNetworkPreference(index === 0 ? "ethernet" : "wifi") popupWidth: 160
alignPopupRight: true
options: {
const devices = NetworkService.wifiDevices;
if (!devices || devices.length === 0)
return [I18n.tr("Auto")];
return [I18n.tr("Auto")].concat(devices.map(d => d.name));
}
currentValue: NetworkService.wifiDeviceOverride || I18n.tr("Auto")
onValueChanged: value => {
const deviceName = value === I18n.tr("Auto") ? "" : value;
NetworkService.setWifiDeviceOverride(deviceName);
}
}
DankButtonGroup {
id: preferenceControls
anchors.verticalCenter: parent.verticalCenter
visible: NetworkService.backend === "networkmanager" && DMSService.apiVersion > 10
model: ["Ethernet", "WiFi"]
currentIndex: currentPreferenceIndex
selectionMode: "single"
onSelectionChanged: (index, selected) => {
if (!selected)
return;
NetworkService.setNetworkPreference(index === 0 ? "ethernet" : "wifi");
}
} }
} }
} }
Item { Item {
id: wifiToggleContent id: wifiToggleContent
anchors.top: headerRow.bottom anchors.top: headerRow.bottom
@@ -194,7 +222,6 @@ Rectangle {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: NetworkService.toggleWifiRadio() onClicked: NetworkService.toggleWifiRadio()
} }
} }
} }
} }
@@ -219,15 +246,17 @@ Rectangle {
Repeater { Repeater {
model: ScriptModel { model: ScriptModel {
values: { values: {
const currentUuid = NetworkService.ethernetConnectionUuid const currentUuid = NetworkService.ethernetConnectionUuid;
const networks = NetworkService.wiredConnections const networks = NetworkService.wiredConnections;
let sorted = [...networks] let sorted = [...networks];
sorted.sort((a, b) => { sorted.sort((a, b) => {
if (a.isActive && !b.isActive) return -1 if (a.isActive && !b.isActive)
if (!a.isActive && b.isActive) return 1 return -1;
return a.id.localeCompare(b.id) if (!a.isActive && b.isActive)
}) return 1;
return sorted return a.id.localeCompare(b.id);
});
return sorted;
} }
} }
@@ -279,12 +308,12 @@ Rectangle {
buttonSize: 28 buttonSize: 28
onClicked: { onClicked: {
if (wiredNetworkContextMenu.visible) { if (wiredNetworkContextMenu.visible) {
wiredNetworkContextMenu.close() wiredNetworkContextMenu.close();
} else { } else {
wiredNetworkContextMenu.currentID = modelData.id wiredNetworkContextMenu.currentID = modelData.id;
wiredNetworkContextMenu.currentUUID = modelData.uuid wiredNetworkContextMenu.currentUUID = modelData.uuid;
wiredNetworkContextMenu.currentConnected = modelData.isActive wiredNetworkContextMenu.currentConnected = modelData.isActive;
wiredNetworkContextMenu.popup(wiredOptionsButton, -wiredNetworkContextMenu.width + wiredOptionsButton.width, wiredOptionsButton.height + Theme.spacingXS) wiredNetworkContextMenu.popup(wiredOptionsButton, -wiredNetworkContextMenu.width + wiredOptionsButton.width, wiredOptionsButton.height + Theme.spacingXS);
} }
} }
} }
@@ -295,14 +324,13 @@ Rectangle {
anchors.rightMargin: wiredOptionsButton.width + Theme.spacingS anchors.rightMargin: wiredOptionsButton.width + Theme.spacingS
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: function(event) { onClicked: function (event) {
if (modelData.uuid !== NetworkService.ethernetConnectionUuid) { if (modelData.uuid !== NetworkService.ethernetConnectionUuid) {
NetworkService.connectToSpecificWiredConfig(modelData.uuid) NetworkService.connectToSpecificWiredConfig(modelData.uuid);
} }
event.accepted = true event.accepted = true;
} }
} }
} }
} }
} }
@@ -343,7 +371,7 @@ Rectangle {
onTriggered: { onTriggered: {
if (!networkContextMenu.currentConnected) { if (!networkContextMenu.currentConnected) {
NetworkService.connectToSpecificWiredConfig(wiredNetworkContextMenu.currentUUID) NetworkService.connectToSpecificWiredConfig(wiredNetworkContextMenu.currentUUID);
} }
} }
} }
@@ -366,8 +394,8 @@ Rectangle {
} }
onTriggered: { onTriggered: {
let networkData = NetworkService.getWiredNetworkInfo(wiredNetworkContextMenu.currentUUID) let networkData = NetworkService.getWiredNetworkInfo(wiredNetworkContextMenu.currentUUID);
networkWiredInfoModal.showNetworkInfo(wiredNetworkContextMenu.currentID, networkData) networkWiredInfoModal.showNetworkInfo(wiredNetworkContextMenu.currentID, networkData);
} }
} }
} }
@@ -416,26 +444,30 @@ Rectangle {
Repeater { Repeater {
model: ScriptModel { model: ScriptModel {
values: { values: {
const ssid = NetworkService.currentWifiSSID const ssid = NetworkService.currentWifiSSID;
const networks = NetworkService.wifiNetworks const networks = NetworkService.wifiNetworks;
const pins = SettingsData.wifiNetworkPins || {} const pins = SettingsData.wifiNetworkPins || {};
const pinnedSSID = pins["preferredWifi"] const pinnedSSID = pins["preferredWifi"];
let sorted = [...networks] let sorted = [...networks];
sorted.sort((a, b) => { sorted.sort((a, b) => {
// Pinned network first // Pinned network first
if (a.ssid === pinnedSSID && b.ssid !== pinnedSSID) return -1 if (a.ssid === pinnedSSID && b.ssid !== pinnedSSID)
if (b.ssid === pinnedSSID && a.ssid !== pinnedSSID) return 1 return -1;
if (b.ssid === pinnedSSID && a.ssid !== pinnedSSID)
return 1;
// Then currently connected // Then currently connected
if (a.ssid === ssid) return -1 if (a.ssid === ssid)
if (b.ssid === ssid) return 1 return -1;
if (b.ssid === ssid)
return 1;
// Then by signal strength // Then by signal strength
return b.signal - a.signal return b.signal - a.signal;
}) });
if (!wifiContent.menuOpen) { if (!wifiContent.menuOpen) {
wifiContent.frozenNetworks = sorted wifiContent.frozenNetworks = sorted;
} }
return wifiContent.menuOpen ? wifiContent.frozenNetworks : sorted return wifiContent.menuOpen ? wifiContent.frozenNetworks : sorted;
} }
} }
@@ -458,10 +490,12 @@ Rectangle {
DankIcon { DankIcon {
name: { name: {
let strength = modelData.signal || 0 let strength = modelData.signal || 0;
if (strength >= 50) return "wifi" if (strength >= 50)
if (strength >= 25) return "wifi_2_bar" return "wifi";
return "wifi_1_bar" if (strength >= 25)
return "wifi_2_bar";
return "wifi_1_bar";
} }
size: Theme.iconSize - 4 size: Theme.iconSize - 4
color: modelData.ssid === NetworkService.currentWifiSSID ? Theme.primary : Theme.surfaceText color: modelData.ssid === NetworkService.currentWifiSSID ? Theme.primary : Theme.surfaceText
@@ -515,16 +549,16 @@ Rectangle {
buttonSize: 28 buttonSize: 28
onClicked: { onClicked: {
if (networkContextMenu.visible) { if (networkContextMenu.visible) {
networkContextMenu.close() networkContextMenu.close();
} else { } else {
wifiContent.menuOpen = true wifiContent.menuOpen = true;
networkContextMenu.currentSSID = modelData.ssid networkContextMenu.currentSSID = modelData.ssid;
networkContextMenu.currentSecured = modelData.secured networkContextMenu.currentSecured = modelData.secured;
networkContextMenu.currentConnected = modelData.ssid === NetworkService.currentWifiSSID networkContextMenu.currentConnected = modelData.ssid === NetworkService.currentWifiSSID;
networkContextMenu.currentSaved = modelData.saved networkContextMenu.currentSaved = modelData.saved;
networkContextMenu.currentSignal = modelData.signal networkContextMenu.currentSignal = modelData.signal;
networkContextMenu.currentAutoconnect = modelData.autoconnect || false networkContextMenu.currentAutoconnect = modelData.autoconnect || false;
networkContextMenu.popup(optionsButton, -networkContextMenu.width + optionsButton.width, optionsButton.height + Theme.spacingXS) networkContextMenu.popup(optionsButton, -networkContextMenu.width + optionsButton.width, optionsButton.height + Theme.spacingXS);
} }
} }
} }
@@ -537,8 +571,8 @@ Rectangle {
height: 28 height: 28
radius: height / 2 radius: height / 2
color: { color: {
const isThisNetworkPinned = (SettingsData.wifiNetworkPins || {})["preferredWifi"] === modelData.ssid const isThisNetworkPinned = (SettingsData.wifiNetworkPins || {})["preferredWifi"] === modelData.ssid;
return isThisNetworkPinned ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.surfaceText, 0.05) return isThisNetworkPinned ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.surfaceText, 0.05);
} }
Row { Row {
@@ -550,21 +584,21 @@ Rectangle {
name: "push_pin" name: "push_pin"
size: 16 size: 16
color: { color: {
const isThisNetworkPinned = (SettingsData.wifiNetworkPins || {})["preferredWifi"] === modelData.ssid const isThisNetworkPinned = (SettingsData.wifiNetworkPins || {})["preferredWifi"] === modelData.ssid;
return isThisNetworkPinned ? Theme.primary : Theme.surfaceText return isThisNetworkPinned ? Theme.primary : Theme.surfaceText;
} }
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
StyledText { StyledText {
text: { text: {
const isThisNetworkPinned = (SettingsData.wifiNetworkPins || {})["preferredWifi"] === modelData.ssid const isThisNetworkPinned = (SettingsData.wifiNetworkPins || {})["preferredWifi"] === modelData.ssid;
return isThisNetworkPinned ? "Pinned" : "Pin" return isThisNetworkPinned ? "Pinned" : "Pin";
} }
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: { color: {
const isThisNetworkPinned = (SettingsData.wifiNetworkPins || {})["preferredWifi"] === modelData.ssid const isThisNetworkPinned = (SettingsData.wifiNetworkPins || {})["preferredWifi"] === modelData.ssid;
return isThisNetworkPinned ? Theme.primary : Theme.surfaceText return isThisNetworkPinned ? Theme.primary : Theme.surfaceText;
} }
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
@@ -574,16 +608,16 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
const pins = JSON.parse(JSON.stringify(SettingsData.wifiNetworkPins || {})) const pins = JSON.parse(JSON.stringify(SettingsData.wifiNetworkPins || {}));
const isCurrentlyPinned = pins["preferredWifi"] === modelData.ssid const isCurrentlyPinned = pins["preferredWifi"] === modelData.ssid;
if (isCurrentlyPinned) { if (isCurrentlyPinned) {
delete pins["preferredWifi"] delete pins["preferredWifi"];
} else { } else {
pins["preferredWifi"] = modelData.ssid pins["preferredWifi"] = modelData.ssid;
} }
SettingsData.set("wifiNetworkPins", pins) SettingsData.set("wifiNetworkPins", pins);
} }
} }
} }
@@ -594,22 +628,21 @@ Rectangle {
anchors.rightMargin: optionsButton.width + Theme.spacingM + Theme.spacingS + pinWifiRow.width + Theme.spacingS * 4 anchors.rightMargin: optionsButton.width + Theme.spacingM + Theme.spacingS + pinWifiRow.width + Theme.spacingS * 4
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: function(event) { onClicked: function (event) {
if (modelData.ssid !== NetworkService.currentWifiSSID) { if (modelData.ssid !== NetworkService.currentWifiSSID) {
if (modelData.secured && !modelData.saved) { if (modelData.secured && !modelData.saved) {
if (DMSService.apiVersion >= 7) { if (DMSService.apiVersion >= 7) {
NetworkService.connectToWifi(modelData.ssid) NetworkService.connectToWifi(modelData.ssid);
} else if (PopoutService.wifiPasswordModal) { } else if (PopoutService.wifiPasswordModal) {
PopoutService.wifiPasswordModal.show(modelData.ssid) PopoutService.wifiPasswordModal.show(modelData.ssid);
} }
} else { } else {
NetworkService.connectToWifi(modelData.ssid) NetworkService.connectToWifi(modelData.ssid);
} }
} }
event.accepted = true event.accepted = true;
} }
} }
} }
} }
} }
@@ -628,7 +661,7 @@ Rectangle {
property bool currentAutoconnect: false property bool currentAutoconnect: false
onClosed: { onClosed: {
wifiContent.menuOpen = false wifiContent.menuOpen = false;
} }
background: Rectangle { background: Rectangle {
@@ -657,16 +690,16 @@ Rectangle {
onTriggered: { onTriggered: {
if (networkContextMenu.currentConnected) { if (networkContextMenu.currentConnected) {
NetworkService.disconnectWifi() NetworkService.disconnectWifi();
} else { } else {
if (networkContextMenu.currentSecured && !networkContextMenu.currentSaved) { if (networkContextMenu.currentSecured && !networkContextMenu.currentSaved) {
if (DMSService.apiVersion >= 7) { if (DMSService.apiVersion >= 7) {
NetworkService.connectToWifi(networkContextMenu.currentSSID) NetworkService.connectToWifi(networkContextMenu.currentSSID);
} else if (PopoutService.wifiPasswordModal) { } else if (PopoutService.wifiPasswordModal) {
PopoutService.wifiPasswordModal.show(networkContextMenu.currentSSID) PopoutService.wifiPasswordModal.show(networkContextMenu.currentSSID);
} }
} else { } else {
NetworkService.connectToWifi(networkContextMenu.currentSSID) NetworkService.connectToWifi(networkContextMenu.currentSSID);
} }
} }
} }
@@ -690,8 +723,8 @@ Rectangle {
} }
onTriggered: { onTriggered: {
let networkData = NetworkService.getNetworkInfo(networkContextMenu.currentSSID) let networkData = NetworkService.getNetworkInfo(networkContextMenu.currentSSID);
networkInfoModal.showNetworkInfo(networkContextMenu.currentSSID, networkData) networkInfoModal.showNetworkInfo(networkContextMenu.currentSSID, networkData);
} }
} }
@@ -714,7 +747,7 @@ Rectangle {
} }
onTriggered: { onTriggered: {
NetworkService.setWifiAutoconnect(networkContextMenu.currentSSID, !networkContextMenu.currentAutoconnect) NetworkService.setWifiAutoconnect(networkContextMenu.currentSSID, !networkContextMenu.currentAutoconnect);
} }
} }
@@ -737,7 +770,7 @@ Rectangle {
} }
onTriggered: { onTriggered: {
NetworkService.forgetWifiNetwork(networkContextMenu.currentSSID) NetworkService.forgetWifiNetwork(networkContextMenu.currentSSID);
} }
} }
} }
@@ -749,4 +782,4 @@ Rectangle {
NetworkWiredInfoModal { NetworkWiredInfoModal {
id: networkWiredInfoModal id: networkWiredInfoModal
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -26,6 +26,9 @@ Singleton {
property string wifiConnectionUuid: activeService?.wifiConnectionUuid ?? "" property string wifiConnectionUuid: activeService?.wifiConnectionUuid ?? ""
property string wifiDevicePath: activeService?.wifiDevicePath ?? "" property string wifiDevicePath: activeService?.wifiDevicePath ?? ""
property string activeAccessPointPath: activeService?.activeAccessPointPath ?? "" property string activeAccessPointPath: activeService?.activeAccessPointPath ?? ""
property var wifiDevices: activeService?.wifiDevices ?? []
property string wifiDeviceOverride: activeService?.wifiDeviceOverride ?? ""
property string connectingDevice: activeService?.connectingDevice ?? ""
property string currentWifiSSID: activeService?.currentWifiSSID ?? "" property string currentWifiSSID: activeService?.currentWifiSSID ?? ""
property int wifiSignalStrength: activeService?.wifiSignalStrength ?? 0 property int wifiSignalStrength: activeService?.wifiSignalStrength ?? 0
@@ -294,4 +297,10 @@ Singleton {
activeService.setWifiAutoconnect(ssid, autoconnect); activeService.setWifiAutoconnect(ssid, autoconnect);
} }
} }
function setWifiDeviceOverride(deviceName) {
if (activeService && activeService.setWifiDeviceOverride) {
activeService.setWifiDeviceOverride(deviceName);
}
}
} }