diff options
Diffstat (limited to 'src/tdebluez/devicesetupwizard.cpp')
-rw-r--r-- | src/tdebluez/devicesetupwizard.cpp | 662 |
1 files changed, 662 insertions, 0 deletions
diff --git a/src/tdebluez/devicesetupwizard.cpp b/src/tdebluez/devicesetupwizard.cpp new file mode 100644 index 0000000..77f88cf --- /dev/null +++ b/src/tdebluez/devicesetupwizard.cpp @@ -0,0 +1,662 @@ +/* + * + * 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, SIGNAL(timeout()), this, TQT_SLOT(slotAdvancePairingProgressBar())); + connect(connectTimer, SIGNAL(timeout()), this, TQT_SLOT(slotAdvanceConnectProgressBar())); + + connect(manager, SIGNAL(deviceServicesResolvedChanged(const TQString&, bool)), + this, TQT_SLOT(slotDeviceServicesResolvedChanged(const TQString&, bool))); + + connect(buttonSrc2Dst, SIGNAL(clicked()), this, SLOT(slotCopySrc2Dst())); + connect(buttonDst2Src, SIGNAL(clicked()), this, SLOT(slotCopyDst2Src())); + connect(cancelPairingButton, SIGNAL(clicked()), this, SLOT(slotCancelPairing())); + connect(cancelConnectButton, SIGNAL(clicked()), this, 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("Failed in connecting device: %s", error.message().local8Bit().data()); +// } + 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("Failed to get address for the new device: %s", error.message().local8Bit().data()); + + 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("Failed to get paired status for the new device: %s", error.message().local8Bit().data()); + + if (device->getConnected(error)) + { + setAppropriate(pairpage, false); + setAppropriate(pairingpage, false); + setAppropriate(connectpage, false); + setAppropriate(connectingpage, false); + TQWizard::showPage(donepage); + } + if (error.isValid()) + tqDebug("Failed to get connecting status of the new device: %s", error.message().local8Bit().data()); + + if (device->getTrusted(error)) + trustedCheckBox->setChecked(true); + if (error.isValid()) + tqDebug("Failed to get trusted status of the new device: %s", error.message().local8Bit().data()); + + connect(device, SIGNAL(PairAsyncReply(int /*asyncCallId*/)), + this, TQT_SLOT(slotPairAsyncReply(int /*asyncCallId*/))); + connect(device, SIGNAL(CancelPairingAsyncReply(int /*asyncCallId*/)), + this, TQT_SLOT(slotCancelPairingAsyncReply(int /*asyncCallId*/))); + connect(device, SIGNAL(AsyncErrorResponseDetected(int /*asyncCallId*/, const TQT_DBusError)), + this, TQT_SLOT(slotAsyncErrorResponseDetected(int /*asyncCallId*/, const TQT_DBusError))); + connect(device, SIGNAL(ConnectAsyncReply(int /*asyncCallId*/)), + this, TQT_SLOT(slotConnectAsyncReply(int /*asyncCallId*/))); +// connect(device, SIGNAL(DisonnectAsyncReply(int /*asyncCallId*/)), +// this, TQT_SLOT(slotDisconnectAsyncReply(int /*asyncCallId*/))); + connect(device, SIGNAL(ConnectProfileAsyncReply(int /*asyncCallId*/)), + this, TQT_SLOT(slotConnectProfileAsyncReply(int /*asyncCallId*/))); +// connect(device, SIGNAL(DisconnectProfileAsyncReply(int /*asyncCallId*/)), +// this, TQT_SLOT(slotDisconnectProfileAsyncReply(int /*asyncCallId*/))); +} + +void DeviceSetupWizard::updateServiceList() +{ + TQT_DBusError error; + uuids.clear(); + uuids = device->getUUIDs(error); + if (error.isValid()) + tqDebug("Failed to get uuids: %s", error.message().local8Bit().data()); + + 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("Failed to get paired status for the new device: %s", error.message().local8Bit().data()); + } + 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("Failed pairing the new device: %s", error.message().local8Bit().data()); + } + 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("Failed connecting the new device: %s", error.message().local8Bit().data()); + } + 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("Could not set trusted for %s\nError: %s", + address.latin1(), error.message().local8Bit().data()); + } + + 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, SIGNAL(PairAsyncReply(int /*asyncCallId*/)), + this, TQT_SLOT(slotPairAsyncReply(int /*asyncCallId*/))); + disconnect(device, SIGNAL(CancelPairingAsyncReply(int /*asyncCallId*/)), + this, TQT_SLOT(slotCancelPairingAsyncReply(int /*asyncCallId*/))); + disconnect(device, SIGNAL(AsyncErrorResponseDetected(int /*asyncCallId*/, const TQT_DBusError)), + this, TQT_SLOT(slotAsyncErrorResponseDetected(int /*asyncCallId*/, const TQT_DBusError))); + disconnect(device, SIGNAL(ConnectAsyncReply(int /*asyncCallId*/)), + this, TQT_SLOT(slotConnectAsyncReply(int /*asyncCallId*/))); +// disconnect(device, SIGNAL(DisonnectAsyncReply(int /*asyncCallId*/)), +// this, TQT_SLOT(slotDisconnectAsyncReply(int /*asyncCallId*/))); + disconnect(device, SIGNAL(ConnectProfileAsyncReply(int /*asyncCallId*/)), + this, TQT_SLOT(slotConnectProfileAsyncReply(int /*asyncCallId*/))); +// connect(device, SIGNAL(DisconnectProfileAsyncReply(int /*asyncCallId*/)), +// this, TQT_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("Failed to call DBus ConnectProfileAsync: %s", error.message().local8Bit().data()); + } + manager->getConnection()->scheduleDispatch(); + } +} + +void DeviceSetupWizard::slotAsyncErrorResponseDetected(int asyncCallId, const TQT_DBusError error) +{ + tqDebug("AsyncErrorResponseDetected: %i %s %s", error.type(), error.name().local8Bit().data(), error.message().local8Bit().data()); + + 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().local8Bit().data()) +// .arg(error.message().local8Bit().data()), +// TQMessageBox::Ok, TQMessageBox::NoButton, TQMessageBox::NoButton); + + KNotifyClient::event(TDEApplication::kApplication()->mainWidget()->winId(), + "ConnectionError", tr("AsyncErrorResponseDetected: %1\n%2\n%3") + .arg(error.type()) + .arg(error.name().local8Bit().data()) + .arg(error.message().local8Bit().data())); +} + +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, TQT_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("Failed to call DBus CancelPairingAsync: %s", error.message().local8Bit().data()); + } + + if(pairingTimer->isActive()) + pairingTimer->stop(); +} + +void DeviceSetupWizard::slotCancelConnecting() +{ + int asyncCallId = 0; + TQT_DBusError error; + if (device->getConnected(error)) + { + if (!device->DisconnectAsync(asyncCallId, error)) + tqDebug("Failed to call DisconnectAsync: %s", error.message().local8Bit().data()); + } + if (error.isValid()) + tqDebug("Failed in slotCancelConnecting: %s", error.message().local8Bit().data()); + + 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" |