1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-07 05:55:37 -05:00

bluetooth: pairing dialog

- Allows connecting pin/passkey/confirmation dialogs
- Fixes inability to connect to many devices
- Dependent on un-merged quickshell PR: https://github.com/quickshell-mirror/quickshell/pull/138
This commit is contained in:
bbedward
2025-07-22 19:20:10 -04:00
parent 02bd9bbc72
commit 3157622c8b
4 changed files with 555 additions and 29 deletions

View File

@@ -0,0 +1,372 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import Quickshell.Widgets
import Quickshell.Bluetooth
import qs.Common
import qs.Services
import qs.Widgets
PanelWindow {
id: root
property bool bluetoothPairingDialogVisible: BluetoothService.pairingDialogVisible
property int pairingType: BluetoothService.pairingType
property int passkey: BluetoothService.pendingPasskey
property string deviceAddress: BluetoothService.pendingDeviceAddress
property alias inputText: pairingInput.text
visible: bluetoothPairingDialogVisible
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: bluetoothPairingDialogVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
color: "transparent"
onVisibleChanged: {
if (visible) {
console.log("BluetoothPairingDialog: Showing dialog for device:", deviceAddress, "name:", BluetoothService.pendingDeviceName, "type:", pairingType);
pairingInput.enabled = true;
BluetoothService.inputText = "";
Qt.callLater(function() {
if (pairingType === BluetoothPairingRequestType.PinCode || pairingType === BluetoothPairingRequestType.Passkey)
pairingInput.forceActiveFocus();
});
} else {
pairingInput.enabled = false;
}
}
anchors {
top: true
left: true
right: true
bottom: true
}
Rectangle {
anchors.fill: parent
color: Qt.rgba(0, 0, 0, 0.5)
opacity: bluetoothPairingDialogVisible ? 1 : 0
MouseArea {
anchors.fill: parent
onClicked: {
pairingInput.enabled = false;
BluetoothService.rejectPairing();
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.standardEasing
}
}
}
Rectangle {
width: Math.min(400, parent.width - Theme.spacingL * 2)
height: Math.min(contentColumn.implicitHeight + Theme.spacingL * 2, parent.height - Theme.spacingL * 2)
anchors.centerIn: parent
color: Theme.surfaceContainer
radius: Theme.cornerRadiusLarge
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: 1
opacity: bluetoothPairingDialogVisible ? 1 : 0
scale: bluetoothPairingDialogVisible ? 1 : 0.9
MouseArea {
// Prevent propagation to background
anchors.fill: parent
onClicked: {
}
}
Column {
id: contentColumn
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingL
// Header
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "bluetooth"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
Column {
width: parent.width - 40 - Theme.spacingM - Theme.iconSize
spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
Text {
text: "Bluetooth Pairing"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
Text {
text: BluetoothService.pendingDeviceName || deviceAddress || "Unknown Device"
font.pixelSize: Theme.fontSizeMedium
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
width: parent.width
elide: Text.ElideRight
}
Text {
text: deviceAddress || ""
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
font.family: "monospace"
visible: deviceAddress && deviceAddress !== BluetoothService.pendingDeviceName
}
}
DankActionButton {
iconName: "close"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText
hoverColor: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
onClicked: {
pairingInput.enabled = false;
BluetoothService.rejectPairing();
}
}
}
// Dynamic content based on pairing type
Column {
width: parent.width
spacing: Theme.spacingM
// Authorization
Text {
text: "Allow pairing with this device?"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
anchors.horizontalCenter: parent.horizontalCenter
visible: pairingType === BluetoothPairingRequestType.Authorization
}
// Service Authorization
Text {
text: "Allow service connection from this device?"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
anchors.horizontalCenter: parent.horizontalCenter
visible: pairingType === BluetoothPairingRequestType.ServiceAuthorization
}
// Confirmation
Rectangle {
width: parent.width
height: 80
radius: Theme.cornerRadius
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
border.color: Theme.primary
border.width: 1
visible: pairingType === BluetoothPairingRequestType.Confirmation
Column {
anchors.centerIn: parent
spacing: Theme.spacingS
Text {
text: "Confirm this passkey matches on both devices:"
font.pixelSize: Theme.fontSizeMedium
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
anchors.horizontalCenter: parent.horizontalCenter
}
Text {
text: passkey.toString().padStart(6, '0')
font.pixelSize: Theme.fontSizeXXLarge
color: Theme.primary
font.weight: Font.Bold
font.family: "monospace"
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
// PIN Code or Passkey Input
Column {
width: parent.width
spacing: Theme.spacingS
visible: pairingType === BluetoothPairingRequestType.PinCode || pairingType === BluetoothPairingRequestType.Passkey
Text {
text: pairingType === BluetoothPairingRequestType.PinCode ? "Enter PIN code for this device:" : "Enter 6-digit passkey shown on other device:"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
Rectangle {
width: parent.width
height: 50
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
border.color: pairingInput.activeFocus ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: pairingInput.activeFocus ? 2 : 1
DankTextField {
id: pairingInput
anchors.fill: parent
font.pixelSize: Theme.fontSizeLarge
textColor: Theme.surfaceText
text: BluetoothService.inputText
enabled: bluetoothPairingDialogVisible
placeholderText: pairingType === BluetoothPairingRequestType.PinCode ? "e.g., 0000 or 1234" : "123456"
backgroundColor: "transparent"
normalBorderColor: "transparent"
focusedBorderColor: "transparent"
inputMethodHints: pairingType === BluetoothPairingRequestType.Passkey ? Qt.ImhDigitsOnly : Qt.ImhNone
onTextEdited: {
// For passkey, limit to 6 digits only
if (pairingType === BluetoothPairingRequestType.Passkey) {
var filtered = text.replace(/[^0-9]/g, '').substring(0, 6);
if (text !== filtered) {
text = filtered;
return ;
}
}
BluetoothService.inputText = text;
}
onAccepted: {
if (text.length > 0)
BluetoothService.acceptPairing();
}
}
}
}
}
// Buttons
Row {
width: parent.width
spacing: Theme.spacingM
Rectangle {
width: (parent.width - Theme.spacingM) / 2
height: 40
radius: Theme.cornerRadius
color: rejectArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
border.color: Theme.error
border.width: 1
Text {
anchors.centerIn: parent
text: "Cancel"
font.pixelSize: Theme.fontSizeMedium
color: Theme.error
font.weight: Font.Medium
}
MouseArea {
id: rejectArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: BluetoothService.rejectPairing()
}
}
Rectangle {
width: (parent.width - Theme.spacingM) / 2
height: 40
radius: Theme.cornerRadius
color: acceptArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
border.color: Theme.primary
border.width: 1
opacity: {
// Authorization/Confirmation/ServiceAuthorization always enabled
if (pairingType <= BluetoothPairingRequestType.Confirmation || pairingType === BluetoothPairingRequestType.ServiceAuthorization)
return 1;
// PIN/Passkey need input
return BluetoothService.inputText.length > 0 ? 1 : 0.5;
}
Text {
anchors.centerIn: parent
text: {
switch (pairingType) {
case BluetoothPairingRequestType.Authorization:
return "Accept";
case BluetoothPairingRequestType.Confirmation:
return "Confirm";
case BluetoothPairingRequestType.ServiceAuthorization:
return "Allow";
case BluetoothPairingRequestType.PinCode:
return "Pair";
case BluetoothPairingRequestType.Passkey:
return "Enter";
default:
return "OK";
}
}
font.pixelSize: Theme.fontSizeMedium
color: Theme.primary
font.weight: Font.Medium
}
MouseArea {
id: acceptArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
enabled: pairingType <= BluetoothPairingRequestType.Confirmation || pairingType === BluetoothPairingRequestType.ServiceAuthorization || BluetoothService.inputText.length > 0
onClicked: BluetoothService.acceptPairing()
}
}
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on scale {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
}

View File

@@ -25,8 +25,8 @@ Item {
width: parent.width width: parent.width
height: 60 height: 60
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: bluetoothToggle.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : (BluetoothService.adapter && BluetoothService.adapter.enabled ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.12)) color: bluetoothToggle.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : (BluetoothService.enabled ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.12))
border.color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : "transparent" border.color: BluetoothService.enabled ? Theme.primary : "transparent"
border.width: 2 border.width: 2
Row { Row {
@@ -38,7 +38,7 @@ Item {
DankIcon { DankIcon {
name: "bluetooth" name: "bluetooth"
size: Theme.iconSizeLarge size: Theme.iconSizeLarge
color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : Theme.surfaceText color: BluetoothService.enabled ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
@@ -49,12 +49,12 @@ Item {
Text { Text {
text: "Bluetooth" text: "Bluetooth"
font.pixelSize: Theme.fontSizeLarge font.pixelSize: Theme.fontSizeLarge
color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : Theme.surfaceText color: BluetoothService.enabled ? Theme.primary : Theme.surfaceText
font.weight: Font.Medium font.weight: Font.Medium
} }
Text { Text {
text: BluetoothService.adapter && BluetoothService.adapter.enabled ? "Enabled" : "Disabled" text: BluetoothService.enabled ? "Enabled" : "Disabled"
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
} }
@@ -68,11 +68,10 @@ Item {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor enabled: !BluetoothService.operationInProgress
cursorShape: enabled ? Qt.PointingHandCursor : Qt.BusyCursor
onClicked: { onClicked: {
if (BluetoothService.adapter) { BluetoothService.toggleAdapter();
BluetoothService.adapter.enabled = !BluetoothService.adapter.enabled;
}
} }
} }
@@ -81,7 +80,7 @@ Item {
Column { Column {
width: parent.width width: parent.width
spacing: Theme.spacingM spacing: Theme.spacingM
visible: BluetoothService.adapter && BluetoothService.adapter.enabled visible: BluetoothService.enabled
Text { Text {
text: "Paired Devices" text: "Paired Devices"
@@ -91,7 +90,7 @@ Item {
} }
Repeater { Repeater {
model: BluetoothService.adapter && BluetoothService.adapter.devices ? BluetoothService.adapter.devices.values.filter((dev) => { model: BluetoothService.devices ? BluetoothService.devices.values.filter((dev) => {
return dev && (dev.paired || dev.trusted); return dev && (dev.paired || dev.trusted);
}) : [] }) : []
@@ -224,7 +223,7 @@ Item {
Column { Column {
width: parent.width width: parent.width
spacing: Theme.spacingM spacing: Theme.spacingM
visible: BluetoothService.adapter && BluetoothService.adapter.enabled visible: BluetoothService.enabled
Row { Row {
width: parent.width width: parent.width
@@ -256,7 +255,7 @@ Item {
spacing: Theme.spacingXS spacing: Theme.spacingXS
DankIcon { DankIcon {
name: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop" : "bluetooth_searching" name: BluetoothService.discovering ? "stop" : "bluetooth_searching"
size: Theme.iconSize - 4 size: Theme.iconSize - 4
color: Theme.primary color: Theme.primary
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@@ -265,7 +264,7 @@ Item {
Text { Text {
id: scanText id: scanText
text: BluetoothService.adapter && BluetoothService.adapter.discovering ? "Stop Scanning" : "Start Scanning" text: BluetoothService.discovering ? "Stop Scanning" : "Start Scanning"
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: Theme.primary color: Theme.primary
font.weight: Font.Medium font.weight: Font.Medium
@@ -279,11 +278,10 @@ Item {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor enabled: !BluetoothService.operationInProgress
cursorShape: enabled ? Qt.PointingHandCursor : Qt.BusyCursor
onClicked: { onClicked: {
if (BluetoothService.adapter) { BluetoothService.toggleDiscovery();
BluetoothService.adapter.discovering = !BluetoothService.adapter.discovering;
}
} }
} }
@@ -293,10 +291,10 @@ Item {
Repeater { Repeater {
model: { model: {
if (!BluetoothService.adapter || !BluetoothService.adapter.discovering || !Bluetooth.devices) if (!BluetoothService.discovering || !BluetoothService.devices)
return []; return [];
var filtered = Bluetooth.devices.values.filter((dev) => { var filtered = BluetoothService.devices.values.filter((dev) => {
return dev && !dev.paired && !dev.pairing && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0); return dev && !dev.paired && !dev.pairing && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0);
}); });
return BluetoothService.sortDevices(filtered); return BluetoothService.sortDevices(filtered);
@@ -496,10 +494,10 @@ Item {
width: parent.width width: parent.width
spacing: Theme.spacingM spacing: Theme.spacingM
visible: { visible: {
if (!BluetoothService.adapter || !BluetoothService.adapter.discovering || !Bluetooth.devices) if (!BluetoothService.discovering || !BluetoothService.devices)
return false; return false;
var availableCount = Bluetooth.devices.values.filter((dev) => { var availableCount = BluetoothService.devices.values.filter((dev) => {
return dev && !dev.paired && !dev.pairing && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0); return dev && !dev.paired && !dev.pairing && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0);
}).length; }).length;
@@ -550,20 +548,21 @@ Item {
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
visible: { visible: {
if (!BluetoothService.adapter || !Bluetooth.devices) if (!BluetoothService.devices)
return true; return true;
var availableCount = Bluetooth.devices.values.filter((dev) => { var availableCount = BluetoothService.devices.values.filter((dev) => {
return dev && !dev.paired && !dev.pairing && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0); return dev && !dev.paired && !dev.pairing && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0);
}).length; }).length;
return availableCount === 0 && !BluetoothService.adapter.discovering; return availableCount === 0 && !BluetoothService.discovering;
} }
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
width: parent.width width: parent.width
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
} }
} }
} }

View File

@@ -10,8 +10,10 @@ Singleton {
readonly property BluetoothAdapter adapter: Bluetooth.defaultAdapter readonly property BluetoothAdapter adapter: Bluetooth.defaultAdapter
readonly property bool available: adapter !== null readonly property bool available: adapter !== null
readonly property bool enabled: (adapter && adapter.enabled) ?? false readonly property bool enabled: available ? adapter.enabled ?? false : false
readonly property bool discovering: (adapter && adapter.discovering) ?? false readonly property bool discovering: available ? adapter.discovering ?? false : false
property bool operationInProgress: false
readonly property var devices: adapter ? adapter.devices : null readonly property var devices: adapter ? adapter.devices : null
readonly property var pairedDevices: { readonly property var pairedDevices: {
if (!adapter || !adapter.devices) if (!adapter || !adapter.devices)
@@ -30,6 +32,15 @@ Singleton {
}); });
} }
// Pairing dialog properties
property bool pairingDialogVisible: false
property int pairingType: BluetoothPairingRequestType.Authorization
property string pendingDeviceAddress: ""
property string pendingDeviceName: ""
property int pendingPasskey: 0
property var pendingToken: null
property string inputText: ""
function sortDevices(devices) { function sortDevices(devices) {
return devices.sort((a, b) => { return devices.sort((a, b) => {
var aName = a.name || a.deviceName || ""; var aName = a.name || a.deviceName || "";
@@ -167,9 +178,147 @@ Singleton {
function connectDeviceWithTrust(device) { function connectDeviceWithTrust(device) {
if (!device) return; if (!device) return;
device.trusted = true; device.connect()
device.connect(); }
function toggleAdapter() {
if (!available || operationInProgress) {
console.warn("BluetoothService: Cannot toggle adapter - not available or operation in progress");
return false;
}
operationInProgress = true;
var targetState = !adapter.enabled;
try {
adapter.enabled = targetState;
return true;
} catch (error) {
console.error("BluetoothService: Failed to toggle adapter:", error);
operationInProgress = false;
return false;
}
}
function toggleDiscovery() {
if (!available || !adapter.enabled || operationInProgress) {
console.warn("BluetoothService: Cannot toggle discovery - adapter not ready or operation in progress");
return false;
}
operationInProgress = true;
var targetState = !adapter.discovering;
try {
adapter.discovering = targetState;
return true;
} catch (error) {
console.error("BluetoothService: Failed to toggle discovery:", error);
operationInProgress = false;
return false;
}
}
// Monitor adapter state changes to clear operation flags
Connections {
target: adapter
ignoreUnknownSignals: true
function onEnabledChanged() {
operationInProgress = false;
}
function onDiscoveringChanged() {
operationInProgress = false;
}
}
// Pairing agent signal handler
Connections {
target: Bluetooth.agent
ignoreUnknownSignals: true
function onPairingRequested(deviceAddress, type, passkey, token) {
console.log("BluetoothService: Pairing requested for", deviceAddress, "type:", type, "passkey:", passkey, "token:", token);
root.pairingType = type;
root.pendingDeviceAddress = deviceAddress;
root.pendingPasskey = passkey;
root.pendingToken = token;
root.inputText = "";
// Try to find and store the device name using MAC address
var device = root.getDeviceFromAddress(deviceAddress);
root.pendingDeviceName = device ? (device.name || device.deviceName || deviceAddress) : deviceAddress;
console.log("BluetoothService: Device name:", root.pendingDeviceName, "for address:", deviceAddress, "token:", token);
root.pairingDialogVisible = true;
}
}
function acceptPairing() {
console.log("BluetoothService: Accepting pairing for", root.pendingDeviceAddress, "type:", root.pairingType, "token:", root.pendingToken);
if (!Bluetooth.agent || root.pendingToken === null) return;
switch (root.pairingType) {
case BluetoothPairingRequestType.Authorization:
case BluetoothPairingRequestType.Confirmation:
case BluetoothPairingRequestType.ServiceAuthorization:
Bluetooth.agent.respondToRequest(root.pendingToken, true);
break;
case BluetoothPairingRequestType.PinCode:
if (root.inputText.length > 0) {
Bluetooth.agent.respondWithPinCode(root.pendingToken, root.inputText);
} else {
console.warn("BluetoothService: No PIN code entered");
return;
}
break;
case BluetoothPairingRequestType.Passkey:
var passkey = parseInt(root.inputText);
if (passkey >= 0 && passkey <= 999999) {
Bluetooth.agent.respondWithPasskey(root.pendingToken, passkey);
} else {
console.warn("BluetoothService: Invalid passkey:", root.inputText);
return;
}
break;
}
closePairingDialog();
}
function rejectPairing() {
console.log("BluetoothService: Rejecting pairing for", root.pendingDeviceAddress, "token:", root.pendingToken);
if (Bluetooth.agent && root.pendingToken !== null) {
Bluetooth.agent.respondToRequest(root.pendingToken, false);
}
closePairingDialog();
}
function closePairingDialog() {
root.pairingDialogVisible = false;
root.pendingDeviceAddress = "";
root.pendingDeviceName = "";
root.pendingPasskey = 0;
root.pendingToken = null;
root.inputText = "";
root.pairingType = BluetoothPairingRequestType.Authorization;
}
function getDeviceFromPath(devicePath) {
if (!adapter || !adapter.devices || !devicePath)
return null;
return adapter.devices.values.find(d => d && d.path === devicePath) || null;
}
function getDeviceFromAddress(deviceAddress) {
if (!adapter || !adapter.devices || !deviceAddress)
return null;
return adapter.devices.values.find(d => d && d.address === deviceAddress) || null;
} }
} }

View File

@@ -16,6 +16,7 @@ ShellRoot {
delegate: TopBar { delegate: TopBar {
modelData: item modelData: item
} }
} }
// Global popup windows // Global popup windows
@@ -43,6 +44,10 @@ ShellRoot {
id: wifiPasswordDialog id: wifiPasswordDialog
} }
BluetoothPairingDialog {
id: bluetoothPairingDialog
}
NetworkInfoDialog { NetworkInfoDialog {
id: networkInfoDialog id: networkInfoDialog
} }
@@ -95,4 +100,5 @@ ShellRoot {
Toast { Toast {
id: toastWidget id: toastWidget
} }
} }