11package com.google.firebase.quickstart.ai.feature.media.imagen
22
3+ import android.net.Uri
4+ import android.provider.OpenableColumns
5+ import android.text.format.Formatter
6+ import androidx.activity.compose.rememberLauncherForActivityResult
7+ import androidx.activity.result.contract.ActivityResultContracts
38import androidx.compose.foundation.Image
9+ import androidx.compose.foundation.clickable
10+ import androidx.compose.foundation.layout.Arrangement
411import androidx.compose.foundation.layout.Box
512import androidx.compose.foundation.layout.Column
13+ import androidx.compose.foundation.layout.Row
614import androidx.compose.foundation.layout.fillMaxWidth
15+ import androidx.compose.foundation.layout.height
716import androidx.compose.foundation.layout.padding
817import androidx.compose.foundation.lazy.grid.GridCells
9- import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
18+ import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
1019import androidx.compose.foundation.lazy.grid.items
20+ import androidx.compose.foundation.rememberScrollState
21+ import androidx.compose.foundation.verticalScroll
1122import androidx.compose.material3.Card
1223import androidx.compose.material3.CardDefaults
1324import androidx.compose.material3.CircularProgressIndicator
25+ import androidx.compose.material3.DropdownMenu
26+ import androidx.compose.material3.DropdownMenuItem
1427import androidx.compose.material3.ElevatedCard
1528import androidx.compose.material3.MaterialTheme
1629import androidx.compose.material3.OutlinedTextField
1730import androidx.compose.material3.Text
1831import androidx.compose.material3.TextButton
1932import androidx.compose.runtime.Composable
2033import androidx.compose.runtime.getValue
34+ import androidx.compose.runtime.mutableIntStateOf
2135import androidx.compose.runtime.mutableStateOf
36+ import androidx.compose.runtime.remember
37+ import androidx.compose.runtime.rememberCoroutineScope
2238import androidx.compose.runtime.saveable.rememberSaveable
2339import androidx.compose.runtime.setValue
2440import androidx.compose.ui.Alignment
2541import androidx.compose.ui.Modifier
2642import androidx.compose.ui.graphics.asImageBitmap
43+ import androidx.compose.ui.platform.LocalContext
44+ import androidx.compose.ui.res.painterResource
2745import androidx.compose.ui.unit.dp
2846import androidx.lifecycle.compose.collectAsStateWithLifecycle
2947import androidx.lifecycle.viewmodel.compose.viewModel
48+ import com.google.firebase.quickstart.ai.R
49+ import com.google.firebase.quickstart.ai.feature.text.Attachment
50+ import com.google.firebase.quickstart.ai.feature.text.AttachmentsList
51+ import kotlinx.coroutines.launch
3052import kotlinx.serialization.Serializable
3153
3254@Serializable
@@ -40,9 +62,35 @@ fun ImagenScreen(
4062 val errorMessage by imagenViewModel.errorMessage.collectAsStateWithLifecycle()
4163 val isLoading by imagenViewModel.isLoading.collectAsStateWithLifecycle()
4264 val generatedImages by imagenViewModel.generatedBitmaps.collectAsStateWithLifecycle()
65+ val attachedImage by imagenViewModel.attachedImage.collectAsStateWithLifecycle()
66+ val context = LocalContext .current
67+ val contentResolver = context.contentResolver
68+ val scope = rememberCoroutineScope()
69+ val openDocument = rememberLauncherForActivityResult(ActivityResultContracts .OpenDocument ()) { optionalUri: Uri ? ->
70+ optionalUri?.let { uri ->
71+ var fileName: String? = null
72+ // Fetch file name and size
73+ contentResolver.query(uri, null , null , null , null )?.use { cursor ->
74+ val nameIndex = cursor.getColumnIndex(OpenableColumns .DISPLAY_NAME )
75+ val sizeIndex = cursor.getColumnIndex(OpenableColumns .SIZE )
76+ cursor.moveToFirst()
77+ val humanReadableSize = Formatter .formatShortFileSize(
78+ context, cursor.getLong(sizeIndex)
79+ )
80+ fileName = " ${cursor.getString(nameIndex)} ($humanReadableSize )"
81+ }
82+
83+ contentResolver.openInputStream(uri)?.use { stream ->
84+ val bytes = stream.readBytes()
85+ scope.launch {
86+ imagenViewModel.attachImage(bytes)
87+ }
88+ }
89+ }
90+ }
4391
4492 Column (
45- modifier = Modifier
93+ modifier = Modifier .verticalScroll(rememberScrollState())
4694 ) {
4795 ElevatedCard (
4896 modifier = Modifier
@@ -59,18 +107,49 @@ fun ImagenScreen(
59107 .padding(16 .dp)
60108 .fillMaxWidth()
61109 )
62- TextButton (
63- onClick = {
64- if (imagenPrompt.isNotBlank()) {
65- imagenViewModel.generateImages(imagenPrompt)
66- }
67- },
68- modifier = Modifier
69- .padding(end = 16 .dp, bottom = 16 .dp)
70- .align(Alignment .End )
71- ) {
72- Text (" Generate" )
110+ if (imagenViewModel.selectionOptions.isNotEmpty()) {
111+ DropDownMenu (imagenViewModel.selectionOptions) { imagenViewModel.selectOption(it) }
73112 }
113+ val attachmentsList = buildList {
114+ if (imagenViewModel.additionalImage != null ) {
115+ add(
116+ Attachment (
117+ imagenViewModel.imageLabels.getOrElse(0 ) { " " },
118+ imagenViewModel.additionalImage
119+ )
120+ )
121+ }
122+ if (attachedImage != null ) {
123+ add(Attachment (imagenViewModel.imageLabels.getOrElse(1 ) { " " }, attachedImage))
124+ }
125+ }
126+
127+ if (imagenViewModel.includeAttach && attachmentsList.isNotEmpty()) {
128+ AttachmentsList (attachmentsList)
129+ }
130+ Row () {
131+ if (imagenViewModel.includeAttach) {
132+ TextButton (
133+ onClick = {
134+ openDocument.launch(arrayOf(" image/*" ))
135+ },
136+ modifier = Modifier
137+ .padding(end = 16 .dp, bottom = 16 .dp)
138+ ) { Text (" Attach" ) }
139+ }
140+ TextButton (
141+ onClick = {
142+ if (imagenViewModel.allowEmptyPrompt || imagenPrompt.isNotBlank()) {
143+ imagenViewModel.generateImages(imagenPrompt)
144+ }
145+ },
146+ modifier = Modifier
147+ .padding(end = 16 .dp, bottom = 16 .dp)
148+ ) {
149+ Text (" Generate" )
150+ }
151+ }
152+
74153 }
75154
76155 if (isLoading) {
@@ -100,9 +179,11 @@ fun ImagenScreen(
100179 )
101180 }
102181 }
103- LazyVerticalGrid (
104- columns = GridCells .Fixed (2 ),
105- modifier = Modifier .padding(16 .dp)
182+ LazyHorizontalGrid (
183+ rows = GridCells .Fixed (2 ),
184+ modifier = Modifier
185+ .padding(16 .dp)
186+ .height(500 .dp)
106187 ) {
107188 items(generatedImages) { image ->
108189 Card (
@@ -120,3 +201,57 @@ fun ImagenScreen(
120201 }
121202 }
122203}
204+
205+ @Composable
206+ fun DropDownMenu (items : List <String >, onClick : (String ) -> Unit ) {
207+
208+ val isDropDownExpanded = remember {
209+ mutableStateOf(false )
210+ }
211+
212+ val itemPosition = remember {
213+ mutableIntStateOf(0 )
214+ }
215+
216+
217+ Column (
218+ horizontalAlignment = Alignment .Start ,
219+ verticalArrangement = Arrangement .Top ,
220+ modifier = Modifier .padding(horizontal = 10 .dp)
221+ ) {
222+
223+ Box {
224+ Row (
225+ horizontalArrangement = Arrangement .Start ,
226+ verticalAlignment = Alignment .Top ,
227+ modifier = Modifier .clickable {
228+ isDropDownExpanded.value = true
229+ }
230+ ) {
231+ Text (text = items[itemPosition.intValue])
232+ Image (
233+ painter = painterResource(id = R .drawable.round_arrow_drop_down_24),
234+ contentDescription = " Dropdown Icon"
235+ )
236+ }
237+ DropdownMenu (
238+ expanded = isDropDownExpanded.value,
239+ onDismissRequest = {
240+ isDropDownExpanded.value = false
241+ }) {
242+ items.forEachIndexed { index, item ->
243+ DropdownMenuItem (
244+ text = {
245+ Text (text = item)
246+ },
247+ onClick = {
248+ isDropDownExpanded.value = false
249+ itemPosition.intValue = index
250+ onClick(item)
251+ })
252+ }
253+ }
254+ }
255+
256+ }
257+ }
0 commit comments