diff options
author | Dirk Hohndel <dirk@hohndel.org> | 2017-06-05 19:41:57 -0700 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2017-06-11 13:55:41 -0700 |
commit | b14a522f4f308aed41ab49b5529cc10c05168716 (patch) | |
tree | 2b3540e4aeefbe9c21ab9d3af1af2b07a4f503e0 | |
parent | 3b993fbaad0738efe54e797b60e4f49270951323 (diff) | |
download | subsurface-b14a522f4f308aed41ab49b5529cc10c05168716.tar.gz |
QML UI: move BT handling into core code
This shouldn't be part of the UI (qmlmanager), but part of our
overall handling of dive computers and BT devices.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
-rw-r--r-- | core/CMakeLists.txt | 1 | ||||
-rw-r--r-- | core/btdiscovery.cpp | 165 | ||||
-rw-r--r-- | core/btdiscovery.h | 64 | ||||
-rw-r--r-- | core/downloadfromdcthread.cpp | 56 | ||||
-rw-r--r-- | core/downloadfromdcthread.h | 11 | ||||
-rw-r--r-- | mobile-widgets/qml/DownloadFromDiveComputer.qml | 10 | ||||
-rw-r--r-- | mobile-widgets/qmlmanager.cpp | 168 | ||||
-rw-r--r-- | mobile-widgets/qmlmanager.h | 29 |
8 files changed, 305 insertions, 199 deletions
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index b2effd519..77521d6a1 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -26,6 +26,7 @@ endif() # compile the core library, in C. set(SUBSURFACE_CORE_LIB_SRCS + btdiscovery.cpp cochran.c datatrak.c deco.c diff --git a/core/btdiscovery.cpp b/core/btdiscovery.cpp new file mode 100644 index 000000000..cf68bf8eb --- /dev/null +++ b/core/btdiscovery.cpp @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "btdiscovery.h" +#include "downloadfromdcthread.h" +#include <QDebug> + +BTDiscovery *BTDiscovery::m_instance = NULL; + +BTDiscovery::BTDiscovery(QObject *parent) +{ + Q_UNUSED(parent) + if (m_instance) { + qDebug() << "trying to create an additional BTDiscovery object"; + return; + } + m_instance = this; +#if defined(BT_SUPPORT) + if (localBtDevice.isValid() && + localBtDevice.hostMode() == QBluetoothLocalDevice::HostConnectable) { + btPairedDevices.clear(); + qDebug() << "localDevice " + localBtDevice.name() + " is valid, starting discovery"; +#if defined(Q_OS_LINUX) + discoveryAgent = new QBluetoothDeviceDiscoveryAgent(this); + connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &BTDiscovery::btDeviceDiscovered); + discoveryAgent->start(); +#endif +#if defined(Q_OS_ANDROID) && defined(BT_SUPPORT) + getBluetoothDevices(); +#endif + for (int i = 0; i < btPairedDevices.length(); i++) { + qDebug() << "Paired =" << btPairedDevices[i].name << btPairedDevices[i].address.toString(); + } +#if defined(Q_OS_LINUX) + discoveryAgent->stop(); +#endif + } else { + qDebug() << "localBtDevice isn't valid"; + } +#endif +} + +BTDiscovery::~BTDiscovery() +{ + m_instance = NULL; +#if defined(BT_SUPPORT) + free(discoveryAgent); +#endif +} + +BTDiscovery *BTDiscovery::instance() +{ + if (!m_instance) + m_instance = new BTDiscovery(); + return m_instance; +} + +#if defined(BT_SUPPORT) + +extern void addBtUuid(QBluetoothUuid uuid); +extern QHash<QString, QStringList> productList; +extern QStringList vendorList; + +void BTDiscovery::btDeviceDiscovered(const QBluetoothDeviceInfo &device) +{ + btPairedDevice this_d; + this_d.address = device.address(); + this_d.name = device.name(); + btPairedDevices.append(this_d); + + QString newDevice = device.name(); + + // all the HW OSTC BT computers show up as "OSTC" + some other text, depending on model + if (newDevice.startsWith("OSTC")) + newDevice = "OSTC 3"; + QList<QBluetoothUuid> serviceUuids = device.serviceUuids(); + foreach (QBluetoothUuid id, serviceUuids) { + addBtUuid(id); + qDebug() << id.toByteArray(); + } + qDebug() << "Found new device " + newDevice + " (" + device.address().toString() + ")"; + QString vendor, product; + foreach (vendor, productList.keys()) { + if (productList[vendor].contains(newDevice)) { + qDebug() << "this could be a " + vendor + " " + + (newDevice == "OSTC 3" ? "OSTC family" : newDevice); + struct btVendorProduct btVP; + btVP.btdi = device; + btVP.vendorIdx = vendorList.indexOf(vendor); + btVP.productIdx = productList[vendor].indexOf(newDevice); + qDebug() << "adding new btDCs entry" << newDevice << btVP.vendorIdx << btVP.productIdx; + btDCs << btVP; + } + } +} + +QList <struct btVendorProduct> BTDiscovery::getBtDcs() +{ + return btDCs; +} + + +// Android: As Qt is not able to pull the pairing data from a device, i +// a lengthy discovery process is needed to see what devices are paired. On +// https://forum.qt.io/topic/46075/solved-bluetooth-list-paired-devices +// user s.frings74 does, however, present a solution to this using JNI. +// Currently, this code is taken "as is". + +#if defined(Q_OS_ANDROID) +void BTDiscovery::getBluetoothDevices() +{ + struct BTDiscovery::btPairedDevice result; + // Query via Android Java API. + + // returns a BluetoothAdapter + QAndroidJniObject adapter=QAndroidJniObject::callStaticObjectMethod("android/bluetooth/BluetoothAdapter","getDefaultAdapter","()Landroid/bluetooth/BluetoothAdapter;"); + if (checkException("BluetoothAdapter.getDefaultAdapter()", &adapter)) { + return; + } + // returns a Set<BluetoothDevice> + QAndroidJniObject pairedDevicesSet=adapter.callObjectMethod("getBondedDevices","()Ljava/util/Set;"); + if (checkException("BluetoothAdapter.getBondedDevices()", &pairedDevicesSet)) { + return; + } + jint size=pairedDevicesSet.callMethod<jint>("size"); + checkException("Set<BluetoothDevice>.size()", &pairedDevicesSet); + if (size > 0) { + // returns an Iterator<BluetoothDevice> + QAndroidJniObject iterator=pairedDevicesSet.callObjectMethod("iterator","()Ljava/util/Iterator;"); + if (checkException("Set<BluetoothDevice>.iterator()", &iterator)) { + return; + } + for (int i = 0; i < size; i++) { + // returns a BluetoothDevice + QAndroidJniObject dev=iterator.callObjectMethod("next","()Ljava/lang/Object;"); + if (checkException("Iterator<BluetoothDevice>.next()", &dev)) { + continue; + } + + result.address = QBluetoothAddress(dev.callObjectMethod("getAddress","()Ljava/lang/String;").toString()); + result.name = dev.callObjectMethod("getName", "()Ljava/lang/String;").toString(); + + btPairedDevices.append(result); + } + } +} + +bool BTDiscovery::checkException(const char* method, const QAndroidJniObject *obj) +{ + static QAndroidJniEnvironment env; + bool result = false; + + if (env->ExceptionCheck()) { + qCritical("Exception in %s", method); + env->ExceptionDescribe(); + env->ExceptionClear(); + result=true; + } + if (!(obj == NULL || obj->isValid())) { + qCritical("Invalid object returned by %s", method); + result=true; + } + return result; +} +#endif // Q_OS_ANDROID +#endif // BT_SUPPORT diff --git a/core/btdiscovery.h b/core/btdiscovery.h new file mode 100644 index 000000000..796be908a --- /dev/null +++ b/core/btdiscovery.h @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef BTDISCOVERY_H +#define BTDISCOVERY_H + +#include <QObject> +#include <QString> +#include <QLoggingCategory> +#if defined(BT_SUPPORT) +#include <QBluetoothLocalDevice> +#include <QBluetoothDeviceDiscoveryAgent> +#include <QBluetoothUuid> + +struct btVendorProduct { + QBluetoothDeviceInfo btdi; + int vendorIdx; + int productIdx; +}; + +#endif +#if defined(Q_OS_ANDROID) +#include <QAndroidJniObject> +#endif + +class BTDiscovery : public QObject { + Q_OBJECT + +public: + BTDiscovery(QObject *parent = NULL); + ~BTDiscovery(); + static BTDiscovery *instance(); + +#if defined(BT_SUPPORT) + struct btPairedDevice { + QBluetoothAddress address; + QString name; + }; + void btDeviceDiscovered(const QBluetoothDeviceInfo &device); +#if defined(Q_OS_ANDROID) + void getBluetoothDevices(); +#endif + QList<struct btVendorProduct> getBtDcs(); +#endif +private: + static BTDiscovery *m_instance; +#if defined(BT_SUPPORT) + QList<struct btVendorProduct> btDCs; +#endif +#if defined(Q_OS_ANDROID) + bool checkException(const char* method, const QAndroidJniObject* obj); +#endif + +#if defined(BT_SUPPORT) + QList<struct btPairedDevice> btPairedDevices; + QBluetoothLocalDevice localBtDevice; + QBluetoothDeviceDiscoveryAgent *discoveryAgent; +#endif + +signals: + void dcVendorChanged(); + void dcProductChanged(); + void dcBtChanged(); +}; + +#endif // BTDISCOVERY_H diff --git a/core/downloadfromdcthread.cpp b/core/downloadfromdcthread.cpp index 20f170125..bd8a637f1 100644 --- a/core/downloadfromdcthread.cpp +++ b/core/downloadfromdcthread.cpp @@ -99,8 +99,15 @@ void fill_computer_list() #endif } +DCDeviceData *DCDeviceData::m_instance = NULL; + DCDeviceData::DCDeviceData(QObject *parent) : QObject(parent) { + if (m_instance) { + qDebug() << "already have an instance of DCDevieData"; + return; + } + m_instance = this; memset(&data, 0, sizeof(data)); data.trip = nullptr; data.download_table = nullptr; @@ -108,6 +115,18 @@ DCDeviceData::DCDeviceData(QObject *parent) : QObject(parent) data.deviceid = 0; } +DCDeviceData *DCDeviceData::instance() +{ + if (!m_instance) + m_instance = new DCDeviceData(); + return m_instance; +} + +QStringList DCDeviceData::getProductListFromVendor(const QString &vendor) +{ + return productList[vendor]; +} + DCDeviceData * DownloadThread::data() { return m_data; @@ -222,3 +241,40 @@ device_data_t* DCDeviceData::internalData() { return &data; } + +int DCDeviceData::getDetectedVendorIndex() +{ +#if defined(BT_SUPPORT) + QList<btVendorProduct> btDCs = BTDiscovery::instance()->getBtDcs(); + if (!btDCs.isEmpty()) { + qDebug() << "getVendorIdx" << btDCs.first().vendorIdx; + return btDCs.first().vendorIdx; + } +#endif + return -1; +} + +int DCDeviceData::getDetectedProductIndex() +{ +#if defined(BT_SUPPORT) + QList<btVendorProduct> btDCs = BTDiscovery::instance()->getBtDcs(); + if (!btDCs.isEmpty()) { + qDebug() << "getProductIdx" << btDCs.first().productIdx; + return btDCs.first().productIdx; + } +#endif + return -1; +} + +QString DCDeviceData::getDetectedDeviceAddress() +{ +#if BT_SUPPORT + QList<btVendorProduct> btDCs = BTDiscovery::instance()->getBtDcs(); + if (!btDCs.isEmpty()) { + QString btAddr = btDCs.first().btdi.address().toString(); + qDebug() << "getBtAddress" << btAddr; + return btAddr; + } + return QString(); +#endif +} diff --git a/core/downloadfromdcthread.h b/core/downloadfromdcthread.h index bf203a85f..13cca1438 100644 --- a/core/downloadfromdcthread.h +++ b/core/downloadfromdcthread.h @@ -4,9 +4,11 @@ #include <QThread> #include <QMap> #include <QHash> +#include <QLoggingCategory> #include "dive.h" #include "libdivecomputer.h" +#include "core/btdiscovery.h" /* Helper object for access of Device Data in QML */ class DCDeviceData : public QObject { @@ -25,6 +27,7 @@ class DCDeviceData : public QObject { public: DCDeviceData(QObject *parent = nullptr); + static DCDeviceData *instance(); QString vendor() const; QString product() const; @@ -41,6 +44,11 @@ public: /* this needs to be a pointer to make the C-API happy */ device_data_t* internalData(); + Q_INVOKABLE QStringList getProductListFromVendor(const QString& vendor); + Q_INVOKABLE int getDetectedVendorIndex(); + Q_INVOKABLE int getDetectedProductIndex(); + Q_INVOKABLE QString getDetectedDeviceAddress(); + public slots: void setVendor(const QString& vendor); void setProduct(const QString& product); @@ -53,6 +61,7 @@ public slots: void setSaveDump(bool dumpMode); void setSaveLog(bool saveLog); private: + static DCDeviceData *m_instance; device_data_t data; }; @@ -64,7 +73,7 @@ public: DownloadThread(); void run() override; - DCDeviceData *data(); + Q_INVOKABLE DCDeviceData *data(); QString error; private: diff --git a/mobile-widgets/qml/DownloadFromDiveComputer.qml b/mobile-widgets/qml/DownloadFromDiveComputer.qml index b05c1fc9f..8b9f642ff 100644 --- a/mobile-widgets/qml/DownloadFromDiveComputer.qml +++ b/mobile-widgets/qml/DownloadFromDiveComputer.qml @@ -57,11 +57,11 @@ Kirigami.Page { id: comboVendor Layout.fillWidth: true model: vendorList - currentIndex: manager.getVendorIndex() + currentIndex: downloadThread.data().getDetectedVendorIndex() onCurrentTextChanged: { - comboProduct.model = manager.getDCListFromVendor(comboVendor.currentText) - if (currentIndex == manager.getVendorIndex()) - comboProduct.currentIndex = manager.getProductIndex() + comboProduct.model = downloadThread.data().getProductListFromVendor(comboVendor.currentText) + if (currentIndex == downloadThread.data().getDetectedVendorIndex()) + comboProduct.currentIndex = downloadThread.data().getDetectedProductIndex() } } Kirigami.Label { text: qsTr(" Dive Computer:") } @@ -74,7 +74,7 @@ Kirigami.Page { Kirigami.Label { text: qsTr("Bluetooth download:") } CheckBox { id: isBluetooth - checked: manager.getVendorIndex() != -1 + checked: downloadThread.data().getDetectedVendorIndex() != -1 } } diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 856dec03b..96bdab51e 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -94,28 +94,9 @@ QMLManager::QMLManager() : m_locationServiceEnabled(false), alreadySaving(false) { #if defined(BT_SUPPORT) - if (localBtDevice.isValid() && - localBtDevice.hostMode() == QBluetoothLocalDevice::HostConnectable) { - btPairedDevices.clear(); - QString localDeviceName = "localDevice " + localBtDevice.name() + " is valid, starting discovery"; - appendTextToLog(localDeviceName.toUtf8().data()); -#if defined(Q_OS_LINUX) - discoveryAgent = new QBluetoothDeviceDiscoveryAgent(this); - connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &QMLManager::btDeviceDiscovered); - discoveryAgent->start(); -#endif -#if defined(Q_OS_ANDROID) - getBluetoothDevices(); -#endif - for (int i = 0; i < btPairedDevices.length(); i++) { - qDebug() << "Paired =" << btPairedDevices[i].name << btPairedDevices[i].address.toString(); - } -#if defined(Q_OS_LINUX) - discoveryAgent->stop(); -#endif - } else { - appendTextToLog("localBtDevice isn't valid"); - } + // ensure that we start the BTDiscovery - this should be triggered by the export of the class + // to QML, but that doesn't seem to always work + BTDiscovery *btDiscovery = BTDiscovery::instance(); #endif m_instance = this; m_lastDevicePixelRatio = qApp->devicePixelRatio(); @@ -217,78 +198,6 @@ void QMLManager::mergeLocalRepo() process_dives(true, false); } -#if defined(BT_SUPPORT) - -extern void addBtUuid(QBluetoothUuid uuid); - -void QMLManager::btDeviceDiscovered(const QBluetoothDeviceInfo &device) -{ - btPairedDevice this_d; - this_d.address = device.address(); - this_d.name = device.name(); - btPairedDevices.append(this_d); - - QString newDevice = device.name(); - - // all the HW OSTC BT computers show up as "OSTC" + some other text, depending on model - if (newDevice.startsWith("OSTC")) - newDevice = "OSTC 3"; - QList<QBluetoothUuid> serviceUuids = device.serviceUuids(); - foreach (QBluetoothUuid id, serviceUuids) { - addBtUuid(id); - qDebug() << id.toByteArray(); - } - appendTextToLog("Found new device " + newDevice + " (" + device.address().toString() + ")"); - QString vendor, product; - foreach (vendor, productList.keys()) { - if (productList[vendor].contains(newDevice)) { - appendTextToLog("this could be a " + vendor + " " + - (newDevice == "OSTC 3" ? "OSTC family" : newDevice)); - struct btVendorProduct btVP; - btVP.btdi = device; - btVP.vendorIdx = vendorList.indexOf(vendor); - btVP.productIdx = productList[vendor].indexOf(newDevice); - qDebug() << "adding new btDCs entry" << newDevice << btVP.vendorIdx << btVP.productIdx; - btDCs << btVP; - } - } -} -#endif - -int QMLManager::getVendorIndex() -{ -#if defined(BT_SUPPORT) - if (!btDCs.isEmpty()) { - qDebug() << "getVendorIdx" << btDCs.first().vendorIdx; - return btDCs.first().vendorIdx; - } -#endif - return -1; -} - -int QMLManager::getProductIndex() -{ -#if defined(BT_SUPPORT) - if (!btDCs.isEmpty()) { - qDebug() << "getProductIdx" << btDCs.first().productIdx; - return btDCs.first().productIdx; - } -#endif - return -1; -} - -QString QMLManager::getBtAddress() -{ -#if BT_SUPPORT - if (!btDCs.isEmpty()) { - QString btAddr = btDCs.first().btdi.address().toString(); - qDebug() << "getBtAddress" << btAddr; - return btAddr; - } - return QString(); -#endif -} - void QMLManager::finishSetup() { // Initialize cloud credentials. @@ -1611,74 +1520,3 @@ void QMLManager::setShowPin(bool enable) m_showPin = enable; emit showPinChanged(); } - -QStringList QMLManager::getDCListFromVendor(const QString& vendor) -{ - return productList[vendor]; -} - -// Android: As Qt is not able to pull the pairing data from a device, i -// a lengthy discovery process is needed to see what devices are paired. On -// https://forum.qt.io/topic/46075/solved-bluetooth-list-paired-devices -// user s.frings74 does, however, present a solution to this using JNI. -// Currently, this code is taken "as is". - -void QMLManager::getBluetoothDevices() -{ -#if defined(Q_OS_ANDROID) && defined(BT_SUPPORT) - struct QMLManager::btPairedDevice result; - // Query via Android Java API. - - // returns a BluetoothAdapter - QAndroidJniObject adapter=QAndroidJniObject::callStaticObjectMethod("android/bluetooth/BluetoothAdapter","getDefaultAdapter","()Landroid/bluetooth/BluetoothAdapter;"); - if (checkException("BluetoothAdapter.getDefaultAdapter()", &adapter)) { - return; - } - // returns a Set<BluetoothDevice> - QAndroidJniObject pairedDevicesSet=adapter.callObjectMethod("getBondedDevices","()Ljava/util/Set;"); - if (checkException("BluetoothAdapter.getBondedDevices()", &pairedDevicesSet)) { - return; - } - jint size=pairedDevicesSet.callMethod<jint>("size"); - checkException("Set<BluetoothDevice>.size()", &pairedDevicesSet); - if (size > 0) { - // returns an Iterator<BluetoothDevice> - QAndroidJniObject iterator=pairedDevicesSet.callObjectMethod("iterator","()Ljava/util/Iterator;"); - if (checkException("Set<BluetoothDevice>.iterator()", &iterator)) { - return; - } - for (int i = 0; i < size; i++) { - // returns a BluetoothDevice - QAndroidJniObject dev=iterator.callObjectMethod("next","()Ljava/lang/Object;"); - if (checkException("Iterator<BluetoothDevice>.next()", &dev)) { - continue; - } - - result.address = QBluetoothAddress(dev.callObjectMethod("getAddress","()Ljava/lang/String;").toString()); - result.name = dev.callObjectMethod("getName", "()Ljava/lang/String;").toString(); - - btPairedDevices.append(result); - } -} -#endif -} - -#if defined(Q_OS_ANDROID) -bool QMLManager::checkException(const char* method, const QAndroidJniObject *obj) -{ - static QAndroidJniEnvironment env; - bool result = false; - - if (env->ExceptionCheck()) { - qCritical("Exception in %s", method); - env->ExceptionDescribe(); - env->ExceptionClear(); - result=true; - } - if (!(obj == NULL || obj->isValid())) { - qCritical("Invalid object returned by %s", method); - result=true; - } - return result; -} -#endif diff --git a/mobile-widgets/qmlmanager.h b/mobile-widgets/qmlmanager.h index b10ae9af2..a41ee509a 100644 --- a/mobile-widgets/qmlmanager.h +++ b/mobile-widgets/qmlmanager.h @@ -16,6 +16,7 @@ #include <QAndroidJniObject> #endif +#include "core/btdiscovery.h" #include "core/gpslocation.h" #include "qt-models/divelistmodel.h" @@ -121,18 +122,6 @@ public: QStringList cylinderInit() const; bool showPin() const; void setShowPin(bool enable); - Q_INVOKABLE QStringList getDCListFromVendor(const QString& vendor); - Q_INVOKABLE int getVendorIndex(); - Q_INVOKABLE int getProductIndex(); - Q_INVOKABLE QString getBtAddress(); -#if defined(BT_SUPPORT) - struct btPairedDevice { - QBluetoothAddress address; - QString name; - }; - void btDeviceDiscovered(const QBluetoothDeviceInfo &device); - void getBluetoothDevices(); -#endif public slots: void applicationStateChanged(Qt::ApplicationState state); @@ -216,22 +205,6 @@ private: bool currentGitLocalOnly; bool m_showPin; -#if defined(Q_OS_ANDROID) - bool checkException(const char* method, const QAndroidJniObject* obj); -#endif - -#if defined(BT_SUPPORT) - QList<struct btPairedDevice> btPairedDevices; - QBluetoothLocalDevice localBtDevice; - QBluetoothDeviceDiscoveryAgent *discoveryAgent; - struct btVendorProduct { - QBluetoothDeviceInfo btdi; - int vendorIdx; - int productIdx; - }; - QList<struct btVendorProduct> btDCs; -#endif - signals: void cloudUserNameChanged(); void cloudPasswordChanged(); |