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