diff --git a/bluetoothbaseclass.cpp b/bluetoothbaseclass.cpp index ad16e48..6648932 100644 --- a/bluetoothbaseclass.cpp +++ b/bluetoothbaseclass.cpp @@ -1,33 +1,26 @@ #include "bluetoothbaseclass.h" -BluetoothBaseClass::BluetoothBaseClass(QObject *parent) : QObject(parent) +BluetoothBaseClass::BluetoothBaseClass(QObject *parent) : + QObject{parent} { } -QString BluetoothBaseClass::error() const -{ - return m_error; -} - -QString BluetoothBaseClass::info() const -{ - return m_info; -} - void BluetoothBaseClass::setError(const QString &error) { - if (m_error != error) { - m_error = error; - emit errorChanged(); - } + if (m_error == error) + return; + + m_error = error; + emit errorChanged(); } void BluetoothBaseClass::setInfo(const QString &info) { - if (m_info != info) { - m_info = info; - emit infoChanged(); - } + if (m_info == info) + return; + + m_info = info; + emit infoChanged(); } void BluetoothBaseClass::clearMessages() diff --git a/bluetoothbaseclass.h b/bluetoothbaseclass.h index 49568ab..c4992a7 100644 --- a/bluetoothbaseclass.h +++ b/bluetoothbaseclass.h @@ -1,6 +1,6 @@ -#ifndef BLUETOOTHBASECLASS_H -#define BLUETOOTHBASECLASS_H +#pragma once +// Qt includes #include class BluetoothBaseClass : public QObject @@ -12,10 +12,10 @@ class BluetoothBaseClass : public QObject public: explicit BluetoothBaseClass(QObject *parent = nullptr); - QString error() const; + QString error() const { return m_error; } void setError(const QString& error); - QString info() const; + QString info() const { return m_info; } void setInfo(const QString& info); void clearMessages(); @@ -28,5 +28,3 @@ private: QString m_error; QString m_info; }; - -#endif // BLUETOOTHBASECLASS_H diff --git a/bobbycar-app.pro b/bobbycar-app.pro index 04e0f14..abbdcd7 100644 --- a/bobbycar-app.pro +++ b/bobbycar-app.pro @@ -12,7 +12,8 @@ HEADERS += \ bluetoothbaseclass.h \ settings.h -SOURCES += main.cpp \ +SOURCES += \ + main.cpp \ connectionhandler.cpp \ deviceinfo.cpp \ devicefinder.cpp \ @@ -20,12 +21,17 @@ SOURCES += main.cpp \ bluetoothbaseclass.cpp \ settings.cpp -RESOURCES += qml.qrc \ +RESOURCES += \ + qml.qrc \ images.qrc # Additional import path used to resolve QML modules in Qt Creator's code model QML_IMPORT_PATH = +CONFIG += qmltypes +QML_IMPORT_NAME = bobbycar +QML_IMPORT_MAJOR_VERSION = 1 + DISTFILES += \ android/AndroidManifest.xml \ android/build.gradle \ diff --git a/devicefinder.cpp b/devicefinder.cpp index d4e2c80..e48c778 100644 --- a/devicefinder.cpp +++ b/devicefinder.cpp @@ -1,112 +1,190 @@ #include "devicefinder.h" + +// system includes +#include + +// local includes #include "devicehandler.h" -#include "deviceinfo.h" -DeviceFinder::DeviceFinder(DeviceHandler *handler, QObject *parent): - BluetoothBaseClass(parent), - m_deviceHandler(handler) +DeviceFinder::DeviceFinder(QObject *parent): + QAbstractItemModel{parent}, + m_deviceDiscoveryAgent{this} { - //! [devicediscovery-1] - m_deviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(this); - m_deviceDiscoveryAgent->setLowEnergyDiscoveryTimeout(5000); + m_deviceDiscoveryAgent.setLowEnergyDiscoveryTimeout(5000); - connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &DeviceFinder::addDevice); - connect(m_deviceDiscoveryAgent, static_cast(&QBluetoothDeviceDiscoveryAgent::error), + connect(&m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &DeviceFinder::addDevice); + connect(&m_deviceDiscoveryAgent, qOverload(&QBluetoothDeviceDiscoveryAgent::error), this, &DeviceFinder::scanError); - connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &DeviceFinder::scanFinished); - connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::canceled, this, &DeviceFinder::scanFinished); - //! [devicediscovery-1] + connect(&m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &DeviceFinder::scanFinished); + connect(&m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::canceled, this, &DeviceFinder::scanFinished); } -DeviceFinder::~DeviceFinder() +QModelIndex DeviceFinder::index(int row, int column, const QModelIndex &parent) const { - qDeleteAll(m_devices); - m_devices.clear(); + Q_UNUSED(parent) + return createIndex(row, column); +} + +QModelIndex DeviceFinder::parent(const QModelIndex &child) const +{ + Q_UNUSED(child) + return {}; +} + +int DeviceFinder::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return m_devices.size(); +} + +int DeviceFinder::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return 1; +} + +QVariant DeviceFinder::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + { + qWarning() << "invalid index"; + return {}; + } + + if (index.row() < 0 || + index.row() >= m_devices.size() || + index.column() != 0) + { + qWarning() << "index out of bounds" << index; + return {}; + } + + const auto &device = m_devices.at(index.row()); + + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + case Qt::UserRole + 1: + return device.name(); + case Qt::UserRole + 2: + return device.address().toString(); + } + + return {}; +} + +QMap DeviceFinder::itemData(const QModelIndex &index) const +{ + if (!index.isValid()) + { + qWarning() << "invalid index"; + return {}; + } + + if (index.row() < 0 || + index.row() >= m_devices.size() || + index.column() != 0) + { + qWarning() << "index out of bounds" << index; + return {}; + } + + const auto &device = m_devices.at(index.row()); + + return QMap { + { Qt::UserRole + 1, device.name() }, + { Qt::UserRole + 2, device.address().toString() } + }; +} + +QHash DeviceFinder::roleNames() const +{ + return QHash { + { Qt::UserRole + 1, QByteArrayLiteral("deviceName") }, + { Qt::UserRole + 2, QByteArrayLiteral("deviceAddress") } + }; } void DeviceFinder::startSearch() { clearMessages(); - m_deviceHandler->setDevice(nullptr); - qDeleteAll(m_devices); + + beginResetModel(); m_devices.clear(); + endResetModel(); - emit devicesChanged(); - - //! [devicediscovery-2] - m_deviceDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); - //! [devicediscovery-2] + m_deviceDiscoveryAgent.start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); emit scanningChanged(); setInfo(tr("Scanning for devices...")); } -//! [devicediscovery-3] void DeviceFinder::addDevice(const QBluetoothDeviceInfo &device) { // If device is LowEnergy-device, add it to the list - if (device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) { - //m_devices.append(new DeviceInfo(device)); - auto info = new DeviceInfo(device); - if(info->getName().contains("bobby")) { // Only add devices with "bobby" in device name to list; (filter) - m_devices.append(info); - setInfo(tr("Low Energy device found. Scanning more...")); - } -//! [devicediscovery-3] - emit devicesChanged(); -//! [devicediscovery-4] - } - //... + if (!(device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration)) + return; + + //if (!device.name().contains("bobby")) + // return; + + beginInsertRows({}, m_devices.size(), m_devices.size()); + m_devices.push_back(device); + endInsertRows(); + + setInfo(tr("Low Energy device found. Scanning more...")); } -//! [devicediscovery-4] void DeviceFinder::scanError(QBluetoothDeviceDiscoveryAgent::Error error) { - if (error == QBluetoothDeviceDiscoveryAgent::PoweredOffError) + switch (error) + { + case QBluetoothDeviceDiscoveryAgent::PoweredOffError: setError(tr("The Bluetooth adaptor is powered off.")); - else if (error == QBluetoothDeviceDiscoveryAgent::InputOutputError) + break; + case QBluetoothDeviceDiscoveryAgent::InputOutputError: setError(tr("Writing or reading from the device resulted in an error.")); - else + break; + default: setError(tr("An unknown error has occurred.")); + } } void DeviceFinder::scanFinished() { - if (m_devices.isEmpty()) + if (m_devices.empty()) setError(tr("No Low Energy devices found.")); else setInfo(tr("Scanning done.")); emit scanningChanged(); - emit devicesChanged(); } void DeviceFinder::connectToService(const QString &address) { - m_deviceDiscoveryAgent->stop(); + m_deviceDiscoveryAgent.stop(); - DeviceInfo *currentDevice = nullptr; - for (QObject *entry : qAsConst(m_devices)) { - auto device = qobject_cast(entry); - if (device && device->getAddress() == address ) { - currentDevice = device; - break; - } + auto iter = std::find_if(std::cbegin(m_devices), std::cend(m_devices), [&address](const QBluetoothDeviceInfo &device){ + return device.address().toString() == address; + }); + + if (iter == std::cend(m_devices)) + { + qWarning() << "could not find address" << address; + setError(tr("could not find address %0").arg(address)); + return; } - if (currentDevice) - m_deviceHandler->setDevice(currentDevice); + if (!m_handler) + { + qWarning() << "no valid handler!"; + setError(tr("no valid handler!")); + return; + } + + m_handler->setDevice(*iter); clearMessages(); } - -bool DeviceFinder::scanning() const -{ - return m_deviceDiscoveryAgent->isActive(); -} - -QVariant DeviceFinder::devices() -{ - return QVariant::fromValue(m_devices); -} diff --git a/devicefinder.h b/devicefinder.h index 2d6ecf6..88223df 100644 --- a/devicefinder.h +++ b/devicefinder.h @@ -1,48 +1,74 @@ -#ifndef DEVICEFINDER_H -#define DEVICEFINDER_H +#pragma once -#include "bluetoothbaseclass.h" +// system includes +#include +#include +// Qt includes +#include #include #include #include #include +#include - -class DeviceInfo; +// forward declares class DeviceHandler; -class DeviceFinder: public BluetoothBaseClass +class DeviceFinder : public QAbstractItemModel { Q_OBJECT - Q_PROPERTY(bool scanning READ scanning NOTIFY scanningChanged) - Q_PROPERTY(QVariant devices READ devices NOTIFY devicesChanged) + Q_PROPERTY(DeviceHandler* handler READ handler WRITE setHandler NOTIFY handlerChanged) + Q_PROPERTY(QString error READ error WRITE setError NOTIFY errorChanged) + Q_PROPERTY(QString info READ info WRITE setInfo NOTIFY infoChanged) + QML_ELEMENT public: - DeviceFinder(DeviceHandler *handler, QObject *parent = nullptr); - ~DeviceFinder(); + DeviceFinder(QObject *parent = nullptr); - bool scanning() const; - QVariant devices(); + bool scanning() const { return m_deviceDiscoveryAgent.isActive(); } + + DeviceHandler* handler() { return m_handler; } + const DeviceHandler* handler() const { return m_handler; } + void setHandler(DeviceHandler* handler) { if (m_handler == handler) return; m_handler = handler; emit handlerChanged(); } + + QString error() const { return m_error; } + void setError(const QString& error) { if (m_error == error) return; m_error = error; emit errorChanged(); } + + QString info() const { return m_info; } + void setInfo(const QString& info) { if (m_info == info) return; m_info = info; emit infoChanged(); } + + void clearMessages() { setInfo(""); setError(""); } + + // QAbstractItemModel interface + QModelIndex index(int row, int column, const QModelIndex &parent) const override; + QModelIndex parent(const QModelIndex &child) const override; + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + QMap itemData(const QModelIndex &index) const override; + QHash roleNames() const override; + +signals: + void scanningChanged(); + void handlerChanged(); + void errorChanged(); + void infoChanged(); public slots: void startSearch(); void connectToService(const QString &address); private slots: - void addDevice(const QBluetoothDeviceInfo&); + void addDevice(const QBluetoothDeviceInfo &); void scanError(QBluetoothDeviceDiscoveryAgent::Error error); void scanFinished(); -signals: - void scanningChanged(); - void devicesChanged(); - private: - DeviceHandler *m_deviceHandler; - QBluetoothDeviceDiscoveryAgent *m_deviceDiscoveryAgent; - QList m_devices; + DeviceHandler *m_handler{}; + QBluetoothDeviceDiscoveryAgent m_deviceDiscoveryAgent; + std::vector m_devices; + QString m_error; + QString m_info; }; - -#endif // DEVICEFINDER_H diff --git a/devicehandler.cpp b/devicehandler.cpp index 3aad29c..432168a 100644 --- a/devicehandler.cpp +++ b/devicehandler.cpp @@ -27,7 +27,7 @@ DeviceHandler::DeviceHandler(QObject *parent) : { } -void DeviceHandler::setDevice(DeviceInfo *device) +void DeviceHandler::setDevice(const QBluetoothDeviceInfo &device) { clearMessages(); m_currentDevice = device; @@ -41,14 +41,12 @@ void DeviceHandler::setDevice(DeviceInfo *device) } // Create new controller and connect it if device available - if (m_currentDevice) + if (m_currentDevice.isValid()) { // Make connections - //! [Connect-Signals-1] - m_control = QLowEnergyController::createCentral(m_currentDevice->getDevice(), this); - //! [Connect-Signals-1] + m_control = QLowEnergyController::createCentral(m_currentDevice, this); m_control->setRemoteAddressType(m_addressType); - //! [Connect-Signals-2] + connect(m_control, &QLowEnergyController::serviceDiscovered, this, &DeviceHandler::serviceDiscovered); connect(m_control, &QLowEnergyController::discoveryFinished, @@ -69,13 +67,13 @@ void DeviceHandler::setDevice(DeviceInfo *device) // Connect m_control->connectToDevice(); - //! [Connect-Signals-2] } } void DeviceHandler::setAddressType(AddressType type) { - switch (type) { + switch (type) + { case DeviceHandler::AddressType::PublicAddress: m_addressType = QLowEnergyController::PublicAddress; break; @@ -351,6 +349,7 @@ void DeviceHandler::confirmedDescriptorWrite(const QLowEnergyDescriptor &d, cons void DeviceHandler::confirmedCharacteristicWrite(const QLowEnergyCharacteristic &info, const QByteArray &value) { + Q_UNUSED(value) qDebug() << "confirmedCharacteristicWrite"; if (info == m_remotecontrolCharacteristic) diff --git a/devicehandler.h b/devicehandler.h index 8f7ae8e..7d3a39f 100644 --- a/devicehandler.h +++ b/devicehandler.h @@ -1,15 +1,15 @@ -#ifndef DEVICEHANDLER_H -#define DEVICEHANDLER_H - -#include "bluetoothbaseclass.h" +#pragma once +// Qt includes #include #include #include - #include #include +// local includes +#include "bluetoothbaseclass.h" + class DeviceInfo; class DeviceHandler : public BluetoothBaseClass @@ -49,7 +49,7 @@ public: DeviceHandler(QObject *parent = nullptr); - void setDevice(DeviceInfo *device); + void setDevice(const QBluetoothDeviceInfo &device); void setAddressType(AddressType type); AddressType addressType() const; @@ -131,7 +131,7 @@ private: QLowEnergyService *m_service = nullptr; QLowEnergyDescriptor m_notificationDescLivestats; QLowEnergyCharacteristic m_remotecontrolCharacteristic; - DeviceInfo *m_currentDevice{}; + QBluetoothDeviceInfo m_currentDevice; bool m_foundBobbycarService{}; @@ -161,5 +161,3 @@ private: bool m_waitingForWrite{}; }; - -#endif // DEVICEHANDLER_H diff --git a/main.cpp b/main.cpp index daec7b9..5c3f6a9 100644 --- a/main.cpp +++ b/main.cpp @@ -15,13 +15,11 @@ int main(int argc, char *argv[]) ConnectionHandler connectionHandler; DeviceHandler deviceHandler; - DeviceFinder deviceFinder(&deviceHandler); qmlRegisterUncreatableType("Shared", 1, 0, "AddressType", "Enum is not a type"); QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("connectionHandler", &connectionHandler); - engine.rootContext()->setContextProperty("deviceFinder", &deviceFinder); engine.rootContext()->setContextProperty("deviceHandler", &deviceHandler); engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml"))); diff --git a/qml/Connect.qml b/qml/Connect.qml index a5e99c5..fe0ea77 100644 --- a/qml/Connect.qml +++ b/qml/Connect.qml @@ -1,8 +1,12 @@ import QtQuick 2.15 import Shared 1.0 - +import bobbycar 1.0 GamePage { + DeviceFinder { + id: deviceFinder + handler: deviceHandler + } function init() { deviceFinder.startSearch() @@ -49,7 +53,7 @@ GamePage { anchors.right: parent.right anchors.bottom: parent.bottom anchors.top: title.bottom - model: deviceFinder.devices + model: deviceFinder clip: true delegate: Rectangle { @@ -61,15 +65,15 @@ GamePage { MouseArea { anchors.fill: parent onClicked: { - deviceFinder.connectToService(modelData.deviceAddress); + deviceFinder.connectToService(deviceAddress); app.showPage("Livedata.qml") } } Text { - id: device + //id: device font.pixelSize: GameSettings.smallFontSize - text: modelData.deviceName + text: deviceName anchors.top: parent.top anchors.topMargin: parent.height * 0.1 anchors.leftMargin: parent.height * 0.1 @@ -78,9 +82,9 @@ GamePage { } Text { - id: deviceAddress + //id: deviceAddress font.pixelSize: GameSettings.smallFontSize - text: modelData.deviceAddress + text: deviceAddress anchors.bottom: parent.bottom anchors.bottomMargin: parent.height * 0.1 anchors.rightMargin: parent.height * 0.1