• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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