[OpenDocString] kdeconnect-kde (cpp)
notificationsmodel.cpp
NotificationsModel::NotificationsModel(QObject *parent)
    : QAbstractListModel(parent)
    , m_dbusInterface(nullptr)
{
    connect(this, &QAbstractItemModel::rowsInserted, this, &NotificationsModel::rowsChanged);
    connect(this, &QAbstractItemModel::rowsRemoved, this, &NotificationsModel::rowsChanged);

    connect(this, &QAbstractItemModel::dataChanged, this, &NotificationsModel::anyDismissableChanged);
    connect(this, &QAbstractItemModel::rowsInserted, this, &NotificationsModel::anyDismissableChanged);

    QDBusServiceWatcher *watcher =
        new QDBusServiceWatcher(DaemonDbusInterface::activatedService(), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this);
    connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, &NotificationsModel::refreshNotificationList);
    connect(watcher, &QDBusServiceWatcher::serviceUnregistered, this, &NotificationsModel::clearNotifications);
}
This constructor builds a notification list model object from an existing parent object, and its dusi interface. It allows to manage row inserted and row removed events, as well as data changed events.
QHash NotificationsModel::roleNames() const
{
    // Role names for QML
    QHash names = QAbstractItemModel::roleNames();
    names.insert(DbusInterfaceRole, "dbusInterface");
    names.insert(AppNameModelRole, "appName");
    names.insert(IdModelRole, "notificationId");
    names.insert(DismissableModelRole, "dismissable");
    names.insert(RepliableModelRole, "repliable");
    names.insert(IconPathModelRole, "appIcon");
    names.insert(TitleModelRole, "title");
    names.insert(TextModelRole, "notitext");
    return names;
}
Returns the role names of the notification model.
NotificationsModel::~NotificationsModel()
{
}
This implements the notification model design pattern and returns a reference to a notification model object.
QString NotificationsModel::deviceId() const
{
    return m_deviceId;
}
Returns the device id stored in the internal list.
void NotificationsModel::setDeviceId(const QString &deviceId)
{
    m_deviceId = deviceId;

    if (m_dbusInterface) {
        delete m_dbusInterface;
    }

    m_dbusInterface = new DeviceNotificationsDbusInterface(deviceId, this);

    connect(m_dbusInterface, &OrgKdeKdeconnectDeviceNotificationsInterface::notificationPosted, this, &NotificationsModel::notificationAdded);
    connect(m_dbusInterface, &OrgKdeKdeconnectDeviceNotificationsInterface::notificationRemoved, this, &NotificationsModel::notificationRemoved);
    connect(m_dbusInterface, &OrgKdeKdeconnectDeviceNotificationsInterface::allNotificationsRemoved, this, &NotificationsModel::clearNotifications);

    refreshNotificationList();

    Q_EMIT deviceIdChanged(deviceId);
}
Sets the device id, and creates a DbusInterface object for it. It sets up signaling that the device id has changed. It refreshes the notification list and emits the deviceIdChanged signal.
void NotificationsModel::notificationAdded(const QString &id)
{
    beginInsertRows(QModelIndex(), 0, 0);
    NotificationDbusInterface *dbusInterface = new NotificationDbusInterface(m_deviceId, id, this);
    connect(dbusInterface, &NotificationDbusInterface::ready, this, &NotificationsModel::notificationUpdated);
    m_notificationList.prepend(dbusInterface);
    endInsertRows();
}
This adds a notification to the notification list by inserting a DBusInterface object and connects it to the ready signal.
void NotificationsModel::notificationRemoved(const QString &id)
{
    for (int i = 0; i < m_notificationList.size(); ++i) {
        if (m_notificationList[i]->notificationId() == id) {
            beginRemoveRows(QModelIndex(), i, i);
            m_notificationList.removeAt(i);
            endRemoveRows();
            return;
        }
    }
    qCWarning(KDECONNECT_INTERFACES) << "Attempted to remove unknown notification: " << id;
}
This removes the notification of the given id, from the list.
void NotificationsModel::refreshNotificationList()
{
    if (!m_dbusInterface) {
        return;
    }

    clearNotifications();

    if (!m_dbusInterface->isValid()) {
        qCWarning(KDECONNECT_INTERFACES) << "dbus interface not valid";
        return;
    }

    QDBusPendingReply pendingNotificationIds = m_dbusInterface->activeNotifications();
    QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingNotificationIds, this);

    QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &NotificationsModel::receivedNotifications);
}
This refreshes the notification list from the DBus interface. It clears the notifications, creates a QDBusPendingCallWatcher and connects the finished signal to the received notifications.
void NotificationsModel::receivedNotifications(QDBusPendingCallWatcher *watcher)
{
    watcher->deleteLater();
    clearNotifications();
    QDBusPendingReply pendingNotificationIds = *watcher;

    if (pendingNotificationIds.isError()) {
        qCWarning(KDECONNECT_INTERFACES) << pendingNotificationIds.error();
        return;
    }

    const QStringList notificationIds = pendingNotificationIds.value();
    if (notificationIds.isEmpty()) {
        return;
    }

    beginInsertRows(QModelIndex(), 0, notificationIds.size() - 1);
    for (const QString ¬ificationId : notificationIds) {
        NotificationDbusInterface *dbusInterface = new NotificationDbusInterface(m_deviceId, notificationId, this);
        m_notificationList.append(dbusInterface);
    }
    endInsertRows();
}
This removes the notifications of the given type from the internal list of notifications, and inserts them into the notification list.
QVariant NotificationsModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid() || index.row() < 0 || index.row() >= m_notificationList.count() || !m_notificationList[index.row()]->isValid()) {
        return QVariant();
    }

    if (!m_dbusInterface || !m_dbusInterface->isValid()) {
        return QVariant();
    }

    NotificationDbusInterface *notification = m_notificationList[index.row()];

    // FIXME: This function gets called lots of times, producing lots of dbus calls. Add a cache?
    switch (role) {
    case IconModelRole:
        return QIcon::fromTheme(QStringLiteral("device-notifier"));
    case IdModelRole:
        return notification->internalId();
    case NameModelRole:
        return notification->ticker();
    case ContentModelRole:
        return QString(); // To implement in the Android side
    case AppNameModelRole:
        return notification->appName();
    case DbusInterfaceRole:
        return QVariant::fromValue(notification);
    case DismissableModelRole:
        return notification->dismissable();
    case RepliableModelRole:
        return !notification->replyId().isEmpty();
    case IconPathModelRole:
        return notification->iconPath();
    case TitleModelRole:
        return notification->title();
    case TextModelRole:
        return notification->text();
    default:
        return QVariant();
    }
}
Returns a QVariant of the data of the given role of the given index.
NotificationDbusInterface *NotificationsModel::getNotification(const QModelIndex &index) const
{
    if (!index.isValid()) {
        return nullptr;
    }

    int row = index.row();
    if (row < 0 || row >= m_notificationList.size()) {
        return nullptr;
    }

    return m_notificationList[row];
}
Returns a pointer to the notification object of the given index. If the index is invalid, or the row is out of bounds, the function returns null.
int NotificationsModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid()) {
        // Return size 0 if we are a child because this is not a tree
        return 0;
    }

    return m_notificationList.count();
}
Returns the row count for the given parent index. It returns 0 if the parent is not a tree.
bool NotificationsModel::isAnyDimissable() const
{
    for (NotificationDbusInterface *notification : qAsConst(m_notificationList)) {
        if (notification->dismissable()) {
            return true;
        }
    }
    return false;
}
This function returns true if any notification is dismissable.
void NotificationsModel::dismissAll()
{
    for (NotificationDbusInterface *notification : qAsConst(m_notificationList)) {
        if (notification->dismissable()) {
            notification->dismiss();
        }
    }
}
This code dismisses all notifiers.
void NotificationsModel::clearNotifications()
{
    if (!m_notificationList.isEmpty()) {
        beginRemoveRows(QModelIndex(), 0, m_notificationList.size() - 1);
        qDeleteAll(m_notificationList);
        m_notificationList.clear();
        endRemoveRows();
    }
}
This removes all notifications from the internal list.
void NotificationsModel::notificationUpdated()
{
    Q_EMIT dataChanged(index(0, 0), index(m_notificationList.size() - 1, 0));
}
This emits a signal called when the notification list has changed.