File indexing completed on 2024-10-06 04:31:33

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