16
16
package com.android.ai.samples.geminichatbot
17
17
18
18
import android.content.Intent
19
- import android.net.Uri
20
19
import androidx.compose.foundation.layout.Arrangement
21
20
import androidx.compose.foundation.layout.Column
22
21
import androidx.compose.foundation.layout.PaddingValues
@@ -32,7 +31,9 @@ import androidx.compose.foundation.lazy.LazyColumn
32
31
import androidx.compose.foundation.lazy.items
33
32
import androidx.compose.material.icons.Icons
34
33
import androidx.compose.material.icons.filled.Code
34
+ import androidx.compose.material3.AlertDialog
35
35
import androidx.compose.material3.Button
36
+ import androidx.compose.material3.CircularProgressIndicator
36
37
import androidx.compose.material3.ExperimentalMaterial3Api
37
38
import androidx.compose.material3.Icon
38
39
import androidx.compose.material3.MaterialTheme
@@ -44,7 +45,6 @@ import androidx.compose.material3.TopAppBarDefaults
44
45
import androidx.compose.material3.TopAppBarDefaults.topAppBarColors
45
46
import androidx.compose.material3.rememberTopAppBarState
46
47
import androidx.compose.runtime.Composable
47
- import androidx.compose.runtime.collectAsState
48
48
import androidx.compose.runtime.getValue
49
49
import androidx.compose.runtime.mutableStateOf
50
50
import androidx.compose.runtime.saveable.rememberSaveable
@@ -59,14 +59,16 @@ import androidx.compose.ui.unit.Dp
59
59
import androidx.compose.ui.unit.LayoutDirection
60
60
import androidx.compose.ui.unit.dp
61
61
import androidx.compose.ui.unit.sp
62
+ import androidx.core.net.toUri
62
63
import androidx.hilt.navigation.compose.hiltViewModel
64
+ import androidx.lifecycle.compose.collectAsStateWithLifecycle
63
65
64
66
@OptIn(ExperimentalMaterial3Api ::class )
65
67
@Composable
66
68
fun GeminiChatbotScreen (viewModel : GeminiChatbotViewModel = hiltViewModel()) {
67
69
val topAppBarState = rememberTopAppBarState()
68
70
val scrollBehavior = TopAppBarDefaults .pinnedScrollBehavior(topAppBarState)
69
- val messages by viewModel.messageList.collectAsState ()
71
+ val uiState by viewModel.uiState.collectAsStateWithLifecycle ()
70
72
var message by rememberSaveable { mutableStateOf(" " ) }
71
73
72
74
Scaffold (
@@ -88,15 +90,50 @@ fun GeminiChatbotScreen(viewModel: GeminiChatbotViewModel = hiltViewModel()) {
88
90
)
89
91
},
90
92
) { innerPadding ->
93
+
91
94
Column {
92
95
val layoutDirection = LocalLayoutDirection .current
96
+
97
+ val messages = when (val state = uiState) {
98
+ is GeminiChatbotUiState .Initial -> emptyList()
99
+ is GeminiChatbotUiState .Generating -> state.messages
100
+ is GeminiChatbotUiState .Success -> state.messages
101
+ is GeminiChatbotUiState .Error -> state.messages
102
+ }
103
+
93
104
MessageList (
94
105
modifier = Modifier
95
106
.fillMaxWidth()
96
107
.weight(1f ),
97
- messages = messages.sortedBy { - it.timestamp },
108
+ messages = messages.sortedByDescending { it.timestamp },
98
109
contentPadding = innerPadding,
99
110
)
111
+
112
+ when (val state = uiState) {
113
+ is GeminiChatbotUiState .Generating -> {
114
+ CircularProgressIndicator (
115
+ modifier = Modifier
116
+ .padding(vertical = 8 .dp)
117
+ .align(Alignment .CenterHorizontally ),
118
+ )
119
+ }
120
+
121
+ is GeminiChatbotUiState .Error -> {
122
+ AlertDialog (
123
+ onDismissRequest = { viewModel.dismissError() },
124
+ title = { Text (text = stringResource(R .string.error)) },
125
+ text = { Text (text = state.errorMessage) },
126
+ confirmButton = {
127
+ Button (onClick = { viewModel.dismissError() }) {
128
+ Text (text = stringResource(R .string.dismiss_button))
129
+ }
130
+ },
131
+ )
132
+ }
133
+
134
+ else -> { /* No additional UI for Initial or Success states */ }
135
+ }
136
+
100
137
InputBar (
101
138
value = message,
102
139
placeholder = stringResource(R .string.geminichatbot_input_placeholder),
@@ -108,7 +145,7 @@ fun GeminiChatbotScreen(viewModel: GeminiChatbotViewModel = hiltViewModel()) {
108
145
message = " "
109
146
},
110
147
contentPadding = innerPadding.copy(layoutDirection, top = 0 .dp),
111
- sendEnabled = true ,
148
+ sendEnabled = uiState !is GeminiChatbotUiState . Generating ,
112
149
)
113
150
}
114
151
}
@@ -176,7 +213,7 @@ fun SeeCodeButton() {
176
213
177
214
Button (
178
215
onClick = {
179
- val intent = Intent (Intent .ACTION_VIEW , Uri .parse(githubLink ))
216
+ val intent = Intent (Intent .ACTION_VIEW , githubLink.toUri( ))
180
217
context.startActivity(intent)
181
218
},
182
219
modifier = Modifier .padding(end = 8 .dp),
0 commit comments