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
This commit is contained in:
Colin Redman 2019-07-23 16:21:39 +02:00 committed by Albert Vaca Cintora
parent 88e0ffa01a
commit 7799973371
5 changed files with 82 additions and 14 deletions

View file

@ -38,16 +38,28 @@ void ClipboardListener::updateClipboard(QClipboard::Mode mode)
QString content = clipboard->text(); QString content = clipboard->text();
if (content == currentContent) { if (content == m_currentContent) {
return; return;
} }
currentContent = content; m_updateTimestamp = QDateTime::currentDateTime().toMSecsSinceEpoch();
m_currentContent = content;
Q_EMIT clipboardChanged(content); Q_EMIT clipboardChanged(content);
} }
QString ClipboardListener::currentContent()
{
return m_currentContent;
}
qint64 ClipboardListener::updateTimestamp(){
return m_updateTimestamp;
}
void ClipboardListener::setText(const QString& content) void ClipboardListener::setText(const QString& content)
{ {
currentContent = content; m_updateTimestamp = QDateTime::currentDateTime().toMSecsSinceEpoch();
m_currentContent = content;
clipboard->setText(content); clipboard->setText(content);
} }

View file

@ -21,6 +21,7 @@
#ifndef CLIPBOARDLISTENER_H #ifndef CLIPBOARDLISTENER_H
#define CLIPBOARDLISTENER_H #define CLIPBOARDLISTENER_H
#include <QDateTime>
#include <QTimer> #include <QTimer>
#include <QObject> #include <QObject>
#include <QClipboard> #include <QClipboard>
@ -35,7 +36,8 @@ class ClipboardListener : public QObject
private: private:
ClipboardListener(); ClipboardListener();
QString currentContent; QString m_currentContent;
qint64 m_updateTimestamp = 0;
QClipboard* clipboard; QClipboard* clipboard;
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
QTimer m_clipboardMonitorTimer; QTimer m_clipboardMonitorTimer;
@ -56,6 +58,9 @@ public:
void setText(const QString& content); void setText(const QString& content);
QString currentContent();
qint64 updateTimestamp();
Q_SIGNALS: Q_SIGNALS:
void clipboardChanged(const QString& content); void clipboardChanged(const QString& content);
}; };

View file

@ -35,17 +35,44 @@ ClipboardPlugin::ClipboardPlugin(QObject* parent, const QVariantList& args)
this, &ClipboardPlugin::propagateClipboard); this, &ClipboardPlugin::propagateClipboard);
} }
void ClipboardPlugin::connected()
{
sendConnectPacket();
}
void ClipboardPlugin::propagateClipboard(const QString& content) void ClipboardPlugin::propagateClipboard(const QString& content)
{ {
NetworkPacket np(PACKET_TYPE_CLIPBOARD, {{QStringLiteral("content"), content}}); NetworkPacket np(PACKET_TYPE_CLIPBOARD, {{QStringLiteral("content"), content}});
sendPacket(np); 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) bool ClipboardPlugin::receivePacket(const NetworkPacket& np)
{ {
QString content = np.get<QString>(QStringLiteral("content")); QString content = np.get<QString>(QStringLiteral("content"));
ClipboardListener::instance()->setText(content); if (np.type() == PACKET_TYPE_CLIPBOARD) {
return true; ClipboardListener::instance()->setText(content);
return true;
} else if (np.type() == PACKET_TYPE_CLIPBOARD_CONNECT) {
qint64 packetTime = np.get<qint64>(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" #include "clipboardplugin.moc"

View file

@ -27,8 +27,32 @@
#include <core/kdeconnectplugin.h> #include <core/kdeconnectplugin.h>
Q_DECLARE_LOGGING_CATEGORY(KDECONNECT_PLUGIN_CLIPBOARD) Q_DECLARE_LOGGING_CATEGORY(KDECONNECT_PLUGIN_CLIPBOARD)
/**
* Packet containing just clipboard contents, sent when a device updates its clipboard.
* <p>
* The body should look like so:
* {
* "content": "password"
* }
*/
#define PACKET_TYPE_CLIPBOARD QStringLiteral("kdeconnect.clipboard") #define PACKET_TYPE_CLIPBOARD QStringLiteral("kdeconnect.clipboard")
/**
* Packet containing clipboard contents and a timestamp that the contents were last updated, sent
* on first connection
* <p>
* The timestamp is milliseconds since epoch. It can be 0, which indicates that the clipboard
* update time is currently unknown.
* <p>
* The body should look like so:
* {
* "timestamp": 542904563213,
* "content": "password"
* }
*/
#define PACKET_TYPE_CLIPBOARD_CONNECT QStringLiteral("kdeconnect.clipboard.connect")
class ClipboardPlugin class ClipboardPlugin
: public KdeConnectPlugin : public KdeConnectPlugin
{ {
@ -38,10 +62,10 @@ public:
explicit ClipboardPlugin(QObject* parent, const QVariantList& args); explicit ClipboardPlugin(QObject* parent, const QVariantList& args);
bool receivePacket(const NetworkPacket& np) override; bool receivePacket(const NetworkPacket& np) override;
void connected() override { } void connected() override;
private Q_SLOTS: private Q_SLOTS:
void propagateClipboard(const QString& content); void propagateClipboard(const QString& content);
void sendConnectPacket();
}; };

View file

@ -129,9 +129,9 @@
"Website": "https://albertvaka.wordpress.com" "Website": "https://albertvaka.wordpress.com"
}, },
"X-KdeConnect-OutgoingPacketType": [ "X-KdeConnect-OutgoingPacketType": [
"kdeconnect.clipboard" "kdeconnect.clipboard", "kdeconnect.clipboard.connect"
], ],
"X-KdeConnect-SupportedPacketType": [ "X-KdeConnect-SupportedPacketType": [
"kdeconnect.clipboard" "kdeconnect.clipboard", "kdeconnect.clipboard.connect"
] ]
} }