[TP] Handle quick update from slider

When sliding, the color updates are frequent. We do not allow quick
updates to the settings. So we will handle the color changes at the UI
level.

1. Replace the clock preview on the lock screen with an overlay clock
   view
2. Implements the frequent color updates to the view

Test: Manually tested that the view quickly respond to the slider
Bug: 270097085
Change-Id: Ib78058d0dabaeea8cdbe1eed4516ecf02c76b904
diff --git a/res/layout/fragment_clock_settings.xml b/res/layout/fragment_clock_settings.xml
index 088ec2a..5208222 100644
--- a/res/layout/fragment_clock_settings.xml
+++ b/res/layout/fragment_clock_settings.xml
@@ -41,6 +41,11 @@
             android:layout_height="match_parent"
             android:layout_gravity="center" />
 
+        <FrameLayout
+            android:id="@+id/clock_host_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="center" />
     </com.android.wallpaper.picker.DisplayAspectRatioFrameLayout>
 
 
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 6c72a5b..026e333 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
@@ -15,7 +15,6 @@
  */
 package com.android.customization.picker.clock.ui.binder
 
-import android.view.View
 import android.view.ViewGroup
 import android.widget.FrameLayout
 import androidx.core.view.isVisible
@@ -24,6 +23,7 @@
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.customization.picker.clock.ui.view.ClockCarouselView
+import com.android.customization.picker.clock.ui.view.ClockViewFactory
 import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel
 import com.android.wallpaper.R
 import kotlinx.coroutines.launch
@@ -43,7 +43,7 @@
         carouselView: ClockCarouselView,
         singleClockView: ViewGroup,
         viewModel: ClockCarouselViewModel,
-        clockViewFactory: (clockId: String) -> View,
+        clockViewFactory: ClockViewFactory,
         lifecycleOwner: LifecycleOwner,
     ): Binding {
         val singleClockHostView =
@@ -56,7 +56,7 @@
                     viewModel.allClockIds.collect { allClockIds ->
                         carouselView.setUpClockCarouselView(
                             clockIds = allClockIds,
-                            onGetClockPreview = clockViewFactory,
+                            onGetClockPreview = { clockId -> clockViewFactory.getView(clockId) },
                             onClockSelected = { clockId -> viewModel.setSelectedClock(clockId) },
                         )
                     }
@@ -69,13 +69,17 @@
                 }
 
                 launch {
+                    viewModel.seedColor.collect { clockViewFactory.updateColorForAllClocks(it) }
+                }
+
+                launch {
                     viewModel.isSingleClockViewVisible.collect { singleClockView.isVisible = it }
                 }
 
                 launch {
                     viewModel.clockId.collect { clockId ->
                         singleClockHostView.removeAllViews()
-                        val clockView = clockViewFactory(clockId)
+                        val clockView = clockViewFactory.getView(clockId)
                         // The clock view might still be attached to an existing parent. Detach
                         // before adding to another parent.
                         (clockView.parent as? ViewGroup)?.removeView(clockView)
diff --git a/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt b/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
index 62d7217..b63fd27 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
@@ -16,6 +16,8 @@
 package com.android.customization.picker.clock.ui.binder
 
 import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
 import android.widget.SeekBar
 import androidx.core.view.isInvisible
 import androidx.core.view.isVisible
@@ -28,10 +30,12 @@
 import com.android.customization.picker.clock.shared.ClockSize
 import com.android.customization.picker.clock.ui.adapter.ClockSettingsTabAdapter
 import com.android.customization.picker.clock.ui.view.ClockSizeRadioButtonGroup
+import com.android.customization.picker.clock.ui.view.ClockViewFactory
 import com.android.customization.picker.clock.ui.viewmodel.ClockSettingsViewModel
 import com.android.customization.picker.color.ui.adapter.ColorOptionAdapter
 import com.android.customization.picker.common.ui.view.ItemSpacing
 import com.android.wallpaper.R
+import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.launch
 
 /** Bind between the clock settings screen and its view model. */
@@ -39,8 +43,11 @@
     fun bind(
         view: View,
         viewModel: ClockSettingsViewModel,
+        clockViewFactory: ClockViewFactory,
         lifecycleOwner: LifecycleOwner,
     ) {
+        val clockHostView: FrameLayout = view.requireViewById(R.id.clock_host_view)
+
         val tabView: RecyclerView = view.requireViewById(R.id.tabs)
         val tabAdapter = ClockSettingsTabAdapter()
         tabView.adapter = tabAdapter
@@ -82,6 +89,25 @@
         val colorOptionContainer = view.requireViewById<View>(R.id.color_picker_container)
         lifecycleOwner.lifecycleScope.launch {
             lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+                launch {
+                    viewModel.selectedClockId
+                        .mapNotNull { it }
+                        .collect { clockId ->
+                            val clockView = clockViewFactory.getView(clockId)
+                            (clockView.parent as? ViewGroup)?.removeView(clockView)
+                            clockHostView.removeAllViews()
+                            clockHostView.addView(clockView)
+                        }
+                }
+
+                launch {
+                    viewModel.seedColor.collect { seedColor ->
+                        viewModel.selectedClockId.value?.let { selectedClockId ->
+                            clockViewFactory.updateColor(selectedClockId, seedColor)
+                        }
+                    }
+                }
+
                 launch { viewModel.tabs.collect { tabAdapter.setItems(it) } }
 
                 launch {
diff --git a/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt b/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt
index 9b6d737..ef1a5ef 100644
--- a/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt
+++ b/src/com/android/customization/picker/clock/ui/fragment/ClockSettingsFragment.kt
@@ -24,6 +24,7 @@
 import androidx.lifecycle.get
 import com.android.customization.module.ThemePickerInjector
 import com.android.customization.picker.clock.ui.binder.ClockSettingsBinder
+import com.android.systemui.shared.clocks.shared.model.ClockPreviewConstants
 import com.android.wallpaper.R
 import com.android.wallpaper.module.InjectorProvider
 import com.android.wallpaper.picker.AppbarFragment
@@ -95,6 +96,16 @@
                         onWallpaperColorChanged = { colors ->
                             colorViewModel.setLockWallpaperColors(colors)
                         },
+                        initialExtrasProvider = {
+                            Bundle().apply {
+                                // Hide the clock from the system UI rendered preview so we can
+                                // place the carousel on top of it.
+                                putBoolean(
+                                    ClockPreviewConstants.KEY_HIDE_CLOCK,
+                                    true,
+                                )
+                            }
+                        },
                     ),
                 lifecycleOwner = this,
                 offsetToStart = displayUtils.isOnWallpaperDisplay(activity),
@@ -111,6 +122,7 @@
                     ),
                 )
                 .get(),
+            injector.getClockViewFactory(activity),
             this@ClockSettingsFragment,
         )
 
diff --git a/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt b/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt
index 488dd08..59b3a71 100644
--- a/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt
+++ b/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt
@@ -17,6 +17,7 @@
 
 import android.app.Activity
 import android.view.View
+import androidx.annotation.ColorInt
 import com.android.systemui.plugins.ClockController
 import com.android.systemui.shared.clocks.ClockRegistry
 import com.android.wallpaper.R
@@ -32,6 +33,16 @@
         return (clockControllers[clockId] ?: initClockController(clockId)).largeClock.view
     }
 
