• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2022 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 
18 package com.android.wallpaper.picker.customization.ui.section
19 
20 import android.annotation.SuppressLint
21 import android.app.Activity
22 import android.app.WallpaperManager
23 import android.content.Context
24 import android.os.Bundle
25 import android.view.Gravity
26 import android.view.LayoutInflater
27 import android.widget.FrameLayout
28 import androidx.cardview.widget.CardView
29 import androidx.lifecycle.LifecycleOwner
30 import androidx.lifecycle.lifecycleScope
31 import com.android.systemui.shared.clocks.shared.model.ClockPreviewConstants
32 import com.android.wallpaper.R
33 import com.android.wallpaper.model.CustomizationSectionController
34 import com.android.wallpaper.model.WallpaperColorsViewModel
35 import com.android.wallpaper.model.WallpaperInfo
36 import com.android.wallpaper.model.WallpaperPreviewNavigator
37 import com.android.wallpaper.module.CurrentWallpaperInfoFactory
38 import com.android.wallpaper.module.CustomizationSections
39 import com.android.wallpaper.picker.FixedWidthDisplayRatioFrameLayout
40 import com.android.wallpaper.picker.customization.domain.interactor.WallpaperInteractor
41 import com.android.wallpaper.picker.customization.ui.binder.ScreenPreviewBinder
42 import com.android.wallpaper.picker.customization.ui.viewmodel.CustomizationPickerViewModel
43 import com.android.wallpaper.picker.customization.ui.viewmodel.ScreenPreviewViewModel
44 import com.android.wallpaper.util.DisplayUtils
45 import com.android.wallpaper.util.PreviewUtils
46 import kotlinx.coroutines.Dispatchers
47 import kotlinx.coroutines.ExperimentalCoroutinesApi
48 import kotlinx.coroutines.launch
49 import kotlinx.coroutines.suspendCancellableCoroutine
50 import kotlinx.coroutines.withContext
51 
52 /** Controls the screen preview section. */
53 @OptIn(ExperimentalCoroutinesApi::class)
54 open class ScreenPreviewSectionController(
55     private val activity: Activity,
56     private val lifecycleOwner: LifecycleOwner,
57     private val screen: CustomizationSections.Screen,
58     private val wallpaperInfoFactory: CurrentWallpaperInfoFactory,
59     private val colorViewModel: WallpaperColorsViewModel,
60     private val displayUtils: DisplayUtils,
61     private val wallpaperPreviewNavigator: WallpaperPreviewNavigator,
62     private val wallpaperInteractor: WallpaperInteractor,
63     private val wallpaperManager: WallpaperManager,
64     private val isTwoPaneAndSmallWidth: Boolean,
65     private val customizationPickerViewModel: CustomizationPickerViewModel,
66 ) : CustomizationSectionController<ScreenPreviewView> {
67 
68     protected val isOnLockScreen: Boolean = screen == CustomizationSections.Screen.LOCK_SCREEN
69 
70     protected var previewViewBinding: ScreenPreviewBinder.Binding? = null
71 
72     /** Override to hide the lock screen clock preview. */
73     open val hideLockScreenClockPreview = false
74 
75     override fun shouldRetainInstanceWhenSwitchingTabs(): Boolean {
76         return true
77     }
78 
79     override fun isAvailable(context: Context): Boolean {
80         // Assumption is that, if this section controller is included, we are using the revamped UI
81         // so it should always be shown.
82         return true
83     }
84 
85     override fun createView(context: Context): ScreenPreviewView {
86         return createView(context, CustomizationSectionController.ViewCreationParams())
87     }
88 
89     @SuppressLint("InflateParams")
90     override fun createView(
91         context: Context,
92         params: CustomizationSectionController.ViewCreationParams,
93     ): ScreenPreviewView {
94         val view =
95             LayoutInflater.from(context)
96                 .inflate(
97                     R.layout.screen_preview_section,
98                     /* parent= */ null,
99                 ) as ScreenPreviewView
100 
101         if (isTwoPaneAndSmallWidth) {
102             val previewHost =
103                 view.requireViewById<FixedWidthDisplayRatioFrameLayout>(R.id.preview_host)
104             val layoutParams =
105                 FrameLayout.LayoutParams(
106                     context.resources.getDimensionPixelSize(
107                         R.dimen.screen_preview_width_for_2_pane_small_width
108                     ),
109                     FrameLayout.LayoutParams.WRAP_CONTENT,
110                 )
111             layoutParams.gravity = Gravity.CENTER
112             previewHost.layoutParams = layoutParams
113         }
114 
115         view
116             .requireViewById<ScreenPreviewClickView>(R.id.screen_preview_click_view)
117             .setOnPreviewClickedListener {
118                 lifecycleOwner.lifecycleScope.launch {
119                     getWallpaperInfo()?.let { wallpaperInfo ->
120                         wallpaperPreviewNavigator.showViewOnlyPreview(
121                             wallpaperInfo,
122                             /* isAssetIdPresent= */ false,
123                         )
124                     }
125                 }
126             }
127 
128         val previewView: CardView = view.requireViewById(R.id.preview)
129 
130         bindScreenPreview(previewView, context, !params.isWallpaperVisibilityControlledByTab)
131         return view
132     }
133 
134     protected open fun bindScreenPreview(
135         previewView: CardView,
136         context: Context,
137         isWallpaperAlwaysVisible: Boolean,
138     ) {
139         previewViewBinding?.destroy()
140         previewViewBinding =
141             ScreenPreviewBinder.bind(
142                 activity = activity,
143                 previewView = previewView,
144                 viewModel = createScreenPreviewViewModel(context),
145                 lifecycleOwner = lifecycleOwner,
146                 offsetToStart = displayUtils.isSingleDisplayOrUnfoldedHorizontalHinge(activity),
147                 onWallpaperPreviewDirty = { activity.recreate() },
148                 animationStateViewModel = customizationPickerViewModel,
149                 onWorkspacePreviewDirty = {
150                     bindScreenPreview(previewView, context, isWallpaperAlwaysVisible)
151                 },
152                 isWallpaperAlwaysVisible = isWallpaperAlwaysVisible,
153             )
154     }
155 
156     protected open fun createScreenPreviewViewModel(context: Context): ScreenPreviewViewModel {
157         return ScreenPreviewViewModel(
158             previewUtils =
159                 if (isOnLockScreen) {
160                     PreviewUtils(
161                         context = context,
162                         authority =
163                             context.getString(
164                                 R.string.lock_screen_preview_provider_authority,
165                             ),
166                     )
167                 } else {
168                     PreviewUtils(
169                         context = context,
170                         authorityMetadataKey =
171                             context.getString(
172                                 R.string.grid_control_metadata_name,
173                             ),
174                     )
175                 },
176             wallpaperInfoProvider = { forceReload ->
177                 suspendCancellableCoroutine { continuation ->
178                     wallpaperInfoFactory.createCurrentWallpaperInfos(
179                         { homeWallpaper, lockWallpaper, _ ->
180                             val wallpaper =
181                                 if (isOnLockScreen) {
182                                     lockWallpaper ?: homeWallpaper
183                                 } else {
184                                     homeWallpaper ?: lockWallpaper
185                                 }
186                             loadInitialColors(
187                                 context = context,
188                                 screen = screen,
189                             )
190                             continuation.resume(wallpaper, null)
191                         },
192                         forceReload,
193                     )
194                 }
195             },
196             onWallpaperColorChanged = { colors ->
197                 if (isOnLockScreen) {
198                     colorViewModel.setLockWallpaperColors(colors)
199                 } else {
200                     colorViewModel.setHomeWallpaperColors(colors)
201                 }
202             },
203             initialExtrasProvider = { getInitialExtras(isOnLockScreen) },
204             wallpaperInteractor = wallpaperInteractor,
205             screen = screen,
206         )
207     }
208 
209     protected fun loadInitialColors(
210         context: Context,
211         screen: CustomizationSections.Screen,
212     ) {
213         lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
214             val colors =
215                 wallpaperManager.getWallpaperColors(
216                     if (screen == CustomizationSections.Screen.LOCK_SCREEN) {
217                         WallpaperManager.FLAG_LOCK
218                     } else {
219                         WallpaperManager.FLAG_SYSTEM
220                     }
221                 )
222             withContext(Dispatchers.Main) {
223                 if (colors != null) {
224                     if (screen == CustomizationSections.Screen.LOCK_SCREEN) {
225                         colorViewModel.setLockWallpaperColors(colors)
226                     } else {
227                         colorViewModel.setHomeWallpaperColors(colors)
228                     }
229                 }
230             }
231         }
232     }
233 
234     private suspend fun getWallpaperInfo(): WallpaperInfo? {
235         return suspendCancellableCoroutine { continuation ->
236             wallpaperInfoFactory.createCurrentWallpaperInfos(
237                 { homeWallpaper, lockWallpaper, _ ->
238                     continuation.resume(
239                         if (isOnLockScreen) {
240                             lockWallpaper ?: homeWallpaper
241                         } else {
242                             homeWallpaper
243                         },
244                         null
245                     )
246                 },
247                 /* forceRefresh= */ true,
248             )
249         }
250     }
251 
252     protected fun getInitialExtras(isOnLockScreen: Boolean): Bundle? {
253         return if (isOnLockScreen) {
254             Bundle().apply {
255                 // Hide the clock from the system UI rendered preview so we can
256                 // place the carousel on top of it.
257                 putBoolean(
258                     ClockPreviewConstants.KEY_HIDE_CLOCK,
259                     hideLockScreenClockPreview,
260                 )
261             }
262         } else {
263             null
264         }
265     }
266 }
267