// Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "device.h" #include "service.h" #include "ui_device.h" #include #include #include #include #if QT_CONFIG(permissions) #include #include #include #endif // QT_CONFIG(permissions) static QColor colorForPairing(QBluetoothLocalDevice::Pairing pairing) { return pairing == QBluetoothLocalDevice::Paired || pairing == QBluetoothLocalDevice::AuthorizedPaired ? QColor(Qt::green) : QColor(Qt::red); } DeviceDiscoveryDialog::DeviceDiscoveryDialog(QWidget *parent) : QDialog(parent), localDevice(new QBluetoothLocalDevice(this)), ui(new Ui::DeviceDiscovery) { #ifdef Q_OS_ANDROID this->setWindowState(Qt::WindowMaximized); #endif ui->setupUi(this); ui->stopScan->setVisible(false); // In case of multiple Bluetooth adapters it is possible to set the adapter // to be used. Example code: // // QBluetoothAddress address("XX:XX:XX:XX:XX:XX"); // discoveryAgent = new QBluetoothDeviceDiscoveryAgent(address, this); discoveryAgent = new QBluetoothDeviceDiscoveryAgent(this); connect(ui->scan, &QAbstractButton::clicked, this, &DeviceDiscoveryDialog::startScan); connect(ui->stopScan, &QAbstractButton::clicked, this, &DeviceDiscoveryDialog::stopScan); connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &DeviceDiscoveryDialog::addDevice); connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &DeviceDiscoveryDialog::scanFinished); connect(ui->list, &QListWidget::itemActivated, this, &DeviceDiscoveryDialog::itemActivated); connect(localDevice, &QBluetoothLocalDevice::hostModeStateChanged, this, &DeviceDiscoveryDialog::hostModeStateChanged); hostModeStateChanged(localDevice->hostMode()); // add context menu for devices to be able to pair device ui->list->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->list, &QWidget::customContextMenuRequested, this, &DeviceDiscoveryDialog::displayPairingMenu); connect(localDevice, &QBluetoothLocalDevice::pairingFinished, this, &DeviceDiscoveryDialog::pairingDone); } DeviceDiscoveryDialog::~DeviceDiscoveryDialog() { delete ui; } void DeviceDiscoveryDialog::addDevice(const QBluetoothDeviceInfo &info) { const QString label = info.address().toString() + u' ' + info.name(); const auto items = ui->list->findItems(label, Qt::MatchExactly); if (items.isEmpty()) { QListWidgetItem *item = new QListWidgetItem(label); QBluetoothLocalDevice::Pairing pairingStatus = localDevice->pairingStatus(info.address()); item->setForeground(colorForPairing(pairingStatus)); ui->list->addItem(item); } } void DeviceDiscoveryDialog::startScan() { #if QT_CONFIG(permissions) if (qApp->checkPermission(QBluetoothPermission{}) != Qt::PermissionStatus::Granted) { QMessageBox::warning(this, tr("Missing permission"), tr("Permission is needed to use Bluetooth. "\ "Please grant the permission to this "\ "application in the system settings.")); return; } #endif // QT_CONFIG(permissions) discoveryAgent->start(); ui->scan->setVisible(false); ui->stopScan->setVisible(true); } void DeviceDiscoveryDialog::stopScan() { discoveryAgent->stop(); scanFinished(); } void DeviceDiscoveryDialog::scanFinished() { ui->scan->setVisible(true); ui->stopScan->setVisible(false); } void DeviceDiscoveryDialog::itemActivated(QListWidgetItem *item) { const QString text = item->text(); const auto index = text.indexOf(' '); if (index == -1) return; QBluetoothAddress address(text.left(index)); QString name(text.mid(index + 1)); ServiceDiscoveryDialog d(name, address); d.exec(); } void DeviceDiscoveryDialog::on_discoverable_clicked(bool clicked) { if (clicked) localDevice->setHostMode(QBluetoothLocalDevice::HostDiscoverable); else localDevice->setHostMode(QBluetoothLocalDevice::HostConnectable); } void DeviceDiscoveryDialog::on_power_clicked(bool clicked) { if (clicked) localDevice->powerOn(); else localDevice->setHostMode(QBluetoothLocalDevice::HostPoweredOff); } void DeviceDiscoveryDialog::hostModeStateChanged(QBluetoothLocalDevice::HostMode mode) { ui->power->setChecked(mode != QBluetoothLocalDevice::HostPoweredOff); ui->discoverable->setChecked(mode == QBluetoothLocalDevice::HostDiscoverable); const bool on = mode != QBluetoothLocalDevice::HostPoweredOff; ui->scan->setEnabled(on); ui->discoverable->setEnabled(on); } void DeviceDiscoveryDialog::displayPairingMenu(const QPoint &pos) { if (ui->list->count() == 0) return; QMenu menu(this); QAction *pairAction = menu.addAction("Pair"); QAction *removePairAction = menu.addAction("Remove Pairing"); QAction *chosenAction = menu.exec(ui->list->viewport()->mapToGlobal(pos)); QListWidgetItem *currentItem = ui->list->currentItem(); QString text = currentItem->text(); const auto index = text.indexOf(' '); if (index == -1) return; QBluetoothAddress address (text.left(index)); if (chosenAction == pairAction) { localDevice->requestPairing(address, QBluetoothLocalDevice::Paired); } else if (chosenAction == removePairAction) { localDevice->requestPairing(address, QBluetoothLocalDevice::Unpaired); } } void DeviceDiscoveryDialog::pairingDone(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing) { const auto items = ui->list->findItems(address.toString(), Qt::MatchContains); const QColor color = colorForPairing(pairing); for (auto *item : items) item->setForeground(color); }