Bind preset slider

Bind preset slider to enable the clock customization.

Test: Manually tested. See bug.
Bug: 395647577
Flag: com.android.systemui.shared.new_customization_picker_ui
Change-Id: Ia7ccf6f08b428146977735c136e145914dd05876
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 e810c8a..67926e1 100644
--- a/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
+++ b/src/com/android/customization/picker/clock/data/repository/ClockPickerRepositoryImpl.kt
@@ -87,6 +87,10 @@
 
                 val listener =
                     object : ClockRegistry.ClockChangeListener {
+                        override fun onCurrentClockChanged() {
+                            send()
+                        }
+
                         override fun onAvailableClocksChanged() {
                             send()
                         }
diff --git a/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
index d141f30..275b25a 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
@@ -55,6 +55,7 @@
 import com.google.android.material.materialswitch.MaterialSwitch
 import com.google.android.material.slider.LabelFormatter
 import com.google.android.material.slider.Slider
+import com.google.android.material.slider.Slider.OnSliderTouchListener
 import java.lang.ref.WeakReference
 import kotlin.math.roundToInt
 import kotlinx.coroutines.DisposableHandle
@@ -414,6 +415,31 @@
                         }
                     }
                 }
+
+                launch {
+                    viewModel.axisPresetsSliderViewModel.collect {
+                        val axisPresetsSliderViewModel = it ?: return@collect
+                        axisPresetSlider.valueFrom = axisPresetsSliderViewModel.valueFrom
+                        axisPresetSlider.valueTo = axisPresetsSliderViewModel.valueTo
+                        axisPresetSlider.stepSize = axisPresetsSliderViewModel.stepSize
+                        axisPresetSlider.clearOnSliderTouchListeners()
+                        axisPresetSlider.addOnSliderTouchListener(
+                            object : OnSliderTouchListener {
+                                override fun onStartTrackingTouch(slider: Slider) {}
+
+                                override fun onStopTrackingTouch(slider: Slider) {
+                                    axisPresetsSliderViewModel.onSliderStopTrackingTouch(
+                                        slider.value
+                                    )
+                                }
+                            }
+                        )
+                    }
+                }
+
+                launch {
+                    viewModel.axisPresetsSliderSelectedValue.collect { axisPresetSlider.value = it }
+                }
             }
         }
     }
diff --git a/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt
index 5736e0e..e008da2 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt
@@ -486,14 +486,17 @@
                     combine(
                             clockPickerViewModel.previewingSeedColor,
                             clockPickerViewModel.previewingClock,
-                            clockPickerViewModel.previewingClockFontAxisMap,
+                            clockPickerViewModel.previewingClockPresetIndexedStyle,
                             colorUpdateViewModel.systemColorsUpdated,
                             ::Quadruple,
                         )
                         .collect { quadruple ->
-                            val (color, clock, axisMap, _) = quadruple
+                            val (color, clock, clockPresetIndexedStyle, _) = quadruple
                             clockViewFactory.updateColor(clock.clockId, color)
-                            clockViewFactory.updateFontAxes(clock.clockId, ClockAxisStyle(axisMap))
+                            clockViewFactory.updateFontAxes(
+                                clock.clockId,
+                                clockPresetIndexedStyle?.style ?: ClockAxisStyle(),
+                            )
                         }
                 }
 
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ClockAxisPresetSliderViewModel.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ClockAxisPresetSliderViewModel.kt
new file mode 100644
index 0000000..d11044a
--- /dev/null
+++ b/src/com/android/wallpaper/customization/ui/viewmodel/ClockAxisPresetSliderViewModel.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+package com.android.wallpaper.customization.ui.viewmodel
+
+/** Data class representing [com.google.android.material.slider]'s configuration. */
+data class ClockAxisPresetSliderViewModel(
+    val valueFrom: Float,
+    val valueTo: Float,
+    val stepSize: Float,
+    val onSliderStopTrackingTouch: (value: Float) -> Unit,
+)
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModel.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModel.kt
index cd2183a..cff9e08 100644
--- a/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModel.kt
+++ b/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModel.kt
@@ -28,6 +28,8 @@
 import com.android.customization.picker.clock.ui.viewmodel.ClockColorViewModel
 import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor2
 import com.android.customization.picker.color.ui.viewmodel.ColorOptionIconViewModel
+import com.android.systemui.plugins.clocks.AxisPresetConfig
+import com.android.systemui.plugins.clocks.AxisPresetConfig.IndexedStyle
 import com.android.systemui.plugins.clocks.ClockAxisStyle
 import com.android.themepicker.R
 import com.android.wallpaper.picker.common.icon.ui.viewmodel.Icon
