blob: b3c60cb3c24ebbaa69bb3b69dcb1cad16a08cd1e [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "remoteselector.h"
#include "ui_remoteselector.h"
#include <qbluetoothdeviceinfo.h>
#include <qbluetoothaddress.h>
#include <qbluetoothtransferrequest.h>
#include <qbluetoothtransferreply.h>
#include <qbluetoothlocaldevice.h>
#include <QMovie>
#include <QMessageBox>
#include <QFileDialog>
#include <QCheckBox>
#include "progress.h"
#include "pindisplay.h"
QT_USE_NAMESPACE
RemoteSelector::RemoteSelector(QWidget *parent)
: QDialog(parent), ui(new Ui::RemoteSelector),
m_localDevice(new QBluetoothLocalDevice), m_pindisplay(0),
m_pairingError(false)
{
ui->setupUi(this);
//Using default Bluetooth adapter
QBluetoothAddress adapterAddress = m_localDevice->address();
/*
* In case of multiple Bluetooth adapters it is possible to
* set which adapter will be used by providing MAC Address.
* Example code:
*
* QBluetoothAddress adapterAddress("XX:XX:XX:XX:XX:XX");
* m_discoveryAgent = new QBluetoothServiceDiscoveryAgent(adapterAddress);
*/
m_discoveryAgent = new QBluetoothServiceDiscoveryAgent(adapterAddress);
connect(m_discoveryAgent, SIGNAL(serviceDiscovered(QBluetoothServiceInfo)),
this, SLOT(serviceDiscovered(QBluetoothServiceInfo)));
connect(m_discoveryAgent, SIGNAL(finished()), this, SLOT(discoveryFinished()));
connect(m_discoveryAgent, SIGNAL(canceled()), this, SLOT(discoveryFinished()));
ui->remoteDevices->setColumnWidth(3, 75);
ui->remoteDevices->setColumnWidth(4, 100);
connect(m_localDevice, SIGNAL(pairingDisplayPinCode(QBluetoothAddress,QString)),
this, SLOT(displayPin(QBluetoothAddress,QString)));
connect(m_localDevice, SIGNAL(pairingDisplayConfirmation(QBluetoothAddress,QString)),
this, SLOT(displayConfirmation(QBluetoothAddress,QString)));
connect(m_localDevice, SIGNAL(pairingFinished(QBluetoothAddress,QBluetoothLocalDevice::Pairing)),
this, SLOT(pairingFinished(QBluetoothAddress,QBluetoothLocalDevice::Pairing)));
connect(m_localDevice, SIGNAL(error(QBluetoothLocalDevice::Error)),
this, SLOT(pairingError(QBluetoothLocalDevice::Error)));
ui->busyWidget->setMovie(new QMovie(":/icons/busy.gif"));
ui->busyWidget->movie()->start();
ui->pairingBusy->setMovie(new QMovie(":/icons/pairing.gif"));
ui->pairingBusy->hide();
ui->remoteDevices->clearContents();
ui->remoteDevices->setRowCount(0);
}
RemoteSelector::~RemoteSelector()
{
delete ui;
delete m_discoveryAgent;
delete m_localDevice;
}
void RemoteSelector::startDiscovery(const QBluetoothUuid &uuid)
{
ui->stopButton->setDisabled(false);
if (m_discoveryAgent->isActive())
m_discoveryAgent->stop();
m_discoveryAgent->setUuidFilter(uuid);
m_discoveryAgent->start();
if (!m_discoveryAgent->isActive() ||
m_discoveryAgent->error() != QBluetoothServiceDiscoveryAgent::NoError) {
ui->status->setText(tr("Cannot find remote services."));
} else {
ui->status->setText(tr("Scanning..."));
ui->busyWidget->show();
ui->busyWidget->movie()->start();
}
}
QBluetoothServiceInfo RemoteSelector::service() const
{
return m_service;
}
void RemoteSelector::serviceDiscovered(const QBluetoothServiceInfo &serviceInfo)
{
#if 0
qDebug() << "Discovered service on"
<< serviceInfo.device().name() << serviceInfo.device().address().toString();
qDebug() << "\tService name:" << serviceInfo.serviceName();
qDebug() << "\tDescription:"
<< serviceInfo.attribute(QBluetoothServiceInfo::ServiceDescription).toString();
qDebug() << "\tProvider:"
<< serviceInfo.attribute(QBluetoothServiceInfo::ServiceProvider).toString();
qDebug() << "\tL2CAP protocol service multiplexer:"
<< serviceInfo.protocolServiceMultiplexer();
qDebug() << "\tRFCOMM server channel:" << serviceInfo.serverChannel();
#endif
QString remoteName;
if (serviceInfo.device().name().isEmpty())
remoteName = serviceInfo.device().address().toString();
else
remoteName = serviceInfo.device().name();
// QListWidgetItem *item =
// new QListWidgetItem(QString::fromLatin1("%1\t%2\t%3").arg(serviceInfo.device().address().toString(),
// serviceInfo.device().name(), serviceInfo.serviceName()));
const QBluetoothAddress address = serviceInfo.device().address();
for (QBluetoothServiceInfo &info : m_discoveredServices) {
if (info.device().address() == address){
info = serviceInfo;
return;
}
}
int row = ui->remoteDevices->rowCount();
ui->remoteDevices->insertRow(row);
QTableWidgetItem *item = new QTableWidgetItem(address.toString());
ui->remoteDevices->setItem(row, 0, item);
item = new QTableWidgetItem(serviceInfo.device().name());
ui->remoteDevices->setItem(row, 1, item);
item = new QTableWidgetItem(serviceInfo.serviceName());
ui->remoteDevices->setItem(row, 2, item);
QBluetoothLocalDevice::Pairing p = m_localDevice->pairingStatus(address);
ui->remoteDevices->blockSignals(true);
item = new QTableWidgetItem();
if ((p&QBluetoothLocalDevice::Paired) || (p&QBluetoothLocalDevice::AuthorizedPaired))
item->setCheckState(Qt::Checked);
else
item->setCheckState(Qt::Unchecked);
ui->remoteDevices->setItem(row, 3, item);
item = new QTableWidgetItem();
if (p&QBluetoothLocalDevice::AuthorizedPaired)
item->setCheckState(Qt::Checked);
else
item->setCheckState(Qt::Unchecked);
ui->remoteDevices->setItem(row, 4, item);
ui->remoteDevices->blockSignals(false);
m_discoveredServices.insert(row, serviceInfo);
}
void RemoteSelector::discoveryFinished()
{
ui->status->setText(tr("Select the device to send to."));
ui->stopButton->setDisabled(true);
ui->busyWidget->movie()->stop();
ui->busyWidget->hide();
}
void RemoteSelector::startDiscovery()
{
startDiscovery(QBluetoothUuid(QBluetoothUuid::ObexObjectPush));
}
void RemoteSelector::on_refreshPB_clicked()
{
startDiscovery();
ui->stopButton->setDisabled(false);
}
void RemoteSelector::on_fileSelectPB_clicked()
{
ui->fileName->setText(QFileDialog::getOpenFileName());
if (m_service.isValid())
ui->sendButton->setDisabled(false);
}
void RemoteSelector::on_sendButton_clicked()
{
QBluetoothTransferManager mgr;
QBluetoothTransferRequest req(m_service.device().address());
m_file = new QFile(ui->fileName->text());
Progress *p = new Progress;
p->setStatus("Sending to: " + m_service.device().name(), "Waiting for start");
p->show();
QBluetoothTransferReply *reply = mgr.put(req, m_file);
//mgr is default parent
//ensure that mgr doesn't take reply down when leaving scope
reply->setParent(this);
if (reply->error()){
qDebug() << "Failed to send file";
p->finished(reply);
reply->deleteLater();
return;
}
connect(reply, SIGNAL(transferProgress(qint64,qint64)), p, SLOT(uploadProgress(qint64,qint64)));
connect(reply, SIGNAL(finished(QBluetoothTransferReply*)), p, SLOT(finished(QBluetoothTransferReply*)));
connect(p, SIGNAL(rejected()), reply, SLOT(abort()));
}
void RemoteSelector::on_stopButton_clicked()
{
m_discoveryAgent->stop();
}
QString RemoteSelector::addressToName(const QBluetoothAddress &address) const
{
for (const QBluetoothServiceInfo &info : m_discoveredServices) {
if (info.device().address() == address)
return info.device().name();
}
return address.toString();
}
void RemoteSelector::displayPin(const QBluetoothAddress &address, QString pin)
{
if (m_pindisplay)
m_pindisplay->deleteLater();
m_pindisplay = new pinDisplay(QString("Enter pairing pin on: %1").arg(addressToName(address)), pin, this);
m_pindisplay->show();
}
void RemoteSelector::displayConfirmation(const QBluetoothAddress &address, QString pin)
{
Q_UNUSED(address);
if (m_pindisplay)
m_pindisplay->deleteLater();
m_pindisplay = new pinDisplay(QString("Confirm this pin is the same"), pin, this);
connect(m_pindisplay, SIGNAL(accepted()), this, SLOT(displayConfAccepted()));
connect(m_pindisplay, SIGNAL(rejected()), this, SLOT(displayConfReject()));
m_pindisplay->setOkCancel();
m_pindisplay->show();
}
void RemoteSelector::displayConfAccepted()
{
m_localDevice->pairingConfirmation(true);
}
void RemoteSelector::displayConfReject()
{
m_localDevice->pairingConfirmation(false);
}
void RemoteSelector::pairingFinished(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing status)
{
QBluetoothServiceInfo service;
int row = 0;
ui->pairingBusy->hide();
ui->pairingBusy->movie()->stop();
ui->remoteDevices->blockSignals(true);
for (int i = 0; i < m_discoveredServices.count(); i++){
if (m_discoveredServices.value(i).device().address() == address){
service = m_discoveredServices.value(i);
row = i;
break;
}
}
if (m_pindisplay)
delete m_pindisplay;
QMessageBox msgBox;
if (m_pairingError) {
msgBox.setText("Pairing failed with " + address.toString());
} else if (status == QBluetoothLocalDevice::Paired
|| status == QBluetoothLocalDevice::AuthorizedPaired) {
msgBox.setText("Paired successfully with " + address.toString());
} else {
msgBox.setText("Pairing released with " + address.toString());
}
if (service.isValid()){
if (status == QBluetoothLocalDevice::AuthorizedPaired){
ui->remoteDevices->item(row, 3)->setCheckState(Qt::Checked);
ui->remoteDevices->item(row, 4)->setCheckState(Qt::Checked);
}
else if (status == QBluetoothLocalDevice::Paired){
ui->remoteDevices->item(row, 3)->setCheckState(Qt::Checked);
ui->remoteDevices->item(row, 4)->setCheckState(Qt::Unchecked);
}
else {
ui->remoteDevices->item(row, 3)->setCheckState(Qt::Unchecked);
ui->remoteDevices->item(row, 4)->setCheckState(Qt::Unchecked);
}
}
m_pairingError = false;
msgBox.exec();
ui->remoteDevices->blockSignals(false);
}
void RemoteSelector::pairingError(QBluetoothLocalDevice::Error error)
{
if (error != QBluetoothLocalDevice::PairingError)
return;
m_pairingError = true;
pairingFinished(m_service.device().address(), QBluetoothLocalDevice::Unpaired);
}
void RemoteSelector::on_remoteDevices_cellClicked(int row, int column)
{
Q_UNUSED(column);
m_service = m_discoveredServices.value(row);
if (!ui->fileName->text().isEmpty()) {
ui->sendButton->setDisabled(false);
}
}
void RemoteSelector::on_remoteDevices_itemChanged(QTableWidgetItem* item)
{
int row = item->row();
int column = item->column();
m_service = m_discoveredServices.value(row);
if (column < 3)
return;
if (item->checkState() == Qt::Unchecked && column == 3){
m_localDevice->requestPairing(m_service.device().address(), QBluetoothLocalDevice::Unpaired);
return; // don't continue and start movie
}
else if ((item->checkState() == Qt::Checked && column == 3) ||
(item->checkState() == Qt::Unchecked && column == 4)){
m_localDevice->requestPairing(m_service.device().address(), QBluetoothLocalDevice::Paired);
ui->remoteDevices->blockSignals(true);
ui->remoteDevices->item(row, column)->setCheckState(Qt::PartiallyChecked);
ui->remoteDevices->blockSignals(false);
}
else if (item->checkState() == Qt::Checked && column == 4){
m_localDevice->requestPairing(m_service.device().address(), QBluetoothLocalDevice::AuthorizedPaired);
ui->remoteDevices->blockSignals(true);
ui->remoteDevices->item(row, column)->setCheckState(Qt::PartiallyChecked);
ui->remoteDevices->blockSignals(false);
}
ui->pairingBusy->show();
ui->pairingBusy->movie()->start();
}