mirror of
https://github.com/google-ai-edge/gallery.git
synced 2025-07-06 14:40:36 -04:00
- 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:
parent
f4006b35b0
commit
207cc50878
4 changed files with 165 additions and 158 deletions
|
@ -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"
|
||||||
|
|
|
@ -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() } } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue