1 /* 2 * Copyright (C) 2018 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 17 package android.view; 18 19 import android.graphics.Matrix; 20 import android.graphics.Rect; 21 import android.view.SurfaceControl.Transaction; 22 23 import com.android.internal.annotations.VisibleForTesting; 24 25 import java.util.function.Consumer; 26 27 /** 28 * Helper class to apply surface transactions in sync with RenderThread. 29 * @hide 30 */ 31 public class SyncRtSurfaceTransactionApplier { 32 33 public static final int FLAG_ALL = 0xffffffff; 34 public static final int FLAG_ALPHA = 1; 35 public static final int FLAG_MATRIX = 1 << 1; 36 public static final int FLAG_WINDOW_CROP = 1 << 2; 37 public static final int FLAG_LAYER = 1 << 3; 38 public static final int FLAG_CORNER_RADIUS = 1 << 4; 39 public static final int FLAG_BACKGROUND_BLUR_RADIUS = 1 << 5; 40 public static final int FLAG_VISIBILITY = 1 << 6; 41 public static final int FLAG_TRANSACTION = 1 << 7; 42 43 private SurfaceControl mTargetSc; 44 private final ViewRootImpl mTargetViewRootImpl; 45 private final float[] mTmpFloat9 = new float[9]; 46 47 /** 48 * @param targetView The view in the surface that acts as synchronization anchor. 49 */ SyncRtSurfaceTransactionApplier(View targetView)50 public SyncRtSurfaceTransactionApplier(View targetView) { 51 mTargetViewRootImpl = targetView != null ? targetView.getViewRootImpl() : null; 52 } 53 54 /** 55 * Schedules applying surface parameters on the next frame. 56 * 57 * @param params The surface parameters to apply. 58 */ scheduleApply(final SurfaceParams... params)59 public void scheduleApply(final SurfaceParams... params) { 60 if (mTargetViewRootImpl == null) { 61 return; 62 } 63 mTargetSc = mTargetViewRootImpl.getSurfaceControl(); 64 final Transaction t = new Transaction(); 65 applyParams(t, params); 66 67 mTargetViewRootImpl.registerRtFrameCallback(frame -> { 68 if (mTargetSc == null || !mTargetSc.isValid()) { 69 return; 70 } 71 applyTransaction(t, frame); 72 }); 73 74 // Make sure a frame gets scheduled. 75 mTargetViewRootImpl.getView().invalidate(); 76 } 77 78 /** 79 * Applies surface parameters on the next frame. 80 * @param t transaction to apply all parameters in. 81 * @param frame frame to synchronize to. Set -1 when sync is not required. 82 * @param params The surface parameters to apply. 83 */ applyParams(Transaction t, final SurfaceParams... params)84 void applyParams(Transaction t, final SurfaceParams... params) { 85 for (int i = params.length - 1; i >= 0; i--) { 86 SurfaceParams surfaceParams = params[i]; 87 SurfaceControl surface = surfaceParams.surface; 88 applyParams(t, surfaceParams, mTmpFloat9); 89 } 90 } 91 applyTransaction(Transaction t, long frame)92 void applyTransaction(Transaction t, long frame) { 93 if (mTargetViewRootImpl != null) { 94 mTargetViewRootImpl.mergeWithNextTransaction(t, frame); 95 } else { 96 t.apply(); 97 } 98 } 99 applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9)100 public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) { 101 if ((params.flags & FLAG_TRANSACTION) != 0) { 102 t.merge(params.mergeTransaction); 103 } 104 105 if ((params.flags & FLAG_MATRIX) != 0) { 106 t.setMatrix(params.surface, params.matrix, tmpFloat9); 107 } 108 if ((params.flags & FLAG_WINDOW_CROP) != 0) { 109 t.setWindowCrop(params.surface, params.windowCrop); 110 } 111 if ((params.flags & FLAG_ALPHA) != 0) { 112 t.setAlpha(params.surface, params.alpha); 113 } 114 if ((params.flags & FLAG_LAYER) != 0) { 115 t.setLayer(params.surface, params.layer); 116 } 117 if ((params.flags & FLAG_CORNER_RADIUS) != 0) { 118 t.setCornerRadius(params.surface, params.cornerRadius); 119 } 120 if ((params.flags & FLAG_BACKGROUND_BLUR_RADIUS) != 0) { 121 t.setBackgroundBlurRadius(params.surface, params.backgroundBlurRadius); 122 } 123 if ((params.flags & FLAG_VISIBILITY) != 0) { 124 if (params.visible) { 125 t.show(params.surface); 126 } else { 127 t.hide(params.surface); 128 } 129 } 130 } 131 132 /** 133 * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is 134 * attached if necessary. 135 */ create(final View targetView, final Consumer<SyncRtSurfaceTransactionApplier> callback)136 public static void create(final View targetView, 137 final Consumer<SyncRtSurfaceTransactionApplier> callback) { 138 if (targetView == null) { 139 // No target view, no applier 140 callback.accept(null); 141 } else if (targetView.getViewRootImpl() != null) { 142 // Already attached, we're good to go 143 callback.accept(new SyncRtSurfaceTransactionApplier(targetView)); 144 } else { 145 // Haven't been attached before we can get the view root 146 targetView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { 147 @Override 148 public void onViewAttachedToWindow(View v) { 149 targetView.removeOnAttachStateChangeListener(this); 150 callback.accept(new SyncRtSurfaceTransactionApplier(targetView)); 151 } 152 153 @Override 154 public void onViewDetachedFromWindow(View v) { 155 // Do nothing 156 } 157 }); 158 } 159 } 160 161 public static class SurfaceParams { 162 163 public static class Builder { 164 final SurfaceControl surface; 165 int flags; 166 float alpha; 167 float cornerRadius; 168 int backgroundBlurRadius; 169 Matrix matrix; 170 Rect windowCrop; 171 int layer; 172 boolean visible; 173 Transaction mergeTransaction; 174 175 /** 176 * @param surface The surface to modify. 177 */ Builder(SurfaceControl surface)178 public Builder(SurfaceControl surface) { 179 this.surface = surface; 180 } 181 182 /** 183 * @param alpha The alpha value to apply to the surface. 184 * @return this Builder 185 */ withAlpha(float alpha)186 public Builder withAlpha(float alpha) { 187 this.alpha = alpha; 188 flags |= FLAG_ALPHA; 189 return this; 190 } 191 192 /** 193 * @param matrix The matrix to apply to the surface. 194 * @return this Builder 195 */ withMatrix(Matrix matrix)196 public Builder withMatrix(Matrix matrix) { 197 this.matrix = new Matrix(matrix); 198 flags |= FLAG_MATRIX; 199 return this; 200 } 201 202 /** 203 * @param windowCrop The window crop to apply to the surface. 204 * @return this Builder 205 */ withWindowCrop(Rect windowCrop)206 public Builder withWindowCrop(Rect windowCrop) { 207 this.windowCrop = new Rect(windowCrop); 208 flags |= FLAG_WINDOW_CROP; 209 return this; 210 } 211 212 /** 213 * @param layer The layer to assign the surface. 214 * @return this Builder 215 */ withLayer(int layer)216 public Builder withLayer(int layer) { 217 this.layer = layer; 218 flags |= FLAG_LAYER; 219 return this; 220 } 221 222 /** 223 * @param radius the Radius for rounded corners to apply to the surface. 224 * @return this Builder 225 */ withCornerRadius(float radius)226 public Builder withCornerRadius(float radius) { 227 this.cornerRadius = radius; 228 flags |= FLAG_CORNER_RADIUS; 229 return this; 230 } 231 232 /** 233 * @param radius the Radius for blur to apply to the background surfaces. 234 * @return this Builder 235 */ withBackgroundBlur(int radius)236 public Builder withBackgroundBlur(int radius) { 237 this.backgroundBlurRadius = radius; 238 flags |= FLAG_BACKGROUND_BLUR_RADIUS; 239 return this; 240 } 241 242 /** 243 * @param visible The visibility to apply to the surface. 244 * @return this Builder 245 */ withVisibility(boolean visible)246 public Builder withVisibility(boolean visible) { 247 this.visible = visible; 248 flags |= FLAG_VISIBILITY; 249 return this; 250 } 251 252 /** 253 * @param mergeTransaction The transaction to apply to the surface. Note this is applied 254 * first before all the other properties. 255 * @return this Builder 256 */ withMergeTransaction(Transaction mergeTransaction)257 public Builder withMergeTransaction(Transaction mergeTransaction) { 258 this.mergeTransaction = mergeTransaction; 259 flags |= FLAG_TRANSACTION; 260 return this; 261 } 262 263 /** 264 * @return a new SurfaceParams instance 265 */ build()266 public SurfaceParams build() { 267 return new SurfaceParams(surface, flags, alpha, matrix, windowCrop, layer, 268 cornerRadius, backgroundBlurRadius, visible, mergeTransaction); 269 } 270 } 271 SurfaceParams(SurfaceControl surface, int params, float alpha, Matrix matrix, Rect windowCrop, int layer, float cornerRadius, int backgroundBlurRadius, boolean visible, Transaction mergeTransaction)272 private SurfaceParams(SurfaceControl surface, int params, float alpha, Matrix matrix, 273 Rect windowCrop, int layer, float cornerRadius, 274 int backgroundBlurRadius, boolean visible, Transaction mergeTransaction) { 275 this.flags = params; 276 this.surface = surface; 277 this.alpha = alpha; 278 this.matrix = matrix; 279 this.windowCrop = windowCrop; 280 this.layer = layer; 281 this.cornerRadius = cornerRadius; 282 this.backgroundBlurRadius = backgroundBlurRadius; 283 this.visible = visible; 284 this.mergeTransaction = mergeTransaction; 285 } 286 287 private final int flags; 288 289 @VisibleForTesting 290 public final SurfaceControl surface; 291 292 @VisibleForTesting 293 public final float alpha; 294 295 @VisibleForTesting 296 public final float cornerRadius; 297 298 @VisibleForTesting 299 public final int backgroundBlurRadius; 300 301 @VisibleForTesting 302 public final Matrix matrix; 303 304 @VisibleForTesting 305 public final Rect windowCrop; 306 307 @VisibleForTesting 308 public final int layer; 309 310 public final boolean visible; 311 312 public final Transaction mergeTransaction; 313 } 314 } 315