Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ interface RemoteConfigDataSource {
fun getFineTunedModelName(): String

fun getImageGenerationEditsModelName(): String

fun getBotBackgroundInstructionPrompt(): String
}

@Singleton
Expand Down Expand Up @@ -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")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
),
)
}

Expand Down
23 changes: 20 additions & 3 deletions core/network/src/main/res/xml/remote_config_defaults.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,25 @@
-->
<defaults>
<entry>
<key>image_generation_model_edits</key>
<value>gemini-2.0-flash-preview-image-generation</value>
<key>bot_background_instruction_prompt</key>
<value>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:</value>
</entry>
<entry>
<key>use_imagen</key>
<value>true</value>
<value>false</value>
</entry>
<entry>
<key>prompt_image_validation</key>
Expand Down Expand Up @@ -127,6 +140,10 @@
<key>promo_video_link</key>
<value>https://services.google.com/fh/files/misc/androidfy_storyboard_b_v07.mp4</value>
</entry>
<entry>
<key>image_generation_model_edits</key>
<value>gemini-2.0-flash-preview-image-generation</value>
</entry>
<entry>
<key>text_model_name</key>
<value>gemini-2.5-flash</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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() {
Expand Down Expand Up @@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down