Skip to content

feat(notification): add ActivationToken support for wayland#1627

Open
18202781743 wants to merge 1 commit into
linuxdeepin:masterfrom
18202781743:agent/agent/975b667c
Open

feat(notification): add ActivationToken support for wayland#1627
18202781743 wants to merge 1 commit into
linuxdeepin:masterfrom
18202781743:agent/agent/975b667c

Conversation

@18202781743

@18202781743 18202781743 commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

为通知服务添加 wayland 激活令牌支持:

  • 非扩展动作先发送 ActivationToken 信号再发送 ActionInvoked
  • 扩展动作通过环境变量 XDG_ACTIVATION_TOKEN 传递令牌给 dde-am
  • 使用异步方式获取令牌,不阻塞主线程

Changes

  • panels/notification/server/notificationmanager.h: 添加 ActivationToken 信号
  • panels/notification/server/notificationmanager.cpp: 实现异步令牌请求和传递逻辑
  • panels/notification/server/dbusadaptor.h: 添加 ActivationToken D-Bus 信号

Test Plan

  1. 在 wayland 环境下测试通知动作触发
  2. 验证扩展动作是否正确传递 XDG_ACTIVATION_TOKEN 环境变量
  3. 验证非扩展动作是否正确发送 ActivationToken 信号

Summary by Sourcery

Add Wayland activation token support to notification actions and expose it via D-Bus.

New Features:

  • Emit an ActivationToken D-Bus signal from the notification manager for Wayland activation support.
  • Attach XDG activation tokens to extended notification actions when launching dde-am.
  • Emit activation tokens for non-extended notification actions before action invocation when available.

Enhancements:

  • Use asynchronous XdgActivation token retrieval to avoid blocking the notification handling thread.

Add xdg-activation-v1 token support to notification service:
- Emit ActivationToken signal before ActionInvoked for non-extended actions
- Pass XDG_ACTIVATION_TOKEN env to dde-am for extended actions
- Use async token request without blocking

为通知服务添加wayland激活令牌支持:
- 非扩展动作先发送ActivationToken信号再发送ActionInvoked
- 扩展动作通过环境变量传递令牌给dde-am
- 使用异步方式获取令牌,不阻塞主线程

Log: 为通知服务添加wayland激活令牌支持
@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: 18202781743

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai

sourcery-ai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds Wayland XDG activation token support to the notification service by emitting a new ActivationToken D-Bus signal for non-extended actions and propagating the token to dde-am via XDG_ACTIVATION_TOKEN for extended actions, using an asynchronous token retrieval mechanism.

Sequence diagram for Wayland activation token handling in notification actions

sequenceDiagram
    actor User
    participant NotificationManager
    participant XdgActivation
    participant DbusAdaptor
    participant DdeAm

    rect rgb(230,230,255)
        User ->> NotificationManager: actionInvoked(id, bubbleId, actionKey)
        NotificationManager ->> XdgActivation: new XdgActivation
        NotificationManager ->> XdgActivation: requestToken()
        XdgActivation -->> NotificationManager: tokenReady(token)
        alt [token not empty]
            NotificationManager ->> DbusAdaptor: ActivationToken(bubbleId, token)
        end
        NotificationManager ->> DbusAdaptor: ActionInvoked(bubbleId, actionKey)
        NotificationManager ->> DbusAdaptor: NotificationClosed(bubbleId, Closed)
    end

    rect rgb(230,255,230)
        User ->> NotificationManager: doActionInvoked(entity, actionId, actionKey)
        NotificationManager ->> XdgActivation: new XdgActivation
        NotificationManager ->> XdgActivation: requestToken()
        XdgActivation -->> NotificationManager: tokenReady(token)
        NotificationManager ->> DdeAm: startDetached("dde-am", amArgs)
        opt [token not empty]
            NotificationManager ->> DdeAm: set env XDG_ACTIVATION_TOKEN
        end
    end
Loading

File-Level Changes

Change Details Files
Emit ActivationToken signal for non-extended notification actions after asynchronously obtaining a Wayland activation token.
  • Include the XdgActivation header to enable Wayland activation token handling.
  • Wrap the existing ActionInvoked/NotificationClosed emission in an asynchronous XdgActivation::tokenReady callback.
  • On tokenReady, emit the new ActivationToken signal if a non-empty token is available, then emit ActionInvoked and NotificationClosed, and clean up the XdgActivation instance.
