From b2d19b24b68f6b5830a37967d13b7eebab44bd86 Mon Sep 17 00:00:00 2001 From: Rebecca Franks Date: Fri, 5 Sep 2025 11:58:58 +0100 Subject: [PATCH 1/3] Add a feature toggle for background vibes --- .../androidify/RemoteConfigDataSource.kt | 5 +++ .../main/res/xml/remote_config_defaults.xml | 4 +++ .../network/TestRemoteConfigDataSource.kt | 4 +++ .../customize/CustomizeExportViewModel.kt | 31 +++++++++++++++++++ .../androidify/customize/CustomizeState.kt | 12 +------ 5 files changed, 45 insertions(+), 11 deletions(-) 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 f8a38caf..18e37e52 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 @@ -24,6 +24,7 @@ interface RemoteConfigDataSource { fun isAppInactive(): Boolean fun textModelName(): String fun imageModelName(): String + fun isBackgroundVibesFeatureEnabled(): Boolean fun promptTextVerify(): String fun promptImageValidation(): String fun promptImageDescription(): String @@ -60,6 +61,10 @@ class RemoteConfigDataSourceImpl @Inject constructor() : RemoteConfigDataSource return remoteConfig.getString("image_model_name") } + override fun isBackgroundVibesFeatureEnabled(): Boolean { + return remoteConfig.getBoolean("background_vibes_feature_enabled") + } + override fun promptTextVerify(): String { return remoteConfig.getString("prompt_text_verify") } 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 2d4dbb0c..7dc868c5 100644 --- a/core/network/src/main/res/xml/remote_config_defaults.xml +++ b/core/network/src/main/res/xml/remote_config_defaults.xml @@ -15,6 +15,10 @@ limitations under the License. --> + + background_vibes_feature_enabled + false + bot_background_instruction_prompt Add the input image android bot as the main subject to the result, 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 8f5379b6..63b912ef 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 @@ -30,6 +30,10 @@ class TestRemoteConfigDataSource(private val useGeminiNano: Boolean) : RemoteCon TODO("Not yet implemented") } + override fun isBackgroundVibesFeatureEnabled(): Boolean { + return true + } + override fun promptTextVerify(): String { TODO("Not yet implemented") } 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 4b9bd992..ca022637 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 @@ -24,6 +24,7 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.ui.Modifier import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope +import com.android.developers.androidify.RemoteConfigDataSource import com.android.developers.androidify.data.ImageGenerationRepository import com.android.developers.androidify.util.LocalFileProvider import dagger.assisted.Assisted @@ -43,6 +44,7 @@ class CustomizeExportViewModel @AssistedInject constructor( val imageGenerationRepository: ImageGenerationRepository, val composableBitmapRenderer: ComposableBitmapRenderer, val localFileProvider: LocalFileProvider, + val remoteConfigDataSource: RemoteConfigDataSource, application: Application, ) : AndroidViewModel(application) { @@ -63,10 +65,39 @@ class CustomizeExportViewModel @AssistedInject constructor( get() = _snackbarHostState init { + val enableBackgroundVibes = remoteConfigDataSource.isBackgroundVibesFeatureEnabled() + var backgrounds = mutableListOf( + BackgroundOption.None, + BackgroundOption.Plain, + BackgroundOption.Lightspeed, + BackgroundOption.IO, + ) + if (enableBackgroundVibes) { + val backgroundVibes = listOf( + BackgroundOption.MusicLover, + BackgroundOption.PoolMaven, + BackgroundOption.SoccerFanatic, + BackgroundOption.StarGazer, + BackgroundOption.FitnessBuff, + BackgroundOption.Fandroid, + BackgroundOption.GreenThumb, + BackgroundOption.Gamer, + BackgroundOption.Jetsetter, + BackgroundOption.Chef, + ) + backgrounds.addAll(backgroundVibes) + } + _state.update { it.copy( originalImageUrl = originalImageUrl, exportImageCanvas = it.exportImageCanvas.copy(imageUri = resultImageUrl), + toolState = mapOf( + CustomizeTool.Size to AspectRatioToolState(), + CustomizeTool.Background to BackgroundToolState( + options = backgrounds + ), + ) ) } loadInitialBitmap(resultImageUrl) diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeState.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeState.kt index 588c33e6..78b4450b 100644 --- a/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeState.kt +++ b/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeState.kt @@ -60,17 +60,7 @@ data class BackgroundToolState( BackgroundOption.None, BackgroundOption.Plain, BackgroundOption.Lightspeed, - BackgroundOption.IO, - BackgroundOption.MusicLover, - BackgroundOption.PoolMaven, - BackgroundOption.SoccerFanatic, - BackgroundOption.StarGazer, - BackgroundOption.FitnessBuff, - BackgroundOption.Fandroid, - BackgroundOption.GreenThumb, - BackgroundOption.Gamer, - BackgroundOption.Jetsetter, - BackgroundOption.Chef, + BackgroundOption.IO ), ) : ToolState From b336e742590fdc5a8bddcd2594bb4b4fcda64263 Mon Sep 17 00:00:00 2001 From: Rebecca Franks Date: Fri, 5 Sep 2025 13:34:25 +0100 Subject: [PATCH 2/3] Update tests and run formatting --- .../startup/FirebaseAppCheckInitializer.kt | 4 +-- .../network/TestRemoteConfigDataSource.kt | 4 +-- .../androidify/creation/CreationScreen.kt | 4 +-- .../creation/CreationViewModelTest.kt | 4 +-- .../customize/ComposableBitmapRendererImpl.kt | 1 - .../customize/CustomizeExportViewModel.kt | 4 +-- .../androidify/customize/CustomizeState.kt | 2 +- .../customize/CustomizeViewModelTest.kt | 27 +++++++++++++++++++ gradle/libs.versions.toml | 2 +- 9 files changed, 39 insertions(+), 13 deletions(-) diff --git a/core/network/src/main/java/com/android/developers/androidify/startup/FirebaseAppCheckInitializer.kt b/core/network/src/main/java/com/android/developers/androidify/startup/FirebaseAppCheckInitializer.kt index 385b0a21..ffa5d0c0 100644 --- a/core/network/src/main/java/com/android/developers/androidify/startup/FirebaseAppCheckInitializer.kt +++ b/core/network/src/main/java/com/android/developers/androidify/startup/FirebaseAppCheckInitializer.kt @@ -36,13 +36,13 @@ class FirebaseAppCheckInitializer : Initializer { if (BuildConfig.DEBUG) { Log.i("AndroidifyAppCheck", "Firebase debug") installAppCheckProviderFactory( - DebugAppCheckProviderFactory.getInstance() + DebugAppCheckProviderFactory.getInstance(), ) } else { Log.i("AndroidifyAppCheck", "Play integrity") installAppCheckProviderFactory( PlayIntegrityAppCheckProviderFactory.getInstance(), - ) + ) } } } 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 63b912ef..60482e33 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 @@ -29,9 +29,9 @@ class TestRemoteConfigDataSource(private val useGeminiNano: Boolean) : RemoteCon override fun imageModelName(): String { TODO("Not yet implemented") } - + var backgroundVibeEnabled: Boolean = true override fun isBackgroundVibesFeatureEnabled(): Boolean { - return true + return backgroundVibeEnabled } override fun promptTextVerify(): String { diff --git a/feature/creation/src/main/java/com/android/developers/androidify/creation/CreationScreen.kt b/feature/creation/src/main/java/com/android/developers/androidify/creation/CreationScreen.kt index 4a6afcb7..8cdd2681 100644 --- a/feature/creation/src/main/java/com/android/developers/androidify/creation/CreationScreen.kt +++ b/feature/creation/src/main/java/com/android/developers/androidify/creation/CreationScreen.kt @@ -493,8 +493,8 @@ private fun MainCreationPane( // Workaround for https://issuetracker.google.com/432431393 val showTextPrompt by remember { derivedStateOf { - pagerState.currentPage == PromptType.TEXT.ordinal - && pagerState.targetPage == pagerState.currentPage + pagerState.currentPage == PromptType.TEXT.ordinal && + pagerState.targetPage == pagerState.currentPage } } if (showTextPrompt) { diff --git a/feature/creation/src/test/kotlin/com/android/developers/androidify/creation/CreationViewModelTest.kt b/feature/creation/src/test/kotlin/com/android/developers/androidify/creation/CreationViewModelTest.kt index af360858..4a98ddc5 100644 --- a/feature/creation/src/test/kotlin/com/android/developers/androidify/creation/CreationViewModelTest.kt +++ b/feature/creation/src/test/kotlin/com/android/developers/androidify/creation/CreationViewModelTest.kt @@ -72,7 +72,7 @@ class CreationViewModelTest { viewModel.uiState.value.screenState, ) assertEquals(false, viewModel.uiState.value.promptGenerationInProgress) - assertEquals( fakeUri, viewModel.uiState.value.imageUri) + assertEquals(fakeUri, viewModel.uiState.value.imageUri) } @Test @@ -91,7 +91,7 @@ class CreationViewModelTest { viewModel.uiState.value.screenState, ) assertEquals(false, viewModel.uiState.value.promptGenerationInProgress) - assertEquals( null, viewModel.uiState.value.imageUri) + assertEquals(null, viewModel.uiState.value.imageUri) } @Test diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/ComposableBitmapRendererImpl.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/ComposableBitmapRendererImpl.kt index 81794f69..643c04fc 100644 --- a/feature/results/src/main/java/com/android/developers/androidify/customize/ComposableBitmapRendererImpl.kt +++ b/feature/results/src/main/java/com/android/developers/androidify/customize/ComposableBitmapRendererImpl.kt @@ -22,7 +22,6 @@ import android.content.Context.DISPLAY_SERVICE import android.graphics.Bitmap import android.graphics.SurfaceTexture import android.hardware.display.DisplayManager -import android.hardware.display.VirtualDisplay import android.view.Display import android.view.Surface import android.view.ViewGroup 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 ca022637..ec75abfd 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 @@ -95,9 +95,9 @@ class CustomizeExportViewModel @AssistedInject constructor( toolState = mapOf( CustomizeTool.Size to AspectRatioToolState(), CustomizeTool.Background to BackgroundToolState( - options = backgrounds + options = backgrounds, ), - ) + ), ) } loadInitialBitmap(resultImageUrl) diff --git a/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeState.kt b/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeState.kt index 78b4450b..68fe8d87 100644 --- a/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeState.kt +++ b/feature/results/src/main/java/com/android/developers/androidify/customize/CustomizeState.kt @@ -60,7 +60,7 @@ data class BackgroundToolState( BackgroundOption.None, BackgroundOption.Plain, BackgroundOption.Lightspeed, - BackgroundOption.IO + BackgroundOption.IO, ), ) : ToolState diff --git a/feature/results/src/test/kotlin/com/android/developers/androidify/customize/CustomizeViewModelTest.kt b/feature/results/src/test/kotlin/com/android/developers/androidify/customize/CustomizeViewModelTest.kt index db0a1fb4..a77c6cad 100644 --- a/feature/results/src/test/kotlin/com/android/developers/androidify/customize/CustomizeViewModelTest.kt +++ b/feature/results/src/test/kotlin/com/android/developers/androidify/customize/CustomizeViewModelTest.kt @@ -21,6 +21,7 @@ import android.net.Uri import androidx.test.core.app.ApplicationProvider import com.android.developers.testing.data.TestFileProvider import com.android.developers.testing.data.bitmapSample +import com.android.developers.testing.network.TestRemoteConfigDataSource import com.android.developers.testing.repository.FakeImageGenerationRepository import com.android.developers.testing.util.FakeComposableBitmapRenderer import com.android.developers.testing.util.MainDispatcherRule @@ -52,6 +53,8 @@ class CustomizeViewModelTest { @Before fun setup() { + val remoteConfigDataSource = TestRemoteConfigDataSource(true) + remoteConfigDataSource.backgroundVibeEnabled = false viewModel = CustomizeExportViewModel( fakeUri, originalFakeUri, @@ -59,6 +62,7 @@ class CustomizeViewModelTest { composableBitmapRenderer = FakeComposableBitmapRenderer(), application = ApplicationProvider.getApplicationContext(), localFileProvider = TestFileProvider(), + remoteConfigDataSource = remoteConfigDataSource, ) } @@ -82,6 +86,8 @@ class CustomizeViewModelTest { @Test fun setArgumentsWithPrompt() = runTest { + val remoteConfigDataSource = TestRemoteConfigDataSource(true) + remoteConfigDataSource.backgroundVibeEnabled = false val viewModel = CustomizeExportViewModel( fakeUri, null, @@ -89,6 +95,7 @@ class CustomizeViewModelTest { composableBitmapRenderer = FakeComposableBitmapRenderer(), application = ApplicationProvider.getApplicationContext(), localFileProvider = TestFileProvider(), + remoteConfigDataSource = remoteConfigDataSource, ) assertEquals( CustomizeExportState( @@ -141,6 +148,7 @@ class CustomizeViewModelTest { composableBitmapRenderer = FakeComposableBitmapRenderer(), application = ApplicationProvider.getApplicationContext(), localFileProvider = TestFileProvider(), + remoteConfigDataSource = TestRemoteConfigDataSource(false), ) val values = mutableListOf() // Launch collector on the backgroundScope directly to use runTest's scheduler @@ -190,4 +198,23 @@ class CustomizeViewModelTest { assertTrue { !values[values.lastIndex].showImageEditProgress } assertNull(values.last().exportImageCanvas.imageWithEdit) } + + @Test + fun remoteConfigDataSource_BackgroundVibesFeatureEnabled_ContainsVibeList() = runTest { + val remoteConfigDataSource = TestRemoteConfigDataSource(true) + remoteConfigDataSource.backgroundVibeEnabled = true + val viewModel = CustomizeExportViewModel( + fakeUri, + null, + FakeImageGenerationRepository(), + composableBitmapRenderer = FakeComposableBitmapRenderer(), + application = ApplicationProvider.getApplicationContext(), + localFileProvider = TestFileProvider(), + remoteConfigDataSource = remoteConfigDataSource, + ) + val state = viewModel.state.value.toolState[CustomizeTool.Background] as BackgroundToolState + + assertTrue(state.options.size > 5) + assertTrue(state.options.any { it.aiBackground }) + } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 214af35a..e5729ba9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,7 +34,7 @@ hiltAndroid = "2.56.2" hiltLifecycleViewmodel = "1.0.0-alpha03" hiltNavigationCompose = "1.2.0" junit = "4.13.2" -junitVersion = "1.2.1" +junitVersion = "1.3.0" kotlin = "2.2.0" ksp = "2.2.0-2.0.2" kotlinxCoroutines = "1.10.2" From 909703e6b52d51b0bf54daa838f6607c82746f68 Mon Sep 17 00:00:00 2001 From: Rebecca Franks Date: Fri, 5 Sep 2025 13:39:38 +0100 Subject: [PATCH 3/3] Update tests --- .../androidify/customize/CustomizeStateTest.kt | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/feature/results/src/test/kotlin/com/android/developers/androidify/customize/CustomizeStateTest.kt b/feature/results/src/test/kotlin/com/android/developers/androidify/customize/CustomizeStateTest.kt index 87e7481e..8f4214ae 100644 --- a/feature/results/src/test/kotlin/com/android/developers/androidify/customize/CustomizeStateTest.kt +++ b/feature/results/src/test/kotlin/com/android/developers/androidify/customize/CustomizeStateTest.kt @@ -69,16 +69,6 @@ class CustomizeStateTest { BackgroundOption.Plain, BackgroundOption.Lightspeed, BackgroundOption.IO, - BackgroundOption.MusicLover, - BackgroundOption.PoolMaven, - BackgroundOption.SoccerFanatic, - BackgroundOption.StarGazer, - BackgroundOption.FitnessBuff, - BackgroundOption.Fandroid, - BackgroundOption.GreenThumb, - BackgroundOption.Gamer, - BackgroundOption.Jetsetter, - BackgroundOption.Chef, ), state.options, )