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