+    fun updateColorForAllClocks(@ColorInt seedColor: Int?) {
+        clockControllers.values.forEach { it.events.onSeedColorChanged(seedColor = seedColor) }
+    }
+
+    fun updateColor(clockId: String, @ColorInt seedColor: Int?) {
+        return (clockControllers[clockId] ?: initClockController(clockId))
+            .events
+            .onSeedColorChanged(seedColor)
+    }
+
     private fun initClockController(clockId: String): ClockController {
         val controller =
             registry.createExampleClock(clockId).also { it?.initialize(activity.resources, 0f, 0f) }
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 8fe9dca..60a9e85 100644
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
@@ -42,6 +42,8 @@
             allClocks.map { it.clockId }
         }
 
+    val seedColor: Flow<Int?> = interactor.seedColor
+
     private val shouldShowCarousel = MutableStateFlow(false)
     val isCarouselVisible: Flow<Boolean> =
         combine(allClockIds.map { it.size > 1 }.distinctUntilChanged(), shouldShowCarousel) {
diff --git a/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt b/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt
index 06fd2af..21d39f4 100644
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt
@@ -57,6 +57,11 @@
         SIZE,
     }
 
+    val selectedClockId: StateFlow<String?> =
+        clockPickerInteractor.selectedClockId
+            .distinctUntilChanged()
+            .stateIn(viewModelScope, SharingStarted.Eagerly, null)
+
     val selectedColor: StateFlow<Int?> =
         clockPickerInteractor.selectedColor.stateIn(viewModelScope, SharingStarted.Eagerly, null)
 
diff --git a/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt b/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
index 0b197b4..700439b 100644
--- a/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
+++ b/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
@@ -79,7 +79,7 @@
                     carouselView = carouselView,
                     singleClockView = singleClockView,
                     viewModel = clockCarouselViewModel,
-                    clockViewFactory = { clockId -> clockViewFactory.getView(clockId) },
+                    clockViewFactory = clockViewFactory,
                     lifecycleOwner = lifecycleOwner,
                 )
             onScreenSwitched(