1 /* 2 * Copyright (C) 2020 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; 17 18 import android.app.Activity; 19 import android.app.WallpaperColors; 20 import android.content.Intent; 21 import android.graphics.Rect; 22 import android.graphics.RenderEffect; 23 import android.graphics.Shader.TileMode; 24 import android.service.wallpaper.WallpaperService; 25 import android.view.Surface; 26 import android.view.SurfaceView; 27 import android.view.View; 28 import android.view.ViewGroup; 29 import android.widget.ImageView; 30 31 import androidx.annotation.MainThread; 32 import androidx.annotation.Nullable; 33 import androidx.cardview.widget.CardView; 34 import androidx.lifecycle.Lifecycle; 35 import androidx.lifecycle.LifecycleObserver; 36 import androidx.lifecycle.OnLifecycleEvent; 37 38 import com.android.wallpaper.model.LiveWallpaperInfo; 39 import com.android.wallpaper.model.WallpaperInfo; 40 import com.android.wallpaper.util.ResourceUtils; 41 import com.android.wallpaper.util.ScreenSizeCalculator; 42 import com.android.wallpaper.util.SizeCalculator; 43 import com.android.wallpaper.util.VideoWallpaperUtils; 44 import com.android.wallpaper.util.WallpaperConnection; 45 import com.android.wallpaper.util.WallpaperConnection.WallpaperConnectionListener; 46 import com.android.wallpaper.util.WallpaperSurfaceCallback; 47 import com.android.wallpaper.widget.WallpaperColorsLoader; 48 49 /** A class to load the wallpaper to the view. */ 50 public class WallpaperPreviewer implements LifecycleObserver { 51 52 private final Rect mPreviewLocalRect = new Rect(); 53 private final Rect mPreviewGlobalRect = new Rect(); 54 private final int[] mLivePreviewLocation = new int[2]; 55 56 private final Activity mActivity; 57 private final ImageView mHomePreview; 58 private final SurfaceView mWallpaperSurface; 59 @Nullable private final ImageView mFadeInScrim; 60 61 private WallpaperSurfaceCallback mWallpaperSurfaceCallback; 62 private WallpaperInfo mWallpaper; 63 private WallpaperConnection mWallpaperConnection; 64 @Nullable private WallpaperColorsListener mWallpaperColorsListener; 65 66 /** Interface for getting {@link WallpaperColors} from wallpaper. */ 67 public interface WallpaperColorsListener { 68 /** Gets called when wallpaper color is available or updated. */ onWallpaperColorsChanged(WallpaperColors colors)69 void onWallpaperColorsChanged(WallpaperColors colors); 70 } 71 WallpaperPreviewer(Lifecycle lifecycle, Activity activity, ImageView homePreview, SurfaceView wallpaperSurface)72 public WallpaperPreviewer(Lifecycle lifecycle, Activity activity, ImageView homePreview, 73 SurfaceView wallpaperSurface) { 74 this(lifecycle, activity, homePreview, wallpaperSurface, null); 75 } 76 WallpaperPreviewer(Lifecycle lifecycle, Activity activity, ImageView homePreview, SurfaceView wallpaperSurface, @Nullable ImageView fadeInScrim)77 public WallpaperPreviewer(Lifecycle lifecycle, Activity activity, ImageView homePreview, 78 SurfaceView wallpaperSurface, @Nullable ImageView fadeInScrim) { 79 lifecycle.addObserver(this); 80 81 mActivity = activity; 82 mHomePreview = homePreview; 83 mWallpaperSurface = wallpaperSurface; 84 mFadeInScrim = fadeInScrim; 85 mWallpaperSurfaceCallback = new WallpaperSurfaceCallback(activity, mHomePreview, 86 mWallpaperSurface, this::setUpWallpaperPreview); 87 mWallpaperSurface.setZOrderMediaOverlay(true); 88 mWallpaperSurface.getHolder().addCallback(mWallpaperSurfaceCallback); 89 90 View rootView = homePreview.getRootView(); 91 rootView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { 92 @Override 93 public void onLayoutChange(View v, int left, int top, int right, int bottom, 94 int oldLeft, int oldTop, int oldRight, int oldBottom) { 95 updatePreviewCardRadius(); 96 rootView.removeOnLayoutChangeListener(this); 97 } 98 }); 99 } 100 101 @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) 102 @MainThread onResume()103 public void onResume() { 104 if (mWallpaperConnection != null) { 105 mWallpaperConnection.setVisibility(true); 106 } 107 } 108 109 @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) 110 @MainThread onPause()111 public void onPause() { 112 if (mWallpaperConnection != null) { 113 mWallpaperConnection.setVisibility(false); 114 } 115 } 116 117 @OnLifecycleEvent(Lifecycle.Event.ON_STOP) 118 @MainThread onStop()119 public void onStop() { 120 if (mWallpaperConnection != null) { 121 mWallpaperConnection.disconnect(); 122 mWallpaperConnection = null; 123 } 124 } 125 126 @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) 127 @MainThread onDestroy()128 public void onDestroy() { 129 if (mWallpaperConnection != null) { 130 mWallpaperConnection.disconnect(); 131 mWallpaperConnection = null; 132 } 133 134 mWallpaperSurfaceCallback.cleanUp(); 135 mWallpaperSurface.getHolder().removeCallback(mWallpaperSurfaceCallback); 136 Surface surface = mWallpaperSurface.getHolder().getSurface(); 137 if (surface != null) { 138 surface.release(); 139 } 140 } 141 142 /** 143 * Sets a wallpaper to be shown on preview screen. 144 * 145 * @param wallpaperInfo the wallpaper to preview 146 * @param listener the listener for getting the wallpaper color of {@param wallpaperInfo} 147 */ setWallpaper(WallpaperInfo wallpaperInfo, @Nullable WallpaperColorsListener listener)148 public void setWallpaper(WallpaperInfo wallpaperInfo, 149 @Nullable WallpaperColorsListener listener) { 150 mWallpaper = wallpaperInfo; 151 mWallpaperColorsListener = listener; 152 if (mFadeInScrim != null && VideoWallpaperUtils.needsFadeIn(wallpaperInfo)) { 153 mFadeInScrim.animate().cancel(); 154 mFadeInScrim.setAlpha(1f); 155 mFadeInScrim.setVisibility(View.VISIBLE); 156 } 157 setUpWallpaperPreview(); 158 } 159 setUpWallpaperPreview()160 private void setUpWallpaperPreview() { 161 ImageView homeImageWallpaper = mWallpaperSurfaceCallback.getHomeImageWallpaper(); 162 if (mWallpaper != null && homeImageWallpaper != null) { 163 homeImageWallpaper.post(() -> { 164 if (mActivity == null || mActivity.isDestroyed()) { 165 return; 166 } 167 boolean renderInImageWallpaperSurface = !(mWallpaper instanceof LiveWallpaperInfo); 168 mWallpaper.getThumbAsset(mActivity.getApplicationContext()) 169 .loadPreviewImage(mActivity, 170 renderInImageWallpaperSurface ? homeImageWallpaper : mHomePreview, 171 ResourceUtils.getColorAttr( 172 mActivity, android.R.attr.colorSecondary), 173 /* offsetToStart= */ true); 174 if (mWallpaper instanceof LiveWallpaperInfo) { 175 ImageView preview = homeImageWallpaper; 176 if (VideoWallpaperUtils.needsFadeIn(mWallpaper) && mFadeInScrim != null) { 177 preview = mFadeInScrim; 178 preview.setRenderEffect( 179 RenderEffect.createBlurEffect(150f, 150f, TileMode.CLAMP)); 180 } 181 mWallpaper.getThumbAsset(mActivity.getApplicationContext()) 182 .loadPreviewImage( 183 mActivity, 184 preview, 185 ResourceUtils.getColorAttr( 186 mActivity, android.R.attr.colorSecondary), 187 /* offsetToStart= */ true); 188 setUpLiveWallpaperPreview(mWallpaper); 189 } else { 190 // Ensure live wallpaper connection is disconnected. 191 if (mWallpaperConnection != null) { 192 mWallpaperConnection.disconnect(); 193 mWallpaperConnection = null; 194 } 195 196 // Load wallpaper color for static wallpaper. 197 if (mWallpaperColorsListener != null) { 198 WallpaperColorsLoader.getWallpaperColors( 199 mActivity, 200 mWallpaper.getThumbAsset(mActivity), 201 mWallpaperColorsListener::onWallpaperColorsChanged); 202 } 203 } 204 }); 205 } 206 } 207 setUpLiveWallpaperPreview(WallpaperInfo homeWallpaper)208 private void setUpLiveWallpaperPreview(WallpaperInfo homeWallpaper) { 209 if (mActivity == null || mActivity.isFinishing()) { 210 return; 211 } 212 213 if (mWallpaperConnection != null) { 214 mWallpaperConnection.disconnect(); 215 } 216 if (WallpaperConnection.isPreviewAvailable()) { 217 mHomePreview.getLocationOnScreen(mLivePreviewLocation); 218 mPreviewGlobalRect.set(0, 0, mHomePreview.getMeasuredWidth(), 219 mHomePreview.getMeasuredHeight()); 220 mPreviewLocalRect.set(mPreviewGlobalRect); 221 mPreviewGlobalRect.offset(mLivePreviewLocation[0], mLivePreviewLocation[1]); 222 223 mWallpaperConnection = new WallpaperConnection( 224 getWallpaperIntent(homeWallpaper.getWallpaperComponent()), mActivity, 225 new WallpaperConnectionListener() { 226 @Override 227 public void onWallpaperColorsChanged(WallpaperColors colors, 228 int displayId) { 229 if (mWallpaperColorsListener != null) { 230 mWallpaperColorsListener.onWallpaperColorsChanged(colors); 231 } 232 } 233 234 @Override 235 public void onEngineShown() { 236 if (mFadeInScrim != null && VideoWallpaperUtils.needsFadeIn( 237 homeWallpaper)) { 238 mFadeInScrim.animate().alpha(0.0f) 239 .setDuration(VideoWallpaperUtils.TRANSITION_MILLIS) 240 .withEndAction( 241 () -> mFadeInScrim.setVisibility(View.INVISIBLE)); 242 } 243 } 244 }, mWallpaperSurface, WallpaperConnection.WHICH_PREVIEW.PREVIEW_CURRENT); 245 246 mWallpaperConnection.setVisibility(true); 247 mHomePreview.post(() -> { 248 if (mWallpaperConnection != null && !mWallpaperConnection.connect()) { 249 mWallpaperConnection = null; 250 } 251 }); 252 } else { 253 // Load wallpaper color from the thumbnail. 254 if (mWallpaperColorsListener != null) { 255 WallpaperColorsLoader.getWallpaperColors( 256 mActivity, 257 mWallpaper.getThumbAsset(mActivity), 258 mWallpaperColorsListener::onWallpaperColorsChanged); 259 } 260 } 261 } 262 263 /** Updates the preview card view corner radius to match the device corner radius. */ updatePreviewCardRadius()264 private void updatePreviewCardRadius() { 265 final float screenAspectRatio = 266 ScreenSizeCalculator.getInstance().getScreenAspectRatio(mActivity); 267 CardView cardView = (CardView) mHomePreview.getParent(); 268 final int cardWidth = (int) (cardView.getMeasuredHeight() / screenAspectRatio); 269 ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams(); 270 layoutParams.width = cardWidth; 271 cardView.setLayoutParams(layoutParams); 272 cardView.setRadius(SizeCalculator.getPreviewCornerRadius(mActivity, cardWidth)); 273 } 274 getWallpaperIntent(android.app.WallpaperInfo info)275 private static Intent getWallpaperIntent(android.app.WallpaperInfo info) { 276 return new Intent(WallpaperService.SERVICE_INTERFACE) 277 .setClassName(info.getPackageName(), info.getServiceName()); 278 } 279 } 280