Make clock carousel extend ViewModel
Make clock carousel view model has the benefits:
1. view model scope can be used, so that we know what to create a new
view model
2. Initial state is applied when new view model created
3. by making allClockIds we avoid redundently create many listeners for
the clock registry
Test: manually test it works as what it is before
Bug: 278784119
Change-Id: Ie805725eee32e390c4442ccaea22a691b83ac106
diff --git a/src/com/android/customization/module/CustomizationInjector.kt b/src/com/android/customization/module/CustomizationInjector.kt
index 479bca2..c0e4124 100644
--- a/src/com/android/customization/module/CustomizationInjector.kt
+++ b/src/com/android/customization/module/CustomizationInjector.kt
@@ -63,7 +63,9 @@
wallpaperColorsViewModel: WallpaperColorsViewModel,
): ColorPickerViewModel.Factory
- fun getClockCarouselViewModel(context: Context): ClockCarouselViewModel
+ fun getClockCarouselViewModelFactory(
+ interactor: ClockPickerInteractor,
+ ): ClockCarouselViewModel.Factory
fun getClockViewFactory(activity: Activity): ClockViewFactory
diff --git a/src/com/android/customization/module/ThemePickerInjector.kt b/src/com/android/customization/module/ThemePickerInjector.kt
index da11d51..2d36db4 100644
--- a/src/com/android/customization/module/ThemePickerInjector.kt
+++ b/src/com/android/customization/module/ThemePickerInjector.kt
@@ -26,6 +26,7 @@
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.get
import com.android.customization.model.color.ColorCustomizationManager
import com.android.customization.model.color.ColorOptionsProvider
import com.android.customization.model.grid.GridOptionsManager
@@ -102,7 +103,7 @@
private var clockRegistry: ClockRegistry? = null
private var clockPickerInteractor: ClockPickerInteractor? = null
private var clockSectionViewModel: ClockSectionViewModel? = null
- private var clockCarouselViewModel: ClockCarouselViewModel? = null
+ private var clockCarouselViewModelFactory: ClockCarouselViewModel.Factory? = null
private var clockViewFactory: ClockViewFactory? = null
private var notificationsInteractor: NotificationsInteractor? = null
private var notificationSectionViewModelFactory: NotificationSectionViewModel.Factory? = null
@@ -119,6 +120,14 @@
private var gridScreenViewModelFactory: GridScreenViewModel.Factory? = null
override fun getCustomizationSections(activity: ComponentActivity): CustomizationSections {
+ val clockCarouselViewModel =
+ ViewModelProvider(
+ activity,
+ getClockCarouselViewModelFactory(
+ getClockPickerInteractor(activity.applicationContext),
+ ),
+ )
+ .get() as ClockCarouselViewModel
return customizationSections
?: DefaultCustomizationSections(
getColorPickerViewModelFactory(
@@ -131,7 +140,7 @@
interactor = getNotificationsInteractor(activity),
),
getFlags(),
- getClockCarouselViewModel(activity),
+ clockCarouselViewModel,
getClockViewFactory(activity),
getDarkModeSnapshotRestorer(activity),
getThemedIconSnapshotRestorer(activity),
@@ -346,10 +355,12 @@
}
}
- override fun getClockCarouselViewModel(context: Context): ClockCarouselViewModel {
- return clockCarouselViewModel
- ?: ClockCarouselViewModel(getClockPickerInteractor(context)).also {
- clockCarouselViewModel = it
+ override fun getClockCarouselViewModelFactory(
+ interactor: ClockPickerInteractor,
+ ): ClockCarouselViewModel.Factory {
+ return clockCarouselViewModelFactory
+ ?: ClockCarouselViewModel.Factory(interactor).also {
+ clockCarouselViewModelFactory = it
}
}
diff --git a/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt b/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
index 8d5924e..641c2d6 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
@@ -47,14 +47,18 @@
launch {
viewModel.allClockIds.collect { allClockIds ->
- carouselView.setUpClockCarouselView(
- clockIds = allClockIds,
- onGetClockController = { clockId ->
- clockViewFactory.getController(clockId)
- },
- onClockSelected = { clockId -> viewModel.setSelectedClock(clockId) },
- getPreviewRatio = { clockViewFactory.getRatio() }
- )
+ if (allClockIds.size > 1) {
+ carouselView.setUpClockCarouselView(
+ clockIds = allClockIds,
+ onGetClockController = { clockId ->
+ clockViewFactory.getController(clockId)
+ },
+ onClockSelected = { clockId ->
+ viewModel.setSelectedClock(clockId)
+ },
+ getPreviewRatio = { clockViewFactory.getRatio() }
+ )
+ }
}
}
diff --git a/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt b/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
index c01f56a..34ccdb6 100644
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
@@ -15,30 +15,39 @@
*/
package com.android.customization.picker.clock.ui.viewmodel
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.viewModelScope
import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.stateIn
/**
* Clock carousel view model that provides data for the carousel of clock previews. When there is
* only one item, we should show a single clock preview instead of a carousel.
*/
-class ClockCarouselViewModel(
+class ClockCarouselViewModel
+constructor(
private val interactor: ClockPickerInteractor,
-) {
+) : ViewModel() {
@OptIn(ExperimentalCoroutinesApi::class)
- val allClockIds: Flow<List<String>> =
- interactor.allClocks.mapLatest { allClocks ->
- // Delay to avoid the case that the full list of clocks is not initiated.
- delay(CLOCKS_EVENT_UPDATE_DELAY_MILLIS)
- allClocks.map { it.clockId }
- }
+ val allClockIds: StateFlow<List<String>> =
+ interactor.allClocks
+ .mapLatest { allClocks ->
+ // Delay to avoid the case that the full list of clocks is not initiated.
+ delay(CLOCKS_EVENT_UPDATE_DELAY_MILLIS)
+ allClocks.map { it.clockId }
+ }
+ .stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
val seedColor: Flow<Int?> = interactor.seedColor
@@ -72,6 +81,18 @@
interactor.setSelectedClock(clockId)
}
+ class Factory(
+ private val interactor: ClockPickerInteractor,
+ ) : ViewModelProvider.Factory {
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ @Suppress("UNCHECKED_CAST")
+ return ClockCarouselViewModel(
+ interactor = interactor,
+ )
+ as T
+ }
+ }
+
companion object {
const val CLOCKS_EVENT_UPDATE_DELAY_MILLIS: Long = 100
}