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