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.picker.common.preview.ui.binder 18 19 import android.app.WallpaperColors 20 import android.content.Context 21 import android.graphics.Point 22 import android.view.LayoutInflater 23 import android.view.SurfaceHolder 24 import android.view.SurfaceView 25 import androidx.lifecycle.Lifecycle 26 import androidx.lifecycle.LifecycleOwner 27 import androidx.lifecycle.lifecycleScope 28 import androidx.lifecycle.repeatOnLifecycle 29 import com.android.wallpaper.R 30 import com.android.wallpaper.model.Screen 31 import com.android.wallpaper.model.wallpaper.DeviceDisplayType 32 import com.android.wallpaper.picker.common.preview.ui.viewmodel.BasePreviewViewModel 33 import com.android.wallpaper.picker.customization.shared.model.WallpaperColorsModel 34 import com.android.wallpaper.picker.data.WallpaperModel 35 import com.android.wallpaper.util.SurfaceViewUtils 36 import com.android.wallpaper.util.SurfaceViewUtils.attachView 37 import com.android.wallpaper.util.wallpaperconnection.WallpaperConnectionUtils 38 import com.android.wallpaper.util.wallpaperconnection.WallpaperConnectionUtils.Companion.shouldEnforceSingleEngine 39 import com.android.wallpaper.util.wallpaperconnection.WallpaperEngineConnection 40 import kotlinx.coroutines.CompletableDeferred 41 import kotlinx.coroutines.CoroutineScope 42 import kotlinx.coroutines.Job 43 import kotlinx.coroutines.launch 44 45 /** 46 * Bind the [SurfaceView] with [BasePreviewViewModel] for rendering static or live wallpaper 47 * preview, with regard to its underlying [WallpaperModel]. 48 */ 49 // Based on SmallWallpaperPreviewBinder, mostly unchanged, except with LoadingAnimationBinding 50 // removed. Also we enable a screen to be defined during binding rather than reading from 51 // viewModel.isViewAsHome. In addition the call to WallpaperConnectionUtils.disconnectAllServices at 52 // the end of the static wallpaper binding is removed since it interferes with previewing one live 53 // and one static wallpaper side by side, but should be re-visited when integrating into 54 // WallpaperPreviewActivity for the cinematic wallpaper toggle case. 55 object WallpaperPreviewBinder { 56 fun bind( 57 applicationContext: Context, 58 surfaceView: SurfaceView, 59 viewModel: BasePreviewViewModel, 60 screen: Screen, 61 displaySize: Point, 62 deviceDisplayType: DeviceDisplayType, 63 mainScope: CoroutineScope, 64 viewLifecycleOwner: LifecycleOwner, 65 wallpaperConnectionUtils: WallpaperConnectionUtils, 66 isFirstBindingDeferred: CompletableDeferred<Boolean>, 67 ) { 68 var surfaceCallback: SurfaceViewUtils.SurfaceCallback? = null 69 viewLifecycleOwner.lifecycleScope.launch { 70 viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) { 71 surfaceCallback = 72 bindSurface( 73 applicationContext = applicationContext, 74 surfaceView = surfaceView, 75 viewModel = viewModel, 76 screen = screen, 77 deviceDisplayType = deviceDisplayType, 78 displaySize = displaySize, 79 mainScope = mainScope, 80 lifecycleOwner = viewLifecycleOwner, 81 wallpaperConnectionUtils = wallpaperConnectionUtils, 82 isFirstBindingDeferred = isFirstBindingDeferred, 83 ) 84 surfaceView.setZOrderMediaOverlay(true) 85 surfaceCallback?.let { surfaceView.holder.addCallback(it) } 86 } 87 // When OnDestroy, release the surface 88 surfaceCallback?.let { 89 surfaceView.holder.removeCallback(it) 90 surfaceCallback = null 91 } 92 } 93 } 94 95 /** 96 * Create a surface callback that binds the surface when surface created. Note that we return 97 * the surface callback reference so that we can remove the callback from the surface when the 98 * screen is destroyed. 99 */ 100 private fun bindSurface( 101 applicationContext: Context, 102 surfaceView: SurfaceView, 103 viewModel: BasePreviewViewModel, 104 screen: Screen, 105 deviceDisplayType: DeviceDisplayType, 106 displaySize: Point, 107 mainScope: CoroutineScope, 108 lifecycleOwner: LifecycleOwner, 109 wallpaperConnectionUtils: WallpaperConnectionUtils, 110 isFirstBindingDeferred: CompletableDeferred<Boolean>, 111 ): SurfaceViewUtils.SurfaceCallback { 112 113 return object : SurfaceViewUtils.SurfaceCallback { 114 115 var job: Job? = null 116 117 override fun surfaceCreated(holder: SurfaceHolder) { 118 job = 119 // Ensure the wallpaper connection is connected / disconnected in [mainScope]. 120 mainScope.launch { 121 viewModel.wallpapersAndWhichPreview.collect { (wallpapers, whichPreview) -> 122 val wallpaper = 123 if (screen == Screen.HOME_SCREEN) wallpapers.homeWallpaper 124 else wallpapers.lockWallpaper ?: wallpapers.homeWallpaper 125 if (wallpaper is WallpaperModel.LiveWallpaperModel) { 126 val engineRenderingConfig = 127 WallpaperConnectionUtils.Companion.EngineRenderingConfig( 128 wallpaper.shouldEnforceSingleEngine(), 129 deviceDisplayType = deviceDisplayType, 130 viewModel.smallerDisplaySize, 131 viewModel.wallpaperDisplaySize.value, 132 ) 133 val listener = 134 object : 135 WallpaperEngineConnection.WallpaperEngineConnectionListener { 136 override fun onWallpaperColorsChanged( 137 colors: WallpaperColors?, 138 displayId: Int 139 ) { 140 viewModel.setWallpaperConnectionColors( 141 WallpaperColorsModel.Loaded(colors) 142 ) 143 } 144 } 145 wallpaperConnectionUtils.connect( 146 applicationContext, 147 wallpaper, 148 whichPreview, 149 screen.toFlag(), 150 surfaceView, 151 engineRenderingConfig, 152 isFirstBindingDeferred, 153 listener, 154 ) 155 } else if (wallpaper is WallpaperModel.StaticWallpaperModel) { 156 val staticPreviewView = 157 LayoutInflater.from(applicationContext) 158 .inflate(R.layout.fullscreen_wallpaper_preview, null) 159 // surfaceView.width and surfaceFrame.width here can be different, 160 // one represents the size of the view and the other represents the 161 // size of the surface. When setting a view to the surface host, 162 // we want to set it based on the surface's size not the view's size 163 val surfacePosition = surfaceView.holder.surfaceFrame 164 surfaceView.attachView( 165 staticPreviewView, 166 surfacePosition.width(), 167 surfacePosition.height() 168 ) 169 // Bind static wallpaper 170 StaticPreviewBinder.bind( 171 lowResImageView = 172 staticPreviewView.requireViewById(R.id.low_res_image), 173 fullResImageView = 174 staticPreviewView.requireViewById(R.id.full_res_image), 175 viewModel = 176 if ( 177 screen == Screen.LOCK_SCREEN && 178 wallpapers.lockWallpaper != null 179 ) { 180 // Only if home and lock screen are different, use lock 181 // view model, otherwise, re-use home view model for 182 // lock. 183 viewModel.staticLockWallpaperPreviewViewModel 184 } else { 185 viewModel.staticHomeWallpaperPreviewViewModel 186 }, 187 displaySize = displaySize, 188 parentCoroutineScope = this, 189 ) 190 // TODO (b/348462236): investigate cinematic wallpaper toggle case 191 // Previously all live wallpaper services are shut down to enable 192 // static photos wallpaper to show up when cinematic effect is 193 // toggled off, using WallpaperConnectionUtils.disconnectAllServices 194 // This cannot work when previewing current wallpaper, and one 195 // wallpaper is live and the other is static--it causes live 196 // wallpaper to black screen occasionally. 197 } 198 } 199 } 200 } 201 202 override fun surfaceDestroyed(holder: SurfaceHolder) { 203 job?.cancel() 204 job = null 205 // Note that we disconnect wallpaper connection for live wallpapers in 206 // WallpaperPreviewActivity's onDestroy(). 207 // This is to reduce multiple times of connecting and disconnecting live 208 // wallpaper services, when going back and forth small and full preview. 209 } 210 } 211 } 212 } 213