From 72ce9953b6dc404711c23e7d2364af2b3affee6e Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Sun, 23 Nov 2025 13:08:29 +0100 Subject: [PATCH] BluetoothService: revert to old version --- Services/Networking/BluetoothService.qml | 518 +++++++++-------------- 1 file changed, 202 insertions(+), 316 deletions(-) diff --git a/Services/Networking/BluetoothService.qml b/Services/Networking/BluetoothService.qml index e6be5a45..761c26d9 100644 --- a/Services/Networking/BluetoothService.qml +++ b/Services/Networking/BluetoothService.qml @@ -10,249 +10,64 @@ import qs.Services.UI Singleton { id: root - // Properties - - readonly property BluetoothAdapter adapter: Bluetooth.defaultAdapter - readonly property int state: adapter?.state ?? 0 - readonly property bool available: adapter !== null - readonly property bool enabled: adapter?.enabled ?? false - readonly property bool blocked: adapter?.state === BluetoothAdapterState.Blocked - readonly property bool discovering: adapter?.discovering ?? false - readonly property var devices: adapter?.devices ?? null - - readonly property var pairedDevices: _filterDevices(dev => dev.paired || dev.trusted) - readonly property var connectedDevices: _filterDevices(dev => dev.connected) - readonly property var allDevicesWithBattery: _filterDevices(dev => dev.batteryAvailable && dev.battery > 0) - - // Internal state tracking property bool airplaneModeToggled: false property bool lastBluetoothBlocked: false - property var devicesBeingPaired: ({}) - property var connectionAttempts: ({}) + readonly property BluetoothAdapter adapter: Bluetooth.defaultAdapter + readonly property int state: adapter?.state ?? 0 + readonly property bool available: (adapter !== null) + readonly property bool enabled: adapter?.enabled ?? false + readonly property bool blocked: (adapter?.state === BluetoothAdapterState.Blocked) + readonly property bool discovering: (adapter && adapter.discovering) ?? false + readonly property var devices: adapter ? adapter.devices : null + readonly property var pairedDevices: { + if (!adapter || !adapter.devices) { + return []; + } + return adapter.devices.values.filter(dev => { + return dev && (dev.paired || dev.trusted); + }); + } + readonly property var connectedDevices: { + if (!adapter || !adapter.devices) { + return []; + } + return adapter.devices.values.filter(dev => dev && dev.connected); + } - // Init + readonly property var allDevicesWithBattery: { + if (!adapter || !adapter.devices) { + return []; + } + return adapter.devices.values.filter(dev => { + return dev && dev.batteryAvailable && dev.battery > 0; + }); + } function init() { Logger.i("Bluetooth", "Service started"); - _configureAdapter(); } - onAdapterChanged: _configureAdapter() - - // Device Actions - - function connectDeviceWithTrust(device) { - if (!device) - return; - - const deviceName = _getDeviceName(device); - if (!device.trusted) { - Logger.i("Bluetooth", "Setting device as trusted:", deviceName); - device.trusted = true; - } - - if (!device.paired) { - Logger.i("Bluetooth", "Pairing device before connection:", deviceName); - devicesBeingPaired[device.address] = true; - device.pair(); - } else { - Qt.callLater(() => { - if (device && !device.connected) { - Logger.i("Bluetooth", "Connecting to paired device:", deviceName); - device.connect(); - } - }); - } + Timer { + id: discoveryTimer + interval: 1000 + repeat: false + onTriggered: adapter.discovering = true } - function disconnectDevice(device) { - if (device) - device.disconnect(); - } - - function forgetDevice(device) { - if (!device) - return; - - Logger.i("Bluetooth", "Forgetting device:", _getDeviceName(device)); - _cleanupDeviceTracking(device.address); - device.trusted = false; - device.forget(); - } - - function forgetAndRepair(device) { - if (!device) - return; - - Logger.i("Bluetooth", "Force re-pairing device:", _getDeviceName(device)); - const deviceAddress = device.address; - - delete connectionAttempts[deviceAddress]; - device.trusted = false; - device.forget(); - - Qt.callLater(() => { - if (device) { - Logger.i("Bluetooth", "Starting fresh pairing for:", _getDeviceName(device)); - devicesBeingPaired[deviceAddress] = true; - device.trusted = true; - device.pair(); - } - }); - } - - function setBluetoothEnabled(state) { - if (!adapter) { - Logger.w("Bluetooth", "No adapter available"); - return; - } - Logger.i("Bluetooth", "SetBluetoothEnabled", state); - adapter.enabled = state; - } - - // Device Info Helpers - - function sortDevices(devices) { - return devices.sort((a, b) => { - const aName = _getDeviceName(a); - const bName = _getDeviceName(b); - const aHasRealName = aName.includes(" ") && aName.length > 3; - const bHasRealName = bName.includes(" ") && bName.length > 3; - - if (aHasRealName !== bHasRealName) - return aHasRealName ? -1 : 1; - - const aSignal = a.signalStrength > 0 ? a.signalStrength : 0; - const bSignal = b.signalStrength > 0 ? b.signalStrength : 0; - return bSignal - aSignal; - }); - } - - function getDeviceIcon(device) { - if (!device) - return "bt-device-generic"; - - const name = _getDeviceName(device).toLowerCase(); - const icon = (device.icon || "").toLowerCase(); - - const patterns = { - "bt-device-headphones": ["headset", "audio", "headphone", "airpod", "arctis"], - "bt-device-mouse": ["mouse"], - "bt-device-keyboard": ["keyboard"], - "bt-device-phone": ["phone", "iphone", "android", "samsung"], - "bt-device-watch": ["watch"], - "bt-device-speaker": ["speaker"], - "bt-device-tv": ["display", "tv"] - }; - - for (const [deviceIcon, keywords] of Object.entries(patterns)) { - if (keywords.some(keyword => icon.includes(keyword) || name.includes(keyword))) { - return deviceIcon; - } - } - return "bt-device-generic"; - } - - function canConnect(device) { - return device && !device.connected && !device.pairing && !device.blocked; - } - - function canDisconnect(device) { - return device && device.connected && !device.pairing && !device.blocked; - } - - function isDeviceBusy(device) { - return device && (device.pairing || device.state === BluetoothDeviceState.Disconnecting || device.state === BluetoothDeviceState.Connecting); - } - - function getStatusString(device) { - if (device.state === BluetoothDeviceState.Connecting) - return "Connecting..."; - if (device.pairing) - return "Pairing..."; - if (device.blocked) - return "Blocked"; - return ""; - } - - function getSignalStrength(device) { - if (!device || !device.signalStrength || device.signalStrength <= 0) { - return "Signal: Unknown"; - } - const signal = device.signalStrength; - const levels = [[80, "Excellent"], [60, "Good"], [40, "Fair"], [20, "Poor"]]; - for (const [threshold, label] of levels) { - if (signal >= threshold) - return `Signal: ${label}`; - } - return "Signal: Very poor"; - } - - function getSignalIcon(device) { - if (!device || !device.signalStrength || device.signalStrength <= 0) { - return "antenna-bars-off"; - } - const signal = device.signalStrength; - const icons = [[80, "5"], [60, "4"], [40, "3"], [20, "2"]]; - for (const [threshold, level] of icons) { - if (signal >= threshold) - return `antenna-bars-${level}`; - } - return "antenna-bars-1"; - } - - function getBattery(device) { - return `Battery: ${Math.round(device.battery * 100)}%`; - } - - // Device Monitoring - - Repeater { - model: root.devices - - Connections { - target: modelData - - function onPairedChanged() { - if (!modelData?.paired) - return; - _handlePairingSuccess(modelData); - } - - function onPairingChanged() { - if (!modelData) - return; - _handlePairingCancelled(modelData); - } - - function onConnectedChanged() { - if (!modelData) - return; - _handleConnectionChanged(modelData); - } - - function onStateChanged() { - if (!modelData) - return; - _handleStateChanged(modelData); - } - } - } - - // Adapter State Monitoring - Connections { target: adapter - function onStateChanged() { - if (!adapter || adapter.state === BluetoothAdapterState.Enabling || adapter.state === BluetoothAdapterState.Disabling) { + if (!adapter) { + Logger.w("Bluetooth", "onStateChanged", "No adapter available"); + return; + } + if (adapter.state === BluetoothAdapterState.Enabling || adapter.state === BluetoothAdapterState.Disabling) { return; } - Logger.d("Bluetooth", "Adapter state changed:", adapter.state); - const bluetoothBlockedToggled = root.blocked !== lastBluetoothBlocked; - lastBluetoothBlocked = root.blocked; - + Logger.d("Bluetooth", "onStateChanged", adapter.state); + const bluetoothBlockedToggled = (root.blocked !== lastBluetoothBlocked); + root.lastBluetoothBlocked = root.blocked; if (bluetoothBlockedToggled) { checkWifiBlocked.running = true; } else if (adapter.state === BluetoothAdapterState.Enabled) { @@ -264,109 +79,176 @@ Singleton { } } - // Private + function sortDevices(devices) { + return devices.sort((a, b) => { + var aName = a.name || a.deviceName || ""; + var bName = b.name || b.deviceName || ""; - function _filterDevices(filterFn) { - if (!adapter?.devices) - return []; - return adapter.devices.values.filter(dev => dev && filterFn(dev)); + var aHasRealName = aName.includes(" ") && aName.length > 3; + var bHasRealName = bName.includes(" ") && bName.length > 3; + + if (aHasRealName && !bHasRealName) + return -1; + if (!aHasRealName && bHasRealName) + return 1; + + var aSignal = (a.signalStrength !== undefined && a.signalStrength > 0) ? a.signalStrength : 0; + var bSignal = (b.signalStrength !== undefined && b.signalStrength > 0) ? b.signalStrength : 0; + return bSignal - aSignal; + }); } - function _getDeviceName(device) { - return device?.name || device?.deviceName || "Unknown"; + function getDeviceIcon(device) { + if (!device) { + return "bt-device-generic"; + } + + var name = (device.name || device.deviceName || "").toLowerCase(); + var icon = (device.icon || "").toLowerCase(); + if (icon.includes("headset") || icon.includes("audio") || name.includes("headphone") || name.includes("airpod") || name.includes("headset") || name.includes("arctis")) { + return "bt-device-headphones"; + } + + if (icon.includes("mouse") || name.includes("mouse")) { + return "bt-device-mouse"; + } + if (icon.includes("keyboard") || name.includes("keyboard")) { + return "bt-device-keyboard"; + } + if (icon.includes("phone") || name.includes("phone") || name.includes("iphone") || name.includes("android") || name.includes("samsung")) { + return "bt-device-phone"; + } + if (icon.includes("watch") || name.includes("watch")) { + return "bt-device-watch"; + } + if (icon.includes("speaker") || name.includes("speaker")) { + return "bt-device-speaker"; + } + if (icon.includes("display") || name.includes("tv")) { + return "bt-device-tv"; + } + return "bt-device-generic"; } - function _cleanupDeviceTracking(address) { - delete devicesBeingPaired[address]; - delete connectionAttempts[address]; + function canConnect(device) { + if (!device) + return false; + + /* + Paired + Means you’ve successfully exchanged keys with the device. + The devices remember each other and can authenticate without repeating the pairing process. + Example: once your headphones are paired, you don’t need to type a PIN every time. + Hence, instead of !device.paired, should be device.connected + */ + return !device.connected && !device.pairing && !device.blocked; } - function _configureAdapter() { - if (!adapter) + function canDisconnect(device) { + if (!device) + return false; + return device.connected && !device.pairing && !device.blocked; + } + + function getStatusString(device) { + if (device.state === BluetoothDeviceState.Connecting) { + return "Connecting..."; + } + if (device.pairing) { + return "Pairing..."; + } + if (device.blocked) { + return "Blocked"; + } + return ""; + } + + function getSignalStrength(device) { + if (!device || device.signalStrength === undefined || device.signalStrength <= 0) { + return "Signal: Unknown"; + } + var signal = device.signalStrength; + if (signal >= 80) { + return "Signal: Excellent"; + } + if (signal >= 60) { + return "Signal: Good"; + } + if (signal >= 40) { + return "Signal: Fair"; + } + if (signal >= 20) { + return "Signal: Poor"; + } + return "Signal: Very poor"; + } + + function getBattery(device) { + return `Battery: ${Math.round(device.battery * 100)}%`; + } + + function getSignalIcon(device) { + if (!device || device.signalStrength === undefined || device.signalStrength <= 0) { + return "antenna-bars-off"; + } + var signal = device.signalStrength; + if (signal >= 80) { + return "antenna-bars-5"; + } + if (signal >= 60) { + return "antenna-bars-4"; + } + if (signal >= 40) { + return "antenna-bars-3"; + } + if (signal >= 20) { + return "antenna-bars-2"; + } + return "antenna-bars-1"; + } + + function isDeviceBusy(device) { + if (!device) { + return false; + } + + return device.pairing || device.state === BluetoothDeviceState.Disconnecting || device.state === BluetoothDeviceState.Connecting; + } + + function connectDeviceWithTrust(device) { + if (!device) { return; + } - Logger.i("Bluetooth", "Configuring adapter..."); - if (!adapter.pairable) - adapter.pairable = true; - adapter.pairableTimeout = 0; + device.trusted = true; + device.connect(); } - function _handlePairingSuccess(device) { - const address = device.address; - if (!devicesBeingPaired[address]) + function disconnectDevice(device) { + if (!device) { return; - - Logger.i("Bluetooth", "Device paired successfully, connecting:", _getDeviceName(device)); - delete devicesBeingPaired[address]; - - Qt.callLater(() => { - if (device?.paired && !device.connected) { - Logger.i("Bluetooth", "Auto-connecting after pairing:", _getDeviceName(device)); - device.connect(); - } - }); - } - - function _handlePairingCancelled(device) { - const address = device.address; - if (!device.pairing && devicesBeingPaired[address] && !device.paired) { - Logger.w("Bluetooth", "Pairing cancelled or failed for:", _getDeviceName(device)); - delete devicesBeingPaired[address]; } + + device.disconnect(); } - function _handleConnectionChanged(device) { - const name = _getDeviceName(device); - const address = device.address; - - if (device.connected) { - Logger.i("Bluetooth", "Device connected:", name); - delete connectionAttempts[address]; - ToastService.showNotice(I18n.tr("bluetooth.panel.title"), `${name} connected`, "bluetooth-connected"); - } else { - Logger.i("Bluetooth", "Device disconnected:", name); - } - } - - function _handleStateChanged(device) { - const name = _getDeviceName(device); - const address = device.address; - const state = device.state; - - if (state === BluetoothDeviceState.Connecting) { - Logger.d("Bluetooth", "Device connecting:", name); - connectionAttempts[address] = { - name: name, - startTime: Date.now(), - wasConnecting: true - }; - } else if (state === BluetoothDeviceState.Disconnecting) { - Logger.d("Bluetooth", "Device disconnecting:", name); - } else if (state === BluetoothDeviceState.Disconnected) { - _checkFailedConnection(device, address, name); - } - } - - function _checkFailedConnection(device, address, name) { - const attempt = connectionAttempts[address]; - if (!attempt?.wasConnecting || device.connected) + function forgetDevice(device) { + if (!device) { return; - - const timeSinceAttempt = Date.now() - attempt.startTime; - if (timeSinceAttempt < 5000) { - Logger.w("Bluetooth", "Connection failed quickly for:", name, "- likely missing Bluetooth profiles"); - ToastService.showError("Bluetooth Connection Failed", `${name} - Missing audio profiles. Right-click to forget and try re-pairing, or check system Bluetooth services.`, "bluetooth-off"); } - delete connectionAttempts[address]; + + device.trusted = false; + device.forget(); } - // Internal + function setBluetoothEnabled(state) { + if (!adapter) { + Logger.w("Bluetooth", "No adapter available"); + return; + } - Timer { - id: discoveryTimer - interval: 1000 - repeat: false - onTriggered: adapter.discovering = true + Logger.i("Bluetooth", "SetBluetoothEnabled", state); + adapter.enabled = state; } Process { @@ -376,14 +258,18 @@ Singleton { stdout: StdioCollector { onStreamFinished: { - const wifiBlocked = text?.trim().includes("Soft blocked: yes") ?? false; - Logger.d("Network", "Wi-Fi adapter blocked:", wifiBlocked); + const wifiBlocked = text && text.trim().includes("Soft blocked: yes"); + Logger.d("Network", "Wi-Fi adapter was detected as blocked:", blocked); - if (wifiBlocked === root.blocked) { + // Check if airplane mode has been toggled + if (wifiBlocked && wifiBlocked === root.blocked) { root.airplaneModeToggled = true; - NetworkService.setWifiEnabled(!wifiBlocked); - const mode = wifiBlocked ? "enabled" : "disabled"; - ToastService.showNotice(I18n.tr("toast.airplane-mode.title"), I18n.tr(`toast.airplane-mode.${mode}`), wifiBlocked ? "plane" : "plane-off"); + NetworkService.setWifiEnabled(false); + ToastService.showNotice(I18n.tr("toast.airplane-mode.title"), I18n.tr("toast.airplane-mode.enabled"), "plane"); + } else if (!wifiBlocked && wifiBlocked === root.blocked) { + root.airplaneModeToggled = true; + NetworkService.setWifiEnabled(true); + ToastService.showNotice(I18n.tr("toast.airplane-mode.title"), I18n.tr("toast.airplane-mode.disabled"), "plane-off"); } else if (adapter.enabled) { ToastService.showNotice(I18n.tr("bluetooth.panel.title"), I18n.tr("toast.bluetooth.enabled"), "bluetooth"); discoveryTimer.running = true;