Compare commits

...

1 commit

Author SHA1 Message Date
Kristen McWilliam
caaac80ca5
fix: copy auth code to desktop clipboard
SMS auth codes generate a notification that has a "Copy $code" button
(eg. "Copy 123456"), but the button doesn't actually copy the code as
expected. This patch fixes that.

BUG: 451539

BUG: 435614
2024-10-17 13:51:23 -04:00
5 changed files with 136 additions and 1 deletions

View file

@ -5,8 +5,9 @@ target_link_libraries(kdeconnect_notifications
kdeconnectcore kdeconnectcore
Qt::DBus Qt::DBus
Qt::Widgets Qt::Widgets
KF6::Notifications KF6::GuiAddons
KF6::I18n KF6::I18n
KF6::Notifications
KF6::WindowSystem KF6::WindowSystem
) )

View file

@ -11,11 +11,15 @@
#include <dbushelper.h> #include <dbushelper.h>
#include <KPluginFactory> #include <KPluginFactory>
#include <KSystemClipboard>
#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) #if !defined(Q_OS_WIN) && !defined(Q_OS_MAC)
#include <KStartupInfo> #include <KStartupInfo>
#include <private/qtx11extras_p.h> #include <private/qtx11extras_p.h>
#endif #endif
#include <QMimeData>
K_PLUGIN_CLASS_WITH_JSON(NotificationsPlugin, "kdeconnect_notifications.json") K_PLUGIN_CLASS_WITH_JSON(NotificationsPlugin, "kdeconnect_notifications.json")
void NotificationsPlugin::connected() void NotificationsPlugin::connected()
@ -175,6 +179,32 @@ void NotificationsPlugin::sendAction(const QString &key, const QString &action)
np.set<QString>(QStringLiteral("key"), key); np.set<QString>(QStringLiteral("key"), key);
np.set<QString>(QStringLiteral("action"), action); np.set<QString>(QStringLiteral("action"), action);
sendPacket(np); sendPacket(np);
copyAuthCodeIfPresent(action);
}
void NotificationsPlugin::copyAuthCodeIfPresent(const QString &action)
{
// The auth code we receive has invisible characters in it for some reason.
// (U+2063 INVISIBLE SEPARATOR between each digit).
// Remove them if present before continuing.
QString sanitizedAction = action;
sanitizedAction.remove(QChar(0x2063));
// Match blocks of digits, 4-10 digits long. This should match auth codes
// in any language without relying on the action text having a specific
// keyword in it such as "Copy" in English.
QRegularExpression authCodeRegex(QStringLiteral("\\b(\\d{4,10})\\b"));
QRegularExpressionMatch match = authCodeRegex.match(sanitizedAction);
if (!match.hasMatch()) {
return;
}
QString text = match.captured(1);
auto mimeData = new QMimeData;
mimeData->setText(text);
KSystemClipboard::instance()->setMimeData(mimeData, QClipboard::Clipboard);
} }
QString NotificationsPlugin::newId() QString NotificationsPlugin::newId()

View file

@ -43,6 +43,14 @@ Q_SIGNALS:
Q_SCRIPTABLE void allNotificationsRemoved(); Q_SCRIPTABLE void allNotificationsRemoved();
private: private:
/**
* Check if the action is to copy an auth code, if so add the code to the desktop clipboard.
*
* This is necessary since access to the Android clipboard has become more restricted for apps
* that are not the foreground app since Android 10.
*/
void copyAuthCodeIfPresent(const QString &action);
void removeNotification(const QString &internalId); void removeNotification(const QString &internalId);
QString newId(); // Generates successive identifiers to use as public ids QString newId(); // Generates successive identifiers to use as public ids
void notificationReady(); void notificationReady();

View file

