Branch data Line data Source code
1 : : #include "DesktopNotifier.h"
2 : :
3 : : #include <QLoggingCategory>
4 : :
5 [ + + + - : 63 : Q_LOGGING_CATEGORY(lcNotifier, "mailjd.notifier")
+ - - - ]
6 : :
7 : : namespace {
8 : 8 : QString escapedNotificationText(QString text) {
9 [ + + ]: 251 : for (qsizetype i = text.size() - 1; i >= 0; --i) {
10 : 243 : const ushort code = text.at(i).unicode();
11 [ + + + + : 243 : if ((code < 0x20 && code != '\n' && code != '\t') || code == 0x7f)
- + - + ]
12 : 1 : text.remove(i, 1);
13 : : }
14 : 8 : return text.toHtmlEscaped();
15 : : }
16 : : } // namespace
17 : :
18 : : #ifdef MAILJD_KDE_INTEGRATION
19 : :
20 : : #include <KNotification>
21 : :
22 : 1 : DesktopNotifier::DesktopNotifier(QObject *parent) : QObject(parent) {
23 : 1 : m_available = true;
24 : 1 : }
25 : :
26 : 1 : DesktopNotifier::~DesktopNotifier() = default;
27 : :
28 : 0 : uint DesktopNotifier::notify(const QString &summary, const QString &body,
29 : : const QString &iconName, int timeoutMs,
30 : : uint replacesId) {
31 : : Q_UNUSED(timeoutMs)
32 : :
33 : : // KNotification has no replaces_id — emulate by closing the old popup.
34 [ # # ]: 0 : if (replacesId != 0)
35 [ # # ]: 0 : close(replacesId);
36 : :
37 : : // Sprint 77 (T-77.A4): KF6 dropped the QObject* widget parameter from the
38 : : // KNotification::event(eventId,title,text,iconName,...) overload (present in
39 : : // KF5). Pass flags directly as the 5th argument.
40 [ # # ]: 0 : auto *notif = KNotification::event(
41 [ # # ]: 0 : QStringLiteral("new-mail"), escapedNotificationText(summary),
42 [ # # ]: 0 : escapedNotificationText(body),
43 [ # # # # : 0 : iconName.isEmpty() ? QStringLiteral("mail-message-new") : iconName,
# # ]
44 : : KNotification::CloseOnTimeout);
45 : :
46 : 0 : uint id = m_nextId++;
47 [ # # ]: 0 : m_activeNotifications.insert(id, notif);
48 : :
49 [ # # ]: 0 : configureKdeActions(notif, id, true);
50 : :
51 [ # # ]: 0 : connect(notif, &KNotification::closed, this, [this, id]() {
52 : 0 : m_activeNotifications.remove(id);
53 : 0 : emit notificationClosed(id);
54 : 0 : });
55 : :
56 : 0 : return id;
57 : : }
58 : :
59 : 0 : uint DesktopNotifier::notifySummary(const QString &title, const QString &body,
60 : : uint replacesId) {
61 [ # # ]: 0 : if (replacesId != 0)
62 [ # # ]: 0 : close(replacesId);
63 : :
64 [ # # ]: 0 : auto *notif = KNotification::event(
65 [ # # ]: 0 : QStringLiteral("new-mail"), escapedNotificationText(title),
66 [ # # ]: 0 : escapedNotificationText(body),
67 : 0 : QStringLiteral("mail-message-new"),
68 : : KNotification::CloseOnTimeout);
69 : :
70 : 0 : uint id = m_nextId++;
71 [ # # ]: 0 : m_activeNotifications.insert(id, notif);
72 : :
73 [ # # ]: 0 : configureKdeActions(notif, id, false);
74 [ # # ]: 0 : connect(notif, &KNotification::closed, this, [this, id]() {
75 : 0 : m_activeNotifications.remove(id);
76 : 0 : emit notificationClosed(id);
77 : 0 : });
78 : :
79 : 0 : return id;
80 : : }
81 : :
82 : 1 : void DesktopNotifier::configureKdeActions(KNotification *notification,
83 : : uint id,
84 : : bool includeMarkRead) {
85 : 1 : m_kdeActionsForTest.clear();
86 [ + - + - ]: 1 : auto *openAction = notification->addDefaultAction(tr("Open"));
87 : 1 : m_kdeActionsForTest.append(openAction);
88 [ + - ]: 1 : connect(openAction, &KNotificationAction::activated, this, [this, id]() {
89 [ + - ]: 2 : emit actionInvoked(id, QStringLiteral("open"));
90 : 1 : });
91 : :
92 [ + - ]: 1 : if (includeMarkRead) {
93 [ + - + - ]: 1 : auto *markReadAction = notification->addAction(tr("Mark Read"));
94 : 1 : m_kdeActionsForTest.append(markReadAction);
95 : 1 : connect(markReadAction, &KNotificationAction::activated, this,
96 [ + - ]: 2 : [this, id]() {
97 [ + - ]: 2 : emit actionInvoked(id, QStringLiteral("mark-read"));
98 : 1 : });
99 : : }
100 : 1 : }
101 : :
102 : 0 : void DesktopNotifier::close(uint id) {
103 [ # # ]: 0 : auto it = m_activeNotifications.find(id);
104 [ # # # # ]: 0 : if (it != m_activeNotifications.end()) {
105 [ # # ]: 0 : it.value()->deleteLater();
106 [ # # ]: 0 : m_activeNotifications.erase(it);
107 : : }
108 : 0 : }
109 : :
110 : 0 : bool DesktopNotifier::isAvailable() const { return m_available; }
111 : :
112 : : #else
113 : :
114 : : #include <QDBusConnection>
115 : : #include <QDBusInterface>
116 : : #include <QDBusMessage>
117 : : #include <QDBusReply>
118 : :
119 : 63 : DesktopNotifier::DesktopNotifier(QObject *parent) : QObject(parent) {
120 [ - - ]: 0 : m_dbus = new QDBusInterface(
121 : 126 : QStringLiteral("org.freedesktop.Notifications"),
122 : 126 : QStringLiteral("/org/freedesktop/Notifications"),
123 : 126 : QStringLiteral("org.freedesktop.Notifications"),
124 [ + - + - : 189 : QDBusConnection::sessionBus(), this);
+ - - + ]
125 : :
126 [ + - ]: 63 : m_available = m_dbus->isValid();
127 : :
128 [ + + ]: 63 : if (m_available) {
129 [ + - ]: 2 : setupDBusConnections();
130 [ + - + - : 4 : qCInfo(lcNotifier) << "freedesktop notifications available";
+ - + + ]
131 : : } else {
132 [ + - + - : 122 : qCInfo(lcNotifier) << "freedesktop notifications not available";
+ - + + ]
133 : : }
134 : 63 : }
135 : :
136 : 24 : DesktopNotifier::~DesktopNotifier() = default;
137 : :
138 : 2 : void DesktopNotifier::setupDBusConnections() {
139 [ + - + - : 4 : QDBusConnection::sessionBus().connect(
+ - ]
140 : 4 : QStringLiteral("org.freedesktop.Notifications"),
141 : 4 : QStringLiteral("/org/freedesktop/Notifications"),
142 : 4 : QStringLiteral("org.freedesktop.Notifications"),
143 : 4 : QStringLiteral("ActionInvoked"), this,
144 : : SLOT(onDBusActionInvoked(uint, QString)));
145 : :
146 [ + - + - : 4 : QDBusConnection::sessionBus().connect(
+ - ]
147 : 4 : QStringLiteral("org.freedesktop.Notifications"),
148 : 4 : QStringLiteral("/org/freedesktop/Notifications"),
149 : 4 : QStringLiteral("org.freedesktop.Notifications"),
150 : 4 : QStringLiteral("NotificationClosed"), this,
151 : : SLOT(onDBusNotificationClosed(uint, uint)));
152 : 2 : }
153 : :
154 : 4 : uint DesktopNotifier::notify(const QString &summary, const QString &body,
155 : : const QString &iconName, int timeoutMs,
156 : : uint replacesId) {
157 [ + + ]: 4 : if (!m_available)
158 : 2 : return 0;
159 : :
160 : 2 : QVariantMap hints;
161 [ + - ]: 4 : hints[QStringLiteral("urgency")] = 1;
162 [ + - ]: 4 : hints[QStringLiteral("category")] = QStringLiteral("email.arrived");
163 [ + - ]: 4 : hints[QStringLiteral("desktop-entry")] = QStringLiteral("mailjd");
164 : :
165 : 2 : QStringList capabilities;
166 : : QDBusReply<QStringList> capReply =
167 [ + - + - ]: 4 : m_dbus->call(QStringLiteral("GetCapabilities"));
168 [ + - + - ]: 2 : if (capReply.isValid())
169 : 2 : capabilities = capReply.value();
170 : :
171 : 2 : QStringList actions;
172 [ + - ]: 2 : if (capabilities.contains(QStringLiteral("actions"))) {
173 [ + - + - : 4 : actions << QStringLiteral("open") << tr("Open")
+ - ]
174 [ + - + - : 2 : << QStringLiteral("mark-read") << tr("Mark Read");
+ - ]
175 : : }
176 : :
177 [ + - ]: 4 : QDBusReply<uint> reply = m_dbus->call(
178 : 6 : QStringLiteral("Notify"), QStringLiteral("MailJD"), replacesId,
179 [ + - + - : 6 : iconName.isEmpty() ? QStringLiteral("mail-message-new") : iconName,
- - ]
180 [ + - + - ]: 4 : escapedNotificationText(summary), escapedNotificationText(body),
181 [ + - ]: 2 : actions, hints, timeoutMs);
182 : :
183 [ + - + - ]: 2 : if (reply.isValid()) {
184 : 2 : uint id = reply.value();
185 [ + - ]: 2 : m_activeNotifications.insert(id, nullptr);
186 : 2 : return id;
187 : : }
188 : 0 : return 0;
189 : 2 : }
190 : :
191 : 2 : uint DesktopNotifier::notifySummary(const QString &title, const QString &body,
192 : : uint replacesId) {
193 [ - + ]: 2 : if (!m_available)
194 : 0 : return 0;
195 : :
196 : 2 : QVariantMap hints;
197 [ + - ]: 4 : hints[QStringLiteral("urgency")] = 1;
198 [ + - ]: 4 : hints[QStringLiteral("category")] = QStringLiteral("email.arrived");
199 [ + - ]: 4 : hints[QStringLiteral("desktop-entry")] = QStringLiteral("mailjd");
200 : :
201 : 2 : QStringList capabilities;
202 : : QDBusReply<QStringList> capReply =
203 [ + - + - ]: 4 : m_dbus->call(QStringLiteral("GetCapabilities"));
204 [ + - + - ]: 2 : if (capReply.isValid())
205 : 2 : capabilities = capReply.value();
206 : :
207 : 2 : QStringList actions;
208 [ + - ]: 2 : if (capabilities.contains(QStringLiteral("actions")))
209 [ + - + - : 2 : actions << QStringLiteral("open") << tr("Open");
+ - ]
210 : :
211 [ + - ]: 4 : QDBusReply<uint> reply = m_dbus->call(
212 : 6 : QStringLiteral("Notify"), QStringLiteral("MailJD"), replacesId,
213 [ + - ]: 6 : QStringLiteral("mail-message-new"), escapedNotificationText(title),
214 [ + - + - ]: 6 : escapedNotificationText(body), actions, hints, 5000);
215 : :
216 [ + - + - ]: 2 : if (reply.isValid()) {
217 : 2 : uint id = reply.value();
218 [ + - ]: 2 : m_activeNotifications.insert(id, nullptr);
219 : 2 : return id;
220 : : }
221 : 0 : return 0;
222 : 2 : }
223 : :
224 : 4 : void DesktopNotifier::close(uint id) {
225 [ + + ]: 4 : if (m_available)
226 [ + - ]: 2 : m_dbus->call(QStringLiteral("CloseNotification"), id);
227 : 4 : }
228 : :
229 : 3 : bool DesktopNotifier::isAvailable() const { return m_available; }
230 : :
231 : 1 : void DesktopNotifier::onDBusActionInvoked(uint id, const QString &action) {
232 : 1 : emit actionInvoked(id, action);
233 : 1 : }
234 : :
235 : 1 : void DesktopNotifier::onDBusNotificationClosed(uint id, uint /*reason*/) {
236 : 1 : m_activeNotifications.remove(id);
237 : 1 : emit notificationClosed(id);
238 : 1 : }
239 : :
240 : : #endif
|