Merge "Delay color picker back navigation until apply is complete" into main
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ColorPickerViewModel2.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ColorPickerViewModel2.kt
index e7efebd..4029dbe 100644
--- a/src/com/android/wallpaper/customization/ui/viewmodel/ColorPickerViewModel2.kt
+++ b/src/com/android/wallpaper/customization/ui/viewmodel/ColorPickerViewModel2.kt
@@ -33,6 +33,8 @@
import dagger.assisted.AssistedInject
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.android.scopes.ViewModelScoped
+import kotlin.coroutines.resume
+import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -42,6 +44,7 @@
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
/** Models UI state for a color picker experience. */
class ColorPickerViewModel2
@@ -57,6 +60,7 @@
val previewingColorOption = overridingColorOption.asStateFlow()
private val selectedColorTypeTabId = MutableStateFlow<ColorType?>(null)
+ private var onApplyContinuation: CancellableContinuation<Unit>? = null
/** View-models for each color tab. */
val colorTypeTabs: Flow<List<FloatingToolbarTabViewModel>> =
@@ -165,6 +169,10 @@
.toMap()
}
+ /**
+ * This function suspends until onApplyComplete is called to accommodate for configuration
+ * change updates, which are applied with a latency.
+ */
val onApply: Flow<(suspend () -> Unit)?> =
previewingColorOption.map { previewingColorOption ->
previewingColorOption?.let {
@@ -173,6 +181,12 @@
} else {
{
interactor.select(it)
+ // Suspend until onApplyComplete is called, e.g. on configuration change
+ suspendCancellableCoroutine { continuation: CancellableContinuation<Unit> ->
+ onApplyContinuation?.cancel()
+ onApplyContinuation = continuation
+ continuation.invokeOnCancellation { onApplyContinuation = null }
+ }
logger.logThemeColorApplied(
previewingColorOption.colorOption.sourceForLogging,
previewingColorOption.colorOption.styleForLogging,
@@ -187,6 +201,12 @@
overridingColorOption.value = null
}
+ /** Resumes the onApply function if apply is in progress, otherwise no-op */
+ fun onApplyComplete() {
+ onApplyContinuation?.resume(Unit)
+ onApplyContinuation = null
+ }
+
/** The list of all available color options for the selected Color Type. */
val colorOptions: Flow<List<OptionItemViewModel2<ColorOptionIconViewModel>>> =
combine(allColorOptions, selectedColorTypeTabId) {
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ThemePickerCustomizationOptionsViewModel.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ThemePickerCustomizationOptionsViewModel.kt
index 62a96fe..6bc6180 100644
--- a/src/com/android/wallpaper/customization/ui/viewmodel/ThemePickerCustomizationOptionsViewModel.kt
+++ b/src/com/android/wallpaper/customization/ui/viewmodel/ThemePickerCustomizationOptionsViewModel.kt
@@ -27,6 +27,7 @@
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
@@ -59,6 +60,8 @@
val shapeGridPickerViewModel =
shapeGridPickerViewModelFactory.create(viewModelScope = viewModelScope)
+ private var onApplyJob: Job? = null
+
override val selectedOption = defaultCustomizationOptionsViewModel.selectedOption
override fun handleBackPressed(): Boolean {
@@ -167,9 +170,14 @@
.map { onApply ->
if (onApply != null) {
fun(onComplete: () -> Unit) {
- viewModelScope.launch {
- onApply()
- onComplete()
+ // Prevent double apply
+ if (onApplyJob?.isActive != true) {
+ onApplyJob =
+ viewModelScope.launch {
+ onApply()
+ onComplete()
+ onApplyJob = null
+ }
}
}
} else {
diff --git a/src/com/android/wallpaper/picker/common/preview/ui/binder/ThemePickerWorkspaceCallbackBinder.kt b/src/com/android/wallpaper/picker/common/preview/ui/binder/ThemePickerWorkspaceCallbackBinder.kt
index 6952d91..4b79739 100644
--- a/src/com/android/wallpaper/picker/common/preview/ui/binder/ThemePickerWorkspaceCallbackBinder.kt
+++ b/src/com/android/wallpaper/picker/common/preview/ui/binder/ThemePickerWorkspaceCallbackBinder.kt
@@ -159,13 +159,18 @@
}
launch {
+ colorUpdateViewModel.systemColorsUpdated.collect {
+ viewModel.colorPickerViewModel2.onApplyComplete()
+ }
+ }
+
+ launch {
combine(
viewModel.colorPickerViewModel2.previewingColorOption,
viewModel.darkModeViewModel.overridingIsDarkMode,
- colorUpdateViewModel.systemColorsUpdated,
- ::Triple,
+ ::Pair,
)
- .collect { (colorModel, darkMode, _) ->
+ .collect { (colorModel, darkMode) ->
val bundle =
Bundle().apply {
if (colorModel != null) {
diff --git a/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ColorPickerViewModel2Test.kt b/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ColorPickerViewModel2Test.kt
index 421d8a0..e4a4884 100644
--- a/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ColorPickerViewModel2Test.kt
+++ b/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ColorPickerViewModel2Test.kt
@@ -36,6 +36,7 @@
import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -96,6 +97,27 @@
}
@Test
+ fun onApply_suspendsUntilOnApplyCompleteIsCalled() =
+ testScope.runTest {
+ val colorTypes = collectLastValue(underTest.colorTypeTabs)
+ val colorOptions = collectLastValue(underTest.colorOptions)
+ val onApply = collectLastValue(underTest.onApply)
+
+ // Select "Wallpaper colors" tab
+ colorTypes()?.get(0)?.onClick?.invoke()
+ // Select a color option to preview
+ selectColorOption(colorOptions, 1)
+ // Apply the selected color option
+ val job = testScope.launch { onApply()?.invoke() }
+
+ assertThat(job.isActive).isTrue()
+
+ underTest.onApplyComplete()
+
+ assertThat(job.isActive).isFalse()
+ }
+
+ @Test
fun onApply_wallpaperColor_shouldLogColor() =
testScope.runTest {
repository.setOptions(
@@ -203,7 +225,7 @@
)
}
- /** Simulates a user selecting the affordance at the given index, if that is clickable. */
+ /** Simulates a user selecting the color option at the given index. */
private fun TestScope.selectColorOption(
colorOptions: () -> List<OptionItemViewModel2<ColorOptionIconViewModel>>?,
index: Int,
@@ -217,10 +239,11 @@
}
}
- /** Simulates a user selecting the affordance at the given index, if that is clickable. */
+ /** Simulates a user applying the color option at the given index, and the apply completes. */
private suspend fun TestScope.applySelectedColorOption() {
val onApply = collectLastValue(underTest.onApply)()
- onApply?.invoke()
+ testScope.launch { onApply?.invoke() }
+ underTest.onApplyComplete()
}
/**