Improve clock carousel perf

1. Only call clockViewFactory.getRatio() when set up the carousel,
   instead of whenever scrolling the carousel or populating the views
2. Remove unnessary onClockTransitionCompleted call
3. Do not call jumpToIndex if the current index is the same to avoid
   populating the views again

Test: Manually tested that the carousel works as expected.
Bug: 278850684
Change-Id: I9dc22f6df9052677db7330e7b5dd44b27722118f
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 7d19dc1..7bdd4fc 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
@@ -53,20 +53,18 @@
                             onGetClockController = { clockId ->
                                 clockViewFactory.getController(clockId)
                             },
-                            onClockSelected = { clockId -> viewModel.setSelectedClock(clockId) },
-                            getPreviewRatio = { clockViewFactory.getRatio() },
-                            onClockTransitionCompleted = { startId, endId ->
-                                if (startId != endId ) {
-                                    val hasCustomWeatherDataDisplay =
-                                            clockViewFactory
-                                                    .getController(endId)
-                                                    .largeClock
-                                                    .config
-                                                    .hasCustomWeatherDataDisplay
+                            onClockSelected = { clockId ->
+                                viewModel.setSelectedClock(clockId)
+                                val hasCustomWeatherDataDisplay =
+                                    clockViewFactory
+                                        .getController(clockId)
+                                        .largeClock
+                                        .config
+                                        .hasCustomWeatherDataDisplay
 
-                                    hideSmartspace(hasCustomWeatherDataDisplay)
-                                }
+                                hideSmartspace(hasCustomWeatherDataDisplay)
                             },
+                            previewRatio = clockViewFactory.getRatio(),
                         )
                     }
                 }
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 a084a54..059e498 100644
--- a/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
+++ b/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
@@ -41,8 +41,8 @@
     val carousel: Carousel
     private val motionLayout: MotionLayout
     private lateinit var adapter: ClockCarouselAdapter
-    private lateinit var scalingUpClockController: ClockController
-    private lateinit var scalingDownClockController: ClockController
+    private var scalingUpClockController: ClockController? = null
+    private var scalingDownClockController: ClockController? = null
     private var scalingUpClockView: View? = null
     private var scalingDownClockView: View? = null
     private var showingCardView: View? = null
@@ -58,17 +58,14 @@
         clockIds: List<String>,
         onGetClockController: (clockId: String) -> ClockController,
         onClockSelected: (clockId: String) -> Unit,
