Merge "Bind customization option icon colors (2/2)" into main
diff --git a/res/drawable/ic_contrast_inner_part.xml b/res/drawable/ic_contrast_inner_part.xml
new file mode 100644
index 0000000..03613e9
--- /dev/null
+++ b/res/drawable/ic_contrast_inner_part.xml
@@ -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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="48dp"
+    android:height="48dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/system_accent1_600"
+      android:pathData="M12,11.5m-2.7,0a2.7,2.7 0,1 1,5.4 0a2.7,2.7 0,1 1,-5.4 0"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_contrast_outer_part.xml b/res/drawable/ic_contrast_outer_part.xml
new file mode 100644
index 0000000..70aa6f2
--- /dev/null
+++ b/res/drawable/ic_contrast_outer_part.xml
@@ -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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="48dp"
+    android:height="48dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/system_accent1_100"
+      android:pathData="M12,4C7,4 2.73,7.11 1,11.5C2.73,15.89 7,19 12,19s9.27,-3.11 11,-7.5C21.27,7.11 17,4 12,4zM12,16c-2.48,0 -4.5,-2.02 -4.5,-4.5S9.52,7 12,7s4.5,2.02 4.5,4.5S14.48,16 12,16z"/>
+</vector>
\ No newline at end of file
diff --git a/res/layout/customization_option_entry_color_contrast.xml b/res/layout/customization_option_entry_color_contrast.xml
index e70acc1..a6b50a6 100644
--- a/res/layout/customization_option_entry_color_contrast.xml
+++ b/res/layout/customization_option_entry_color_contrast.xml
@@ -55,12 +55,21 @@
         android:background="@drawable/customization_option_entry_icon_background"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent">
+        app:layout_constraintBottom_toBottomOf="parent"
+        android:importantForAccessibility="noHideDescendants" >
 
         <ImageView