panels/notification/server/notificationmanager.cpp
panels/notification/server/notificationmanager.h
Pass Wayland activation tokens to dde-am as XDG_ACTIVATION_TOKEN for extended actions before launching the process.
  • Replace the direct QProcess start for dde-am extended actions with an asynchronous XdgActivation flow.
  • In the tokenReady callback, construct the QProcess, clear DSG_APP_ID from the environment, and insert XDG_ACTIVATION_TOKEN when a token is available.
  • Start dde-am detached with the modified environment and delete the XdgActivation instance after use.
panels/notification/server/notificationmanager.cpp
Expose the new ActivationToken signal over D-Bus and update copyright years.
  • Add an ActivationToken(uint id, const QString &token) signal to both notification D-Bus adaptor classes, replacing the previous TODO comment.
  • Update the SPDX-FileCopyrightText year range to 2024 - 2026.
panels/notification/server/dbusadaptor.h

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

我来对这段代码进行审查,从语法逻辑、代码质量、性能和安全性几个方面提出改进意见:

语法逻辑

  1. ActivationToken 函数的实现逻辑合理,但存在一些潜在问题:
    • actionInvoked 方法中创建的 XdgActivation 对象没有错误处理机制,如果 requestToken() 失败,可能会导致通知卡住。
    • doActionInvoked 中对 XdgActivation 的使用方式与 actionInvoked 中不一致,可能导致行为不一致。

代码质量

  1. 内存管理:

    • 在两个地方都使用了 new XdgActivationdeleteLater,虽然这样可以避免内存泄漏,但最好使用智能指针(如 QScopedPointer)来管理资源,这样更安全且代码更清晰。
  2. 代码重复:

    • XdgActivation 的使用模式在两处重复,可以提取为一个私有辅助方法,减少代码重复。
  3. 错误处理:

    • 缺少对 XdgActivation 操作失败的处理,应该添加适当的错误处理机制。

性能

  1. 对象创建:

    • 每次触发动作都创建新的 XdgActivation 对象,可能会带来一定的性能开销。可以考虑对象复用或对象池机制。
  2. 连接管理:

    • 使用 Qt::SingleShotConnection 是正确的,但可以确保在对象删除时断开所有连接,避免潜在的内存问题。

安全性

  1. 环境变量处理:

    • 在设置 XDG_ACTIVATION_TOKEN 时,应该验证 token 的有效性,防止恶意注入。
  2. 进程启动:

    • 使用 startDetached 启动进程是安全的,但应该确保参数不会被恶意注入。当前的代码使用 amArgs 传递参数,需要确保这些参数是经过适当清理的。

改进建议

  1. 添加错误处理:
connect(activation, &DS_NAMESPACE::XdgActivation::tokenFailed, this,
    [this, bubbleId, actionKey](const QString &error) {
        qWarning(notifyLog) << "Failed to get activation token:" << error;
        Q_EMIT ActionInvoked(bubbleId, actionKey);
        Q_EMIT NotificationClosed(bubbleId, NotifyEntity::Closed);
        activation->deleteLater();
    }, Qt::SingleShotConnection);
  1. 使用智能指针管理资源:
auto activation = QSharedPointer<DS_NAMESPACE::XdgActivation>::create(this);
  1. 提取公共方法:
void NotificationManager::handleActionWithToken(uint bubbleId, const QString &actionKey, 
    std::function<void(const QString &token)> callback)
{
    auto activation = QSharedPointer<DS_NAMESPACE::XdgActivation>::create(this);
    connect(activation.get(), &DS_NAMESPACE::XdgActivation::tokenReady, this,
        [this, bubbleId, actionKey, callback, activation](const QString &token) {
            callback(token);
            Q_EMIT ActionInvoked(bubbleId, actionKey);
            Q_EMIT NotificationClosed(bubbleId, NotifyEntity::Closed);
        }, Qt::SingleShotConnection);
    connect(activation.get(), &DS_NAMESPACE::XdgActivation::tokenFailed, this,
        [this, bubbleId, actionKey, activation](const QString &error) {
            qWarning(notifyLog) << "Failed to get activation token:" << error;
            Q_EMIT ActionInvoked(bubbleId, actionKey);
            Q_EMIT NotificationClosed(bubbleId, NotifyEntity::Closed);
        }, Qt::SingleShotConnection);
    activation->requestToken();
}
  1. 参数验证:
