Merge "[TP] Clock color picker" into tm-qpr-dev am: c98daa6009 am: 3916a74e39

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

Change-Id: Ic2f23079b8908669029cdb967db01901c4639cff
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/res/layout/fragment_clock_settings.xml b/res/layout/fragment_clock_settings.xml
index 13cec45..696d6eb 100644
--- a/res/layout/fragment_clock_settings.xml
+++ b/res/layout/fragment_clock_settings.xml
@@ -19,11 +19,11 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical">
-
     <FrameLayout
         android:id="@+id/section_header_container"
         android:layout_width="match_parent"
         android:layout_height="wrap_content">
+
         <include layout="@layout/section_header" />
     </FrameLayout>
 
@@ -95,15 +95,34 @@
 
         <FrameLayout
             android:layout_width="match_parent"
-            android:layout_height="wrap_content">
+            android:layout_height="wrap_content"
+            android:paddingTop="16dp">
 
-            <androidx.recyclerview.widget.RecyclerView
-                android:id="@id/affordances"
+            <FrameLayout
+                android:id="@+id/color_options_container"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:clipToPadding="false"
-                android:paddingHorizontal="16dp"
-                android:visibility="gone"/>
+                android:layout_height="wrap_content">
+
+                <androidx.recyclerview.widget.RecyclerView
+                    android:id="@+id/color_options"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:clipToPadding="false"
+                    android:paddingHorizontal="16dp" />
+
+                <!--
+                This is just an invisible placeholder put in place so that the parent keeps its
+                height stable as the RecyclerView updates from 0 items to N items. Keeping it stable
+                allows the layout logic to keep the size of the preview container stable as well,
+                which bodes well for setting up the SurfaceView for remote rendering without
+                changing its size after the content is loaded into the RecyclerView.
+                -->
+                <include
+                    layout="@layout/color_option_with_background"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:visibility="invisible" />
+            </FrameLayout>
 
             <com.android.customization.picker.clock.ui.view.ClockSizeRadioButtonGroup
                 android:id="@+id/clock_size_radio_button_group"
diff --git a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepository.kt b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepository.kt
index c7fa2c5..1904197 100644
--- a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepository.kt
+++ b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepository.kt
@@ -33,5 +33,7 @@
 
     fun setSelectedClock(clockId: String)
 
+    fun setClockColor(color: Int?)
+
     fun setClockSize(size: ClockSize)
 }
diff --git a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
index f6b0514..9e140aa 100644
--- a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
+++ b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
@@ -43,7 +43,7 @@
         registry
             .getClocks()
             .filter { "NOT_IN_USE" !in it.clockId }
-            .map { it.toModel() }
+            .map { it.toModel(null) }
             .toTypedArray()
 
     /** The currently-selected clock. */
@@ -56,7 +56,7 @@
                         registry
                             .getClocks()
                             .find { clockMetadata -> clockMetadata.clockId == currentClockId }
-                            ?.toModel()
+                            ?.toModel(registry.seedColor)
                     if (model == null) {
                         Log.w(
                             TAG,
@@ -77,6 +77,10 @@
         registry.currentClockId = clockId
     }
 
+    override fun setClockColor(color: Int?) {
+        registry.seedColor = color
+    }
+
     // TODO(b/262924055): Use the shared system UI component to query the clock size
     private val _selectedClockSize = MutableStateFlow(ClockSize.DYNAMIC)
     override val selectedClockSize: Flow<ClockSize> = _selectedClockSize.asStateFlow()
@@ -85,8 +89,8 @@
         _selectedClockSize.value = size
     }
 