-            android:id="@+id/option_entry_icon"
+            android:id="@+id/option_entry_icon_inner_part"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:contentDescription="@string/grid_preview_card_content_description" />
+            android:src="@drawable/ic_contrast_inner_part"
+            android:importantForAccessibility="no" />
+
+        <ImageView
+            android:id="@+id/option_entry_icon_outer_part"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:src="@drawable/ic_contrast_outer_part"
+            android:importantForAccessibility="no" />
     </FrameLayout>
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/src/com/android/customization/picker/settings/ui/binder/ColorContrastSectionViewBinder2.kt b/src/com/android/customization/picker/settings/ui/binder/ColorContrastSectionViewBinder2.kt
new file mode 100644
index 0000000..65f047d
--- /dev/null
+++ b/src/com/android/customization/picker/settings/ui/binder/ColorContrastSectionViewBinder2.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.customization.picker.settings.ui.binder
+
+import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_HIGH
+import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_MEDIUM
+import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_STANDARD
+import android.util.Log
+import android.view.View
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.core.view.isVisible
+import androidx.lifecycle.LifecycleOwner
+import com.android.themepicker.R
+import com.android.wallpaper.picker.common.text.ui.viewbinder.TextViewBinder
+import com.android.wallpaper.picker.common.text.ui.viewmodel.Text
+import com.android.wallpaper.picker.customization.ui.binder.ColorUpdateBinder
+import com.android.wallpaper.picker.customization.ui.viewmodel.ColorUpdateViewModel
+
+object ColorContrastSectionViewBinder2 {
+
+    private const val TAG = "ColorContrastSectionViewBinder2"
+
+    interface Binding {
+        /** Destroys the binding in spite of lifecycle state. */
+        fun destroy()
+    }
+
+    fun bind(
+        view: View,
+        contrast: Int,
+        colorUpdateViewModel: ColorUpdateViewModel,
+        shouldAnimateColor: () -> Boolean,
+        lifecycleOwner: LifecycleOwner,
+    ): Binding {
+
+        val descriptionView: TextView = view.requireViewById(R.id.option_entry_description)
+        val iconInner: ImageView = view.requireViewById(R.id.option_entry_icon_inner_part)
+        val iconOuter: ImageView = view.requireViewById(R.id.option_entry_icon_outer_part)
+
+        // Bind outer and inner parts of the contrast icon separately. Use the same material color
+        // tokens despite contrast level because the tokens adjust according to contrast thanks to
+        // dynamic color magic.
+        val bindingOuter: ColorUpdateBinder.Binding =
+            ColorUpdateBinder.bind(
+                setColor = { color -> iconOuter.setColorFilter(color) },
+                color = colorUpdateViewModel.colorPrimaryContainer,
+                shouldAnimate = shouldAnimateColor,
+                lifecycleOwner = lifecycleOwner,
+            )
+        val bindingInner: ColorUpdateBinder.Binding =
+            ColorUpdateBinder.bind(
+                setColor = { color -> iconInner.setColorFilter(color) },
+                color = colorUpdateViewModel.colorPrimary,
+                shouldAnimate = shouldAnimateColor,
+                lifecycleOwner = lifecycleOwner,
+            )
+
+        TextViewBinder.bind(
+            view = descriptionView,
+            viewModel =
+                when (contrast) {
+                    CONTRAST_LEVEL_STANDARD -> Text.Resource(R.string.color_contrast_default_title)
+                    CONTRAST_LEVEL_MEDIUM -> Text.Resource(R.string.color_contrast_medium_title)
+                    CONTRAST_LEVEL_HIGH -> Text.Resource(R.string.color_contrast_high_title)
+                    else -> {
+                        iconInner.isVisible = false
+                        iconOuter.isVisible = false
+                        Log.e(TAG, "Invalid contrast value: $contrast")
+                        throw IllegalArgumentException("Invalid contrast value: $contrast")
+                    }
+                },
+        )
+
+        return object : Binding {
+            override fun destroy() {
+                bindingInner.destroy()
+                bindingOuter.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..fff67e0 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt
@@ -23,7 +23,6 @@
 import android.widget.ImageView
 import android.widget.TextView
 import androidx.constraintlayout.widget.ConstraintSet
-import androidx.core.content.ContextCompat
 import androidx.core.graphics.drawable.DrawableCompat
 import androidx.core.view.isVisible
 import androidx.lifecycle.Lifecycle
@@ -39,6 +38,7 @@
 import com.android.customization.picker.color.ui.view.ColorOptionIconView2
 import com.android.customization.picker.color.ui.viewmodel.ColorOptionIconViewModel
 import com.android.customization.picker.grid.ui.binder.GridIconViewBinder
+import com.android.customization.picker.settings.ui.binder.ColorContrastSectionViewBinder2
 import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.ClockPreviewConfig
 import com.android.systemui.shared.Flags
@@ -195,10 +195,6 @@
                 .first { it.first == ThemePickerHomeCustomizationOption.COLOR_CONTRAST }
                 .second
         optionColorContrast.setOnClickListener { navigateToColorContrastSettingsActivity.invoke() }
-        val optionColorContrastDescription: TextView =
-            optionColorContrast.requireViewById(R.id.option_entry_description)
-        val optionColorContrastIcon: ImageView =
-            optionColorContrast.requireViewById(R.id.option_entry_icon)
 
         val optionThemedIcons =
             homeScreenCustomizationOptionEntries
@@ -207,6 +203,18 @@
         val optionThemedIconsSwitch =
             optionThemedIcons?.findViewById<MaterialSwitch>(R.id.option_entry_switch)
 
+        ColorUpdateBinder.bind(
+            setColor = { color ->
+                optionClockIcon.setColorFilter(color)
+                optionShortcutIcon1.setColorFilter(color)
+                optionShortcutIcon2.setColorFilter(color)
+                optionShapeGridIcon.setColorFilter(color)
+            },
+            color = colorUpdateViewModel.colorOnSurfaceVariant,
+            shouldAnimate = isOnMainScreen,
+            lifecycleOwner = lifecycleOwner,
+        )
+
         lifecycleOwner.lifecycleScope.launch {
             lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                 launch {
@@ -270,28 +278,23 @@
                                 view = optionShapeGridIcon,
                                 viewModel = gridIconViewModel,
                             )
-                            // TODO(b/363018910): Use ColorUpdateBinder to update color
-                            optionShapeGridIcon.setColorFilter(
-                                ContextCompat.getColor(
-                                    view.context,
-                                    com.android.wallpaper.R.color.system_on_surface_variant,
-                                )
-                            )
                         }
                     }
                 }
 
                 launch {
-                    optionsViewModel.colorContrastSectionViewModel.summary.collectLatest { summary
+                    var binding: ColorContrastSectionViewBinder2.Binding? = null
+                    optionsViewModel.colorContrastSectionViewModel.contrast.collectLatest { contrast
                         ->
-                        TextViewBinder.bind(
-                            view = optionColorContrastDescription,
-                            viewModel = summary.description,
-                        )
-                        summary.icon?.let {
-                            IconViewBinder.bind(view = optionColorContrastIcon, viewModel = it)
-                        }
-                        optionColorContrastIcon.isVisible = summary.icon != null
+                        binding?.destroy()
+                        binding =
+                            ColorContrastSectionViewBinder2.bind(
+                                view = optionColorContrast,
+                                contrast = contrast,
+                                colorUpdateViewModel = colorUpdateViewModel,
+                                shouldAnimateColor = isOnMainScreen,
+                                lifecycleOwner = lifecycleOwner,
+                            )
                     }
                 }
 
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ColorContrastSectionViewModel2.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ColorContrastSectionViewModel2.kt
index 7ac0053..5c9d057 100644
--- a/src/com/android/wallpaper/customization/ui/viewmodel/ColorContrastSectionViewModel2.kt
+++ b/src/com/android/wallpaper/customization/ui/viewmodel/ColorContrastSectionViewModel2.kt
@@ -16,57 +16,13 @@
 
 package com.android.wallpaper.customization.ui.viewmodel
 
-import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_HIGH
-import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_MEDIUM
-import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_STANDARD
-import android.util.Log
 import com.android.customization.picker.settings.domain.interactor.ColorContrastSectionInteractor
-import com.android.customization.picker.settings.ui.viewmodel.ColorContrastSectionDataViewModel
-import com.android.themepicker.R
-import com.android.wallpaper.picker.common.icon.ui.viewmodel.Icon
-import com.android.wallpaper.picker.common.text.ui.viewmodel.Text
 import dagger.hilt.android.scopes.ViewModelScoped
 import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.map
 
 @ViewModelScoped
 class ColorContrastSectionViewModel2
 @Inject
 constructor(colorContrastSectionInteractor: ColorContrastSectionInteractor) {
-
-    val summary: Flow<ColorContrastSectionDataViewModel> =
-        colorContrastSectionInteractor.contrast.map { contrastValue ->
-            when (contrastValue) {
-                CONTRAST_LEVEL_STANDARD ->
-                    ColorContrastSectionDataViewModel(
-                        Text.Resource(R.string.color_contrast_default_title),
-                        Icon.Resource(
-                            res = R.drawable.ic_contrast_standard,
-                            contentDescription = null,
-                        ),
-                    )
-                CONTRAST_LEVEL_MEDIUM ->
-                    ColorContrastSectionDataViewModel(
-                        Text.Resource(R.string.color_contrast_medium_title),
-                        Icon.Resource(
-                            res = R.drawable.ic_contrast_medium,
-                            contentDescription = null,
-                        ),
-                    )
-                CONTRAST_LEVEL_HIGH ->
-                    ColorContrastSectionDataViewModel(
-                        Text.Resource(R.string.color_contrast_high_title),
-                        Icon.Resource(res = R.drawable.ic_contrast_high, contentDescription = null),
-                    )
-                else -> {
-                    Log.e(TAG, "Invalid contrast value: $contrastValue")
-                    throw IllegalArgumentException("Invalid contrast value: $contrastValue")
-                }
-            }
-        }
-
-    companion object {
-        private const val TAG = "ColorContrastSectionViewModel2"
-    }
+    val contrast = colorContrastSectionInteractor.contrast
 }