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 ¬ificationId : 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"