@ -5,8 +5,10 @@ set(kdeconnect_libraries
kdeconnectinterfaces kdeconnectinterfaces
kdeconnectsmshelper kdeconnectsmshelper
kdeconnectversion kdeconnectversion
KF6::GuiAddons
KF6::I18n KF6::I18n
KF6::KIOWidgets KF6::KIOWidgets
KF6::Notifications
Qt::DBus Qt::DBus
Qt::Network Qt::Network
KF6::People KF6::People
@ -14,6 +16,7 @@ set(kdeconnect_libraries
Qt::Test Qt::Test
) )
ecm_add_test(notificationstest.cpp LINK_LIBRARIES ${kdeconnect_libraries})
ecm_add_test(pluginloadtest.cpp LINK_LIBRARIES ${kdeconnect_libraries}) ecm_add_test(pluginloadtest.cpp LINK_LIBRARIES ${kdeconnect_libraries})
ecm_add_test(sendfiletest.cpp LINK_LIBRARIES ${kdeconnect_libraries}) ecm_add_test(sendfiletest.cpp LINK_LIBRARIES ${kdeconnect_libraries})
ecm_add_test(smshelpertest.cpp LINK_LIBRARIES ${kdeconnect_libraries}) ecm_add_test(smshelpertest.cpp LINK_LIBRARIES ${kdeconnect_libraries})

View file

@ -0,0 +1,93 @@
/**
* SPDX-FileCopyrightText: 2024 Kristen McWilliam <kmcwilliampublic@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#include "kdeconnectplugin.h"
#include "testdaemon.h"
#include <KSystemClipboard>
#include <QtTest>
/**
* Test the NotificationsPlugin class
*/
class NotificationsPluginTest : public QObject
{
Q_OBJECT
public:
NotificationsPluginTest()
{
QStandardPaths::setTestModeEnabled(true);
m_daemon = new TestDaemon;
}
private:
TestDaemon *m_daemon;
private Q_SLOTS:
/**
* If the user selects the action to copy an auth code, the auth code should
* be copied to the desktop clipboard.
*/
void testAuthCodeIsCopied()
{
Device *device = nullptr;
const QList<Device *> devicesList = m_daemon->devicesList();
for (Device *id : devicesList) {
if (id->isReachable()) {
if (!id->isPaired())
id->requestPairing();
device = id;
}
}
if (device == nullptr) {
QFAIL("Unable to determine device");
}
QCOMPARE(device->isReachable(), true);
QCOMPARE(device->isPaired(), true);
KdeConnectPlugin *plugin = device->plugin(QStringLiteral("kdeconnect_notifications"));
QVERIFY(plugin);
const QString key = QStringLiteral("0|com.google.android.apps.messaging|2|com.google.android.apps.messaging:incoming_message:23|10129");
// Note that the auth code we receive to work with has invisible
// characters in it for some reason. (U+2063 INVISIBLE SEPARATOR between
// each digit)
//
// `action` here is equal to `Copy \"671733\"`, but with the invisible
// characters written out explicitly to make it easier to read, and to
// prevent confusion when reading the test code.
const QString action = QStringLiteral("Copy \"6\u20637\u20631\u20637\u20633\u20633\"");
const QString expectedClipboardContents = QStringLiteral("671733");
// Verify that the clipboard does not contain the auth code already
const QString originalClipboardContents = KSystemClipboard::instance()->text(QClipboard::Clipboard);
QVERIFY(originalClipboardContents != expectedClipboardContents);
// Send the action
plugin->metaObject()->invokeMethod(plugin, "sendAction", Q_ARG(QString, key), Q_ARG(QString, action));
// Verify that the clipboard now contains the auth code
const QString updatedClipboardContents = KSystemClipboard::instance()->text(QClipboard::Clipboard);
QCOMPARE(updatedClipboardContents, expectedClipboardContents);
// Set the clipboard back to its original state
auto mimeData = new QMimeData;
mimeData->setText(originalClipboardContents);
KSystemClipboard::instance()->setMimeData(mimeData, QClipboard::Clipboard);
}
};
QTEST_MAIN(NotificationsPluginTest);
#include "notificationstest.moc"