@@ -6,12 +6,17 @@ import androidx.activity.ComponentActivity
66import androidx.activity.compose.setContent
77import androidx.compose.foundation.layout.Box
88import androidx.compose.foundation.layout.fillMaxSize
9+ import androidx.compose.foundation.layout.fillMaxWidth
910import androidx.compose.foundation.layout.padding
11+ import androidx.compose.foundation.layout.width
12+ import androidx.compose.material3.Button
1013import androidx.compose.material3.CircularProgressIndicator
1114import androidx.compose.material3.MaterialTheme
1215import androidx.compose.material3.Scaffold
1316import androidx.compose.material3.Surface
1417import androidx.compose.material3.Text
18+ import androidx.compose.material3.TextField
19+ import androidx.compose.ui.unit.dp
1520import androidx.compose.runtime.*
1621import androidx.compose.ui.Alignment
1722import androidx.compose.ui.Modifier
@@ -40,68 +45,89 @@ class MainActivity : ComponentActivity() {
4045@Composable
4146fun SampleA2UIScreen () {
4247 val surfaceState = remember { SurfaceState () }
43- var isLoading by remember { mutableStateOf(true ) }
48+ var isLoading by remember { mutableStateOf(false ) }
4449 var errorMsg by remember { mutableStateOf<String ?>(null ) }
50+ var query by remember { mutableStateOf(" Find contact info for Alex Jordan" ) }
4551
4652 // Initialize our native A2A Client
4753 val a2aClient = remember { A2AClient () }
54+ val scope = rememberCoroutineScope()
4855
49- LaunchedEffect (Unit ) {
50- withContext(Dispatchers .IO ) {
51- try {
52- // Send an initial query to start the conversation
53- val messages = a2aClient.sendMessage(" Find contact info for Alex Jordan" )
54-
55- withContext(Dispatchers .Main ) {
56- android.util.Log .d(" MainActivity" , " Applying ${messages.size} updates to surface." )
57- messages.forEach {
58- android.util.Log .d(" MainActivity" , " Applying update: $it " )
59- surfaceState.applyUpdate(it)
56+ androidx.compose.foundation.layout.Column (
57+ modifier = Modifier .fillMaxSize()
58+ ) {
59+ // Input Area
60+ androidx.compose.foundation.layout.Row (
61+ modifier = Modifier
62+ .fillMaxWidth()
63+ .padding(16 .dp),
64+ verticalAlignment = Alignment .CenterVertically
65+ ) {
66+ androidx.compose.material3.TextField (
67+ value = query,
68+ onValueChange = { query = it },
69+ modifier = Modifier .weight(1f ),
70+ label = { Text (" Ask Agent" ) }
71+ )
72+ androidx.compose.foundation.layout.Spacer (modifier = Modifier .width(8 .dp))
73+ androidx.compose.material3.Button (
74+ onClick = {
75+ isLoading = true
76+ errorMsg = null
77+ scope.launch(Dispatchers .IO ) {
78+ try {
79+ val messages = a2aClient.sendMessage(query)
80+ withContext(Dispatchers .Main ) {
81+ messages.forEach { surfaceState.applyUpdate(it) }
82+ isLoading = false
83+ }
84+ } catch (e: Exception ) {
85+ e.printStackTrace()
86+ withContext(Dispatchers .Main ) {
87+ errorMsg = " Error: ${e.message} "
88+ isLoading = false
89+ }
90+ }
6091 }
61- isLoading = false
62- }
63- } catch (e: Exception ) {
64- e.printStackTrace()
65- withContext(Dispatchers .Main ) {
66- errorMsg = " Error: ${e.message} . \n Make sure 'uv run .' is running in 'contact_lookup'!"
67- isLoading = false
68- }
92+ },
93+ enabled = ! isLoading
94+ ) {
95+ Text (" Send" )
6996 }
7097 }
71- }
7298
73- if (isLoading) {
74- Box (modifier = Modifier .fillMaxSize(), contentAlignment = Alignment .Center ) {
75- CircularProgressIndicator ()
76- }
77- } else if (errorMsg != null ) {
78- Box (modifier = Modifier .fillMaxSize(), contentAlignment = Alignment .Center ) {
79- Text (text = errorMsg!! )
80- }
81- } else {
82- A2UISurface (
83- surfaceId = " contact-card" , // Matches server's "contact-card" surface ID
84- state = surfaceState,
85- onUserAction = { action, src, contextMap ->
86- Log .d(" A2UI" , " Action: ${action.name} from $src with $contextMap " )
87- Log .d(" A2UI" , " Action: ${action.name} from $src with $contextMap " )
88-
89- // Construct the JSON object for the user action context
90- val actionData = kotlinx.serialization.json.JsonObject (contextMap)
91-
92- // Launch a coroutine to send the event to the server
93- // Note: ideally this should be handled by a ViewModel to avoid leak
94- kotlinx.coroutines.CoroutineScope (Dispatchers .IO ).launch {
95- try {
96- val updates = a2aClient.sendEvent(actionData)
97- withContext(Dispatchers .Main ) {
98- updates.forEach { surfaceState.applyUpdate(it) }
99+ // Content Area
100+ Box (modifier = Modifier .weight(1f ).fillMaxWidth()) {
101+ if (isLoading) {
102+ CircularProgressIndicator (modifier = Modifier .align(Alignment .Center ))
103+ } else if (errorMsg != null ) {
104+ Text (
105+ text = errorMsg!! ,
106+ color = MaterialTheme .colorScheme.error,
107+ modifier = Modifier .align(Alignment .Center ).padding(16 .dp)
108+ )
109+ } else {
110+ A2UISurface (
111+ surfaceId = " contact-card" ,
112+ state = surfaceState,
113+ onUserAction = { action, src, contextMap ->
114+ Log .d(" A2UI" , " Action: ${action.name} from $src " )
115+
116+ val actionData = kotlinx.serialization.json.JsonObject (contextMap)
117+
118+ scope.launch(Dispatchers .IO ) {
119+ try {
120+ val updates = a2aClient.sendEvent(actionData)
121+ withContext(Dispatchers .Main ) {
122+ updates.forEach { surfaceState.applyUpdate(it) }
123+ }
124+ } catch (e: Exception ) {
125+ e.printStackTrace()
126+ }
99127 }
100- } catch (e: Exception ) {
101- e.printStackTrace()
102128 }
103- }
129+ )
104130 }
105- )
131+ }
106132 }
107133}
0 commit comments