1 /* <lambda>null2 * Copyright (C) 2025 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.customization.picker.settings.ui.binder 18 19 import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_HIGH 20 import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_MEDIUM 21 import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_STANDARD 22 import android.util.Log 23 import android.view.View 24 import android.widget.ImageView 25 import android.widget.TextView 26 import androidx.core.view.isVisible 27 import androidx.lifecycle.LifecycleOwner 28 import com.android.themepicker.R 29 import com.android.wallpaper.picker.common.text.ui.viewbinder.TextViewBinder 30 import com.android.wallpaper.picker.common.text.ui.viewmodel.Text 31 import com.android.wallpaper.picker.customization.ui.binder.ColorUpdateBinder 32 import com.android.wallpaper.picker.customization.ui.viewmodel.ColorUpdateViewModel 33 34 object ColorContrastSectionViewBinder2 { 35 36 private const val TAG = "ColorContrastSectionViewBinder2" 37 38 interface Binding { 39 /** Destroys the binding in spite of lifecycle state. */ 40 fun destroy() 41 } 42 43 fun bind( 44 view: View, 45 contrast: Int, 46 colorUpdateViewModel: ColorUpdateViewModel, 47 shouldAnimateColor: () -> Boolean, 48 lifecycleOwner: LifecycleOwner, 49 ): Binding { 50 51 val descriptionView: TextView = view.requireViewById(R.id.option_entry_description) 52 val iconInner: ImageView = view.requireViewById(R.id.option_entry_icon_inner_part) 53 val iconOuter: ImageView = view.requireViewById(R.id.option_entry_icon_outer_part) 54 55 // Bind outer and inner parts of the contrast icon separately. Use the same material color 56 // tokens despite contrast level because the tokens adjust according to contrast thanks to 57 // dynamic color magic. 58 val bindingOuter: ColorUpdateBinder.Binding = 59 ColorUpdateBinder.bind( 60 setColor = { color -> iconOuter.setColorFilter(color) }, 61 color = colorUpdateViewModel.colorPrimaryContainer, 62 shouldAnimate = shouldAnimateColor, 63 lifecycleOwner = lifecycleOwner, 64 ) 65 val bindingInner: ColorUpdateBinder.Binding = 66 ColorUpdateBinder.bind( 67 setColor = { color -> iconInner.setColorFilter(color) }, 68 color = colorUpdateViewModel.colorPrimary, 69 shouldAnimate = shouldAnimateColor, 70 lifecycleOwner = lifecycleOwner, 71 ) 72 73 TextViewBinder.bind( 74 view = descriptionView, 75 viewModel = 76 when (contrast) { 77 CONTRAST_LEVEL_STANDARD -> Text.Resource(R.string.color_contrast_default_title) 78 CONTRAST_LEVEL_MEDIUM -> Text.Resource(R.string.color_contrast_medium_title) 79 CONTRAST_LEVEL_HIGH -> Text.Resource(R.string.color_contrast_high_title) 80 else -> { 81 iconInner.isVisible = false 82 iconOuter.isVisible = false 83 Log.e(TAG, "Invalid contrast value: $contrast") 84 throw IllegalArgumentException("Invalid contrast value: $contrast") 85 } 86 }, 87 ) 88 89 return object : Binding { 90 override fun destroy() { 91 bindingInner.destroy() 92 bindingOuter.destroy() 93 } 94 } 95 } 96 } 97