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
}