@@ -40,6 +42,7 @@
 import dagger.assisted.AssistedInject
 import dagger.hilt.android.qualifiers.ApplicationContext
 import dagger.hilt.android.scopes.ViewModelScoped
+import kotlin.math.roundToInt
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -51,6 +54,7 @@
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
@@ -131,18 +135,16 @@
 
     // Clock style
     private val overridingClock = MutableStateFlow<ClockMetadataModel?>(null)
-    private val isClockEdited =
-        combine(overridingClock, clockPickerInteractor.selectedClock) {
-            overridingClock,
-            selectedClock ->
-            overridingClock != null && overridingClock.clockId != selectedClock.clockId
-        }
     val selectedClock = clockPickerInteractor.selectedClock
     val previewingClock =
         combine(overridingClock, selectedClock) { overridingClock, selectedClock ->
                 (overridingClock ?: selectedClock)
             }
             .shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
+    private val isClockEdited =
+        combine(overridingClock, selectedClock) { overridingClock, selectedClock ->
+            overridingClock != null && overridingClock.clockId != selectedClock.clockId
+        }
 
     suspend fun getIsShadeLayoutWide() = clockPickerInteractor.getIsShadeLayoutWide()
 
@@ -168,7 +170,81 @@
             .flowOn(backgroundDispatcher.limitedParallelism(1))
             .stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
 
+    // Clock font presets
+    private val overridingClockPresetIndexedStyle: MutableStateFlow<IndexedStyle?> =
+        MutableStateFlow(null)
+    private val selectedClockPresetIndexedStyle: Flow<IndexedStyle?> =
+        previewingClock
+            .map { it.axisPresetConfig?.current }
+            .shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
+    val previewingClockPresetIndexedStyle: Flow<IndexedStyle?> =
+        combine(overridingClockPresetIndexedStyle, selectedClockPresetIndexedStyle) {
+            overridingClockPresetIndexedStyle,
+            selectedClockPresetIndexedStyle ->
+            overridingClockPresetIndexedStyle ?: selectedClockPresetIndexedStyle
+        }
+    private val isClockAxisStyleEdited: Flow<Boolean> =
+        combine(overridingClockPresetIndexedStyle, selectedClockPresetIndexedStyle) {
+            overridingClockPresetIndexedStyle,
+            selectedClockPresetIndexedStyle ->
+            overridingClockPresetIndexedStyle != null &&
+                (overridingClockPresetIndexedStyle.style != selectedClockPresetIndexedStyle?.style)
+        }
+
+    private val groups: Flow<List<AxisPresetConfig.Group>?> =
+        previewingClock.map { it.axisPresetConfig?.groups }
+    private val previewingClockPresetGroupIndex: Flow<Int> =
+        previewingClockPresetIndexedStyle.map { it?.groupIndex ?: 0 }.distinctUntilChanged()
     val shouldShowPresetSlider: Flow<Boolean> = previewingClock.map { it.axisPresetConfig != null }
+    val axisPresetsSliderViewModel: Flow<ClockAxisPresetSliderViewModel?> =
+        combine(groups, previewingClockPresetGroupIndex) { groups, previewingClockPresetGroupIndex
+            ->
+            if (groups.isNullOrEmpty()) {
+                null
+            } else {
+                val group = groups[previewingClockPresetGroupIndex]
+                ClockAxisPresetSliderViewModel(
+                    valueFrom = 0F,
+                    valueTo = (group.presets.size - 1).toFloat(),
+                    stepSize = 1F,
+                    onSliderStopTrackingTouch = { value ->
+                        val presetIndex = value.roundToInt()
+                        overridingClockPresetIndexedStyle.value =
+                            IndexedStyle(
+                                groupIndex = previewingClockPresetGroupIndex,
+                                presetIndex = presetIndex,
+                                style = group.presets[presetIndex],
+                            )
+                    },
+                )
+            }
+        }
+    val axisPresetsSliderSelectedValue: Flow<Float> =
+        previewingClockPresetIndexedStyle.map { it?.presetIndex?.toFloat() }.filterNotNull()
+    val onClockFaceClicked: Flow<() -> Unit> =
+        combine(groups, previewingClockPresetIndexedStyle) { groups, previewingIndexedStyle ->
+            if (groups.isNullOrEmpty()) {
+                {}
+            } else {
+                val groupCount = groups.size
+                if (groupCount == 1) {
+                    {}
+                } else {
+                    val currentGroupIndex = previewingIndexedStyle?.groupIndex ?: 0
+                    val nextGroupIndex = (currentGroupIndex + 1) % groupCount
+                    val nextPresetIndex = previewingIndexedStyle?.presetIndex ?: (groupCount / 2)
+                    val nextGroup = groups[nextGroupIndex]
+                    {
+                        overridingClockPresetIndexedStyle.value =
+                            IndexedStyle(
+                                groupIndex = nextGroupIndex,
+                                presetIndex = nextPresetIndex,
+                                style = nextGroup.presets[nextPresetIndex],
+                            )
+                    }
+                }
+            }
+        }
 
     private suspend fun ClockMetadataModel.toOption(
         resources: Resources
@@ -195,44 +271,6 @@
         )
     }
 
