File indexing completed on 2023-05-30 09:17:24

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 "notificationsmodel.h"
0008 
0009 #include <QDebug>
0010 #include <QIcon>
0011 
0012 #include <dbushelper.h>
0013 
0014 // In older Qt released, qAsConst isnt available
0015 #include "core/qtcompat_p.h"
0016 #include "interfaces_debug.h"
0017 
0018 NotificationsModel::NotificationsModel(QObject *parent)
0019     : QAbstractListModel(parent)
0020     , m_dbusInterface(nullptr)
0021 {
0022     connect(this, &QAbstractItemModel::rowsInserted, this, &NotificationsModel::rowsChanged);
0023     connect(this, &QAbstractItemModel::rowsRemoved, this, &NotificationsModel::rowsChanged);
0024 
0025     connect(this, &QAbstractItemModel::dataChanged, this, &NotificationsModel::anyDismissableChanged);
0026     connect(this, &QAbstractItemModel::rowsInserted, this, &NotificationsModel::anyDismissableChanged);
0027 
0028     QDBusServiceWatcher *watcher =
0029         new QDBusServiceWatcher(DaemonDbusInterface::activatedService(), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this);
0030     connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, &NotificationsModel::refreshNotificationList);
0031     connect(watcher, &QDBusServiceWatcher::serviceUnregistered, this, &NotificationsModel::clearNotifications);
0032 }
0033 
0034 QHash<int, QByteArray> NotificationsModel::roleNames() const
0035 {
0036     // Role names for QML
0037     QHash<int, QByteArray> names = QAbstractItemModel::roleNames();
0038     names.insert(DbusInterfaceRole, "dbusInterface");
0039     names.insert(AppNameModelRole, "appName");
0040     names.insert(IdModelRole, "notificationId");
0041     names.insert(DismissableModelRole, "dismissable");
0042     names.insert(RepliableModelRole, "repliable");
0043     names.insert(IconPathModelRole, "appIcon");
0044     names.insert(TitleModelRole, "title");
0045     names.insert(TextModelRole, "notitext");
0046     return names;
0047 }
0048 
0049 NotificationsModel::~NotificationsModel()
0050 {
0051 }
0052 
0053 QString NotificationsModel::deviceId() const
0054 {
0055     return m_deviceId;
0056 }
0057 
0058 void NotificationsModel::setDeviceId(const QString &deviceId)
0059 {
0060     m_deviceId = deviceId;
0061 
0062     if (m_dbusInterface) {
0063         delete m_dbusInterface;
0064     }
0065 
0066     m_dbusInterface = new DeviceNotificationsDbusInterface(deviceId, this);
0067 
0068     connect(m_dbusInterface, &OrgKdeKdeconnectDeviceNotificationsInterface::notificationPosted, this, &NotificationsModel::notificationAdded);
0069     connect(m_dbusInterface, &OrgKdeKdeconnectDeviceNotificationsInterface::notificationRemoved, this, &NotificationsModel::notificationRemoved);
0070     connect(m_dbusInterface, &OrgKdeKdeconnectDeviceNotificationsInterface::allNotificationsRemoved, this, &NotificationsModel::clearNotifications);
0071 
0072     refreshNotificationList();
0073 
0074     Q_EMIT deviceIdChanged(deviceId);
0075 }
0076 
0077 void NotificationsModel::notificationAdded(const QString &id)
0078 {
0079     beginInsertRows(QModelIndex(), 0, 0);
0080     NotificationDbusInterface *dbusInterface = new NotificationDbusInterface(m_deviceId, id, this);
0081     connect(dbusInterface, &NotificationDbusInterface::ready, this, &NotificationsModel::notificationUpdated);
0082     m_notificationList.prepend(dbusInterface);
0083     endInsertRows();
0084 }
0085 
0086 void NotificationsModel::notificationRemoved(const QString &id)
0087 {
0088     for (int i = 0; i < m_notificationList.size(); ++i) {
0089         if (m_notificationList[i]->notificationId() == id) {
0090             beginRemoveRows(QModelIndex(), i, i);
0091             m_notificationList.removeAt(i);
0092             endRemoveRows();
0093             return;
0094         }
0095     }
0096     qCWarning(KDECONNECT_INTERFACES) << "Attempted to remove unknown notification: " << id;
0097 }
0098 
0099 void NotificationsModel::refreshNotificationList()
0100 {
0101     if (!m_dbusInterface) {
0102         return;
0103     }
0104 
0105     clearNotifications();
0106 
0107     if (!m_dbusInterface->isValid()) {
0108         qCWarning(KDECONNECT_INTERFACES) << "dbus interface not valid";
0109         return;
0110     }
0111 
0112     QDBusPendingReply<QStringList> pendingNotificationIds = m_dbusInterface->activeNotifications();
0113     QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingNotificationIds, this);
0114 
0115     QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &NotificationsModel::receivedNotifications);
0116 }
0117 
0118 void NotificationsModel::receivedNotifications(QDBusPendingCallWatcher *watcher)
0119 {
0120     watcher->deleteLater();
0121     clearNotifications();
0122     QDBusPendingReply<QStringList> pendingNotificationIds = *watcher;
0123 
0124     if (pendingNotificationIds.isError()) {
0125         qCWarning(KDECONNECT_INTERFACES) << pendingNotificationIds.error();
0126         return;
0127     }
0128 
0129     const QStringList notificationIds = pendingNotificationIds.value();
0130     if (notificationIds.isEmpty()) {
0131         return;
0132     }
0133 
0134     beginInsertRows(QModelIndex(), 0, notificationIds.size() - 1);
0135     for (const QString &notificationId : notificationIds) {
0136         NotificationDbusInterface *dbusInterface = new NotificationDbusInterface(m_deviceId, notificationId, this);
0137         m_notificationList.append(dbusInterface);
0138     }
0139     endInsertRows();
0140 }
0141 
0142 QVariant NotificationsModel::data(const QModelIndex &index, int role) const
0143 {
0144     if (!index.isValid() || index.row() < 0 || index.row() >= m_notificationList.count() || !m_notificationList[index.row()]->isValid()) {
0145         return QVariant();
0146     }
0147 
0148     if (!m_dbusInterface || !m_dbusInterface->isValid()) {
0149         return QVariant();
0150     }
0151 
0152     NotificationDbusInterface *notification = m_notificationList[index.row()];
0153 
0154     // FIXME: This function gets called lots of times, producing lots of dbus calls. Add a cache?
0155     switch (role) {
0156     case IconModelRole:
0157         return QIcon::fromTheme(QStringLiteral("device-notifier"));
0158     case IdModelRole:
0159         return notification->internalId();
0160     case NameModelRole:
0161         return notification->ticker();
0162     case ContentModelRole:
0163         return QString(); // To implement in the Android side
0164     case AppNameModelRole:
0165         return notification->appName();
0166     case DbusInterfaceRole:
0167         return QVariant::fromValue<QObject *>(notification);
0168     case DismissableModelRole:
0169         return notification->dismissable();
0170     case RepliableModelRole:
0171         return !notification->replyId().isEmpty();
0172     case IconPathModelRole:
0173         return notification->iconPath();
0174     case TitleModelRole:
0175         return notification->title();
0176     case TextModelRole:
0177         return notification->text();
0178     default:
0179         return QVariant();
0180     }
0181 }
0182 
0183 NotificationDbusInterface *NotificationsModel::getNotification(const QModelIndex &index) const
0184 {
0185     if (!index.isValid()) {
0186         return nullptr;
0187     }
0188 
0189     int row = index.row();
0190     if (row < 0 || row >= m_notificationList.size()) {
0191         return nullptr;
0192     }
0193 
0194     return m_notificationList[row];
0195 }
0196 
0197 int NotificationsModel::rowCount(const QModelIndex &parent) const
0198 {
0199     if (parent.isValid()) {
0200         // Return size 0 if we are a child because this is not a tree
0201         return 0;
0202     }
0203 
0204     return m_notificationList.count();
0205 }
0206 
0207 bool NotificationsModel::isAnyDimissable() const
0208 {
0209     for (NotificationDbusInterface *notification : qAsConst(m_notificationList)) {
0210         if (notification->dismissable()) {
0211             return true;
0212         }
0213     }
0214     return false;
0215 }
0216 
0217 void NotificationsModel::dismissAll()
0218 {
0219     for (NotificationDbusInterface *notification : qAsConst(m_notificationList)) {
0220         if (notification->dismissable()) {
0221             notification->dismiss();
0222         }
0223     }
0224 }
0225 
0226 void NotificationsModel::clearNotifications()
0227 {
0228     if (!m_notificationList.isEmpty()) {
0229         beginRemoveRows(QModelIndex(), 0, m_notificationList.size() - 1);
0230         qDeleteAll(m_notificationList);
0231         m_notificationList.clear();
0232         endRemoveRows();
0233     }
0234 }
0235 
0236 void NotificationsModel::notificationUpdated()
0237 {
0238     Q_EMIT dataChanged(index(0, 0), index(m_notificationList.size() - 1, 0));
0239 }