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.content.Context; 20 import android.os.Bundle; 21 import android.os.Message; 22 import android.os.RemoteException; 23 import android.util.Log; 24 import android.view.Surface; 25 import android.view.SurfaceHolder; 26 import android.view.SurfaceView; 27 28 import androidx.annotation.Nullable; 29 30 import com.android.wallpaper.R; 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 private final SurfaceView mWorkspaceSurface; 52 private final PreviewUtils mPreviewUtils; 53 private final boolean mShouldUseWallpaperColors; 54 private final AtomicBoolean mRequestPending = new AtomicBoolean(false); 55 56 private WallpaperColors mWallpaperColors; 57 private boolean mIsWallpaperColorsReady; 58 private Surface mLastSurface; 59 private Message mCallback; 60 private WorkspaceRenderListener mListener; 61 62 private boolean mNeedsToCleanUp; 63 WorkspaceSurfaceHolderCallback(SurfaceView workspaceSurface, Context context)64 public WorkspaceSurfaceHolderCallback(SurfaceView workspaceSurface, Context context) { 65 this(workspaceSurface, context, false); 66 } 67 68 /** 69 * Creates a new instance of {@link WorkspaceSurfaceHolderCallback} specifying if wallpaper 70 * colors should be used to preview the workspace. 71 * 72 * @param shouldUseWallpaperColors if true, the workspace preview won't be requested until both 73 * the surface is created and wallpaper colors are set via 74 * {@link #setWallpaperColors(WallpaperColors)} 75 */ WorkspaceSurfaceHolderCallback(SurfaceView workspaceSurface, Context context, boolean shouldUseWallpaperColors)76 public WorkspaceSurfaceHolderCallback(SurfaceView workspaceSurface, Context context, 77 boolean shouldUseWallpaperColors) { 78 mWorkspaceSurface = workspaceSurface; 79 mPreviewUtils = new PreviewUtils(context, 80 context.getString(R.string.grid_control_metadata_name)); 81 mShouldUseWallpaperColors = shouldUseWallpaperColors; 82 } 83 84 @Override surfaceCreated(SurfaceHolder holder)85 public void surfaceCreated(SurfaceHolder holder) { 86 if (mPreviewUtils.supportsPreview() && mLastSurface != holder.getSurface()) { 87 mLastSurface = holder.getSurface(); 88 maybeRenderPreview(); 89 } 90 } 91 92 /** 93 * Set the current wallpaper's colors. This method must be called if this instance was created 94 * with shouldUseWallpaperColors = true (even with {@code null} colors), and conversely, calling 95 * this method when {@code shouldUseWallpaperColors = false} will be a no-op. 96 * 97 * @param colors WallpaperColors extracted from the current wallpaper preview, or {@code null} 98 * if none are available. 99 * @see #WorkspaceSurfaceHolderCallback(SurfaceView, Context, boolean) 100 */ setWallpaperColors(@ullable WallpaperColors colors)101 public void setWallpaperColors(@Nullable WallpaperColors colors) { 102 if (!mShouldUseWallpaperColors) { 103 return; 104 } 105 mWallpaperColors = colors; 106 mIsWallpaperColorsReady = true; 107 maybeRenderPreview(); 108 } 109 setListener(WorkspaceRenderListener listener)110 public void setListener(WorkspaceRenderListener listener) { 111 mListener = listener; 112 } 113 maybeRenderPreview()114 private void maybeRenderPreview() { 115 if ((mShouldUseWallpaperColors && !mIsWallpaperColorsReady) || mLastSurface == null) { 116 return; 117 } 118 119 mRequestPending.set(true); 120 requestPreview(mWorkspaceSurface, (result) -> { 121 mRequestPending.set(false); 122 if (result != null && mLastSurface != null) { 123 mWorkspaceSurface.setChildSurfacePackage( 124 SurfaceViewUtils.getSurfacePackage(result)); 125 126 mCallback = SurfaceViewUtils.getCallback(result); 127 128 if (mNeedsToCleanUp) { 129 cleanUp(); 130 } else if (mListener != null) { 131 mListener.onWorkspaceRendered(); 132 } 133 } 134 }); 135 } 136 137 @Override surfaceChanged(SurfaceHolder holder, int format, int width, int height)138 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } 139 140 @Override surfaceDestroyed(SurfaceHolder holder)141 public void surfaceDestroyed(SurfaceHolder holder) { 142 } 143 cleanUp()144 public void cleanUp() { 145 if (mCallback != null) { 146 try { 147 mCallback.replyTo.send(mCallback); 148 mNeedsToCleanUp = false; 149 } catch (RemoteException e) { 150 Log.w(TAG, "Couldn't call cleanup on workspace preview", e); 151 } finally { 152 mCallback = null; 153 } 154 } else { 155 if (mRequestPending.get()) { 156 mNeedsToCleanUp = true; 157 } 158 } 159 } 160 resetLastSurface()161 public void resetLastSurface() { 162 mLastSurface = null; 163 } 164 requestPreview(SurfaceView workspaceSurface, PreviewUtils.WorkspacePreviewCallback callback)165 protected void requestPreview(SurfaceView workspaceSurface, 166 PreviewUtils.WorkspacePreviewCallback callback) { 167 if (workspaceSurface.getDisplay() == null) { 168 Log.w(TAG, 169 "No display ID, avoiding asking for workspace preview, lest WallpaperPicker " 170 + "crash"); 171 return; 172 } 173 Bundle request = SurfaceViewUtils.createSurfaceViewRequest(workspaceSurface); 174 if (mWallpaperColors != null) { 175 request.putParcelable(KEY_WALLPAPER_COLORS, mWallpaperColors); 176 } 177 mPreviewUtils.renderPreview(request, callback); 178 } 179 } 180