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