if (!token.isEmpty() && token.isValid()) {  // 假设 isValid() 是验证方法
    proEnv.insert("XDG_ACTIVATION_TOKEN", token);
}
  1. 版权信息更新:
  • 版权日期从 2024 更新到 2024-2026 是合理的,表明代码维护仍在继续。

这些改进将提高代码的健壮性、可维护性和安全性,同时保持良好的性能表现。

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • Both in actionInvoked and doActionInvoked the asynchronous XdgActivation flow can indefinitely delay emitting ActionInvoked/NotificationClosed or starting dde-am if tokenReady is never emitted; consider adding an error/timeout path or a fallback that proceeds without a token.
  • You unconditionally create a new XdgActivation instance for every action, which may be unnecessary on non-Wayland sessions and could add overhead; consider guarding this logic with a Wayland/session check or reusing a shared helper instead of allocating per-action.
  • The XdgActivation setup and cleanup code is duplicated in two places; consider extracting a small helper (e.g., requestActivationToken(callback)) to centralize token acquisition and ensure consistent behavior and resource management.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Both in `actionInvoked` and `doActionInvoked` the asynchronous `XdgActivation` flow can indefinitely delay emitting `ActionInvoked`/`NotificationClosed` or starting `dde-am` if `tokenReady` is never emitted; consider adding an error/timeout path or a fallback that proceeds without a token.
- You unconditionally create a new `XdgActivation` instance for every action, which may be unnecessary on non-Wayland sessions and could add overhead; consider guarding this logic with a Wayland/session check or reusing a shared helper instead of allocating per-action.
- The `XdgActivation` setup and cleanup code is duplicated in two places; consider extracting a small helper (e.g., `requestActivationToken(callback)`) to centralize token acquisition and ensure consistent behavior and resource management.

## Individual Comments