-        getPreviewRatio: () -> Float,
-        onClockTransitionCompleted: (startId: String, endId: String) -> Unit,
+        previewRatio: Float,
     ) {
         adapter =
-            ClockCarouselAdapter(clockIds, onGetClockController, onClockSelected, getPreviewRatio)
+            ClockCarouselAdapter(clockIds, onGetClockController, onClockSelected, previewRatio)
         carousel.setAdapter(adapter)
         carousel.refresh()
         motionLayout.setTransitionListener(
             object : MotionLayout.TransitionListener {
-                var scalingDownClockId = ""
-                var scalingUpClockId = ""
 
                 override fun onTransitionStarted(
                     motionLayout: MotionLayout?,
@@ -76,11 +73,11 @@
                     endId: Int
                 ) {
                     isCarouselInTransition = true
-                    scalingDownClockId = adapter.clockIds[carousel.currentIndex]
+                    val scalingDownClockId = adapter.clockIds[carousel.currentIndex]
                     val scalingUpIdx =
                         if (endId == R.id.next) (carousel.currentIndex + 1) % adapter.count()
                         else (carousel.currentIndex - 1 + adapter.count()) % adapter.count()
-                    scalingUpClockId = adapter.clockIds[scalingUpIdx]
+                    val scalingUpClockId = adapter.clockIds[scalingUpIdx]
                     scalingDownClockController = adapter.onGetClockController(scalingDownClockId)
                     scalingUpClockController = adapter.onGetClockController(scalingUpClockId)
                     scalingDownClockView = motionLayout?.findViewById(R.id.clock_scale_view_2)
@@ -94,6 +91,7 @@
                         motionLayout?.findViewById(
                             if (endId == R.id.next) R.id.item_card_3 else R.id.item_card_1
                         )
+                    setCardAnimationState(true)
                 }
 
                 override fun onTransitionChange(
@@ -102,14 +100,20 @@
                     endId: Int,
                     progress: Float
                 ) {
-                    scalingDownClockController.largeClock.animations.onPickerCarouselSwiping(
-                        1 - progress,
-                        getPreviewRatio()
-                    )
-                    scalingUpClockController.largeClock.animations.onPickerCarouselSwiping(
-                        progress,
-                        getPreviewRatio()
-                    )
+                    scalingDownClockController
+                        ?.largeClock
+                        ?.animations
+                        ?.onPickerCarouselSwiping(
+                            1 - progress,
+                            previewRatio,
+                        )
+                    scalingUpClockController
+                        ?.largeClock
+                        ?.animations
+                        ?.onPickerCarouselSwiping(
+                            progress,
+                            previewRatio,
+                        )
                     val scalingUpScale = getScalingUpScale(progress)
                     val scalingDownScale = getScalingDownScale(progress)
                     scalingUpClockView?.scaleX = scalingUpScale
@@ -122,7 +126,30 @@
 
                 override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
                     isCarouselInTransition = false
-                    onClockTransitionCompleted(scalingDownClockId, scalingUpClockId)
+                    setCardAnimationState(currentId == R.id.start)
+                }
+
+                private fun setCardAnimationState(isStart: Boolean) {
+                    scalingDownClockView?.scaleX = if (isStart) 1f else CLOCK_CAROUSEL_VIEW_SCALE
+                    scalingDownClockView?.scaleY = if (isStart) 1f else CLOCK_CAROUSEL_VIEW_SCALE
+                    scalingUpClockView?.scaleX = if (isStart) CLOCK_CAROUSEL_VIEW_SCALE else 1f
+                    scalingUpClockView?.scaleY = if (isStart) CLOCK_CAROUSEL_VIEW_SCALE else 1f
+                    scalingDownClockController
+                        ?.largeClock
+                        ?.animations
+                        ?.onPickerCarouselSwiping(
+                            if (isStart) 1f else 0f,
+                            previewRatio,
+                        )
+                    scalingUpClockController
+                        ?.largeClock
+                        ?.animations
+                        ?.onPickerCarouselSwiping(
+                            if (isStart) 0f else 1f,
+                            previewRatio,
+                        )
+                    showingCardView?.alpha = if (isStart) 0f else 1f
+                    hidingCardView?.alpha = if (isStart) 1f else 0f
                 }
 
                 override fun onTransitionTrigger(
@@ -138,14 +165,18 @@
     fun setSelectedClockIndex(
         index: Int,
     ) {
-        carousel.jumpToIndex(index)
+        // jumpToIndex to the same position can cause the views unnecessarily populate again.
+        // Only call jumpToIndex when the jump-to index is different from the current carousel.
+        if (index != carousel.currentIndex) {
+            carousel.jumpToIndex(index)
+        }
     }
 
     class ClockCarouselAdapter(
         val clockIds: List<String>,
         val onGetClockController: (clockId: String) -> ClockController,
         private val onClockSelected: (clockId: String) -> Unit,
-        val getPreviewRatio: () -> Float,
+        private val previewRatio: Float,
     ) : Carousel.Adapter {
 
         override fun count(): Int {
@@ -180,7 +211,7 @@
                 onGetClockController(clockIds[index])
                     .largeClock
                     .animations
-                    .onPickerCarouselSwiping(0F, getPreviewRatio())
+                    .onPickerCarouselSwiping(0F, previewRatio)
             } else {
                 cardView.alpha = 0f
                 clockScaleView.scaleX = 1f
@@ -188,7 +219,7 @@
                 onGetClockController(clockIds[index])
                     .largeClock
                     .animations
-                    .onPickerCarouselSwiping(1F, getPreviewRatio())
+                    .onPickerCarouselSwiping(1F, previewRatio)
             }
         }