Merge "[2/n] Let picker refresh preview when user selects a different grid" into udc-qpr-dev
diff --git a/res/layout/single_clock_view.xml b/res/layout/single_clock_view.xml
deleted file mode 100644
index ffc6039..0000000
--- a/res/layout/single_clock_view.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-     Copyright (C) 2023 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.wallpaper.picker.FixedWidthDisplayRatioFrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/screen_preview_width"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center"
-    android:clipChildren="false">
-    <com.android.customization.picker.clock.ui.view.ClockHostView
-        android:id="@+id/single_clock_host_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_gravity="center" />
-</com.android.wallpaper.picker.FixedWidthDisplayRatioFrameLayout>
\ No newline at end of file
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 89fac89..7880d82 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockCarouselViewBinder.kt
@@ -16,8 +16,6 @@
 package com.android.customization.picker.clock.ui.binder
 
 import android.content.Context
-import android.view.ViewGroup
-import android.widget.FrameLayout
 import androidx.core.view.isVisible
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleEventObserver
@@ -27,7 +25,6 @@
 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 com.android.wallpaper.picker.customization.ui.section.ScreenPreviewClickView
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
@@ -38,7 +35,6 @@
     fun bind(
         context: Context,
         carouselView: ClockCarouselView,
-        singleClockView: ViewGroup,
         screenPreviewClickView: ScreenPreviewClickView,
         viewModel: ClockCarouselViewModel,
         clockViewFactory: ClockViewFactory,
@@ -46,6 +42,7 @@
         isTwoPaneAndSmallWidth: Boolean,
     ) {
         carouselView.setClockViewFactory(clockViewFactory)
+        carouselView.isVisible = true
         clockViewFactory.updateRegionDarkness()
         val carouselAccessibilityDelegate =
             CarouselAccessibilityDelegate(
@@ -61,12 +58,8 @@
             )
         screenPreviewClickView.accessibilityDelegate = carouselAccessibilityDelegate
 
-        val singleClockHostView =
-            singleClockView.requireViewById<FrameLayout>(R.id.single_clock_host_view)
         lifecycleOwner.lifecycleScope.launch {
             lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
-                launch { viewModel.isCarouselVisible.collect { carouselView.isVisible = it } }
-
                 launch {
                     combine(viewModel.selectedClockSize, viewModel.allClocks, ::Pair).collect {
                         (size, allClocks) ->
@@ -98,21 +91,6 @@
                 launch {
                     viewModel.seedColor.collect { clockViewFactory.updateColorForAllClocks(it) }
                 }
-
-                launch {
-                    viewModel.isSingleClockViewVisible.collect { singleClockView.isVisible = it }
-                }
-
-                launch {
-                    viewModel.clockId.collect { clockId ->
-                        singleClockHostView.removeAllViews()
-                        val clockView = clockViewFactory.getLargeView(clockId)
-                        // The clock view might still be attached to an existing parent. Detach
-                        // before adding to another parent.
-                        (clockView.parent as? ViewGroup)?.removeView(clockView)
-                        singleClockHostView.addView(clockView)
-                    }
-                }
             }
         }
 
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 aa6cc91..2d18ab3 100644
--- a/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
+++ b/src/com/android/customization/picker/clock/ui/view/ClockCarouselView.kt
@@ -114,6 +114,7 @@
         }
 
         adapter = ClockCarouselAdapter(clockSize, clocks, clockViewFactory, onClockSelected)
+        carousel.isInfinite = clocks.size >= MIN_CLOCKS_TO_ENABLE_INFINITE_CAROUSEL
         carousel.setAdapter(adapter)
         val indexOfSelectedClock =
             clocks
@@ -470,6 +471,8 @@
     }
 
     companion object {
+        // The carousel needs to have at least 5 different clock faces to be infinite
+        const val MIN_CLOCKS_TO_ENABLE_INFINITE_CAROUSEL = 5
         const val CLOCK_CAROUSEL_VIEW_SCALE = 0.5f
 
         val itemViewIds =
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 7922054..b2dfa61 100644
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModel.kt
@@ -27,7 +27,6 @@
 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
@@ -39,8 +38,7 @@
  * 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
-constructor(
+class ClockCarouselViewModel(
     private val interactor: ClockPickerInteractor,
     private val backgroundDispatcher: CoroutineDispatcher,
 ) : ViewModel() {
@@ -58,8 +56,6 @@
 
     val seedColor: Flow<Int?> = interactor.seedColor
 
-    val isCarouselVisible: Flow<Boolean> = allClocks.map { it.size > 1 }.distinctUntilChanged()
-
     @OptIn(ExperimentalCoroutinesApi::class)
     val selectedIndex: Flow<Int> =
         allClocks
@@ -77,15 +73,6 @@
             }
             .mapNotNull { it }
 
-    // Handle the case when there is only one clock in the carousel
-    val isSingleClockViewVisible: Flow<Boolean> =
-        allClocks.map { it.size == 1 }.distinctUntilChanged()
-
-    val clockId: Flow<String> =
-        allClocks
-            .map { allClockIds -> if (allClockIds.size == 1) allClockIds[0].clockId else null }
-            .mapNotNull { it }
-
     private var setSelectedClockJob: Job? = null
     fun setSelectedClock(clockId: String) {
         setSelectedClockJob?.cancel()
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 fa87029..2174ba7 100644
--- a/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
+++ b/src/com/android/customization/picker/preview/ui/section/PreviewWithClockCarouselSectionController.kt
@@ -23,7 +23,6 @@
 import android.view.TouchDelegate
 import android.view.View
 import android.view.View.OnAttachStateChangeListener
-import android.view.ViewGroup
 import android.view.ViewStub
 import androidx.activity.ComponentActivity
 import androidx.constraintlayout.helper.widget.Carousel
@@ -156,12 +155,6 @@
                 guidelineEnd.layoutParams = layoutParams
             }
 
-            // TODO (b/270716937) We should handle the single clock case in the clock carousel
-            // itself
-            val singleClockViewStub: ViewStub = view.requireViewById(R.id.single_clock_view_stub)
-            singleClockViewStub.layoutResource = R.layout.single_clock_view
-            val singleClockView = singleClockViewStub.inflate() as ViewGroup
-
             /**
              * Only bind after [Carousel.onAttachedToWindow]. This is to avoid the race condition
              * that the flow emits before attached to window where [Carousel.mMotionLayout] is still
@@ -177,7 +170,6 @@
                                 ClockCarouselViewBinder.bind(
                                     context = context,
                                     carouselView = carouselView,
-                                    singleClockView = singleClockView,
                                     screenPreviewClickView = screenPreviewClickView,
                                     viewModel = viewModel,
                                     clockViewFactory = clockViewFactory,
diff --git a/tests/robotests/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModelTest.kt b/tests/robotests/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModelTest.kt
index 1b1eb9a..ca6f8c7 100644
--- a/tests/robotests/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModelTest.kt
+++ b/tests/robotests/src/com/android/customization/picker/clock/ui/viewmodel/ClockCarouselViewModelTest.kt
@@ -88,38 +88,6 @@
         assertThat(observedSelectedIndex()).isEqualTo(2)
     }
 
-    @Test
-    fun multipleClockCase() = runTest {
-        underTest =
-            ClockCarouselViewModel(
-                getClockPickerInteractor(repositoryWithMultipleClocks),
-                testDispatcher
-            )
-        val observedIsCarouselVisible = collectLastValue(underTest.isCarouselVisible)
-        val observedIsSingleClockViewVisible = collectLastValue(underTest.isSingleClockViewVisible)
-
-        advanceTimeBy(ClockCarouselViewModel.CLOCKS_EVENT_UPDATE_DELAY_MILLIS)
-
-        assertThat(observedIsCarouselVisible()).isTrue()
-        assertThat(observedIsSingleClockViewVisible()).isFalse()
-    }
-
-    @Test
-    fun singleClockCase() = runTest {
-        underTest =
-            ClockCarouselViewModel(
-                getClockPickerInteractor(repositoryWithSingleClock),
-                testDispatcher
-            )
-        val observedIsCarouselVisible = collectLastValue(underTest.isCarouselVisible)
-        val observedIsSingleClockViewVisible = collectLastValue(underTest.isSingleClockViewVisible)
-
-        advanceTimeBy(ClockCarouselViewModel.CLOCKS_EVENT_UPDATE_DELAY_MILLIS)
-
-        assertThat(observedIsCarouselVisible()).isFalse()
-        assertThat(observedIsSingleClockViewVisible()).isTrue()
-    }
-
     private fun getClockPickerInteractor(repository: ClockPickerRepository): ClockPickerInteractor {
         return ClockPickerInteractor(
                 repository = repository,