• 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.wallpaper.picker;
17 
18 import android.app.WallpaperColors;
19 import android.os.Bundle;
20 import android.os.Message;
21 import android.os.RemoteException;
22 import android.util.Log;
23 import android.view.Surface;
24 import android.view.SurfaceControlViewHost;
25 import android.view.SurfaceHolder;
26 import android.view.SurfaceView;
27 
28 import androidx.annotation.Nullable;
29 
30 import com.android.wallpaper.picker.common.preview.ui.binder.DefaultWorkspaceCallbackBinder;
31 import com.android.wallpaper.util.PreviewUtils;
32 import com.android.wallpaper.util.SurfaceViewUtils;
33 
34 import java.util.concurrent.atomic.AtomicBoolean;
35 
36 /** A surface holder callback that renders user's workspace on the passed in surface view. */
37 public class WorkspaceSurfaceHolderCallback implements SurfaceHolder.Callback {
38 
39     /**
40      * Listener to be called when workspace surface is updated with a new Surface Package.
41      */
42     public interface WorkspaceRenderListener {
43         /**
44          * Called on the main thread after the workspace surface is updated from the provider
45          */
onWorkspaceRendered()46         void onWorkspaceRendered();
47     }
48 
49     private static final String TAG = "WsSurfaceHolderCallback";
50     private static final String KEY_WALLPAPER_COLORS = "wallpaper_colors";
51     public static final int MESSAGE_ID_COLOR_OVERRIDE = 1234;
52     public static final String KEY_COLOR_OVERRIDE = "color_override"; // ColorInt Encoded as string
53     private final SurfaceView mWorkspaceSurface;
54     private final PreviewUtils mPreviewUtils;
55     private final boolean mShouldUseWallpaperColors;
56     private final AtomicBoolean mRequestPending = new AtomicBoolean(false);
57 
58     private WallpaperColors mWallpaperColors;
59     private boolean mHideBottomRow;
60     private boolean mIsWallpaperColorsReady;
61     private Surface mLastSurface;
62     private Message mCallback;
63     private Message mDelayedMessage;
64     private WorkspaceRenderListener mListener;
65 
66     private boolean mNeedsToCleanUp;
67     @Nullable private final Bundle mExtras;
68 
69     private int mWidth = -1;
70 
71     private int mHeight = -1;
72 
WorkspaceSurfaceHolderCallback( SurfaceView workspaceSurface, PreviewUtils previewUtils)73     public WorkspaceSurfaceHolderCallback(
74             SurfaceView workspaceSurface,
75             PreviewUtils previewUtils) {
76         this(workspaceSurface, previewUtils, false, null);
77     }
78 
79     /**
80      * Creates a new instance of {@link WorkspaceSurfaceHolderCallback} specifying if wallpaper
81      * colors should be used to preview the workspace.
82      *
83      * @param shouldUseWallpaperColors if true, the workspace preview won't be requested until both
84      *                                 the surface is created and wallpaper colors are set via
85      *                                 {@link #setWallpaperColors(WallpaperColors)}
86      */
WorkspaceSurfaceHolderCallback( SurfaceView workspaceSurface, PreviewUtils previewUtils, boolean shouldUseWallpaperColors)87     public WorkspaceSurfaceHolderCallback(
88             SurfaceView workspaceSurface,
89             PreviewUtils previewUtils,
90             boolean shouldUseWallpaperColors) {
91         this(
92                 workspaceSurface,
93                 previewUtils,
94                 shouldUseWallpaperColors,
95                 null);
96     }
97 
WorkspaceSurfaceHolderCallback( SurfaceView workspaceSurface, PreviewUtils previewUtils, @Nullable Bundle extras)98     public WorkspaceSurfaceHolderCallback(
99             SurfaceView workspaceSurface,
100             PreviewUtils previewUtils,
101             @Nullable Bundle extras) {
102         this(workspaceSurface, previewUtils, false, extras);
103     }
104 
WorkspaceSurfaceHolderCallback( SurfaceView workspaceSurface, PreviewUtils previewUtils, boolean shouldUseWallpaperColors, @Nullable Bundle extras)105     private WorkspaceSurfaceHolderCallback(
106             SurfaceView workspaceSurface,
107             PreviewUtils previewUtils,
108             boolean shouldUseWallpaperColors,
109             @Nullable Bundle extras) {
110         mWorkspaceSurface = workspaceSurface;
111         mPreviewUtils = previewUtils;
112         mShouldUseWallpaperColors = shouldUseWallpaperColors;
113         mExtras = extras;
114     }
115 
116     @Override
surfaceCreated(SurfaceHolder holder)117     public void surfaceCreated(SurfaceHolder holder) {
118         if (mPreviewUtils.supportsPreview() && mLastSurface != holder.getSurface()) {
119             mLastSurface = holder.getSurface();
120             maybeRenderPreview();
121         }
122     }
123 
124     /**
125      * Set the current wallpaper's colors. This method must be called  if this instance was created
126      * with shouldUseWallpaperColors = true (even with {@code null} colors), and conversely, calling
127      * this method when {@code shouldUseWallpaperColors = false} will be a no-op.
128      *
129      * @param colors WallpaperColors extracted from the current wallpaper preview, or {@code null}
130      *               if none are available.
131      * @see #WorkspaceSurfaceHolderCallback(SurfaceView, PreviewUtils, boolean)
132      */
setWallpaperColors(@ullable WallpaperColors colors)133     public void setWallpaperColors(@Nullable WallpaperColors colors) {
134         if (!mShouldUseWallpaperColors) {
135             return;
136         }
137         mWallpaperColors = colors;
138         mIsWallpaperColorsReady = true;
139     }
140 
141     /**
142      * Set the current flag if we should hide the workspace bottom row.
143      */
setHideBottomRow(boolean hideBottomRow)144     public void setHideBottomRow(boolean hideBottomRow) {
145         mHideBottomRow = hideBottomRow;
146     }
147 
setListener(WorkspaceRenderListener listener)148     public void setListener(WorkspaceRenderListener listener) {
149         mListener = listener;
150     }
151 
152     /**
153      * Render the preview with the current selected {@link #mWallpaperColors} and
154      * {@link #mHideBottomRow}.
155      */
maybeRenderPreview()156     public void maybeRenderPreview() {
157         if ((mShouldUseWallpaperColors && !mIsWallpaperColorsReady) || mLastSurface == null) {
158             return;
159         }
160         mRequestPending.set(true);
161         requestPreview(mWorkspaceSurface, (result) -> {
162             mRequestPending.set(false);
163             if (result != null && mLastSurface != null) {
164                 final SurfaceControlViewHost.SurfacePackage pkg =
165                         SurfaceViewUtils.INSTANCE.getSurfacePackage(result);
166                 if (pkg != null) {
167                     mWorkspaceSurface.setChildSurfacePackage(pkg);
168                 } else {
169                     Log.w(TAG,
170                             "Result bundle from rendering preview does not contain a child "
171                                     + "surface package.");
172                 }
173                 mCallback = SurfaceViewUtils.INSTANCE.getCallback(result);
174                 if (mCallback != null && mDelayedMessage != null) {
175                     try {
176                         mCallback.replyTo.send(mDelayedMessage);
177                     } catch (RemoteException e) {
178                         Log.w(TAG, "Couldn't send message to workspace preview", e);
179                     }
180                     mDelayedMessage = null;
181                 }
182                 if (mNeedsToCleanUp) {
183                     cleanUp();
184                 } else if (mListener != null) {
185                     mListener.onWorkspaceRendered();
186                 }
187             }
188         });
189     }
190 
191     @Override
surfaceChanged(SurfaceHolder holder, int format, int width, int height)192     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
193         if ((mWidth != -1 || mHeight != -1) && (mWidth != width || mHeight != height)) {
194             maybeRenderPreview();
195         }
196         mWidth = width;
197         mHeight = height;
198     }
199 
200     @Override
surfaceDestroyed(SurfaceHolder holder)201     public void surfaceDestroyed(SurfaceHolder holder) {
202     }
203 
204     /**
205      * Sends a message to the remote renderer.
206      *
207      * @param what An ID for the message (the remote side can pick this up through
208      * {@link Message#what}.
209      * @param bundle The data of the message (the remote side can pick this up through
210      * {@link Message#getData()}.
211      */
send(final int what, @Nullable Bundle bundle)212     public void send(final int what, @Nullable Bundle bundle) {
213         final Message message = new Message();
214         message.what = what;
215         message.setData(bundle);
216         if (mCallback != null) {
217             try {
218                 mCallback.replyTo.send(message);
219             } catch (RemoteException e) {
220                 Log.w(TAG, "Couldn't send message to workspace preview", e);
221             }
222         } else {
223             mDelayedMessage = message;
224         }
225     }
226 
cleanUp()227     public void cleanUp() {
228         if (mCallback != null) {
229             try {
230                 mCallback.replyTo.send(mCallback);
231                 mNeedsToCleanUp = false;
232             } catch (RemoteException e) {
233                 Log.w(TAG, "Couldn't call cleanup on workspace preview", e);
234             } finally {
235                 mCallback = null;
236             }
237         } else {
238             if (mRequestPending.get()) {
239                 mNeedsToCleanUp = true;
240             }
241         }
242     }
243 
resetLastSurface()244     public void resetLastSurface() {
245         mLastSurface = null;
246     }
247 
requestPreview(SurfaceView workspaceSurface, PreviewUtils.WorkspacePreviewCallback callback)248     protected void requestPreview(SurfaceView workspaceSurface,
249             PreviewUtils.WorkspacePreviewCallback callback) {
250         if (workspaceSurface.getDisplay() == null) {
251             Log.w(TAG,
252                     "No display ID, avoiding asking for workspace preview, lest WallpaperPicker "
253                             + "crash");
254             return;
255         }
256         Bundle request = SurfaceViewUtils.INSTANCE.createSurfaceViewRequest(workspaceSurface,
257                 mExtras);
258         if (mWallpaperColors != null) {
259             request.putParcelable(KEY_WALLPAPER_COLORS, mWallpaperColors);
260         }
261         request.putBoolean(DefaultWorkspaceCallbackBinder.KEY_HIDE_BOTTOM_ROW, mHideBottomRow);
262         mPreviewUtils.renderPreview(request, callback);
263     }
264 }
265