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.wallpaper.picker.preview.ui.binder 17 18 import android.app.WallpaperColors 19 import android.content.Context 20 import android.graphics.Point 21 import android.view.LayoutInflater 22 import android.view.SurfaceHolder 23 import android.view.SurfaceView 24 import androidx.lifecycle.Lifecycle 25 import androidx.lifecycle.LifecycleOwner 26 import androidx.lifecycle.lifecycleScope 27 import androidx.lifecycle.repeatOnLifecycle 28 import com.android.wallpaper.R 29 import com.android.wallpaper.model.wallpaper.DeviceDisplayType 30 import com.android.wallpaper.picker.customization.shared.model.WallpaperColorsModel 31 import com.android.wallpaper.picker.data.WallpaperModel 32 import com.android.wallpaper.picker.preview.ui.util.SurfaceViewUtil 33 import com.android.wallpaper.picker.preview.ui.util.SurfaceViewUtil.attachView 34 import com.android.wallpaper.picker.preview.ui.viewmodel.WallpaperPreviewViewModel 35 import com.android.wallpaper.util.wallpaperconnection.WallpaperConnectionUtils 36 import com.android.wallpaper.util.wallpaperconnection.WallpaperConnectionUtils.shouldEnforceSingleEngine 37 import com.android.wallpaper.util.wallpaperconnection.WallpaperEngineConnection.WallpaperEngineConnectionListener 38 import kotlinx.coroutines.Job 39 import kotlinx.coroutines.launch 40 41 /** 42 * Bind the [SurfaceView] with [WallpaperPreviewViewModel] for rendering static or live wallpaper 43 * preview, with regard to its underlying [WallpaperModel]. 44 */ 45 object SmallWallpaperPreviewBinder { 46 /** 47 * @param onFullResImageViewCreated This callback is only used when the wallpaperModel is a 48 * [WallpaperModel.StaticWallpaperModel]. [FullWallpaperPreviewBinder] needs the callback to 49 * further delegate the touch events and set the state change listener. 50 */ 51 fun bind( 52 surface: SurfaceView, 53 viewModel: WallpaperPreviewViewModel, 54 displaySize: Point, 55 applicationContext: Context, 56 viewLifecycleOwner: LifecycleOwner, 57 deviceDisplayType: DeviceDisplayType, 58 isFirstBinding: Boolean, 59 ) { 60 var surfaceCallback: SurfaceViewUtil.SurfaceCallback? = null 61 viewLifecycleOwner.lifecycleScope.launch { 62 viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) { 63 surfaceCallback = 64 bindSurface( 65 applicationContext = applicationContext, 66 surface = surface, 67 viewModel = viewModel, 68 deviceDisplayType = deviceDisplayType, 69 displaySize = displaySize, 70 lifecycleOwner = viewLifecycleOwner, 71 isFirstBinding 72 ) 73 surface.setZOrderMediaOverlay(true) 74 surfaceCallback?.let { surface.holder.addCallback(it) } 75 } 76 // When OnDestroy, release the surface 77 surfaceCallback?.let { 78 surface.holder.removeCallback(it) 79 surfaceCallback = null 80 } 81 } 82 } 83 84 /** 85 * Create a surface callback that binds the surface when surface created. Note that we return 86 * the surface callback reference so that we can remove the callback from the surface when the 87 * screen is destroyed. 88 */ 89 private fun bindSurface( 90 applicationContext: Context, 91 surface: SurfaceView, 92 viewModel: WallpaperPreviewViewModel, 93 deviceDisplayType: DeviceDisplayType, 94 displaySize: Point, 95 lifecycleOwner: LifecycleOwner, 96 isFirstBinding: Boolean, 97 ): SurfaceViewUtil.SurfaceCallback { 98 99 return object : SurfaceViewUtil.SurfaceCallback { 100 101 var job: Job? = null 102 var loadingAnimationBinding: PreviewEffectsLoadingBinder.Binding? = null 103 104 override fun surfaceCreated(holder: SurfaceHolder) { 105 job = 106 lifecycleOwner.lifecycleScope.launch { 107 viewModel.smallWallpaper.collect { (wallpaper, whichPreview) -> 108 if (wallpaper is WallpaperModel.LiveWallpaperModel) { 109 WallpaperConnectionUtils.connect( 110 applicationContext, 111 wallpaper, 112 whichPreview, 113 viewModel.getWallpaperPreviewSource().toFlag(), 114 surface, 115 WallpaperConnectionUtils.EngineRenderingConfig( 116 wallpaper.shouldEnforceSingleEngine(), 117 deviceDisplayType = deviceDisplayType, 118 viewModel.smallerDisplaySize, 119 viewModel.wallpaperDisplaySize.value, 120 ), 121 isFirstBinding, 122 object : WallpaperEngineConnectionListener { 123 override fun onWallpaperColorsChanged( 124 colors: WallpaperColors?, 125 displayId: Int 126 ) { 127 viewModel.setWallpaperConnectionColors( 128 WallpaperColorsModel.Loaded(colors) 129 ) 130 } 131 }, 132 ) 133 } else if (wallpaper is WallpaperModel.StaticWallpaperModel) { 134 val staticPreviewView = 135 LayoutInflater.from(applicationContext) 136 .inflate(R.layout.fullscreen_wallpaper_preview, null) 137 surface.attachView(staticPreviewView) 138 // Bind static wallpaper 139 StaticWallpaperPreviewBinder.bind( 140 lowResImageView = 141 staticPreviewView.requireViewById(R.id.low_res_image), 142 fullResImageView = 143 staticPreviewView.requireViewById(R.id.full_res_image), 144 viewModel = viewModel.staticWallpaperPreviewViewModel, 145 displaySize = displaySize, 146 parentCoroutineScope = this, 147 ) 148 // This is to possibly shut down all live wallpaper services 149 // if they exist; otherwise static wallpaper can not show up. 150 WallpaperConnectionUtils.disconnectAllServices(applicationContext) 151 152 loadingAnimationBinding = 153 PreviewEffectsLoadingBinder.bind( 154 view = 155 staticPreviewView.requireViewById(R.id.full_res_image), 156 viewModel = viewModel, 157 viewLifecycleOwner = lifecycleOwner, 158 ) 159 } 160 } 161 } 162 } 163 164 override fun surfaceDestroyed(holder: SurfaceHolder) { 165 job?.cancel() 166 job = null 167 loadingAnimationBinding?.destroy() 168 loadingAnimationBinding = null 169 // Note that we disconnect wallpaper connection for live wallpapers in 170 // WallpaperPreviewActivity's onDestroy(). 171 // This is to reduce multiple times of connecting and disconnecting live 172 // wallpaper services, when going back and forth small and full preview. 173 } 174 } 175 } 176 } 177