File indexing completed on 2023-09-24 08:52:40
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 setDisplayFilter(NoFilter); 0047 } 0048 0049 QHash<int, QByteArray> DevicesModel::roleNames() const 0050 { 0051 QHash<int, QByteArray> names = QAbstractItemModel::roleNames(); 0052 names.insert(NameModelRole, "name"); 0053 names.insert(IdModelRole, "deviceId"); 0054 names.insert(IconNameRole, "iconName"); 0055 names.insert(DeviceRole, "device"); 0056 names.insert(StatusModelRole, "status"); 0057 return names; 0058 } 0059 0060 DevicesModel::~DevicesModel() 0061 { 0062 } 0063 0064 int DevicesModel::rowForDevice(const QString &id) const 0065 { 0066 for (int i = 0, c = m_deviceList.size(); i < c; ++i) { 0067 if (m_deviceList[i]->id() == id) { 0068 return i; 0069 } 0070 } 0071 return -1; 0072 } 0073 0074 void DevicesModel::deviceAdded(const QString &id) 0075 { 0076 if (rowForDevice(id) >= 0) { 0077 Q_ASSERT_X(false, "deviceAdded", "Trying to add a device twice"); 0078 return; 0079 } 0080 0081 DeviceDbusInterface *dev = new DeviceDbusInterface(id, this); 0082 Q_ASSERT(dev->isValid()); 0083 0084 if (!passesFilter(dev)) { 0085 delete dev; 0086 return; 0087 } 0088 0089 beginInsertRows(QModelIndex(), m_deviceList.size(), m_deviceList.size()); 0090 appendDevice(dev); 0091 endInsertRows(); 0092 } 0093 0094 void DevicesModel::deviceRemoved(const QString &id) 0095 { 0096 int row = rowForDevice(id); 0097 if (row >= 0) { 0098 beginRemoveRows(QModelIndex(), row, row); 0099 delete m_deviceList.takeAt(row); 0100 endRemoveRows(); 0101 } 0102 } 0103 0104 void DevicesModel::deviceUpdated(const QString &id, bool isVisible) 0105 { 0106 Q_UNUSED(isVisible); 0107 int row = rowForDevice(id); 0108 0109 if (row < 0) { 0110 // FIXME: when m_dbusInterface is not valid refreshDeviceList() does 0111 // nothing and we can miss some devices. 0112 // Someone can reproduce this problem by restarting kdeconnectd while 0113 // kdeconnect's plasmoid is still running. 0114 // Another reason for this branch is that we removed the device previously 0115 // because of the filter settings. 0116 qCDebug(KDECONNECT_INTERFACES) << "Adding missing or previously removed device" << id; 0117 deviceAdded(id); 0118 } else { 0119 DeviceDbusInterface *dev = getDevice(row); 0120 if (!passesFilter(dev)) { 0121 beginRemoveRows(QModelIndex(), row, row); 0122 delete m_deviceList.takeAt(row); 0123 endRemoveRows(); 0124 qCDebug(KDECONNECT_INTERFACES) << "Removed changed device " << id; 0125 } else { 0126 const QModelIndex idx = index(row); 0127 Q_EMIT dataChanged(idx, idx); 0128 } 0129 } 0130 } 0131 0132 int DevicesModel::displayFilter() const 0133 { 0134 return m_displayFilter; 0135 } 0136 0137 void DevicesModel::setDisplayFilter(int flags) 0138 { 0139 m_displayFilter = (StatusFilterFlag)flags; 0140 0141 refreshDeviceList(); 0142 } 0143 0144 void DevicesModel::refreshDeviceList() 0145 { 0146 if (!m_dbusInterface->isValid()) { 0147 clearDevices(); 0148 qCWarning(KDECONNECT_INTERFACES) << "dbus interface not valid"; 0149 return; 0150 } 0151 0152 bool onlyPaired = (m_displayFilter & StatusFilterFlag::Paired); 0153 bool onlyReachable = (m_displayFilter & StatusFilterFlag::Reachable); 0154 0155 QDBusPendingReply<QStringList> pendingDeviceIds = m_dbusInterface->devices(onlyReachable, onlyPaired); 0156 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingDeviceIds, this); 0157 0158 QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &DevicesModel::receivedDeviceList); 0159 } 0160 0161 void DevicesModel::receivedDeviceList(QDBusPendingCallWatcher *watcher) 0162 { 0163 watcher->deleteLater(); 0164 clearDevices(); 0165 QDBusPendingReply<QStringList> pendingDeviceIds = *watcher; 0166 if (pendingDeviceIds.isError()) { 0167 qCWarning(KDECONNECT_INTERFACES) << "error while refreshing device list" << pendingDeviceIds.error().message(); 0168 return; 0169 } 0170 0171 Q_ASSERT(m_deviceList.isEmpty()); 0172 const QStringList deviceIds = pendingDeviceIds.value(); 0173 0174 if (deviceIds.isEmpty()) 0175 return; 0176 0177 beginInsertRows(QModelIndex(), 0, deviceIds.count() - 1); 0178 for (const QString &id : deviceIds) { 0179 appendDevice(new DeviceDbusInterface(id, this)); 0180 } 0181 endInsertRows(); 0182 } 0183 0184 void DevicesModel::appendDevice(DeviceDbusInterface *dev) 0185 { 0186 m_deviceList.append(dev); 0187 connect(dev, &OrgKdeKdeconnectDeviceInterface::nameChanged, this, &DevicesModel::nameChanged); 0188 } 0189 0190 void DevicesModel::nameChanged(const QString &newName) 0191 { 0192 Q_UNUSED(newName); 0193 DeviceDbusInterface *device = static_cast<DeviceDbusInterface *>(sender()); 0194 0195 Q_ASSERT(rowForDevice(device->id()) >= 0); 0196 0197 deviceUpdated(device->id(), true); 0198 } 0199 0200 void DevicesModel::clearDevices() 0201 { 0202 if (!m_deviceList.isEmpty()) { 0203 beginRemoveRows(QModelIndex(), 0, m_deviceList.size() - 1); 0204 qDeleteAll(m_deviceList); 0205 m_deviceList.clear(); 0206 endRemoveRows(); 0207 } 0208 } 0209 0210 QVariant DevicesModel::data(const QModelIndex &index, int role) const 0211 { 0212 if (!index.isValid() || index.row() < 0 || index.row() >= m_deviceList.size()) { 0213 return QVariant(); 0214 } 0215 0216 Q_ASSERT(m_dbusInterface->isValid()); 0217 0218 DeviceDbusInterface *device = m_deviceList[index.row()]; 0219 Q_ASSERT(device->isValid()); 0220 0221 // This function gets called lots of times, producing lots of dbus calls. Add a cache? 0222 switch (role) { 0223 case Qt::SizeHintRole: 0224 return QSize(0, 32); 0225 case IconModelRole: { 0226 QString icon = data(index, IconNameRole).toString(); 0227 return QIcon::fromTheme(icon); 0228 } 0229 case IdModelRole: 0230 return device->id(); 0231 case NameModelRole: 0232 return device->name(); 0233 case Qt::ToolTipRole: { 0234 bool trusted = device->isPaired(); 0235 bool reachable = device->isReachable(); 0236 QString status = reachable ? (trusted ? i18n("Device trusted and connected") : i18n("Device not trusted")) : i18n("Device disconnected"); 0237 return status; 0238 } 0239 case StatusModelRole: { 0240 int status = StatusFilterFlag::NoFilter; 0241 if (device->isReachable()) { 0242 status |= StatusFilterFlag::Reachable; 0243 } 0244 if (device->isPaired()) { 0245 status |= StatusFilterFlag::Paired; 0246 } 0247 return status; 0248 } 0249 case IconNameRole: 0250 return device->statusIconName(); 0251 case DeviceRole: 0252 return QVariant::fromValue<QObject *>(device); 0253 default: 0254 return QVariant(); 0255 } 0256 } 0257 0258 DeviceDbusInterface *DevicesModel::getDevice(int row) const 0259 { 0260 if (row < 0 || row >= m_deviceList.size()) { 0261 return nullptr; 0262 } 0263 0264 return m_deviceList[row]; 0265 } 0266 0267 int DevicesModel::rowCount(const QModelIndex &parent) const 0268 { 0269 if (parent.isValid()) { 0270 // Return size 0 if we are a child because this is not a tree 0271 return 0; 0272 } 0273 0274 return m_deviceList.size(); 0275 } 0276 0277 bool DevicesModel::passesFilter(DeviceDbusInterface *dev) const 0278 { 0279 bool onlyPaired = (m_displayFilter & StatusFilterFlag::Paired); 0280 bool onlyReachable = (m_displayFilter & StatusFilterFlag::Reachable); 0281 0282 return !((onlyReachable && !dev->isReachable()) || (onlyPaired && !dev->isPaired())); 0283 }