From 779997337179e035a13b277784d40e67d4fe765e Mon Sep 17 00:00:00 2001 From: Colin Redman Date: Tue, 23 Jul 2019 16:21:39 +0200 Subject: [PATCH] Clipboard sync on new connection Summary: Added a timestamp to track clipboard changes, so when a new device connects it will sync the most recently updated clipboard to both devices. Reviewers: #kde_connect, albertvaka Reviewed By: #kde_connect, albertvaka Subscribers: albertvaka, sredman, kdeconnect Tags: #kde_connect Differential Revision: https://phabricator.kde.org/D22584 --- plugins/clipboard/clipboardlistener.cpp | 22 +++++++++++---- plugins/clipboard/clipboardlistener.h | 11 ++++++-- plugins/clipboard/clipboardplugin.cpp | 31 +++++++++++++++++++-- plugins/clipboard/clipboardplugin.h | 28 +++++++++++++++++-- plugins/clipboard/kdeconnect_clipboard.json | 4 +-- 5 files changed, 82 insertions(+), 14 deletions(-) diff --git a/plugins/clipboard/clipboardlistener.cpp b/plugins/clipboard/clipboardlistener.cpp index abd43876a..222e279f1 100644 --- a/plugins/clipboard/clipboardlistener.cpp +++ b/plugins/clipboard/clipboardlistener.cpp @@ -20,7 +20,7 @@ #include "clipboardlistener.h" -ClipboardListener::ClipboardListener() +ClipboardListener::ClipboardListener() : clipboard(QGuiApplication::clipboard()) { #ifdef Q_OS_MAC @@ -30,7 +30,7 @@ ClipboardListener::ClipboardListener() connect(clipboard, &QClipboard::changed, this, &ClipboardListener::updateClipboard); } -void ClipboardListener::updateClipboard(QClipboard::Mode mode) +void ClipboardListener::updateClipboard(QClipboard::Mode mode) { if (mode != QClipboard::Clipboard) { return; @@ -38,16 +38,28 @@ void ClipboardListener::updateClipboard(QClipboard::Mode mode) QString content = clipboard->text(); - if (content == currentContent) { + if (content == m_currentContent) { return; } - currentContent = content; + m_updateTimestamp = QDateTime::currentDateTime().toMSecsSinceEpoch(); + m_currentContent = content; Q_EMIT clipboardChanged(content); } +QString ClipboardListener::currentContent() +{ + return m_currentContent; +} + +qint64 ClipboardListener::updateTimestamp(){ + + return m_updateTimestamp; +} + void ClipboardListener::setText(const QString& content) { - currentContent = content; + m_updateTimestamp = QDateTime::currentDateTime().toMSecsSinceEpoch(); + m_currentContent = content; clipboard->setText(content); } diff --git a/plugins/clipboard/clipboardlistener.h b/plugins/clipboard/clipboardlistener.h index 20973b12b..43db8147c 100644 --- a/plugins/clipboard/clipboardlistener.h +++ b/plugins/clipboard/clipboardlistener.h @@ -21,6 +21,7 @@ #ifndef CLIPBOARDLISTENER_H #define CLIPBOARDLISTENER_H +#include #include #include #include @@ -29,13 +30,14 @@ /** * Wrapper around QClipboard, which emits clipboardChanged only when it really changed */ -class ClipboardListener : public QObject +class ClipboardListener : public QObject { Q_OBJECT private: ClipboardListener(); - QString currentContent; + QString m_currentContent; + qint64 m_updateTimestamp = 0; QClipboard* clipboard; #ifdef Q_OS_MAC QTimer m_clipboardMonitorTimer; @@ -43,7 +45,7 @@ private: public: - static ClipboardListener* instance() + static ClipboardListener* instance() { static ClipboardListener* me = nullptr; if (!me) { @@ -56,6 +58,9 @@ public: void setText(const QString& content); + QString currentContent(); + qint64 updateTimestamp(); + Q_SIGNALS: void clipboardChanged(const QString& content); }; diff --git a/plugins/clipboard/clipboardplugin.cpp b/plugins/clipboard/clipboardplugin.cpp index d99269686..bab72f5c5 100644 --- a/plugins/clipboard/clipboardplugin.cpp +++ b/plugins/clipboard/clipboardplugin.cpp @@ -35,17 +35,44 @@ ClipboardPlugin::ClipboardPlugin(QObject* parent, const QVariantList& args) this, &ClipboardPlugin::propagateClipboard); } +void ClipboardPlugin::connected() +{ + sendConnectPacket(); +} + void ClipboardPlugin::propagateClipboard(const QString& content) { NetworkPacket np(PACKET_TYPE_CLIPBOARD, {{QStringLiteral("content"), content}}); sendPacket(np); } +void ClipboardPlugin::sendConnectPacket() +{ + NetworkPacket np(PACKET_TYPE_CLIPBOARD_CONNECT, { + {QStringLiteral("content"), ClipboardListener::instance()->currentContent()}, + {QStringLiteral("timestamp"), ClipboardListener::instance()->updateTimestamp()} + }); + sendPacket(np); +} + + bool ClipboardPlugin::receivePacket(const NetworkPacket& np) { QString content = np.get(QStringLiteral("content")); - ClipboardListener::instance()->setText(content); - return true; + if (np.type() == PACKET_TYPE_CLIPBOARD) { + ClipboardListener::instance()->setText(content); + return true; + } else if (np.type() == PACKET_TYPE_CLIPBOARD_CONNECT) { + qint64 packetTime = np.get(QStringLiteral("timestamp")); + // If the packetTime is 0, it means the timestamp is unknown (so do nothing). + if (packetTime == 0 || packetTime < ClipboardListener::instance()->updateTimestamp()) { + return false; + } + + ClipboardListener::instance()->setText(content); + return true; + } + return false; } #include "clipboardplugin.moc" diff --git a/plugins/clipboard/clipboardplugin.h b/plugins/clipboard/clipboardplugin.h index 1e1805738..8d104b623 100644 --- a/plugins/clipboard/clipboardplugin.h +++ b/plugins/clipboard/clipboardplugin.h @@ -27,8 +27,32 @@ #include Q_DECLARE_LOGGING_CATEGORY(KDECONNECT_PLUGIN_CLIPBOARD) + +/** + * Packet containing just clipboard contents, sent when a device updates its clipboard. + *

