- Update genai task and app version.

- Fix edge-to-edge on three-button nav bar (thanks https://github.com/Goooler!)
- Fix a layout issue where the added images are not properly positioned when text input grows in height.

PiperOrigin-RevId: 771175188
This commit is contained in:
Google AI Edge Gallery 2025-06-13 12:00:20 -07:00 committed by Copybara-Service
parent f4006b35b0
commit 207cc50878
4 changed files with 165 additions and 158 deletions

View file

@ -31,7 +31,7 @@ android {
minSdk = 26 minSdk = 26
targetSdk = 35 targetSdk = 35
versionCode = 1 versionCode = 1
versionName = "1.0.3" versionName = "1.0.4"
// Needed for HuggingFace auth workflows. // Needed for HuggingFace auth workflows.
manifestPlaceholders["appAuthRedirectScheme"] = "com.google.ai.edge.gallery.oauth" manifestPlaceholders["appAuthRedirectScheme"] = "com.google.ai.edge.gallery.oauth"

View file

@ -16,6 +16,7 @@
package com.google.ai.edge.gallery package com.google.ai.edge.gallery
import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
@ -32,6 +33,11 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Fix for three-button nav not properly going edge-to-edge.
// See: https://issuetracker.google.com/issues/298296168
window.isNavigationBarContrastEnforced = false
}
setContent { GalleryTheme { Surface(modifier = Modifier.fillMaxSize()) { GalleryApp() } } } setContent { GalleryTheme { Surface(modifier = Modifier.fillMaxSize()) { GalleryApp() } } }
} }
} }

View file

