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