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.SurfaceHolder; 25 import android.view.SurfaceView; 26 27 import androidx.annotation.Nullable; 28 29 import com.android.wallpaper.util.PreviewUtils; 30 import com.android.wallpaper.util.SurfaceViewUtils; 31 32 import java.util.concurrent.atomic.AtomicBoolean; 33 34 /** A surface holder callback that renders user's workspace on the passed in surface view. */ 35 public class WorkspaceSurfaceHolderCallback implements SurfaceHolder.Callback { 36 37 /** 38 * Listener to be called when workspace surface is updated with a new Surface Package. 39 */ 40 public interface WorkspaceRenderListener { 41 /** 42 * Called on the main thread after the workspace surface is updated from the provider 43 */ onWorkspaceRendered()44 void onWorkspaceRendered(); 45 } 46 47 private static final String TAG = "WsSurfaceHolderCallback"; 48 private static final String KEY_WALLPAPER_COLORS = "wallpaper_colors"; 49 public static final int MESSAGE_ID_UPDATE_PREVIEW = 1337; 50 public static final String KEY_HIDE_BOTTOM_ROW = "hide_bottom_row"; 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 mWorkspaceSurface.setChildSurfacePackage( 165 SurfaceViewUtils.getSurfacePackage(result)); 166 mCallback = SurfaceViewUtils.getCallback(result); 167 if (mCallback != null && mDelayedMessage != null) { 168 try { 169 mCallback.replyTo.send(mDelayedMessage); 170 } catch (RemoteException e) { 171 Log.w(TAG, "Couldn't send message to workspace preview", e); 172 } 173 mDelayedMessage = null; 174 } 175 if (mNeedsToCleanUp) { 176 cleanUp(); 177 } else if (mListener != null) { 178 mListener.onWorkspaceRendered(); 179 } 180 } 181 }); 182 } 183 184 @Override surfaceChanged(SurfaceHolder holder, int format, int width, int height)185 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 186 if ((mWidth != -1 || mHeight != -1) && (mWidth != width || mHeight != height)) { 187 maybeRenderPreview(); 188 } 189 mWidth = width; 190 mHeight = height; 191 } 192 193 @Override surfaceDestroyed(SurfaceHolder holder)194 public void surfaceDestroyed(SurfaceHolder holder) { 195 } 196 197 /** 198 * Sends a message to the remote renderer. 199 * 200 * @param what An ID for the message (the remote side can pick this up through 201 * {@link Message#what}. 202 * @param bundle The data of the message (the remote side can pick this up through 203 * {@link Message#getData()}. 204 */ send(final int what, @Nullable Bundle bundle)205 public void send(final int what, @Nullable Bundle bundle) { 206 final Message message = new Message(); 207 message.what = what; 208 message.setData(bundle); 209 if (mCallback != null) { 210 try { 211 mCallback.replyTo.send(message); 212 } catch (RemoteException e) { 213 Log.w(TAG, "Couldn't send message to workspace preview", e); 214 } 215 } else { 216 mDelayedMessage = message; 217 } 218 } 219 cleanUp()220 public void cleanUp() { 221 if (mCallback != null) { 222 try { 223 mCallback.replyTo.send(mCallback); 224 mNeedsToCleanUp = false; 225 } catch (RemoteException e) { 226 Log.w(TAG, "Couldn't call cleanup on workspace preview", e); 227 } finally { 228 mCallback = null; 229 } 230 } else { 231 if (mRequestPending.get()) { 232 mNeedsToCleanUp = true; 233 } 234 } 235 } 236 resetLastSurface()237 public void resetLastSurface() { 238 mLastSurface = null; 239 } 240 requestPreview(SurfaceView workspaceSurface, PreviewUtils.WorkspacePreviewCallback callback)241 protected void requestPreview(SurfaceView workspaceSurface, 242 PreviewUtils.WorkspacePreviewCallback callback) { 243 if (workspaceSurface.getDisplay() == null) { 244 Log.w(TAG, 245 "No display ID, avoiding asking for workspace preview, lest WallpaperPicker " 246 + "crash"); 247 return; 248 } 249 Bundle request = SurfaceViewUtils.createSurfaceViewRequest(workspaceSurface, mExtras); 250 if (mWallpaperColors != null) { 251 request.putParcelable(KEY_WALLPAPER_COLORS, mWallpaperColors); 252 } 253 request.putBoolean(KEY_HIDE_BOTTOM_ROW, mHideBottomRow); 254 mPreviewUtils.renderPreview(request, callback); 255 } 256 } 257