You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdebluez/src/tdebluez/devicesetupwizard.cpp

662 lines
20 KiB

/*
*
* Dialogs for tdebluez device configuration
*
* Copyright (C) 2018 Emanoil Kotsev <deloptes@gmail.com>
*
*
* This file is part of tdebluez.
*
* tdebluez is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* tdebluez is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with kbluetooth; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <tqradiobutton.h>
#include <tqpushbutton.h>
#include <tqprogressbar.h>
#include <tqcheckbox.h>
#include <tdeconfig.h>
#include <knotifydialog.h>
#include <knotifyclient.h>
#include <interfaces/device1Proxy.h>
#include <btuuids.h>
#include <devicemimeconverter.h>
#include "application.h"
#include "devicesetupwizard.h"
#define LOGOTIMEOUT 100 //100 msec
#define ASYNC_TIMEOUT 15000 //15 sec
#define CONNECT_TIMEOUT 5000 // 5 sec
#define PROGRESS_TIMEOUT 50 // 50 msec
DeviceSetupWizard::DeviceSetupWizard(ObjectManagerImpl* _manager) :
DeviceSetupWizardDialog(), manager(_manager)
{
device = 0;
address = TQString();
pairpage = page(0);
setHelpEnabled(pairpage, false);
pairingpage = page(1);
setHelpEnabled(pairingpage, false);
connectpage = page(2);
setHelpEnabled(connectpage, false);
connectingpage = page(3);
setHelpEnabled(connectingpage, false);
donepage = page(4);
setHelpEnabled(donepage, false);
setFinishEnabled(donepage, true);
cancelButton()->setText(i18n("S&kip Wizard"));
setModal(true);
m_config = TDEGlobal::config();
// create the first ListView
tQListViewSrc->setRootIsDecorated(TRUE);
tQListViewSrc->setSelectionMode(TQListView::Multi);
tQListViewSrc->clear();
// create the second ListView
tQListViewDst->setRootIsDecorated(TRUE);
tQListViewDst->setSelectionMode(TQListView::Multi);
tQListViewDst->clear();
// progress bars
pairingProgressBar->setProgress(0,ASYNC_TIMEOUT);
pairingProgressBar->setPercentageVisible(false);
connectingProgressBar->setProgress(0,ASYNC_TIMEOUT);
connectingProgressBar->setPercentageVisible(false);
pairingTimer = new TQTimer(this);
connectTimer = new TQTimer(this);
connect(pairingTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotAdvancePairingProgressBar()));
connect(connectTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotAdvanceConnectProgressBar()));
connect(manager, TQ_SIGNAL(deviceServicesResolvedChanged(const TQString&, bool)),
this, TQ_SLOT(slotDeviceServicesResolvedChanged(const TQString&, bool)));
connect(buttonSrc2Dst, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotCopySrc2Dst()));
connect(buttonDst2Src, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotCopyDst2Src()));
connect(cancelPairingButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotCancelPairing()));
connect(cancelConnectButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotCancelConnecting()));
}
DeviceSetupWizard::~DeviceSetupWizard()
{
}
void DeviceSetupWizard::next()
{
if (pairingTimer->isActive())
{
pairingTimer->stop();
}
if (connectTimer->isActive())
{
connectTimer->stop();
}
if (currentPage() == pairpage)
{
if (pairingRadioButton1->isChecked())
{
pairingProgressBar->setProgress(0,ASYNC_TIMEOUT);
pairingTimer->start(PROGRESS_TIMEOUT);
setNextEnabled(pairpage, false);
setNextEnabled(pairingpage, false);
TQWizard::showPage(pairingpage);
startPairing();
}
else
TQWizard::showPage(donepage);
}
else if (currentPage() == connectpage)
{
preferredProfiles.clear();
TQListViewItemIterator it2(tQListViewDst);
while (it2.current())
{
TQString selText = it2.current()->text(0);
for (TQStringList::iterator it3 = uuids.begin(); it3 != uuids.end();
++it3)
{
TQString u = (*it3);
if (selText == resolveUUID(u))
{
kdDebug() << "REQUESTED UUID: " << u << endl;
preferredProfiles.append(u);
}
}
++it2;
}
m_config->setGroup(address);
m_config->writeEntry("profile", preferredProfiles);
m_config->sync();
// set the progress bar depending on the number of profiles to be connected
// and CONNECT_TIMEOUT value
// connectingProgressBar->setProgress(0, (ASYNC_TIMEOUT + CONNECT_TIMEOUT) * preferredProfiles.count());
connectingProgressBar->setProgress(0,ASYNC_TIMEOUT);
connectTimer->start(PROGRESS_TIMEOUT);
TQWizard::showPage(connectingpage);
slotConnectNextProfile();
}
// else if (currentPage() == connectingpage)
// {
// TQT_DBusError error;
// if (!device->getConnected(error))
// {
// int asyncCallId=0;
// device->ConnectAsync(asyncCallId, error);
// manager->getConnection()->scheduleDispatch();
// }
// else
// {
// TQWizard::next();
// }
// if (error.isValid())
// tqDebug(i18n("Failed in connecting device: %1").arg(error.message()));
// }
else if (currentPage() == donepage)
{
if (trustedCheckBox->isChecked())
{
finishButton()->setFocus();
}
else
{
trustedCheckBox->setFocus();
}
}
}
void DeviceSetupWizard::back()
{
TQWizard::back();
}
void DeviceSetupWizard::setDevice(DeviceImpl *_device)
{
kdDebug() << "New device: " << _device << endl;
if (device == _device)
return;
if (device)
closeDevice();
device = _device;
TQWizard::showPage(pairpage);
setNextEnabled(pairpage, true);
TQT_DBusError error;
address = device->getAddress(error);
if (error.isValid())
tqDebug(i18n("Failed to get address for the new device: %1").arg(error.message()));
if (device->getPaired(error))
{
updateServiceList();
preferredProfiles.clear();
tQListViewDst->clear();
m_config->setGroup(address);
preferredProfiles = m_config->readListEntry("profile");
TQStringList::iterator it = preferredProfiles.begin();
for (it; it != preferredProfiles.end(); ++it)
{
(void) new TQListViewItem(tQListViewDst, resolveUUID(*it));
}
setAppropriate(pairpage, false);
if (tQListViewDst->childCount() > 0)
setNextEnabled(connectpage, true);
TQWizard::showPage(connectpage);
} else {
tQListViewDst->clear();
}
if (error.isValid())
tqDebug(i18n("Failed to get paired status for the new device: %1").arg(error.message()));
if (device->getConnected(error))
{
setAppropriate(pairpage, false);
setAppropriate(pairingpage, false);
setAppropriate(connectpage, false);
setAppropriate(connectingpage, false);
TQWizard::showPage(donepage);
}
if (error.isValid())
tqDebug(i18n("Failed to get connecting status of the new device: %1").arg(error.message()));
if (device->getTrusted(error))
trustedCheckBox->setChecked(true);
if (error.isValid())
tqDebug(i18n("Failed to get trusted status of the new device: %1").arg(error.message()));
connect(device, TQ_SIGNAL(PairAsyncReply(int /*asyncCallId*/)),
this, TQ_SLOT(slotPairAsyncReply(int /*asyncCallId*/)));
connect(device, TQ_SIGNAL(CancelPairingAsyncReply(int /*asyncCallId*/)),
this, TQ_SLOT(slotCancelPairingAsyncReply(int /*asyncCallId*/)));
connect(device, TQ_SIGNAL(AsyncErrorResponseDetected(int /*asyncCallId*/, const TQT_DBusError)),
this, TQ_SLOT(slotAsyncErrorResponseDetected(int /*asyncCallId*/, const TQT_DBusError)));
connect(device, TQ_SIGNAL(ConnectAsyncReply(int /*asyncCallId*/)),
this, TQ_SLOT(slotConnectAsyncReply(int /*asyncCallId*/)));
// connect(device, TQ_SIGNAL(DisonnectAsyncReply(int /*asyncCallId*/)),
// this, TQ_SLOT(slotDisconnectAsyncReply(int /*asyncCallId*/)));
connect(device, TQ_SIGNAL(ConnectProfileAsyncReply(int /*asyncCallId*/)),
this, TQ_SLOT(slotConnectProfileAsyncReply(int /*asyncCallId*/)));
// connect(device, TQ_SIGNAL(DisconnectProfileAsyncReply(int /*asyncCallId*/)),
// this, TQ_SLOT(slotDisconnectProfileAsyncReply(int /*asyncCallId*/)));
}
void DeviceSetupWizard::updateServiceList()
{
TQT_DBusError error;
uuids.clear();
uuids = device->getUUIDs(error);
if (error.isValid())
tqDebug(i18n("Failed to get uuids: %1").arg(error.message()));
tQListViewSrc->clear();
for (TQStringList::iterator it = uuids.begin(); it != uuids.end(); ++it)
{
if (
((*it) == "00001203-0000-1000-8000-00805f9b34fb") || //Generic Audio
((*it) == "00001108-0000-1000-8000-00805f9b34fb") || //Headset
((*it) == "0000111e-0000-1000-8000-00805f9b34fb") || //Handsfree
((*it) == "0000111f-0000-1000-8000-00805f9b34fb") || //Handsfree AG
((*it) == "0000110a-0000-1000-8000-00805f9b34fb") || //A2DP Source
((*it) == "0000110b-0000-1000-8000-00805f9b34fb") || //A2DP Sink
((*it) == "00001103-0000-1000-8000-00805f9b34fb") || //DUN Gateway
((*it) == "00001800-0000-1000-8000-00805f9b34fb") //GAP
)
{
(void) new TQListViewItem(tQListViewSrc, resolveUUID((*it)));
}
}
}
void DeviceSetupWizard::startPairing()
{
TQT_DBusError error;
int asyncCallId = 0;
if (!device->PairAsync(asyncCallId, error))
{
if (error.isValid())
tqDebug(i18n("Failed to get paired status for the new device: %1").arg(error.message()));
}
manager->getConnection()->scheduleDispatch();
}
void DeviceSetupWizard::slotPairingTimeOut()
{
if(pairingTimer->isActive())
pairingTimer->stop();
if (!device)
return;
TQT_DBusError error;
if (!device->getPaired(error))
{
if (!error.isValid())
{
TQWizard::showPage(pairpage);
setNextEnabled(pairpage, true);
}
else
tqDebug(i18n("Failed pairing the new device: %1").arg(error.message()));
}
else
{
if (tQListViewDst->childCount() > 0)
setNextEnabled(connectpage, true);
TQWizard::showPage(connectpage);
}
}
void DeviceSetupWizard::slotConnectTimeOut()
{
if(connectTimer->isActive())
connectTimer->stop();
if (!device)
return;
TQT_DBusError error;
if (!device->getConnected(error))
{
if (!error.isValid())
{
TQWizard::showPage(connectpage);
if (tQListViewDst->childCount() > 0)
setNextEnabled(connectpage, true);
setNextEnabled(connectpage, true);
}
else
tqDebug(i18n("Failed connecting the new device: %1").arg(error.message()));
}
else
{
setNextEnabled(connectingpage, false);
setBackEnabled(donepage, false);
TQWizard::showPage(donepage);
}
}
/** the cancel button is connected to the reject() slot of TQDialog,
* so we have to reimplement this here to add a dialogbox to ask if we
* really want to quit the wizard.
*/
void DeviceSetupWizard::reject()
{
close(); // this will trigger the close event caught below
}
void DeviceSetupWizard::closeEvent(TQCloseEvent* e)
{
if (askClose())
{
hide();
closeDevice();
}
else
{
e->ignore();
}
}
/** maybe call a dialog that the wizard has finished. */
void DeviceSetupWizard::accept()
{
TQT_DBusError error;
if (trustedCheckBox->isChecked())
{
if (!device->getTrusted(error))
{
device->setTrusted(true, error);
}
if (error.isValid())
tqDebug(i18n("Could not set trusted for %1\nError: %2").arg(address).arg(error.message()));
}
hide();
closeDevice();
}
void DeviceSetupWizard::closeDevice()
{
// make sure timers go down
if (pairingTimer->isActive())
{
pairingTimer->stop();
}
if (connectTimer->isActive())
{
connectTimer->stop();
}
if (!device)
return;
disconnect(device, TQ_SIGNAL(PairAsyncReply(int /*asyncCallId*/)),
this, TQ_SLOT(slotPairAsyncReply(int /*asyncCallId*/)));
disconnect(device, TQ_SIGNAL(CancelPairingAsyncReply(int /*asyncCallId*/)),
this, TQ_SLOT(slotCancelPairingAsyncReply(int /*asyncCallId*/)));
disconnect(device, TQ_SIGNAL(AsyncErrorResponseDetected(int /*asyncCallId*/, const TQT_DBusError)),
this, TQ_SLOT(slotAsyncErrorResponseDetected(int /*asyncCallId*/, const TQT_DBusError)));
disconnect(device, TQ_SIGNAL(ConnectAsyncReply(int /*asyncCallId*/)),
this, TQ_SLOT(slotConnectAsyncReply(int /*asyncCallId*/)));
// disconnect(device, TQ_SIGNAL(DisonnectAsyncReply(int /*asyncCallId*/)),
// this, TQ_SLOT(slotDisconnectAsyncReply(int /*asyncCallId*/)));
disconnect(device, TQ_SIGNAL(ConnectProfileAsyncReply(int /*asyncCallId*/)),
this, TQ_SLOT(slotConnectProfileAsyncReply(int /*asyncCallId*/)));
// connect(device, TQ_SIGNAL(DisconnectProfileAsyncReply(int /*asyncCallId*/)),
// this, TQ_SLOT(slotDisconnectProfileAsyncReply(int /*asyncCallId*/)));
preferredProfiles.clear();
address = TQString();
device = 0;
}
void DeviceSetupWizard::slotDeviceServicesResolvedChanged(const TQString &path, bool resolved)
{
if (!device)
return;
if (path != device->getPath())
return;
updateServiceList();
}
void DeviceSetupWizard::slotConnectNextProfile()
{
if (preferredProfiles.isEmpty())
{
slotConnectTimeOut();
}
else
{
TQString connect = preferredProfiles.first();
//disable next button while profiles are being handled
setBackEnabled(connectpage, false);
setNextEnabled(connectpage, false);
setBackEnabled(connectingpage, false);
setNextEnabled(connectingpage, false);
int asyncCallId = 0;
TQT_DBusError error;
if (!device->ConnectProfileAsync(asyncCallId, connect, error))
{
if (error.isValid())
tqDebug(i18n("Failed to call DBus ConnectProfileAsync: %1").arg(error.message()));
}
manager->getConnection()->scheduleDispatch();
}
}
void DeviceSetupWizard::slotAsyncErrorResponseDetected(int asyncCallId, const TQT_DBusError error)
{
tqDebug(i18n("AsyncErrorResponseDetected: %1 %2 %3").arg(error.type()).arg(error.name()).arg(error.message()));
if(pairingTimer->isActive())
pairingTimer->stop();
if(connectTimer->isActive())
connectTimer->stop();
switch (error.type())
{
case 5: //org.freedesktop.DBus.Error.NoReply
case 22: // org.bluez.Error.InProgress, org.bluez.Error.Failed Host is down
default:
if (currentPage() == pairingpage)
{
slotPairingTimeOut();
}
if (currentPage() == connectingpage)
{
slotConnectTimeOut();
}
}
// TQMessageBox::critical( 0, "Device Setup Wizard",
// TQString("AsyncErrorResponseDetected: %1\n%2\n%3")
// .arg(error.type())
// .arg(error.name())
// .arg(error.message()),
// TQMessageBox::Ok, TQMessageBox::NoButton, TQMessageBox::NoButton);
KNotifyClient::event(TDEApplication::kApplication()->mainWidget()->winId(),
"ConnectionError", i18n("AsyncErrorResponseDetected: %1\n%2\n%3")
.arg(error.type())
.arg(error.name())
.arg(error.message()));
}
void DeviceSetupWizard::slotConnectAsyncReply(int asyncCallId)
{
slotConnectTimeOut();
}
//void DeviceSetupWizard::slotDisconnectAsyncReply(int asyncCallId)
//{
// slotConnectNextProfile();
//}
void DeviceSetupWizard::slotConnectProfileAsyncReply(int asyncCallId)
{
kdDebug() << __func__ << endl;
if (!preferredProfiles.isEmpty())
preferredProfiles.pop_front();
if (!preferredProfiles.isEmpty() && connectTimer->isActive())
// delay connecting next profile to prevent error InProgress
TQTimer::singleShot(CONNECT_TIMEOUT, this, TQ_SLOT(slotConnectNextProfile()));
else
slotConnectTimeOut();
}
//void DeviceSetupWizard::slotDisconnectProfileAsyncReply(int asyncCallId)
//{
// slotConnectTimeOut();
//}
void DeviceSetupWizard::slotPairAsyncReply(int asyncCallId)
{
slotPairingTimeOut();
}
void DeviceSetupWizard::slotCancelPairingAsyncReply(int asyncCallId)
{
slotPairingTimeOut();
}
void DeviceSetupWizard::slotCancelPairing()
{
int asyncCallId = 0;
TQT_DBusError error;
if (!device->CancelPairingAsync(asyncCallId, error))
{
if (error.isValid())
tqDebug(i18n("Failed to call DBus CancelPairingAsync: %1").arg(error.message()));
}
if(pairingTimer->isActive())
pairingTimer->stop();
}
void DeviceSetupWizard::slotCancelConnecting()
{
int asyncCallId = 0;
TQT_DBusError error;
if (device->getConnected(error))
{
if (!device->DisconnectAsync(asyncCallId, error))
tqDebug(i18n("Failed to call DisconnectAsync: %1").arg(error.message()));
}
if (error.isValid())
tqDebug(i18n("Failed in slotCancelConnecting: %1").arg(error.message()));
if(connectTimer->isActive())
connectTimer->stop();
}
void DeviceSetupWizard::slotAdvancePairingProgressBar()
{
if (pairingProgressBar->progress() < pairingProgressBar->totalSteps())
{
pairingProgressBar->setProgress(pairingProgressBar->progress() + ASYNC_TIMEOUT/PROGRESS_TIMEOUT);
}
else
pairingProgressBar->setProgress(0,ASYNC_TIMEOUT);
}
void DeviceSetupWizard::slotAdvanceConnectProgressBar()
{
if (connectingProgressBar->progress() < connectingProgressBar->totalSteps())
{
connectingProgressBar->setProgress(connectingProgressBar->progress() + ASYNC_TIMEOUT/PROGRESS_TIMEOUT);
}
else
connectingProgressBar->setProgress(0,ASYNC_TIMEOUT);
}
void DeviceSetupWizard::slotNext()
{
TQWizard::next();
}
bool DeviceSetupWizard::askClose()
{
TQString text;
if (currentPage() == page(0))
{
text = i18n("<p>Are you sure you want to quit the Device Settings Wizard?</p>"
"<p>The Device Settings Wizard helps you to configure the BT device and use it later.</p>"
"<p>Click <b>Cancel</b> to return and finish your setup.</p>");
}
else
{
text = i18n("<p>Are you sure you want to quit the Device Settings Wizard?</p>"
"<p>If yes, click <b>Quit</b> and all changes will be lost."
"<br>If not, click <b>Cancel</b> to return and finish your setup.</p>");
}
int status = KMessageBox::warningContinueCancel(this, text, i18n("All Changes Will Be Lost"), KStdGuiItem::quit());
if (status == KMessageBox::Continue)
return true;
else
return false;
}
void DeviceSetupWizard::slotCopySrc2Dst()
{
tQListViewDst->clear();
// iterate through the first ListView...
TQListViewItemIterator it(tQListViewSrc, TQListViewItemIterator::Selected);
while (it.current())
{
(void) new TQListViewItem(tQListViewDst, it.current()->text(0));
++it;
}
if (tQListViewDst->childCount() > 0)
setNextEnabled(connectpage, true);
}
void DeviceSetupWizard::slotCopyDst2Src()
{
// iterate through the first ListView...
TQListViewItemIterator it(tQListViewDst, TQListViewItemIterator::Selected);
while (it.current())
{
TQListViewItem *item = it.current();
++it;
delete item;
}
if (tQListViewDst->childCount() == 0)
setNextEnabled(connectpage, false);
}
#include "devicesetupwizard.moc"