[OpenDocString] kdeconnect-kde (cpp)
notification.cpp
Notification::Notification(const NetworkPacket &np, const Device *device, QObject *parent)
    : QObject(parent)
    , m_imagesDir()
    , m_device(device)
{
    // Make a own directory for each user so no one can see each others icons
    QString username;
#ifdef Q_OS_WIN
    username = QString::fromLatin1(qgetenv("USERNAME"));
#else
    username = QString::fromLatin1(qgetenv("USER"));
#endif

    m_imagesDir.setPath(QDir::temp().absoluteFilePath(QStringLiteral("kdeconnect_") + username));
    m_imagesDir.mkpath(m_imagesDir.absolutePath());
    QFile(m_imagesDir.absolutePath()).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner);
    m_ready = false;

    parseNetworkPacket(np);
    createKNotification(np);

    connect(m_notification, QOverload::of(&KNotification::activated), this, [this](unsigned int actionIndex) {
// Since 5.81 we use KNotification's inline reply instead of our own action
#if KNOTIFICATIONS_VERSION < QT_VERSION_CHECK(5, 81, 0)
        // Do nothing for our own reply action
        if (!m_requestReplyId.isEmpty() && actionIndex == 1) {
            return;
        }
#endif
        // Notification action indices start at 1
        Q_EMIT actionTriggered(m_internalId, m_actions[actionIndex - 1]);
    });
}
This constructs a notification object from a network packet, a device and a parent object, creates the images dir, sets permissions for the user and creates the notification object, and connects the notification to the internal action signal.
Notification::~Notification()
{
}
This removes the notification object from the list of notifications.
void Notification::dismiss()
{
    if (m_dismissable) {
        Q_EMIT dismissRequested(m_internalId);
    }
}
This sends a dismiss request if the notification is dismissable.
void Notification::show()
{
    m_ready = true;
    Q_EMIT ready();
    if (!m_silent) {
        m_notification->sendEvent();
    }
}
This shows the notification. It is called when the application is ready and is ready to send a notification.
void Notification::update(const NetworkPacket &np)
{
    parseNetworkPacket(np);
    createKNotification(np);
}
This creates a Notification object from a NetworkPacket object. It parses the network packet, creates it and updates the notification.
void Notification::createKNotification(const NetworkPacket &np)
{
    if (!m_notification) {
        m_notification = new KNotification(QStringLiteral("notification"), KNotification::CloseOnTimeout, this);
        m_notification->setComponentName(QStringLiteral("kdeconnect"));
        m_notification->setHint(QStringLiteral("resident"),
                                true); // This means the notification won't be deleted automatically, but only with KNotifications 5.81
    }

    QString escapedTitle = m_title.toHtmlEscaped();
    // notification title text does not have markup, but in some cases below it is used in body text so we escape it
    QString escapedText = m_text.toHtmlEscaped();
    QString escapedTicker = m_ticker.toHtmlEscaped();

    if (NotificationServerInfo::instance().supportedHints().testFlag(NotificationServerInfo::X_KDE_DISPLAY_APPNAME)) {
        m_notification->setTitle(m_title);
        m_notification->setText(escapedText);
        m_notification->setHint(QStringLiteral("x-kde-display-appname"), m_appName.toHtmlEscaped());
    } else {
        m_notification->setTitle(m_appName);

        if (m_title.isEmpty() && m_text.isEmpty()) {
            m_notification->setText(escapedTicker);
        } else if (m_appName == m_title) {
            m_notification->setText(escapedText);
        } else if (m_title.isEmpty()) {
            m_notification->setText(escapedText);
        } else if (m_text.isEmpty()) {
            m_notification->setText(escapedTitle);
        } else {
            m_notification->setText(escapedTitle + QStringLiteral(": ") + escapedText);
        }
    }

    m_notification->setHint(QStringLiteral("x-kde-origin-name"), m_device->name());

    if (!m_requestReplyId.isEmpty()) {
#if KNOTIFICATIONS_VERSION >= QT_VERSION_CHECK(5, 81, 0)
        auto replyAction = std::make_unique(i18nc("@action:button", "Reply"));
        replyAction->setPlaceholderText(i18nc("@info:placeholder", "Reply to %1...", m_appName));
        replyAction->setFallbackBehavior(KNotificationReplyAction::FallbackBehavior::UseRegularAction);
        QObject::connect(replyAction.get(), &KNotificationReplyAction::replied, this, &Notification::replied);
        QObject::connect(replyAction.get(), &KNotificationReplyAction::activated, this, &Notification::reply);
        m_notification->setReplyAction(std::move(replyAction));
#else
        m_actions.prepend(i18n("Reply"));
        connect(m_notification, &KNotification::action1Activated, this, &Notification::reply, Qt::UniqueConnection);
#endif
    }

    m_notification->setActions(m_actions);

    m_hasIcon = m_hasIcon && !m_payloadHash.isEmpty();

    if (!m_hasIcon) {
        show();
    } else {
        m_iconPath = m_imagesDir.absoluteFilePath(m_payloadHash);
        loadIcon(np);
    }
}
This creates a KNotification object from a NetworkPacket object. It takes the notification title and text from the internal data of the notification object, and escapes the title, text, and ticker values, if they are empty, and adds the app name and text to the notification title. If the notification server is not supported hints, the notification title is prefixed with the x - kde - display-appname markup, but in some cases, the text is prefixed with the app name. If the notification request is not reachable, the reply is made a new one that will be sent to the device. If the request is not reachable, the reply is made a place holder of the reply action.
void Notification::loadIcon(const NetworkPacket &np)
{
    m_ready = false;

    if (QFileInfo::exists(m_iconPath)) {
        applyIcon();
        show();
    } else {
        FileTransferJob *fileTransferJob = s_downloadsInProgress.value(m_iconPath);
        if (!fileTransferJob) {
            fileTransferJob = np.createPayloadTransferJob(QUrl::fromLocalFile(m_iconPath));
            fileTransferJob->start();
            s_downloadsInProgress[m_iconPath] = fileTransferJob;
        }

        connect(fileTransferJob, &FileTransferJob::result, this, [this, fileTransferJob] {
            s_downloadsInProgress.remove(m_iconPath);
            if (fileTransferJob->error()) {
                qCDebug(KDECONNECT_PLUGIN_NOTIFICATION) << "Error in FileTransferJob: " << fileTransferJob->errorString();
            } else {
                applyIcon();
            }
            show();
        });
    }
}
This loads the icon from the given path, if it exists, creates it if it doesn't exist, and connects it to the Notification object. It also connects the FileTransferJob object to the NetworkPacket object, which will handle the transfer of the icon.
void Notification::applyIcon()
{
    QPixmap icon(m_iconPath, "PNG");
    m_notification->setPixmap(icon);
}
Adds the icon to the notification.
void Notification::reply()
{
    Q_EMIT replyRequested();
}
This sends a reply to the user when there is a response.
void Notification::sendReply(const QString& message)
{
    Q_EMIT replied(message);
}
This sends a reply message, with a timeout.
void Notification::parseNetworkPacket(const NetworkPacket& np)
{
    m_internalId = np.get(QStringLiteral("id"));
    m_appName = np.get(QStringLiteral("appName"));
    m_ticker = np.get(QStringLiteral("ticker"));
    m_title = np.get(QStringLiteral("title"));
    m_text = np.get(QStringLiteral("text"));
    m_dismissable = np.get(QStringLiteral("isClearable"));
    m_hasIcon = np.hasPayload();
    m_silent = np.get(QStringLiteral("silent"));
    m_payloadHash = np.get(QStringLiteral("payloadHash"));
    m_requestReplyId = np.get(QStringLiteral("requestReplyId"), QString());

    m_actions.clear();

    const auto actions = np.get(QStringLiteral("actions"));
    for (const QJsonValue &value : actions) {
        m_actions.append(value.toString());
    }
}
This constructs a Notification object from a QJsonArray. It takes the id, appName, ticker, text, hasIcon, requestReplyId, actions from a network packet.