Warning, file /network/kdeconnect-kde/interfaces/devicesmodel.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /** 0002 * SPDX-FileCopyrightText: 2013 Albert Vaca <albertvaka@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "devicesmodel.h" 0008 #include "interfaces_debug.h" 0009 0010 #include <KLocalizedString> 0011 0012 #include <QDBusPendingReply> 0013 #include <QDBusServiceWatcher> 0014 #include <QIcon> 0015 #include <QString> 0016 0017 #include "dbusinterfaces.h" 0018 #include <dbushelper.h> 0019 0020 #include "interfaces_debug.h" 0021 0022 static QString createId() 0023 { 0024 return QCoreApplication::instance()->applicationName() + QString::number(QCoreApplication::applicationPid()); 0025 } 0026 0027 Q_GLOBAL_STATIC_WITH_ARGS(QString, s_keyId, (createId())); 0028 0029 DevicesModel::DevicesModel(QObject *parent) 0030 : QAbstractListModel(parent) 0031 , m_dbusInterface(new DaemonDbusInterface(this)) 0032 , m_displayFilter(StatusFilterFlag::NoFilter) 0033 { 0034 connect(this, &QAbstractItemModel::rowsRemoved, this, &DevicesModel::rowsChanged); 0035 connect(this, &QAbstractItemModel::rowsInserted, this, &DevicesModel::rowsChanged); 0036 0037 connect(m_dbusInterface, SIGNAL(deviceAdded(QString)), this, SLOT(deviceAdded(QString))); 0038 connect(m_dbusInterface, &OrgKdeKdeconnectDaemonInterface::deviceVisibilityChanged, this, &DevicesModel::deviceUpdated); 0039 connect(m_dbusInterface, &OrgKdeKdeconnectDaemonInterface::deviceRemoved, this, &DevicesModel::deviceRemoved); 0040 0041 QDBusServiceWatcher *watcher = 0042 new QDBusServiceWatcher(DaemonDbusInterface::activatedService(), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this); 0043 connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, &DevicesModel::refreshDeviceList); 0044 connect(watcher, &QDBusServiceWatcher::serviceUnregistered, this, &DevicesModel::clearDevices); 0045 0046 // refresh the view, acquireDiscoveryMode if necessary 0047 setDisplayFilter(NoFilter); 0048 } 0049 0050 QHash<int, QByteArray> DevicesModel::roleNames() const 0051 { 0052 QHash<int, QByteArray> names = QAbstractItemModel::roleNames(); 0053 names.insert(NameModelRole, "name"); 0054 names.insert(IdModelRole, "deviceId"); 0055 names.insert(IconNameRole, "iconName"); 0056 names.insert(DeviceRole, "device"); 0057 names.insert(StatusModelRole, "status"); 0058 return names; 0059 } 0060 0061 DevicesModel::~DevicesModel() 0062 { 0063 m_dbusInterface->releaseDiscoveryMode(*s_keyId); 0064 } 0065 0066 int DevicesModel::rowForDevice(const QString &id) const 0067 { 0068 for (int i = 0, c = m_deviceList.size(); i < c; ++i) { 0069 if (m_deviceList[i]->id() == id) { 0070 return i; 0071 } 0072 } 0073 return -1; 0074 } 0075 0076 void DevicesModel::deviceAdded(const QString &id) 0077 { 0078 if (rowForDevice(id) >= 0) { 0079 Q_ASSERT_X(false, "deviceAdded", "Trying to add a device twice"); 0080 return; 0081 } 0082 0083 DeviceDbusInterface *dev = new DeviceDbusInterface(id, this); 0084 Q_ASSERT(dev->isValid()); 0085 0086 if (!passesFilter(dev)) { 0087 delete dev; 0088 return; 0089 } 0090 0091 beginInsertRows(QModelIndex(), m_deviceList.size(), m_deviceList.size()); 0092 appendDevice(dev); 0093 endInsertRows(); 0094 } 0095 0096 void DevicesModel::deviceRemoved(const QString &id) 0097 { 0098 int row = rowForDevice(id); 0099 if (row >= 0) { 0100 beginRemoveRows(QModelIndex(), row, row); 0101 delete m_deviceList.takeAt(row); 0102 endRemoveRows(); 0103 } 0104 } 0105 0106 void DevicesModel::deviceUpdated(const QString &id, bool isVisible) 0107 { 0108 Q_UNUSED(isVisible); 0109 int row = rowForDevice(id); 0110 0111 if (row < 0) { 0112 // FIXME: when m_dbusInterface is not valid refreshDeviceList() does 0113 // nothing and we can miss some devices. 0114 // Someone can reproduce this problem by restarting kdeconnectd while 0115 // kdeconnect's plasmoid is still running. 0116 // Another reason for this branch is that we removed the device previously 0117 // because of the filter settings. 0118 qCDebug(KDECONNECT_INTERFACES) << "Adding missing or previously removed device" << id; 0119 deviceAdded(id); 0120 } else { 0121 DeviceDbusInterface *dev = getDevice(row); 0122 if (!passesFilter(dev)) { 0123 beginRemoveRows(QModelIndex(), row, row); 0124 delete m_deviceList.takeAt(row); 0125 endRemoveRows(); 0126 qCDebug(KDECONNECT_INTERFACES) << "Removed changed device " << id; 0127 } else { 0128 const QModelIndex idx = index(row); 0129 Q_EMIT dataChanged(idx, idx); 0130 } 0131 } 0132 } 0133 0134 int DevicesModel::displayFilter() const 0135 { 0136 return m_displayFilter; 0137 } 0138 0139 void DevicesModel::setDisplayFilter(int flags) 0140 { 0141 m_displayFilter = (StatusFilterFlag)flags; 0142 0143 const bool reachableNeeded = (m_displayFilter & StatusFilterFlag::Reachable); 0144 if (reachableNeeded) 0145 m_dbusInterface->acquireDiscoveryMode(*s_keyId); 0146 else 0147 m_dbusInterface->releaseDiscoveryMode(*s_keyId); 0148 0149 refreshDeviceList(); 0150 } 0151 0152 void DevicesModel::refreshDeviceList() 0153 { 0154 if (!m_dbusInterface->isValid()) { 0155 clearDevices(); 0156 qCWarning(KDECONNECT_INTERFACES) << "dbus interface not valid"; 0157 return; 0158 } 0159 0160 bool onlyPaired = (m_displayFilter & StatusFilterFlag::Paired); 0161 bool onlyReachable = (m_displayFilter & StatusFilterFlag::Reachable); 0162 0163 QDBusPendingReply<QStringList> pendingDeviceIds = m_dbusInterface->devices(onlyReachable, onlyPaired); 0164 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingDeviceIds, this); 0165 0166 QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &DevicesModel::receivedDeviceList); 0167 } 0168 0169 void DevicesModel::receivedDeviceList(QDBusPendingCallWatcher *watcher) 0170 { 0171 watcher->deleteLater(); 0172 clearDevices(); 0173 QDBusPendingReply<QStringList> pendingDeviceIds = *watcher; 0174 if (pendingDeviceIds.isError()) { 0175 qCWarning(KDECONNECT_INTERFACES) << "error while refreshing device list" << pendingDeviceIds.error().message(); 0176 return; 0177 } 0178 0179 Q_ASSERT(m_deviceList.isEmpty()); 0180 const QStringList deviceIds = pendingDeviceIds.value(); 0181 0182 if (deviceIds.isEmpty()) 0183 return; 0184 0185 beginInsertRows(QModelIndex(), 0, deviceIds.count() - 1); 0186 for (const QString &id : deviceIds) { 0187 appendDevice(new DeviceDbusInterface(id, this)); 0188 } 0189 endInsertRows(); 0190 } 0191 0192 void DevicesModel::appendDevice(DeviceDbusInterface *dev) 0193 { 0194 m_deviceList.append(dev); 0195 connect(dev, &OrgKdeKdeconnectDeviceInterface::nameChanged, this, &DevicesModel::nameChanged); 0196 } 0197 0198 void DevicesModel::nameChanged(const QString &newName) 0199 { 0200 Q_UNUSED(newName); 0201 DeviceDbusInterface *device = static_cast<DeviceDbusInterface *>(sender()); 0202 0203 Q_ASSERT(rowForDevice(device->id()) >= 0); 0204 0205 deviceUpdated(device->id(), true); 0206 } 0207 0208 void DevicesModel::clearDevices() 0209 { 0210 if (!m_deviceList.isEmpty()) { 0211 beginRemoveRows(QModelIndex(), 0, m_deviceList.size() - 1); 0212 qDeleteAll(m_deviceList); 0213 m_deviceList.clear(); 0214 endRemoveRows(); 0215 } 0216 } 0217 0218 QVariant DevicesModel::data(const QModelIndex &index, int role) const 0219 { 0220 if (!index.isValid() || index.row() < 0 || index.row() >= m_deviceList.size()) { 0221 return QVariant(); 0222 } 0223 0224 Q_ASSERT(m_dbusInterface->isValid()); 0225 0226 DeviceDbusInterface *device = m_deviceList[index.row()]; 0227 Q_ASSERT(device->isValid()); 0228 0229 // This function gets called lots of times, producing lots of dbus calls. Add a cache? 0230 switch (role) { 0231 case Qt::SizeHintRole: 0232 return QSize(0, 32); 0233 case IconModelRole: { 0234 QString icon = data(index, IconNameRole).toString(); 0235 return QIcon::fromTheme(icon); 0236 } 0237 case IdModelRole: 0238 return device->id(); 0239 case NameModelRole: 0240 return device->name(); 0241 case Qt::ToolTipRole: { 0242 bool trusted = device->isTrusted(); 0243 bool reachable = device->isReachable(); 0244 QString status = reachable ? (trusted ? i18n("Device trusted and connected") : i18n("Device not trusted")) : i18n("Device disconnected"); 0245 return status; 0246 } 0247 case StatusModelRole: { 0248 int status = StatusFilterFlag::NoFilter; 0249 if (device->isReachable()) { 0250 status |= StatusFilterFlag::Reachable; 0251 } 0252 if (device->isTrusted()) { 0253 status |= StatusFilterFlag::Paired; 0254 } 0255 return status; 0256 } 0257 case IconNameRole: 0258 return device->statusIconName(); 0259 case DeviceRole: 0260 return QVariant::fromValue<QObject *>(device); 0261 default: 0262 return QVariant(); 0263 } 0264 } 0265 0266 DeviceDbusInterface *DevicesModel::getDevice(int row) const 0267 { 0268 if (row < 0 || row >= m_deviceList.size()) { 0269 return nullptr; 0270 } 0271 0272 return m_deviceList[row]; 0273 } 0274 0275 int DevicesModel::rowCount(const QModelIndex &parent) const 0276 { 0277 if (parent.isValid()) { 0278 // Return size 0 if we are a child because this is not a tree 0279 return 0; 0280 } 0281 0282 return m_deviceList.size(); 0283 } 0284 0285 bool DevicesModel::passesFilter(DeviceDbusInterface *dev) const 0286 { 0287 bool onlyPaired = (m_displayFilter & StatusFilterFlag::Paired); 0288 bool onlyReachable = (m_displayFilter & StatusFilterFlag::Reachable); 0289 0290 return !((onlyReachable && !dev->isReachable()) || (onlyPaired && !dev->isTrusted())); 0291 }