Make related Agent sessions navigable

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Iliyan Malchev
2026-03-21 20:36:47 -07:00
parent 087c63cebb
commit 3503db0c34
3 changed files with 84 additions and 12 deletions

View File

@@ -4,11 +4,13 @@ import android.app.Activity
import android.app.agent.AgentManager
import android.app.agent.AgentSessionInfo
import android.content.Intent
import android.graphics.Typeface
import android.os.Binder
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import kotlin.concurrent.thread
@@ -153,8 +155,7 @@ class SessionDetailActivity : Activity() {
}
}
findViewById<TextView>(R.id.session_detail_summary).text = summary.trimEnd()
findViewById<TextView>(R.id.session_detail_related_sessions).text =
SessionUiFormatter.relatedSessionsText(this, snapshot.relatedSessions, selectedSession.sessionId)
renderRelatedSessions(snapshot.relatedSessions, selectedSession.sessionId)
findViewById<TextView>(R.id.session_detail_timeline).text = renderTimeline(snapshot)
val isWaitingForUser = selectedSession.state == AgentSessionInfo.STATE_WAITING_FOR_USER &&
@@ -188,6 +189,40 @@ class SessionDetailActivity : Activity() {
updateSessionUiLease(snapshot.parentSession?.sessionId ?: topLevelSession.sessionId)
}
private fun renderRelatedSessions(
sessions: List<AgentSessionDetails>,
selectedSessionId: String,
) {
val container = findViewById<LinearLayout>(R.id.session_detail_related_sessions_container)
val emptyView = findViewById<TextView>(R.id.session_detail_related_sessions_empty)
container.removeAllViews()
emptyView.visibility = if (sessions.isEmpty()) View.VISIBLE else View.GONE
sessions.forEach { session ->
val row = LinearLayout(this).apply {
orientation = LinearLayout.VERTICAL
setPadding(0, dp(8), 0, dp(8))
isClickable = true
isFocusable = true
setBackgroundResource(android.R.drawable.list_selector_background)
setOnClickListener {
if (session.sessionId != focusedSessionId) {
startActivity(intentForSession(session.sessionId))
}
}
}
val title = TextView(this).apply {
text = SessionUiFormatter.relatedSessionTitle(this@SessionDetailActivity, session)
setTypeface(typeface, if (session.sessionId == selectedSessionId) Typeface.BOLD else Typeface.NORMAL)
}
val subtitle = TextView(this).apply {
text = SessionUiFormatter.relatedSessionSubtitle(session)
}
row.addView(title)
row.addView(subtitle)
container.addView(row)
}
}
private fun renderTimeline(snapshot: AgentSnapshot): String {
val selectedSession = snapshot.selectedSession ?: return "No framework events yet."
val parentSession = snapshot.parentSession
@@ -355,4 +390,8 @@ class SessionDetailActivity : Activity() {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
}
private fun dp(value: Int): Int {
return (value * resources.displayMetrics.density).toInt()
}
}

View File

@@ -66,6 +66,34 @@ object SessionUiFormatter {
}.trimEnd()
}
fun relatedSessionTitle(
context: Context,
session: AgentSessionDetails,
): String {
val targetLabel = AppLabelResolver.loadAppLabel(context, session.targetPackage)
return buildString {
append(anchorLabel(session.anchor))
append("")
append(session.stateLabel)
append("")
append(targetLabel)
session.targetPackage?.let { append(" ($it)") }
}
}
fun relatedSessionSubtitle(session: AgentSessionDetails): String {
val detail = summarizeListDetail(
session.latestQuestion ?: session.latestResult ?: session.latestError ?: session.latestTrace,
)
return buildString {
append(session.targetPresentationLabel)
detail?.let {
append("")
append(it)
}
}
}
fun relatedSessionsText(
context: Context,
sessions: List<AgentSessionDetails>,
@@ -76,15 +104,13 @@ object SessionUiFormatter {
}
return sessions.joinToString("\n") { session ->
val marker = if (session.sessionId == selectedSessionId) "*" else "-"
val targetLabel = AppLabelResolver.loadAppLabel(context, session.targetPackage)
val detail = session.latestQuestion ?: session.latestResult ?: session.latestError ?: session.latestTrace
buildString {
append("$marker ${anchorLabel(session.anchor)} ${session.stateLabel} $targetLabel")
session.targetPackage?.let { append(" ($it)") }
append(" [${session.targetPresentationLabel}]")
detail?.takeIf(String::isNotBlank)?.let {
append("\n $it")
}
append(marker)
append(" ")
append(relatedSessionTitle(context, session))
append(" [")
append(relatedSessionSubtitle(session))
append("]")
}
}
}

View File

@@ -105,13 +105,20 @@
android:text="Related Sessions"
android:textStyle="bold" />
<LinearLayout
android:id="@+id/session_detail_related_sessions_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="vertical" />
<TextView
android:id="@+id/session_detail_related_sessions"
android:id="@+id/session_detail_related_sessions_empty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="No related sessions"
android:textIsSelectable="true" />
android:visibility="gone" />
<TextView
android:layout_width="wrap_content"