• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 package com.android.internal.widget.remotecompose.core.operations.layout;
17 
18 import android.annotation.NonNull;
19 import android.annotation.Nullable;
20 
21 import com.android.internal.widget.remotecompose.core.Operation;
22 import com.android.internal.widget.remotecompose.core.OperationInterface;
23 import com.android.internal.widget.remotecompose.core.PaintContext;
24 import com.android.internal.widget.remotecompose.core.RemoteContext;
25 import com.android.internal.widget.remotecompose.core.TouchListener;
26 import com.android.internal.widget.remotecompose.core.VariableSupport;
27 import com.android.internal.widget.remotecompose.core.operations.BitmapData;
28 import com.android.internal.widget.remotecompose.core.operations.ComponentData;
29 import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
30 import com.android.internal.widget.remotecompose.core.operations.MatrixSave;
31 import com.android.internal.widget.remotecompose.core.operations.MatrixTranslate;
32 import com.android.internal.widget.remotecompose.core.operations.layout.animation.AnimationSpec;
33 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentModifiers;
34 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentVisibilityOperation;
35 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.DimensionModifierOperation;
36 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.GraphicsLayerModifierOperation;
37 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightInModifierOperation;
38 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightModifierOperation;
39 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation;
40 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.PaddingModifierOperation;
41 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation;
42 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthInModifierOperation;
43 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthModifierOperation;
44 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ZIndexModifierOperation;
45 import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
46 import com.android.internal.widget.remotecompose.core.serialize.SerializeTags;
47 
48 import java.util.ArrayList;
49 import java.util.HashMap;
50 
51 /** Component with modifiers and children */
52 public class LayoutComponent extends Component {
53 
54     @Nullable protected WidthModifierOperation mWidthModifier = null;
55     @Nullable protected HeightModifierOperation mHeightModifier = null;
56     @Nullable protected ZIndexModifierOperation mZIndexModifier = null;
57     @Nullable protected GraphicsLayerModifierOperation mGraphicsLayerModifier = null;
58 
59     protected float mPaddingLeft = 0f;
60     protected float mPaddingRight = 0f;
61     protected float mPaddingTop = 0f;
62     protected float mPaddingBottom = 0f;
63 
64     float mScrollX = 0f;
65     float mScrollY = 0f;
66 
67     @Nullable protected ScrollDelegate mHorizontalScrollDelegate = null;
68     @Nullable protected ScrollDelegate mVerticalScrollDelegate = null;
69 
70     @NonNull protected ComponentModifiers mComponentModifiers = new ComponentModifiers();
71 
72     @NonNull
73     protected ArrayList<Component> mChildrenComponents = new ArrayList<>(); // members are not null
74 
75     protected boolean mChildrenHaveZIndex = false;
76     private CanvasOperations mDrawContentOperations;
77 
LayoutComponent( @ullable Component parent, int componentId, int animationId, float x, float y, float width, float height)78     public LayoutComponent(
79             @Nullable Component parent,
80             int componentId,
81             int animationId,
82             float x,
83             float y,
84             float width,
85             float height) {
86         super(parent, componentId, animationId, x, y, width, height);
87     }
88 
getPaddingLeft()89     public float getPaddingLeft() {
90         return mPaddingLeft;
91     }
92 
getPaddingTop()93     public float getPaddingTop() {
94         return mPaddingTop;
95     }
96 
getPaddingRight()97     public float getPaddingRight() {
98         return mPaddingRight;
99     }
100 
getPaddingBottom()101     public float getPaddingBottom() {
102         return mPaddingBottom;
103     }
104 
105     @Nullable
getWidthModifier()106     public WidthModifierOperation getWidthModifier() {
107         return mWidthModifier;
108     }
109 
110     @Nullable
getHeightModifier()111     public HeightModifierOperation getHeightModifier() {
112         return mHeightModifier;
113     }
114 
115     @Override
getZIndex()116     public float getZIndex() {
117         if (mZIndexModifier != null) {
118             return mZIndexModifier.getValue();
119         }
120         return mZIndex;
121     }
122 
123     @Nullable protected LayoutComponentContent mContent = null;
124 
125     // Should be removed after ImageLayout is in
126     private static final boolean USE_IMAGE_TEMP_FIX = true;
127 
128     /**
129      * Set canvas operations op on this component
130      *
131      * @param operations
132      */
setCanvasOperations(@ullable CanvasOperations operations)133     public void setCanvasOperations(@Nullable CanvasOperations operations) {
134         mDrawContentOperations = operations;
135     }
136 
137     @Override
inflate()138     public void inflate() {
139         ArrayList<Operation> data = new ArrayList<>();
140         ArrayList<Operation> supportedOperations = new ArrayList<>();
141 
142         for (Operation op : mList) {
143             if (op instanceof LayoutComponentContent) {
144                 mContent = (LayoutComponentContent) op;
145                 mContent.mParent = this;
146                 mChildrenComponents.clear();
147                 LayoutComponentContent content = (LayoutComponentContent) op;
148                 content.getComponents(mChildrenComponents);
149                 if (USE_IMAGE_TEMP_FIX) {
150                     if (mChildrenComponents.isEmpty() && !mContent.mList.isEmpty()) {
151                         CanvasContent canvasContent =
152                                 new CanvasContent(-1, 0f, 0f, 0f, 0f, this, -1);
153                         for (Operation opc : mContent.mList) {
154                             if (opc instanceof BitmapData) {
155                                 canvasContent.mList.add(opc);
156                                 int w = ((BitmapData) opc).getWidth();
157                                 int h = ((BitmapData) opc).getHeight();
158                                 canvasContent.setWidth(w);
159                                 canvasContent.setHeight(h);
160                             } else {
161                                 if (!((opc instanceof MatrixTranslate)
162                                         || (opc instanceof MatrixSave)
163                                         || (opc instanceof MatrixRestore))) {
164                                     canvasContent.mList.add(opc);
165                                 }
166                             }
167                         }
168                         if (!canvasContent.mList.isEmpty()) {
169                             mContent.mList.clear();
170                             mChildrenComponents.add(canvasContent);
171                             canvasContent.inflate();
172                         }
173                     } else {
174                         content.getData(data);
175                     }
176                 } else {
177                     content.getData(data);
178                 }
179             } else if (op instanceof ModifierOperation) {
180                 if (op instanceof ComponentVisibilityOperation) {
181                     ((ComponentVisibilityOperation) op).setParent(this);
182                 }
183                 if (op instanceof ScrollModifierOperation) {
184                     ((ScrollModifierOperation) op).inflate(this);
185                 }
186                 mComponentModifiers.add((ModifierOperation) op);
187             } else if (op instanceof ComponentData) {
188                 supportedOperations.add(op);
189                 if (op instanceof TouchListener) {
190                     ((TouchListener) op).setComponent(this);
191                 }
192             } else {
193                 // nothing
194             }
195         }
196 
197         mList.clear();
198         mList.addAll(data);
199         mList.addAll(supportedOperations);
200         mList.add(mComponentModifiers);
201         for (Component c : mChildrenComponents) {
202             c.mParent = this;
203             mList.add(c);
204             if (c instanceof LayoutComponent && ((LayoutComponent) c).mZIndexModifier != null) {
205                 mChildrenHaveZIndex = true;
206             }
207         }
208 
209         mX = 0f;
210         mY = 0f;
211         mPaddingLeft = 0f;
212         mPaddingTop = 0f;
213         mPaddingRight = 0f;
214         mPaddingBottom = 0f;
215 
216         WidthInModifierOperation widthInConstraints = null;
217         HeightInModifierOperation heightInConstraints = null;
218 
219         for (OperationInterface op : mComponentModifiers.getList()) {
220             if (op instanceof PaddingModifierOperation) {
221                 // We are accumulating padding modifiers to compute the margin
222                 // until we hit a dimension; the computed padding for the
223                 // content simply accumulate all the padding modifiers.
224                 float left = ((PaddingModifierOperation) op).getLeft();
225                 float right = ((PaddingModifierOperation) op).getRight();
226                 float top = ((PaddingModifierOperation) op).getTop();
227                 float bottom = ((PaddingModifierOperation) op).getBottom();
228                 mPaddingLeft += left;
229                 mPaddingTop += top;
230                 mPaddingRight += right;
231                 mPaddingBottom += bottom;
232             } else if (op instanceof WidthModifierOperation && mWidthModifier == null) {
233                 mWidthModifier = (WidthModifierOperation) op;
234             } else if (op instanceof HeightModifierOperation && mHeightModifier == null) {
235                 mHeightModifier = (HeightModifierOperation) op;
236             } else if (op instanceof WidthInModifierOperation) {
237                 widthInConstraints = (WidthInModifierOperation) op;
238             } else if (op instanceof HeightInModifierOperation) {
239                 heightInConstraints = (HeightInModifierOperation) op;
240             } else if (op instanceof ZIndexModifierOperation) {
241                 mZIndexModifier = (ZIndexModifierOperation) op;
242             } else if (op instanceof GraphicsLayerModifierOperation) {
243                 mGraphicsLayerModifier = (GraphicsLayerModifierOperation) op;
244             } else if (op instanceof AnimationSpec) {
245                 mAnimationSpec = (AnimationSpec) op;
246             } else if (op instanceof ScrollDelegate) {
247                 ScrollDelegate scrollDelegate = (ScrollDelegate) op;
248                 if (scrollDelegate.handlesHorizontalScroll()) {
249                     mHorizontalScrollDelegate = scrollDelegate;
250                 }
251                 if (scrollDelegate.handlesVerticalScroll()) {
252                     mVerticalScrollDelegate = scrollDelegate;
253                 }
254             }
255         }
256         if (mWidthModifier == null) {
257             mWidthModifier = new WidthModifierOperation(DimensionModifierOperation.Type.WRAP);
258         }
259         if (mHeightModifier == null) {
260             mHeightModifier = new HeightModifierOperation(DimensionModifierOperation.Type.WRAP);
261         }
262         if (widthInConstraints != null) {
263             mWidthModifier.setWidthIn(widthInConstraints);
264         }
265         if (heightInConstraints != null) {
266             mHeightModifier.setHeightIn(heightInConstraints);
267         }
268 
269         if (mAnimationSpec != AnimationSpec.DEFAULT) {
270             for (int i = 0; i < mChildrenComponents.size(); i++) {
271                 Component c = mChildrenComponents.get(i);
272                 if (c != null && c.getAnimationSpec() == AnimationSpec.DEFAULT) {
273                     c.setAnimationSpec(mAnimationSpec);
274                 }
275             }
276         }
277 
278         setWidth(computeModifierDefinedWidth(null));
279         setHeight(computeModifierDefinedHeight(null));
280     }
281 
282     @NonNull
283     @Override
toString()284     public String toString() {
285         return "UNKNOWN LAYOUT_COMPONENT";
286     }
287 
288     @Override
getLocationInWindow(@onNull float[] value, boolean forSelf)289     public void getLocationInWindow(@NonNull float[] value, boolean forSelf) {
290         value[0] += mX + mPaddingLeft;
291         value[1] += mY + mPaddingTop;
292         if (mParent != null) {
293             mParent.getLocationInWindow(value, false);
294         }
295     }
296 
297     @Override
getScrollX()298     public float getScrollX() {
299         if (mHorizontalScrollDelegate != null) {
300             return mHorizontalScrollDelegate.getScrollX(mScrollX);
301         }
302         return mScrollX;
303     }
304 
setScrollX(float value)305     public void setScrollX(float value) {
306         mScrollX = value;
307     }
308 
309     @Override
getScrollY()310     public float getScrollY() {
311         if (mVerticalScrollDelegate != null) {
312             return mVerticalScrollDelegate.getScrollY(mScrollY);
313         }
314         return mScrollY;
315     }
316 
setScrollY(float value)317     public void setScrollY(float value) {
318         mScrollY = value;
319     }
320 
321     @Override
paint(@onNull PaintContext context)322     public void paint(@NonNull PaintContext context) {
323         if (mDrawContentOperations != null) {
324             context.save();
325             context.translate(mX, mY);
326             mDrawContentOperations.paint(context);
327             context.restore();
328             return;
329         }
330         super.paint(context);
331     }
332 
333     /**
334      * Paint the component content. Used by the DrawContent operation. (back out mX/mY -- TODO:
335      * refactor paintingComponent instead, to not include mX/mY etc.)
336      *
337      * @param context painting context
338      */
drawContent(@onNull PaintContext context)339     public void drawContent(@NonNull PaintContext context) {
340         context.save();
341         context.translate(-mX, -mY);
342         paintingComponent(context);
343         context.restore();
344     }
345 
346     protected final HashMap<Integer, Object> mCachedAttributes = new HashMap<>();
347 
348     @Override
paintingComponent(@onNull PaintContext context)349     public void paintingComponent(@NonNull PaintContext context) {
350         Component prev = context.getContext().mLastComponent;
351         RemoteContext remoteContext = context.getContext();
352 
353         remoteContext.mLastComponent = this;
354         context.save();
355         context.translate(mX, mY);
356         if (context.isVisualDebug()) {
357             debugBox(this, context);
358         }
359         if (mGraphicsLayerModifier != null) {
360             context.startGraphicsLayer((int) getWidth(), (int) getHeight());
361             mCachedAttributes.clear();
362             mGraphicsLayerModifier.fillInAttributes(mCachedAttributes);
363             context.setGraphicsLayer(mCachedAttributes);
364         }
365         mComponentModifiers.paint(context);
366         float tx = mPaddingLeft + getScrollX();
367         float ty = mPaddingTop + getScrollY();
368         context.translate(tx, ty);
369         if (mChildrenHaveZIndex) {
370             // TODO -- should only sort when something has changed
371             ArrayList<Component> sorted = new ArrayList<Component>(mChildrenComponents);
372             sorted.sort((a, b) -> (int) (a.getZIndex() - b.getZIndex()));
373             for (Component child : sorted) {
374                 if (child.isDirty() && child instanceof VariableSupport) {
375                     child.updateVariables(context.getContext());
376                     child.markNotDirty();
377                 }
378                 remoteContext.incrementOpCount();
379                 child.paint(context);
380             }
381         } else {
382             for (Component child : mChildrenComponents) {
383                 if (child.isDirty() && child instanceof VariableSupport) {
384                     child.updateVariables(context.getContext());
385                     child.markNotDirty();
386                 }
387                 remoteContext.incrementOpCount();
388                 child.paint(context);
389             }
390         }
391         if (mGraphicsLayerModifier != null) {
392             context.endGraphicsLayer();
393         }
394         context.translate(-tx, -ty);
395         context.restore();
396         context.getContext().mLastComponent = prev;
397     }
398 
399     /** Traverse the modifiers to compute indicated dimension */
computeModifierDefinedWidth(@ullable RemoteContext context)400     public float computeModifierDefinedWidth(@Nullable RemoteContext context) {
401         float s = 0f;
402         float e = 0f;
403         float w = 0f;
404         for (OperationInterface c : mComponentModifiers.getList()) {
405             if (context != null && c.isDirty() && c instanceof VariableSupport) {
406                 ((VariableSupport) c).updateVariables(context);
407                 c.markNotDirty();
408             }
409             if (c instanceof WidthModifierOperation) {
410                 WidthModifierOperation o = (WidthModifierOperation) c;
411                 if (o.getType() == DimensionModifierOperation.Type.EXACT
412                         || o.getType() == DimensionModifierOperation.Type.EXACT_DP) {
413                     w = o.getValue();
414                 }
415                 break;
416             }
417             if (c instanceof PaddingModifierOperation) {
418                 PaddingModifierOperation pop = (PaddingModifierOperation) c;
419                 s += pop.getLeft();
420                 e += pop.getRight();
421             }
422         }
423         return s + w + e;
424     }
425 
426     /**
427      * Traverse the modifiers to compute padding width
428      *
429      * @param padding output start and end padding values
430      * @return padding width
431      */
computeModifierDefinedPaddingWidth(@onNull float[] padding)432     public float computeModifierDefinedPaddingWidth(@NonNull float[] padding) {
433         float s = 0f;
434         float e = 0f;
435         for (OperationInterface c : mComponentModifiers.getList()) {
436             if (c instanceof PaddingModifierOperation) {
437                 PaddingModifierOperation pop = (PaddingModifierOperation) c;
438                 s += pop.getLeft();
439                 e += pop.getRight();
440             }
441         }
442         padding[0] = s;
443         padding[1] = e;
444         return s + e;
445     }
446 
447     /** Traverse the modifiers to compute indicated dimension */
computeModifierDefinedHeight(@ullable RemoteContext context)448     public float computeModifierDefinedHeight(@Nullable RemoteContext context) {
449         float t = 0f;
450         float b = 0f;
451         float h = 0f;
452         for (OperationInterface c : mComponentModifiers.getList()) {
453             if (context != null && c.isDirty() && c instanceof VariableSupport) {
454                 ((VariableSupport) c).updateVariables(context);
455                 c.markNotDirty();
456             }
457             if (c instanceof HeightModifierOperation) {
458                 HeightModifierOperation o = (HeightModifierOperation) c;
459                 if (o.getType() == DimensionModifierOperation.Type.EXACT
460                         || o.getType() == DimensionModifierOperation.Type.EXACT_DP) {
461                     h = o.getValue();
462                 }
463                 break;
464             }
465             if (c instanceof PaddingModifierOperation) {
466                 PaddingModifierOperation pop = (PaddingModifierOperation) c;
467                 t += pop.getTop();
468                 b += pop.getBottom();
469             }
470         }
471         return t + h + b;
472     }
473 
474     /**
475      * Traverse the modifiers to compute padding height
476      *
477      * @param padding output top and bottom padding values
478      * @return padding height
479      */
computeModifierDefinedPaddingHeight(@onNull float[] padding)480     public float computeModifierDefinedPaddingHeight(@NonNull float[] padding) {
481         float t = 0f;
482         float b = 0f;
483         for (OperationInterface c : mComponentModifiers.getList()) {
484             if (c instanceof PaddingModifierOperation) {
485                 PaddingModifierOperation pop = (PaddingModifierOperation) c;
486                 t += pop.getTop();
487                 b += pop.getBottom();
488             }
489         }
490         padding[0] = t;
491         padding[1] = b;
492         return t + b;
493     }
494 
495     @NonNull
getComponentModifiers()496     public ComponentModifiers getComponentModifiers() {
497         return mComponentModifiers;
498     }
499 
500     @NonNull
getChildrenComponents()501     public ArrayList<Component> getChildrenComponents() {
502         return mChildrenComponents;
503     }
504 
505     @Override
serialize(MapSerializer serializer)506     public void serialize(MapSerializer serializer) {
507         super.serialize(serializer);
508         serializer
509                 .addTags(SerializeTags.LAYOUT_COMPONENT)
510                 .add("paddingLeft", mPaddingLeft)
511                 .add("paddingRight", mPaddingRight)
512                 .add("paddingTop", mPaddingTop)
513                 .add("paddingBottom", mPaddingBottom);
514     }
515 
516     @Override
selfOrModifier(Class<T> operationClass)517     public <T> @Nullable T selfOrModifier(Class<T> operationClass) {
518         if (operationClass.isInstance(this)) {
519             return operationClass.cast(this);
520         }
521 
522         for (ModifierOperation op : mComponentModifiers.getList()) {
523             if (operationClass.isInstance(op)) {
524                 return operationClass.cast(op);
525             }
526         }
527 
528         return null;
529     }
530 
531     @Override
registerVariables(RemoteContext context)532     public void registerVariables(RemoteContext context) {
533         if (mDrawContentOperations != null) {
534             mDrawContentOperations.registerListening(context);
535         }
536     }
537 }
538