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