diff --git a/README.md b/README.md index cfac5c60..d15851d1 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,11 @@ fontName="Roboto Flex" For Googlers, get this info from go/androidify-api-setup +## Availability +Due to the background vibe feature using +`gemini-2.0-flash-preview-image-generation`, its not currently supported in a number of countries in Europe, Middle East & Africa. +See [this](https://ai.google.dev/gemini-api/docs/models#gemini-2.0-flash-preview-image-generation) doc for more information. + ## Contributing See [Contributing](CONTRIBUTING.md). diff --git a/core/network/src/main/java/com/android/developers/androidify/RemoteConfigDataSource.kt b/core/network/src/main/java/com/android/developers/androidify/RemoteConfigDataSource.kt index 0999fc9e..f8a38caf 100644 --- a/core/network/src/main/java/com/android/developers/androidify/RemoteConfigDataSource.kt +++ b/core/network/src/main/java/com/android/developers/androidify/RemoteConfigDataSource.kt @@ -40,6 +40,8 @@ interface RemoteConfigDataSource { fun getFineTunedModelName(): String fun getImageGenerationEditsModelName(): String + + fun getBotBackgroundInstructionPrompt(): String } @Singleton @@ -100,4 +102,8 @@ class RemoteConfigDataSourceImpl @Inject constructor() : RemoteConfigDataSource override fun getImageGenerationEditsModelName(): String { return remoteConfig.getString("image_generation_model_edits") } + + override fun getBotBackgroundInstructionPrompt(): String { + return remoteConfig.getString("bot_background_instruction_prompt") + } } diff --git a/core/network/src/main/java/com/android/developers/androidify/vertexai/FirebaseAiDataSource.kt b/core/network/src/main/java/com/android/developers/androidify/vertexai/FirebaseAiDataSource.kt index fef0f431..20103ff6 100644 --- a/core/network/src/main/java/com/android/developers/androidify/vertexai/FirebaseAiDataSource.kt +++ b/core/network/src/main/java/com/android/developers/androidify/vertexai/FirebaseAiDataSource.kt @@ -85,12 +85,12 @@ class FirebaseAiDataSourceImpl @Inject constructor( return Firebase.ai(backend = GenerativeBackend.vertexAI()).imagenModel( remoteConfigDataSource.imageModelName(), safetySettings = - ImagenSafetySettings( - safetyFilterLevel = ImagenSafetyFilterLevel.BLOCK_LOW_AND_ABOVE, - // Uses `ALLOW_ADULT` filter since `ALLOW_ALL` requires a special approval - // See https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen#person-face-gen - personFilterLevel = ImagenPersonFilterLevel.ALLOW_ADULT, - ), + ImagenSafetySettings( + safetyFilterLevel = ImagenSafetyFilterLevel.BLOCK_LOW_AND_ABOVE, + // Uses `ALLOW_ADULT` filter since `ALLOW_ALL` requires a special approval + // See https://cloud.google.com/vertex-ai/generative-ai/docs/image/responsible-ai-imagen#person-face-gen + personFilterLevel = ImagenPersonFilterLevel.ALLOW_ADULT, + ), ) } diff --git a/core/network/src/main/res/xml/remote_config_defaults.xml b/core/network/src/main/res/xml/remote_config_defaults.xml index 36cfa63b..2d4dbb0c 100644 --- a/core/network/src/main/res/xml/remote_config_defaults.xml +++ b/core/network/src/main/res/xml/remote_config_defaults.xml @@ -16,12 +16,25 @@ --> - image_generation_model_edits - gemini-2.0-flash-preview-image-generation + bot_background_instruction_prompt + Add the input image android bot as the main subject to the result, + it should be the most prominent element of the resultant image, large and + filling the foreground - more than 50% of the resultant frame, + standing in the center of the frame with the central + focus, and the background just underneath the content. + + Always include the input Android Bot in the final result image as the subject of the image. + It should be prominently featured in the foreground, center of the frame, without any adjustments other + than the lighting of the surrounding environment. There should only be one of the bots in the image. + style="3d animation style, simplified shapes, mouthless character, realistic physics simulation" + + Do not alter the input Android Bot image, do not change its shape or add any hands, eyes, mouths etc. Do not change the characters color scheme. + + The background is described as follows: use_imagen - true + false prompt_image_validation @@ -127,6 +140,10 @@ promo_video_link https://services.google.com/fh/files/misc/androidfy_storyboard_b_v07.mp4 + + image_generation_model_edits + gemini-2.0-flash-preview-image-generation + text_model_name gemini-2.5-flash diff --git a/core/testing/src/main/java/com/android/developers/testing/network/TestRemoteConfigDataSource.kt b/core/testing/src/main/java/com/android/developers/testing/network/TestRemoteConfigDataSource.kt index 8790c38b..8f5379b6 100644 --- a/core/testing/src/main/java/com/android/developers/testing/network/TestRemoteConfigDataSource.kt +++ b/core/testing/src/main/java/com/android/developers/testing/network/TestRemoteConfigDataSource.kt @@ -71,4 +71,8 @@ class TestRemoteConfigDataSource(private val useGeminiNano: Boolean) : RemoteCon override fun getImageGenerationEditsModelName(): String { return "test_image_model" } + + override fun getBotBackgroundInstructionPrompt(): String { + return "bot_background_instruction_prompt" + } } diff --git a/core/testing/src/main/java/com/android/developers/testing/repository/FakeImageGenerationRepository.kt b/core/testing/src/main/java/com/android/developers/testing/repository/FakeImageGenerationRepository.kt index ab654b84..7cefbb1e 100644 --- a/core/testing/src/main/java/com/android/developers/testing/repository/FakeImageGenerationRepository.kt +++ b/core/testing/src/main/java/com/android/developers/testing/repository/FakeImageGenerationRepository.kt @@ -58,9 +58,9 @@ class FakeImageGenerationRepository : ImageGenerationRepository { return imageUri } - override suspend fun generateImageWithEdit( + override suspend fun addBackgroundToBot( image: Bitmap, - editPrompt: String, + backgroundPrompt: String, ): Bitmap { return createBitmap(1, 1) } diff --git a/data/src/main/java/com/android/developers/androidify/data/ImageGenerationRepository.kt b/data/src/main/java/com/android/developers/androidify/data/ImageGenerationRepository.kt index 710f5d9e..9c3c881c 100644 --- a/data/src/main/java/com/android/developers/androidify/data/ImageGenerationRepository.kt +++ b/data/src/main/java/com/android/developers/androidify/data/ImageGenerationRepository.kt @@ -19,6 +19,7 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri import android.util.Log +import com.android.developers.androidify.RemoteConfigDataSource import com.android.developers.androidify.model.ValidatedDescription import com.android.developers.androidify.model.ValidatedImage import com.android.developers.androidify.util.LocalFileProvider @@ -35,7 +36,8 @@ interface ImageGenerationRepository { suspend fun saveImage(imageBitmap: Bitmap): Uri suspend fun saveImageToExternalStorage(imageBitmap: Bitmap): Uri suspend fun saveImageToExternalStorage(imageUri: Uri): Uri - suspend fun generateImageWithEdit(image: Bitmap, editPrompt: String): Bitmap + + suspend fun addBackgroundToBot(image: Bitmap, backgroundPrompt: String) : Bitmap } @Singleton @@ -44,6 +46,7 @@ internal class ImageGenerationRepositoryImpl @Inject constructor( private val internetConnectivityManager: InternetConnectivityManager, private val geminiNanoDataSource: GeminiNanoGenerationDataSource, private val firebaseAiDataSource: FirebaseAiDataSource, + private val remoteConfigDataSource: RemoteConfigDataSource ) : ImageGenerationRepository { override suspend fun initialize() { @@ -126,7 +129,9 @@ internal class ImageGenerationRepositoryImpl @Inject constructor( } } - override suspend fun generateImageWithEdit(image: Bitmap, editPrompt: String): Bitmap { - return firebaseAiDataSource.generateImageWithEdit(image, editPrompt) + override suspend fun addBackgroundToBot(image: Bitmap, backgroundPrompt: String): Bitmap { + val backgroundBotInstructions = remoteConfigDataSource.getBotBackgroundInstructionPrompt() + + "\"" + backgroundPrompt + "\"" + return firebaseAiDataSource.generateImageWithEdit(image, backgroundBotInstructions) } } diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportViewModel.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportViewModel.kt index d136ab11..64c95b78 100644 --- a/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportViewModel.kt +++ b/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeExportViewModel.kt @@ -147,9 +147,8 @@ class CustomizeExportViewModel @Inject constructor( _state.update { it.copy(showImageEditProgress = true) } try { - val bitmap = imageGenerationRepository.generateImageWithEdit( - image, - "Add the input image android bot as the main subject to the result, it should be the most prominent element of the resultant image, large and filling the foreground, standing in the center of the frame with the central focus, and the background just underneath the content. The background is described as follows: \"" + backgroundOption.prompt + "\"", + val bitmap = imageGenerationRepository.addBackgroundToBot( + image, backgroundOption.prompt, ) _state.update { it.copy( diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/ImageRenderer.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/ImageRenderer.kt index 63522b46..3f023fb5 100644 --- a/feature/results/src/main/java/com/android/developers/androidify/customize/ImageRenderer.kt +++ b/feature/results/src/main/java/com/android/developers/androidify/customize/ImageRenderer.kt @@ -158,7 +158,8 @@ fun BackgroundLayout( .then(safeAnimateBounds) .rotate(rotationAnimation), ) { - val clip = if (exportImageCanvas.selectedBackgroundOption == BackgroundOption.None) { + val clip = if (exportImageCanvas.selectedBackgroundOption == BackgroundOption.None + || exportImageCanvas.selectedBackgroundOption.aiBackground) { Modifier } else { Modifier.clip(RoundedCornerShape(6))