1 /*
<lambda>null2  * Copyright (C) 2023 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 package com.android.customization.picker.color.ui.fragment
17 
18 import android.app.WallpaperManager
19 import android.os.Bundle
20 import android.view.LayoutInflater
21 import android.view.View
22 import android.view.ViewGroup
23 import android.view.ViewGroup.MarginLayoutParams
24 import android.widget.FrameLayout
25 import androidx.cardview.widget.CardView
26 import androidx.core.content.ContextCompat
27 import androidx.core.view.ViewCompat
28 import androidx.core.view.WindowInsetsCompat
29 import androidx.core.view.isVisible
30 import androidx.core.view.updateLayoutParams
31 import androidx.lifecycle.ViewModelProvider
32 import androidx.lifecycle.get
33 import androidx.lifecycle.lifecycleScope
34 import androidx.transition.Transition
35 import androidx.transition.doOnStart
36 import com.android.customization.model.mode.DarkModeSectionController
37 import com.android.customization.module.ThemePickerInjector
38 import com.android.customization.picker.color.ui.binder.ColorPickerBinder
39 import com.android.themepicker.R
40 import com.android.wallpaper.model.Screen
41 import com.android.wallpaper.module.InjectorProvider
42 import com.android.wallpaper.picker.AppbarFragment
43 import com.android.wallpaper.picker.customization.data.repository.WallpaperColorsRepository
44 import com.android.wallpaper.picker.customization.shared.model.WallpaperColorsModel
45 import com.android.wallpaper.picker.customization.ui.binder.ScreenPreviewBinder
46 import com.android.wallpaper.picker.customization.ui.viewmodel.ScreenPreviewViewModel
47 import com.android.wallpaper.util.DisplayUtils
48 import com.android.wallpaper.util.PreviewUtils
49 import kotlinx.coroutines.Dispatchers
50 import kotlinx.coroutines.ExperimentalCoroutinesApi
51 import kotlinx.coroutines.launch
52 import kotlinx.coroutines.suspendCancellableCoroutine
53 import kotlinx.coroutines.withContext
54 
55 @OptIn(ExperimentalCoroutinesApi::class)
56 class ColorPickerFragment : AppbarFragment() {
57     private var binding: ColorPickerBinder.Binding? = null
58 
59     companion object {
60         @JvmStatic
61         fun newInstance(): ColorPickerFragment {
62             return ColorPickerFragment()
63         }
64     }
65 
66     override fun onCreateView(
67         inflater: LayoutInflater,
68         container: ViewGroup?,
69         savedInstanceState: Bundle?
70     ): View {
71         val view =
72             inflater.inflate(
73                 R.layout.fragment_color_picker,
74                 container,
75                 false,
76             )
77         ViewCompat.setOnApplyWindowInsetsListener(view) { v, windowInsets ->
78             val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
79             v.updateLayoutParams<MarginLayoutParams> {
80                 topMargin = insets.top
81                 bottomMargin = insets.bottom
82             }
83             WindowInsetsCompat.CONSUMED
84         }
85         setUpToolbar(view)
86 
87         val injector = InjectorProvider.getInjector() as ThemePickerInjector
88         val lockScreenView: CardView = view.requireViewById(R.id.lock_preview)
89         val homeScreenView: CardView = view.requireViewById(R.id.home_preview)
90         val wallpaperInfoFactory = injector.getCurrentWallpaperInfoFactory(requireContext())
91         val displayUtils: DisplayUtils = injector.getDisplayUtils(requireContext())
92         val wallpaperColorsRepository = injector.getWallpaperColorsRepository()
93         val wallpaperManager = WallpaperManager.getInstance(requireContext())
94 
95         binding =
96             ColorPickerBinder.bind(
97                 view = view,
98                 viewModel =
99                     ViewModelProvider(
100                             requireActivity(),
101                             injector.getColorPickerViewModelFactory(requireContext()),
102                         )
103                         .get(),
104                 lifecycleOwner = this,
105             )
106 
107         savedInstanceState?.let { binding?.restoreInstanceState(it) }
108 
109         val lockScreenPreviewBinder =
110             ScreenPreviewBinder.bind(
111                 activity = requireActivity(),
112                 previewView = lockScreenView,
113                 viewModel =
114                     ScreenPreviewViewModel(
115                         previewUtils =
116                             PreviewUtils(
117                                 context = requireContext(),
118                                 authority =
119                                     requireContext()
120                                         .getString(
121                                             com.android.wallpaper.R.string
122                                                 .lock_screen_preview_provider_authority,
123                                         ),
124                             ),
125                         wallpaperInfoProvider = { forceReload ->
126                             suspendCancellableCoroutine { continuation ->
127                                 wallpaperInfoFactory.createCurrentWallpaperInfos(
128                                     context,
129                                     forceReload,
130                                 ) { homeWallpaper, lockWallpaper, _ ->
131                                     lifecycleScope.launch {
132                                         if (
133                                             wallpaperColorsRepository.lockWallpaperColors.value
134                                                 is WallpaperColorsModel.Loading
135                                         ) {
136                                             loadInitialColors(
137                                                 wallpaperManager,
138                                                 wallpaperColorsRepository,
139                                                 Screen.LOCK_SCREEN
140                                             )
141                                         }
142                                     }
143                                     continuation.resume(lockWallpaper ?: homeWallpaper, null)
144                                 }
145                             }
146                         },
147                         onWallpaperColorChanged = { colors ->
148                             wallpaperColorsRepository.setLockWallpaperColors(colors)
149                         },
150                         wallpaperInteractor = injector.getWallpaperInteractor(requireContext()),
151                         screen = Screen.LOCK_SCREEN,
152                     ),
153                 lifecycleOwner = this,
154                 offsetToStart =
155                     displayUtils.isSingleDisplayOrUnfoldedHorizontalHinge(requireActivity()),
156                 onWallpaperPreviewDirty = { activity?.recreate() },
157             )
158         val shouldMirrorHomePreview =
159             wallpaperManager.getWallpaperInfo(WallpaperManager.FLAG_SYSTEM) != null &&
160                 wallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0
161         val mirrorSurface = if (shouldMirrorHomePreview) lockScreenPreviewBinder.surface() else null
162         ScreenPreviewBinder.bind(
163             activity = requireActivity(),
164             previewView = homeScreenView,
165             viewModel =
166                 ScreenPreviewViewModel(
167                     previewUtils =
168                         PreviewUtils(
169                             context = requireContext(),
170                             authorityMetadataKey =
171                                 requireContext()
172                                     .getString(
173                                         com.android.wallpaper.R.string.grid_control_metadata_name,
174                                     ),
175                         ),
176                     wallpaperInfoProvider = { forceReload ->
177                         suspendCancellableCoroutine { continuation ->
178                             wallpaperInfoFactory.createCurrentWallpaperInfos(
179                                 context,
180                                 forceReload,
181                             ) { homeWallpaper, lockWallpaper, _ ->
182                                 lifecycleScope.launch {
183                                     if (
184                                         wallpaperColorsRepository.homeWallpaperColors.value
185                                             is WallpaperColorsModel.Loading
186                                     ) {
187                                         loadInitialColors(
188                                             wallpaperManager,
189                                             wallpaperColorsRepository,
190                                             Screen.HOME_SCREEN
191                                         )
192                                     }
193                                 }
194                                 continuation.resume(homeWallpaper ?: lockWallpaper, null)
195                             }
196                         }
197                     },
198                     onWallpaperColorChanged = { colors ->
199                         wallpaperColorsRepository.setHomeWallpaperColors(colors)
200                     },
201                     wallpaperInteractor = injector.getWallpaperInteractor(requireContext()),
202                     screen = Screen.HOME_SCREEN,
203                 ),
204             lifecycleOwner = this,
205             offsetToStart =
206                 displayUtils.isSingleDisplayOrUnfoldedHorizontalHinge(requireActivity()),
207             onWallpaperPreviewDirty = { activity?.recreate() },
208             mirrorSurface = mirrorSurface,
209         )
210         val darkModeToggleContainerView: FrameLayout =
211             view.requireViewById(R.id.dark_mode_toggle_container)
212         val darkModeSectionView =
213             DarkModeSectionController(
214                     context,
215                     lifecycle,
216                     injector.getDarkModeSnapshotRestorer(requireContext()),
217                     injector.uiModeManager.get(),
218                     injector.getUserEventLogger(),
219                 )
220                 .createView(requireContext())
221         darkModeSectionView.background = null
222         darkModeToggleContainerView.addView(darkModeSectionView)
223 
224         (returnTransition as? Transition)?.doOnStart {
225             lockScreenView.isVisible = false
226             homeScreenView.isVisible = false
227         }
228 
229         return view
230     }
231 
232     private suspend fun loadInitialColors(
233         wallpaperManager: WallpaperManager,
234         colorViewModel: WallpaperColorsRepository,
235         screen: Screen,
236     ) {
237         withContext(Dispatchers.IO) {
238             val colors =
239                 wallpaperManager.getWallpaperColors(
240                     if (screen == Screen.LOCK_SCREEN) {
241                         WallpaperManager.FLAG_LOCK
242                     } else {
243                         WallpaperManager.FLAG_SYSTEM
244                     }
245                 )
246             withContext(Dispatchers.Main) {
247                 if (screen == Screen.LOCK_SCREEN) {
248                     colorViewModel.setLockWallpaperColors(colors)
249                 } else {
250                     colorViewModel.setHomeWallpaperColors(colors)
251                 }
252             }
253         }
254     }
255 
256     override fun onSaveInstanceState(savedInstanceState: Bundle) {
257         super.onSaveInstanceState(savedInstanceState)
258         binding?.saveInstanceState(savedInstanceState)
259     }
260 
261     override fun getDefaultTitle(): CharSequence {
262         return requireContext().getString(R.string.color_picker_title)
263     }
264 
265     override fun getToolbarTextColor(): Int {
266         return ContextCompat.getColor(
267             requireContext(),
268             com.android.wallpaper.R.color.system_on_surface
269         )
270     }
271 }
272