From 48a766a755ebf5028492d9210ddf7eef5be55230 Mon Sep 17 00:00:00 2001 From: Thomas Ezan Date: Thu, 2 Oct 2025 19:47:30 -0700 Subject: [PATCH 1/5] WIP --- .../samples/imagen-editing/build.gradle.kts | 1 + .../imagenediting/ui/ImagenEditingScreen.kt | 281 ++++++++++++++---- .../src/main/res/values/strings.xml | 4 +- 3 files changed, 228 insertions(+), 58 deletions(-) diff --git a/ai-catalog/samples/imagen-editing/build.gradle.kts b/ai-catalog/samples/imagen-editing/build.gradle.kts index 8191899e..bf26d7b5 100644 --- a/ai-catalog/samples/imagen-editing/build.gradle.kts +++ b/ai-catalog/samples/imagen-editing/build.gradle.kts @@ -70,6 +70,7 @@ dependencies { implementation(libs.hilt.navigation.compose) implementation(libs.androidx.runtime.livedata) implementation(libs.ui.tooling.preview) + implementation(project(":ui-component")) debugImplementation(libs.ui.tooling) ksp(libs.hilt.compiler) diff --git a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt index 65ea7dde..dbb054a2 100644 --- a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt +++ b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt @@ -16,26 +16,48 @@ package com.android.ai.samples.imagenediting.ui import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.util.Log import androidx.activity.compose.BackHandler +import androidx.activity.compose.LocalOnBackPressedDispatcherOwner import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.ime import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.windowInsetsBottomHeight import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.input.TextFieldState +import androidx.compose.foundation.text.input.rememberTextFieldState import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Pause +import androidx.compose.material.icons.filled.PlayArrow +import androidx.compose.material.icons.materialIcon import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ContainedLoadingIndicator import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -43,17 +65,29 @@ import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults.topAppBarColors import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.ImageShader +import androidx.compose.ui.graphics.ShaderBrush +import androidx.compose.ui.graphics.TileMode import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.platform.SoftwareKeyboardController +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.ai.samples.imagenediting.R +import com.android.ai.uicomponent.GenerateButton +import com.android.ai.uicomponent.SampleDetailTopAppBar +import com.android.ai.uicomponent.TextInput @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -62,11 +96,10 @@ fun ImagenEditingScreen(viewModel: ImagenEditingViewModel = hiltViewModel()) { val showMaskEditor: Boolean by viewModel.showMaskEditor.collectAsStateWithLifecycle() val bitmapForMasking: Bitmap? by viewModel.bitmapForMasking.collectAsStateWithLifecycle() - BackHandler(enabled = showMaskEditor) { - viewModel.onCancelMasking() - } +// BackHandler(enabled = showMaskEditor) { +// viewModel.onCancelMasking() +// } - Box(modifier = Modifier.fillMaxSize()) { ImagenEditingScreenContent( uiState = uiState, showMaskEditor = showMaskEditor, @@ -78,11 +111,10 @@ fun ImagenEditingScreen(viewModel: ImagenEditingViewModel = hiltViewModel()) { onCancelMasking = viewModel::onCancelMasking, modifier = Modifier.fillMaxSize(), ) - } } @Composable -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) private fun ImagenEditingScreenContent( uiState: ImagenEditingUIState, showMaskEditor: Boolean, @@ -95,64 +127,207 @@ private fun ImagenEditingScreenContent( modifier: Modifier = Modifier, ) { val isGenerating = uiState is ImagenEditingUIState.Loading + val backDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher Scaffold( - modifier = modifier, + containerColor = MaterialTheme.colorScheme.surface, topBar = { - TopAppBar( - colors = topAppBarColors( - containerColor = MaterialTheme.colorScheme.primaryContainer, - titleContentColor = MaterialTheme.colorScheme.primary, - ), - title = { - Text(text = stringResource(R.string.editing_title_image_generation_screen)) - }, + SampleDetailTopAppBar( + sampleName = stringResource(R.string.editing_title_image_generation_title), + sampleDescription = stringResource(R.string.editing_title_image_generation_subtitle), + sourceCodeUrl = "https://github.com/android/ai-samples/tree/main/ai-catalog/samples/imagen-editing", + onBackClick = { backDispatcher?.onBackPressed() }, ) }, + modifier = Modifier.fillMaxWidth(), ) { innerPadding -> - Column( - Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) + val context = LocalContext.current + val imageBitmap = remember { + val bitmap = BitmapFactory.decodeResource(context.resources, com.android.ai.uicomponent.R.drawable.img_fill) + bitmap.asImageBitmap() + } + val imageShader = remember { + ImageShader( + image = imageBitmap, + tileModeX = TileMode.Repeated, + tileModeY = TileMode.Repeated, + ) + } + + Box( + modifier = Modifier .padding(innerPadding) - .padding(16.dp) - .imePadding(), + .fillMaxSize(), + contentAlignment = Alignment.Center ) { - ImagenEditingGeneratedContent( - uiState = uiState, - showMaskEditor = showMaskEditor, - bitmapForMasking = bitmapForMasking, - onImageClick = { - if (uiState is ImagenEditingUIState.ImageGenerated) { - onImageToMaskClicked(it) + Box( + Modifier + .padding(16.dp) + .imePadding() + .widthIn(max = 440.dp) + .fillMaxHeight(0.85f) + .border( + 1.dp, + MaterialTheme.colorScheme.outline, + shape = RoundedCornerShape(40.dp), + ) + .clip(RoundedCornerShape(40.dp)) + .background(ShaderBrush(imageShader)), + contentAlignment = Alignment.Center, + ) { + val textFieldState = rememberTextFieldState() + val keyboardController = LocalSoftwareKeyboardController.current + + when (uiState) { + is ImagenEditingUIState.Initial -> { + Text( + text = "Generate an image to edit", + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier + .padding(24.dp) + .align(Alignment.Center), + ) + TextField(textFieldState, isGenerating, onGenerateClick, keyboardController) } - }, - onMaskFinalized = onImageMaskReady, - onCancelMasking = onCancelMasking, - modifier = Modifier - .fillMaxWidth() - .aspectRatio(1f), - ) - Spacer(modifier = Modifier.height(16.dp)) + is ImagenEditingUIState.Loading -> { + Box(modifier.fillMaxSize()) { + ContainedLoadingIndicator( + modifier = Modifier + .size(60.dp) + .align(Alignment.Center) + ) + } + TextField(textFieldState, isGenerating, onGenerateClick, keyboardController) + } - GenerationInput( - uiState = uiState, - onGenerateClick = onGenerateClick, - onInpaintClick = { prompt -> - if (uiState is ImagenEditingUIState.ImageMasked) { - onInpaintClick(uiState.originalBitmap, uiState.maskBitmap, prompt) + is ImagenEditingUIState.ImageGenerated -> { + ImagenEditingGeneratedContent( + uiState = uiState, + showMaskEditor = showMaskEditor, + bitmapForMasking = bitmapForMasking, + onImageClick = { + onImageToMaskClicked(it) + }, + onMaskFinalized = onImageMaskReady, + onCancelMasking = onCancelMasking, + modifier = Modifier + .fillMaxSize(), + ) + +// Image( +// bitmap = uiState.bitmap.asImageBitmap(), +// contentDescription = uiState.contentDescription, +// contentScale = ContentScale.Crop, +// modifier = Modifier.fillMaxSize() +// ) +// +// Row ( +// modifier = Modifier.align(Alignment.BottomEnd) +// .padding(8.dp) +// .background(MaterialTheme.colorScheme.background, +// shape = RoundedCornerShape(32.dp)) +// .padding(start = 8.dp, end = 8.dp, top = 12.dp, bottom = 12.dp) +// ) { +// Icon(painterResource(id = com.android.ai.uicomponent.R.drawable.ic_delete), +// contentDescription = null, +// modifier = Modifier.padding(10.dp)) +// Icon(painterResource(id = com.android.ai.uicomponent.R.drawable.ic_redo), +// contentDescription = null, +// modifier = Modifier.padding(10.dp)) +// Icon(imageVector = Icons.Default.Check, +// contentDescription = null, +// modifier = Modifier.padding(10.dp)) +// } } - }, - enabled = !isGenerating, - modifier = Modifier.fillMaxWidth(), - ) - Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime)) + is ImagenEditingUIState.ImageMasked -> { + Box(modifier = Modifier.fillMaxSize()) { + Image( + bitmap = uiState.originalBitmap.asImageBitmap(), + contentDescription = stringResource(R.string.editing_generated_image), + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Fit, + ) + Image( + bitmap = uiState.maskBitmap.asImageBitmap(), + contentDescription = stringResource(R.string.editing_generated_mask), + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Fit, + colorFilter = ColorFilter.tint(Color.Red.copy(alpha = 0.5f)), + ) + } + } + + else -> {} + } + +// ImagenEditingGeneratedContent( +// uiState = uiState, +// showMaskEditor = showMaskEditor, +// bitmapForMasking = bitmapForMasking, +// onImageClick = { +// if (uiState is ImagenEditingUIState.ImageGenerated) { +// onImageToMaskClicked(it) +// } +// }, +// onMaskFinalized = onImageMaskReady, +// onCancelMasking = onCancelMasking, +// modifier = Modifier +// .fillMaxWidth() +// .aspectRatio(1f), +// ) +// +// Spacer(modifier = Modifier.height(16.dp)) +// +// GenerationInput( +// uiState = uiState, +// onGenerateClick = onGenerateClick, +// onInpaintClick = { prompt -> +// if (uiState is ImagenEditingUIState.ImageMasked) { +// onInpaintClick(uiState.originalBitmap, uiState.maskBitmap, prompt) +// } +// }, +// enabled = !isGenerating, +// modifier = Modifier.fillMaxWidth(), +// ) + } } } } +@Composable +private fun BoxScope.TextField( + textFieldState: TextFieldState, + isGenerating: Boolean, + onGenerateClick: (String) -> Unit, + keyboardController: SoftwareKeyboardController?, +) { + TextInput( + state = textFieldState, + placeholder = "an oil painting of the San Francisco Ferry Building", + primaryButton = { + GenerateButton( + text = "", + icon = painterResource(id = com.android.ai.uicomponent.R.drawable.ic_ai_img), + modifier = Modifier + .width(72.dp) + .height(55.dp) + .padding(4.dp), + enabled = !isGenerating, + onClick = { + onGenerateClick(textFieldState.text.toString()) + keyboardController?.hide() + }, + ) + }, + modifier = Modifier + .widthIn(max = 646.dp) + .padding(start = 10.dp, end = 10.dp, bottom = 10.dp) + .align(Alignment.BottomCenter), + ) +} + @Composable fun ImagenEditingGeneratedContent( uiState: ImagenEditingUIState, @@ -178,17 +353,12 @@ fun ImagenEditingGeneratedContent( ) } else { when (uiState) { - is ImagenEditingUIState.Loading -> { - CircularProgressIndicator() - } - is ImagenEditingUIState.ImageGenerated -> { - Box(modifier = Modifier.fillMaxSize()) { Image( bitmap = uiState.bitmap.asImageBitmap(), - contentDescription = stringResource(R.string.editing_generated_image), - modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.Fit, + contentDescription = uiState.contentDescription, + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize() ) Button( onClick = { onImageClick(uiState.bitmap) }, @@ -198,7 +368,6 @@ fun ImagenEditingGeneratedContent( ) { Text(text = stringResource(R.string.editing_edit_mask_button)) } - } } is ImagenEditingUIState.ImageMasked -> { diff --git a/ai-catalog/samples/imagen-editing/src/main/res/values/strings.xml b/ai-catalog/samples/imagen-editing/src/main/res/values/strings.xml index cfa2181b..9945b10f 100644 --- a/ai-catalog/samples/imagen-editing/src/main/res/values/strings.xml +++ b/ai-catalog/samples/imagen-editing/src/main/res/values/strings.xml @@ -16,7 +16,6 @@ --> - See Code Generate Generating… Prompt @@ -29,7 +28,8 @@ Generate an image, then tap to draw a mask. An image of dog working as a chef An unknown error occurred. - Imagen Editing + Imagen Editing + Generate images with Imagen, Google image generation model. Image to be masked The generated image The generated mask From 7e64238b7ae25014543aeb34ba431ed340229c97 Mon Sep 17 00:00:00 2001 From: Thomas Ezan Date: Mon, 6 Oct 2025 09:05:36 -0700 Subject: [PATCH 2/5] WIP --- .../ui/ImagenEditingGeneratedContent.kt | 2 - .../ui/ImagenEditingMaskEditor.kt | 91 ++++-- .../imagenediting/ui/ImagenEditingScreen.kt | 284 +++++++----------- .../ui/ImagenEditingViewModel.kt | 6 +- .../src/main/res/values/strings.xml | 5 + .../com/android/ai/uicomponent/TextInput.kt | 4 +- 6 files changed, 185 insertions(+), 207 deletions(-) diff --git a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingGeneratedContent.kt b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingGeneratedContent.kt index 6f1aae15..9b9ec2fe 100644 --- a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingGeneratedContent.kt +++ b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingGeneratedContent.kt @@ -79,14 +79,12 @@ fun ImagenEditingGeneratedContent( ) currentDrawingPath = Path() pathVersion++ - bitmapToMask = null } ImagenEditingUIState.Loading -> { CircularProgressIndicator() currentDrawingPath = Path() pathVersion++ - bitmapToMask = null } is ImagenEditingUIState.ImageGenerated -> { diff --git a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingMaskEditor.kt b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingMaskEditor.kt index a172939a..34adb948 100644 --- a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingMaskEditor.kt +++ b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingMaskEditor.kt @@ -17,8 +17,11 @@ package com.android.ai.samples.imagenediting.ui import android.graphics.Bitmap import android.graphics.Paint +import android.graphics.drawable.Icon import androidx.compose.foundation.Canvas import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -27,7 +30,19 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.Undo +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.SmartToy +import androidx.compose.material.icons.filled.Undo import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -49,6 +64,7 @@ import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.drawscope.withTransform import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.core.graphics.createBitmap @@ -103,7 +119,7 @@ fun ImagenEditingMaskEditor(sourceBitmap: Bitmap, onMaskFinalized: (Bitmap) -> U bitmap = sourceBitmap.asImageBitmap(), contentDescription = stringResource(R.string.editing_image_to_mask), modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.Fit, + contentScale = ContentScale.Crop, ) Canvas(modifier = Modifier.fillMaxSize()) { val canvasWidth = size.width @@ -130,36 +146,51 @@ fun ImagenEditingMaskEditor(sourceBitmap: Bitmap, onMaskFinalized: (Bitmap) -> U } } } - } - Row( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp), - horizontalArrangement = Arrangement.SpaceEvenly, - ) { - Button(onClick = { if (paths.isNotEmpty()) paths.removeAt(paths.lastIndex) }, enabled = paths.isNotEmpty()) { - Text("Undo") - } - Button(onClick = onCancel) { - Text("Cancel") - } - Button( - onClick = { - val maskBitmap = createBitmap(sourceBitmap.width, sourceBitmap.height) - val canvas = android.graphics.Canvas(maskBitmap) - val paint = Paint().apply { - color = android.graphics.Color.WHITE - strokeWidth = 70f - style = Paint.Style.STROKE - strokeCap = Paint.Cap.ROUND - strokeJoin = Paint.Join.ROUND - isAntiAlias = true - } - paths.forEach { path -> canvas.drawPath(path.asAndroidPath(), paint) } - onMaskFinalized(maskBitmap) - }, + + Row( + modifier = Modifier + .padding(16.dp) + .align(Alignment.BottomCenter) + .background(color = MaterialTheme.colorScheme.surfaceContainer, shape = RoundedCornerShape(20.dp)), ) { - Text("Finalize Mask") + Icon( + Icons.Default.Delete, + contentDescription = stringResource(R.string.cancel_masking), + modifier = Modifier + .padding(10.dp) + .clickable(true) { + onCancel + }, + ) + Icon( + Icons.AutoMirrored.Filled.Undo, + contentDescription = stringResource(R.string.undo_the_mask), + modifier = Modifier + .padding(10.dp) + .clickable(true) { + if (paths.isNotEmpty()) paths.removeAt(paths.lastIndex) + }, + ) + Icon( + Icons.Default.Check, + contentDescription = stringResource(R.string.save_the_mask), + modifier = Modifier + .padding(10.dp) + .clickable(true) { + val maskBitmap = createBitmap(sourceBitmap.width, sourceBitmap.height) + val canvas = android.graphics.Canvas(maskBitmap) + val paint = Paint().apply { + color = android.graphics.Color.WHITE + strokeWidth = 70f + style = Paint.Style.STROKE + strokeCap = Paint.Cap.ROUND + strokeJoin = Paint.Join.ROUND + isAntiAlias = true + } + paths.forEach { path -> canvas.drawPath(path.asAndroidPath(), paint) } + onMaskFinalized(maskBitmap) + }, + ) } } } diff --git a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt index dbb054a2..bb2db438 100644 --- a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt +++ b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt @@ -17,52 +17,30 @@ package com.android.ai.samples.imagenediting.ui import android.graphics.Bitmap import android.graphics.BitmapFactory -import android.util.Log -import androidx.activity.compose.BackHandler import androidx.activity.compose.LocalOnBackPressedDispatcherOwner import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.border -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.ime import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.layout.windowInsetsBottomHeight -import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.foundation.text.input.rememberTextFieldState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Check -import androidx.compose.material.icons.filled.Pause -import androidx.compose.material.icons.filled.PlayArrow -import androidx.compose.material.icons.materialIcon -import androidx.compose.material3.Button -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ContainedLoadingIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults.topAppBarColors import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember @@ -96,17 +74,12 @@ fun ImagenEditingScreen(viewModel: ImagenEditingViewModel = hiltViewModel()) { val showMaskEditor: Boolean by viewModel.showMaskEditor.collectAsStateWithLifecycle() val bitmapForMasking: Bitmap? by viewModel.bitmapForMasking.collectAsStateWithLifecycle() -// BackHandler(enabled = showMaskEditor) { -// viewModel.onCancelMasking() -// } - ImagenEditingScreenContent( uiState = uiState, showMaskEditor = showMaskEditor, bitmapForMasking = bitmapForMasking, onGenerateClick = viewModel::generateImage, onInpaintClick = { source, mask, prompt -> viewModel.inpaintImage(source, mask, prompt) }, - onImageToMaskClicked = { bitmap -> viewModel.onStartMasking(bitmap) }, onImageMaskReady = { source, mask -> viewModel.onImageMaskReady(source, mask) }, onCancelMasking = viewModel::onCancelMasking, modifier = Modifier.fillMaxSize(), @@ -121,7 +94,6 @@ private fun ImagenEditingScreenContent( bitmapForMasking: Bitmap?, onGenerateClick: (String) -> Unit, onInpaintClick: (source: Bitmap, mask: Bitmap, prompt: String) -> Unit, - onImageToMaskClicked: (Bitmap) -> Unit, onImageMaskReady: (source: Bitmap, mask: Bitmap) -> Unit, onCancelMasking: () -> Unit, modifier: Modifier = Modifier, @@ -181,13 +153,19 @@ private fun ImagenEditingScreenContent( when (uiState) { is ImagenEditingUIState.Initial -> { Text( - text = "Generate an image to edit", + text = stringResource(R.string.generate_an_image_to_edit), style = MaterialTheme.typography.bodyMedium, modifier = Modifier .padding(24.dp) .align(Alignment.Center), ) - TextField(textFieldState, isGenerating, onGenerateClick, keyboardController) + TextField( + textFieldState, + isGenerating, + onGenerateClick, + keyboardController, + placeholder = stringResource(R.string.describe_the_image_to_generate), + ) } is ImagenEditingUIState.Loading -> { @@ -198,47 +176,32 @@ private fun ImagenEditingScreenContent( .align(Alignment.Center) ) } - TextField(textFieldState, isGenerating, onGenerateClick, keyboardController) } is ImagenEditingUIState.ImageGenerated -> { - ImagenEditingGeneratedContent( - uiState = uiState, - showMaskEditor = showMaskEditor, - bitmapForMasking = bitmapForMasking, - onImageClick = { - onImageToMaskClicked(it) - }, - onMaskFinalized = onImageMaskReady, - onCancelMasking = onCancelMasking, - modifier = Modifier - .fillMaxSize(), - ) - -// Image( -// bitmap = uiState.bitmap.asImageBitmap(), -// contentDescription = uiState.contentDescription, -// contentScale = ContentScale.Crop, -// modifier = Modifier.fillMaxSize() -// ) -// -// Row ( -// modifier = Modifier.align(Alignment.BottomEnd) -// .padding(8.dp) -// .background(MaterialTheme.colorScheme.background, -// shape = RoundedCornerShape(32.dp)) -// .padding(start = 8.dp, end = 8.dp, top = 12.dp, bottom = 12.dp) -// ) { -// Icon(painterResource(id = com.android.ai.uicomponent.R.drawable.ic_delete), -// contentDescription = null, -// modifier = Modifier.padding(10.dp)) -// Icon(painterResource(id = com.android.ai.uicomponent.R.drawable.ic_redo), -// contentDescription = null, -// modifier = Modifier.padding(10.dp)) -// Icon(imageVector = Icons.Default.Check, -// contentDescription = null, -// modifier = Modifier.padding(10.dp)) -// } + if (showMaskEditor && bitmapForMasking != null) { + ImagenEditingMaskEditor( + sourceBitmap = bitmapForMasking, + onMaskFinalized = { maskBitmap -> + onImageMaskReady(bitmapForMasking, maskBitmap) + }, + onCancel = onCancelMasking, + modifier = Modifier.fillMaxSize(), + ) + } else { + Image( + bitmap = uiState.bitmap.asImageBitmap(), + contentDescription = uiState.contentDescription, + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize() + ) + TextField( + textFieldState, + isGenerating, + onGenerateClick, + keyboardController, + placeholder = "describe the image to in-paint") + } } is ImagenEditingUIState.ImageMasked -> { @@ -247,50 +210,26 @@ private fun ImagenEditingScreenContent( bitmap = uiState.originalBitmap.asImageBitmap(), contentDescription = stringResource(R.string.editing_generated_image), modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.Fit, + contentScale = ContentScale.Crop, ) Image( bitmap = uiState.maskBitmap.asImageBitmap(), contentDescription = stringResource(R.string.editing_generated_mask), modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.Fit, + contentScale = ContentScale.Crop, colorFilter = ColorFilter.tint(Color.Red.copy(alpha = 0.5f)), ) } + + TextField( + textFieldState = textFieldState, + isGenerating = isGenerating, + onGenerateClick = { prompt -> onInpaintClick(uiState.originalBitmap, uiState.maskBitmap, prompt) }, + keyboardController) } else -> {} } - -// ImagenEditingGeneratedContent( -// uiState = uiState, -// showMaskEditor = showMaskEditor, -// bitmapForMasking = bitmapForMasking, -// onImageClick = { -// if (uiState is ImagenEditingUIState.ImageGenerated) { -// onImageToMaskClicked(it) -// } -// }, -// onMaskFinalized = onImageMaskReady, -// onCancelMasking = onCancelMasking, -// modifier = Modifier -// .fillMaxWidth() -// .aspectRatio(1f), -// ) -// -// Spacer(modifier = Modifier.height(16.dp)) -// -// GenerationInput( -// uiState = uiState, -// onGenerateClick = onGenerateClick, -// onInpaintClick = { prompt -> -// if (uiState is ImagenEditingUIState.ImageMasked) { -// onInpaintClick(uiState.originalBitmap, uiState.maskBitmap, prompt) -// } -// }, -// enabled = !isGenerating, -// modifier = Modifier.fillMaxWidth(), -// ) } } } @@ -302,10 +241,11 @@ private fun BoxScope.TextField( isGenerating: Boolean, onGenerateClick: (String) -> Unit, keyboardController: SoftwareKeyboardController?, + placeholder: String = "", ) { TextInput( state = textFieldState, - placeholder = "an oil painting of the San Francisco Ferry Building", + placeholder = placeholder, primaryButton = { GenerateButton( text = "", @@ -328,74 +268,74 @@ private fun BoxScope.TextField( ) } -@Composable -fun ImagenEditingGeneratedContent( - uiState: ImagenEditingUIState, - showMaskEditor: Boolean, - bitmapForMasking: Bitmap?, - onImageClick: (Bitmap) -> Unit, - onMaskFinalized: (source: Bitmap, mask: Bitmap) -> Unit, - onCancelMasking: () -> Unit, - modifier: Modifier = Modifier, -) { - Box( - modifier = modifier.background(MaterialTheme.colorScheme.surfaceVariant), - contentAlignment = Alignment.Center, - ) { - if (showMaskEditor && bitmapForMasking != null) { - ImagenEditingMaskEditor( - sourceBitmap = bitmapForMasking, - onMaskFinalized = { maskBitmap -> - onMaskFinalized(bitmapForMasking, maskBitmap) - }, - onCancel = onCancelMasking, - modifier = Modifier.fillMaxSize(), - ) - } else { - when (uiState) { - is ImagenEditingUIState.ImageGenerated -> { - Image( - bitmap = uiState.bitmap.asImageBitmap(), - contentDescription = uiState.contentDescription, - contentScale = ContentScale.Crop, - modifier = Modifier.fillMaxSize() - ) - Button( - onClick = { onImageClick(uiState.bitmap) }, - modifier = Modifier - .align(Alignment.BottomCenter) - .padding(16.dp), - ) { - Text(text = stringResource(R.string.editing_edit_mask_button)) - } - } - - is ImagenEditingUIState.ImageMasked -> { - Box(modifier = Modifier.fillMaxSize()) { - Image( - bitmap = uiState.originalBitmap.asImageBitmap(), - contentDescription = stringResource(R.string.editing_generated_image), - modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.Fit, - ) - Image( - bitmap = uiState.maskBitmap.asImageBitmap(), - contentDescription = stringResource(R.string.editing_generated_mask), - modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.Fit, - colorFilter = ColorFilter.tint(Color.Red.copy(alpha = 0.5f)), - ) - } - } - - is ImagenEditingUIState.Error -> { - uiState.message?.let { Text(text = it) } - } - - else -> { - Text(text = stringResource(R.string.editing_placeholder_prompt)) - } - } - } - } -} +//@Composable +//fun ImagenEditingGeneratedContent( +// uiState: ImagenEditingUIState, +// showMaskEditor: Boolean, +// bitmapForMasking: Bitmap?, +// onImageClick: (Bitmap) -> Unit, +// onMaskFinalized: (source: Bitmap, mask: Bitmap) -> Unit, +// onCancelMasking: () -> Unit, +// modifier: Modifier = Modifier, +//) { +// Box( +// modifier = modifier.background(MaterialTheme.colorScheme.surfaceVariant), +// contentAlignment = Alignment.Center, +// ) { +// if (showMaskEditor && bitmapForMasking != null) { +// ImagenEditingMaskEditor( +// sourceBitmap = bitmapForMasking, +// onMaskFinalized = { maskBitmap -> +// onMaskFinalized(bitmapForMasking, maskBitmap) +// }, +// onCancel = onCancelMasking, +// modifier = Modifier.fillMaxSize(), +// ) +// } else { +// when (uiState) { +// is ImagenEditingUIState.ImageGenerated -> { +// Image( +// bitmap = uiState.bitmap.asImageBitmap(), +// contentDescription = uiState.contentDescription, +// contentScale = ContentScale.Inside, +// modifier = Modifier.fillMaxSize() +// ) +// Button( +// onClick = { onImageClick(uiState.bitmap) }, +// modifier = Modifier +// .align(Alignment.BottomCenter) +// .padding(16.dp), +// ) { +// Text(text = stringResource(R.string.editing_edit_mask_button)) +// } +// } +// +// is ImagenEditingUIState.ImageMasked -> { +// Box(modifier = Modifier.fillMaxSize()) { +// Image( +// bitmap = uiState.originalBitmap.asImageBitmap(), +// contentDescription = stringResource(R.string.editing_generated_image), +// modifier = Modifier.fillMaxSize(), +// contentScale = ContentScale.Fit, +// ) +// Image( +// bitmap = uiState.maskBitmap.asImageBitmap(), +// contentDescription = stringResource(R.string.editing_generated_mask), +// modifier = Modifier.fillMaxSize(), +// contentScale = ContentScale.Fit, +// colorFilter = ColorFilter.tint(Color.Red.copy(alpha = 0.5f)), +// ) +// } +// } +// +// is ImagenEditingUIState.Error -> { +// uiState.message?.let { Text(text = it) } +// } +// +// else -> { +// Text(text = stringResource(R.string.editing_placeholder_prompt)) +// } +// } +// } +// } +//} diff --git a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingViewModel.kt b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingViewModel.kt index f259b160..0e8e31f5 100644 --- a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingViewModel.kt +++ b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingViewModel.kt @@ -42,7 +42,11 @@ class ImagenEditingViewModel @Inject constructor(private val imagenDataSource: I viewModelScope.launch { try { val bitmap = imagenDataSource.generateImage(prompt) + + _bitmapForMasking.value = bitmap + _showMaskEditor.value = true _uiState.value = ImagenEditingUIState.ImageGenerated(bitmap, contentDescription = prompt) + } catch (e: Exception) { _uiState.value = ImagenEditingUIState.Error(e.message) } @@ -69,7 +73,7 @@ class ImagenEditingViewModel @Inject constructor(private val imagenDataSource: I } } - fun onStartMasking(bitmap: Bitmap) { + private fun onStartMasking(bitmap: Bitmap) { _bitmapForMasking.value = bitmap _showMaskEditor.value = true } diff --git a/ai-catalog/samples/imagen-editing/src/main/res/values/strings.xml b/ai-catalog/samples/imagen-editing/src/main/res/values/strings.xml index 9945b10f..6c0bcdf1 100644 --- a/ai-catalog/samples/imagen-editing/src/main/res/values/strings.xml +++ b/ai-catalog/samples/imagen-editing/src/main/res/values/strings.xml @@ -34,4 +34,9 @@ The generated image The generated mask Draw a mask + Cancel masking + Undo the mask + Save the mask + describe the image to generate + Generate an image to edit \ No newline at end of file diff --git a/ai-catalog/ui-component/src/main/java/com/android/ai/uicomponent/TextInput.kt b/ai-catalog/ui-component/src/main/java/com/android/ai/uicomponent/TextInput.kt index 1ee4788a..130ff3d0 100644 --- a/ai-catalog/ui-component/src/main/java/com/android/ai/uicomponent/TextInput.kt +++ b/ai-catalog/ui-component/src/main/java/com/android/ai/uicomponent/TextInput.kt @@ -73,8 +73,8 @@ fun TextInput( placeholder = { Text( text = placeholder, - maxLines = 1, - overflow = TextOverflow.Ellipsis, + maxLines = 2, + style = MaterialTheme.typography.bodyMedium, ) }, lineLimits = TextFieldLineLimits.MultiLine(maxHeightInLines = maxLines), From 902a595bd359c4ff81b1aae757b0af11349663eb Mon Sep 17 00:00:00 2001 From: Thomas Ezan Date: Mon, 6 Oct 2025 11:48:38 -0700 Subject: [PATCH 3/5] WIP --- .../ai/catalog/domain/SampleCatalog.kt | 2 +- .../ui/ImagenEditingMaskEditor.kt | 4 +- .../imagenediting/ui/ImagenEditingScreen.kt | 95 ++++--------------- .../ui/ImagenEditingViewModel.kt | 3 + .../src/main/res/values/strings.xml | 1 + .../com/android/ai/uicomponent/TextInput.kt | 6 +- 6 files changed, 29 insertions(+), 82 deletions(-) diff --git a/ai-catalog/app/src/main/java/com/android/ai/catalog/domain/SampleCatalog.kt b/ai-catalog/app/src/main/java/com/android/ai/catalog/domain/SampleCatalog.kt index 8ee92cdd..0be222a8 100644 --- a/ai-catalog/app/src/main/java/com/android/ai/catalog/domain/SampleCatalog.kt +++ b/ai-catalog/app/src/main/java/com/android/ai/catalog/domain/SampleCatalog.kt @@ -103,7 +103,7 @@ val sampleCatalog = listOf( description = R.string.imagen_editing_sample_description, route = "ImagenMaskEditing", sampleEntryScreen = { ImagenEditingScreen() }, - tags = listOf(SampleTags.IMAGEN, SampleTags.FIREBASE, SampleTags.MEDIA3), + tags = listOf(SampleTags.IMAGEN, SampleTags.FIREBASE), needsFirebase = true, keyArt = R.drawable.img_keyart_imagen, ), diff --git a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingMaskEditor.kt b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingMaskEditor.kt index 34adb948..a20217b2 100644 --- a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingMaskEditor.kt +++ b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingMaskEditor.kt @@ -150,7 +150,7 @@ fun ImagenEditingMaskEditor(sourceBitmap: Bitmap, onMaskFinalized: (Bitmap) -> U Row( modifier = Modifier .padding(16.dp) - .align(Alignment.BottomCenter) + .align(Alignment.BottomEnd) .background(color = MaterialTheme.colorScheme.surfaceContainer, shape = RoundedCornerShape(20.dp)), ) { Icon( @@ -159,7 +159,7 @@ fun ImagenEditingMaskEditor(sourceBitmap: Bitmap, onMaskFinalized: (Bitmap) -> U modifier = Modifier .padding(10.dp) .clickable(true) { - onCancel + onCancel() }, ) Icon( diff --git a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt index bb2db438..c31dd6cd 100644 --- a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt +++ b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt @@ -34,6 +34,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.input.TextFieldState +import androidx.compose.foundation.text.input.clearText import androidx.compose.foundation.text.input.rememberTextFieldState import androidx.compose.material3.ContainedLoadingIndicator import androidx.compose.material3.ExperimentalMaterial3Api @@ -180,15 +181,29 @@ private fun ImagenEditingScreenContent( is ImagenEditingUIState.ImageGenerated -> { if (showMaskEditor && bitmapForMasking != null) { + textFieldState.clearText() + ImagenEditingMaskEditor( sourceBitmap = bitmapForMasking, onMaskFinalized = { maskBitmap -> + textFieldState.clearText() onImageMaskReady(bitmapForMasking, maskBitmap) }, - onCancel = onCancelMasking, + onCancel = { onCancelMasking() }, modifier = Modifier.fillMaxSize(), ) + + Text( + text = "Draw a mask on the image", + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier + .padding(24.dp) + .align(Alignment.TopCenter) + .background(color = MaterialTheme.colorScheme.surfaceContainer), + ) } else { + textFieldState.clearText() + Image( bitmap = uiState.bitmap.asImageBitmap(), contentDescription = uiState.contentDescription, @@ -200,7 +215,8 @@ private fun ImagenEditingScreenContent( isGenerating, onGenerateClick, keyboardController, - placeholder = "describe the image to in-paint") + placeholder = stringResource(R.string.describe_the_image_to_generate), + ) } } @@ -225,7 +241,8 @@ private fun ImagenEditingScreenContent( textFieldState = textFieldState, isGenerating = isGenerating, onGenerateClick = { prompt -> onInpaintClick(uiState.originalBitmap, uiState.maskBitmap, prompt) }, - keyboardController) + keyboardController, + placeholder = stringResource(R.string.describe_the_image_to_in_paint)) } else -> {} @@ -267,75 +284,3 @@ private fun BoxScope.TextField( .align(Alignment.BottomCenter), ) } - -//@Composable -//fun ImagenEditingGeneratedContent( -// uiState: ImagenEditingUIState, -// showMaskEditor: Boolean, -// bitmapForMasking: Bitmap?, -// onImageClick: (Bitmap) -> Unit, -// onMaskFinalized: (source: Bitmap, mask: Bitmap) -> Unit, -// onCancelMasking: () -> Unit, -// modifier: Modifier = Modifier, -//) { -// Box( -// modifier = modifier.background(MaterialTheme.colorScheme.surfaceVariant), -// contentAlignment = Alignment.Center, -// ) { -// if (showMaskEditor && bitmapForMasking != null) { -// ImagenEditingMaskEditor( -// sourceBitmap = bitmapForMasking, -// onMaskFinalized = { maskBitmap -> -// onMaskFinalized(bitmapForMasking, maskBitmap) -// }, -// onCancel = onCancelMasking, -// modifier = Modifier.fillMaxSize(), -// ) -// } else { -// when (uiState) { -// is ImagenEditingUIState.ImageGenerated -> { -// Image( -// bitmap = uiState.bitmap.asImageBitmap(), -// contentDescription = uiState.contentDescription, -// contentScale = ContentScale.Inside, -// modifier = Modifier.fillMaxSize() -// ) -// Button( -// onClick = { onImageClick(uiState.bitmap) }, -// modifier = Modifier -// .align(Alignment.BottomCenter) -// .padding(16.dp), -// ) { -// Text(text = stringResource(R.string.editing_edit_mask_button)) -// } -// } -// -// is ImagenEditingUIState.ImageMasked -> { -// Box(modifier = Modifier.fillMaxSize()) { -// Image( -// bitmap = uiState.originalBitmap.asImageBitmap(), -// contentDescription = stringResource(R.string.editing_generated_image), -// modifier = Modifier.fillMaxSize(), -// contentScale = ContentScale.Fit, -// ) -// Image( -// bitmap = uiState.maskBitmap.asImageBitmap(), -// contentDescription = stringResource(R.string.editing_generated_mask), -// modifier = Modifier.fillMaxSize(), -// contentScale = ContentScale.Fit, -// colorFilter = ColorFilter.tint(Color.Red.copy(alpha = 0.5f)), -// ) -// } -// } -// -// is ImagenEditingUIState.Error -> { -// uiState.message?.let { Text(text = it) } -// } -// -// else -> { -// Text(text = stringResource(R.string.editing_placeholder_prompt)) -// } -// } -// } -// } -//} diff --git a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingViewModel.kt b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingViewModel.kt index 0e8e31f5..7ee8878c 100644 --- a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingViewModel.kt +++ b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingViewModel.kt @@ -16,6 +16,7 @@ package com.android.ai.samples.imagenediting.ui import android.graphics.Bitmap +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.android.ai.samples.imagenediting.data.ImagenEditingDataSource @@ -90,7 +91,9 @@ class ImagenEditingViewModel @Inject constructor(private val imagenDataSource: I } fun onCancelMasking() { + Log.d("ImagenEditingViewModel", "onCancelMasking") _showMaskEditor.value = false _bitmapForMasking.value = null + _uiState.value = ImagenEditingUIState.Initial } } diff --git a/ai-catalog/samples/imagen-editing/src/main/res/values/strings.xml b/ai-catalog/samples/imagen-editing/src/main/res/values/strings.xml index 6c0bcdf1..3742da74 100644 --- a/ai-catalog/samples/imagen-editing/src/main/res/values/strings.xml +++ b/ai-catalog/samples/imagen-editing/src/main/res/values/strings.xml @@ -39,4 +39,5 @@ Save the mask describe the image to generate Generate an image to edit + describe the image to in-paint \ No newline at end of file diff --git a/ai-catalog/ui-component/src/main/java/com/android/ai/uicomponent/TextInput.kt b/ai-catalog/ui-component/src/main/java/com/android/ai/uicomponent/TextInput.kt index 130ff3d0..32f29a9a 100644 --- a/ai-catalog/ui-component/src/main/java/com/android/ai/uicomponent/TextInput.kt +++ b/ai-catalog/ui-component/src/main/java/com/android/ai/uicomponent/TextInput.kt @@ -21,12 +21,10 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.input.TextFieldLineLimits import androidx.compose.foundation.text.input.TextFieldState -import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextField @@ -37,7 +35,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.android.ai.theme.AISampleCatalogTheme @@ -74,7 +72,7 @@ fun TextInput( Text( text = placeholder, maxLines = 2, - style = MaterialTheme.typography.bodyMedium, + style = MaterialTheme.typography.bodyMedium.copy(fontStyle = FontStyle.Italic), ) }, lineLimits = TextFieldLineLimits.MultiLine(maxHeightInLines = maxLines), From 841fbffe92904d7f3f85799e874767e94758fa73 Mon Sep 17 00:00:00 2001 From: Thomas Ezan Date: Mon, 6 Oct 2025 13:51:53 -0700 Subject: [PATCH 4/5] code cleanup --- .../ui/ImagenEditingGeneratedContent.kt | 239 ------------------ .../ui/ImagenEditingGenerationInput.kt | 121 --------- .../ui/ImagenEditingMaskEditor.kt | 10 - .../imagenediting/ui/ImagenEditingScreen.kt | 41 +-- .../ui/ImagenEditingSeeCodeButton.kt | 0 5 files changed, 22 insertions(+), 389 deletions(-) delete mode 100644 ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingGeneratedContent.kt delete mode 100644 ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingGenerationInput.kt delete mode 100644 ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingSeeCodeButton.kt diff --git a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingGeneratedContent.kt b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingGeneratedContent.kt deleted file mode 100644 index 9b9ec2fe..00000000 --- a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingGeneratedContent.kt +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2025 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.ai.samples.imagenediting.ui - -import android.graphics.Bitmap -import android.graphics.Canvas as AndroidCanvas -import android.graphics.Paint as AndroidPaint -import androidx.compose.foundation.Canvas -import androidx.compose.foundation.Image -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.gestures.detectDragGestures -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.wrapContentSize -import androidx.compose.material3.Button -import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Path -import androidx.compose.ui.graphics.StrokeCap -import androidx.compose.ui.graphics.StrokeJoin -import androidx.compose.ui.graphics.asAndroidPath -import androidx.compose.ui.graphics.asImageBitmap -import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import androidx.core.graphics.createBitmap -import com.android.ai.samples.imagenediting.R - -@Composable -fun ImagenEditingGeneratedContent( - uiState: ImagenEditingUIState, - modifier: Modifier = Modifier, - onImageClick: (Bitmap) -> Unit = {}, - onMaskFinalized: (source: Bitmap, mask: Bitmap) -> Unit, -) { - var currentDrawingPath by remember { mutableStateOf(Path()) } - var pathVersion by remember { mutableIntStateOf(0) } - var bitmapToMask by remember { mutableStateOf(null) } - - Box( - modifier = modifier.border(1.dp, MaterialTheme.colorScheme.outlineVariant), - contentAlignment = Alignment.Center, - ) { - when (uiState) { - ImagenEditingUIState.Initial -> { - Text( - text = stringResource(R.string.editing_placeholder_prompt_entry), - style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(16.dp), - ) - currentDrawingPath = Path() - pathVersion++ - } - - ImagenEditingUIState.Loading -> { - CircularProgressIndicator() - currentDrawingPath = Path() - pathVersion++ - } - - is ImagenEditingUIState.ImageGenerated -> { - // Set the bitmap that can be masked - bitmapToMask = uiState.bitmap - Image( - bitmap = uiState.bitmap.asImageBitmap(), - contentDescription = uiState.contentDescription, - contentScale = ContentScale.Fit, - modifier = Modifier - .fillMaxSize() - .clickable { - currentDrawingPath = Path() - pathVersion++ - onImageClick(uiState.bitmap) - }, - ) - - DrawingCanvas( - currentDrawingPath = currentDrawingPath, - pathVersion = pathVersion, - onPathUpdate = { newPath, newVersion -> - currentDrawingPath = newPath - pathVersion = newVersion - }, - modifier = Modifier.fillMaxSize(), - ) - bitmapToMask?.let { currentSourceBitmap -> - Button( - onClick = { - val maskBitmap = createMaskBitmap( - currentSourceBitmap.width, - currentSourceBitmap.height, - currentDrawingPath, - ) - onMaskFinalized(currentSourceBitmap, maskBitmap) - // Optionally reset the path after finalizing - currentDrawingPath = Path() - pathVersion++ - }, - modifier = Modifier - .align(Alignment.BottomCenter) - .padding(16.dp), - enabled = !currentDrawingPath.isEmpty, - ) { - Text(stringResource(R.string.editing_finalize_mask_button)) - } - } - } - - is ImagenEditingUIState.ImageMasked -> { - bitmapToMask = null - - Box(modifier = Modifier.fillMaxSize()) { - Image( - bitmap = uiState.originalBitmap.asImageBitmap(), - contentDescription = uiState.contentDescription, - contentScale = ContentScale.Fit, - modifier = Modifier - .fillMaxSize() - .clickable { - bitmapToMask = uiState.originalBitmap - currentDrawingPath = Path() - pathVersion++ - onImageClick(uiState.originalBitmap) - }, - ) - Image( - bitmap = uiState.maskBitmap.asImageBitmap(), - contentDescription = "Mask Overlay", - contentScale = ContentScale.Fit, - modifier = Modifier - .fillMaxSize() - .graphicsLayer(alpha = 0.5f), - ) - } - } - - is ImagenEditingUIState.Error -> { - Text( - text = uiState.message ?: stringResource(R.string.editing_error_message_unknown), - modifier = Modifier - .fillMaxSize() - .wrapContentSize(Alignment.Center), - textAlign = TextAlign.Center, - ) - currentDrawingPath = Path() - pathVersion++ - bitmapToMask = null - } - } - } -} - -@Composable -private fun DrawingCanvas(currentDrawingPath: Path, pathVersion: Int, onPathUpdate: (Path, Int) -> Unit, modifier: Modifier = Modifier) { - var internalPath by remember(pathVersion) { mutableStateOf(currentDrawingPath) } - var internalVersion by remember { mutableIntStateOf(pathVersion) } - val pathToDraw = remember(internalVersion) { internalPath } - - Canvas( - modifier = modifier - .pointerInput(Unit) { - detectDragGestures( - onDragStart = { offset -> - internalPath = Path().apply { moveTo(offset.x, offset.y) } - internalVersion++ - onPathUpdate(internalPath, internalVersion) - }, - onDrag = { change, _ -> - internalPath.lineTo(change.position.x, change.position.y) - internalVersion++ - onPathUpdate(internalPath, internalVersion) - change.consume() - }, - ) - }, - ) { - if (!pathToDraw.isEmpty) { - drawPath( - path = pathToDraw, - color = Color.White.copy(alpha = 0.7f), - style = Stroke( - width = 40f, - cap = StrokeCap.Round, - join = StrokeJoin.Round, - ), - ) - } - } -} -private fun createMaskBitmap(width: Int, height: Int, composePath: Path?): Bitmap { - val maskBitmap = createBitmap(width, height) - val canvas = AndroidCanvas(maskBitmap) - canvas.drawColor(android.graphics.Color.BLACK) - - composePath?.let { - if (!it.isEmpty) { - val androidPath = it.asAndroidPath() - val paint = AndroidPaint().apply { - color = android.graphics.Color.WHITE // Drawn area is white in the mask - isAntiAlias = true - style = AndroidPaint.Style.STROKE - strokeWidth = 40f - strokeCap = AndroidPaint.Cap.ROUND - strokeJoin = AndroidPaint.Join.ROUND - } - canvas.drawPath(androidPath, paint) - } - } - return maskBitmap -} diff --git a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingGenerationInput.kt b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingGenerationInput.kt deleted file mode 100644 index 47bbc20d..00000000 --- a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingGenerationInput.kt +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2025 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -@file:Suppress("ktlint:standard:import-ordering") - -package com.android.ai.samples.imagenediting.ui - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.AutoFixHigh // Icon for Inpaint/Edit -import androidx.compose.material.icons.filled.SmartToy -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.Text -import androidx.compose.material3.TextField -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.unit.dp -import com.android.ai.samples.imagenediting.R - -@Composable -fun GenerationInput( - uiState: ImagenEditingUIState, - onGenerateClick: (String) -> Unit, - onInpaintClick: (prompt: String) -> Unit, - enabled: Boolean, - modifier: Modifier = Modifier, -) { - val placeholder = stringResource(R.string.editing_placeholder_prompt_entry) - var promptTextField by rememberSaveable { mutableStateOf(placeholder) } - - val canInpaint = uiState is ImagenEditingUIState.ImageMasked && enabled - - val canGenerate = uiState !is ImagenEditingUIState.ImageMasked && enabled - - Column( - verticalArrangement = Arrangement.spacedBy(8.dp), - modifier = modifier, - ) { - TextField( - value = promptTextField, - onValueChange = { promptTextField = it }, - label = { Text(stringResource(R.string.editing_prompt_label)) }, - modifier = Modifier.fillMaxWidth(), - enabled = enabled, - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Send), - keyboardActions = KeyboardActions( - onSend = { - if (uiState is ImagenEditingUIState.ImageMasked) { - if (canInpaint) onInpaintClick(promptTextField) - } else { - if (canGenerate) onGenerateClick(promptTextField) - } - }, - ), - ) - - if (uiState !is ImagenEditingUIState.ImageMasked) { - Button( - onClick = { - onGenerateClick(promptTextField) - }, - enabled = canGenerate, - contentPadding = ButtonDefaults.ButtonWithIconContentPadding, - modifier = Modifier.fillMaxWidth(), - ) { - Icon( - Icons.Default.SmartToy, - contentDescription = null, - modifier = Modifier.size(ButtonDefaults.IconSize), - ) - Spacer(Modifier.size(ButtonDefaults.IconSpacing)) - Text(text = stringResource(R.string.editing_generate_button)) - } - } - - if (uiState is ImagenEditingUIState.ImageMasked) { - Button( - onClick = { - onInpaintClick(promptTextField) - }, - enabled = canInpaint, - contentPadding = ButtonDefaults.ButtonWithIconContentPadding, - modifier = Modifier.fillMaxWidth(), - ) { - Icon( - Icons.Default.AutoFixHigh, // Using a different icon for inpainting - contentDescription = null, - modifier = Modifier.size(ButtonDefaults.IconSize), - ) - Spacer(Modifier.size(ButtonDefaults.IconSpacing)) - Text(text = stringResource(R.string.editing_inpaint_button)) - } - } - } -} diff --git a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingMaskEditor.kt b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingMaskEditor.kt index a20217b2..6d66f321 100644 --- a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingMaskEditor.kt +++ b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingMaskEditor.kt @@ -17,33 +17,24 @@ package com.android.ai.samples.imagenediting.ui import android.graphics.Bitmap import android.graphics.Paint -import android.graphics.drawable.Icon import androidx.compose.foundation.Canvas import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.detectDragGestures -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.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.Undo import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Delete -import androidx.compose.material.icons.filled.SmartToy -import androidx.compose.material.icons.filled.Undo -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf @@ -64,7 +55,6 @@ import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.drawscope.withTransform import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.core.graphics.createBitmap diff --git a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt index c31dd6cd..7f349097 100644 --- a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt +++ b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt @@ -75,16 +75,16 @@ fun ImagenEditingScreen(viewModel: ImagenEditingViewModel = hiltViewModel()) { val showMaskEditor: Boolean by viewModel.showMaskEditor.collectAsStateWithLifecycle() val bitmapForMasking: Bitmap? by viewModel.bitmapForMasking.collectAsStateWithLifecycle() - ImagenEditingScreenContent( - uiState = uiState, - showMaskEditor = showMaskEditor, - bitmapForMasking = bitmapForMasking, - onGenerateClick = viewModel::generateImage, - onInpaintClick = { source, mask, prompt -> viewModel.inpaintImage(source, mask, prompt) }, - onImageMaskReady = { source, mask -> viewModel.onImageMaskReady(source, mask) }, - onCancelMasking = viewModel::onCancelMasking, - modifier = Modifier.fillMaxSize(), - ) + ImagenEditingScreenContent( + uiState = uiState, + showMaskEditor = showMaskEditor, + bitmapForMasking = bitmapForMasking, + onGenerateClick = viewModel::generateImage, + onInpaintClick = { source, mask, prompt -> viewModel.inpaintImage(source, mask, prompt) }, + onImageMaskReady = { source, mask -> viewModel.onImageMaskReady(source, mask) }, + onCancelMasking = viewModel::onCancelMasking, + modifier = Modifier.fillMaxSize(), + ) } @Composable @@ -131,7 +131,7 @@ private fun ImagenEditingScreenContent( modifier = Modifier .padding(innerPadding) .fillMaxSize(), - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { Box( Modifier @@ -148,7 +148,6 @@ private fun ImagenEditingScreenContent( .background(ShaderBrush(imageShader)), contentAlignment = Alignment.Center, ) { - val textFieldState = rememberTextFieldState() val keyboardController = LocalSoftwareKeyboardController.current when (uiState) { @@ -160,6 +159,9 @@ private fun ImagenEditingScreenContent( .padding(24.dp) .align(Alignment.Center), ) + + val textFieldState = rememberTextFieldState() + TextField( textFieldState, isGenerating, @@ -174,19 +176,18 @@ private fun ImagenEditingScreenContent( ContainedLoadingIndicator( modifier = Modifier .size(60.dp) - .align(Alignment.Center) + .align(Alignment.Center), ) } } - is ImagenEditingUIState.ImageGenerated -> { + is ImagenEditingUIState.ImageGenerated -> { if (showMaskEditor && bitmapForMasking != null) { - textFieldState.clearText() + val textFieldState = rememberTextFieldState() ImagenEditingMaskEditor( sourceBitmap = bitmapForMasking, onMaskFinalized = { maskBitmap -> - textFieldState.clearText() onImageMaskReady(bitmapForMasking, maskBitmap) }, onCancel = { onCancelMasking() }, @@ -202,13 +203,13 @@ private fun ImagenEditingScreenContent( .background(color = MaterialTheme.colorScheme.surfaceContainer), ) } else { - textFieldState.clearText() + val textFieldState = rememberTextFieldState() Image( bitmap = uiState.bitmap.asImageBitmap(), contentDescription = uiState.contentDescription, contentScale = ContentScale.Crop, - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), ) TextField( textFieldState, @@ -236,13 +237,15 @@ private fun ImagenEditingScreenContent( colorFilter = ColorFilter.tint(Color.Red.copy(alpha = 0.5f)), ) } + val textFieldState = rememberTextFieldState() TextField( textFieldState = textFieldState, isGenerating = isGenerating, onGenerateClick = { prompt -> onInpaintClick(uiState.originalBitmap, uiState.maskBitmap, prompt) }, keyboardController, - placeholder = stringResource(R.string.describe_the_image_to_in_paint)) + placeholder = stringResource(R.string.describe_the_image_to_in_paint), + ) } else -> {} diff --git a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingSeeCodeButton.kt b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingSeeCodeButton.kt deleted file mode 100644 index e69de29b..00000000 From 81d4c50d0f13b6856f7038b6293e80eac228540a Mon Sep 17 00:00:00 2001 From: Thomas Ezan Date: Mon, 6 Oct 2025 14:35:03 -0700 Subject: [PATCH 5/5] Addressing feedback --- .../ai/samples/imagenediting/ui/ImagenEditingScreen.kt | 1 - .../ai/samples/imagenediting/ui/ImagenEditingViewModel.kt | 5 ----- .../samples/imagen-editing/src/main/res/values/strings.xml | 2 +- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt index 7f349097..d76ff549 100644 --- a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt +++ b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingScreen.kt @@ -34,7 +34,6 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.input.TextFieldState -import androidx.compose.foundation.text.input.clearText import androidx.compose.foundation.text.input.rememberTextFieldState import androidx.compose.material3.ContainedLoadingIndicator import androidx.compose.material3.ExperimentalMaterial3Api diff --git a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingViewModel.kt b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingViewModel.kt index 7ee8878c..4dbb5c8b 100644 --- a/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingViewModel.kt +++ b/ai-catalog/samples/imagen-editing/src/main/java/com/android/ai/samples/imagenediting/ui/ImagenEditingViewModel.kt @@ -74,11 +74,6 @@ class ImagenEditingViewModel @Inject constructor(private val imagenDataSource: I } } - private fun onStartMasking(bitmap: Bitmap) { - _bitmapForMasking.value = bitmap - _showMaskEditor.value = true - } - fun onImageMaskReady(originalBitmap: Bitmap, maskBitmap: Bitmap) { val originalContentDescription = (_uiState.value as? ImagenEditingUIState.ImageGenerated)?.contentDescription ?: "Edited image" _uiState.value = ImagenEditingUIState.ImageMasked( diff --git a/ai-catalog/samples/imagen-editing/src/main/res/values/strings.xml b/ai-catalog/samples/imagen-editing/src/main/res/values/strings.xml index 3742da74..4f7efff1 100644 --- a/ai-catalog/samples/imagen-editing/src/main/res/values/strings.xml +++ b/ai-catalog/samples/imagen-editing/src/main/res/values/strings.xml @@ -29,7 +29,7 @@ An image of dog working as a chef An unknown error occurred. Imagen Editing - Generate images with Imagen, Google image generation model. + Generate images with Imagen, Google\'s image generation model. Image to be masked The generated image The generated mask