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