@ -49,6 +49,7 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@ -183,14 +184,12 @@ fun MessageInputText(
} }
} }
Box(contentAlignment = Alignment.CenterStart) { Column {
// A preview panel for the selected image. // A preview panel for the selected image.
if (pickedImages.isNotEmpty()) { if (pickedImages.isNotEmpty()) {
Row( Row(
modifier = modifier =
Modifier.offset(x = 16.dp, y = (-80).dp) Modifier.offset(x = 16.dp).fillMaxWidth().horizontalScroll(rememberScrollState()),
.fillMaxWidth()
.horizontalScroll(rememberScrollState()),
horizontalArrangement = Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp),
) { ) {
for (image in pickedImages) { for (image in pickedImages) {
@ -223,176 +222,178 @@ fun MessageInputText(
} }
} }
// A plus button to show a popup menu to add stuff to the chat. Box(contentAlignment = Alignment.CenterStart) {
IconButton( // A plus button to show a popup menu to add stuff to the chat.
enabled = !inProgress && !isResettingSession, IconButton(
onClick = { showAddContentMenu = true }, enabled = !inProgress && !isResettingSession,
modifier = Modifier.offset(x = 16.dp).alpha(0.8f), onClick = { showAddContentMenu = true },
) { modifier = Modifier.offset(x = 16.dp).alpha(0.8f),
Icon(Icons.Rounded.Add, contentDescription = "", modifier = Modifier.size(28.dp))
}
Row(
modifier =
Modifier.fillMaxWidth()
.padding(12.dp)
.border(1.dp, MaterialTheme.colorScheme.outlineVariant, RoundedCornerShape(28.dp)),
verticalAlignment = Alignment.CenterVertically,
) {
val enableAddImageMenuItems = (imageMessageCount + pickedImages.size) < MAX_IMAGE_COUNT
DropdownMenu(
expanded = showAddContentMenu,
onDismissRequest = { showAddContentMenu = false },
) { ) {
if (showImagePickerInMenu) { Icon(Icons.Rounded.Add, contentDescription = "", modifier = Modifier.size(28.dp))
// Take a picture.
DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp),
) {
Icon(Icons.Rounded.PhotoCamera, contentDescription = "")
Text("Take a picture")
}
},
enabled = enableAddImageMenuItems,
onClick = {
// Check permission
when (PackageManager.PERMISSION_GRANTED) {
// Already got permission. Call the lambda.
ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) -> {
showAddContentMenu = false
showCameraCaptureBottomSheet = true
}
// Otherwise, ask for permission
else -> {
takePicturePermissionLauncher.launch(Manifest.permission.CAMERA)
}
}
},
)
// Pick an image from album.
DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp),
) {
Icon(Icons.Rounded.Photo, contentDescription = "")
Text("Pick from album")
}
},
enabled = enableAddImageMenuItems,
onClick = {
// Launch the photo picker and let the user choose only images.
pickMedia.launch(
PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
)
showAddContentMenu = false
},
)
}
// Prompt templates.
if (showPromptTemplatesInMenu) {
DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp),
) {
Icon(Icons.Rounded.PostAdd, contentDescription = "")
Text("Prompt templates")
}
},
onClick = {
onOpenPromptTemplatesClicked()
showAddContentMenu = false
},
)
}
// Prompt history.
DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp),
) {
Icon(Icons.Rounded.History, contentDescription = "")
Text("Input history")
}
},
onClick = {
showAddContentMenu = false
showTextInputHistorySheet = true
},
)
} }
Row(
modifier =
Modifier.fillMaxWidth()
.padding(12.dp)
.border(1.dp, MaterialTheme.colorScheme.outlineVariant, RoundedCornerShape(28.dp)),
verticalAlignment = Alignment.CenterVertically,
) {
val enableAddImageMenuItems = (imageMessageCount + pickedImages.size) < MAX_IMAGE_COUNT
DropdownMenu(
expanded = showAddContentMenu,
onDismissRequest = { showAddContentMenu = false },
) {
if (showImagePickerInMenu) {
// Take a picture.
DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp),
) {
Icon(Icons.Rounded.PhotoCamera, contentDescription = "")
Text("Take a picture")
}
},
enabled = enableAddImageMenuItems,
onClick = {
// Check permission
when (PackageManager.PERMISSION_GRANTED) {
// Already got permission. Call the lambda.
ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) -> {
showAddContentMenu = false
showCameraCaptureBottomSheet = true
}
// Text field. // Otherwise, ask for permission
TextField( else -> {
value = curMessage, takePicturePermissionLauncher.launch(Manifest.permission.CAMERA)
minLines = 1, }
maxLines = 3, }
onValueChange = onValueChanged, },
colors = )
TextFieldDefaults.colors(
unfocusedContainerColor = Color.Transparent,
focusedContainerColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
disabledContainerColor = Color.Transparent,
),
textStyle = MaterialTheme.typography.bodyLarge,
modifier = Modifier.weight(1f).padding(start = 36.dp),
placeholder = { Text(stringResource(textFieldPlaceHolderRes)) },
)
Spacer(modifier = Modifier.width(8.dp)) // Pick an image from album.
DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp),
) {
Icon(Icons.Rounded.Photo, contentDescription = "")
Text("Pick from album")
}
},
enabled = enableAddImageMenuItems,
onClick = {
// Launch the photo picker and let the user choose only images.
pickMedia.launch(
PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
)
showAddContentMenu = false
},
)
}
if (inProgress && showStopButtonWhenInProgress) { // Prompt templates.
if (!modelInitializing && !modelPreparing) { if (showPromptTemplatesInMenu) {
DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp),
) {
Icon(Icons.Rounded.PostAdd, contentDescription = "")
Text("Prompt templates")
}
},
onClick = {
onOpenPromptTemplatesClicked()
showAddContentMenu = false
},
)
}
// Prompt history.
DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp),
) {
Icon(Icons.Rounded.History, contentDescription = "")
Text("Input history")
}
},
onClick = {
showAddContentMenu = false
showTextInputHistorySheet = true
},
)
}
// Text field.
TextField(
value = curMessage,
minLines = 1,
maxLines = 3,
onValueChange = onValueChanged,
colors =
TextFieldDefaults.colors(
unfocusedContainerColor = Color.Transparent,
focusedContainerColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
disabledContainerColor = Color.Transparent,
),
textStyle = MaterialTheme.typography.bodyLarge,
modifier = Modifier.weight(1f).padding(start = 36.dp),
placeholder = { Text(stringResource(textFieldPlaceHolderRes)) },
)
Spacer(modifier = Modifier.width(8.dp))
if (inProgress && showStopButtonWhenInProgress) {
if (!modelInitializing && !modelPreparing) {
IconButton(
onClick = onStopButtonClicked,
colors =
IconButtonDefaults.iconButtonColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer
),
) {
Icon(
Icons.Rounded.Stop,
contentDescription = "",
tint = MaterialTheme.colorScheme.primary,
)
}
}
} // Send button. Only shown when text is not empty.
else if (curMessage.isNotEmpty()) {
IconButton( IconButton(
onClick = onStopButtonClicked, enabled = !inProgress && !isResettingSession,
onClick = {
onSendMessage(
createMessagesToSend(pickedImages = pickedImages, text = curMessage.trim())
)
pickedImages = listOf()
},
colors = colors =
IconButtonDefaults.iconButtonColors( IconButtonDefaults.iconButtonColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer containerColor = MaterialTheme.colorScheme.secondaryContainer
), ),
) { ) {
Icon( Icon(
Icons.Rounded.Stop, Icons.AutoMirrored.Rounded.Send,
contentDescription = "", contentDescription = "",
modifier = Modifier.offset(x = 2.dp),
tint = MaterialTheme.colorScheme.primary, tint = MaterialTheme.colorScheme.primary,
) )
} }
} }
} // Send button. Only shown when text is not empty. Spacer(modifier = Modifier.width(4.dp))
else if (curMessage.isNotEmpty()) {
IconButton(
enabled = !inProgress && !isResettingSession,
onClick = {
onSendMessage(
createMessagesToSend(pickedImages = pickedImages, text = curMessage.trim())
)
pickedImages = listOf()
},
colors =
IconButtonDefaults.iconButtonColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer
),
) {
Icon(
Icons.AutoMirrored.Rounded.Send,
contentDescription = "",
modifier = Modifier.offset(x = 2.dp),
tint = MaterialTheme.colorScheme.primary,
)
}
} }
Spacer(modifier = Modifier.width(4.dp))
} }
} }

View file

@ -20,7 +20,7 @@ protobuf = "0.9.5"
protobufJavaLite = "4.26.1" protobufJavaLite = "4.26.1"
#noinspection GradleDependency #noinspection GradleDependency
mediapipeTasksText = "0.10.21" mediapipeTasksText = "0.10.21"
mediapipeTasksGenai = "0.10.24" mediapipeTasksGenai = "0.10.25"
mediapipeTasksImageGenerator = "0.10.21" mediapipeTasksImageGenerator = "0.10.21"
commonmark = "1.0.0-alpha02" commonmark = "1.0.0-alpha02"
richtext = "1.0.0-alpha02" richtext = "1.0.0-alpha02"