Merge "[TP] Reuse clock views in clock carousel" into tm-qpr-dev am: b6239929b5

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/ThemePicker/+/21303070

Change-Id: I73f2da751d10ff6bc98d978ca9f2fca25401d0fd
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/src/com/android/customization/module/CustomizationInjector.kt b/src/com/android/customization/module/CustomizationInjector.kt
index 26bd925..900431e 100644
--- a/src/com/android/customization/module/CustomizationInjector.kt
+++ b/src/com/android/customization/module/CustomizationInjector.kt
@@ -22,6 +22,7 @@
 import com.android.customization.model.theme.ThemeManager
 import com.android.customization.picker.clock.data.repository.ClockRegistryProvider
 import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
+import com.android.customization.picker.clock.ui.view.ClockViewFactory
 import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel
 import com.android.customization.picker.clock.ui.viewmodel.ClockSectionViewModel
 import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor
@@ -71,4 +72,9 @@
         context: Context,
         clockRegistry: ClockRegistry,
     ): ClockCarouselViewModel
+
+    fun getClockViewFactory(
+        context: Context,
+        registry: ClockRegistry,
+    ): ClockViewFactory
 }
diff --git a/src/com/android/customization/module/DefaultCustomizationSections.java b/src/com/android/customization/module/DefaultCustomizationSections.java
index ab894f8..16d4bc7 100644
--- a/src/com/android/customization/module/DefaultCustomizationSections.java
+++ b/src/com/android/customization/module/DefaultCustomizationSections.java
@@ -19,6 +19,7 @@
 import com.android.customization.picker.notifications.ui.viewmodel.NotificationSectionViewModel;
 import com.android.customization.picker.preview.ui.section.PreviewWithClockCarouselSectionController;
 import com.android.customization.picker.preview.ui.section.PreviewWithClockCarouselSectionController.ClockCarouselViewModelProvider;
+import com.android.customization.picker.preview.ui.section.PreviewWithClockCarouselSectionController.ClockViewFactoryProvider;
 import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor;
 import com.android.customization.picker.quickaffordance.ui.section.KeyguardQuickAffordanceSectionController;
 import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordancePickerViewModel;
@@ -53,6 +54,8 @@
     private final ClockRegistryProvider mClockRegistryProvider;
     private final PreviewWithClockCarouselSectionController.ClockCarouselViewModelProvider
             mClockCarouselViewModelProvider;
+    private final PreviewWithClockCarouselSectionController.ClockViewFactoryProvider
+            mClockViewFactoryProvider;
 
     public DefaultCustomizationSections(
             KeyguardQuickAffordancePickerInteractor keyguardQuickAffordancePickerInteractor,
@@ -61,7 +64,8 @@
             NotificationSectionViewModel.Factory notificationSectionViewModelFactory,
             BaseFlags flags,
             ClockRegistryProvider clockRegistryProvider,
-            ClockCarouselViewModelProvider clockCarouselViewModelProvider) {
+            ClockCarouselViewModelProvider clockCarouselViewModelProvider,
+            ClockViewFactoryProvider clockViewFactoryProvider) {
         mKeyguardQuickAffordancePickerInteractor = keyguardQuickAffordancePickerInteractor;
         mKeyguardQuickAffordancePickerViewModelFactory =
                 keyguardQuickAffordancePickerViewModelFactory;
@@ -69,6 +73,7 @@
         mFlags = flags;
         mClockRegistryProvider = clockRegistryProvider;
         mClockCarouselViewModelProvider = clockCarouselViewModelProvider;
+        mClockViewFactoryProvider = clockViewFactoryProvider;
     }
 
     @Override
@@ -98,7 +103,8 @@
                         wallpaperColorsViewModel,
                         displayUtils,
                         mClockRegistryProvider,
