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