diff --git a/libnotificationmanager/CMakeLists.txt b/libnotificationmanager/CMakeLists.txt
--- a/libnotificationmanager/CMakeLists.txt
+++ b/libnotificationmanager/CMakeLists.txt
@@ -14,11 +14,13 @@
notifications.cpp
notification.cpp
+ abstractnotificationsmodel.cpp
notificationsmodel.cpp
notificationfilterproxymodel.cpp
notificationsortproxymodel.cpp
notificationgroupingproxymodel.cpp
notificationgroupcollapsingproxymodel.cpp
+ watchednotificationsmodel.cpp
jobsmodel.cpp
jobsmodel_p.cpp
@@ -46,6 +48,8 @@
# DBus
# Notifications
qt5_add_dbus_adaptor(notificationmanager_LIB_SRCS dbus/org.freedesktop.Notifications.xml server_p.h NotificationManager::ServerPrivate)
+qt5_add_dbus_adaptor(notificationmanager_LIB_SRCS dbus/org.kde.notificationmanager.xml server_p.h NotificationManager::ServerPrivate)
+qt5_add_dbus_interface(notificationmanager_LIB_SRCS dbus/org.freedesktop.Notifications.xml fdonotifications_interface)
# JobView
qt5_add_dbus_adaptor(notificationmanager_LIB_SRCS dbus/org.kde.kuiserver.xml jobsmodel_p.h NotificationManager::JobsModelPrivate)
qt5_add_dbus_adaptor(notificationmanager_LIB_SRCS dbus/org.kde.JobViewServer.xml jobsmodel_p.h NotificationManager::JobsModelPrivate)
diff --git a/libnotificationmanager/notificationsmodel.h b/libnotificationmanager/abstractnotificationsmodel.h
copy from libnotificationmanager/notificationsmodel.h
copy to libnotificationmanager/abstractnotificationsmodel.h
--- a/libnotificationmanager/notificationsmodel.h
+++ b/libnotificationmanager/abstractnotificationsmodel.h
@@ -18,26 +18,27 @@
* License along with this library. If not, see .
*/
-#pragma once
+#ifndef ABSTRACTNOTIFICATIONSMODEL_H
+#define ABSTRACTNOTIFICATIONSMODEL_H
#include
#include
#include
+#include
#include "notifications.h"
+#include "notification.h"
+#include "server.h"
namespace NotificationManager
{
-class NotificationsModel : public QAbstractListModel
+class Q_DECL_EXPORT AbstractNotificationsModel : public QAbstractListModel
{
Q_OBJECT
public:
- ~NotificationsModel() override;
-
- using Ptr = QSharedPointer;
- static Ptr createNotificationsModel();
+ ~AbstractNotificationsModel() override;
QDateTime lastRead() const;
void setLastRead(const QDateTime &lastRead);
@@ -47,13 +48,15 @@
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QHash roleNames() const override;
- void expire(uint notificationId);
- void close(uint notificationId);
- void configure(uint notificationId);
- void configure(const QString &desktopEntry, const QString ¬ifyRcName, const QString &eventId);
- void invokeDefaultAction(uint notificationId);
- void invokeAction(uint notificationId, const QString &actionName);
- void reply(uint notificationId, const QString &text);
+ virtual void expire(uint notificationId) = 0;
+ virtual void close(uint notificationId) = 0;
+
+ // Currently configure actions are not exposed in AbstractNotificationsModel to keep it very minimal
+ // if usecase for this comes up in future, we can revisit it.
+
+ virtual void invokeDefaultAction(uint notificationId) = 0;
+ virtual void invokeAction(uint notificationId, const QString &actionName) = 0;
+ virtual void reply(uint notificationId, const QString &text) = 0;
void startTimeout(uint notificationId);
void stopTimeout(uint notificationId);
@@ -63,13 +66,25 @@
signals:
void lastReadChanged();
+protected:
+ AbstractNotificationsModel();
+ void onNotificationAdded(const Notification ¬ification);
+ void onNotificationReplaced(uint replacedId, const Notification ¬ification);
+ void onNotificationRemoved(uint notificationId, Server::CloseReason reason);
+
+ void setupNotificationTimeout(const Notification ¬ification);
+ const QVector& notifications();
+ int rowOfNotification(uint id) const;
+
+
private:
class Private;
QScopedPointer d;
- NotificationsModel();
- Q_DISABLE_COPY(NotificationsModel)
+ Q_DISABLE_COPY(AbstractNotificationsModel)
};
} // namespace NotificationManager
+
+#endif //ABSTRACTNOTIFICATIONSMODEL_H
diff --git a/libnotificationmanager/notificationsmodel.cpp b/libnotificationmanager/abstractnotificationsmodel.cpp
copy from libnotificationmanager/notificationsmodel.cpp
copy to libnotificationmanager/abstractnotificationsmodel.cpp
--- a/libnotificationmanager/notificationsmodel.cpp
+++ b/libnotificationmanager/abstractnotificationsmodel.cpp
@@ -18,8 +18,8 @@
* License along with this library. If not, see .
*/
-#include "notificationsmodel.h"
-
+#include "abstractnotificationsmodel.h"
+#include "abstractnotificationsmodel_p.h"
#include "debug.h"
#include "server.h"
@@ -43,46 +43,20 @@
using namespace NotificationManager;
-class Q_DECL_HIDDEN NotificationsModel::Private
-{
-public:
- explicit Private(NotificationsModel *q);
- ~Private();
-
- void onNotificationAdded(const Notification ¬ification);
- void onNotificationReplaced(uint replacedId, const Notification ¬ification);
- void onNotificationRemoved(uint notificationId, Server::CloseReason reason);
-
- void setupNotificationTimeout(const Notification ¬ification);
-
- int rowOfNotification(uint id) const;
-
- NotificationsModel *q;
-
- QVector notifications;
- // Fallback timeout to ensure all notifications expire eventually
- // otherwise when it isn't shown to the user and doesn't expire
- // an app might wait indefinitely for the notification to do so
- QHash notificationTimeouts;
-
- QDateTime lastRead;
-
-};
-
-NotificationsModel::Private::Private(NotificationsModel *q)
+AbstractNotificationsModel::Private::Private(AbstractNotificationsModel *q)
: q(q)
, lastRead(QDateTime::currentDateTimeUtc())
{
}
-NotificationsModel::Private::~Private()
+AbstractNotificationsModel::Private::~Private()
{
qDeleteAll(notificationTimeouts);
notificationTimeouts.clear();
}
-void NotificationsModel::Private::onNotificationAdded(const Notification ¬ification)
+void AbstractNotificationsModel::Private::onNotificationAdded(const Notification ¬ification)
{
// Once we reach a certain insane number of notifications discard some old ones
// as we keep pixmaps around etc
@@ -104,9 +78,9 @@
q->endInsertRows();
}
-void NotificationsModel::Private::onNotificationReplaced(uint replacedId, const Notification ¬ification)
+void AbstractNotificationsModel::Private::onNotificationReplaced(uint replacedId, const Notification ¬ification)
{
- const int row = rowOfNotification(replacedId);
+ const int row = q->rowOfNotification(replacedId);
if (row == -1) {
qCWarning(NOTIFICATIONMANAGER) << "Trying to replace notification with id" << replacedId << "which doesn't exist, creating a new one. This is an application bug!";
@@ -121,9 +95,9 @@
emit q->dataChanged(idx, idx);
}
-void NotificationsModel::Private::onNotificationRemoved(uint removedId, Server::CloseReason reason)
+void AbstractNotificationsModel::Private::onNotificationRemoved(uint removedId, Server::CloseReason reason)
{
- const int row = rowOfNotification(removedId);
+ const int row = q->rowOfNotification(removedId);
if (row == -1) {
return;
}
@@ -161,7 +135,7 @@
q->endRemoveRows();
}
-void NotificationsModel::Private::setupNotificationTimeout(const Notification ¬ification)
+void AbstractNotificationsModel::Private::setupNotificationTimeout(const Notification ¬ification)
{
if (notification.timeout() == 0) {
// In case it got replaced by a persistent notification
@@ -187,72 +161,41 @@
timer->start();
}
-int NotificationsModel::Private::rowOfNotification(uint id) const
+int AbstractNotificationsModel::rowOfNotification(uint id) const
{
- auto it = std::find_if(notifications.constBegin(), notifications.constEnd(), [id](const Notification &item) {
+ auto it = std::find_if(d->notifications.constBegin(), d->notifications.constEnd(), [id](const Notification &item) {
return item.id() == id;
});
- if (it == notifications.constEnd()) {
+ if (it == d->notifications.constEnd()) {
return -1;
}
- return std::distance(notifications.constBegin(), it);
+ return std::distance(d->notifications.constBegin(), it);
}
-NotificationsModel::NotificationsModel()
+AbstractNotificationsModel::AbstractNotificationsModel()
: QAbstractListModel(nullptr)
, d(new Private(this))
{
- connect(&Server::self(), &Server::notificationAdded, this, [this](const Notification ¬ification) {
- d->onNotificationAdded(notification);
- });
- connect(&Server::self(), &Server::notificationReplaced, this, [this](uint replacedId, const Notification ¬ification) {
- d->onNotificationReplaced(replacedId, notification);
- });
- connect(&Server::self(), &Server::notificationRemoved, this, [this](uint removedId, Server::CloseReason reason) {
- d->onNotificationRemoved(removedId, reason);
- });
- connect(&Server::self(), &Server::serviceOwnershipLost, this, [this] {
- // Expire all notifications as we're defunct now
- const auto notifications = d->notifications;
- for (const Notification ¬ification : notifications) {
- if (!notification.expired()) {
- d->onNotificationRemoved(notification.id(), Server::CloseReason::Expired);
- }
- }
- });
-
- Server::self().init();
}
-NotificationsModel::~NotificationsModel() = default;
+AbstractNotificationsModel::~AbstractNotificationsModel() = default;
-NotificationsModel::Ptr NotificationsModel::createNotificationsModel()
-{
- static QWeakPointer s_instance;
- if (!s_instance) {
- QSharedPointer ptr(new NotificationsModel());
- s_instance = ptr.toWeakRef();
- return ptr;
- }
- return s_instance.toStrongRef();
-}
-
-QDateTime NotificationsModel::lastRead() const
+QDateTime AbstractNotificationsModel::lastRead() const
{
return d->lastRead;
}
-void NotificationsModel::setLastRead(const QDateTime &lastRead)
+void AbstractNotificationsModel::setLastRead(const QDateTime &lastRead)
{
if (d->lastRead != lastRead) {
d->lastRead = lastRead;
emit lastReadChanged();
}
}
-QVariant NotificationsModel::data(const QModelIndex &index, int role) const
+QVariant AbstractNotificationsModel::data(const QModelIndex &index, int role) const
{
if (!checkIndex(index)) {
return QVariant();
@@ -322,7 +265,7 @@
return QVariant();
}
-bool NotificationsModel::setData(const QModelIndex &index, const QVariant &value, int role)
+bool AbstractNotificationsModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!checkIndex(index)) {
return false;
@@ -342,134 +285,23 @@
return false;
}
-int NotificationsModel::rowCount(const QModelIndex &parent) const
+int AbstractNotificationsModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return 0;
}
return d->notifications.count();
}
-QHash NotificationsModel::roleNames() const
+QHash AbstractNotificationsModel::roleNames() const
{
return Utils::roleNames();
}
-void NotificationsModel::expire(uint notificationId)
-{
- if (d->rowOfNotification(notificationId) > -1) {
- Server::self().closeNotification(notificationId, Server::CloseReason::Expired);
- }
-}
-
-void NotificationsModel::close(uint notificationId)
+void AbstractNotificationsModel::startTimeout(uint notificationId)
{
- if (d->rowOfNotification(notificationId) > -1) {
- Server::self().closeNotification(notificationId, Server::CloseReason::DismissedByUser);
- }
-}
-
-void NotificationsModel::configure(uint notificationId)
-{
- const int row = d->rowOfNotification(notificationId);
- if (row == -1) {
- return;
- }
-
- const Notification ¬ification = d->notifications.at(row);
-
- if (notification.d->hasConfigureAction) {
- Server::self().invokeAction(notificationId, QStringLiteral("settings")); // FIXME make a static Notification::configureActionName() or something
- return;
- }
-
- if (!notification.desktopEntry().isEmpty() || !notification.notifyRcName().isEmpty()) {
- configure(notification.desktopEntry(), notification.notifyRcName(), notification.eventId());
- return;
- }
-
- qCWarning(NOTIFICATIONMANAGER) << "Trying to configure notification" << notificationId << "which isn't configurable";
-}
-
-void NotificationsModel::configure(const QString &desktopEntry, const QString ¬ifyRcName, const QString &eventId)
-{
- // TODO would be nice to just have a signal but since NotificationsModel is shared,
- // if we connect to this from Notifications you would get a signal in every instance
- // and potentially open the config dialog multiple times.
-
- QStringList args;
- if (!desktopEntry.isEmpty()) {
- args.append(QStringLiteral("--desktop-entry"));
- args.append(desktopEntry);
- }
- if (!notifyRcName.isEmpty()) {
- args.append(QStringLiteral("--notifyrc"));
- args.append(notifyRcName);
- }
- if (!eventId.isEmpty()) {
- args.append(QStringLiteral("--event-id"));
- args.append(eventId);
- }
-
- QProcess::startDetached(QStringLiteral("kcmshell5"), {
- QStringLiteral("notifications"),
- QStringLiteral("--args"),
- KShell::joinArgs(args)
- });
-}
-
-void NotificationsModel::invokeDefaultAction(uint notificationId)
-{
- const int row = d->rowOfNotification(notificationId);
- if (row == -1) {
- return;
- }
-
- const Notification ¬ification = d->notifications.at(row);
- if (!notification.hasDefaultAction()) {
- qCWarning(NOTIFICATIONMANAGER) << "Trying to invoke default action on notification" << notificationId << "which doesn't have one";
- return;
- }
-
- Server::self().invokeAction(notificationId, QStringLiteral("default")); // FIXME make a static Notification::defaultActionName() or something
-}
-
-void NotificationsModel::invokeAction(uint notificationId, const QString &actionName)
-{
- const int row = d->rowOfNotification(notificationId);
- if (row == -1) {
- return;
- }
-
- const Notification ¬ification = d->notifications.at(row);
- if (!notification.actionNames().contains(actionName)) {
- qCWarning(NOTIFICATIONMANAGER) << "Trying to invoke action" << actionName << "on notification" << notificationId << "which it doesn't have";
- return;
- }
-
- Server::self().invokeAction(notificationId, actionName);
-}
-
-void NotificationsModel::reply(uint notificationId, const QString &text)
-{
- const int row = d->rowOfNotification(notificationId);
- if (row == -1) {
- return;
- }
-
- const Notification ¬ification = d->notifications.at(row);
- if (!notification.hasReplyAction()) {
- qCWarning(NOTIFICATIONMANAGER) << "Trying to reply to a notification which doesn't have a reply action";
- return;
- }
-
- Server::self().reply(notification.dBusService(), notificationId, text);
-}
-
-void NotificationsModel::startTimeout(uint notificationId)
-{
- const int row = d->rowOfNotification(notificationId);
+ const int row = rowOfNotification(notificationId);
if (row == -1) {
return;
}
@@ -483,12 +315,12 @@
d->setupNotificationTimeout(notification);
}
-void NotificationsModel::stopTimeout(uint notificationId)
+void AbstractNotificationsModel::stopTimeout(uint notificationId)
{
delete d->notificationTimeouts.take(notificationId);
}
-void NotificationsModel::clear(Notifications::ClearFlags flags)
+void AbstractNotificationsModel::clear(Notifications::ClearFlags flags)
{
if (d->notifications.isEmpty()) {
return;
@@ -534,3 +366,28 @@
endRemoveRows();
}
}
+
+void AbstractNotificationsModel::onNotificationAdded(const Notification ¬ification)
+{
+ d->onNotificationAdded(notification);
+}
+
+void AbstractNotificationsModel::onNotificationReplaced(uint replacedId, const Notification ¬ification)
+{
+ d->onNotificationReplaced(replacedId, notification);
+}
+
+void AbstractNotificationsModel::onNotificationRemoved(uint notificationId, Server::CloseReason reason)
+{
+ d->onNotificationRemoved(notificationId, reason);
+}
+
+void AbstractNotificationsModel::setupNotificationTimeout(const Notification ¬ification)
+{
+ d->setupNotificationTimeout(notification);
+}
+
+const QVector& AbstractNotificationsModel::notifications()
+{
+ return d->notifications;
+}
diff --git a/libnotificationmanager/abstractnotificationsmodel_p.h b/libnotificationmanager/abstractnotificationsmodel_p.h
new file mode 100644
--- /dev/null
+++ b/libnotificationmanager/abstractnotificationsmodel_p.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018-2019 Kai Uwe Broulik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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 6 of version 3 of the license.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ */
+
+#ifndef ABSTRACTNOTIFICATIONSMODEL_P_H
+#define ABSTRACTNOTIFICATIONSMODEL_P_H
+
+#include "notification.h"
+#include "server.h"
+
+#include
+
+namespace NotificationManager
+{
+
+class Q_DECL_HIDDEN AbstractNotificationsModel::Private
+{
+public:
+ explicit Private(AbstractNotificationsModel *q);
+ ~Private();
+
+ void onNotificationAdded(const Notification ¬ification);
+ void onNotificationReplaced(uint replacedId, const Notification ¬ification);
+ void onNotificationRemoved(uint notificationId, Server::CloseReason reason);
+
+ void setupNotificationTimeout(const Notification ¬ification);
+
+ AbstractNotificationsModel *q;
+
+ QVector notifications;
+ // Fallback timeout to ensure all notifications expire eventually
+ // otherwise when it isn't shown to the user and doesn't expire
+ // an app might wait indefinitely for the notification to do so
+ QHash notificationTimeouts;
+
+ QDateTime lastRead;
+
+};
+
+}
+
+#endif // ABSTRACTNOTIFICATIONSMODEL_P_H
diff --git a/libnotificationmanager/dbus/org.kde.notificationmanager.xml b/libnotificationmanager/dbus/org.kde.notificationmanager.xml
new file mode 100644
--- /dev/null
+++ b/libnotificationmanager/dbus/org.kde.notificationmanager.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/libnotificationmanager/declarative/notificationmanagerplugin.cpp b/libnotificationmanager/declarative/notificationmanagerplugin.cpp
--- a/libnotificationmanager/declarative/notificationmanagerplugin.cpp
+++ b/libnotificationmanager/declarative/notificationmanagerplugin.cpp
@@ -25,6 +25,7 @@
#include "server.h"
#include "serverinfo.h"
#include "settings.h"
+#include "watchednotificationsmodel.h"
#include
@@ -42,4 +43,5 @@
return &Server::self();
});
qmlRegisterUncreatableType(uri, 1, 0, "ServerInfo", QStringLiteral("Can only access ServerInfo via Server"));
+ qmlRegisterType(uri, 1, 1, "WatchedNotificationsModel");
}
diff --git a/libnotificationmanager/notification.h b/libnotificationmanager/notification.h
--- a/libnotificationmanager/notification.h
+++ b/libnotificationmanager/notification.h
@@ -70,6 +70,11 @@
QString body() const;
void setBody(const QString &body);
+ // This returns the raw body data as provided by the notification
+ // this is useful when you want to html sanitization at different
+ // stage then the notification server.
+ QString rawBody() const;
+
QString icon() const;
void setIcon(const QString &icon);
@@ -124,10 +129,15 @@
bool dismissed() const;
void setDismissed(bool dismissed);
+ // Little bit of mess here, we want to sometime keep track of processed hints, and not process it.
+ QVariantMap hints() const;
+ void setHints(const QVariantMap &hints);
+
void processHints(const QVariantMap &hints);
private:
friend class NotificationsModel;
+ friend class AbstractNotificationsModel;
friend class ServerPrivate;
class Private;
diff --git a/libnotificationmanager/notification.cpp b/libnotificationmanager/notification.cpp
--- a/libnotificationmanager/notification.cpp
+++ b/libnotificationmanager/notification.cpp
@@ -532,9 +532,15 @@
void Notification::setBody(const QString &body)
{
+ d->rawBody = body;
d->body = Private::sanitize(body.trimmed());
}
+QString Notification::rawBody() const
+{
+ return d->rawBody;
+}
+
QString Notification::icon() const
{
return d->icon;
@@ -750,6 +756,16 @@
d->dismissed = dismissed;
}
+QVariantMap Notification::hints() const
+{
+ return d->hints;
+}
+
+void Notification::setHints(const QVariantMap &hints)
+{
+ d->hints = hints;
+}
+
void Notification::processHints(const QVariantMap &hints)
{
d->processHints(hints);
diff --git a/libnotificationmanager/notification_p.h b/libnotificationmanager/notification_p.h
--- a/libnotificationmanager/notification_p.h
+++ b/libnotificationmanager/notification_p.h
@@ -66,6 +66,8 @@
QString summary;
QString body;
+ // raw body text without sanitize called.
+ QString rawBody;
// Can be theme icon name or path
QString icon;
QImage image;
@@ -97,6 +99,7 @@
QString replySubmitButtonIconName;
QList urls;
+ QVariantMap hints = QVariantMap();
bool userActionFeedback = false;
Notifications::Urgency urgency = Notifications::NormalUrgency;
diff --git a/libnotificationmanager/notificationsmodel.h b/libnotificationmanager/notificationsmodel.h
--- a/libnotificationmanager/notificationsmodel.h
+++ b/libnotificationmanager/notificationsmodel.h
@@ -1,4 +1,5 @@
/*
+ * Copyright 2020 Shah Bhushan
* Copyright 2018-2019 Kai Uwe Broulik
*
* This library is free software; you can redistribute it and/or
@@ -20,56 +21,27 @@
#pragma once
-#include
-#include
-#include
+#include "abstractnotificationsmodel.h"
-#include "notifications.h"
+namespace NotificationManager {
-namespace NotificationManager
+class NotificationsModel : public AbstractNotificationsModel
{
-
-class NotificationsModel : public QAbstractListModel
-{
- Q_OBJECT
-
public:
- ~NotificationsModel() override;
-
using Ptr = QSharedPointer;
static Ptr createNotificationsModel();
+ void expire(uint notificationId) override;
+ void close(uint notificationId) override;
- QDateTime lastRead() const;
- void setLastRead(const QDateTime &lastRead);
-
- QVariant data(const QModelIndex &index, int role) const override;
- bool setData(const QModelIndex &index, const QVariant &value, int role) override;
- int rowCount(const QModelIndex &parent = QModelIndex()) const override;
- QHash roleNames() const override;
+ void invokeDefaultAction(uint notificationId) override;
+ void invokeAction(uint notificationId, const QString &actionName) override;
+ void reply(uint notificationId, const QString &text) override;
- void expire(uint notificationId);
- void close(uint notificationId);
void configure(uint notificationId);
void configure(const QString &desktopEntry, const QString ¬ifyRcName, const QString &eventId);
- void invokeDefaultAction(uint notificationId);
- void invokeAction(uint notificationId, const QString &actionName);
- void reply(uint notificationId, const QString &text);
-
- void startTimeout(uint notificationId);
- void stopTimeout(uint notificationId);
-
- void clear(Notifications::ClearFlags flags);
-
-signals:
- void lastReadChanged();
private:
- class Private;
- QScopedPointer d;
-
NotificationsModel();
- Q_DISABLE_COPY(NotificationsModel)
-
};
-} // namespace NotificationManager
+}
diff --git a/libnotificationmanager/notificationsmodel.cpp b/libnotificationmanager/notificationsmodel.cpp
--- a/libnotificationmanager/notificationsmodel.cpp
+++ b/libnotificationmanager/notificationsmodel.cpp
@@ -1,4 +1,5 @@
/*
+ * Copyright 2020 Shah Bhushan
* Copyright 2018-2019 Kai Uwe Broulik
*
* This library is free software; you can redistribute it and/or
@@ -19,365 +20,124 @@
*/
#include "notificationsmodel.h"
-
-#include "debug.h"
-
#include "server.h"
-#include "utils_p.h"
-
-#include "notifications.h"
-
-#include "notification.h"
+#include "abstractnotificationsmodel_p.h"
#include "notification_p.h"
-#include
+#include "debug.h"
+
#include
-#include
#include
-#include
-#include
-
-static const int s_notificationsLimit = 1000;
-
using namespace NotificationManager;
-class Q_DECL_HIDDEN NotificationsModel::Private
-{
-public:
- explicit Private(NotificationsModel *q);
- ~Private();
-
- void onNotificationAdded(const Notification ¬ification);
- void onNotificationReplaced(uint replacedId, const Notification ¬ification);
- void onNotificationRemoved(uint notificationId, Server::CloseReason reason);
-
- void setupNotificationTimeout(const Notification ¬ification);
-
- int rowOfNotification(uint id) const;
-
- NotificationsModel *q;
-
- QVector notifications;
- // Fallback timeout to ensure all notifications expire eventually
- // otherwise when it isn't shown to the user and doesn't expire
- // an app might wait indefinitely for the notification to do so
- QHash notificationTimeouts;
-
- QDateTime lastRead;
-
-};
-
-NotificationsModel::Private::Private(NotificationsModel *q)
- : q(q)
- , lastRead(QDateTime::currentDateTimeUtc())
-{
-
-}
-
-NotificationsModel::Private::~Private()
-{
- qDeleteAll(notificationTimeouts);
- notificationTimeouts.clear();
-}
-
-void NotificationsModel::Private::onNotificationAdded(const Notification ¬ification)
-{
- // Once we reach a certain insane number of notifications discard some old ones
- // as we keep pixmaps around etc
- if (notifications.count() >= s_notificationsLimit) {
- const int cleanupCount = s_notificationsLimit / 2;
- qCDebug(NOTIFICATIONMANAGER) << "Reached the notification limit of" << s_notificationsLimit << ", discarding the oldest" << cleanupCount << "notifications";
- q->beginRemoveRows(QModelIndex(), 0, cleanupCount - 1);
- for (int i = 0 ; i < cleanupCount; ++i) {
- notifications.removeAt(0);
- // TODO close gracefully?
- }
- q->endRemoveRows();
- }
-
- setupNotificationTimeout(notification);
-
- q->beginInsertRows(QModelIndex(), notifications.count(), notifications.count());
- notifications.append(std::move(notification));
- q->endInsertRows();
-}
-
-void NotificationsModel::Private::onNotificationReplaced(uint replacedId, const Notification ¬ification)
-{
- const int row = rowOfNotification(replacedId);
-
- if (row == -1) {
- qCWarning(NOTIFICATIONMANAGER) << "Trying to replace notification with id" << replacedId << "which doesn't exist, creating a new one. This is an application bug!";
- onNotificationAdded(notification);
- return;
- }
-
- setupNotificationTimeout(notification);
-
- notifications[row] = notification;
- const QModelIndex idx = q->index(row, 0);
- emit q->dataChanged(idx, idx);
-}
-
-void NotificationsModel::Private::onNotificationRemoved(uint removedId, Server::CloseReason reason)
-{
- const int row = rowOfNotification(removedId);
- if (row == -1) {
- return;
- }
-
- q->stopTimeout(removedId);
-
- // When a notification expired, keep it around in the history and mark it as such
- if (reason == Server::CloseReason::Expired) {
- const QModelIndex idx = q->index(row, 0);
-
- Notification ¬ification = notifications[row];
- notification.setExpired(true);
-
- // Since the notification is "closed" it cannot have any actions
- // unless it is "resident" which we don't support
- notification.setActions(QStringList());
-
- emit q->dataChanged(idx, idx, {
- Notifications::ExpiredRole,
- // TODO only emit those if actually changed?
- Notifications::ActionNamesRole,
- Notifications::ActionLabelsRole,
- Notifications::HasDefaultActionRole,
- Notifications::DefaultActionLabelRole,
- Notifications::ConfigurableRole
- });
-
- return;
- }
-
- // Otherwise if explicitly closed by either user or app, remove it
-
- q->beginRemoveRows(QModelIndex(), row, row);
- notifications.removeAt(row);
- q->endRemoveRows();
-}
-
-void NotificationsModel::Private::setupNotificationTimeout(const Notification ¬ification)
-{
- if (notification.timeout() == 0) {
- // In case it got replaced by a persistent notification
- q->stopTimeout(notification.id());
- return;
- }
-
- QTimer *timer = notificationTimeouts.value(notification.id());
- if (!timer) {
- timer = new QTimer();
- timer->setSingleShot(true);
-
- connect(timer, &QTimer::timeout, q, [this, timer] {
- const uint id = timer->property("notificationId").toUInt();
- q->expire(id);
- });
- notificationTimeouts.insert(notification.id(), timer);
- }
-
- timer->stop();
- timer->setProperty("notificationId", notification.id());
- timer->setInterval(60000 /*1min*/ + (notification.timeout() == -1 ? 120000 /*2min, max configurable default timeout*/ : notification.timeout()));
- timer->start();
-}
-
-int NotificationsModel::Private::rowOfNotification(uint id) const
+NotificationsModel::Ptr NotificationsModel::createNotificationsModel()
{
- auto it = std::find_if(notifications.constBegin(), notifications.constEnd(), [id](const Notification &item) {
- return item.id() == id;
- });
-
- if (it == notifications.constEnd()) {
- return -1;
+ static QWeakPointer s_instance;
+ if (!s_instance) {
+ QSharedPointer ptr(new NotificationsModel());
+ s_instance = ptr.toWeakRef();
+ return ptr;
}
-
- return std::distance(notifications.constBegin(), it);
+ return s_instance.toStrongRef();
}
NotificationsModel::NotificationsModel()
- : QAbstractListModel(nullptr)
- , d(new Private(this))
{
connect(&Server::self(), &Server::notificationAdded, this, [this](const Notification ¬ification) {
- d->onNotificationAdded(notification);
+ onNotificationAdded(notification);
});
connect(&Server::self(), &Server::notificationReplaced, this, [this](uint replacedId, const Notification ¬ification) {
- d->onNotificationReplaced(replacedId, notification);
+ onNotificationReplaced(replacedId, notification);
});
connect(&Server::self(), &Server::notificationRemoved, this, [this](uint removedId, Server::CloseReason reason) {
- d->onNotificationRemoved(removedId, reason);
+ onNotificationRemoved(removedId, reason);
});
connect(&Server::self(), &Server::serviceOwnershipLost, this, [this] {
// Expire all notifications as we're defunct now
- const auto notifications = d->notifications;
- for (const Notification ¬ification : notifications) {
+ const auto notificationList = notifications();
+ for (const Notification ¬ification : notificationList) {
if (!notification.expired()) {
- d->onNotificationRemoved(notification.id(), Server::CloseReason::Expired);
+ onNotificationRemoved(notification.id(), Server::CloseReason::Expired);
}
}
});
-
Server::self().init();
}
-NotificationsModel::~NotificationsModel() = default;
-
-NotificationsModel::Ptr NotificationsModel::createNotificationsModel()
+void NotificationsModel::expire(uint notificationId)
{
- static QWeakPointer s_instance;
- if (!s_instance) {
- QSharedPointer ptr(new NotificationsModel());
- s_instance = ptr.toWeakRef();
- return ptr;
+ if (rowOfNotification(notificationId) > -1) {
+ Server::self().closeNotification(notificationId, Server::CloseReason::Expired);
}
- return s_instance.toStrongRef();
-}
-
-QDateTime NotificationsModel::lastRead() const
-{
- return d->lastRead;
}
-void NotificationsModel::setLastRead(const QDateTime &lastRead)
+void NotificationsModel::close(uint notificationId)
{
- if (d->lastRead != lastRead) {
- d->lastRead = lastRead;
- emit lastReadChanged();
+ if (rowOfNotification(notificationId) > -1) {
+ Server::self().closeNotification(notificationId, Server::CloseReason::DismissedByUser);
}
}
-QVariant NotificationsModel::data(const QModelIndex &index, int role) const
+
+void NotificationsModel::invokeDefaultAction(uint notificationId)
{
- if (!checkIndex(index)) {
- return QVariant();
+ const int row = rowOfNotification(notificationId);
+ if (row == -1) {
+ return;
}
- const Notification ¬ification = d->notifications.at(index.row());
-
- switch (role) {
- case Notifications::IdRole: return notification.id();
- case Notifications::TypeRole: return Notifications::NotificationType;
-
- case Notifications::CreatedRole:
- if (notification.created().isValid()) {
- return notification.created();
- }
- break;
- case Notifications::UpdatedRole:
- if (notification.updated().isValid()) {
- return notification.updated();
- }
- break;
- case Notifications::SummaryRole: return notification.summary();
- case Notifications::BodyRole: return notification.body();
- case Notifications::IconNameRole:
- if (notification.image().isNull()) {
- return notification.icon();
- }
- break;
- case Notifications::ImageRole:
- if (!notification.image().isNull()) {
- return notification.image();
- }
- break;
- case Notifications::DesktopEntryRole: return notification.desktopEntry();
- case Notifications::NotifyRcNameRole: return notification.notifyRcName();
-
- case Notifications::ApplicationNameRole: return notification.applicationName();
- case Notifications::ApplicationIconNameRole: return notification.applicationIconName();
- case Notifications::OriginNameRole: return notification.originName();
-
- case Notifications::ActionNamesRole: return notification.actionNames();
- case Notifications::ActionLabelsRole: return notification.actionLabels();
- case Notifications::HasDefaultActionRole: return notification.hasDefaultAction();
- case Notifications::DefaultActionLabelRole: return notification.defaultActionLabel();
-
- case Notifications::UrlsRole: return QVariant::fromValue(notification.urls());
-
- case Notifications::UrgencyRole: return static_cast(notification.urgency());
- case Notifications::UserActionFeedbackRole: return notification.userActionFeedback();
-
- case Notifications::TimeoutRole: return notification.timeout();
-
- case Notifications::ClosableRole: return true;
- case Notifications::ConfigurableRole: return notification.configurable();
- case Notifications::ConfigureActionLabelRole: return notification.configureActionLabel();
-
- case Notifications::ExpiredRole: return notification.expired();
- case Notifications::ReadRole: return notification.read();
-
- case Notifications::HasReplyActionRole: return notification.hasReplyAction();
- case Notifications::ReplyActionLabelRole: return notification.replyActionLabel();
- case Notifications::ReplyPlaceholderTextRole: return notification.replyPlaceholderText();
- case Notifications::ReplySubmitButtonTextRole: return notification.replySubmitButtonText();
- case Notifications::ReplySubmitButtonIconNameRole: return notification.replySubmitButtonIconName();
+ const Notification ¬ification = notifications().at(row);
+ if (!notification.hasDefaultAction()) {
+ qCWarning(NOTIFICATIONMANAGER) << "Trying to invoke default action on notification" << notificationId << "which doesn't have one";
+ return;
}
- return QVariant();
+ Server::self().invokeAction(notificationId, QStringLiteral("default")); // FIXME make a static Notification::defaultActionName() or something
}
-bool NotificationsModel::setData(const QModelIndex &index, const QVariant &value, int role)
+void NotificationsModel::invokeAction(uint notificationId, const QString &actionName)
{
- if (!checkIndex(index)) {
- return false;
+ const int row = rowOfNotification(notificationId);
+ if (row == -1) {
+ return;
}
- Notification ¬ification = d->notifications[index.row()];
-
- switch (role) {
- case Notifications::ReadRole:
- if (value.toBool() != notification.read()) {
- notification.setRead(value.toBool());
- return true;
- }
- break;
+ const Notification ¬ification = notifications().at(row);
+ if (!notification.actionNames().contains(actionName)) {
+ qCWarning(NOTIFICATIONMANAGER) << "Trying to invoke action" << actionName << "on notification" << notificationId << "which it doesn't have";
+ return;
}
- return false;
+ Server::self().invokeAction(notificationId, actionName);
}
-int NotificationsModel::rowCount(const QModelIndex &parent) const
+void NotificationsModel::reply(uint notificationId, const QString &text)
{
- if (parent.isValid()) {
- return 0;
+ const int row = rowOfNotification(notificationId);
+ if (row == -1) {
+ return;
}
- return d->notifications.count();
-}
-
-QHash NotificationsModel::roleNames() const
-{
- return Utils::roleNames();
-}
-
-void NotificationsModel::expire(uint notificationId)
-{
- if (d->rowOfNotification(notificationId) > -1) {
- Server::self().closeNotification(notificationId, Server::CloseReason::Expired);
+ const Notification ¬ification = notifications().at(row);
+ if (!notification.hasReplyAction()) {
+ qCWarning(NOTIFICATIONMANAGER) << "Trying to reply to a notification which doesn't have a reply action";
+ return;
}
-}
-void NotificationsModel::close(uint notificationId)
-{
- if (d->rowOfNotification(notificationId) > -1) {
- Server::self().closeNotification(notificationId, Server::CloseReason::DismissedByUser);
- }
+ Server::self().reply(notification.dBusService(), notificationId, text);
}
+
void NotificationsModel::configure(uint notificationId)
{
- const int row = d->rowOfNotification(notificationId);
+ const int row = rowOfNotification(notificationId);
if (row == -1) {
return;
}
- const Notification ¬ification = d->notifications.at(row);
+ const Notification ¬ification = notifications().at(row);
if (notification.d->hasConfigureAction) {
Server::self().invokeAction(notificationId, QStringLiteral("settings")); // FIXME make a static Notification::configureActionName() or something
@@ -418,119 +178,3 @@
KShell::joinArgs(args)
});
}
-
-void NotificationsModel::invokeDefaultAction(uint notificationId)
-{
- const int row = d->rowOfNotification(notificationId);
- if (row == -1) {
- return;
- }
-
- const Notification ¬ification = d->notifications.at(row);
- if (!notification.hasDefaultAction()) {
- qCWarning(NOTIFICATIONMANAGER) << "Trying to invoke default action on notification" << notificationId << "which doesn't have one";
- return;
- }
-
- Server::self().invokeAction(notificationId, QStringLiteral("default")); // FIXME make a static Notification::defaultActionName() or something
-}
-
-void NotificationsModel::invokeAction(uint notificationId, const QString &actionName)
-{
- const int row = d->rowOfNotification(notificationId);
- if (row == -1) {
- return;
- }
-
- const Notification ¬ification = d->notifications.at(row);
- if (!notification.actionNames().contains(actionName)) {
- qCWarning(NOTIFICATIONMANAGER) << "Trying to invoke action" << actionName << "on notification" << notificationId << "which it doesn't have";
- return;
- }
-
- Server::self().invokeAction(notificationId, actionName);
-}
-
-void NotificationsModel::reply(uint notificationId, const QString &text)
-{
- const int row = d->rowOfNotification(notificationId);
- if (row == -1) {
- return;
- }
-
- const Notification ¬ification = d->notifications.at(row);
- if (!notification.hasReplyAction()) {
- qCWarning(NOTIFICATIONMANAGER) << "Trying to reply to a notification which doesn't have a reply action";
- return;
- }
-
- Server::self().reply(notification.dBusService(), notificationId, text);
-}
-
-void NotificationsModel::startTimeout(uint notificationId)
-{
- const int row = d->rowOfNotification(notificationId);
- if (row == -1) {
- return;
- }
-
- const Notification ¬ification = d->notifications.at(row);
-
- if (!notification.timeout() || notification.expired()) {
- return;
- }
-
- d->setupNotificationTimeout(notification);
-}
-
-void NotificationsModel::stopTimeout(uint notificationId)
-{
- delete d->notificationTimeouts.take(notificationId);
-}
-
-void NotificationsModel::clear(Notifications::ClearFlags flags)
-{
- if (d->notifications.isEmpty()) {
- return;
- }
-
- // Tries to remove a contiguous group if possible as the likely case is
- // you have n unread notifications at the end of the list, we don't want to
- // remove and signal each item individually
- QVector> clearQueue;
-
- QPair clearRange{-1, -1};
-
- for (int i = d->notifications.count() - 1; i >= 0; --i) {
- const Notification ¬ification = d->notifications.at(i);
-
- bool clear = (flags.testFlag(Notifications::ClearExpired) && notification.expired());
-
- if (clear) {
- if (clearRange.second == -1) {
- clearRange.second = i;
- }
- clearRange.first = i;
- } else {
- if (clearRange.first != -1) {
- clearQueue.append(clearRange);
- clearRange.first = -1;
- clearRange.second = -1;
- }
- }
- }
-
- if (clearRange.first != -1) {
- clearQueue.append(clearRange);
- clearRange.first = -1;
- clearRange.second = -1;
- }
-
- for (const auto &range : clearQueue) {
- beginRemoveRows(QModelIndex(), range.first, range.second);
- for (int i = range.second; i >= range.first; --i) {
- d->notifications.removeAt(i);
- }
- endRemoveRows();
- }
-}
diff --git a/libnotificationmanager/server_p.h b/libnotificationmanager/server_p.h
--- a/libnotificationmanager/server_p.h
+++ b/libnotificationmanager/server_p.h
@@ -22,6 +22,7 @@
#include
#include
+#include
#include "notification.h"
@@ -68,6 +69,12 @@
void UnInhibit(uint cookie);
bool inhibited() const; // property getter
+ // Notifition watcher
+ void RegisterWatcher();
+ void UnRegisterWatcher();
+
+ void InvokeAction(uint id, const QString &actionKey);
+
Q_SIGNALS:
// DBus
void NotificationClosed(uint id, uint reason);
@@ -121,6 +128,7 @@
mutable QScopedPointer m_currentOwner;
QDBusServiceWatcher *m_inhibitionWatcher = nullptr;
+ QDBusServiceWatcher *m_notificationWatchers = nullptr;
uint m_highestInhibitionCookie = 0;
QHash m_externalInhibitions;
QHash m_inhibitionServices;
diff --git a/libnotificationmanager/server_p.cpp b/libnotificationmanager/server_p.cpp
--- a/libnotificationmanager/server_p.cpp
+++ b/libnotificationmanager/server_p.cpp
@@ -23,6 +23,7 @@
#include "debug.h"
#include "notificationsadaptor.h"
+#include "notificationmanageradaptor.h"
#include "notification.h"
#include "notification_p.h"
@@ -45,10 +46,17 @@
ServerPrivate::ServerPrivate(QObject *parent)
: QObject(parent)
, m_inhibitionWatcher(new QDBusServiceWatcher(this))
+ , m_notificationWatchers (new QDBusServiceWatcher(this))
{
m_inhibitionWatcher->setConnection(QDBusConnection::sessionBus());
m_inhibitionWatcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration);
connect(m_inhibitionWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &ServerPrivate::onInhibitionServiceUnregistered);
+
+ m_notificationWatchers->setConnection(QDBusConnection::sessionBus());
+ m_notificationWatchers->setWatchMode(QDBusServiceWatcher::WatchForUnregistration);
+ connect(m_notificationWatchers, &QDBusServiceWatcher::serviceUnregistered, [=](const QString &service) {
+ m_notificationWatchers->removeWatchedService(service);
+ });
}
ServerPrivate::~ServerPrivate() = default;
@@ -84,6 +92,7 @@
}
new NotificationsAdaptor(this);
+ new NotificationManagerAdaptor(this);
if (!m_dbusObjectValid) { // if already registered, don't fail here
m_dbusObjectValid = QDBusConnection::sessionBus().registerObject(notificationServicePath(), this);
@@ -223,11 +232,48 @@
emit static_cast(parent())->notificationAdded(notification);
}
+ // currently we dispatch all notification, this is ugly
+ // TODO: come up with proper authentication/user selection
+ for (const QString &service : m_notificationWatchers->watchedServices()) {
+ QDBusMessage msg = QDBusMessage::createMethodCall(
+ service,
+ QStringLiteral("/NotificationWatcher"),
+ QStringLiteral("org.kde.NotificationWatcher"),
+ QStringLiteral("Notify")
+ );
+ msg.setArguments({
+ notificationId,
+ notification.applicationName(),
+ replaces_id,
+ notification.applicationIconName(),
+ notification.summary(),
+ // we pass raw body data since this data goes through another sanitization
+ // in WatchedNotificationsModel when notification object is created.
+ notification.rawBody(),
+ notification.actionNames(),
+ hints,
+ notification.timeout()
+ });
+ QDBusConnection::sessionBus().call(msg, QDBus::NoBlock);
+ }
+
return notificationId;
}
void ServerPrivate::CloseNotification(uint id)
{
+ for (const QString &service : m_notificationWatchers->watchedServices()) {
+ QDBusMessage msg = QDBusMessage::createMethodCall(
+ service,
+ QStringLiteral("/NotificationWatcher"),
+ QStringLiteral("org.kde.NotificationWatcher"),
+ QStringLiteral("CloseNotification")
+ );
+ msg.setArguments({
+ id
+ });
+ QDBusConnection::sessionBus().call(msg, QDBus::NoBlock);
+ }
// spec says "If the notification no longer exists, an empty D-BUS Error message is sent back."
static_cast(parent())->closeNotification(id, Server::CloseReason::Revoked);
}
@@ -482,3 +528,18 @@
emit externalInhibitedChanged();
emit externalInhibitionsChanged();
}
+
+void ServerPrivate::RegisterWatcher()
+{
+ m_notificationWatchers->addWatchedService(message().service());
+}
+
+void ServerPrivate::UnRegisterWatcher()
+{
+ m_notificationWatchers->removeWatchedService(message().service());
+}
+
+void ServerPrivate::InvokeAction(uint id, const QString& actionKey)
+{
+ ActionInvoked(id, actionKey);
+}
diff --git a/libnotificationmanager/watchednotificationsmodel.h b/libnotificationmanager/watchednotificationsmodel.h
new file mode 100644
--- /dev/null
+++ b/libnotificationmanager/watchednotificationsmodel.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 Shah Bhushan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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 6 of version 3 of the license.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ */
+
+#ifndef WATCHEDNOTIFICATIONSMODEL_H
+#define WATCHEDNOTIFICATIONSMODEL_H
+
+#include "abstractnotificationsmodel.h"
+
+#include "notificationmanager_export.h"
+
+namespace NotificationManager
+{
+
+class NOTIFICATIONMANAGER_EXPORT WatchedNotificationsModel : public AbstractNotificationsModel
+{
+ Q_OBJECT
+ Q_PROPERTY(bool valid READ valid NOTIFY validChanged)
+
+public:
+ explicit WatchedNotificationsModel();
+ ~WatchedNotificationsModel();
+
+ void expire(uint notificationId) override;
+ void close(uint notificationId) override;
+
+ void invokeDefaultAction(uint notificationId) override;
+ void invokeAction(uint notificationId, const QString &actionName) override;
+ void reply(uint notificationId, const QString &text) override;
+ bool valid();
+
+signals:
+ void validChanged(bool valid);
+
+private:
+ class Private;
+ Private * const d;
+ Q_DISABLE_COPY(WatchedNotificationsModel)
+
+};
+
+}
+
+#endif // WATCHEDNOTIFICATIONSMODEL_H
diff --git a/libnotificationmanager/watchednotificationsmodel.cpp b/libnotificationmanager/watchednotificationsmodel.cpp
new file mode 100644
--- /dev/null
+++ b/libnotificationmanager/watchednotificationsmodel.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2020 Shah Bhushan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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 6 of version 3 of the license.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ */
+
+#include "watchednotificationsmodel.h"
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include "fdonotifications_interface.h"
+
+using namespace NotificationManager;
+
+class WatchedNotificationsModel::Private : public QObject
+{
+ Q_OBJECT
+public:
+ explicit Private(WatchedNotificationsModel* q, QObject* parent = nullptr);
+ ~Private();
+ bool valid = false;
+
+public Q_SLOTS:
+ Q_SCRIPTABLE void Notify(uint id, const QString &app_name, uint replaces_id, const QString &app_icon,
+ const QString &summary, const QString &body, const QStringList &actions,
+ const QVariantMap &hints, int timeout);
+ Q_SCRIPTABLE void CloseNotification(uint id);
+ void NotificationClosed(uint id, uint reason);
+
+private:
+ WatchedNotificationsModel* q;
+ OrgFreedesktopNotificationsInterface *fdoNotificationsInterface;
+};
+
+WatchedNotificationsModel::Private::Private(WatchedNotificationsModel* q, QObject *parent)
+ : q(q)
+ , QObject(parent)
+{
+ QDBusConnection dbus = QDBusConnection::sessionBus();
+ fdoNotificationsInterface = new OrgFreedesktopNotificationsInterface(QStringLiteral("org.freedesktop.Notifications"),
+ QStringLiteral("/org/freedesktop/Notifications"),
+ dbus,
+ this);
+ connect(fdoNotificationsInterface, &OrgFreedesktopNotificationsInterface::NotificationClosed,
+ this, &WatchedNotificationsModel::Private::NotificationClosed);
+ dbus.registerObject("/NotificationWatcher", QStringLiteral("org.kde.NotificationWatcher"), this, QDBusConnection::ExportScriptableSlots);
+ QDBusMessage msg = QDBusMessage::createMethodCall(
+ QStringLiteral("org.freedesktop.Notifications"),
+ QStringLiteral("/org/freedesktop/Notifications"),
+ QStringLiteral("org.kde.NotificationManager"),
+ QStringLiteral("RegisterWatcher")
+ );
+ QDBusMessage reply = QDBusConnection::sessionBus().call(msg, QDBus::NoBlock);
+ if(reply.type() != QDBusMessage::ErrorMessage) {
+ valid = true;
+ Q_EMIT q->validChanged(valid);
+ }
+}
+
+WatchedNotificationsModel::Private::~Private()
+{
+ QDBusMessage msg = QDBusMessage::createMethodCall(
+ QStringLiteral("org.freedesktop.Notifications"),
+ QStringLiteral("/org/freedesktop/Notifications"),
+ QStringLiteral("org.kde.NotificationManager"),
+ QStringLiteral("UnRegisterWatcher")
+ );
+ QDBusConnection::sessionBus().call(msg, QDBus::NoBlock);
+}
+
+void WatchedNotificationsModel::Private::Notify(uint id, const QString &app_name, uint replaces_id, const QString &app_icon,
+ const QString &summary, const QString &body, const QStringList &actions,
+ const QVariantMap &hints, int timeout)
+{
+ const bool wasReplaced = replaces_id > 0;
+
+ qDebug() << summary;
+ qDebug() << body;
+ Notification notification(id);
+ notification.setSummary(summary);
+ notification.setBody(body);
+ notification.setApplicationName(app_name);
+
+ notification.setActions(actions);
+ notification.setTimeout(timeout);
+ notification.setHints(hints);
+ notification.setIcon(app_icon);
+ if(wasReplaced) {
+ q->onNotificationReplaced(replaces_id, notification);
+ } else {
+ q->onNotificationAdded(notification);
+ }
+}
+
+void WatchedNotificationsModel::Private::CloseNotification(uint id)
+{
+ q->onNotificationRemoved(id, Server::CloseReason::Expired);
+}
+
+void WatchedNotificationsModel::Private::NotificationClosed(uint id, uint reason)
+{
+ q->onNotificationRemoved(id, static_cast(reason));
+}
+
+WatchedNotificationsModel::WatchedNotificationsModel()
+ : AbstractNotificationsModel(),
+ d(new Private(this, nullptr))
+{
+}
+
+WatchedNotificationsModel::~WatchedNotificationsModel()
+{
+}
+
+void WatchedNotificationsModel::close(uint notificationId)
+{
+ onNotificationRemoved(notificationId, Server::CloseReason::DismissedByUser);
+}
+
+void WatchedNotificationsModel::expire(uint notificationId)
+{
+ onNotificationRemoved(notificationId, Server::CloseReason::Expired);
+}
+
+void WatchedNotificationsModel::invokeDefaultAction(uint notificationId)
+{
+ this->invokeAction(notificationId, QStringLiteral("default"));
+}
+
+void WatchedNotificationsModel::invokeAction(uint notificationId, const QString &actionName)
+{
+ QDBusConnection dbus = QDBusConnection::sessionBus();
+ dbus.registerObject("/NotificationWatcher", this, QDBusConnection::ExportScriptableSlots);
+ QDBusMessage msg = QDBusMessage::createMethodCall(
+ QStringLiteral("org.freedesktop.Notifications"),
+ QStringLiteral("/org/freedesktop/Notifications"),
+ QStringLiteral("org.kde.NotificationManager"),
+ QStringLiteral("InvokeAction")
+ );
+ msg.setArguments({
+ notificationId,
+ actionName
+ });
+ QDBusConnection::sessionBus().call(msg, QDBus::NoBlock);
+}
+
+void WatchedNotificationsModel::reply(uint notificationId, const QString &text)
+{
+ // todo
+ Q_UNUSED(notificationId)
+ Q_UNUSED(text)
+}
+
+bool WatchedNotificationsModel::valid()
+{
+ return d->valid;
+}
+
+#include "watchednotificationsmodel.moc"