-                        mClockCarouselViewModelProvider)
+                        mClockCarouselViewModelProvider,
+                        mClockViewFactoryProvider)
                         : new ScreenPreviewSectionController(
                                 activity,
                                 lifecycleOwner,
diff --git a/src/com/android/customization/module/ThemePickerInjector.kt b/src/com/android/customization/module/ThemePickerInjector.kt
index f2ebb68..c2a6565 100644
--- a/src/com/android/customization/module/ThemePickerInjector.kt
+++ b/src/com/android/customization/module/ThemePickerInjector.kt
@@ -28,6 +28,7 @@
 import com.android.customization.picker.clock.data.repository.ClockPickerRepositoryImpl
 import com.android.customization.picker.clock.data.repository.ClockRegistryProvider
 import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
+import com.android.customization.picker.clock.ui.view.ClockViewFactory
 import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel
 import com.android.customization.picker.clock.ui.viewmodel.ClockSectionViewModel
 import com.android.customization.picker.color.data.repository.ColorPickerRepositoryImpl
@@ -37,7 +38,8 @@
 import com.android.customization.picker.notifications.domain.interactor.NotificationsInteractor
 import com.android.customization.picker.notifications.domain.interactor.NotificationsSnapshotRestorer
 import com.android.customization.picker.notifications.ui.viewmodel.NotificationSectionViewModel
-import com.android.customization.picker.preview.ui.section.PreviewWithClockCarouselSectionController
+import com.android.customization.picker.preview.ui.section.PreviewWithClockCarouselSectionController.ClockCarouselViewModelProvider
+import com.android.customization.picker.preview.ui.section.PreviewWithClockCarouselSectionController.ClockViewFactoryProvider
 import com.android.customization.picker.quickaffordance.data.repository.KeyguardQuickAffordancePickerRepository
 import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor
 import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordanceSnapshotRestorer
@@ -79,6 +81,7 @@
     private var clockPickerInteractor: ClockPickerInteractor? = null
     private var clockSectionViewModel: ClockSectionViewModel? = null
     private var clockCarouselViewModel: ClockCarouselViewModel? = null
+    private var clockViewFactory: ClockViewFactory? = null
     private var notificationsInteractor: NotificationsInteractor? = null
     private var notificationSectionViewModelFactory: NotificationSectionViewModel.Factory? = null
     private var colorPickerInteractor: ColorPickerInteractor? = null
@@ -94,14 +97,21 @@
                     ),
                     getFlags(),
                     getClockRegistryProvider(activity),
-                    object :
-                        PreviewWithClockCarouselSectionController.ClockCarouselViewModelProvider {
+                    object : ClockCarouselViewModelProvider {
                         override fun get(registry: ClockRegistry): ClockCarouselViewModel {
                             return getClockCarouselViewModel(
                                 context = activity,
                                 clockRegistry = registry,
                             )
                         }
+                    },
+                    object : ClockViewFactoryProvider {
+                        override fun get(registry: ClockRegistry): ClockViewFactory {
+                            return getClockViewFactory(
+                                context = activity,
+                                registry = registry,
+                            )
+                        }
                     }
                 )
                 .also { customizationSections = it }
@@ -293,6 +303,14 @@
             }
     }
 