-    // Clock font axis
-    private val overrideClockFontAxisMap = MutableStateFlow<Map<String, Float>>(emptyMap())
-    val previewingClockFontAxes =
-        previewingClock
-            .map { clock -> clock.fontAxes }
-            .shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
-    private val selectedClockFontAxisMap =
-        selectedClock
-            .map { clock -> clock.fontAxes.associate { it.key to it.currentValue } }
-            .shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
-    private val isFontAxisMapEdited =
-        combine(overrideClockFontAxisMap, selectedClockFontAxisMap) {
-            overrideClockFontAxisMap,
-            selectedClockFontAxisMap ->
-            !overrideClockFontAxisMap.all { (key, value) -> selectedClockFontAxisMap[key] == value }
-        }
-    val previewingClockFontAxisMap =
-        combine(overrideClockFontAxisMap, selectedClockFontAxisMap) {
-                overrideAxisMap,
-                selectedAxisMap ->
-                if (overrideAxisMap.isEmpty()) {
-                    selectedAxisMap
-                } else {
-                    overrideAxisMap.let {
-                        val mutableMap = selectedAxisMap.toMutableMap()
-                        overrideAxisMap.forEach { (key, value) -> mutableMap[key] = value }
-                        mutableMap.toMap()
-                    }
-                }
-            }
-            .stateIn(viewModelScope, SharingStarted.Eagerly, emptyMap())
-
-    fun updatePreviewFontAxis(key: String, value: Float) {
-        val axisMap = overrideClockFontAxisMap.value.toMutableMap()
-        axisMap[key] = value
-        overrideClockFontAxisMap.value = axisMap.toMap()
-    }
-
     // Clock size
     private val overridingClockSize = MutableStateFlow<ClockSize?>(null)
     private val isClockSizeEdited =
@@ -416,18 +454,18 @@
     private val isEdited =
         combine(
             isClockEdited,
-            isFontAxisMapEdited,
+            isClockAxisStyleEdited,
             isClockSizeEdited,
             isClockColorIdEdited,
             isSliderProgressEdited,
         ) {
             isClockEdited,
-            isFontAxisMapEdited,
+            isClockAxisStyleEdited,
             isClockSizeEdited,
             isClockColorEdited,
             isSliderProgressEdited ->
             isClockEdited ||
-                isFontAxisMapEdited ||
+                isClockAxisStyleEdited ||
                 isClockSizeEdited ||
                 isClockColorEdited ||
                 isSliderProgressEdited
@@ -441,7 +479,7 @@
             previewingClockSize,
             previewingClockColorId,
             previewingSliderProgress,
-            previewingClockFontAxisMap,
+            previewingClockPresetIndexedStyle,
         ) { array ->
             val onApplyClicked: Boolean = array[0] as Boolean
             val isEdited: Boolean = array[1] as Boolean
@@ -449,7 +487,8 @@
             val size: ClockSize = array[3] as ClockSize
             val previewingColorId: String = array[4] as String
             val previewProgress: Int = array[5] as Int
-            val axisMap: Map<String, Float> = array[6] as Map<String, Float>
+            val clockAxisStyle: ClockAxisStyle =
+                (array[6] as? IndexedStyle)?.style ?: ClockAxisStyle()
             if (isEdited && !onApplyClicked) {
                 {
                     this.onApplyClicked.value = true
@@ -465,7 +504,7 @@
                                     colorTone = it.getColorTone(previewProgress),
                                 )
                             },
-                        axisSettings = ClockAxisStyle(axisMap),
+                        axisSettings = clockAxisStyle,
                     )
                 }
             } else {
@@ -478,7 +517,7 @@
         overridingClockSize.value = null
         overridingClockColorId.value = null
         overridingSliderProgress.value = null
-        overrideClockFontAxisMap.value = emptyMap()
+        overridingClockPresetIndexedStyle.value = null
         _selectedTab.value = Tab.STYLE
         onApplyClicked.value = false
     }