Skip to content

Commit b8409fc

Browse files
feat(vertexai): enable edge-to-edge (#2661)
* feat: enable edge-to-edge * refactor: fix padding in ChatScreen * refactor: reuse chat ui for function calling Co-authored-by: Marina Coelho <marinacoelho16@gmail.com>
1 parent 0a8c6ff commit b8409fc

6 files changed

Lines changed: 85 additions & 283 deletions

File tree

vertexai/app/src/main/kotlin/com/google/firebase/quickstart/vertexai/MainActivity.kt

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,12 @@ package com.google.firebase.quickstart.vertexai
1919
import android.os.Bundle
2020
import androidx.activity.ComponentActivity
2121
import androidx.activity.compose.setContent
22+
import androidx.activity.enableEdgeToEdge
2223
import androidx.compose.foundation.layout.fillMaxSize
24+
import androidx.compose.foundation.layout.navigationBarsPadding
25+
import androidx.compose.foundation.layout.padding
2326
import androidx.compose.material3.MaterialTheme
27+
import androidx.compose.material3.Scaffold
2428
import androidx.compose.material3.Surface
2529
import androidx.compose.ui.Modifier
2630
import androidx.navigation.compose.NavHost
@@ -38,39 +42,49 @@ class MainActivity : ComponentActivity() {
3842

3943
override fun onCreate(savedInstanceState: Bundle?) {
4044
super.onCreate(savedInstanceState)
41-
45+
enableEdgeToEdge()
4246
setContent {
4347
GenerativeAISample {
4448
// A surface container using the 'background' color from the theme
4549
Surface(
4650
modifier = Modifier.fillMaxSize(),
4751
color = MaterialTheme.colorScheme.background
4852
) {
49-
val navController = rememberNavController()
53+
Scaffold(
54+
modifier = Modifier
55+
.fillMaxSize()
56+
.navigationBarsPadding(),
57+
) { innerPadding ->
58+
val navController = rememberNavController()
5059

51-
NavHost(navController = navController, startDestination = "menu") {
52-
composable("menu") {
53-
MenuScreen(onItemClicked = { routeId ->
54-
navController.navigate(routeId)
55-
})
56-
}
57-
composable("summarize") {
58-
SummarizeRoute()
59-
}
60-
composable("photo_reasoning") {
61-
PhotoReasoningRoute()
62-
}
63-
composable("chat") {
64-
ChatRoute()
65-
}
66-
composable("functions_chat") {
67-
FunctionsChatRoute()
68-
}
69-
composable("audio") {
70-
AudioRoute()
71-
}
72-
composable("images") {
73-
ImagenRoute()
60+
NavHost(
61+
modifier = Modifier.padding(innerPadding),
62+
navController = navController,
63+
startDestination = "menu"
64+
) {
65+
composable("menu") {
66+
MenuScreen(onItemClicked = { routeId ->
67+
navController.navigate(routeId)
68+
})
69+
}
70+
composable("summarize") {
71+
SummarizeRoute()
72+
}
73+
composable("photo_reasoning") {
74+
PhotoReasoningRoute()
75+
}
76+
composable("chat") {
77+
ChatRoute()
78+
}
79+
composable("functions_chat") {
80+
FunctionsChatRoute()
81+
}
82+
composable("audio") {
83+
AudioRoute()
84+
}
85+
composable("images") {
86+
ImagenRoute()
87+
}
7488
}
7589
}
7690
}

vertexai/app/src/main/kotlin/com/google/firebase/quickstart/vertexai/feature/chat/ChatScreen.kt

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package com.google.firebase.quickstart.vertexai.feature.chat
1818

19+
import androidx.compose.foundation.layout.Arrangement
20+
import androidx.compose.foundation.layout.Box
1921
import androidx.compose.foundation.layout.BoxWithConstraints
2022
import androidx.compose.foundation.layout.Column
2123
import androidx.compose.foundation.layout.Row
@@ -66,8 +68,19 @@ internal fun ChatRoute(
6668
val listState = rememberLazyListState()
6769
val coroutineScope = rememberCoroutineScope()
6870

69-
Scaffold(
70-
bottomBar = {
71+
Column(
72+
modifier = Modifier
73+
.fillMaxSize()
74+
) {
75+
ChatList(
76+
chatUiState.messages,
77+
listState,
78+
modifier = Modifier.fillMaxSize()
79+
.weight(0.5f)
80+
)
81+
Box(
82+
contentAlignment = Alignment.BottomCenter
83+
) {
7184
MessageInput(
7285
onSendMessage = { inputText ->
7386
chatViewModel.sendMessage(inputText)
@@ -79,26 +92,19 @@ internal fun ChatRoute(
7992
}
8093
)
8194
}
82-
) { paddingValues ->
83-
Column(
84-
modifier = Modifier
85-
.padding(paddingValues)
86-
.fillMaxSize()
87-
) {
88-
// Messages List
89-
ChatList(chatUiState.messages, listState)
90-
}
9195
}
9296
}
9397

9498
@Composable
9599
fun ChatList(
96100
chatMessages: List<ChatMessage>,
97-
listState: LazyListState
101+
listState: LazyListState,
102+
modifier: Modifier = Modifier
98103
) {
99104
LazyColumn(
100105
reverseLayout = true,
101-
state = listState
106+
state = listState,
107+
modifier = modifier
102108
) {
103109
items(chatMessages.reversed()) { message ->
104110
ChatBubbleItem(message)

vertexai/app/src/main/kotlin/com/google/firebase/quickstart/vertexai/feature/functioncalling/FunctionsChatMessage.kt

Lines changed: 0 additions & 30 deletions
This file was deleted.

vertexai/app/src/main/kotlin/com/google/firebase/quickstart/vertexai/feature/functioncalling/FunctionsChatScreen.kt

Lines changed: 17 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -16,46 +16,20 @@
1616

1717
package com.google.firebase.quickstart.vertexai.feature.functioncalling
1818

19-
import androidx.compose.foundation.layout.BoxWithConstraints
19+
import androidx.compose.foundation.layout.Box
2020
import androidx.compose.foundation.layout.Column
21-
import androidx.compose.foundation.layout.Row
2221
import androidx.compose.foundation.layout.fillMaxSize
23-
import androidx.compose.foundation.layout.fillMaxWidth
24-
import androidx.compose.foundation.layout.padding
25-
import androidx.compose.foundation.layout.widthIn
26-
import androidx.compose.foundation.lazy.LazyColumn
27-
import androidx.compose.foundation.lazy.LazyListState
28-
import androidx.compose.foundation.lazy.items
2922
import androidx.compose.foundation.lazy.rememberLazyListState
30-
import androidx.compose.foundation.shape.RoundedCornerShape
31-
import androidx.compose.foundation.text.KeyboardOptions
32-
import androidx.compose.material.icons.Icons
33-
import androidx.compose.material.icons.filled.Send
34-
import androidx.compose.material3.Card
35-
import androidx.compose.material3.CardDefaults
36-
import androidx.compose.material3.CircularProgressIndicator
37-
import androidx.compose.material3.ElevatedCard
38-
import androidx.compose.material3.Icon
39-
import androidx.compose.material3.IconButton
40-
import androidx.compose.material3.MaterialTheme
41-
import androidx.compose.material3.OutlinedTextField
42-
import androidx.compose.material3.Scaffold
43-
import androidx.compose.material3.Text
4423
import androidx.compose.runtime.Composable
4524
import androidx.compose.runtime.collectAsState
4625
import androidx.compose.runtime.getValue
47-
import androidx.compose.runtime.mutableStateOf
4826
import androidx.compose.runtime.rememberCoroutineScope
49-
import androidx.compose.runtime.saveable.rememberSaveable
50-
import androidx.compose.runtime.setValue
5127
import androidx.compose.ui.Alignment
5228
import androidx.compose.ui.Modifier
53-
import androidx.compose.ui.res.stringResource
54-
import androidx.compose.ui.text.input.KeyboardCapitalization
55-
import androidx.compose.ui.unit.dp
5629
import androidx.lifecycle.viewmodel.compose.viewModel
5730
import com.google.firebase.quickstart.vertexai.GenerativeViewModelFactory
58-
import com.google.firebase.quickstart.vertexai.R
31+
import com.google.firebase.quickstart.vertexai.feature.chat.ChatList
32+
import com.google.firebase.quickstart.vertexai.feature.chat.MessageInput
5933
import kotlinx.coroutines.launch
6034

6135
@Composable
@@ -66,9 +40,20 @@ internal fun FunctionsChatRoute(
6640
val listState = rememberLazyListState()
6741
val coroutineScope = rememberCoroutineScope()
6842

69-
Scaffold(
70-
bottomBar = {
71-
FunctionMessageInput(
43+
Column(
44+
modifier = Modifier
45+
.fillMaxSize()
46+
) {
47+
ChatList(
48+
chatUiState.messages,
49+
listState,
50+
modifier = Modifier.fillMaxSize()
51+
.weight(0.5f)
52+
)
53+
Box(
54+
contentAlignment = Alignment.BottomCenter
55+
) {
56+
MessageInput(
7257
onSendMessage = { inputText ->
7358
chatViewModel.sendMessage(inputText)
7459
},
@@ -79,141 +64,6 @@ internal fun FunctionsChatRoute(
7964
}
8065
)
8166
}
82-
) { paddingValues ->
83-
Column(
84-
modifier = Modifier
85-
.padding(paddingValues)
86-
.fillMaxSize()
87-
) {
88-
// Messages List
89-
FunctionChatList(chatUiState.messages, listState)
90-
}
91-
}
92-
}
93-
94-
@Composable
95-
fun FunctionChatList(
96-
chatMessages: List<FunctionsChatMessage>,
97-
listState: LazyListState
98-
) {
99-
LazyColumn(
100-
reverseLayout = true,
101-
state = listState
102-
) {
103-
items(chatMessages.reversed()) { message ->
104-
FunctionChatBubbleItem(message)
105-
}
106-
}
107-
}
108-
109-
@Composable
110-
fun FunctionChatBubbleItem(
111-
chatMessage: FunctionsChatMessage
112-
) {
113-
val isModelMessage = chatMessage.participant == Participant.MODEL ||
114-
chatMessage.participant == Participant.ERROR
115-
116-
val backgroundColor = when (chatMessage.participant) {
117-
Participant.MODEL -> MaterialTheme.colorScheme.primaryContainer
118-
Participant.USER -> MaterialTheme.colorScheme.tertiaryContainer
119-
Participant.ERROR -> MaterialTheme.colorScheme.errorContainer
120-
}
121-
122-
val bubbleShape = if (isModelMessage) {
123-
RoundedCornerShape(4.dp, 20.dp, 20.dp, 20.dp)
124-
} else {
125-
RoundedCornerShape(20.dp, 4.dp, 20.dp, 20.dp)
126-
}
127-
128-
val horizontalAlignment = if (isModelMessage) {
129-
Alignment.Start
130-
} else {
131-
Alignment.End
132-
}
133-
134-
Column(
135-
horizontalAlignment = horizontalAlignment,
136-
modifier = Modifier
137-
.padding(horizontal = 8.dp, vertical = 4.dp)
138-
.fillMaxWidth()
139-
) {
140-
Text(
141-
text = chatMessage.participant.name,
142-
style = MaterialTheme.typography.bodySmall,
143-
modifier = Modifier.padding(bottom = 4.dp)
144-
)
145-
Row {
146-
if (chatMessage.isPending) {
147-
CircularProgressIndicator(
148-
modifier = Modifier
149-
.align(Alignment.CenterVertically)
150-
.padding(all = 8.dp)
151-
)
152-
}
153-
BoxWithConstraints {
154-
Card(
155-
colors = CardDefaults.cardColors(containerColor = backgroundColor),
156-
shape = bubbleShape,
157-
modifier = Modifier.widthIn(0.dp, maxWidth * 0.9f)
158-
) {
159-
Text(
160-
text = chatMessage.text,
161-
modifier = Modifier.padding(16.dp)
162-
)
163-
}
164-
}
165-
}
16667
}
16768
}
16869

169-
@Composable
170-
fun FunctionMessageInput(
171-
onSendMessage: (String) -> Unit,
172-
resetScroll: () -> Unit = {}
173-
) {
174-
var userMessage by rememberSaveable { mutableStateOf("") }
175-
176-
ElevatedCard(
177-
modifier = Modifier
178-
.fillMaxWidth()
179-
) {
180-
Row(
181-
modifier = Modifier
182-
.padding(16.dp)
183-
.fillMaxWidth()
184-
) {
185-
OutlinedTextField(
186-
value = userMessage,
187-
label = { Text(stringResource(R.string.chat_label)) },
188-
onValueChange = { userMessage = it },
189-
keyboardOptions = KeyboardOptions(
190-
capitalization = KeyboardCapitalization.Sentences
191-
),
192-
modifier = Modifier
193-
.align(Alignment.CenterVertically)
194-
.fillMaxWidth()
195-
.weight(0.85f)
196-
)
197-
IconButton(
198-
onClick = {
199-
if (userMessage.isNotBlank()) {
200-
onSendMessage(userMessage)
201-
userMessage = ""
202-
resetScroll()
203-
}
204-
},
205-
modifier = Modifier
206-
.padding(start = 16.dp)
207-
.align(Alignment.CenterVertically)
208-
.fillMaxWidth()
209-
.weight(0.15f)
210-
) {
211-
Icon(
212-
Icons.Default.Send,
213-
contentDescription = stringResource(R.string.action_send),
214-
modifier = Modifier
215-
)
216-
}
217-
}
218-
}
219-
}

0 commit comments

Comments
 (0)