+ * The body should look like so: + * { + * "content": "password" + * } + */ #define PACKET_TYPE_CLIPBOARD QStringLiteral("kdeconnect.clipboard") +/** + * Packet containing clipboard contents and a timestamp that the contents were last updated, sent + * on first connection + *

+ * The timestamp is milliseconds since epoch. It can be 0, which indicates that the clipboard + * update time is currently unknown. + *

+ * The body should look like so: + * { + * "timestamp": 542904563213, + * "content": "password" + * } + */ +#define PACKET_TYPE_CLIPBOARD_CONNECT QStringLiteral("kdeconnect.clipboard.connect") + class ClipboardPlugin : public KdeConnectPlugin { @@ -38,10 +62,10 @@ public: explicit ClipboardPlugin(QObject* parent, const QVariantList& args); bool receivePacket(const NetworkPacket& np) override; - void connected() override { } - + void connected() override; private Q_SLOTS: void propagateClipboard(const QString& content); + void sendConnectPacket(); }; diff --git a/plugins/clipboard/kdeconnect_clipboard.json b/plugins/clipboard/kdeconnect_clipboard.json index fba6cc009..040a439dd 100644 --- a/plugins/clipboard/kdeconnect_clipboard.json +++ b/plugins/clipboard/kdeconnect_clipboard.json @@ -129,9 +129,9 @@ "Website": "https://albertvaka.wordpress.com" }, "X-KdeConnect-OutgoingPacketType": [ - "kdeconnect.clipboard" + "kdeconnect.clipboard", "kdeconnect.clipboard.connect" ], "X-KdeConnect-SupportedPacketType": [ - "kdeconnect.clipboard" + "kdeconnect.clipboard", "kdeconnect.clipboard.connect" ] }