From b06c4fa268fadd157a5e2d1e6171a27ec5342c9c Mon Sep 17 00:00:00 2001 From: Rob Emery Date: Tue, 25 Nov 2025 16:42:06 +0000 Subject: [PATCH] Adding info into the UI for how devices are accessible --- app/qml/DevicePage.qml | 11 +++++++++-- app/qml/Settings.qml | 12 ++---------- cli/kdeconnect-cli.cpp | 5 ++++- core/backends/bluetooth/asynclinkprovider.cpp | 7 ++++++- core/backends/bluetooth/asynclinkprovider.h | 2 ++ .../backends/bluetooth/bluetoothdevicelink.cpp | 5 +++++ core/backends/bluetooth/bluetoothdevicelink.h | 2 ++ .../backends/bluetooth/bluetoothlinkprovider.h | 5 +++++ .../bluetooth/connectionmultiplexer.cpp | 5 +++++ .../backends/bluetooth/connectionmultiplexer.h | 6 ++++++ core/backends/devicelink.cpp | 2 ++ core/backends/devicelink.h | 14 ++++++++++++++ core/backends/lan/landevicelink.h | 5 +++++ core/backends/lan/lanlinkprovider.h | 5 +++++ core/backends/linkprovider.h | 2 ++ core/backends/loopback/loopbackdevicelink.h | 5 +++++ core/backends/loopback/loopbacklinkprovider.h | 6 ++++++ core/daemon.cpp | 4 ++-- core/device.cpp | 18 ++++++++++++++++++ core/device.h | 5 +++++ 20 files changed, 110 insertions(+), 16 deletions(-) diff --git a/app/qml/DevicePage.qml b/app/qml/DevicePage.qml index bc8aaf297..ac0ca4a83 100644 --- a/app/qml/DevicePage.qml +++ b/app/qml/DevicePage.qml @@ -65,6 +65,8 @@ Kirigami.ScrollablePage { return i18nc("@title:group device page section header", "Actions"); case "control": return i18nc("@title:group device page section header", "Controls"); + case "info": + return i18nc("@title:group device page section header", "Information"); } } Accessible.role: Accessible.List @@ -76,13 +78,13 @@ Kirigami.ScrollablePage { DelegateModelGroup {name: "loadedPlugins"} ] filterOnGroup: "loadedPlugins" - property int numberPluginsLoaded: pluginsListView.plugins.filter(plugin => plugin.loaded).length ?? 0 + property int numberPluginsLoaded: pluginsListView.plugins.filter(plugin => plugin.loaded || plugin.section === "info").length ?? 0 onNumberPluginsLoadedChanged: update() function update() { for (let i = 0; i < items.count; ++i) { let item = items.get(i); - item.inLoadedPlugins = item.model.loaded + item.inLoadedPlugins = item.model.loaded || item.model.section === "info" } } @@ -204,6 +206,11 @@ Kirigami.ScrollablePage { } section: "action" device: root.currentDevice + }, + PluginItem { + name: i18nd("kdeconnect-app", "Address: %1 via %2", root.currentDevice.reachableAddresses, root.currentDevice.activeProviderNames) + section: "info" + device: root.currentDevice } ] diff --git a/app/qml/Settings.qml b/app/qml/Settings.qml index 03deeca99..d1e3e595c 100644 --- a/app/qml/Settings.qml +++ b/app/qml/Settings.qml @@ -48,20 +48,12 @@ FormCard.FormCardPage { FormCard.FormCheckDelegate { required property string modelData - readonly property string linkProviderId: modelData.split('|')[0] - readonly property string displayName: switch (linkProviderId) { - case 'BluetoothLinkProvider': - return i18nc("@info KDE Connect provider name", "Bluetooth") - case 'LoopbackLinkProvider': - return i18nc("@info KDE Connect provider name", "Loopback") - case 'LanLinkProvider': - return i18nc("@info KDE Connect provider name", "Network") - } + readonly property string displayName: modelData.split('|')[0] checked: modelData.split('|')[1] === 'enabled' text: displayName - onToggled: DaemonDbusInterface.setLinkProviderState(linkProviderId, checked); + onToggled: DaemonDbusInterface.setLinkProviderState(displayName, checked); } } } diff --git a/cli/kdeconnect-cli.cpp b/cli/kdeconnect-cli.cpp index 1fdd72fb2..b2ffb8153 100644 --- a/cli/kdeconnect-cli.cpp +++ b/cli/kdeconnect-cli.cpp @@ -154,7 +154,10 @@ int main(int argc, char **argv) } else if (isPaired) { statusInfo = i18n("(paired)"); } - QTextStream(stdout) << "- " << deviceIface.name() << ": " << deviceIface.id() << ' ' << statusInfo << Qt::endl; + + QTextStream(stdout) << "- " << deviceIface.name() << ": " << deviceIface.id() << QStringLiteral(" on ") + << deviceIface.reachableAddresses().join(QStringLiteral(", ")) << QStringLiteral(" via ") + << deviceIface.activeProviderNames().join(QStringLiteral(", ")) << ' ' << statusInfo << Qt::endl; } } if (displayCount) { diff --git a/core/backends/bluetooth/asynclinkprovider.cpp b/core/backends/bluetooth/asynclinkprovider.cpp index 77f08016d..081bfff26 100644 --- a/core/backends/bluetooth/asynclinkprovider.cpp +++ b/core/backends/bluetooth/asynclinkprovider.cpp @@ -31,7 +31,12 @@ AsyncLinkProvider::AsyncLinkProvider(bool isDisabled) QString AsyncLinkProvider::name() { - return QStringLiteral("BluetoothLinkProvider"); + return QStringLiteral("AsyncLinkProvider"); +} + +QString AsyncLinkProvider::displayName() +{ + return i18nc("@info", "Bluetooth"); } int AsyncLinkProvider::priority() diff --git a/core/backends/bluetooth/asynclinkprovider.h b/core/backends/bluetooth/asynclinkprovider.h index deb5bde7e..4271bfa29 100644 --- a/core/backends/bluetooth/asynclinkprovider.h +++ b/core/backends/bluetooth/asynclinkprovider.h @@ -34,6 +34,8 @@ public: AsyncLinkProvider(bool isDisabled); QString name() override; + QString displayName() override; + int priority() override; void enable() override; void disable() override; diff --git a/core/backends/bluetooth/bluetoothdevicelink.cpp b/core/backends/bluetooth/bluetoothdevicelink.cpp index fd65a2368..ccfc97825 100644 --- a/core/backends/bluetooth/bluetoothdevicelink.cpp +++ b/core/backends/bluetooth/bluetoothdevicelink.cpp @@ -33,6 +33,11 @@ BluetoothDeviceLink::BluetoothDeviceLink(const DeviceInfo &deviceInfo, connect(socket.data(), &MultiplexChannel::aboutToClose, this, &QObject::deleteLater); } +QString BluetoothDeviceLink::address() const +{ + return mConnection->address().toString(); +} + bool BluetoothDeviceLink::sendPacket(NetworkPacket &np) { if (np.hasPayload()) { diff --git a/core/backends/bluetooth/bluetoothdevicelink.h b/core/backends/bluetooth/bluetoothdevicelink.h index 94a4d2f9c..7b2a2c5ca 100644 --- a/core/backends/bluetooth/bluetoothdevicelink.h +++ b/core/backends/bluetooth/bluetoothdevicelink.h @@ -35,6 +35,8 @@ public: return mDeviceInfo; } + QString address() const override; + private Q_SLOTS: void dataReceived(); diff --git a/core/backends/bluetooth/bluetoothlinkprovider.h b/core/backends/bluetooth/bluetoothlinkprovider.h index ccf717080..c50e35555 100644 --- a/core/backends/bluetooth/bluetoothlinkprovider.h +++ b/core/backends/bluetooth/bluetoothlinkprovider.h @@ -37,6 +37,11 @@ public: return QStringLiteral("SynchronousBluetoothLinkProvider"); } + QString displayName() override + { + return i18nc("@info", "Bluetooth"); + } + int priority() override { return 10; diff --git a/core/backends/bluetooth/connectionmultiplexer.cpp b/core/backends/bluetooth/connectionmultiplexer.cpp index 6f1c0729f..80545829a 100644 --- a/core/backends/bluetooth/connectionmultiplexer.cpp +++ b/core/backends/bluetooth/connectionmultiplexer.cpp @@ -107,6 +107,11 @@ bool ConnectionMultiplexer::isOpen() const return mSocket->isOpen(); } +QBluetoothAddress ConnectionMultiplexer::address() const +{ + return mSocket->peerAddress(); +} + bool ConnectionMultiplexer::tryParseMessage() { mSocket->startTransaction(); diff --git a/core/backends/bluetooth/connectionmultiplexer.h b/core/backends/bluetooth/connectionmultiplexer.h index 76a17987d..da5ac8c63 100644 --- a/core/backends/bluetooth/connectionmultiplexer.h +++ b/core/backends/bluetooth/connectionmultiplexer.h @@ -7,6 +7,7 @@ #ifndef CONNECTIONMULTIPLEXER_H #define CONNECTIONMULTIPLEXER_H +#include #include #include #include @@ -67,6 +68,11 @@ public: */ bool isOpen() const; + /** + * Returns the address of the connected device for display. + */ + QBluetoothAddress address() const; + private: /** * The underlying connection diff --git a/core/backends/devicelink.cpp b/core/backends/devicelink.cpp index 8d7126438..112f44e02 100644 --- a/core/backends/devicelink.cpp +++ b/core/backends/devicelink.cpp @@ -15,6 +15,8 @@ DeviceLink::DeviceLink(const QString &deviceId, LinkProvider *parent) parent->onLinkDestroyed(deviceId, this); }); this->priorityFromProvider = parent->priority(); + this->displayNameFromProvider = parent->displayName(); + this->nameFromProvider = parent->name(); } #include "moc_devicelink.cpp" diff --git a/core/backends/devicelink.h b/core/backends/devicelink.h index 3eb2596e9..4022cbc19 100644 --- a/core/backends/devicelink.h +++ b/core/backends/devicelink.h @@ -30,12 +30,26 @@ public: return priorityFromProvider; } + QString provider() const + { + return nameFromProvider; + } + + QString providerName() const + { + return displayNameFromProvider; + } + virtual bool sendPacket(NetworkPacket &np) = 0; virtual DeviceInfo deviceInfo() const = 0; + virtual QString address() const = 0; + private: int priorityFromProvider; + QString nameFromProvider; + QString displayNameFromProvider; Q_SIGNALS: void receivedPacket(const NetworkPacket &np); diff --git a/core/backends/lan/landevicelink.h b/core/backends/lan/landevicelink.h index 1d8983ab2..f4cbd4984 100644 --- a/core/backends/lan/landevicelink.h +++ b/core/backends/lan/landevicelink.h @@ -35,6 +35,11 @@ public: return m_deviceInfo; } + QString address() const override + { + return hostAddress().toString(); + } + QHostAddress hostAddress() const; private Q_SLOTS: diff --git a/core/backends/lan/lanlinkprovider.h b/core/backends/lan/lanlinkprovider.h index 1531e086c..e621bfd0e 100644 --- a/core/backends/lan/lanlinkprovider.h +++ b/core/backends/lan/lanlinkprovider.h @@ -38,6 +38,11 @@ public: return QStringLiteral("LanLinkProvider"); } + QString displayName() override + { + return i18nc("@info", "LAN"); + } + int priority() override { return 20; diff --git a/core/backends/linkprovider.h b/core/backends/linkprovider.h index 7b863ba30..b2446b8db 100644 --- a/core/backends/linkprovider.h +++ b/core/backends/linkprovider.h @@ -7,6 +7,7 @@ #ifndef LINKPROVIDER_H #define LINKPROVIDER_H +#include #include #include "networkpacket.h" @@ -22,6 +23,7 @@ public: LinkProvider(); virtual QString name() = 0; + virtual QString displayName() = 0; virtual int priority() = 0; virtual void enable() = 0; diff --git a/core/backends/loopback/loopbackdevicelink.h b/core/backends/loopback/loopbackdevicelink.h index 4f21e93b9..0ab6ba47a 100644 --- a/core/backends/loopback/loopbackdevicelink.h +++ b/core/backends/loopback/loopbackdevicelink.h @@ -21,6 +21,11 @@ public: virtual bool sendPacket(NetworkPacket &np) override; virtual DeviceInfo deviceInfo() const override; + + QString address() const override + { + return QStringLiteral("127.0.0.1"); + } }; #endif diff --git a/core/backends/loopback/loopbacklinkprovider.h b/core/backends/loopback/loopbacklinkprovider.h index fc5adda2e..41f189660 100644 --- a/core/backends/loopback/loopbacklinkprovider.h +++ b/core/backends/loopback/loopbacklinkprovider.h @@ -22,6 +22,12 @@ public: { return QStringLiteral("LoopbackLinkProvider"); } + + QString displayName() override + { + return i18nc("@info", "Loopback"); + } + int priority() override { return 0; diff --git a/core/daemon.cpp b/core/daemon.cpp index 5fdf9cb72..0557b63de 100644 --- a/core/daemon.cpp +++ b/core/daemon.cpp @@ -164,7 +164,7 @@ QStringList Daemon::linkProviders() const QStringList returnValue; for (LinkProvider *a : std::as_const(d->m_linkProviders)) { - QString line(a->name()); + QString line(a->displayName()); if (disabledLinkProviders.contains(a->name())) { line += QStringLiteral("|disabled"); @@ -186,7 +186,7 @@ void Daemon::setLinkProviderState(const QString &linkProviderName, bool enabled) LinkProvider *providerByName = nullptr; const auto allLinkProviders = getLinkProviders(); for (LinkProvider *provider : allLinkProviders) { - if (provider->name() == linkProviderName) { + if (provider->displayName() == linkProviderName) { providerByName = provider; break; } diff --git a/core/device.cpp b/core/device.cpp index ed61d1c5b..a719c8849 100644 --- a/core/device.cpp +++ b/core/device.cpp @@ -128,6 +128,24 @@ bool Device::isReachable() const return !d->m_deviceLinks.isEmpty(); } +QStringList Device::reachableAddresses() const +{ + QList addresses; + for (const DeviceLink *deviceLink : std::as_const(d->m_deviceLinks)) { + addresses.append(deviceLink->address()); + } + return addresses; +} + +QStringList Device::activeProviderNames() const +{ + QList providers; + for (const DeviceLink *deviceLink : std::as_const(d->m_deviceLinks)) { + providers.append(deviceLink->providerName()); + } + return providers; +} + int Device::protocolVersion() { return d->m_deviceInfo.protocolVersion; diff --git a/core/device.h b/core/device.h index 4e24d4db4..4efd187b0 100644 --- a/core/device.h +++ b/core/device.h @@ -29,6 +29,8 @@ class KDECONNECTCORE_EXPORT Device : public QObject Q_PROPERTY(QString verificationKey READ verificationKey NOTIFY pairStateChanged) Q_PROPERTY(QString statusIconName READ statusIconName NOTIFY statusIconNameChanged) Q_PROPERTY(bool isReachable READ isReachable NOTIFY reachableChanged) + Q_PROPERTY(QStringList reachableAddresses READ reachableAddresses CONSTANT) + Q_PROPERTY(QStringList activeProviderNames READ activeProviderNames CONSTANT) Q_PROPERTY(bool isPaired READ isPaired NOTIFY pairStateChanged) Q_PROPERTY(bool isPairRequested READ isPairRequested NOTIFY pairStateChanged) Q_PROPERTY(bool isPairRequestedByPeer READ isPairRequestedByPeer NOTIFY pairStateChanged) @@ -84,6 +86,9 @@ public: Q_SCRIPTABLE bool isPairRequestedByPeer() const; virtual bool isReachable() const; + virtual QStringList reachableAddresses() const; + virtual QStringList activeProviderNames() const; + Q_SCRIPTABLE QStringList loadedPlugins() const; Q_SCRIPTABLE bool hasPlugin(const QString &name) const;