1 /* <lambda>null2 * Copyright (C) 2024 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.wallpaper.customization.ui.binder 18 19 import android.content.res.Configuration.UI_MODE_NIGHT_MASK 20 import android.content.res.Configuration.UI_MODE_NIGHT_YES 21 import android.view.View 22 import android.view.ViewGroup 23 import android.widget.TextView 24 import androidx.core.graphics.drawable.DrawableCompat 25 import androidx.lifecycle.Lifecycle 26 import androidx.lifecycle.LifecycleOwner 27 import androidx.lifecycle.lifecycleScope 28 import androidx.lifecycle.repeatOnLifecycle 29 import androidx.recyclerview.widget.LinearLayoutManager 30 import androidx.recyclerview.widget.RecyclerView 31 import com.android.customization.picker.color.ui.binder.ColorOptionIconBinder2 32 import com.android.customization.picker.color.ui.view.ColorOptionIconView2 33 import com.android.customization.picker.color.ui.viewmodel.ColorOptionIconViewModel 34 import com.android.customization.picker.mode.ui.binder.DarkModeBinder 35 import com.android.themepicker.R 36 import com.android.wallpaper.customization.ui.util.ThemePickerCustomizationOptionUtil.ThemePickerHomeCustomizationOption.COLORS 37 import com.android.wallpaper.customization.ui.viewmodel.ThemePickerCustomizationOptionsViewModel 38 import com.android.wallpaper.picker.customization.ui.binder.ColorUpdateBinder 39 import com.android.wallpaper.picker.customization.ui.view.FloatingToolbar 40 import com.android.wallpaper.picker.customization.ui.view.adapter.FloatingToolbarTabAdapter 41 import com.android.wallpaper.picker.customization.ui.viewmodel.ColorUpdateViewModel 42 import com.android.wallpaper.picker.option.ui.adapter.OptionItemAdapter2 43 import java.lang.ref.WeakReference 44 import kotlinx.coroutines.DisposableHandle 45 import kotlinx.coroutines.flow.combine 46 import kotlinx.coroutines.launch 47 48 object ColorsFloatingSheetBinder { 49 50 fun bind( 51 view: View, 52 optionsViewModel: ThemePickerCustomizationOptionsViewModel, 53 colorUpdateViewModel: ColorUpdateViewModel, 54 lifecycleOwner: LifecycleOwner, 55 ) { 56 val colorsViewModel = optionsViewModel.colorPickerViewModel2 57 val darkModeViewModel = optionsViewModel.darkModeViewModel 58 val isFloatingSheetActive = { optionsViewModel.selectedOption.value == COLORS } 59 60 ColorUpdateBinder.bind( 61 setColor = { color -> 62 view.requireViewById<TextView>(R.id.color_type_tab_subhead).setTextColor(color) 63 view.requireViewById<TextView>(R.id.dark_mode_toggle_title).setTextColor(color) 64 }, 65 color = colorUpdateViewModel.colorOnSurface, 66 shouldAnimate = isFloatingSheetActive, 67 lifecycleOwner = lifecycleOwner, 68 ) 69 70 val tabs = view.requireViewById<FloatingToolbar>(R.id.floating_toolbar) 71 val tabContainer = 72 tabs.findViewById<ViewGroup>(com.android.wallpaper.R.id.floating_toolbar_tab_container) 73 ColorUpdateBinder.bind( 74 setColor = { color -> 75 DrawableCompat.setTint(DrawableCompat.wrap(tabContainer.background), color) 76 }, 77 color = colorUpdateViewModel.floatingToolbarBackground, 78 shouldAnimate = isFloatingSheetActive, 79 lifecycleOwner = lifecycleOwner, 80 ) 81 val tabAdapter = 82 FloatingToolbarTabAdapter( 83 colorUpdateViewModel = WeakReference(colorUpdateViewModel), 84 shouldAnimateColor = isFloatingSheetActive, 85 ) 86 .also { tabs.setAdapter(it) } 87 88 val floatingSheetContainer = 89 view.requireViewById<ViewGroup>(R.id.floating_sheet_content_container) 90 ColorUpdateBinder.bind( 91 setColor = { color -> 92 DrawableCompat.setTint( 93 DrawableCompat.wrap(floatingSheetContainer.background), 94 color, 95 ) 96 }, 97 color = colorUpdateViewModel.colorSurfaceBright, 98 shouldAnimate = isFloatingSheetActive, 99 lifecycleOwner = lifecycleOwner, 100 ) 101 102 val subhead = view.requireViewById<TextView>(R.id.color_type_tab_subhead) 103 104 val colorsAdapter = 105 createOptionItemAdapter( 106 uiMode = view.resources.configuration.uiMode, 107 colorUpdateViewModel = colorUpdateViewModel, 108 shouldAnimateColor = isFloatingSheetActive, 109 lifecycleOwner = lifecycleOwner, 110 ) 111 val colorsList = 112 view.requireViewById<RecyclerView>(R.id.colors_horizontal_list).apply { 113 adapter = colorsAdapter 114 layoutManager = 115 LinearLayoutManager( 116 view.context.applicationContext, 117 LinearLayoutManager.HORIZONTAL, 118 false, 119 ) 120 } 121 122 DarkModeBinder.bind( 123 darkModeToggle = view.findViewById(R.id.dark_mode_toggle), 124 viewModel = darkModeViewModel, 125 colorUpdateViewModel = colorUpdateViewModel, 126 shouldAnimateColor = isFloatingSheetActive, 127 lifecycleOwner = lifecycleOwner, 128 ) 129 130 lifecycleOwner.lifecycleScope.launch { 131 lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { 132 launch { colorsViewModel.colorTypeTabs.collect { tabAdapter.submitList(it) } } 133 134 launch { colorsViewModel.colorTypeTabSubheader.collect { subhead.text = it } } 135 136 launch { 137 colorsViewModel.colorOptions.collect { colorOptions -> 138 colorsAdapter.setItems(colorOptions) { 139 var indexToFocus = colorOptions.indexOfFirst { it.isSelected.value } 140 indexToFocus = if (indexToFocus < 0) 0 else indexToFocus 141 (colorsList.layoutManager as LinearLayoutManager) 142 .scrollToPositionWithOffset(indexToFocus, 0) 143 } 144 } 145 } 146 147 launch { 148 combine( 149 colorsViewModel.previewingColorOption, 150 colorsViewModel.selectedColorOption, 151 darkModeViewModel.overridingIsDarkMode, 152 ::Triple, 153 ) 154 .collect { (previewColor, selectedColor, overridingIsDarkMode) -> 155 if (previewColor != null || overridingIsDarkMode != null) { 156 val previewColorOption = previewColor ?: selectedColor 157 val previewIsDarkMode = 158 overridingIsDarkMode 159 ?: view.resources.configuration.isNightModeActive 160 previewColorOption?.let { 161 colorUpdateViewModel.previewColors( 162 previewColorOption.seedColor, 163 previewColorOption.style, 164 previewIsDarkMode, 165 ) 166 } 167 } else colorUpdateViewModel.resetPreview() 168 } 169 } 170 } 171 } 172 } 173 174 private fun createOptionItemAdapter( 175 uiMode: Int, 176 colorUpdateViewModel: ColorUpdateViewModel, 177 shouldAnimateColor: () -> Boolean, 178 lifecycleOwner: LifecycleOwner, 179 ): OptionItemAdapter2<ColorOptionIconViewModel> = 180 OptionItemAdapter2( 181 layoutResourceId = R.layout.color_option2, 182 lifecycleOwner = lifecycleOwner, 183 bindPayload = { itemView: View, colorIcon: ColorOptionIconViewModel -> 184 val colorOptionIconView = 185 itemView.requireViewById<ColorOptionIconView2>( 186 com.android.wallpaper.R.id.background 187 ) 188 val night = uiMode and UI_MODE_NIGHT_MASK == UI_MODE_NIGHT_YES 189 val binding = 190 ColorOptionIconBinder2.bind( 191 view = colorOptionIconView, 192 viewModel = colorIcon, 193 darkTheme = night, 194 colorUpdateViewModel = colorUpdateViewModel, 195 shouldAnimateColor = shouldAnimateColor, 196 lifecycleOwner = lifecycleOwner, 197 ) 198 return@OptionItemAdapter2 DisposableHandle { binding.destroy() } 199 }, 200 colorUpdateViewModel = WeakReference(colorUpdateViewModel), 201 shouldAnimateColor = shouldAnimateColor, 202 ) 203 } 204