From 6a80077e386e8a74940e81268055ff5ea0d97771 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Wed, 20 Mar 2019 14:22:57 -0700 Subject: libqtappfw: bluetooth: add BluetoothModel support Switch from using single threaded and incorrect QML processing of ListViews to using models provided from libqtappfw Bug-AGL: SPEC-2270 Change-Id: I83f02ab104a18ade95dfd172902e32a808c3d897 Signed-off-by: Matt Ranostay --- bluetooth/CMakeLists.txt | 4 +- bluetooth/bluetooth.cpp | 47 +++++++++-- bluetooth/bluetooth.h | 10 +-- bluetooth/bluetoothmodel.cpp | 192 +++++++++++++++++++++++++++++++++++++++++++ bluetooth/bluetoothmodel.h | 81 ++++++++++++++++++ 5 files changed, 319 insertions(+), 15 deletions(-) create mode 100644 bluetooth/bluetoothmodel.cpp create mode 100644 bluetooth/bluetoothmodel.h diff --git a/bluetooth/CMakeLists.txt b/bluetooth/CMakeLists.txt index c068a9f..cdefb0d 100644 --- a/bluetooth/CMakeLists.txt +++ b/bluetooth/CMakeLists.txt @@ -1,2 +1,2 @@ -add_headers(bluetooth.h bluetoothmessage.h) -add_sources(bluetooth.cpp bluetoothmessage.cpp) +add_headers(bluetooth.h bluetoothmessage.h bluetoothmodel.h) +add_sources(bluetooth.cpp bluetoothmessage.cpp bluetoothmodel.cpp) diff --git a/bluetooth/bluetooth.cpp b/bluetooth/bluetooth.cpp index 25ff16e..eb00df4 100644 --- a/bluetooth/bluetooth.cpp +++ b/bluetooth/bluetooth.cpp @@ -20,15 +20,27 @@ #include "bluetoothmessage.h" #include "responsemessage.h" -Bluetooth::Bluetooth (QUrl &url, QObject * parent) : +Bluetooth::Bluetooth (QUrl &url, QQmlContext *context, QObject * parent) : QObject(parent), + m_context(context), m_mloop(nullptr) { m_mloop = new MessageEngine(url); + m_bluetooth = new BluetoothModel(); QObject::connect(m_mloop, &MessageEngine::connected, this, &Bluetooth::onConnected); QObject::connect(m_mloop, &MessageEngine::disconnected, this, &Bluetooth::onDisconnected); QObject::connect(m_mloop, &MessageEngine::messageReceived, this, &Bluetooth::onMessageReceived); + BluetoothModelFilter *m_model = new BluetoothModelFilter(); + m_model->setSourceModel(m_bluetooth); + m_model->setFilterFixedString("true"); + context->setContextProperty("BluetoothPairedModel", m_model); + + m_model = new BluetoothModelFilter(); + m_model->setSourceModel(m_bluetooth); + m_model->setFilterFixedString("false"); + context->setContextProperty("BluetoothDiscoveryModel", m_model); + uuids.insert("a2dp", "0000110a-0000-1000-8000-00805f9b34fb"); uuids.insert("avrcp", "0000110e-0000-1000-8000-00805f9b34fb"); uuids.insert("hfp", "0000111f-0000-1000-8000-00805f9b34fb"); @@ -192,23 +204,42 @@ void Bluetooth::onDisconnected() } } +void Bluetooth::populateDeviceList(QJsonObject data) +{ + QJsonArray devices = data.value("devices").toArray(); + + m_bluetooth->removeAllDevices(); + + for (auto value : devices) { + BluetoothDevice *device = m_bluetooth->updateDeviceProperties(nullptr, value.toObject()); + m_bluetooth->addDevice(device); + } +} + void Bluetooth::processDeviceChangesEvent(QJsonObject data) { QString action = data.value("action").toString(); + QString id = data.value("device").toString(); - if (action == "added") - emit deviceAddedEvent(data); - else if (action == "changed") { + if (action == "added") { + BluetoothDevice *device = m_bluetooth->updateDeviceProperties(nullptr, data); + m_bluetooth->addDevice(device); + } else if (action == "changed") { auto powered = data.find("powered").value(); if (powered.isBool()) { m_power = powered.toBool(); + if (!m_power) + m_bluetooth->removeAllDevices(); emit powerChanged(m_power); } else { - emit deviceUpdatedEvent(data); + BluetoothDevice *device = m_bluetooth->getDevice(id); + m_bluetooth->updateDeviceProperties(device, data); } - } else if (action == "removed") - emit deviceRemovedEvent(data); + } else if (action == "removed") { + BluetoothDevice *device = m_bluetooth->getDevice(id); + m_bluetooth->removeDevice(device); + } } void Bluetooth::onMessageReceived(MessageType type, Message *msg) @@ -226,7 +257,7 @@ void Bluetooth::onMessageReceived(MessageType type, Message *msg) ResponseMessage *tmsg = qobject_cast(msg); if (tmsg->requestVerb() == "managed_objects") { - emit deviceListEvent(tmsg->replyData()); + populateDeviceList(tmsg->replyData()); } else if (tmsg->requestVerb() == "adapter_state") { m_power = tmsg->replyData().value("powered").toBool(); emit powerChanged(m_power); diff --git a/bluetooth/bluetooth.h b/bluetooth/bluetooth.h index 0f38381..6cef0ce 100644 --- a/bluetooth/bluetooth.h +++ b/bluetooth/bluetooth.h @@ -22,6 +22,7 @@ #include #include "messageengine.h" +#include "bluetoothmodel.h" class Bluetooth : public QObject { @@ -30,7 +31,7 @@ class Bluetooth : public QObject Q_PROPERTY(bool discoverable READ discoverable WRITE setDiscoverable NOTIFY discoverableChanged) public: - explicit Bluetooth(QUrl &url, QObject * parent = Q_NULLPTR); + explicit Bluetooth(QUrl &url, QQmlContext *context, QObject * parent = Q_NULLPTR); virtual ~Bluetooth(); void setPower(bool); @@ -60,16 +61,15 @@ class Bluetooth : public QObject void connectionEvent(QJsonObject data); void requestConfirmationEvent(QJsonObject data); - void deviceAddedEvent(QJsonObject data); - void deviceRemovedEvent(QJsonObject data); - void deviceUpdatedEvent(QJsonObject data); - void deviceListEvent(QJsonObject data); private: MessageEngine *m_mloop; + QQmlContext *m_context; + BluetoothModel *m_bluetooth; void send_command(QString, QJsonObject); void set_discovery_filter(); void discovery_command(bool); + void populateDeviceList(QJsonObject data); void processDeviceChangesEvent(QJsonObject data); // slots diff --git a/bluetooth/bluetoothmodel.cpp b/bluetooth/bluetoothmodel.cpp new file mode 100644 index 0000000..f6c3d09 --- /dev/null +++ b/bluetooth/bluetoothmodel.cpp @@ -0,0 +1,192 @@ +#include "bluetoothmodel.h" +#include + +BluetoothDevice::BluetoothDevice(const QString &id, + const QString &address, + const QString &name, + const bool paired, + const bool connected) + : m_id(id), m_address(address), m_name(name), m_paired(paired), + m_connected(connected) +{ +} + +QString BluetoothDevice::id() const +{ + return m_id; +} + +QString BluetoothDevice::address() const +{ + return m_address; +} + +QString BluetoothDevice::name() const +{ + return m_name; +} + +bool BluetoothDevice::paired() const +{ + return m_paired; +} + +bool BluetoothDevice::connected() const +{ + return m_connected; +} + +void BluetoothDevice::setId(const QString id) +{ + m_id = id; +} + +void BluetoothDevice::setAddress(const QString address) +{ + m_address = address; +} + +void BluetoothDevice::setName(const QString name) +{ + m_name = name; +} + +void BluetoothDevice::setPaired(const bool paired) +{ + m_paired = paired; +} + +void BluetoothDevice::setConnected(const bool connected) +{ + m_connected = connected; +} + +BluetoothModel::BluetoothModel(QObject *parent) + : QAbstractListModel(parent) +{ +} + +void BluetoothModel::addDevice(BluetoothDevice *device) +{ + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_devices << device; + endInsertRows(); +} + +void BluetoothModel::removeDevice(BluetoothDevice *device) +{ + int row = m_devices.indexOf(device); + beginRemoveRows(QModelIndex(), row, row); + m_devices.removeAt(row); + endRemoveRows(); + delete device; +} + +void BluetoothModel::removeAllDevices() +{ + beginRemoveRows(QModelIndex(), 0, m_devices.count() - 1); + qDeleteAll(m_devices.begin(), m_devices.end()); + m_devices.clear(); + endRemoveRows(); +} + +int BluetoothModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return m_devices.count(); +} + +QVariant BluetoothModel::data(const QModelIndex &index, int role) const +{ + if (index.row() < 0 || index.row() >= m_devices.count()) + return QVariant(); + + const BluetoothDevice *device = m_devices[index.row()]; + + switch (role) { + case IdRole: + return device->id(); + case AddressRole: + return device->address(); + case NameRole: + return device->name(); + case PairedRole: + return device->paired(); + case ConnectedRole: + return device->connected(); + } + + return QVariant(); +} + +QHash BluetoothModel::roleNames() const { + QHash roles; + roles[IdRole] = "id"; + roles[AddressRole] = "address"; + roles[NameRole] = "name"; + roles[PairedRole] = "paired"; + roles[ConnectedRole] = "connected"; + + return roles; +} + +QModelIndex BluetoothModel::indexOf(BluetoothDevice *device) +{ + int row = m_devices.indexOf(device); + + return index(row); +} + +BluetoothDevice *BluetoothModel::getDevice(QString id) +{ + for (auto device : m_devices) { + if (device->id() == id) + return device; + } + + return nullptr; +} + +BluetoothDevice *BluetoothModel::updateDeviceProperties(BluetoothDevice *device, QJsonObject data) +{ + QJsonObject properties = data.value("properties").toObject(); + QString id = data.value("device").toString(); + QString address = properties.value("address").toString(); + QString name = properties.value("name").toString(); + bool paired = properties.value("paired").toBool(); + bool connected = properties.value("connected").toBool(); + + if (device == nullptr) + return new BluetoothDevice(id, address, name, paired, connected); + + device->setId(id); + + if (!address.isEmpty()) + device->setAddress(address); + + if (!name.isEmpty()) + device->setName(name); + + if (properties.contains("paired")) + device->setPaired(paired); + + if (properties.contains("connected")) + device->setConnected(connected); + + emit dataChanged(indexOf(device), indexOf(device)); + + return device; +} + +BluetoothModelFilter::BluetoothModelFilter(QObject *parent) : QSortFilterProxyModel(parent) +{ +} + +bool BluetoothModelFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + BluetoothModel *model = qobject_cast(sourceModel()); + QModelIndex index = model->index(sourceRow); + bool paired = model->data(index, BluetoothModel::BluetoothRoles::PairedRole).toBool(); + + return ((paired ? "true" : "false") == filterRegExp().pattern()); +} diff --git a/bluetooth/bluetoothmodel.h b/bluetooth/bluetoothmodel.h new file mode 100644 index 0000000..0fc07aa --- /dev/null +++ b/bluetooth/bluetoothmodel.h @@ -0,0 +1,81 @@ +#ifndef BLUETOOTH_MODEL_H +#define BLUETOOTH_MODEL_H + +#include +#include +#include +#include +#include + +class BluetoothDevice +{ + public: + BluetoothDevice(const QString &id, + const QString &address, + const QString &name, + const bool paired, + const bool connected); + QString id() const; + QString address() const; + QString name() const; + bool paired() const; + bool connected() const; + void setId(const QString id); + void setAddress(const QString address); + void setName(const QString name); + void setPaired(const bool paired); + void setConnected(const bool connected); + + private: + QString m_id; + QString m_address; + QString m_name; + bool m_paired; + bool m_connected; +}; + +class BluetoothModel : public QAbstractListModel +{ + Q_OBJECT + + public: + enum BluetoothRoles { + IdRole = Qt::UserRole + 1, + AddressRole, + NameRole, + PairedRole, + ConnectedRole + }; + + BluetoothModel(QObject *parent = Q_NULLPTR); + + void addDevice(BluetoothDevice *device); + void removeDevice(BluetoothDevice *device); + void removeAllDevices(); + BluetoothDevice *getDevice(QString address); + BluetoothDevice *updateDeviceProperties(BluetoothDevice *device, QJsonObject data); + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + signals: + void propertiesChanged(int connected); + + protected: + QHash roleNames() const; + + private: + QList m_devices; + QModelIndex indexOf(BluetoothDevice *device); +}; + +class BluetoothModelFilter : public QSortFilterProxyModel +{ + Q_OBJECT + + public: + BluetoothModelFilter(QObject *parent = nullptr); + + protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; +}; +#endif // BLUETOOTH_MODEL_H -- cgit 1.2.3-korg