docs: add master agent background notification plan
This commit is contained in:
@@ -0,0 +1,497 @@
|
|||||||
|
# Master Agent Background Notification Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** 为 Boss Android 原生客户端补齐主 Agent 会话后台通知,让用户离开前台后仍能及时收到主 Agent 新回复,并避免前台重复提醒。
|
||||||
|
|
||||||
|
**Architecture:** 继续复用现有 SSE 实时链路,在 Android 本地增加应用前后台可见性跟踪和主 Agent 通知路由器。`BossRealtimeClient` 负责把包含完整 payload 的实时事件抛给界面层,`MainActivity` 与 `ProjectDetailActivity` 在收到 `master-agent` 新消息事件时交给通知路由器判定是否通知、如何去重以及点击回跳。通知逻辑完全本地化,不引入 FCM,也不改变服务端协议。
|
||||||
|
|
||||||
|
**Tech Stack:** Android Java, NotificationCompat, Robolectric, JUnit4, existing Boss SSE client
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: 建立应用前后台与当前会话可见性跟踪
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `android/app/src/main/java/com/hyzq/boss/BossAppVisibilityTracker.java`
|
||||||
|
- Modify: `android/app/src/main/java/com/hyzq/boss/BossApplication.java`
|
||||||
|
- Modify: `android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java`
|
||||||
|
- Test: `android/app/src/test/java/com/hyzq/boss/BossNotificationRouterTest.java`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 写失败测试,约束前后台和当前会话可见性状态**
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Test
|
||||||
|
public void visibilityTrackerMarksForegroundAndVisibleProject() {
|
||||||
|
BossAppVisibilityTracker tracker = new BossAppVisibilityTracker();
|
||||||
|
|
||||||
|
tracker.onAppForegrounded();
|
||||||
|
tracker.setVisibleProjectId("master-agent");
|
||||||
|
|
||||||
|
assertTrue(tracker.isAppInForeground());
|
||||||
|
assertEquals("master-agent", tracker.getVisibleProjectId());
|
||||||
|
|
||||||
|
tracker.clearVisibleProjectId("master-agent");
|
||||||
|
tracker.onAppBackgrounded();
|
||||||
|
|
||||||
|
assertFalse(tracker.isAppInForeground());
|
||||||
|
assertNull(tracker.getVisibleProjectId());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 运行测试确认失败**
|
||||||
|
|
||||||
|
Run: `cd android && ./gradlew testDebugUnitTest --tests com.hyzq.boss.BossNotificationRouterTest.visibilityTrackerMarksForegroundAndVisibleProject`
|
||||||
|
Expected: FAIL,提示 `BossAppVisibilityTracker` 或测试方法不存在。
|
||||||
|
|
||||||
|
- [ ] **Step 3: 写最小实现**
|
||||||
|
|
||||||
|
```java
|
||||||
|
package com.hyzq.boss;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
final class BossAppVisibilityTracker {
|
||||||
|
private volatile boolean appInForeground;
|
||||||
|
private volatile @Nullable String visibleProjectId;
|
||||||
|
|
||||||
|
void onAppForegrounded() {
|
||||||
|
appInForeground = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onAppBackgrounded() {
|
||||||
|
appInForeground = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isAppInForeground() {
|
||||||
|
return appInForeground;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setVisibleProjectId(@Nullable String projectId) {
|
||||||
|
visibleProjectId = projectId == null || projectId.trim().isEmpty() ? null : projectId.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearVisibleProjectId(@Nullable String projectId) {
|
||||||
|
if (visibleProjectId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (projectId == null || visibleProjectId.equals(projectId.trim())) {
|
||||||
|
visibleProjectId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
String getVisibleProjectId() {
|
||||||
|
return visibleProjectId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
在 `BossApplication` 中挂单例:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public final class BossApplication extends Application {
|
||||||
|
private final BossAppVisibilityTracker visibilityTracker = new BossAppVisibilityTracker();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||||
|
super.onCreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BossAppVisibilityTracker getVisibilityTracker() {
|
||||||
|
return visibilityTracker;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
在 `ProjectDetailActivity` 的 `onResume/onPause` 中同步当前会话:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
((BossApplication) getApplication()).getVisibilityTracker().onAppForegrounded();
|
||||||
|
((BossApplication) getApplication()).getVisibilityTracker().setVisibleProjectId(projectId);
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
((BossApplication) getApplication()).getVisibilityTracker().clearVisibleProjectId(projectId);
|
||||||
|
...
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 运行测试确认通过**
|
||||||
|
|
||||||
|
Run: `cd android && ./gradlew testDebugUnitTest --tests com.hyzq.boss.BossNotificationRouterTest.visibilityTrackerMarksForegroundAndVisibleProject`
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
- [ ] **Step 5: 提交**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add android/app/src/main/java/com/hyzq/boss/BossAppVisibilityTracker.java android/app/src/main/java/com/hyzq/boss/BossApplication.java android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java android/app/src/test/java/com/hyzq/boss/BossNotificationRouterTest.java
|
||||||
|
git commit -m "feat: track app visibility for master agent notifications"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 2: 实现主 Agent 后台通知路由与去重
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `android/app/src/main/java/com/hyzq/boss/BossNotificationRouter.java`
|
||||||
|
- Modify: `android/app/src/main/AndroidManifest.xml`
|
||||||
|
- Modify: `android/app/src/main/java/com/hyzq/boss/MainActivity.java`
|
||||||
|
- Test: `android/app/src/test/java/com/hyzq/boss/BossNotificationRouterTest.java`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 写失败测试,约束后台主 Agent 新回复会发通知、重复消息不会重复通知**
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Test
|
||||||
|
public void routerNotifiesOnlyForNewMasterAgentRepliesWhileBackgrounded() throws Exception {
|
||||||
|
Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
BossAppVisibilityTracker tracker = new BossAppVisibilityTracker();
|
||||||
|
tracker.onAppBackgrounded();
|
||||||
|
BossNotificationRouter router = new BossNotificationRouter(context, tracker);
|
||||||
|
|
||||||
|
JSONObject message = new JSONObject()
|
||||||
|
.put("id", "m-2")
|
||||||
|
.put("sender", "master")
|
||||||
|
.put("senderLabel", "主 Agent · gpt-5.4-mini")
|
||||||
|
.put("body", "主 Agent 已完成同步。")
|
||||||
|
.put("sentAt", "2026-04-21T10:00:00.000Z");
|
||||||
|
JSONObject payload = new JSONObject()
|
||||||
|
.put("projectId", "master-agent")
|
||||||
|
.put("projectMessagesPayload", new JSONObject().put(
|
||||||
|
"project",
|
||||||
|
new JSONObject().put("messages", new JSONArray().put(message))
|
||||||
|
));
|
||||||
|
|
||||||
|
assertTrue(router.maybeNotifyForRealtimeEvent(new BossRealtimeEvent("project.messages.updated", payload)));
|
||||||
|
assertFalse(router.maybeNotifyForRealtimeEvent(new BossRealtimeEvent("project.messages.updated", payload)));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 运行测试确认失败**
|
||||||
|
|
||||||
|
Run: `cd android && ./gradlew testDebugUnitTest --tests com.hyzq.boss.BossNotificationRouterTest.routerNotifiesOnlyForNewMasterAgentRepliesWhileBackgrounded`
|
||||||
|
Expected: FAIL,提示 `BossNotificationRouter` 不存在或断言不满足。
|
||||||
|
|
||||||
|
- [ ] **Step 3: 写最小实现**
|
||||||
|
|
||||||
|
```java
|
||||||
|
package com.hyzq.boss;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
final class BossNotificationRouter {
|
||||||
|
static final String CHANNEL_ID = "boss_master_agent_messages";
|
||||||
|
static final int MASTER_AGENT_NOTIFICATION_ID = 2001;
|
||||||
|
|
||||||
|
private final Context appContext;
|
||||||
|
private final BossAppVisibilityTracker visibilityTracker;
|
||||||
|
private @Nullable String lastNotifiedMessageId;
|
||||||
|
|
||||||
|
BossNotificationRouter(Context context, BossAppVisibilityTracker visibilityTracker) {
|
||||||
|
this.appContext = context.getApplicationContext();
|
||||||
|
this.visibilityTracker = visibilityTracker;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean maybeNotifyForRealtimeEvent(@Nullable BossRealtimeEvent event) {
|
||||||
|
if (!shouldNotify(event)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
JSONObject latestMessage = latestMasterMessage(event.payload);
|
||||||
|
if (latestMessage == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String messageId = latestMessage.optString("id", "").trim();
|
||||||
|
if (messageId.isEmpty() || messageId.equals(lastNotifiedMessageId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ensureChannel();
|
||||||
|
if (!notificationsAllowed()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
lastNotifiedMessageId = messageId;
|
||||||
|
NotificationManagerCompat.from(appContext).notify(
|
||||||
|
MASTER_AGENT_NOTIFICATION_ID,
|
||||||
|
new NotificationCompat.Builder(appContext, CHANNEL_ID)
|
||||||
|
.setSmallIcon(R.mipmap.ic_launcher)
|
||||||
|
.setContentTitle("主 Agent")
|
||||||
|
.setContentText(latestMessage.optString("body", "你有一条新回复"))
|
||||||
|
.setStyle(new NotificationCompat.BigTextStyle().bigText(latestMessage.optString("body", "你有一条新回复")))
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.setContentIntent(buildContentIntent())
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldNotify(@Nullable BossRealtimeEvent event) {
|
||||||
|
if (event == null || !"project.messages.updated".equals(event.eventName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!"master-agent".equals(event.payload.optString("projectId", "").trim())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (visibilityTracker.isAppInForeground() && "master-agent".equals(visibilityTracker.getVisibleProjectId())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable JSONObject latestMasterMessage(JSONObject payload) {
|
||||||
|
JSONObject projectPayload = payload.optJSONObject("projectMessagesPayload");
|
||||||
|
JSONObject project = projectPayload == null ? null : projectPayload.optJSONObject("project");
|
||||||
|
JSONArray messages = project == null ? null : project.optJSONArray("messages");
|
||||||
|
if (messages == null || messages.length() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
JSONObject latest = messages.optJSONObject(messages.length() - 1);
|
||||||
|
if (latest == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String sender = latest.optString("sender", "");
|
||||||
|
String senderLabel = latest.optString("senderLabel", "");
|
||||||
|
if (!"master".equals(sender) && !senderLabel.contains("主 Agent")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return latest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PendingIntent buildContentIntent() {
|
||||||
|
Intent intent = new Intent(appContext, ProjectDetailActivity.class)
|
||||||
|
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "master-agent")
|
||||||
|
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "主 Agent")
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
return PendingIntent.getActivity(
|
||||||
|
appContext,
|
||||||
|
901,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean notificationsAllowed() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
|
||||||
|
&& ContextCompat.checkSelfPermission(appContext, Manifest.permission.POST_NOTIFICATIONS)
|
||||||
|
!= PackageManager.PERMISSION_GRANTED) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return NotificationManagerCompat.from(appContext).areNotificationsEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureChannel() {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NotificationManager manager = appContext.getSystemService(NotificationManager.class);
|
||||||
|
if (manager == null || manager.getNotificationChannel(CHANNEL_ID) != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NotificationChannel channel = new NotificationChannel(
|
||||||
|
CHANNEL_ID,
|
||||||
|
"主 Agent 消息",
|
||||||
|
NotificationManager.IMPORTANCE_DEFAULT
|
||||||
|
);
|
||||||
|
channel.setDescription("Boss 主 Agent 在后台的新回复通知");
|
||||||
|
manager.createNotificationChannel(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
在 `AndroidManifest.xml` 增加:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
```
|
||||||
|
|
||||||
|
在 `MainActivity.onCreate` 初始化通知路由器,`onResume/onPause` 同步前后台状态,并在 `handleRealtimeEvent` 开头调用:
|
||||||
|
|
||||||
|
```java
|
||||||
|
notificationRouter.maybeNotifyForRealtimeEvent(event);
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 运行测试确认通过**
|
||||||
|
|
||||||
|
Run: `cd android && ./gradlew testDebugUnitTest --tests com.hyzq.boss.BossNotificationRouterTest`
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
- [ ] **Step 5: 提交**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add android/app/src/main/java/com/hyzq/boss/BossNotificationRouter.java android/app/src/main/AndroidManifest.xml android/app/src/main/java/com/hyzq/boss/MainActivity.java android/app/src/test/java/com/hyzq/boss/BossNotificationRouterTest.java
|
||||||
|
git commit -m "feat: notify master agent replies while app is backgrounded"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 3: 让主 Agent 会话前台抑制通知且不影响现有实时刷新
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java`
|
||||||
|
- Modify: `android/app/src/test/java/com/hyzq/boss/ProjectDetailActivityRealtimeTest.java`
|
||||||
|
- Modify: `android/app/src/test/java/com/hyzq/boss/BossRealtimeClientTest.java`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 写失败测试,约束前台停留在主 Agent 会话时不发通知,但实时 patch 仍正常落屏**
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Test
|
||||||
|
public void masterAgentForegroundConversationSuppressesNotificationButStillProcessesRealtimePatch() throws Exception {
|
||||||
|
Intent intent = new Intent()
|
||||||
|
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, "master-agent")
|
||||||
|
.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, "主 Agent");
|
||||||
|
TestRealtimeProjectDetailActivity activity = Robolectric
|
||||||
|
.buildActivity(TestRealtimeProjectDetailActivity.class, intent)
|
||||||
|
.setup()
|
||||||
|
.resume()
|
||||||
|
.get();
|
||||||
|
|
||||||
|
JSONObject payload = new JSONObject()
|
||||||
|
.put("projectId", "master-agent")
|
||||||
|
.put("projectMessagesPayload", new JSONObject().put(
|
||||||
|
"project",
|
||||||
|
new JSONObject().put("messages", new JSONArray().put(
|
||||||
|
new JSONObject()
|
||||||
|
.put("id", "m-2")
|
||||||
|
.put("sender", "master")
|
||||||
|
.put("senderLabel", "主 Agent · gpt-5.4-mini")
|
||||||
|
.put("body", "我已经同步完成。")
|
||||||
|
))
|
||||||
|
));
|
||||||
|
|
||||||
|
ReflectionHelpers.callInstanceMethod(
|
||||||
|
activity,
|
||||||
|
"handleRealtimeEvent",
|
||||||
|
ReflectionHelpers.ClassParameter.from(BossRealtimeEvent.class, new BossRealtimeEvent("project.messages.updated", payload))
|
||||||
|
);
|
||||||
|
|
||||||
|
assertEquals(1, activity.messageReloadCount);
|
||||||
|
assertEquals(0, activity.emittedNotificationCount);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 运行测试确认失败**
|
||||||
|
|
||||||
|
Run: `cd android && ./gradlew testDebugUnitTest --tests com.hyzq.boss.ProjectDetailActivityRealtimeTest.masterAgentForegroundConversationSuppressesNotificationButStillProcessesRealtimePatch`
|
||||||
|
Expected: FAIL,通知抑制钩子还不存在。
|
||||||
|
|
||||||
|
- [ ] **Step 3: 写最小实现**
|
||||||
|
|
||||||
|
在 `ProjectDetailActivity` 增加可覆写的通知入口:
|
||||||
|
|
||||||
|
```java
|
||||||
|
void maybeNotifyForRealtimeEvent(BossRealtimeEvent event) {
|
||||||
|
BossApplication app = (BossApplication) getApplication();
|
||||||
|
if (app == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
app.notificationRouter().maybeNotifyForRealtimeEvent(event);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
并在 `handleRealtimeEvent` 的 projectId 校验后、reload 判断前调用:
|
||||||
|
|
||||||
|
```java
|
||||||
|
maybeNotifyForRealtimeEvent(event);
|
||||||
|
```
|
||||||
|
|
||||||
|
同时让 `BossApplication` 提供共享 `BossNotificationRouter`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
private BossNotificationRouter notificationRouter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
...
|
||||||
|
notificationRouter = new BossNotificationRouter(this, visibilityTracker);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BossNotificationRouter notificationRouter() {
|
||||||
|
return notificationRouter;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`ProjectDetailActivityRealtimeTest.TestRealtimeProjectDetailActivity` 增加测试桩:
|
||||||
|
|
||||||
|
```java
|
||||||
|
int emittedNotificationCount;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void maybeNotifyForRealtimeEvent(BossRealtimeEvent event) {
|
||||||
|
emittedNotificationCount += 1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
再把测试断言改为:前台主 Agent 会话通过真实 `BossNotificationRouter` 判断后应为 0;普通线程不影响现有 reload 行为。
|
||||||
|
|
||||||
|
- [ ] **Step 4: 运行测试确认通过**
|
||||||
|
|
||||||
|
Run: `cd android && ./gradlew testDebugUnitTest --tests com.hyzq.boss.ProjectDetailActivityRealtimeTest --tests com.hyzq.boss.BossRealtimeClientTest`
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
- [ ] **Step 5: 提交**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add android/app/src/main/java/com/hyzq/boss/BossApplication.java android/app/src/main/java/com/hyzq/boss/ProjectDetailActivity.java android/app/src/test/java/com/hyzq/boss/ProjectDetailActivityRealtimeTest.java android/app/src/test/java/com/hyzq/boss/BossRealtimeClientTest.java
|
||||||
|
git commit -m "test: cover foreground suppression for master agent notifications"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 4: 真机验证 PLB110 后台通知与前台抑制
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Verify only: `android/app/build/outputs/apk/debug/app-debug.apk`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 构建 debug APK**
|
||||||
|
|
||||||
|
Run: `cd android && ./gradlew assembleDebug`
|
||||||
|
Expected: `BUILD SUCCESSFUL`
|
||||||
|
|
||||||
|
- [ ] **Step 2: 安装到 PLB110**
|
||||||
|
|
||||||
|
Run: `adb -s 'adb-HYKVDI4TT4OVAYZT-SHKaaU (2)._adb-tls-connect._tcp' install -r app/build/outputs/apk/debug/app-debug.apk`
|
||||||
|
Expected: `Success`
|
||||||
|
|
||||||
|
- [ ] **Step 3: 后台通知验证**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
adb -s 'adb-HYKVDI4TT4OVAYZT-SHKaaU (2)._adb-tls-connect._tcp' shell monkey -p com.hyzq.boss -c android.intent.category.LAUNCHER 1
|
||||||
|
# 进入主 Agent 会话后返回桌面
|
||||||
|
# 再用 curl 向 master-agent 触发一条新回复或复用现有联调账号触发主 Agent 回复
|
||||||
|
adb -s 'adb-HYKVDI4TT4OVAYZT-SHKaaU (2)._adb-tls-connect._tcp' shell dumpsys notification --noredact | grep -n "主 Agent"
|
||||||
|
```
|
||||||
|
Expected: 后台能看到主 Agent 新通知;点击通知回到主 Agent 会话。
|
||||||
|
|
||||||
|
- [ ] **Step 4: 前台抑制验证**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
# 保持 Boss 前台停留在主 Agent 会话
|
||||||
|
# 再触发一条主 Agent 新回复
|
||||||
|
adb -s 'adb-HYKVDI4TT4OVAYZT-SHKaaU (2)._adb-tls-connect._tcp' shell dumpsys notification --noredact | grep -n "主 Agent"
|
||||||
|
```
|
||||||
|
Expected: 不新增系统通知,但会话内容实时刷新到界面。
|
||||||
|
|
||||||
|
- [ ] **Step 5: 汇总并提交**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add android/app/src/main AndroidManifest.xml android/app/src/test/java/com/hyzq/boss
|
||||||
|
git commit -m "feat: add background notifications for master agent replies"
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user