summaryrefslogtreecommitdiffstats
path: root/src/tdebluez/devicesetupwizard.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tdebluez/devicesetupwizard.cpp')
-rw-r--r--src/tdebluez/devicesetupwizard.cpp662
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"