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