File indexing completed on 2024-04-21 04:56:52

0001 /**
0002  * SPDX-FileCopyrightText: 2013 Albert Vaca <albertvaka@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "notificationsplugin.h"
0008 
0009 #include "plugin_notifications_debug.h"
0010 #include "sendreplydialog.h"
0011 #include <dbushelper.h>
0012 
0013 #include <KPluginFactory>
0014 #if !defined(Q_OS_WIN) && !defined(Q_OS_MAC)
0015 #include <KStartupInfo>
0016 #include <private/qtx11extras_p.h>
0017 #endif
0018 
0019 K_PLUGIN_CLASS_WITH_JSON(NotificationsPlugin, "kdeconnect_notifications.json")
0020 
0021 void NotificationsPlugin::connected()
0022 {
0023     NetworkPacket np(PACKET_TYPE_NOTIFICATION_REQUEST, {{QStringLiteral("request"), true}});
0024     sendPacket(np);
0025 }
0026 
0027 void NotificationsPlugin::receivePacket(const NetworkPacket &np)
0028 {
0029     if (np.get<bool>(QStringLiteral("request"))) {
0030         qCWarning(KDECONNECT_PLUGIN_NOTIFICATIONS) << "Unexpected notification request. Maybe the paired client is very old?";
0031     }
0032 
0033     if (np.get<bool>(QStringLiteral("isCancel"))) {
0034         QString id = np.get<QString>(QStringLiteral("id"));
0035         // cut off kdeconnect-android's prefix if there:
0036         if (id.startsWith(QLatin1String("org.kde.kdeconnect_tp::")))
0037             id = id.mid(id.indexOf(QLatin1String("::")) + 2);
0038         removeNotification(id);
0039         return;
0040     }
0041 
0042     QString id = np.get<QString>(QStringLiteral("id"));
0043 
0044     Notification *noti = nullptr;
0045 
0046     if (!m_internalIdToPublicId.contains(id)) {
0047         noti = new Notification(np, device(), this);
0048 
0049         if (noti->isReady()) {
0050             addNotification(noti);
0051         } else {
0052             connect(noti, &Notification::ready, this, &NotificationsPlugin::notificationReady);
0053         }
0054     } else {
0055         QString pubId = m_internalIdToPublicId.value(id);
0056         noti = m_notifications.value(pubId);
0057         noti->update(np);
0058     }
0059 }
0060 
0061 void NotificationsPlugin::clearNotifications()
0062 {
0063     qDeleteAll(m_notifications);
0064     m_notifications.clear();
0065     Q_EMIT allNotificationsRemoved();
0066 }
0067 
0068 QStringList NotificationsPlugin::activeNotifications()
0069 {
0070     return m_notifications.keys();
0071 }
0072 
0073 void NotificationsPlugin::notificationReady()
0074 {
0075     Notification *noti = static_cast<Notification *>(sender());
0076     disconnect(noti, &Notification::ready, this, &NotificationsPlugin::notificationReady);
0077     addNotification(noti);
0078 }
0079 
0080 void NotificationsPlugin::addNotification(Notification *noti)
0081 {
0082     const QString &internalId = noti->internalId();
0083 
0084     if (m_internalIdToPublicId.contains(internalId)) {
0085         removeNotification(internalId);
0086     }
0087 
0088     // qCDebug(KDECONNECT_PLUGIN_NOTIFICATIONS) << "addNotification" << internalId;
0089 
0090     connect(noti, &Notification::dismissRequested, this, &NotificationsPlugin::dismissRequested);
0091 
0092     connect(noti, &Notification::replyRequested, this, [this, noti] {
0093         replyRequested(noti);
0094     });
0095 
0096     connect(noti, &Notification::actionTriggered, this, &NotificationsPlugin::sendAction);
0097     connect(noti, &Notification::replied, this, [this, noti](const QString &message) {
0098         sendReply(noti->replyId(), message);
0099     });
0100 
0101     const QString &publicId = newId();
0102     m_notifications[publicId] = noti;
0103     m_internalIdToPublicId[internalId] = publicId;
0104 
0105     QDBusConnection::sessionBus().registerObject(device()->dbusPath() + QStringLiteral("/notifications/") + publicId,
0106                                                  noti,
0107                                                  QDBusConnection::ExportScriptableContents);
0108     Q_EMIT notificationPosted(publicId);
0109 }
0110 
0111 void NotificationsPlugin::removeNotification(const QString &internalId)
0112 {
0113     // qCDebug(KDECONNECT_PLUGIN_NOTIFICATIONS) << "removeNotification" << internalId;
0114 
0115     if (!m_internalIdToPublicId.contains(internalId)) {
0116         qCDebug(KDECONNECT_PLUGIN_NOTIFICATIONS) << "Not found noti by internal Id: " << internalId;
0117         return;
0118     }
0119 
0120     QString publicId = m_internalIdToPublicId.take(internalId);
0121 
0122     Notification *noti = m_notifications.take(publicId);
0123     if (!noti) {
0124         qCDebug(KDECONNECT_PLUGIN_NOTIFICATIONS) << "Not found noti by public Id: " << publicId;
0125         return;
0126     }
0127 
0128     // Deleting the notification will unregister it automatically
0129     noti->deleteLater();
0130 
0131     Q_EMIT notificationRemoved(publicId);
0132 }
0133 
0134 void NotificationsPlugin::dismissRequested(const QString &internalId)
0135 {
0136     NetworkPacket np(PACKET_TYPE_NOTIFICATION_REQUEST);
0137     np.set<QString>(QStringLiteral("cancel"), internalId);
0138     sendPacket(np);
0139 
0140     // Workaround: we erase notifications without waiting a response from the
0141     // phone because we won't receive a response if we are out of sync and this
0142     // notification no longer exists. Ideally, each time we reach the phone
0143     // after some time disconnected we should re-sync all the notifications.
0144     removeNotification(internalId);
0145 }
0146 
0147 void NotificationsPlugin::replyRequested(Notification *noti)
0148 {
0149     QString replyId = noti->replyId();
0150     QString appName = noti->appName();
0151     QString originalMessage = noti->ticker();
0152     SendReplyDialog *dialog = new SendReplyDialog(originalMessage, replyId, appName);
0153     connect(dialog, &SendReplyDialog::sendReply, this, &NotificationsPlugin::sendReply);
0154     dialog->show();
0155 #if !defined(Q_OS_WIN) && !defined(Q_OS_MAC)
0156     auto window = qobject_cast<QWindow *>(dialog->windowHandle());
0157     if (window && QX11Info::isPlatformX11()) {
0158         KStartupInfo::setNewStartupId(window, QX11Info::nextStartupId());
0159     }
0160 #endif
0161     dialog->raise();
0162 }
0163 
0164 void NotificationsPlugin::sendReply(const QString &replyId, const QString &message)
0165 {
0166     NetworkPacket np(PACKET_TYPE_NOTIFICATION_REPLY);
0167     np.set<QString>(QStringLiteral("requestReplyId"), replyId);
0168     np.set<QString>(QStringLiteral("message"), message);
0169     sendPacket(np);
0170 }
0171 
0172 void NotificationsPlugin::sendAction(const QString &key, const QString &action)
0173 {
0174     NetworkPacket np(PACKET_TYPE_NOTIFICATION_ACTION);
0175     np.set<QString>(QStringLiteral("key"), key);
0176     np.set<QString>(QStringLiteral("action"), action);
0177     sendPacket(np);
0178 }
0179 
0180 QString NotificationsPlugin::newId()
0181 {
0182     return QString::number(++m_lastId);
0183 }
0184 
0185 QString NotificationsPlugin::dbusPath() const
0186 {
0187     return QLatin1String("/modules/kdeconnect/devices/%1/notifications").arg(device()->id());
0188 }
0189 
0190 #include "moc_notificationsplugin.cpp"
0191 #include "notificationsplugin.moc"