Bind switch color (2/2)
Create SwitchColorBinder for binding switch color and use it to bind
switches across the picker.
Flag: com.android.systemui.shared.new_customization_picker_ui
Test: manually verified by applying new system color
Bug: 363018910
Change-Id: I2c577684dfb2e21be63d21c41df3cdaea676845f
diff --git a/src/com/android/customization/picker/mode/ui/binder/DarkModeBinder.kt b/src/com/android/customization/picker/mode/ui/binder/DarkModeBinder.kt
index ef9e662..9e9db7a 100644
--- a/src/com/android/customization/picker/mode/ui/binder/DarkModeBinder.kt
+++ b/src/com/android/customization/picker/mode/ui/binder/DarkModeBinder.kt
@@ -21,6 +21,8 @@
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.customization.picker.mode.ui.viewmodel.DarkModeViewModel
+import com.android.wallpaper.customization.ui.binder.SwitchColorBinder
+import com.android.wallpaper.picker.customization.ui.viewmodel.ColorUpdateViewModel
import com.google.android.material.materialswitch.MaterialSwitch
import kotlinx.coroutines.launch
@@ -28,12 +30,28 @@
fun bind(
darkModeToggle: MaterialSwitch,
viewModel: DarkModeViewModel,
+ colorUpdateViewModel: ColorUpdateViewModel,
+ shouldAnimateColor: () -> Boolean,
lifecycleOwner: LifecycleOwner,
) {
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
launch { viewModel.isEnabled.collect { darkModeToggle.isEnabled = it } }
- launch { viewModel.previewingIsDarkMode.collect { darkModeToggle.isChecked = it } }
+ launch {
+ var binding: SwitchColorBinder.Binding? = null
+ viewModel.previewingIsDarkMode.collect {
+ darkModeToggle.isChecked = it
+ binding?.destroy()
+ binding =
+ SwitchColorBinder.bind(
+ switch = darkModeToggle,
+ isChecked = it,
+ colorUpdateViewModel = colorUpdateViewModel,
+ shouldAnimateColor = shouldAnimateColor,
+ lifecycleOwner = lifecycleOwner,
+ )
+ }
+ }
launch {
viewModel.toggleDarkMode.collect {
darkModeToggle.setOnCheckedChangeListener { _, _ -> it.invoke() }
diff --git a/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
index 3196257..480ae11 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
@@ -325,11 +325,25 @@
}
launch {
+ var binding: SwitchColorBinder.Binding? = null
viewModel.previewingClockSize.collect { size ->
when (size) {
ClockSize.DYNAMIC -> clockSizeSwitch.isChecked = true
ClockSize.SMALL -> clockSizeSwitch.isChecked = false
}
+ binding?.destroy()
+ binding =
+ SwitchColorBinder.bind(
+ switch = clockSizeSwitch,
+ isChecked =
+ when (size) {
+ ClockSize.DYNAMIC -> true
+ ClockSize.SMALL -> false
+ },
+ colorUpdateViewModel = colorUpdateViewModel,
+ shouldAnimateColor = isFloatingSheetActive,
+ lifecycleOwner = lifecycleOwner,
+ )
}
}
@@ -346,6 +360,8 @@
bindClockFontContent(
clockFontContent = clockFontContent,
viewModel = viewModel,
+ colorUpdateViewModel = colorUpdateViewModel,
+ shouldAnimateColor = isFloatingSheetActive,
lifecycleOwner = lifecycleOwner,
)
}
@@ -353,6 +369,8 @@
private fun bindClockFontContent(
clockFontContent: View,
viewModel: ClockPickerViewModel,
+ colorUpdateViewModel: ColorUpdateViewModel,
+ shouldAnimateColor: () -> Boolean,
lifecycleOwner: LifecycleOwner,
) {
val sliderViewList =
@@ -409,9 +427,15 @@
viewHolder.setIsVisible(booleanAxis != null)
booleanAxis?.let {
switchViewMap[it.key] = viewHolder
- viewHolder.initView(booleanAxis) { value ->
- viewModel.updatePreviewFontAxis(booleanAxis.key, value)
- }
+ viewHolder.initView(
+ clockFontAxis = booleanAxis,
+ onFontAxisValueUpdated = { value ->
+ viewModel.updatePreviewFontAxis(booleanAxis.key, value)
+ },
+ colorUpdateViewModel = colorUpdateViewModel,
+ shouldAnimateColor = shouldAnimateColor,
+ lifecycleOwner = lifecycleOwner,
+ )
}
}
}
diff --git a/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt
index a097828..4c6a3e9 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt
@@ -110,6 +110,8 @@
DarkModeBinder.bind(
darkModeToggle = view.findViewById(R.id.dark_mode_toggle),
viewModel = optionsViewModel.darkModeViewModel,
+ colorUpdateViewModel = colorUpdateViewModel,
+ shouldAnimateColor = isFloatingSheetActive,
lifecycleOwner = lifecycleOwner,
)
diff --git a/src/com/android/wallpaper/customization/ui/binder/SwitchColorBinder.kt b/src/com/android/wallpaper/customization/ui/binder/SwitchColorBinder.kt
new file mode 100644
index 0000000..2710916
--- /dev/null
+++ b/src/com/android/wallpaper/customization/ui/binder/SwitchColorBinder.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.binder
+
+import android.content.res.ColorStateList
+import androidx.lifecycle.LifecycleOwner
+import com.android.wallpaper.picker.customization.ui.binder.ColorUpdateBinder
+import com.android.wallpaper.picker.customization.ui.viewmodel.ColorUpdateViewModel
+import com.google.android.material.materialswitch.MaterialSwitch
+
+object SwitchColorBinder {
+
+ private const val COLOR_TRANSPARENT = 0
+
+ interface Binding {
+ /** Destroys the binding in spite of lifecycle state. */
+ fun destroy()
+ }
+
+ /**
+ * Binds the color of a [MaterialSwitch] using [ColorUpdateBinder] according to Material 3
+ * specs.
+ */
+ fun bind(
+ switch: MaterialSwitch,
+ isChecked: Boolean,
+ colorUpdateViewModel: ColorUpdateViewModel,
+ shouldAnimateColor: () -> Boolean,
+ lifecycleOwner: LifecycleOwner,
+ ): Binding {
+ val bindingThumb: ColorUpdateBinder.Binding
+ val bindingTrack: ColorUpdateBinder.Binding
+ if (isChecked) {
+ switch.trackDecorationTintList = ColorStateList.valueOf(COLOR_TRANSPARENT)
+ bindingThumb =
+ ColorUpdateBinder.bind(
+ setColor = { color -> switch.thumbTintList = ColorStateList.valueOf(color) },
+ color = colorUpdateViewModel.colorOnPrimary,
+ shouldAnimate = shouldAnimateColor,
+ lifecycleOwner = lifecycleOwner,
+ )
+ bindingTrack =
+ ColorUpdateBinder.bind(
+ setColor = { color -> switch.trackTintList = ColorStateList.valueOf(color) },
+ color = colorUpdateViewModel.colorPrimary,
+ shouldAnimate = shouldAnimateColor,
+ lifecycleOwner = lifecycleOwner,
+ )
+ } else {
+ bindingThumb =
+ ColorUpdateBinder.bind(
+ setColor = { color ->
+ switch.thumbTintList = ColorStateList.valueOf(color)
+ switch.trackDecorationTintList = ColorStateList.valueOf(color)
+ },
+ color = colorUpdateViewModel.colorOutline,
+ shouldAnimate = shouldAnimateColor,
+ lifecycleOwner = lifecycleOwner,
+ )
+ bindingTrack =
+ ColorUpdateBinder.bind(
+ setColor = { color -> switch.trackTintList = ColorStateList.valueOf(color) },
+ color = colorUpdateViewModel.colorSurfaceContainerHighest,
+ shouldAnimate = shouldAnimateColor,
+ lifecycleOwner = lifecycleOwner,
+ )
+ }
+ return object : Binding {
+ override fun destroy() {
+ bindingThumb.destroy()
+ bindingTrack.destroy()
+ }
+ }
+ }
+}
diff --git a/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt
index d8b14b2..cd65e0c 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt
@@ -202,10 +202,10 @@
val optionThemedIcons =
homeScreenCustomizationOptionEntries
- .find { it.first == ThemePickerHomeCustomizationOption.THEMED_ICONS }
- ?.second
+ .first { it.first == ThemePickerHomeCustomizationOption.THEMED_ICONS }
+ .second
val optionThemedIconsSwitch =
- optionThemedIcons?.findViewById<MaterialSwitch>(R.id.option_entry_switch)
+ optionThemedIcons.requireViewById<MaterialSwitch>(R.id.option_entry_switch)
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -315,24 +315,32 @@
}
}
- if (optionThemedIconsSwitch != null) {
- launch {
- optionsViewModel.themedIconViewModel.isAvailable.collect { isAvailable ->
- optionThemedIconsSwitch.isEnabled = isAvailable
- }
+ launch {
+ optionsViewModel.themedIconViewModel.isAvailable.collect { isAvailable ->
+ optionThemedIconsSwitch.isEnabled = isAvailable
}
+ }
- launch {
- optionsViewModel.themedIconViewModel.isActivated.collect {
- optionThemedIconsSwitch.isChecked = it
- }
+ launch {
+ var binding: SwitchColorBinder.Binding? = null
+ optionsViewModel.themedIconViewModel.isActivated.collect {
+ optionThemedIconsSwitch.isChecked = it
+ binding?.destroy()
+ binding =
+ SwitchColorBinder.bind(
+ switch = optionThemedIconsSwitch,
+ isChecked = it,
+ colorUpdateViewModel = colorUpdateViewModel,
+ shouldAnimateColor = isOnMainScreen,
+ lifecycleOwner = lifecycleOwner,
+ )
}
+ }
- launch {
- optionsViewModel.themedIconViewModel.toggleThemedIcon.collect {
- optionThemedIconsSwitch.setOnCheckedChangeListener { _, _ ->
- launch { it.invoke() }
- }
+ launch {
+ optionsViewModel.themedIconViewModel.toggleThemedIcon.collect {
+ optionThemedIconsSwitch.setOnCheckedChangeListener { _, _ ->
+ launch { it.invoke() }
}
}
}
diff --git a/src/com/android/wallpaper/customization/ui/view/ClockFontSwitchViewHolder.kt b/src/com/android/wallpaper/customization/ui/view/ClockFontSwitchViewHolder.kt
index 22e5dea..fcf70d3 100644
--- a/src/com/android/wallpaper/customization/ui/view/ClockFontSwitchViewHolder.kt
+++ b/src/com/android/wallpaper/customization/ui/view/ClockFontSwitchViewHolder.kt
@@ -18,7 +18,10 @@
import android.widget.TextView
import androidx.core.view.isVisible
+import androidx.lifecycle.LifecycleOwner
import com.android.systemui.plugins.clocks.ClockFontAxis
+import com.android.wallpaper.customization.ui.binder.SwitchColorBinder
+import com.android.wallpaper.picker.customization.ui.viewmodel.ColorUpdateViewModel
import com.google.android.material.materialswitch.MaterialSwitch
import kotlin.math.abs
@@ -31,12 +34,35 @@
switch.isVisible = isVisible
}
- fun initView(clockFontAxis: ClockFontAxis, onFontAxisValueUpdated: (value: Float) -> Unit) {
+ fun initView(
+ clockFontAxis: ClockFontAxis,
+ onFontAxisValueUpdated: (value: Float) -> Unit,
+ colorUpdateViewModel: ColorUpdateViewModel,
+ shouldAnimateColor: () -> Boolean,
+ lifecycleOwner: LifecycleOwner,
+ ) {
switchMaxValue = clockFontAxis.maxValue
name.text = clockFontAxis.name
switch.apply {
isChecked = abs(clockFontAxis.currentValue - clockFontAxis.maxValue) < 0.01f
+ var binding: SwitchColorBinder.Binding =
+ SwitchColorBinder.bind(
+ switch = switch,
+ isChecked = isChecked,
+ colorUpdateViewModel = colorUpdateViewModel,
+ shouldAnimateColor = shouldAnimateColor,
+ lifecycleOwner = lifecycleOwner,
+ )
setOnCheckedChangeListener { v, _ ->
+ binding.destroy()
+ binding =
+ SwitchColorBinder.bind(
+ switch = switch,
+ isChecked = v.isChecked,
+ colorUpdateViewModel = colorUpdateViewModel,
+ shouldAnimateColor = shouldAnimateColor,
+ lifecycleOwner = lifecycleOwner,
+ )
val value = if (v.isChecked) clockFontAxis.maxValue else clockFontAxis.minValue
onFontAxisValueUpdated.invoke(value)
}