+    override fun getClockViewFactory(
+        context: Context,
+        registry: ClockRegistry,
+    ): ClockViewFactory {
+        return clockViewFactory
+            ?: ClockViewFactory(context, registry).also { clockViewFactory = it }
+    }
+
     protected fun getNotificationsInteractor(
         context: Context,
     ): NotificationsInteractor {
diff --git a/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt b/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
index 1f49c55..2771b12 100644
--- a/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
+++ b/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
@@ -69,7 +69,11 @@
         override fun populate(view: View?, index: Int) {
             val viewGroup = view as ViewGroup
             viewGroup.removeAllViews()
-            viewGroup.addView(onGetClockPreview(clockIds[index]))
+            val clockView = onGetClockPreview(clockIds[index])
+            // The clock view might still be attached to an existing parent. Detach before adding to
+            // another parent.
+            (clockView.parent as? ViewGroup)?.removeView(clockView)
+            viewGroup.addView(clockView)
         }
 
         override fun onNewItem(index: Int) {
diff --git a/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt b/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt
new file mode 100644
index 0000000..8f4fb28
--- /dev/null
+++ b/src/com/android/customization/picker/clock/ui/view/ClockViewFactory.kt
@@ -0,0 +1,24 @@
+package com.android.customization.picker.clock.ui.view
+
+import android.content.Context
+import android.view.View
+import com.android.systemui.plugins.ClockController
+import com.android.systemui.shared.clocks.ClockRegistry
+import java.util.HashMap
+
+class ClockViewFactory(private val context: Context, private val registry: ClockRegistry) {
+    private val clockControllers: HashMap<String, ClockController> =
+        HashMap<String, ClockController>()
+
+    fun getView(clockId: String): View {
+        return (clockControllers[clockId] ?: initClockController(clockId)).largeClock.view
+    }
+
+    private fun initClockController(clockId: String): ClockController {
+        val controller =
+            registry.createExampleClock(clockId).also { it?.initialize(context.resources, 0f, 0f) }
+        checkNotNull(controller)
+        clockControllers[clockId] = controller
+        return controller
+    }
+}
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 d90961b..f1319c6 100644
--- a/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
+++ b/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
@@ -25,6 +25,7 @@
 import com.android.customization.picker.clock.data.repository.ClockRegistryProvider
 import com.android.customization.picker.clock.ui.binder.ClockCarouselViewBinder
 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.systemui.shared.clocks.ClockRegistry
 import com.android.wallpaper.R
@@ -50,6 +51,7 @@
     private val displayUtils: DisplayUtils,
     private val clockRegistryProvider: ClockRegistryProvider,
     private val clockCarouselViewModelProvider: ClockCarouselViewModelProvider,
+    private val clockViewFactoryProvider: ClockViewFactoryProvider,
 ) :
     ScreenPreviewSectionController(
         activity,
@@ -67,13 +69,12 @@
         val carouselView: ClockCarouselView = carouselViewStub.inflate() as ClockCarouselView
         lifecycleOwner.lifecycleScope.launch {
             val registry = withContext(Dispatchers.IO) { clockRegistryProvider.get() }
+            val clockViewFactory = clockViewFactoryProvider.get(registry)
             clockCarouselBinding =
                 ClockCarouselViewBinder.bind(
                     view = carouselView,
                     viewModel = clockCarouselViewModelProvider.get(registry),
-                    clockViewFactory = { clockId ->
-                        registry.createExampleClock(clockId)?.largeClock?.view!!
-                    },
+                    clockViewFactory = { clockId -> clockViewFactory.getView(clockId) },
                     lifecycleOwner = lifecycleOwner,
                 )
             onScreenSwitched(
@@ -95,4 +96,8 @@
     interface ClockCarouselViewModelProvider {
         fun get(registry: ClockRegistry): ClockCarouselViewModel
     }
+
+    interface ClockViewFactoryProvider {
+        fun get(registry: ClockRegistry): ClockViewFactory
+    }
 }
diff --git a/tests/src/com/android/customization/testing/TestCustomizationInjector.kt b/tests/src/com/android/customization/testing/TestCustomizationInjector.kt
index 3dce587..e4ceb4f 100644
--- a/tests/src/com/android/customization/testing/TestCustomizationInjector.kt
+++ b/tests/src/com/android/customization/testing/TestCustomizationInjector.kt
@@ -11,6 +11,7 @@
 import com.android.customization.picker.clock.data.repository.ClockRegistryProvider
 import com.android.customization.picker.clock.data.repository.FakeClockPickerRepository
 import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
+import com.android.customization.picker.clock.ui.view.ClockViewFactory
 import com.android.customization.picker.clock.ui.viewmodel.ClockCarouselViewModel
 import com.android.customization.picker.clock.ui.viewmodel.ClockSectionViewModel
 import com.android.customization.picker.color.data.repository.ColorPickerRepositoryImpl
@@ -47,6 +48,7 @@
     private var clockRegistryProvider: ClockRegistryProvider? = null
     private var clockPickerInteractor: ClockPickerInteractor? = null
     private var clockSectionViewModel: ClockSectionViewModel? = null
+    private var clockViewFactory: ClockViewFactory? = null
     private var colorPickerInteractor: ColorPickerInteractor? = null
     private var colorPickerViewModelFactory: ColorPickerViewModel.Factory? = null
     private var clockCarouselViewModel: ClockCarouselViewModel? = null
@@ -191,6 +193,11 @@
             }
     }
 
+    override fun getClockViewFactory(context: Context, registry: ClockRegistry): ClockViewFactory {
+        return clockViewFactory
+            ?: ClockViewFactory(context, registry).also { clockViewFactory = it }
+    }
+
     companion object {
         private const val KEY_QUICK_AFFORDANCE_SNAPSHOT_RESTORER = 1
     }