MailJD nbsp;·nbsp; Test Dashboard nbsp;·nbsp; Coverage
LCOV - code coverage report
Current view: top level - service - DesktopNotifier.cpp (source / functions) Coverage Total Hit
Test: MailJD Coverage (Unit + E2E) Lines: 72.7 % 150 109
Test Date: 2026-06-21 21:10:19 Functions: 87.5 % 16 14
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 41.8 % 220 92

             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
        

Generated by: LCOV version 2.0-1