- 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
targetSdk = 35
versionCode = 1
versionName = "1.0.3"
versionName = "1.0.4"
// Needed for HuggingFace auth workflows.
manifestPlaceholders["appAuthRedirectScheme"] = "com.google.ai.edge.gallery.oauth"

View file

@ -16,6 +16,7 @@
package com.google.ai.edge.gallery
import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
@ -32,6 +33,11 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
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() } } }
}
}

View file

@ -49,6 +49,7 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
@ -183,14 +184,12 @@ fun MessageInputText(
}
}
Box(contentAlignment = Alignment.CenterStart) {
Column {
// A preview panel for the selected image.
if (pickedImages.isNotEmpty()) {
Row(
modifier =
Modifier.offset(x = 16.dp, y = (-80).dp)
.fillMaxWidth()
.horizontalScroll(rememberScrollState()),
Modifier.offset(x = 16.dp).fillMaxWidth().horizontalScroll(rememberScrollState()),
horizontalArrangement = Arrangement.spacedBy(16.dp),
) {
for (image in pickedImages) {
@ -223,176 +222,178 @@ fun MessageInputText(
}
}
// A plus button to show a popup menu to add stuff to the chat.
IconButton(
enabled = !inProgress && !isResettingSession,
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 },
Box(contentAlignment = Alignment.CenterStart) {
// A plus button to show a popup menu to add stuff to the chat.
IconButton(
enabled = !inProgress && !isResettingSession,
onClick = { showAddContentMenu = true },
modifier = Modifier.offset(x = 16.dp).alpha(0.8f),
) {
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
}
// 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
},
)
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) {
// 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.
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)) },
)
// Otherwise, ask for permission
else -> {
takePicturePermissionLauncher.launch(Manifest.permission.CAMERA)
}
}
},
)
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) {
if (!modelInitializing && !modelPreparing) {
// 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
},
)
}
// 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(
onClick = onStopButtonClicked,
enabled = !inProgress && !isResettingSession,
onClick = {
onSendMessage(
createMessagesToSend(pickedImages = pickedImages, text = curMessage.trim())
)
pickedImages = listOf()
},
colors =
IconButtonDefaults.iconButtonColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer
),
) {
Icon(
Icons.Rounded.Stop,
Icons.AutoMirrored.Rounded.Send,
contentDescription = "",
modifier = Modifier.offset(x = 2.dp),
tint = MaterialTheme.colorScheme.primary,
)
}
}
} // Send button. Only shown when text is not empty.
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))
}
Spacer(modifier = Modifier.width(4.dp))
}
}

View file

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