diff --git a/plugins/notifications/notification.cpp b/plugins/notifications/notification.cpp index 3f4d29ff..c9ae4f6f 100644 --- a/plugins/notifications/notification.cpp +++ b/plugins/notifications/notification.cpp @@ -1,154 +1,155 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "notification.h" #include #include #include #include #include #include #include Notification::Notification(const NetworkPackage& np, QObject* parent) : QObject(parent) { mImagesDir = QDir::temp().absoluteFilePath(QStringLiteral("kdeconnect")); mImagesDir.mkpath(mImagesDir.absolutePath()); mClosed = false; parseNetworkPackage(np); createKNotification(false, np); } Notification::~Notification() { } void Notification::dismiss() { if (mDismissable) { Q_EMIT dismissRequested(mInternalId); } } void Notification::show() { + Q_EMIT ready(); if (!mSilent) { mClosed = false; mNotification->sendEvent(); } } void Notification::applyIconAndShow() { if (!mSilent) { QPixmap icon(mIconPath, "PNG"); mNotification->setPixmap(icon); show(); } } void Notification::update(const NetworkPackage &np) { parseNetworkPackage(np); createKNotification(!mClosed, np); } KNotification* Notification::createKNotification(bool update, const NetworkPackage &np) { if (!update) { mNotification = new KNotification(QStringLiteral("notification"), KNotification::CloseOnTimeout, this); mNotification->setComponentName(QStringLiteral("kdeconnect")); } QString escapedTitle = mTitle.toHtmlEscaped(); QString escapedText = mText.toHtmlEscaped(); QString escapedTicker = mTicker.toHtmlEscaped(); mNotification->setTitle(mAppName.toHtmlEscaped()); if (mTitle.isEmpty() && mText.isEmpty()) { mNotification->setText(escapedTicker); } else if (mAppName==mTitle) { mNotification->setText(escapedText); } else if (mTitle.isEmpty()){ mNotification->setText(escapedText); } else if (mText.isEmpty()){ mNotification->setText(escapedTitle); } else { mNotification->setText(escapedTitle+": "+escapedText); } if (!mHasIcon) { //HACK The only way to display no icon at all is trying to load a non-existant icon mNotification->setIconName(QString("not_a_real_icon")); show(); } else { QString filename = mPayloadHash; if (filename.isEmpty()) { mHasIcon = false; } else { mIconPath = mImagesDir.absoluteFilePath(filename); QUrl destinationUrl(mIconPath); FileTransferJob* job = np.createPayloadTransferJob(destinationUrl); job->start(); connect(job, &FileTransferJob::result, this, &Notification::applyIconAndShow); } } if(!mRequestReplyId.isEmpty()) { mNotification->setActions( QStringList(i18n("Reply")) ); connect(mNotification, &KNotification::action1Activated, this, &Notification::reply); } connect(mNotification, &KNotification::closed, this, &Notification::closed); return mNotification; } void Notification::reply() { Q_EMIT replyRequested(); } void Notification::closed() { mClosed = true; } void Notification::parseNetworkPackage(const NetworkPackage &np) { mInternalId = np.get(QStringLiteral("id")); mAppName = np.get(QStringLiteral("appName")); mTicker = np.get(QStringLiteral("ticker")); mTitle = np.get(QStringLiteral("title")); mText = np.get(QStringLiteral("text")); mDismissable = np.get(QStringLiteral("isClearable")); mHasIcon = np.hasPayload(); mSilent = np.get(QStringLiteral("silent")); mPayloadHash = np.get(QStringLiteral("payloadHash")); mRequestReplyId = np.get(QStringLiteral("requestReplyId"), QString()); } diff --git a/plugins/notifications/notification.h b/plugins/notifications/notification.h index 420347bb..1f287134 100644 --- a/plugins/notifications/notification.h +++ b/plugins/notifications/notification.h @@ -1,94 +1,95 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #ifndef NOTIFICATION_H #define NOTIFICATION_H #include #include #include #include #include class Notification : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.device.notifications.notification") Q_PROPERTY(QString internalId READ internalId) Q_PROPERTY(QString appName READ appName) Q_PROPERTY(QString ticker READ ticker) Q_PROPERTY(QString title READ title) Q_PROPERTY(QString text READ text) Q_PROPERTY(QString iconPath READ iconPath) Q_PROPERTY(bool dismissable READ dismissable) Q_PROPERTY(bool hasIcon READ hasIcon) Q_PROPERTY(bool silent READ silent) Q_PROPERTY(QString replyId READ replyId) public: Notification(const NetworkPackage& np, QObject* parent); ~Notification() override; QString internalId() const { return mInternalId; } QString appName() const { return mAppName; } QString ticker() const { return mTicker; } QString title() const { return mTitle; } QString text() const { return mText; } QString iconPath() const { return mIconPath; } bool dismissable() const { return mDismissable; } QString replyId() const { return mRequestReplyId; } bool hasIcon() const { return mHasIcon; } void show(); bool silent() const { return mSilent; } void update(const NetworkPackage &np); KNotification* createKNotification(bool update, const NetworkPackage &np); public Q_SLOTS: Q_SCRIPTABLE void dismiss(); Q_SCRIPTABLE void applyIconAndShow(); Q_SCRIPTABLE void reply(); void closed(); Q_SIGNALS: void dismissRequested(const QString& mInternalId); void replyRequested(); + void ready(); private: QString mInternalId; QString mAppName; QString mTicker; QString mTitle; QString mText; QString mIconPath; QString mRequestReplyId; bool mDismissable; bool mHasIcon; KNotification* mNotification; QDir mImagesDir; bool mSilent; bool mClosed; QString mPayloadHash; void parseNetworkPackage(const NetworkPackage& np); }; #endif diff --git a/plugins/notifications/notificationsdbusinterface.cpp b/plugins/notifications/notificationsdbusinterface.cpp index d6cb3cfc..7e37797f 100644 --- a/plugins/notifications/notificationsdbusinterface.cpp +++ b/plugins/notifications/notificationsdbusinterface.cpp @@ -1,176 +1,185 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #include "notificationsdbusinterface.h" #include "notification_debug.h" #include "notification.h" #include #include #include #include "notificationsplugin.h" #include "sendreplydialog.h" NotificationsDbusInterface::NotificationsDbusInterface(KdeConnectPlugin* plugin) : QDBusAbstractAdaptor(const_cast(plugin->device())) , mDevice(plugin->device()) , mPlugin(plugin) , mLastId(0) { } NotificationsDbusInterface::~NotificationsDbusInterface() { qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "Destroying NotificationsDbusInterface"; } void NotificationsDbusInterface::clearNotifications() { qDeleteAll(mNotifications); mNotifications.clear(); Q_EMIT allNotificationsRemoved(); } QStringList NotificationsDbusInterface::activeNotifications() { return mNotifications.keys(); } void NotificationsDbusInterface::processPackage(const NetworkPackage& np) { if (np.get(QStringLiteral("isCancel"))) { QString id = np.get(QStringLiteral("id")); // cut off kdeconnect-android's prefix if there: if (id.startsWith(QLatin1String("org.kde.kdeconnect_tp::"))) id = id.mid(id.indexOf(QLatin1String("::")) + 2); removeNotification(id); } else if (np.get(QStringLiteral("isRequest"))) { for (const auto& n : qAsConst(mNotifications)) { NetworkPackage np(PACKAGE_TYPE_NOTIFICATION_REQUEST, { {"id", n->internalId()}, {"appName", n->appName()}, {"ticker", n->ticker()}, {"isClearable", n->dismissable()}, {"requestAnswer", true} }); mPlugin->sendPackage(np); } } else if(np.get(QStringLiteral("requestAnswer"), false)) { } else { QString id = np.get(QStringLiteral("id")); + Notification* noti; + if (!mInternalIdToPublicId.contains(id)) { - Notification* noti = new Notification(np, this); - addNotification(noti); + noti = new Notification(np, this); } else { QString pubId = mInternalIdToPublicId[id]; - mNotifications[pubId]->update(np); + noti = mNotifications[pubId]; + noti->update(np); } + + connect(noti, &Notification::ready, this, [this, noti]{ + addNotification(noti); + }); } } void NotificationsDbusInterface::addNotification(Notification* noti) { const QString& internalId = noti->internalId(); if (mInternalIdToPublicId.contains(internalId)) { - removeNotification(internalId); + removeNotification(internalId, KeepNotification); } //qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "addNotification" << internalId; connect(noti, &Notification::dismissRequested, this, &NotificationsDbusInterface::dismissRequested); - - connect(noti, &Notification::replyRequested, this, [this,noti]{ - replyRequested(noti); + + connect(noti, &Notification::replyRequested, this, [this,noti]{ + replyRequested(noti); }); const QString& publicId = newId(); mNotifications[publicId] = noti; mInternalIdToPublicId[internalId] = publicId; QDBusConnection::sessionBus().registerObject(mDevice->dbusPath()+"/notifications/"+publicId, noti, QDBusConnection::ExportScriptableContents); Q_EMIT notificationPosted(publicId); } -void NotificationsDbusInterface::removeNotification(const QString& internalId) +void NotificationsDbusInterface::removeNotification(const QString& internalId, RemoveType removetype) { //qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "removeNotification" << internalId; if (!mInternalIdToPublicId.contains(internalId)) { - qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "Not found noti by internal Id: " << internalId; + //qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "Not found noti by internal Id: " << internalId; return; } QString publicId = mInternalIdToPublicId.take(internalId); Notification* noti = mNotifications.take(publicId); if (!noti) { - qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "Not found noti by public Id: " << publicId; + //qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "Not found noti by public Id: " << publicId; return; } //Deleting the notification will unregister it automatically - //QDBusConnection::sessionBus().unregisterObject(mDevice->dbusPath()+"/notifications/"+publicId); - noti->deleteLater(); + if (removetype==KeepNotification){ + QDBusConnection::sessionBus().unregisterObject(mDevice->dbusPath()+"/notifications/"+publicId); + } else if (removetype==DestroyNotification){ + noti->deleteLater(); + } Q_EMIT notificationRemoved(publicId); } void NotificationsDbusInterface::dismissRequested(const QString& internalId) { NetworkPackage np(PACKAGE_TYPE_NOTIFICATION_REQUEST); np.set(QStringLiteral("cancel"), internalId); mPlugin->sendPackage(np); //Workaround: we erase notifications without waiting a repsonse from the //phone because we won't receive a response if we are out of sync and this //notification no longer exists. Ideally, each time we reach the phone //after some time disconnected we should re-sync all the notifications. removeNotification(internalId); } void NotificationsDbusInterface::replyRequested(Notification* noti) { QString replyId = noti->replyId(); QString appName = noti->appName(); QString originalMessage = noti->ticker(); SendReplyDialog* dialog = new SendReplyDialog(originalMessage, replyId, appName); connect(dialog, &SendReplyDialog::sendReply, this, &NotificationsDbusInterface::sendReply); dialog->show(); } void NotificationsDbusInterface::sendReply(const QString& replyId, const QString& message) { NetworkPackage np(PACKAGE_TYPE_NOTIFICATION_REPLY); np.set(QStringLiteral("requestReplyId"), replyId); np.set(QStringLiteral("message"), message); mPlugin->sendPackage(np); } QString NotificationsDbusInterface::newId() { return QString::number(++mLastId); } diff --git a/plugins/notifications/notificationsdbusinterface.h b/plugins/notifications/notificationsdbusinterface.h index f320a305..f6b5b70c 100644 --- a/plugins/notifications/notificationsdbusinterface.h +++ b/plugins/notifications/notificationsdbusinterface.h @@ -1,72 +1,76 @@ /** * Copyright 2013 Albert Vaca * * 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 . */ #ifndef NOTIFICATIONSDBUSINTERFACE_H #define NOTIFICATIONSDBUSINTERFACE_H #include #include #include #include #include #include "notification.h" class KdeConnectPlugin; class Device; class NotificationsDbusInterface : public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.device.notifications") public: + enum RemoveType{ + KeepNotification, DestroyNotification + }; + explicit NotificationsDbusInterface(KdeConnectPlugin* plugin); ~NotificationsDbusInterface() override; void processPackage(const NetworkPackage& np); void clearNotifications(); void dismissRequested(const QString& notification); void replyRequested(Notification* noti); void addNotification(Notification* noti); public Q_SLOTS: Q_SCRIPTABLE QStringList activeNotifications(); Q_SCRIPTABLE void sendReply(const QString& replyId, const QString& message); Q_SIGNALS: Q_SCRIPTABLE void notificationPosted(const QString& publicId); Q_SCRIPTABLE void notificationRemoved(const QString& publicId); Q_SCRIPTABLE void allNotificationsRemoved(); private /*methods*/: - void removeNotification(const QString& internalId); + void removeNotification(const QString& internalId, RemoveType removetype=DestroyNotification); QString newId(); //Generates successive identifitiers to use as public ids private /*attributes*/: const Device* mDevice; KdeConnectPlugin* mPlugin; QHash mNotifications; QHash mInternalIdToPublicId; int mLastId; }; #endif