-    private fun ClockMetadata.toModel(): ClockMetadataModel {
-        return ClockMetadataModel(clockId = clockId, name = name)
+    private fun ClockMetadata.toModel(color: Int?): ClockMetadataModel {
+        return ClockMetadataModel(clockId = clockId, name = name, color = color)
     }
 
     companion object {
diff --git a/src/com/android/customization/picker/clock/domain/interactor/ClockPickerInteractor.kt b/src/com/android/customization/picker/clock/domain/interactor/ClockPickerInteractor.kt
index 627c4a9..846ea21 100644
--- a/src/com/android/customization/picker/clock/domain/interactor/ClockPickerInteractor.kt
+++ b/src/com/android/customization/picker/clock/domain/interactor/ClockPickerInteractor.kt
@@ -21,6 +21,7 @@
 import com.android.customization.picker.clock.shared.ClockSize
 import com.android.customization.picker.clock.shared.model.ClockMetadataModel
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
 
 /**
  * Interactor for accessing application clock settings, as well as selecting and configuring custom
@@ -31,12 +32,18 @@
 
     val selectedClock: Flow<ClockMetadataModel> = repository.selectedClock
 
+    val selectedClockColor: Flow<Int?> = repository.selectedClock.map { clock -> clock.color }
+
     val selectedClockSize: Flow<ClockSize> = repository.selectedClockSize
 
     fun setSelectedClock(clockId: String) {
         repository.setSelectedClock(clockId)
     }
 
+    fun setClockColor(color: Int?) {
+        repository.setClockColor(color)
+    }
+
     fun setClockSize(size: ClockSize) {
         repository.setClockSize(size)
     }
diff --git a/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt b/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt
index ea84d2b..acbc792 100644
--- a/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt
+++ b/src/com/android/customization/picker/clock/shared/model/ClockMetadataModel.kt
@@ -21,4 +21,5 @@
 data class ClockMetadataModel(
     val clockId: String,
     val name: String,
+    val color: Int?,
 )
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 9a94a69..b460bc1 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
@@ -28,6 +28,7 @@
 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.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.launch
@@ -40,6 +41,7 @@
         lifecycleOwner: LifecycleOwner,
     ) {
         val tabView: RecyclerView = view.requireViewById(R.id.tabs)
+        val colorOptionContainer = view.requireViewById<View>(R.id.color_options_container)
         val sizeOptions =
             view.requireViewById<ClockSizeRadioButtonGroup>(R.id.clock_size_radio_button_group)
 
@@ -55,6 +57,13 @@
                 }
             }
 
+        val colorOptionContainerView: RecyclerView = view.requireViewById(R.id.color_options)
+        val colorOptionAdapter = ColorOptionAdapter()
+        colorOptionContainerView.adapter = colorOptionAdapter
+        colorOptionContainerView.layoutManager =
+            LinearLayoutManager(view.context, RecyclerView.HORIZONTAL, false)
+        colorOptionContainerView.addItemDecoration(ItemSpacing(ItemSpacing.ITEM_SPACING_DP))
+
         lifecycleOwner.lifecycleScope.launch {
             lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                 launch { viewModel.tabs.collect { tabAdapter.setItems(it) } }
@@ -63,9 +72,11 @@
                     viewModel.selectedTabPosition.collect { tab ->
                         when (tab) {
                             ClockSettingsViewModel.Tab.COLOR -> {
+                                colorOptionContainer.isVisible = true
                                 sizeOptions.isInvisible = true
                             }
                             ClockSettingsViewModel.Tab.SIZE -> {
+                                colorOptionContainer.isInvisible = true
                                 sizeOptions.isVisible = true
                             }
                         }
@@ -73,6 +84,12 @@
                 }
 
                 launch {
+                    viewModel.colorOptions.collect { colorOptions ->
+                        colorOptionAdapter.setItems(colorOptions)
+                    }
+                }
+
+                launch {
                     viewModel.selectedClockSize.collect { size ->
                         when (size) {
                             ClockSize.DYNAMIC -> {
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 8c0925f..11e0273 100644
--- a/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt
+++ b/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsViewModel.kt
@@ -16,8 +16,10 @@
 package com.android.customization.picker.clock.ui.viewmodel
 
 import android.content.Context
+import android.graphics.Color
 import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
 import com.android.customization.picker.clock.shared.ClockSize
+import com.android.customization.picker.color.ui.viewmodel.ColorOptionViewModel
 import com.android.wallpaper.R
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -33,6 +35,47 @@
         SIZE,
     }
 
+    val colorOptions: Flow<List<ColorOptionViewModel>> =
+        interactor.selectedClockColor.map { selectedColor ->
+            buildList {
+                // TODO (b/241966062) Change design of the placeholder for default theme color
+                add(
+                    ColorOptionViewModel(
+                        color0 = Color.TRANSPARENT,
+                        color1 = Color.TRANSPARENT,
+                        color2 = Color.TRANSPARENT,
+                        color3 = Color.TRANSPARENT,
+                        contentDescription = "description",
+                        isSelected = selectedColor == null,
+                        onClick =
+                            if (selectedColor == null) {
+                                null
+                            } else {
+                                { interactor.setClockColor(null) }
+                            },
+                    )
+                )
+                COLOR_LIST.forEach { color ->
+                    add(
+                        ColorOptionViewModel(
+                            color0 = color,
+                            color1 = color,
+                            color2 = color,
+                            color3 = color,
+                            contentDescription = "description",
+                            isSelected = selectedColor == color,
+                            onClick =
+                                if (selectedColor == color) {
+                                    null
+                                } else {
+                                    { interactor.setClockColor(color) }
+                                },
+                        )
+                    )
+                }
+            }
+        }
+
     val selectedClockSize: Flow<ClockSize> = interactor.selectedClockSize
 
     fun setClockSize(size: ClockSize) {
@@ -66,4 +109,10 @@
                 ),
             )
         }
+
+    companion object {
+        // TODO (b/241966062) The color integers here are temporary for dev purposes. We need to
+        //                    finalize the overridden colors.
+        val COLOR_LIST = listOf(-2563329, -8775, -1777665, -5442872)
+    }
 }
diff --git a/src/com/android/customization/picker/common/ui/view/ItemSpacing.kt b/src/com/android/customization/picker/common/ui/view/ItemSpacing.kt
index cbf9e97..ca689aa 100644
--- a/src/com/android/customization/picker/common/ui/view/ItemSpacing.kt
+++ b/src/com/android/customization/picker/common/ui/view/ItemSpacing.kt
@@ -44,6 +44,6 @@
 
     companion object {
         const val TAB_ITEM_SPACING_DP = 12
-        const val AFFORDANCE_ITEM_SPACING_DP = 8
+        const val ITEM_SPACING_DP = 8
     }
 }
diff --git a/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt b/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt
index f03fda3..af5cd13 100644
--- a/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt
@@ -66,7 +66,7 @@
         affordancesView.adapter = affordancesAdapter
         affordancesView.layoutManager =
             LinearLayoutManager(view.context, RecyclerView.HORIZONTAL, false)
-        affordancesView.addItemDecoration(ItemSpacing(ItemSpacing.AFFORDANCE_ITEM_SPACING_DP))
+        affordancesView.addItemDecoration(ItemSpacing(ItemSpacing.ITEM_SPACING_DP))
 
         var dialog: Dialog? = null
 
diff --git a/tests/src/com/android/customization/picker/clock/data/repository/FakeClockPickerRepository.kt b/tests/src/com/android/customization/picker/clock/data/repository/FakeClockPickerRepository.kt
index af045d5..c201527 100644
--- a/tests/src/com/android/customization/picker/clock/data/repository/FakeClockPickerRepository.kt
+++ b/tests/src/com/android/customization/picker/clock/data/repository/FakeClockPickerRepository.kt
@@ -20,21 +20,30 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
 
 class FakeClockPickerRepository : ClockPickerRepository {
 
     override val allClocks: Array<ClockMetadataModel> = fakeClocks
 
-    private val _selectedClock = MutableStateFlow(fakeClocks[0])
-    override val selectedClock: Flow<ClockMetadataModel> = _selectedClock.asStateFlow()
+    private val _selectedClockId = MutableStateFlow(fakeClocks[0].clockId)
+    private val _clockColor = MutableStateFlow<Int?>(null)
+    override val selectedClock: Flow<ClockMetadataModel> =
+        combine(_selectedClockId, _clockColor) { selectedClockId, clockColor ->
+            val selectedClock = allClocks.find { clock -> clock.clockId == selectedClockId }
+            checkNotNull(selectedClock)
+            ClockMetadataModel(selectedClock.clockId, selectedClock.name, clockColor)
+        }
 
     private val _selectedClockSize = MutableStateFlow(ClockSize.LARGE)
     override val selectedClockSize: Flow<ClockSize> = _selectedClockSize.asStateFlow()
 
     override fun setSelectedClock(clockId: String) {
-        val clock = fakeClocks.find { it.clockId == clockId }
-        checkNotNull(clock)
-        _selectedClock.value = clock
+        _selectedClockId.value = clockId
+    }
+
+    override fun setClockColor(color: Int?) {
+        _clockColor.value = color
     }
 
     override fun setClockSize(size: ClockSize) {
@@ -44,10 +53,10 @@
     companion object {
         val fakeClocks =
             arrayOf(
-                ClockMetadataModel("clock0", "clock0"),
-                ClockMetadataModel("clock1", "clock1"),
-                ClockMetadataModel("clock2", "clock2"),
-                ClockMetadataModel("clock3", "clock3"),
+                ClockMetadataModel("clock0", "clock0", null),
+                ClockMetadataModel("clock1", "clock1", null),
+                ClockMetadataModel("clock2", "clock2", null),
+                ClockMetadataModel("clock3", "clock3", null),
             )
     }
 }
diff --git a/tests/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsTabViewModelTest.kt b/tests/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsTabViewModelTest.kt
index 63bdd7b..a484027 100644
--- a/tests/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsTabViewModelTest.kt
+++ b/tests/src/com/android/customization/picker/clock/ui/viewmodel/ClockSettingsTabViewModelTest.kt
@@ -44,6 +44,17 @@
     }
 
     @Test
+    fun setClockColor() = runTest {
+        val observedClockColorOptions = collectLastValue(underTest.colorOptions)
+        assertThat(observedClockColorOptions()!![0].isSelected).isTrue()
+        assertThat(observedClockColorOptions()!![0].onClick).isNull()
+
+        observedClockColorOptions()!![1].onClick?.invoke()
+        assertThat(observedClockColorOptions()!![1].isSelected).isTrue()
+        assertThat(observedClockColorOptions()!![1].onClick).isNull()
+    }
+
+    @Test
     fun setClockSize() = runTest {
         val observedClockSize = collectLastValue(underTest.selectedClockSize)
         underTest.setClockSize(ClockSize.DYNAMIC)