### Comment 1
<location path="panels/notification/server/notificationmanager.cpp" line_range="139" />
<code_context>
-    Q_EMIT ActionInvoked(bubbleId, actionKey);
-    Q_EMIT NotificationClosed(bubbleId, NotifyEntity::Closed);
+    // For non-extended actions, emit ActivationToken first if available
+    auto *activation = new DS_NAMESPACE::XdgActivation(this);
+    connect(activation, &DS_NAMESPACE::XdgActivation::tokenReady, this,
+        [this, bubbleId, actionKey, activation](const QString &token) {
</code_context>
<issue_to_address>
**issue (complexity):** Consider extracting the repeated XdgActivation setup into a reusable helper that takes a handler functor to simplify control flow and avoid duplication in both call sites.

The `XdgActivation` usage is duplicated and adds nested lambdas and lifetime handling in both call sites. Extracting a small helper that owns `XdgActivation` and just calls a functor when the token is ready will keep both sites linear and remove the extra `amArgsCopy`.

Example helper (private method in `NotificationManager`):

```c++
class NotificationManager : public QObject
{
    Q_OBJECT
    // ...
private:
    template <typename F>
    void withActivationToken(F &&handler)
    {
        auto *activation = new DS_NAMESPACE::XdgActivation(this);
        connect(activation, &DS_NAMESPACE::XdgActivation::tokenReady, this,
                [activation, handler = std::forward<F>(handler)](const QString &token) mutable {
                    handler(token);
                    activation->deleteLater();
                },
                Qt::SingleShotConnection);
        activation->requestToken();
    }
};
```

Then `actionInvoked` becomes simpler and closer to the original structure:

```c++
void NotificationManager::actionInvoked(qint64 id, uint bubbleId, const QString &actionKey)
{
    qInfo(notifyLog) << "Action invoked, bubbleId:" << bubbleId << ", id:" << id << ", actionKey" << actionKey;
    actionInvoked(id, actionKey);

    withActivationToken([this, bubbleId, actionKey](const QString &token) {
        if (!token.isEmpty()) {
            Q_EMIT ActivationToken(bubbleId, token);
            qDebug(notifyLog) << "Emitted ActivationToken for non-extended action:" << token;
        }
        Q_EMIT ActionInvoked(bubbleId, actionKey);
        Q_EMIT NotificationClosed(bubbleId, NotifyEntity::Closed);
    });
}
```

And the extended action (`dde-am` launch) stays linear and avoids `amArgsCopy`:

```c++
if (!args.isEmpty()) {
    QString cmd = args.takeFirst();

    QStringList amArgs;
    amArgs << "-c" << cmd;
    if (!args.isEmpty()) {
        amArgs << "--" << args;
    }

    withActivationToken([amArgs = std::move(amArgs)](const QString &token) {
        QProcess pro;
        pro.setProgram("dde-am");
        pro.setArguments(amArgs);

        QProcessEnvironment proEnv = QProcessEnvironment::systemEnvironment();
        proEnv.remove("DSG_APP_ID");

        if (!token.isEmpty()) {
            proEnv.insert("XDG_ACTIVATION_TOKEN", token);
            qDebug(notifyLog) << "Set XDG_ACTIVATION_TOKEN for extended action:" << token;
        }

        pro.setProcessEnvironment(proEnv);
        pro.startDetached();
    });
}
```

This keeps all behavior (including token emission, `ActivationToken` signal, and process environment setup) while reducing duplication, nesting, and manual `new`/`deleteLater` in the main logic.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Q_EMIT ActionInvoked(bubbleId, actionKey);
Q_EMIT NotificationClosed(bubbleId, NotifyEntity::Closed);
// For non-extended actions, emit ActivationToken first if available
auto *activation = new DS_NAMESPACE::XdgActivation(this);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider extracting the repeated XdgActivation setup into a reusable helper that takes a handler functor to simplify control flow and avoid duplication in both call sites.

The XdgActivation usage is duplicated and adds nested lambdas and lifetime handling in both call sites. Extracting a small helper that owns XdgActivation and just calls a functor when the token is ready will keep both sites linear and remove the extra amArgsCopy.

Example helper (private method in NotificationManager):

class NotificationManager : public QObject
{
    Q_OBJECT
    // ...
private:
    template <typename F>
    void withActivationToken(F &&handler)
    {
        auto *activation = new DS_NAMESPACE::XdgActivation(this);
        connect(activation, &DS_NAMESPACE::XdgActivation::tokenReady, this,
                [activation, handler = std::forward<F>(handler)](const QString &token) mutable {
                    handler(token);
                    activation->deleteLater();
                },
                Qt::SingleShotConnection);
        activation->requestToken();
    }
};

Then actionInvoked becomes simpler and closer to the original structure:

void NotificationManager::actionInvoked(qint64 id, uint bubbleId, const QString &actionKey)
{
    qInfo(notifyLog) << "Action invoked, bubbleId:" << bubbleId << ", id:" << id << ", actionKey" << actionKey;
    actionInvoked(id, actionKey);

    withActivationToken([this, bubbleId, actionKey](const QString &token) {
        if (!token.isEmpty()) {
            Q_EMIT ActivationToken(bubbleId, token);
            qDebug(notifyLog) << "Emitted ActivationToken for non-extended action:" << token;
        }
        Q_EMIT ActionInvoked(bubbleId, actionKey);
        Q_EMIT NotificationClosed(bubbleId, NotifyEntity::Closed);
    });
}

And the extended action (dde-am launch) stays linear and avoids amArgsCopy:

if (!args.isEmpty()) {
    QString cmd = args.takeFirst();

    QStringList amArgs;
    amArgs << "-c" << cmd;
    if (!args.isEmpty()) {
        amArgs << "--" << args;
    }

    withActivationToken([amArgs = std::move(amArgs)](const QString &token) {
        QProcess pro;
        pro.setProgram("dde-am");
        pro.setArguments(amArgs);

        QProcessEnvironment proEnv = QProcessEnvironment::systemEnvironment();
        proEnv.remove("DSG_APP_ID");

        if (!token.isEmpty()) {
            proEnv.insert("XDG_ACTIVATION_TOKEN", token);
            qDebug(notifyLog) << "Set XDG_ACTIVATION_TOKEN for extended action:" << token;
        }

        pro.setProcessEnvironment(proEnv);
        pro.startDetached();
    });
}

This keeps all behavior (including token emission, ActivationToken signal, and process environment setup) while reducing duplication, nesting, and manual new/deleteLater in the main logic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants