File indexing completed on 2024-04-28 16:51:32
0001 /* 0002 SPDX-FileCopyrightText: 2017 Kai Uwe Broulik <kde@privat.broulik.de> 0003 SPDX-FileCopyrightText: 2017 David Edmundson <davidedmundson@kde.org> 0004 0005 SPDX-License-Identifier: MIT 0006 */ 0007 0008 #include "kdeconnectplugin.h" 0009 0010 #include "connection.h" 0011 #include <QDBusConnection> 0012 #include <QDBusMessage> 0013 #include <QDBusPendingReply> 0014 0015 static const QString s_kdeConnectServiceName = QStringLiteral("org.kde.kdeconnect"); 0016 static const QString s_kdeConnectObjectPath = QStringLiteral("/modules/kdeconnect"); 0017 0018 static const QString s_kdeConnectDaemon = QStringLiteral("org.kde.kdeconnect.daemon"); 0019 0020 KDEConnectPlugin::KDEConnectPlugin(QObject *parent) 0021 : AbstractBrowserPlugin(QStringLiteral("kdeconnect"), 1, parent) 0022 { 0023 } 0024 0025 bool KDEConnectPlugin::onLoad() 0026 { 0027 QDBusConnection bus = QDBusConnection::sessionBus(); 0028 0029 bus.connect(s_kdeConnectServiceName, s_kdeConnectObjectPath, s_kdeConnectDaemon, QStringLiteral("deviceAdded"), this, SLOT(onDeviceAdded(QString))); 0030 bus.connect(s_kdeConnectServiceName, s_kdeConnectObjectPath, s_kdeConnectDaemon, QStringLiteral("deviceRemoved"), this, SLOT(onDeviceRemoved(QString))); 0031 bus.connect(s_kdeConnectServiceName, 0032 s_kdeConnectObjectPath, 0033 s_kdeConnectDaemon, 0034 QStringLiteral("deviceVisibilityChanged"), 0035 this, 0036 SLOT(onDeviceVisibilityChanged(QString, bool))); 0037 0038 QDBusMessage msg = QDBusMessage::createMethodCall(s_kdeConnectServiceName, s_kdeConnectObjectPath, s_kdeConnectDaemon, QStringLiteral("devices")); 0039 msg.setArguments({true /* only reachable */, true /* only paired */}); 0040 QDBusPendingReply<QStringList> reply = bus.asyncCall(msg); 0041 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); 0042 QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { 0043 QDBusPendingReply<QStringList> reply = *watcher; 0044 watcher->deleteLater(); 0045 if (reply.isError()) { 0046 debug() << "kdeconnect discovery failed:" << reply.error().name(); 0047 return; 0048 } 0049 0050 const QStringList devices = reply.value(); 0051 for (const QString &deviceId : devices) { 0052 onDeviceAdded(deviceId); 0053 } 0054 }); 0055 return true; 0056 } 0057 0058 bool KDEConnectPlugin::onUnload() 0059 { 0060 QDBusConnection bus = QDBusConnection::sessionBus(); 0061 0062 bus.disconnect(s_kdeConnectServiceName, s_kdeConnectObjectPath, s_kdeConnectDaemon, QStringLiteral("deviceAdded"), this, SLOT(onDeviceAdded(QString))); 0063 bus.disconnect(s_kdeConnectServiceName, s_kdeConnectObjectPath, s_kdeConnectDaemon, QStringLiteral("deviceRemoved"), this, SLOT(onDeviceRemoved(QString))); 0064 bus.disconnect(s_kdeConnectServiceName, 0065 s_kdeConnectObjectPath, 0066 s_kdeConnectDaemon, 0067 QStringLiteral("deviceVisibilityChanged"), 0068 this, 0069 SLOT(onDeviceVisibilityChanged(QString, bool))); 0070 0071 const QStringList devices = m_devices; 0072 for (const QString &deviceId : devices) { 0073 onDeviceRemoved(deviceId); 0074 } 0075 return true; 0076 } 0077 0078 void KDEConnectPlugin::onDeviceAdded(const QString &deviceId) 0079 { 0080 if (m_devices.contains(deviceId)) { 0081 return; 0082 } 0083 0084 QDBusMessage msg = QDBusMessage::createMethodCall(s_kdeConnectServiceName, 0085 QStringLiteral("/modules/kdeconnect/devices/") + deviceId, 0086 QStringLiteral("org.freedesktop.DBus.Properties"), 0087 QStringLiteral("GetAll")); 0088 msg.setArguments({QStringLiteral("org.kde.kdeconnect.device")}); 0089 QDBusPendingReply<QVariantMap> reply = QDBusConnection::sessionBus().asyncCall(msg); 0090 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); 0091 0092 QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, deviceId](QDBusPendingCallWatcher *watcher) { 0093 QDBusPendingReply<QVariantMap> reply = *watcher; 0094 watcher->deleteLater(); 0095 0096 if (reply.isError()) { 0097 debug() << "getting device for properties for" << deviceId << "failed:" << reply.error().message(); 0098 return; 0099 } 0100 0101 // We might have gotten a second deviceAdded signal, check again. 0102 if (m_devices.contains(deviceId)) { 0103 return; 0104 } 0105 0106 QVariantMap props = reply.value(); 0107 0108 // Also check isTrusted for compatibility with older KDEConnect 0109 const bool paired = props.value(QStringLiteral("isPaired")).toBool() || props.value(QStringLiteral("isTrusted")).toBool(); 0110 if (!props.value(QStringLiteral("isReachable")).toBool() || !paired) { 0111 return; 0112 } 0113 0114 props.insert(QStringLiteral("id"), deviceId); 0115 0116 m_devices.append(deviceId); 0117 sendData(QStringLiteral("deviceAdded"), QJsonObject::fromVariantMap(props)); 0118 }); 0119 } 0120 0121 void KDEConnectPlugin::onDeviceRemoved(const QString &deviceId) 0122 { 0123 if (m_devices.removeOne(deviceId)) { 0124 sendData(QStringLiteral("deviceRemoved"), {{QStringLiteral("id"), deviceId}}); 0125 } 0126 } 0127 0128 void KDEConnectPlugin::onDeviceVisibilityChanged(const QString &deviceId, bool visible) 0129 { 0130 if (visible) { 0131 onDeviceAdded(deviceId); 0132 } else { 0133 onDeviceRemoved(deviceId); 0134 } 0135 } 0136 0137 void KDEConnectPlugin::handleData(const QString &event, const QJsonObject &json) 0138 { 0139 if (event == QLatin1String("shareUrl")) { 0140 const QString deviceId = json.value(QStringLiteral("deviceId")).toString(); 0141 const QString url = json.value(QStringLiteral("url")).toString(); 0142 0143 debug() << "sending kde connect url" << url << "to device" << deviceId; 0144 0145 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect"), 0146 QStringLiteral("/modules/kdeconnect/devices/") + deviceId + QStringLiteral("/share"), 0147 QStringLiteral("org.kde.kdeconnect.device.share"), 0148 QStringLiteral("shareUrl")); 0149 msg.setArguments({url}); 0150 QDBusConnection::sessionBus().call(msg, QDBus::NoBlock); 0151 } 0152 }