Purpose

devicesmodel.cpp
1/*
2 SPDX-FileCopyrightText: 2024 Nicolas Fella <nicolas.fella@gmx.de>
3
4 SPDX-License-Identifier: LGPL-2.1-or-later
5*/
6
7#include "devicesmodel.h"
8
9#include <QDBusPendingReply>
10#include <QDBusServiceWatcher>
11#include <QString>
12
13#include "DaemonDbusInterface.h"
14#include "DeviceDbusInterface.h"
15
16DevicesModel::DevicesModel(QObject *parent)
17 : QAbstractListModel(parent)
18 , m_daemonInterface(
19 new OrgKdeKdeconnectDaemonInterface(QStringLiteral("org.kde.kdeconnect"), QStringLiteral("/modules/kdeconnect"), QDBusConnection::sessionBus(), this))
20{
21 connect(m_daemonInterface, &OrgKdeKdeconnectDaemonInterface::deviceAdded, this, &DevicesModel::deviceAdded);
22 connect(m_daemonInterface, &OrgKdeKdeconnectDaemonInterface::deviceVisibilityChanged, this, &DevicesModel::deviceUpdated);
23 connect(m_daemonInterface, &OrgKdeKdeconnectDaemonInterface::deviceRemoved, this, &DevicesModel::deviceRemoved);
24
25 QDBusServiceWatcher *watcher =
26 new QDBusServiceWatcher(QStringLiteral("org.kde.kdeconnect"), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this);
27 connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, &DevicesModel::loadDeviceList);
28 connect(watcher, &QDBusServiceWatcher::serviceUnregistered, this, &DevicesModel::clearDevices);
29
30 loadDeviceList();
31}
32
33QHash<int, QByteArray> DevicesModel::roleNames() const
34{
36 names.insert(NameModelRole, "name");
37 names.insert(IconNameRole, "iconName");
38 names.insert(IdRole, "deviceId");
39 return names;
40}
41
42DevicesModel::~DevicesModel() = default;
43
44int DevicesModel::rowForDevice(const QString &id) const
45{
46 for (int i = 0, c = m_devices.size(); i < c; ++i) {
47 if (m_devices[i].id == id) {
48 return i;
49 }
50 }
51 return -1;
52}
53
54void DevicesModel::deviceAdded(const QString &id)
55{
56 if (rowForDevice(id) >= 0) {
57 Q_ASSERT_X(false, "deviceAdded", "Trying to add a device twice");
58 return;
59 }
60
61 auto dev = std::make_unique<OrgKdeKdeconnectDeviceInterface>(QStringLiteral("org.kde.kdeconnect"),
62 QStringLiteral("/modules/kdeconnect/devices/") + id,
64 this);
65 Q_ASSERT(dev->isValid());
66
67 if (!dev->isPaired() || !dev->isReachable()) {
68 return;
69 }
70
71 beginInsertRows(QModelIndex(), m_devices.size(), m_devices.size());
72 addDevice({id, std::move(dev)});
74}
75
76void DevicesModel::deviceRemoved(const QString &id)
77{
78 int row = rowForDevice(id);
79 if (row >= 0) {
80 beginRemoveRows(QModelIndex(), row, row);
81 m_devices.erase(m_devices.begin() + row);
83 }
84}
85
86void DevicesModel::deviceUpdated(const QString &id)
87{
88 int row = rowForDevice(id);
89
90 if (row < 0) {
91 deviceAdded(id);
92 } else {
93 auto *dev = m_devices[row].interface.get();
94
95 if (dev->isPaired() && dev->isReachable()) {
96 const QModelIndex idx = index(row);
97 Q_EMIT dataChanged(idx, idx);
98 } else {
99 beginRemoveRows(QModelIndex(), row, row);
100 m_devices.erase(m_devices.begin() + row);
102 }
103 }
104}
105
106void DevicesModel::loadDeviceList()
107{
108 if (!m_daemonInterface->isValid()) {
109 clearDevices();
110 return;
111 }
112
113 QDBusPendingReply<QStringList> call = m_daemonInterface->devices(true /*onlyReachable*/, true /*onlyPaired*/);
114 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this);
115
116 QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &DevicesModel::gotDeviceList);
117}
118
119void DevicesModel::gotDeviceList(QDBusPendingCallWatcher *watcher)
120{
121 watcher->deleteLater();
122 clearDevices();
123 QDBusPendingReply<QStringList> pendingDeviceIds = *watcher;
124 if (pendingDeviceIds.isError()) {
125 qWarning() << "Error while loading device list" << pendingDeviceIds.error().message();
126 return;
127 }
128
129 Q_ASSERT(m_devices.empty());
130 const QStringList deviceIds = pendingDeviceIds.value();
131
132 if (deviceIds.isEmpty())
133 return;
134
135 beginInsertRows(QModelIndex(), 0, deviceIds.count() - 1);
136 for (const QString &id : deviceIds) {
137 auto interface = std::make_unique<OrgKdeKdeconnectDeviceInterface>(QStringLiteral("org.kde.kdeconnect"),
138 QStringLiteral("/modules/kdeconnect/devices/") + id,
139 QDBusConnection::sessionBus(),
140 this);
141
142 if (interface->isPaired() && interface->isReachable()) {
143 addDevice({id, std::move(interface)});
144 }
145 }
147}
148
149void DevicesModel::addDevice(DeviceInterface &&dev)
150{
151 connect(dev.interface.get(), &OrgKdeKdeconnectDeviceInterface::nameChanged, this, [this, id = dev.id]() {
152 Q_ASSERT(rowForDevice(id) >= 0);
153 deviceUpdated(id);
154 });
155 m_devices.push_back(std::move(dev));
156}
157
158void DevicesModel::clearDevices()
159{
161 m_devices.clear();
163}
164
165QVariant DevicesModel::data(const QModelIndex &index, int role) const
166{
167 if (!index.isValid() || index.row() < 0 || index.row() >= (int)m_devices.size()) {
168 return QVariant();
169 }
170
171 OrgKdeKdeconnectDeviceInterface *device = m_devices[index.row()].interface.get();
172 Q_ASSERT(device->isValid());
173
174 switch (role) {
175 case NameModelRole:
176 return device->name();
177 case IconNameRole:
178 return device->statusIconName();
179 case IdRole:
180 return m_devices[index.row()].id;
181 default:
182 return QVariant();
183 }
184}
185
186int DevicesModel::rowCount(const QModelIndex &) const
187{
188 return m_devices.size();
189}
190
191#include "moc_devicesmodel.cpp"
void beginInsertRows(const QModelIndex &parent, int first, int last)
void beginRemoveRows(const QModelIndex &parent, int first, int last)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
virtual QHash< int, QByteArray > roleNames() const const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
QDBusConnection sessionBus()
QString message() const const
void finished(QDBusPendingCallWatcher *self)
QDBusError error() const const
bool isError() const const
typename Select< 0 >::Type value() const const
void serviceRegistered(const QString &serviceName)
void serviceUnregistered(const QString &serviceName)
iterator insert(const Key &key, const T &value)
qsizetype count() const const
bool isEmpty() const const
bool isValid() const const
int row() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 3 2025 11:49:11 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.