Clear Android session notifications consistently

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Iliyan Malchev
2026-03-22 23:05:32 -07:00
parent ebf0d2994c
commit 57b32d5a22
5 changed files with 122 additions and 3 deletions

View File

@@ -4,7 +4,6 @@ import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.agent.AgentManager
import android.content.Context
import android.content.Intent
import android.os.Build
@@ -39,8 +38,8 @@ object AgentQuestionNotifier {
val contentIntent = PendingIntent.getActivity(
context,
notificationId(sessionId),
Intent(context, MainActivity::class.java).apply {
putExtra(AgentManager.EXTRA_SESSION_ID, sessionId)
Intent(context, SessionDetailActivity::class.java).apply {
putExtra(SessionDetailActivity.EXTRA_SESSION_ID, sessionId)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
},
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,

View File

@@ -56,6 +56,17 @@ class AgentSessionController(context: Context) {
agentManager?.unregisterSessionUiLease(parentSessionId, token)
}
fun acknowledgeSessionUi(parentSessionId: String) {
val manager = agentManager ?: return
val token = Binder()
runCatching {
manager.registerSessionUiLease(parentSessionId, token)
}
runCatching {
manager.unregisterSessionUiLease(parentSessionId, token)
}
}
fun loadSnapshot(focusedSessionId: String?): AgentSnapshot {
val manager = agentManager ?: return AgentSnapshot.unavailable
val roleHolders = manager.getGenieRoleHolders(currentUserId())

View File

@@ -376,6 +376,14 @@ class SessionDetailActivity : Activity() {
showToast("Failed to answer question: ${err.message}")
}.onSuccess {
answerInput.post { answerInput.text.clear() }
topLevelSession(latestSnapshot)?.let { topLevelSession ->
SessionNotificationCoordinator.acknowledgeSessionTree(
context = this,
sessionController = sessionController,
topLevelSessionId = topLevelSession.sessionId,
sessionIds = listOf(topLevelSession.sessionId, selectedSession.sessionId),
)
}
showToast("Answered ${selectedSession.sessionId}")
refreshSnapshot(force = true)
}
@@ -412,6 +420,12 @@ class SessionDetailActivity : Activity() {
}.onFailure { err ->
showToast("Failed to cancel session: ${err.message}")
}.onSuccess {
SessionNotificationCoordinator.acknowledgeSessionTree(
context = this,
sessionController = sessionController,
topLevelSessionId = topLevelSession.sessionId,
sessionIds = listOf(topLevelSession.sessionId) + childSessions(latestSnapshot).map(AgentSessionDetails::sessionId),
)
showToast(
if (topLevelSession.anchor == AgentSessionInfo.ANCHOR_AGENT) {
"Cancelled active child sessions"
@@ -445,6 +459,12 @@ class SessionDetailActivity : Activity() {
childSessions(latestSnapshot).forEach { childSession ->
dismissedSessionStore.dismiss(childSession.sessionId)
}
SessionNotificationCoordinator.acknowledgeSessionTree(
context = this,
sessionController = sessionController,
topLevelSessionId = topLevelSession.sessionId,
sessionIds = listOf(topLevelSession.sessionId) + childSessions(latestSnapshot).map(AgentSessionDetails::sessionId),
)
}.onFailure { err ->
showToast("Failed to delete session: ${err.message}")
}.onSuccess {
@@ -468,6 +488,14 @@ class SessionDetailActivity : Activity() {
}.onFailure { err ->
showToast("Failed to cancel child session: ${err.message}")
}.onSuccess {
topLevelSession(latestSnapshot)?.let { topLevelSession ->
SessionNotificationCoordinator.acknowledgeSessionTree(
context = this,
sessionController = sessionController,
topLevelSessionId = topLevelSession.sessionId,
sessionIds = listOf(selectedChildSession.sessionId),
)
}
showToast("Cancelled ${selectedChildSession.sessionId}")
refreshSnapshot(force = true)
}
@@ -482,6 +510,14 @@ class SessionDetailActivity : Activity() {
}.onFailure { err ->
showToast("Failed to delete child session: ${err.message}")
}.onSuccess {
topLevelSession(latestSnapshot)?.let { topLevelSession ->
SessionNotificationCoordinator.acknowledgeSessionTree(
context = this,
sessionController = sessionController,
topLevelSessionId = topLevelSession.sessionId,
sessionIds = listOf(selectedChildSession.sessionId),
)
}
selectedChildSessionId = null
showToast("Deleted child session")
refreshSnapshot(force = true)

View File

@@ -0,0 +1,17 @@
package com.openai.codex.agent
import android.content.Context
object SessionNotificationCoordinator {
fun acknowledgeSessionTree(
context: Context,
sessionController: AgentSessionController,
topLevelSessionId: String,
sessionIds: Collection<String>,
) {
sessionIds.forEach { sessionId ->
AgentQuestionNotifier.cancel(context, sessionId)
}
sessionController.acknowledgeSessionUi(topLevelSessionId)
}
}

View File

@@ -125,6 +125,62 @@ fi
"${adb_cmd[@]}" get-state >/dev/null 2>&1 || fail "adb device is not available"
purge_existing_agent_sessions() {
local sessions_output line session_id parent_session_id initiator_package state top_level_session_id
local -a all_session_ids=()
local -a top_level_session_ids=()
local -a home_session_specs=()
sessions_output="$(${adb_cmd[@]} shell cmd agent list-sessions "$user_id" 2>/dev/null | tr -d '\r')"
[[ -n "$sessions_output" ]] || return 0
while IFS= read -r line; do
[[ "$line" == AgentSessionInfo\{* ]] || continue
session_id="$(printf '%s\n' "$line" | sed -n 's/.*sessionId=\([^,}]*\).*/\1/p')"
parent_session_id="$(printf '%s\n' "$line" | sed -n 's/.*parentSessionId=\([^,}]*\).*/\1/p')"
initiator_package="$(printf '%s\n' "$line" | sed -n 's/.*initiatorPackage=\([^,}]*\).*/\1/p')"
state="$(printf '%s\n' "$line" | sed -n 's/.*state=\([0-9][0-9]*\).*/\1/p')"
[[ -n "$session_id" ]] || continue
all_session_ids+=("$session_id")
if [[ "$parent_session_id" == "null" ]]; then
top_level_session_ids+=("$session_id")
if [[ "$initiator_package" != "null" ]] && printf '%s\n' "$line" | grep -q 'targetPackage=' && ! printf '%s\n' "$line" | grep -q 'targetPackage=null'; then
home_session_specs+=("$initiator_package:$session_id:$state")
fi
fi
done <<<"$sessions_output"
if [[ ${#all_session_ids[@]} -eq 0 ]]; then
return 0
fi
echo "Purging existing framework sessions"
for session_id in "${all_session_ids[@]}"; do
"${adb_cmd[@]}" shell cmd agent cancel-session "$session_id" >/dev/null 2>&1 || true
done
if (( ${#top_level_session_ids[@]} > 0 )); then
for top_level_session_id in "${top_level_session_ids[@]}"; do
local lease_id="codex-install-${top_level_session_id}"
"${adb_cmd[@]}" shell cmd agent register-ui-lease "$top_level_session_id" "$lease_id" >/dev/null 2>&1 || true
"${adb_cmd[@]}" shell cmd agent unregister-ui-lease "$top_level_session_id" "$lease_id" >/dev/null 2>&1 || true
done
fi
if (( ${#home_session_specs[@]} > 0 )); then
for home_spec in "${home_session_specs[@]}"; do
IFS=':' read -r initiator_package session_id state <<<"$home_spec"
if [[ "$state" == "4" ]]; then
"${adb_cmd[@]}" shell cmd agent consume-completed-home-session "$initiator_package" "$session_id" >/dev/null 2>&1 || true
else
"${adb_cmd[@]}" shell cmd agent consume-home-session "$initiator_package" "$session_id" >/dev/null 2>&1 || true
fi
done
fi
}
purge_existing_agent_sessions
echo "Stopping existing Agent/Genie processes"
for package_name in \
"$agent_package" \