notifications: add initial support for desktop-to-xxx notifications

Eavesdrop on the Notify call of the org.freedesktop.Notifications
dbus-interface, proxy all caught notifications to our peer device and
track them in the internal notifications-list.

Also fix "cancel" requests from peer devices, by cutting of
kdeconnect-android's id-prefix.
This commit is contained in:
Holger Kaelberer 2015-09-10 16:36:03 +02:00
parent 2ca1d9720e
commit 2a74eb68f8
7 changed files with 171 additions and 3 deletions

View file

@ -4,6 +4,7 @@ set(kdeconnect_notifications_SRCS
notification.cpp notification.cpp
notificationsplugin.cpp notificationsplugin.cpp
notificationsdbusinterface.cpp notificationsdbusinterface.cpp
notificationslistener.cpp
) )
kdeconnect_add_plugin(kdeconnect_notifications JSON kdeconnect_notifications.json SOURCES ${kdeconnect_notifications_SRCS}) kdeconnect_add_plugin(kdeconnect_notifications JSON kdeconnect_notifications.json SOURCES ${kdeconnect_notifications_SRCS})

View file

@ -45,6 +45,7 @@ NotificationsDbusInterface::NotificationsDbusInterface(KdeConnectPlugin* plugin)
NotificationsDbusInterface::~NotificationsDbusInterface() NotificationsDbusInterface::~NotificationsDbusInterface()
{ {
qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "Destroying NotificationsDbusInterface";
} }
void NotificationsDbusInterface::clearNotifications() void NotificationsDbusInterface::clearNotifications()
@ -60,7 +61,21 @@ QStringList NotificationsDbusInterface::activeNotifications()
void NotificationsDbusInterface::processPackage(const NetworkPackage& np) void NotificationsDbusInterface::processPackage(const NetworkPackage& np)
{ {
if (np.get<bool>("isCancel")) { if (np.get<bool>("isCancel")) {
removeNotification(np.get<QString>("id")); QString id = np.get<QString>("id");
// cut off kdeconnect-android's prefix if there:
if (id.startsWith("org.kde.kdeconnect_tp::"))
id = id.mid(id.indexOf("::") + 2);
removeNotification(id);
} else if (np.get<bool>("isRequest")) {
for (const auto& n: mNotifications) {
NetworkPackage np(PACKAGE_TYPE_NOTIFICATION);
np.set("id", n->internalId());
np.set("appName", n->appName());
np.set("ticker", n->ticker());
np.set("isClearable", n->dismissable());
np.set("requestAnswer", true);
mPlugin->sendPackage(np);
}
} else { } else {
//TODO: Uncoment when we are able to display app icon on plasmoid //TODO: Uncoment when we are able to display app icon on plasmoid
@ -114,7 +129,7 @@ void NotificationsDbusInterface::removeNotification(const QString& internalId)
qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "removeNotification" << internalId; qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "removeNotification" << internalId;
if (!mInternalIdToPublicId.contains(internalId)) { if (!mInternalIdToPublicId.contains(internalId)) {
qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "Not found"; qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "Not found: " << internalId;
return; return;
} }

View file

@ -45,6 +45,7 @@ public:
void processPackage(const NetworkPackage& np); void processPackage(const NetworkPackage& np);
void clearNotifications(); void clearNotifications();
void dismissRequested(const QString& notification); void dismissRequested(const QString& notification);
void addNotification(Notification* noti);
public Q_SLOTS: public Q_SLOTS:
Q_SCRIPTABLE QStringList activeNotifications(); Q_SCRIPTABLE QStringList activeNotifications();
@ -54,7 +55,6 @@ Q_SIGNALS:
Q_SCRIPTABLE void notificationRemoved(const QString& publicId); Q_SCRIPTABLE void notificationRemoved(const QString& publicId);
private /*methods*/: private /*methods*/:
void addNotification(Notification* noti);
void removeNotification(const QString& internalId); void removeNotification(const QString& internalId);
QString newId(); //Generates successive identifitiers to use as public ids QString newId(); //Generates successive identifitiers to use as public ids

View file

@ -0,0 +1,101 @@
/**
* Copyright 2015 Holger Kaelberer <holger.k@elberer.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusInterface>
#include <QtDebug>
#include <QLoggingCategory>
#include <core/device.h>
#include <core/kdeconnectplugin.h>
#include "notificationslistener.h"
#include "notificationsplugin.h"
#include "notification_debug.h"
#include "notificationsdbusinterface.h"
NotificationsListener::NotificationsListener(KdeConnectPlugin* aPlugin,
NotificationsDbusInterface* aDbusInterface)
: QDBusAbstractAdaptor(aPlugin),
mPlugin(aPlugin),
dbusInterface(aDbusInterface)
{
bool ret = QDBusConnection::sessionBus()
.registerObject("/org/freedesktop/Notifications",
this,
QDBusConnection::ExportScriptableContents);
if (!ret)
qCWarning(KDECONNECT_PLUGIN_NOTIFICATION)
<< "Error registering notifications listener for device"
<< mPlugin->device()->name() << ":"
<< QDBusConnection::sessionBus().lastError();
else
qCDebug(KDECONNECT_PLUGIN_NOTIFICATION)
<< "Registered notifications listener for device"
<< mPlugin->device()->name();
QDBusInterface iface("org.freedesktop.DBus", "/org/freedesktop/DBus",
"org.freedesktop.DBus");
iface.call("AddMatch",
"interface='org.freedesktop.Notifications',member='Notify',type='method_call',eavesdrop='true'");
}
NotificationsListener::~NotificationsListener()
{
qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "Destroying NotificationsListener";
QDBusInterface iface("org.freedesktop.DBus", "/org/freedesktop/DBus",
"org.freedesktop.DBus");
QDBusMessage res = iface.call("RemoveMatch",
"interface='org.freedesktop.Notifications',member='Notify',type='method_call',eavesdrop='true'");
QDBusConnection::sessionBus().unregisterObject("/org/freedesktop/Notifications");
}
uint NotificationsListener::Notify(const QString &appName, uint replacesId,
const QString &appIcon,
const QString &summary, const QString &body,
const QStringList &actions,
const QVariantMap &hints, int timeout)
{
static int id = 0;
qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "Got notification appName=" << appName << "replacesId=" << replacesId << "appIcon=" << appIcon << "summary=" << summary << "body=" << body << "actions=" << actions << "hints=" << hints << "timeout=" << timeout;
Q_UNUSED(hints);
Q_UNUSED(actions);
Q_UNUSED(appIcon);
Q_UNUSED(body);
// skip our own notifications
if (appName == QLatin1String("KDE Connect"))
return 0;
NetworkPackage np(PACKAGE_TYPE_NOTIFICATION);
np.set("id", QString::number(replacesId > 0 ? replacesId : ++id));
np.set("appName", appName);
np.set("ticker", summary);
np.set("isClearable", timeout == 0); // KNotifications are persistent if
// timeout == 0, for other notifications
// clearability is pointless
Notification *notification = new Notification(np, QString(), this);
dbusInterface->addNotification(notification);
mPlugin->sendPackage(np);
return (replacesId > 0 ? replacesId : id);
}

View file

@ -0,0 +1,46 @@
/**
* Copyright 2015 Holger Kaelberer <holger.k@elberer.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QtDBus/QDBusAbstractAdaptor>
#include <core/device.h>
class KdeConnectPlugin;
class NotificationsDbusInterface;
class Notification;
class NotificationsListener : public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Notifications")
public:
explicit NotificationsListener(KdeConnectPlugin* aPlugin,
NotificationsDbusInterface* aDbusInterface);
virtual ~NotificationsListener();
private:
KdeConnectPlugin* mPlugin;
NotificationsDbusInterface* dbusInterface;
public Q_SLOTS:
Q_SCRIPTABLE uint Notify(const QString&, uint, const QString&,
const QString&, const QString&,
const QStringList&, const QVariantMap&, int);
};

View file

@ -21,6 +21,7 @@
#include "notificationsplugin.h" #include "notificationsplugin.h"
#include "notificationsdbusinterface.h" #include "notificationsdbusinterface.h"
#include "notificationslistener.h"
#include "notification_debug.h" #include "notification_debug.h"
#include <KPluginFactory> #include <KPluginFactory>
@ -33,6 +34,7 @@ NotificationsPlugin::NotificationsPlugin(QObject* parent, const QVariantList& ar
: KdeConnectPlugin(parent, args) : KdeConnectPlugin(parent, args)
{ {
notificationsDbusInterface = new NotificationsDbusInterface(this); notificationsDbusInterface = new NotificationsDbusInterface(this);
notificationsListener = new NotificationsListener(this, notificationsDbusInterface);
} }
void NotificationsPlugin::connected() void NotificationsPlugin::connected()
@ -44,6 +46,7 @@ void NotificationsPlugin::connected()
NotificationsPlugin::~NotificationsPlugin() NotificationsPlugin::~NotificationsPlugin()
{ {
qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "Destroying NotificationsPlugin";
//FIXME: Qt dbus does not allow to remove an adaptor! (it causes a crash in //FIXME: Qt dbus does not allow to remove an adaptor! (it causes a crash in
// the next dbus access to its parent). The implication of not deleting this // the next dbus access to its parent). The implication of not deleting this
// is that disabling the plugin leaks the interface. As a mitigation we are // is that disabling the plugin leaks the interface. As a mitigation we are

View file

@ -32,6 +32,7 @@
* KdeConnectPlugin at the same time (both are QObject) * KdeConnectPlugin at the same time (both are QObject)
*/ */
class NotificationsDbusInterface; class NotificationsDbusInterface;
class NotificationsListener;
class NotificationsPlugin class NotificationsPlugin
: public KdeConnectPlugin : public KdeConnectPlugin
@ -48,6 +49,7 @@ public Q_SLOTS:
private: private:
NotificationsDbusInterface* notificationsDbusInterface; NotificationsDbusInterface* notificationsDbusInterface;
NotificationsListener* notificationsListener;
}; };