• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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