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
}