1 /*
2  * Copyright (C) 2006 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.widget;
18 
19 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
20 
21 import android.annotation.NonNull;
22 import android.compat.annotation.UnsupportedAppUsage;
23 import android.content.Context;
24 import android.content.res.ResourceId;
25 import android.content.res.TypedArray;
26 import android.graphics.Rect;
27 import android.os.Build;
28 import android.util.ArrayMap;
29 import android.util.AttributeSet;
30 import android.util.Pools.SynchronizedPool;
31 import android.util.SparseArray;
32 import android.view.Gravity;
33 import android.view.View;
34 import android.view.ViewDebug;
35 import android.view.ViewGroup;
36 import android.view.ViewHierarchyEncoder;
37 import android.view.accessibility.AccessibilityEvent;
38 import android.view.inspector.InspectableProperty;
39 import android.view.inspector.PropertyMapper;
40 import android.view.inspector.PropertyReader;
41 import android.widget.RemoteViews.RemoteView;
42 
43 import com.android.internal.R;
44 
45 import java.util.ArrayDeque;
46 import java.util.ArrayList;
47 import java.util.Comparator;
48 import java.util.SortedSet;
49 import java.util.TreeSet;
50 
51 /**
52  * A Layout where the positions of the children can be described in relation to each other or to the
53  * parent.
54  *
55  * <p>
56  * Note that you cannot have a circular dependency between the size of the RelativeLayout and the
57  * position of its children. For example, you cannot have a RelativeLayout whose height is set to
58  * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT} and a child set to
59  * {@link #ALIGN_PARENT_BOTTOM}.
60  * </p>
61  *
62  * <p><strong>Note:</strong> In platform version 17 and lower, RelativeLayout was affected by
63  * a measurement bug that could cause child views to be measured with incorrect
64  * {@link android.view.View.MeasureSpec MeasureSpec} values. (See
65  * {@link android.view.View.MeasureSpec#makeMeasureSpec(int, int) MeasureSpec.makeMeasureSpec}
66  * for more details.) This was triggered when a RelativeLayout container was placed in
67  * a scrolling container, such as a ScrollView or HorizontalScrollView. If a custom view
68  * not equipped to properly measure with the MeasureSpec mode
69  * {@link android.view.View.MeasureSpec#UNSPECIFIED UNSPECIFIED} was placed in a RelativeLayout,
70  * this would silently work anyway as RelativeLayout would pass a very large
71  * {@link android.view.View.MeasureSpec#AT_MOST AT_MOST} MeasureSpec instead.</p>
72  *
73  * <p>This behavior has been preserved for apps that set <code>android:targetSdkVersion="17"</code>
74  * or older in their manifest's <code>uses-sdk</code> tag for compatibility. Apps targeting SDK
75  * version 18 or newer will receive the correct behavior.</p>
76  *
77  * <p>See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative
78  * Layout</a> guide.</p>
79  *
80  * <p>
81  * Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for
82  * layout attributes
83  * </p>
84  *
85  * @attr ref android.R.styleable#RelativeLayout_gravity
86  * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
87  */
88 @RemoteView
89 public class RelativeLayout extends ViewGroup {
90     public static final int TRUE = -1;
91 
92     /**
93      * Rule that aligns a child's right edge with another child's left edge.
94      */
95     public static final int LEFT_OF                  = 0;
96     /**
97      * Rule that aligns a child's left edge with another child's right edge.
98      */
99     public static final int RIGHT_OF                 = 1;
100     /**
101      * Rule that aligns a child's bottom edge with another child's top edge.
102      */
103     public static final int ABOVE                    = 2;
104     /**
105      * Rule that aligns a child's top edge with another child's bottom edge.
106      */
107     public static final int BELOW                    = 3;
108 
109     /**
110      * Rule that aligns a child's baseline with another child's baseline.
111      */
112     public static final int ALIGN_BASELINE           = 4;
113     /**
114      * Rule that aligns a child's left edge with another child's left edge.
115      */
116     public static final int ALIGN_LEFT               = 5;
117     /**
118      * Rule that aligns a child's top edge with another child's top edge.
119      */
120     public static final int ALIGN_TOP                = 6;
121     /**
122      * Rule that aligns a child's right edge with another child's right edge.
123      */
124     public static final int ALIGN_RIGHT              = 7;
125     /**
126      * Rule that aligns a child's bottom edge with another child's bottom edge.
127      */
128     public static final int ALIGN_BOTTOM             = 8;
129 
130     /**
131      * Rule that aligns the child's left edge with its RelativeLayout
132      * parent's left edge.
133      */
134     public static final int ALIGN_PARENT_LEFT        = 9;
135     /**
136      * Rule that aligns the child's top edge with its RelativeLayout
137      * parent's top edge.
138      */
139     public static final int ALIGN_PARENT_TOP         = 10;
140     /**
141      * Rule that aligns the child's right edge with its RelativeLayout
142      * parent's right edge.
143      */
144     public static final int ALIGN_PARENT_RIGHT       = 11;
145     /**
146      * Rule that aligns the child's bottom edge with its RelativeLayout
147      * parent's bottom edge.
148      */
149     public static final int ALIGN_PARENT_BOTTOM      = 12;
150 
151     /**
152      * Rule that centers the child with respect to the bounds of its
153      * RelativeLayout parent.
154      */
155     public static final int CENTER_IN_PARENT         = 13;
156     /**
157      * Rule that centers the child horizontally with respect to the
158      * bounds of its RelativeLayout parent.
159      */
160     public static final int CENTER_HORIZONTAL        = 14;
161     /**
162      * Rule that centers the child vertically with respect to the
163      * bounds of its RelativeLayout parent.
164      */
165     public static final int CENTER_VERTICAL          = 15;
166     /**
167      * Rule that aligns a child's end edge with another child's start edge.
168      */
169     public static final int START_OF                 = 16;
170     /**
171      * Rule that aligns a child's start edge with another child's end edge.
172      */
173     public static final int END_OF                   = 17;
174     /**
175      * Rule that aligns a child's start edge with another child's start edge.
176      */
177     public static final int ALIGN_START              = 18;
178     /**
179      * Rule that aligns a child's end edge with another child's end edge.
180      */
181     public static final int ALIGN_END                = 19;
182     /**
183      * Rule that aligns the child's start edge with its RelativeLayout
184      * parent's start edge.
185      */
186     public static final int ALIGN_PARENT_START       = 20;
187     /**
188      * Rule that aligns the child's end edge with its RelativeLayout
189      * parent's end edge.
190      */
191     public static final int ALIGN_PARENT_END         = 21;
192 
193     private static final int VERB_COUNT              = 22;
194 
195 
196     private static final int[] RULES_VERTICAL = {
197             ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM
198     };
199 
200     private static final int[] RULES_HORIZONTAL = {
201             LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT, START_OF, END_OF, ALIGN_START, ALIGN_END
202     };
203 
204     /**
205      * Used to indicate left/right/top/bottom should be inferred from constraints
206      */
207     private static final int VALUE_NOT_SET = Integer.MIN_VALUE;
208 
209     private View mBaselineView = null;
210 
211     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
212     private int mGravity = Gravity.START | Gravity.TOP;
213     private final Rect mContentBounds = new Rect();
214     private final Rect mSelfBounds = new Rect();
215     private int mIgnoreGravity;
216 
217     private SortedSet<View> mTopToBottomLeftToRightSet = null;
218 
219     private boolean mDirtyHierarchy;
220     private View[] mSortedHorizontalChildren;
221     private View[] mSortedVerticalChildren;
222     private final DependencyGraph mGraph = new DependencyGraph();
223 
224     // Compatibility hack. Old versions of the platform had problems
225     // with MeasureSpec value overflow and RelativeLayout was one source of them.
226     // Some apps came to rely on them. :(
227     private boolean mAllowBrokenMeasureSpecs = false;
228     // Compatibility hack. Old versions of the platform would not take
229     // margins and padding into account when generating the height measure spec
230     // for children during the horizontal measure pass.
231     private boolean mMeasureVerticalWithPaddingMargin = false;
232 
233     // A default width used for RTL measure pass
234     /**
235      * Value reduced so as not to interfere with View's measurement spec. flags. See:
236      * {@link View#MEASURED_SIZE_MASK}.
237      * {@link View#MEASURED_STATE_TOO_SMALL}.
238      **/
239     private static final int DEFAULT_WIDTH = 0x00010000;
240 
RelativeLayout(Context context)241     public RelativeLayout(Context context) {
242         this(context, null);
243     }
244 
RelativeLayout(Context context, AttributeSet attrs)245     public RelativeLayout(Context context, AttributeSet attrs) {
246         this(context, attrs, 0);
247     }
248 
RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr)249     public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
250         this(context, attrs, defStyleAttr, 0);
251     }
252 
RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)253     public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
254         super(context, attrs, defStyleAttr, defStyleRes);
255         initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
256         queryCompatibilityModes(context);
257     }
258 
initFromAttributes( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)259     private void initFromAttributes(
260             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
261         final TypedArray a = context.obtainStyledAttributes(
262                 attrs, R.styleable.RelativeLayout, defStyleAttr, defStyleRes);
263         saveAttributeDataForStyleable(context, R.styleable.RelativeLayout,
264                 attrs, a, defStyleAttr, defStyleRes);
265         mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, View.NO_ID);
266         mGravity = a.getInt(R.styleable.RelativeLayout_gravity, mGravity);
267         a.recycle();
268     }
269 
queryCompatibilityModes(Context context)270     private void queryCompatibilityModes(Context context) {
271         int version = context.getApplicationInfo().targetSdkVersion;
272         mAllowBrokenMeasureSpecs = version <= Build.VERSION_CODES.JELLY_BEAN_MR1;
273         mMeasureVerticalWithPaddingMargin = version >= Build.VERSION_CODES.JELLY_BEAN_MR2;
274     }
275 
276     @Override
shouldDelayChildPressedState()277     public boolean shouldDelayChildPressedState() {
278         return false;
279     }
280 
281     /**
282      * Defines which View is ignored when the gravity is applied. This setting has no
283      * effect if the gravity is <code>Gravity.START | Gravity.TOP</code>.
284      *
285      * @param viewId The id of the View to be ignored by gravity, or 0 if no View
286      *        should be ignored.
287      *
288      * @see #setGravity(int)
289      *
290      * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
291      */
292     @android.view.RemotableViewMethod
setIgnoreGravity(int viewId)293     public void setIgnoreGravity(int viewId) {
294         mIgnoreGravity = viewId;
295     }
296 
297     /**
298      * Get the id of the View to be ignored by gravity
299      *
300      * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
301      */
302     @InspectableProperty
getIgnoreGravity()303     public int getIgnoreGravity() {
304         return mIgnoreGravity;
305     }
306 
307     /**
308      * Describes how the child views are positioned.
309      *
310      * @return the gravity.
311      *
312      * @see #setGravity(int)
313      * @see android.view.Gravity
314      *
315      * @attr ref android.R.styleable#RelativeLayout_gravity
316      */
317     @InspectableProperty(valueType = InspectableProperty.ValueType.GRAVITY)
getGravity()318     public int getGravity() {
319         return mGravity;
320     }
321 
322     /**
323      * Describes how the child views are positioned. Defaults to
324      * <code>Gravity.START | Gravity.TOP</code>.
325      *
326      * <p>Note that since RelativeLayout considers the positioning of each child
327      * relative to one another to be significant, setting gravity will affect
328      * the positioning of all children as a single unit within the parent.
329      * This happens after children have been relatively positioned.</p>
330      *
331      * @param gravity See {@link android.view.Gravity}
332      *
333      * @see #setHorizontalGravity(int)
334      * @see #setVerticalGravity(int)
335      *
336      * @attr ref android.R.styleable#RelativeLayout_gravity
337      */
338     @android.view.RemotableViewMethod
setGravity(int gravity)339     public void setGravity(int gravity) {
340         if (mGravity != gravity) {
341             if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
342                 gravity |= Gravity.START;
343             }
344 
345             if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
346                 gravity |= Gravity.TOP;
347             }
348 
349             mGravity = gravity;
350             requestLayout();
351         }
352     }
353 
354     @android.view.RemotableViewMethod
setHorizontalGravity(int horizontalGravity)355     public void setHorizontalGravity(int horizontalGravity) {
356         final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
357         if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
358             mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
359             requestLayout();
360         }
361     }
362 
363     @android.view.RemotableViewMethod
setVerticalGravity(int verticalGravity)364     public void setVerticalGravity(int verticalGravity) {
365         final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
366         if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
367             mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
368             requestLayout();
369         }
370     }
371 
372     @Override
getBaseline()373     public int getBaseline() {
374         return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline();
375     }
376 
377     @Override
requestLayout()378     public void requestLayout() {
379         super.requestLayout();
380         mDirtyHierarchy = true;
381     }
382 
sortChildren()383     private void sortChildren() {
384         final int count = getChildCount();
385         if (mSortedVerticalChildren == null || mSortedVerticalChildren.length != count) {
386             mSortedVerticalChildren = new View[count];
387         }
388 
389         if (mSortedHorizontalChildren == null || mSortedHorizontalChildren.length != count) {
390             mSortedHorizontalChildren = new View[count];
391         }
392 
393         final DependencyGraph graph = mGraph;
394         graph.clear();
395 
396         for (int i = 0; i < count; i++) {
397             graph.add(getChildAt(i));
398         }
399 
400         graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL);
401         graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL);
402     }
403 
404     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)405     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
406         if (mDirtyHierarchy) {
407             mDirtyHierarchy = false;
408             sortChildren();
409         }
410 
411         int myWidth = -1;
412         int myHeight = -1;
413 
414         int width = 0;
415         int height = 0;
416 
417         final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
418         final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
419         final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
420         final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
421 
422         // Record our dimensions if they are known;
423         if (widthMode != MeasureSpec.UNSPECIFIED) {
424             myWidth = widthSize;
425         }
426 
427         if (heightMode != MeasureSpec.UNSPECIFIED) {
428             myHeight = heightSize;
429         }
430 
431         if (widthMode == MeasureSpec.EXACTLY) {
432             width = myWidth;
433         }
434 
435         if (heightMode == MeasureSpec.EXACTLY) {
436             height = myHeight;
437         }
438 
439         View ignore = null;
440         int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
441         final boolean horizontalGravity = gravity != Gravity.START && gravity != 0;
442         gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
443         final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;
444 
445         int left = Integer.MAX_VALUE;
446         int top = Integer.MAX_VALUE;
447         int right = Integer.MIN_VALUE;
448         int bottom = Integer.MIN_VALUE;
449 
450         boolean offsetHorizontalAxis = false;
451         boolean offsetVerticalAxis = false;
452 
453         if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
454             ignore = findViewById(mIgnoreGravity);
455         }
456 
457         final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
458         final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
459 
460         // We need to know our size for doing the correct computation of children positioning in RTL
461         // mode but there is no practical way to get it instead of running the code below.
462         // So, instead of running the code twice, we just set the width to a "default display width"
463         // before the computation and then, as a last pass, we will update their real position with
464         // an offset equals to "DEFAULT_WIDTH - width".
465         final int layoutDirection = getLayoutDirection();
466         if (isLayoutRtl() && myWidth == -1) {
467             myWidth = DEFAULT_WIDTH;
468         }
469 
470         View[] views = mSortedHorizontalChildren;
471         int count = views.length;
472 
473         for (int i = 0; i < count; i++) {
474             View child = views[i];
475             if (child.getVisibility() != GONE) {
476                 LayoutParams params = (LayoutParams) child.getLayoutParams();
477                 int[] rules = params.getRules(layoutDirection);
478 
479                 applyHorizontalSizeRules(params, myWidth, rules);
480                 measureChildHorizontal(child, params, myWidth, myHeight);
481 
482                 if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
483                     offsetHorizontalAxis = true;
484                 }
485             }
486         }
487 
488         views = mSortedVerticalChildren;
489         count = views.length;
490         final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
491 
492         for (int i = 0; i < count; i++) {
493             final View child = views[i];
494             if (child.getVisibility() != GONE) {
495                 final LayoutParams params = (LayoutParams) child.getLayoutParams();
496 
497                 applyVerticalSizeRules(params, myHeight, child.getBaseline());
498                 measureChild(child, params, myWidth, myHeight);
499                 if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
500                     offsetVerticalAxis = true;
501                 }
502 
503                 if (isWrapContentWidth) {
504                     if (isLayoutRtl()) {
505                         if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
506                             width = Math.max(width, myWidth - params.mLeft);
507                         } else {
508                             width = Math.max(width, myWidth - params.mLeft + params.leftMargin);
509                         }
510                     } else {
511                         if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
512                             width = Math.max(width, params.mRight);
513                         } else {
514                             width = Math.max(width, params.mRight + params.rightMargin);
515                         }
516                     }
517                 }
518 
519                 if (isWrapContentHeight) {
520                     if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
521                         height = Math.max(height, params.mBottom);
522                     } else {
523                         height = Math.max(height, params.mBottom + params.bottomMargin);
524                     }
525                 }
526 
527                 if (child != ignore || verticalGravity) {
528                     left = Math.min(left, params.mLeft - params.leftMargin);
529                     top = Math.min(top, params.mTop - params.topMargin);
530                 }
531 
532                 if (child != ignore || horizontalGravity) {
533                     right = Math.max(right, params.mRight + params.rightMargin);
534                     bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
535                 }
536             }
537         }
538 
539         // Use the top-start-most laid out view as the baseline. RTL offsets are
540         // applied later, so we can use the left-most edge as the starting edge.
541         View baselineView = null;
542         LayoutParams baselineParams = null;
543         for (int i = 0; i < count; i++) {
544             final View child = views[i];
545             if (child.getVisibility() != GONE) {
546                 final LayoutParams childParams = (LayoutParams) child.getLayoutParams();
547                 if (baselineView == null || baselineParams == null
548                         || compareLayoutPosition(childParams, baselineParams) < 0) {
549                     baselineView = child;
550                     baselineParams = childParams;
551                 }
552             }
553         }
554         mBaselineView = baselineView;
555 
556         if (isWrapContentWidth) {
557             // Width already has left padding in it since it was calculated by looking at
558             // the right of each child view
559             width += mPaddingRight;
560 
561             if (mLayoutParams != null && mLayoutParams.width >= 0) {
562                 width = Math.max(width, mLayoutParams.width);
563             }
564 
565             width = Math.max(width, getSuggestedMinimumWidth());
566             width = resolveSize(width, widthMeasureSpec);
567 
568             if (offsetHorizontalAxis) {
569                 for (int i = 0; i < count; i++) {
570                     final View child = views[i];
571                     if (child.getVisibility() != GONE) {
572                         final LayoutParams params = (LayoutParams) child.getLayoutParams();
573                         final int[] rules = params.getRules(layoutDirection);
574                         if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
575                             centerHorizontal(child, params, width);
576                         } else if (rules[ALIGN_PARENT_RIGHT] != 0) {
577                             final int childWidth = child.getMeasuredWidth();
578                             params.mLeft = width - mPaddingRight - childWidth;
579                             params.mRight = params.mLeft + childWidth;
580                         }
581                     }
582                 }
583             }
584         }
585 
586         if (isWrapContentHeight) {
587             // Height already has top padding in it since it was calculated by looking at
588             // the bottom of each child view
589             height += mPaddingBottom;
590 
591             if (mLayoutParams != null && mLayoutParams.height >= 0) {
592                 height = Math.max(height, mLayoutParams.height);
593             }
594 
595             height = Math.max(height, getSuggestedMinimumHeight());
596             height = resolveSize(height, heightMeasureSpec);
597 
598             if (offsetVerticalAxis) {
599                 for (int i = 0; i < count; i++) {
600                     final View child = views[i];
601                     if (child.getVisibility() != GONE) {
602                         final LayoutParams params = (LayoutParams) child.getLayoutParams();
603                         final int[] rules = params.getRules(layoutDirection);
604                         if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
605                             centerVertical(child, params, height);
606                         } else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
607                             final int childHeight = child.getMeasuredHeight();
608                             params.mTop = height - mPaddingBottom - childHeight;
609                             params.mBottom = params.mTop + childHeight;
610                         }
611                     }
612                 }
613             }
614         }
615 
616         if (horizontalGravity || verticalGravity) {
617             final Rect selfBounds = mSelfBounds;
618             selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
619                     height - mPaddingBottom);
620 
621             final Rect contentBounds = mContentBounds;
622             Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,
623                     layoutDirection);
624 
625             final int horizontalOffset = contentBounds.left - left;
626             final int verticalOffset = contentBounds.top - top;
627             if (horizontalOffset != 0 || verticalOffset != 0) {
628                 for (int i = 0; i < count; i++) {
629                     final View child = views[i];
630                     if (child.getVisibility() != GONE && child != ignore) {
631                         final LayoutParams params = (LayoutParams) child.getLayoutParams();
632                         if (horizontalGravity) {
633                             params.mLeft += horizontalOffset;
634                             params.mRight += horizontalOffset;
635                         }
636                         if (verticalGravity) {
637                             params.mTop += verticalOffset;
638                             params.mBottom += verticalOffset;
639                         }
640                     }
641                 }
642             }
643         }
644 
645         if (isLayoutRtl()) {
646             final int offsetWidth = myWidth - width;
647             for (int i = 0; i < count; i++) {
648                 final View child = views[i];
649                 if (child.getVisibility() != GONE) {
650                     final LayoutParams params = (LayoutParams) child.getLayoutParams();
651                     params.mLeft -= offsetWidth;
652                     params.mRight -= offsetWidth;
653                 }
654             }
655         }
656 
657         setMeasuredDimension(width, height);
658     }
659 
660     /**
661      * @return a negative number if the top of {@code p1} is above the top of
662      *         {@code p2} or if they have identical top values and the left of
663      *         {@code p1} is to the left of {@code p2}, or a positive number
664      *         otherwise
665      */
compareLayoutPosition(LayoutParams p1, LayoutParams p2)666     private int compareLayoutPosition(LayoutParams p1, LayoutParams p2) {
667         final int topDiff = p1.mTop - p2.mTop;
668         if (topDiff != 0) {
669             return topDiff;
670         }
671         return p1.mLeft - p2.mLeft;
672     }
673 
674     /**
675      * Measure a child. The child should have left, top, right and bottom information
676      * stored in its LayoutParams. If any of these values is VALUE_NOT_SET it means
677      * that the view can extend up to the corresponding edge.
678      *
679      * @param child Child to measure
680      * @param params LayoutParams associated with child
681      * @param myWidth Width of the the RelativeLayout
682      * @param myHeight Height of the RelativeLayout
683      */
measureChild(View child, LayoutParams params, int myWidth, int myHeight)684     private void measureChild(View child, LayoutParams params, int myWidth, int myHeight) {
685         int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
686                 params.mRight, params.width,
687                 params.leftMargin, params.rightMargin,
688                 mPaddingLeft, mPaddingRight,
689                 myWidth);
690         int childHeightMeasureSpec = getChildMeasureSpec(params.mTop,
691                 params.mBottom, params.height,
692                 params.topMargin, params.bottomMargin,
693                 mPaddingTop, mPaddingBottom,
694                 myHeight);
695         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
696     }
697 
measureChildHorizontal( View child, LayoutParams params, int myWidth, int myHeight)698     private void measureChildHorizontal(
699             View child, LayoutParams params, int myWidth, int myHeight) {
700         final int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, params.mRight,
701                 params.width, params.leftMargin, params.rightMargin, mPaddingLeft, mPaddingRight,
702                 myWidth);
703 
704         final int childHeightMeasureSpec;
705         if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
706             if (params.height >= 0) {
707                 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
708                         params.height, MeasureSpec.EXACTLY);
709             } else {
710                 // Negative values in a mySize/myWidth/myWidth value in
711                 // RelativeLayout measurement is code for, "we got an
712                 // unspecified mode in the RelativeLayout's measure spec."
713                 // Carry it forward.
714                 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
715             }
716         } else {
717             final int maxHeight;
718             if (mMeasureVerticalWithPaddingMargin) {
719                 maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom
720                         - params.topMargin - params.bottomMargin);
721             } else {
722                 maxHeight = Math.max(0, myHeight);
723             }
724 
725             final int heightMode;
726             if (params.height == LayoutParams.MATCH_PARENT) {
727                 heightMode = MeasureSpec.EXACTLY;
728             } else {
729                 heightMode = MeasureSpec.AT_MOST;
730             }
731             childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, heightMode);
732         }
733 
734         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
735     }
736 
737     /**
738      * Get a measure spec that accounts for all of the constraints on this view.
739      * This includes size constraints imposed by the RelativeLayout as well as
740      * the View's desired dimension.
741      *
742      * @param childStart The left or top field of the child's layout params
743      * @param childEnd The right or bottom field of the child's layout params
744      * @param childSize The child's desired size (the width or height field of
745      *        the child's layout params)
746      * @param startMargin The left or top margin
747      * @param endMargin The right or bottom margin
748      * @param startPadding mPaddingLeft or mPaddingTop
749      * @param endPadding mPaddingRight or mPaddingBottom
750      * @param mySize The width or height of this view (the RelativeLayout)
751      * @return MeasureSpec for the child
752      */
getChildMeasureSpec(int childStart, int childEnd, int childSize, int startMargin, int endMargin, int startPadding, int endPadding, int mySize)753     private int getChildMeasureSpec(int childStart, int childEnd,
754             int childSize, int startMargin, int endMargin, int startPadding,
755             int endPadding, int mySize) {
756         int childSpecMode = 0;
757         int childSpecSize = 0;
758 
759         // Negative values in a mySize value in RelativeLayout
760         // measurement is code for, "we got an unspecified mode in the
761         // RelativeLayout's measure spec."
762         final boolean isUnspecified = mySize < 0;
763         if (isUnspecified && !mAllowBrokenMeasureSpecs) {
764             if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
765                 // Constraints fixed both edges, so child has an exact size.
766                 childSpecSize = Math.max(0, childEnd - childStart);
767                 childSpecMode = MeasureSpec.EXACTLY;
768             } else if (childSize >= 0) {
769                 // The child specified an exact size.
770                 childSpecSize = childSize;
771                 childSpecMode = MeasureSpec.EXACTLY;
772             } else {
773                 // Allow the child to be whatever size it wants.
774                 childSpecSize = 0;
775                 childSpecMode = MeasureSpec.UNSPECIFIED;
776             }
777 
778             return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
779         }
780 
781         // Figure out start and end bounds.
782         int tempStart = childStart;
783         int tempEnd = childEnd;
784 
785         // If the view did not express a layout constraint for an edge, use
786         // view's margins and our padding
787         if (tempStart == VALUE_NOT_SET) {
788             tempStart = startPadding + startMargin;
789         }
790         if (tempEnd == VALUE_NOT_SET) {
791             tempEnd = mySize - endPadding - endMargin;
792         }
793 
794         // Figure out maximum size available to this view
795         final int maxAvailable = tempEnd - tempStart;
796 
797         if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
798             // Constraints fixed both edges, so child must be an exact size.
799             childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
800             childSpecSize = Math.max(0, maxAvailable);
801         } else {
802             if (childSize >= 0) {
803                 // Child wanted an exact size. Give as much as possible.
804                 childSpecMode = MeasureSpec.EXACTLY;
805 
806                 if (maxAvailable >= 0) {
807                     // We have a maximum size in this dimension.
808                     childSpecSize = Math.min(maxAvailable, childSize);
809                 } else {
810                     // We can grow in this dimension.
811                     childSpecSize = childSize;
812                 }
813             } else if (childSize == LayoutParams.MATCH_PARENT) {
814                 // Child wanted to be as big as possible. Give all available
815                 // space.
816                 childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
817                 childSpecSize = Math.max(0, maxAvailable);
818             } else if (childSize == LayoutParams.WRAP_CONTENT) {
819                 // Child wants to wrap content. Use AT_MOST to communicate
820                 // available space if we know our max size.
821                 if (maxAvailable >= 0) {
822                     // We have a maximum size in this dimension.
823                     childSpecMode = MeasureSpec.AT_MOST;
824                     childSpecSize = maxAvailable;
825                 } else {
826                     // We can grow in this dimension. Child can be as big as it
827                     // wants.
828                     childSpecMode = MeasureSpec.UNSPECIFIED;
829                     childSpecSize = 0;
830                 }
831             }
832         }
833 
834         return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
835     }
836 
positionChildHorizontal(View child, LayoutParams params, int myWidth, boolean wrapContent)837     private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth,
838             boolean wrapContent) {
839 
840         final int layoutDirection = getLayoutDirection();
841         int[] rules = params.getRules(layoutDirection);
842 
843         if (params.mLeft == VALUE_NOT_SET && params.mRight != VALUE_NOT_SET) {
844             // Right is fixed, but left varies
845             params.mLeft = params.mRight - child.getMeasuredWidth();
846         } else if (params.mLeft != VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
847             // Left is fixed, but right varies
848             params.mRight = params.mLeft + child.getMeasuredWidth();
849         } else if (params.mLeft == VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
850             // Both left and right vary
851             if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
852                 if (!wrapContent) {
853                     centerHorizontal(child, params, myWidth);
854                 } else {
855                     positionAtEdge(child, params, myWidth);
856                 }
857                 return true;
858             } else {
859                 // This is the default case. For RTL we start from the right and for LTR we start
860                 // from the left. This will give LEFT/TOP for LTR and RIGHT/TOP for RTL.
861                 positionAtEdge(child, params, myWidth);
862             }
863         }
864         return rules[ALIGN_PARENT_END] != 0;
865     }
866 
positionAtEdge(View child, LayoutParams params, int myWidth)867     private void positionAtEdge(View child, LayoutParams params, int myWidth) {
868         if (isLayoutRtl()) {
869             params.mRight = myWidth - mPaddingRight - params.rightMargin;
870             params.mLeft = params.mRight - child.getMeasuredWidth();
871         } else {
872             params.mLeft = mPaddingLeft + params.leftMargin;
873             params.mRight = params.mLeft + child.getMeasuredWidth();
874         }
875     }
876 
positionChildVertical(View child, LayoutParams params, int myHeight, boolean wrapContent)877     private boolean positionChildVertical(View child, LayoutParams params, int myHeight,
878             boolean wrapContent) {
879 
880         int[] rules = params.getRules();
881 
882         if (params.mTop == VALUE_NOT_SET && params.mBottom != VALUE_NOT_SET) {
883             // Bottom is fixed, but top varies
884             params.mTop = params.mBottom - child.getMeasuredHeight();
885         } else if (params.mTop != VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) {
886             // Top is fixed, but bottom varies
887             params.mBottom = params.mTop + child.getMeasuredHeight();
888         } else if (params.mTop == VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) {
889             // Both top and bottom vary
890             if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
891                 if (!wrapContent) {
892                     centerVertical(child, params, myHeight);
893                 } else {
894                     params.mTop = mPaddingTop + params.topMargin;
895                     params.mBottom = params.mTop + child.getMeasuredHeight();
896                 }
897                 return true;
898             } else {
899                 params.mTop = mPaddingTop + params.topMargin;
900                 params.mBottom = params.mTop + child.getMeasuredHeight();
901             }
902         }
903         return rules[ALIGN_PARENT_BOTTOM] != 0;
904     }
905 
applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules)906     private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) {
907         RelativeLayout.LayoutParams anchorParams;
908 
909         // VALUE_NOT_SET indicates a "soft requirement" in that direction. For example:
910         // left=10, right=VALUE_NOT_SET means the view must start at 10, but can go as far as it
911         // wants to the right
912         // left=VALUE_NOT_SET, right=10 means the view must end at 10, but can go as far as it
913         // wants to the left
914         // left=10, right=20 means the left and right ends are both fixed
915         childParams.mLeft = VALUE_NOT_SET;
916         childParams.mRight = VALUE_NOT_SET;
917 
918         anchorParams = getRelatedViewParams(rules, LEFT_OF);
919         if (anchorParams != null) {
920             childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
921                     childParams.rightMargin);
922         } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
923             if (myWidth >= 0) {
924                 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
925             }
926         }
927 
928         anchorParams = getRelatedViewParams(rules, RIGHT_OF);
929         if (anchorParams != null) {
930             childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
931                     childParams.leftMargin);
932         } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
933             childParams.mLeft = mPaddingLeft + childParams.leftMargin;
934         }
935 
936         anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
937         if (anchorParams != null) {
938             childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
939         } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
940             childParams.mLeft = mPaddingLeft + childParams.leftMargin;
941         }
942 
943         anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
944         if (anchorParams != null) {
945             childParams.mRight = anchorParams.mRight - childParams.rightMargin;
946         } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
947             if (myWidth >= 0) {
948                 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
949             }
950         }
951 
952         if (0 != rules[ALIGN_PARENT_LEFT]) {
953             childParams.mLeft = mPaddingLeft + childParams.leftMargin;
954         }
955 
956         if (0 != rules[ALIGN_PARENT_RIGHT]) {
957             if (myWidth >= 0) {
958                 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
959             }
960         }
961     }
962 
applyVerticalSizeRules(LayoutParams childParams, int myHeight, int myBaseline)963     private void applyVerticalSizeRules(LayoutParams childParams, int myHeight, int myBaseline) {
964         final int[] rules = childParams.getRules();
965 
966         // Baseline alignment overrides any explicitly specified top or bottom.
967         int baselineOffset = getRelatedViewBaselineOffset(rules);
968         if (baselineOffset != -1) {
969             if (myBaseline != -1) {
970                 baselineOffset -= myBaseline;
971             }
972             childParams.mTop = baselineOffset;
973             childParams.mBottom = VALUE_NOT_SET;
974             return;
975         }
976 
977         RelativeLayout.LayoutParams anchorParams;
978 
979         childParams.mTop = VALUE_NOT_SET;
980         childParams.mBottom = VALUE_NOT_SET;
981 
982         anchorParams = getRelatedViewParams(rules, ABOVE);
983         if (anchorParams != null) {
984             childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin +
985                     childParams.bottomMargin);
986         } else if (childParams.alignWithParent && rules[ABOVE] != 0) {
987             if (myHeight >= 0) {
988                 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
989             }
990         }
991 
992         anchorParams = getRelatedViewParams(rules, BELOW);
993         if (anchorParams != null) {
994             childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin +
995                     childParams.topMargin);
996         } else if (childParams.alignWithParent && rules[BELOW] != 0) {
997             childParams.mTop = mPaddingTop + childParams.topMargin;
998         }
999 
1000         anchorParams = getRelatedViewParams(rules, ALIGN_TOP);
1001         if (anchorParams != null) {
1002             childParams.mTop = anchorParams.mTop + childParams.topMargin;
1003         } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) {
1004             childParams.mTop = mPaddingTop + childParams.topMargin;
1005         }
1006 
1007         anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM);
1008         if (anchorParams != null) {
1009             childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin;
1010         } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) {
1011             if (myHeight >= 0) {
1012                 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
1013             }
1014         }
1015 
1016         if (0 != rules[ALIGN_PARENT_TOP]) {
1017             childParams.mTop = mPaddingTop + childParams.topMargin;
1018         }
1019 
1020         if (0 != rules[ALIGN_PARENT_BOTTOM]) {
1021             if (myHeight >= 0) {
1022                 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
1023             }
1024         }
1025     }
1026 
getRelatedView(int[] rules, int relation)1027     private View getRelatedView(int[] rules, int relation) {
1028         int id = rules[relation];
1029         if (id != 0) {
1030             DependencyGraph.Node node = mGraph.mKeyNodes.get(id);
1031             if (node == null) return null;
1032             View v = node.view;
1033 
1034             // Find the first non-GONE view up the chain
1035             while (v.getVisibility() == View.GONE) {
1036                 rules = ((LayoutParams) v.getLayoutParams()).getRules(v.getLayoutDirection());
1037                 node = mGraph.mKeyNodes.get((rules[relation]));
1038                 // ignore self dependency. for more info look in git commit: da3003
1039                 if (node == null || v == node.view) return null;
1040                 v = node.view;
1041             }
1042 
1043             return v;
1044         }
1045 
1046         return null;
1047     }
1048 
getRelatedViewParams(int[] rules, int relation)1049     private LayoutParams getRelatedViewParams(int[] rules, int relation) {
1050         View v = getRelatedView(rules, relation);
1051         if (v != null) {
1052             ViewGroup.LayoutParams params = v.getLayoutParams();
1053             if (params instanceof LayoutParams) {
1054                 return (LayoutParams) v.getLayoutParams();
1055             }
1056         }
1057         return null;
1058     }
1059 
getRelatedViewBaselineOffset(int[] rules)1060     private int getRelatedViewBaselineOffset(int[] rules) {
1061         final View v = getRelatedView(rules, ALIGN_BASELINE);
1062         if (v != null) {
1063             final int baseline = v.getBaseline();
1064             if (baseline != -1) {
1065                 final ViewGroup.LayoutParams params = v.getLayoutParams();
1066                 if (params instanceof LayoutParams) {
1067                     final LayoutParams anchorParams = (LayoutParams) v.getLayoutParams();
1068                     return anchorParams.mTop + baseline;
1069                 }
1070             }
1071         }
1072         return -1;
1073     }
1074 
centerHorizontal(View child, LayoutParams params, int myWidth)1075     private static void centerHorizontal(View child, LayoutParams params, int myWidth) {
1076         int childWidth = child.getMeasuredWidth();
1077         int left = (myWidth - childWidth) / 2;
1078 
1079         params.mLeft = left;
1080         params.mRight = left + childWidth;
1081     }
1082 
centerVertical(View child, LayoutParams params, int myHeight)1083     private static void centerVertical(View child, LayoutParams params, int myHeight) {
1084         int childHeight = child.getMeasuredHeight();
1085         int top = (myHeight - childHeight) / 2;
1086 
1087         params.mTop = top;
1088         params.mBottom = top + childHeight;
1089     }
1090 
1091     @Override
onLayout(boolean changed, int l, int t, int r, int b)1092     protected void onLayout(boolean changed, int l, int t, int r, int b) {
1093         //  The layout has actually already been performed and the positions
1094         //  cached.  Apply the cached values to the children.
1095         final int count = getChildCount();
1096 
1097         for (int i = 0; i < count; i++) {
1098             View child = getChildAt(i);
1099             if (child.getVisibility() != GONE) {
1100                 RelativeLayout.LayoutParams st =
1101                         (RelativeLayout.LayoutParams) child.getLayoutParams();
1102                 child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
1103             }
1104         }
1105     }
1106 
1107     @Override
generateLayoutParams(AttributeSet attrs)1108     public LayoutParams generateLayoutParams(AttributeSet attrs) {
1109         return new RelativeLayout.LayoutParams(getContext(), attrs);
1110     }
1111 
1112     /**
1113      * Returns a set of layout parameters with a width of
1114      * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT},
1115      * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning.
1116      */
1117     @Override
generateDefaultLayoutParams()1118     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
1119         return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1120     }
1121 
1122     // Override to allow type-checking of LayoutParams.
1123     @Override
checkLayoutParams(ViewGroup.LayoutParams p)1124     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1125         return p instanceof RelativeLayout.LayoutParams;
1126     }
1127 
1128     @Override
generateLayoutParams(ViewGroup.LayoutParams lp)1129     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
1130         if (sPreserveMarginParamsInLayoutParamConversion) {
1131             if (lp instanceof LayoutParams) {
1132                 return new LayoutParams((LayoutParams) lp);
1133             } else if (lp instanceof MarginLayoutParams) {
1134                 return new LayoutParams((MarginLayoutParams) lp);
1135             }
1136         }
1137         return new LayoutParams(lp);
1138     }
1139 
1140     /** @hide */
1141     @Override
dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event)1142     public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
1143         if (mTopToBottomLeftToRightSet == null) {
1144             mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator());
1145         }
1146 
1147         // sort children top-to-bottom and left-to-right
1148         for (int i = 0, count = getChildCount(); i < count; i++) {
1149             mTopToBottomLeftToRightSet.add(getChildAt(i));
1150         }
1151 
1152         for (View view : mTopToBottomLeftToRightSet) {
1153             if (view.getVisibility() == View.VISIBLE
1154                     && view.dispatchPopulateAccessibilityEvent(event)) {
1155                 mTopToBottomLeftToRightSet.clear();
1156                 return true;
1157             }
1158         }
1159 
1160         mTopToBottomLeftToRightSet.clear();
1161         return false;
1162     }
1163 
1164     @Override
getAccessibilityClassName()1165     public CharSequence getAccessibilityClassName() {
1166         return RelativeLayout.class.getName();
1167     }
1168 
1169     /**
1170      * Compares two views in left-to-right and top-to-bottom fashion.
1171      */
1172      private class TopToBottomLeftToRightComparator implements Comparator<View> {
compare(View first, View second)1173         public int compare(View first, View second) {
1174             // top - bottom
1175             int topDifference = first.getTop() - second.getTop();
1176             if (topDifference != 0) {
1177                 return topDifference;
1178             }
1179             // left - right
1180             int leftDifference = first.getLeft() - second.getLeft();
1181             if (leftDifference != 0) {
1182                 return leftDifference;
1183             }
1184             // break tie by height
1185             int heightDiference = first.getHeight() - second.getHeight();
1186             if (heightDiference != 0) {
1187                 return heightDiference;
1188             }
1189             // break tie by width
1190             int widthDiference = first.getWidth() - second.getWidth();
1191             if (widthDiference != 0) {
1192                 return widthDiference;
1193             }
1194             return 0;
1195         }
1196     }
1197 
1198     /**
1199      * Specifies how a view is positioned within a {@link RelativeLayout}.
1200      * The relative layout containing the view uses the value of these layout parameters to
1201      * determine where to position the view on the screen.  If the view is not contained
1202      * within a relative layout, these attributes are ignored.
1203      *
1204      * See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative
1205      * Layout</a> guide for example code demonstrating how to use relative layout's
1206      * layout parameters in a layout XML.
1207      *
1208      * To learn more about layout parameters and how they differ from typical view attributes,
1209      * see the <a href="{@docRoot}guide/topics/ui/declaring-layout.html#attributes">Layouts
1210      * guide</a>.
1211      *
1212      *
1213      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing
1214      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toLeftOf
1215      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toRightOf
1216      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_above
1217      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_below
1218      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBaseline
1219      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignLeft
1220      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignTop
1221      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignRight
1222      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBottom
1223      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentLeft
1224      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentTop
1225      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentRight
1226      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentBottom
1227      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerInParent
1228      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerHorizontal
1229      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical
1230      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toStartOf
1231      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toEndOf
1232      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignStart
1233      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignEnd
1234      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentStart
1235      * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentEnd
1236      */
1237     public static class LayoutParams extends ViewGroup.MarginLayoutParams {
1238         @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
1239             @ViewDebug.IntToString(from = ABOVE,               to = "above"),
1240             @ViewDebug.IntToString(from = ALIGN_BASELINE,      to = "alignBaseline"),
1241             @ViewDebug.IntToString(from = ALIGN_BOTTOM,        to = "alignBottom"),
1242             @ViewDebug.IntToString(from = ALIGN_LEFT,          to = "alignLeft"),
1243             @ViewDebug.IntToString(from = ALIGN_PARENT_BOTTOM, to = "alignParentBottom"),
1244             @ViewDebug.IntToString(from = ALIGN_PARENT_LEFT,   to = "alignParentLeft"),
1245             @ViewDebug.IntToString(from = ALIGN_PARENT_RIGHT,  to = "alignParentRight"),
1246             @ViewDebug.IntToString(from = ALIGN_PARENT_TOP,    to = "alignParentTop"),
1247             @ViewDebug.IntToString(from = ALIGN_RIGHT,         to = "alignRight"),
1248             @ViewDebug.IntToString(from = ALIGN_TOP,           to = "alignTop"),
1249             @ViewDebug.IntToString(from = BELOW,               to = "below"),
1250             @ViewDebug.IntToString(from = CENTER_HORIZONTAL,   to = "centerHorizontal"),
1251             @ViewDebug.IntToString(from = CENTER_IN_PARENT,    to = "center"),
1252             @ViewDebug.IntToString(from = CENTER_VERTICAL,     to = "centerVertical"),
1253             @ViewDebug.IntToString(from = LEFT_OF,             to = "leftOf"),
1254             @ViewDebug.IntToString(from = RIGHT_OF,            to = "rightOf"),
1255             @ViewDebug.IntToString(from = ALIGN_START,         to = "alignStart"),
1256             @ViewDebug.IntToString(from = ALIGN_END,           to = "alignEnd"),
1257             @ViewDebug.IntToString(from = ALIGN_PARENT_START,  to = "alignParentStart"),
1258             @ViewDebug.IntToString(from = ALIGN_PARENT_END,    to = "alignParentEnd"),
1259             @ViewDebug.IntToString(from = START_OF,            to = "startOf"),
1260             @ViewDebug.IntToString(from = END_OF,              to = "endOf")
1261         }, mapping = {
1262             @ViewDebug.IntToString(from = TRUE, to = "true"),
1263             @ViewDebug.IntToString(from = 0,    to = "false/NO_ID")
1264         })
1265 
1266         private int[] mRules = new int[VERB_COUNT];
1267         private int[] mInitialRules = new int[VERB_COUNT];
1268 
1269         @UnsupportedAppUsage
1270         private int mLeft;
1271         @UnsupportedAppUsage
1272         private int mTop;
1273         @UnsupportedAppUsage
1274         private int mRight;
1275         @UnsupportedAppUsage
1276         private int mBottom;
1277 
1278         /**
1279          * Whether this view had any relative rules modified following the most
1280          * recent resolution of layout direction.
1281          */
1282         private boolean mNeedsLayoutResolution;
1283 
1284         private boolean mRulesChanged = false;
1285         private boolean mIsRtlCompatibilityMode = false;
1286 
1287         /**
1288          * When true, uses the parent as the anchor if the anchor doesn't exist or if
1289          * the anchor's visibility is GONE.
1290          */
1291         @ViewDebug.ExportedProperty(category = "layout")
1292         public boolean alignWithParent;
1293 
LayoutParams(Context c, AttributeSet attrs)1294         public LayoutParams(Context c, AttributeSet attrs) {
1295             super(c, attrs);
1296 
1297             TypedArray a = c.obtainStyledAttributes(attrs,
1298                     com.android.internal.R.styleable.RelativeLayout_Layout);
1299 
1300             final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
1301             mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 ||
1302                     !c.getApplicationInfo().hasRtlSupport());
1303 
1304             final int[] rules = mRules;
1305             //noinspection MismatchedReadAndWriteOfArray
1306             final int[] initialRules = mInitialRules;
1307 
1308             final int N = a.getIndexCount();
1309             for (int i = 0; i < N; i++) {
1310                 int attr = a.getIndex(i);
1311                 switch (attr) {
1312                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing:
1313                         alignWithParent = a.getBoolean(attr, false);
1314                         break;
1315                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf:
1316                         rules[LEFT_OF] = a.getResourceId(attr, 0);
1317                         break;
1318                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf:
1319                         rules[RIGHT_OF] = a.getResourceId(attr, 0);
1320                         break;
1321                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above:
1322                         rules[ABOVE] = a.getResourceId(attr, 0);
1323                         break;
1324                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below:
1325                         rules[BELOW] = a.getResourceId(attr, 0);
1326                         break;
1327                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline:
1328                         rules[ALIGN_BASELINE] = a.getResourceId(attr, 0);
1329                         break;
1330                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft:
1331                         rules[ALIGN_LEFT] = a.getResourceId(attr, 0);
1332                         break;
1333                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop:
1334                         rules[ALIGN_TOP] = a.getResourceId(attr, 0);
1335                         break;
1336                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight:
1337                         rules[ALIGN_RIGHT] = a.getResourceId(attr, 0);
1338                         break;
1339                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom:
1340                         rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0);
1341                         break;
1342                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft:
1343                         rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0;
1344                         break;
1345                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop:
1346                         rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0;
1347                         break;
1348                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight:
1349                         rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0;
1350                         break;
1351                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom:
1352                         rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0;
1353                         break;
1354                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent:
1355                         rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0;
1356                         break;
1357                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal:
1358                         rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0;
1359                         break;
1360                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical:
1361                         rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0;
1362                        break;
1363                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf:
1364                         rules[START_OF] = a.getResourceId(attr, 0);
1365                         break;
1366                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf:
1367                         rules[END_OF] = a.getResourceId(attr, 0);
1368                         break;
1369                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart:
1370                         rules[ALIGN_START] = a.getResourceId(attr, 0);
1371                         break;
1372                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd:
1373                         rules[ALIGN_END] = a.getResourceId(attr, 0);
1374                         break;
1375                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart:
1376                         rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0;
1377                         break;
1378                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd:
1379                         rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0;
1380                         break;
1381                 }
1382             }
1383             mRulesChanged = true;
1384             System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT);
1385 
1386             a.recycle();
1387         }
1388 
LayoutParams(int w, int h)1389         public LayoutParams(int w, int h) {
1390             super(w, h);
1391         }
1392 
1393         /**
1394          * {@inheritDoc}
1395          */
LayoutParams(ViewGroup.LayoutParams source)1396         public LayoutParams(ViewGroup.LayoutParams source) {
1397             super(source);
1398         }
1399 
1400         /**
1401          * {@inheritDoc}
1402          */
LayoutParams(ViewGroup.MarginLayoutParams source)1403         public LayoutParams(ViewGroup.MarginLayoutParams source) {
1404             super(source);
1405         }
1406 
1407         /**
1408          * Copy constructor. Clones the width, height, margin values, and rules
1409          * of the source.
1410          *
1411          * @param source The layout params to copy from.
1412          */
LayoutParams(LayoutParams source)1413         public LayoutParams(LayoutParams source) {
1414             super(source);
1415 
1416             this.mIsRtlCompatibilityMode = source.mIsRtlCompatibilityMode;
1417             this.mRulesChanged = source.mRulesChanged;
1418             this.alignWithParent = source.alignWithParent;
1419 
1420             System.arraycopy(source.mRules, LEFT_OF, this.mRules, LEFT_OF, VERB_COUNT);
1421             System.arraycopy(
1422                     source.mInitialRules, LEFT_OF, this.mInitialRules, LEFT_OF, VERB_COUNT);
1423         }
1424 
1425         @Override
debug(String output)1426         public String debug(String output) {
1427             return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) +
1428                     ", height=" + sizeToString(height) + " }";
1429         }
1430 
1431         /**
1432          * Adds a layout rule to be interpreted by the RelativeLayout.
1433          * <p>
1434          * This method should only be used for verbs that don't refer to a
1435          * sibling (ex. {@link #ALIGN_RIGHT}) or take a boolean
1436          * value ({@link #TRUE} for true or 0 for false). To
1437          * specify a verb that takes a subject, use {@link #addRule(int, int)}.
1438          * <p>
1439          * If the rule is relative to the layout direction (ex.
1440          * {@link #ALIGN_PARENT_START}), then the layout direction must be
1441          * resolved using {@link #resolveLayoutDirection(int)} before calling
1442          * {@link #getRule(int)} an absolute rule (ex.
1443          * {@link #ALIGN_PARENT_LEFT}.
1444          *
1445          * @param verb a layout verb, such as {@link #ALIGN_PARENT_LEFT}
1446          * @see #addRule(int, int)
1447          * @see #removeRule(int)
1448          * @see #getRule(int)
1449          */
addRule(int verb)1450         public void addRule(int verb) {
1451             addRule(verb, TRUE);
1452         }
1453 
1454         /**
1455          * Adds a layout rule to be interpreted by the RelativeLayout.
1456          * <p>
1457          * Use this for verbs that refer to a sibling (ex.
1458          * {@link #ALIGN_RIGHT}) or take a boolean value (ex.
1459          * {@link #CENTER_IN_PARENT}).
1460          * <p>
1461          * If the rule is relative to the layout direction (ex.
1462          * {@link #START_OF}), then the layout direction must be resolved using
1463          * {@link #resolveLayoutDirection(int)} before calling
1464          * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}.
1465          *
1466          * @param verb a layout verb, such as {@link #ALIGN_RIGHT}
1467          * @param subject the ID of another view to use as an anchor, or a
1468          *                boolean value (represented as {@link #TRUE} for true
1469          *                or 0 for false)
1470          * @see #addRule(int)
1471          * @see #removeRule(int)
1472          * @see #getRule(int)
1473          */
addRule(int verb, int subject)1474         public void addRule(int verb, int subject) {
1475             // If we're removing a relative rule, we'll need to force layout
1476             // resolution the next time it's requested.
1477             if (!mNeedsLayoutResolution && isRelativeRule(verb)
1478                     && mInitialRules[verb] != 0 && subject == 0) {
1479                 mNeedsLayoutResolution = true;
1480             }
1481 
1482             mRules[verb] = subject;
1483             mInitialRules[verb] = subject;
1484             mRulesChanged = true;
1485         }
1486 
1487         /**
1488          * Removes a layout rule to be interpreted by the RelativeLayout.
1489          * <p>
1490          * If the rule is relative to the layout direction (ex.
1491          * {@link #START_OF}, {@link #ALIGN_PARENT_START}, etc.) then the
1492          * layout direction must be resolved using
1493          * {@link #resolveLayoutDirection(int)} before before calling
1494          * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}.
1495          *
1496          * @param verb One of the verbs defined by
1497          *        {@link android.widget.RelativeLayout RelativeLayout}, such as
1498          *         ALIGN_WITH_PARENT_LEFT.
1499          * @see #addRule(int)
1500          * @see #addRule(int, int)
1501          * @see #getRule(int)
1502          */
removeRule(int verb)1503         public void removeRule(int verb) {
1504             addRule(verb, 0);
1505         }
1506 
1507         /**
1508          * Returns the layout rule associated with a specific verb.
1509          *
1510          * @param verb one of the verbs defined by {@link RelativeLayout}, such
1511          *             as ALIGN_WITH_PARENT_LEFT
1512          * @return the id of another view to use as an anchor, a boolean value
1513          *         (represented as {@link RelativeLayout#TRUE} for true
1514          *         or 0 for false), or -1 for verbs that don't refer to another
1515          *         sibling (for example, ALIGN_WITH_PARENT_BOTTOM)
1516          * @see #addRule(int)
1517          * @see #addRule(int, int)
1518          */
getRule(int verb)1519         public int getRule(int verb) {
1520             return mRules[verb];
1521         }
1522 
hasRelativeRules()1523         private boolean hasRelativeRules() {
1524             return (mInitialRules[START_OF] != 0 || mInitialRules[END_OF] != 0 ||
1525                     mInitialRules[ALIGN_START] != 0 || mInitialRules[ALIGN_END] != 0 ||
1526                     mInitialRules[ALIGN_PARENT_START] != 0 || mInitialRules[ALIGN_PARENT_END] != 0);
1527         }
1528 
isRelativeRule(int rule)1529         private boolean isRelativeRule(int rule) {
1530             return rule == START_OF || rule == END_OF
1531                     || rule == ALIGN_START || rule == ALIGN_END
1532                     || rule == ALIGN_PARENT_START || rule == ALIGN_PARENT_END;
1533         }
1534 
1535         // The way we are resolving rules depends on the layout direction and if we are pre JB MR1
1536         // or not.
1537         //
1538         // If we are pre JB MR1 (said as "RTL compatibility mode"), "left"/"right" rules are having
1539         // predominance over any "start/end" rules that could have been defined. A special case:
1540         // if no "left"/"right" rule has been defined and "start"/"end" rules are defined then we
1541         // resolve those "start"/"end" rules to "left"/"right" respectively.
1542         //
1543         // If we are JB MR1+, then "start"/"end" rules are having predominance over "left"/"right"
1544         // rules. If no "start"/"end" rule is defined then we use "left"/"right" rules.
1545         //
1546         // In all cases, the result of the resolution should clear the "start"/"end" rules to leave
1547         // only the "left"/"right" rules at the end.
resolveRules(int layoutDirection)1548         private void resolveRules(int layoutDirection) {
1549             final boolean isLayoutRtl = (layoutDirection == View.LAYOUT_DIRECTION_RTL);
1550 
1551             // Reset to initial state
1552             System.arraycopy(mInitialRules, LEFT_OF, mRules, LEFT_OF, VERB_COUNT);
1553 
1554             // Apply rules depending on direction and if we are in RTL compatibility mode
1555             if (mIsRtlCompatibilityMode) {
1556                 if (mRules[ALIGN_START] != 0) {
1557                     if (mRules[ALIGN_LEFT] == 0) {
1558                         // "left" rule is not defined but "start" rule is: use the "start" rule as
1559                         // the "left" rule
1560                         mRules[ALIGN_LEFT] = mRules[ALIGN_START];
1561                     }
1562                     mRules[ALIGN_START] = 0;
1563                 }
1564 
1565                 if (mRules[ALIGN_END] != 0) {
1566                     if (mRules[ALIGN_RIGHT] == 0) {
1567                         // "right" rule is not defined but "end" rule is: use the "end" rule as the
1568                         // "right" rule
1569                         mRules[ALIGN_RIGHT] = mRules[ALIGN_END];
1570                     }
1571                     mRules[ALIGN_END] = 0;
1572                 }
1573 
1574                 if (mRules[START_OF] != 0) {
1575                     if (mRules[LEFT_OF] == 0) {
1576                         // "left" rule is not defined but "start" rule is: use the "start" rule as
1577                         // the "left" rule
1578                         mRules[LEFT_OF] = mRules[START_OF];
1579                     }
1580                     mRules[START_OF] = 0;
1581                 }
1582 
1583                 if (mRules[END_OF] != 0) {
1584                     if (mRules[RIGHT_OF] == 0) {
1585                         // "right" rule is not defined but "end" rule is: use the "end" rule as the
1586                         // "right" rule
1587                         mRules[RIGHT_OF] = mRules[END_OF];
1588                     }
1589                     mRules[END_OF] = 0;
1590                 }
1591 
1592                 if (mRules[ALIGN_PARENT_START] != 0) {
1593                     if (mRules[ALIGN_PARENT_LEFT] == 0) {
1594                         // "left" rule is not defined but "start" rule is: use the "start" rule as
1595                         // the "left" rule
1596                         mRules[ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1597                     }
1598                     mRules[ALIGN_PARENT_START] = 0;
1599                 }
1600 
1601                 if (mRules[ALIGN_PARENT_END] != 0) {
1602                     if (mRules[ALIGN_PARENT_RIGHT] == 0) {
1603                         // "right" rule is not defined but "end" rule is: use the "end" rule as the
1604                         // "right" rule
1605                         mRules[ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1606                     }
1607                     mRules[ALIGN_PARENT_END] = 0;
1608                 }
1609             } else {
1610                 // JB MR1+ case
1611                 if ((mRules[ALIGN_START] != 0 || mRules[ALIGN_END] != 0) &&
1612                         (mRules[ALIGN_LEFT] != 0 || mRules[ALIGN_RIGHT] != 0)) {
1613                     // "start"/"end" rules take precedence over "left"/"right" rules
1614                     mRules[ALIGN_LEFT] = 0;
1615                     mRules[ALIGN_RIGHT] = 0;
1616                 }
1617                 if (mRules[ALIGN_START] != 0) {
1618                     // "start" rule resolved to "left" or "right" depending on the direction
1619                     mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START];
1620                     mRules[ALIGN_START] = 0;
1621                 }
1622                 if (mRules[ALIGN_END] != 0) {
1623                     // "end" rule resolved to "left" or "right" depending on the direction
1624                     mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END];
1625                     mRules[ALIGN_END] = 0;
1626                 }
1627 
1628                 if ((mRules[START_OF] != 0 || mRules[END_OF] != 0) &&
1629                         (mRules[LEFT_OF] != 0 || mRules[RIGHT_OF] != 0)) {
1630                     // "start"/"end" rules take precedence over "left"/"right" rules
1631                     mRules[LEFT_OF] = 0;
1632                     mRules[RIGHT_OF] = 0;
1633                 }
1634                 if (mRules[START_OF] != 0) {
1635                     // "start" rule resolved to "left" or "right" depending on the direction
1636                     mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF];
1637                     mRules[START_OF] = 0;
1638                 }
1639                 if (mRules[END_OF] != 0) {
1640                     // "end" rule resolved to "left" or "right" depending on the direction
1641                     mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF];
1642                     mRules[END_OF] = 0;
1643                 }
1644 
1645                 if ((mRules[ALIGN_PARENT_START] != 0 || mRules[ALIGN_PARENT_END] != 0) &&
1646                         (mRules[ALIGN_PARENT_LEFT] != 0 || mRules[ALIGN_PARENT_RIGHT] != 0)) {
1647                     // "start"/"end" rules take precedence over "left"/"right" rules
1648                     mRules[ALIGN_PARENT_LEFT] = 0;
1649                     mRules[ALIGN_PARENT_RIGHT] = 0;
1650                 }
1651                 if (mRules[ALIGN_PARENT_START] != 0) {
1652                     // "start" rule resolved to "left" or "right" depending on the direction
1653                     mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1654                     mRules[ALIGN_PARENT_START] = 0;
1655                 }
1656                 if (mRules[ALIGN_PARENT_END] != 0) {
1657                     // "end" rule resolved to "left" or "right" depending on the direction
1658                     mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1659                     mRules[ALIGN_PARENT_END] = 0;
1660                 }
1661             }
1662 
1663             mRulesChanged = false;
1664             mNeedsLayoutResolution = false;
1665         }
1666 
1667         /**
1668          * Retrieves a complete list of all supported rules, where the index is the rule
1669          * verb, and the element value is the value specified, or "false" if it was never
1670          * set. If there are relative rules defined (*_START / *_END), they will be resolved
1671          * depending on the layout direction.
1672          *
1673          * @param layoutDirection the direction of the layout.
1674          *                        Should be either {@link View#LAYOUT_DIRECTION_LTR}
1675          *                        or {@link View#LAYOUT_DIRECTION_RTL}
1676          * @return the supported rules
1677          * @see #addRule(int, int)
1678          *
1679          * @hide
1680          */
getRules(int layoutDirection)1681         public int[] getRules(int layoutDirection) {
1682             resolveLayoutDirection(layoutDirection);
1683             return mRules;
1684         }
1685 
1686         /**
1687          * Retrieves a complete list of all supported rules, where the index is the rule
1688          * verb, and the element value is the value specified, or "false" if it was never
1689          * set. There will be no resolution of relative rules done.
1690          *
1691          * @return the supported rules
1692          * @see #addRule(int, int)
1693          */
getRules()1694         public int[] getRules() {
1695             return mRules;
1696         }
1697 
1698         /**
1699          * This will be called by {@link android.view.View#requestLayout()} to
1700          * resolve layout parameters that are relative to the layout direction.
1701          * <p>
1702          * After this method is called, any rules using layout-relative verbs
1703          * (ex. {@link #START_OF}) previously added via {@link #addRule(int)}
1704          * may only be accessed via their resolved absolute verbs (ex.
1705          * {@link #LEFT_OF}).
1706          */
1707         @Override
resolveLayoutDirection(int layoutDirection)1708         public void resolveLayoutDirection(int layoutDirection) {
1709             if (shouldResolveLayoutDirection(layoutDirection)) {
1710                 resolveRules(layoutDirection);
1711             }
1712 
1713             // This will set the layout direction.
1714             super.resolveLayoutDirection(layoutDirection);
1715         }
1716 
shouldResolveLayoutDirection(int layoutDirection)1717         private boolean shouldResolveLayoutDirection(int layoutDirection) {
1718             return (mNeedsLayoutResolution || hasRelativeRules())
1719                     && (mRulesChanged || layoutDirection != getLayoutDirection());
1720         }
1721 
1722         /** @hide */
1723         @Override
encodeProperties(@onNull ViewHierarchyEncoder encoder)1724         protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
1725             super.encodeProperties(encoder);
1726             encoder.addProperty("layout:alignWithParent", alignWithParent);
1727         }
1728 
1729         /** @hide */
1730         public static final class InspectionCompanion
1731                 implements android.view.inspector.InspectionCompanion<LayoutParams> {
1732             private boolean mPropertiesMapped;
1733 
1734             private int mAboveId;
1735             private int mAlignBaselineId;
1736             private int mAlignBottomId;
1737             private int mAlignEndId;
1738             private int mAlignLeftId;
1739             private int mAlignParentBottomId;
1740             private int mAlignParentEndId;
1741             private int mAlignParentLeftId;
1742             private int mAlignParentRightId;
1743             private int mAlignParentStartId;
1744             private int mAlignParentTopId;
1745             private int mAlignRightId;
1746             private int mAlignStartId;
1747             private int mAlignTopId;
1748             private int mAlignWithParentIfMissingId;
1749             private int mBelowId;
1750             private int mCenterHorizontalId;
1751             private int mCenterInParentId;
1752             private int mCenterVerticalId;
1753             private int mToEndOfId;
1754             private int mToLeftOfId;
1755             private int mToRightOfId;
1756             private int mToStartOfId;
1757 
1758             @Override
mapProperties(@onNull PropertyMapper propertyMapper)1759             public void mapProperties(@NonNull PropertyMapper propertyMapper) {
1760                 mPropertiesMapped = true;
1761 
1762                 mAboveId = propertyMapper.mapResourceId("layout_above", R.attr.layout_above);
1763 
1764                 mAlignBaselineId = propertyMapper.mapResourceId(
1765                         "layout_alignBaseline", R.attr.layout_alignBaseline);
1766 
1767                 mAlignBottomId = propertyMapper.mapResourceId(
1768                         "layout_alignBottom", R.attr.layout_alignBottom);
1769 
1770                 mAlignEndId = propertyMapper.mapResourceId(
1771                         "layout_alignEnd", R.attr.layout_alignEnd);
1772 
1773                 mAlignLeftId = propertyMapper.mapResourceId(
1774                         "layout_alignLeft", R.attr.layout_alignLeft);
1775 
1776                 mAlignParentBottomId = propertyMapper.mapBoolean(
1777                         "layout_alignParentBottom", R.attr.layout_alignParentBottom);
1778 
1779                 mAlignParentEndId = propertyMapper.mapBoolean(
1780                         "layout_alignParentEnd", R.attr.layout_alignParentEnd);
1781 
1782                 mAlignParentLeftId = propertyMapper.mapBoolean(
1783                         "layout_alignParentLeft", R.attr.layout_alignParentLeft);
1784 
1785                 mAlignParentRightId = propertyMapper.mapBoolean(
1786                         "layout_alignParentRight", R.attr.layout_alignParentRight);
1787 
1788                 mAlignParentStartId = propertyMapper.mapBoolean(
1789                         "layout_alignParentStart", R.attr.layout_alignParentStart);
1790 
1791                 mAlignParentTopId = propertyMapper.mapBoolean(
1792                         "layout_alignParentTop", R.attr.layout_alignParentTop);
1793 
1794                 mAlignRightId = propertyMapper.mapResourceId(
1795                         "layout_alignRight", R.attr.layout_alignRight);
1796 
1797                 mAlignStartId = propertyMapper.mapResourceId(
1798                         "layout_alignStart", R.attr.layout_alignStart);
1799 
1800                 mAlignTopId = propertyMapper.mapResourceId(
1801                         "layout_alignTop", R.attr.layout_alignTop);
1802 
1803                 mAlignWithParentIfMissingId = propertyMapper.mapBoolean(
1804                         "layout_alignWithParentIfMissing",
1805                         R.attr.layout_alignWithParentIfMissing);
1806 
1807                 mBelowId = propertyMapper.mapResourceId("layout_below", R.attr.layout_below);
1808 
1809                 mCenterHorizontalId = propertyMapper.mapBoolean(
1810                         "layout_centerHorizontal", R.attr.layout_centerHorizontal);
1811 
1812                 mCenterInParentId = propertyMapper.mapBoolean(
1813                         "layout_centerInParent", R.attr.layout_centerInParent);
1814 
1815                 mCenterVerticalId = propertyMapper.mapBoolean(
1816                         "layout_centerVertical", R.attr.layout_centerVertical);
1817 
1818                 mToEndOfId = propertyMapper.mapResourceId(
1819                         "layout_toEndOf", R.attr.layout_toEndOf);
1820 
1821                 mToLeftOfId = propertyMapper.mapResourceId(
1822                         "layout_toLeftOf", R.attr.layout_toLeftOf);
1823 
1824                 mToRightOfId = propertyMapper.mapResourceId(
1825                         "layout_toRightOf", R.attr.layout_toRightOf);
1826 
1827                 mToStartOfId = propertyMapper.mapResourceId(
1828                         "layout_toStartOf", R.attr.layout_toStartOf);
1829             }
1830 
1831             @Override
readProperties( @onNull LayoutParams node, @NonNull PropertyReader propertyReader )1832             public void readProperties(
1833                     @NonNull LayoutParams node,
1834                     @NonNull PropertyReader propertyReader
1835             ) {
1836                 if (!mPropertiesMapped) {
1837                     throw new UninitializedPropertyMapException();
1838                 }
1839 
1840                 final int[] rules = node.getRules();
1841 
1842                 propertyReader.readResourceId(mAboveId, rules[ABOVE]);
1843                 propertyReader.readResourceId(mAlignBaselineId, rules[ALIGN_BASELINE]);
1844                 propertyReader.readResourceId(mAlignBottomId, rules[ALIGN_BOTTOM]);
1845                 propertyReader.readResourceId(mAlignEndId, rules[ALIGN_END]);
1846                 propertyReader.readResourceId(mAlignLeftId, rules[ALIGN_LEFT]);
1847                 propertyReader.readBoolean(
1848                         mAlignParentBottomId, rules[ALIGN_PARENT_BOTTOM] == TRUE);
1849                 propertyReader.readBoolean(mAlignParentEndId, rules[ALIGN_PARENT_END] == TRUE);
1850                 propertyReader.readBoolean(mAlignParentLeftId, rules[ALIGN_PARENT_LEFT] == TRUE);
1851                 propertyReader.readBoolean(mAlignParentRightId, rules[ALIGN_PARENT_RIGHT] == TRUE);
1852                 propertyReader.readBoolean(mAlignParentStartId, rules[ALIGN_PARENT_START] == TRUE);
1853                 propertyReader.readBoolean(mAlignParentTopId, rules[ALIGN_PARENT_TOP] == TRUE);
1854                 propertyReader.readResourceId(mAlignRightId, rules[ALIGN_RIGHT]);
1855                 propertyReader.readResourceId(mAlignStartId, rules[ALIGN_START]);
1856                 propertyReader.readResourceId(mAlignTopId, rules[ALIGN_TOP]);
1857                 propertyReader.readBoolean(mAlignWithParentIfMissingId, node.alignWithParent);
1858                 propertyReader.readResourceId(mBelowId, rules[BELOW]);
1859                 propertyReader.readBoolean(mCenterHorizontalId, rules[CENTER_HORIZONTAL] == TRUE);
1860                 propertyReader.readBoolean(mCenterInParentId, rules[CENTER_IN_PARENT] == TRUE);
1861                 propertyReader.readBoolean(mCenterVerticalId, rules[CENTER_VERTICAL] == TRUE);
1862                 propertyReader.readResourceId(mToEndOfId, rules[END_OF]);
1863                 propertyReader.readResourceId(mToLeftOfId, rules[LEFT_OF]);
1864                 propertyReader.readResourceId(mToRightOfId, rules[RIGHT_OF]);
1865                 propertyReader.readResourceId(mToStartOfId, rules[START_OF]);
1866             }
1867         }
1868     }
1869 
1870     private static class DependencyGraph {
1871         /**
1872          * List of all views in the graph.
1873          */
1874         private ArrayList<Node> mNodes = new ArrayList<Node>();
1875 
1876         /**
1877          * List of nodes in the graph. Each node is identified by its
1878          * view id (see View#getId()).
1879          */
1880         private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
1881 
1882         /**
1883          * Temporary data structure used to build the list of roots
1884          * for this graph.
1885          */
1886         private ArrayDeque<Node> mRoots = new ArrayDeque<Node>();
1887 
1888         /**
1889          * Clears the graph.
1890          */
clear()1891         void clear() {
1892             final ArrayList<Node> nodes = mNodes;
1893             final int count = nodes.size();
1894 
1895             for (int i = 0; i < count; i++) {
1896                 nodes.get(i).release();
1897             }
1898             nodes.clear();
1899 
1900             mKeyNodes.clear();
1901             mRoots.clear();
1902         }
1903 
1904         /**
1905          * Adds a view to the graph.
1906          *
1907          * @param view The view to be added as a node to the graph.
1908          */
add(View view)1909         void add(View view) {
1910             final int id = view.getId();
1911             final Node node = Node.acquire(view);
1912 
1913             if (id != View.NO_ID) {
1914                 mKeyNodes.put(id, node);
1915             }
1916 
1917             mNodes.add(node);
1918         }
1919 
1920         /**
1921          * Builds a sorted list of views. The sorting order depends on the dependencies
1922          * between the view. For instance, if view C needs view A to be processed first
1923          * and view A needs view B to be processed first, the dependency graph
1924          * is: B -> A -> C. The sorted array will contain views B, A and C in this order.
1925          *
1926          * @param sorted The sorted list of views. The length of this array must
1927          *        be equal to getChildCount().
1928          * @param rules The list of rules to take into account.
1929          */
getSortedViews(View[] sorted, int... rules)1930         void getSortedViews(View[] sorted, int... rules) {
1931             final ArrayDeque<Node> roots = findRoots(rules);
1932             int index = 0;
1933 
1934             Node node;
1935             while ((node = roots.pollLast()) != null) {
1936                 final View view = node.view;
1937                 final int key = view.getId();
1938 
1939                 sorted[index++] = view;
1940 
1941                 final ArrayMap<Node, DependencyGraph> dependents = node.dependents;
1942                 final int count = dependents.size();
1943                 for (int i = 0; i < count; i++) {
1944                     final Node dependent = dependents.keyAt(i);
1945                     final SparseArray<Node> dependencies = dependent.dependencies;
1946 
1947                     dependencies.remove(key);
1948                     if (dependencies.size() == 0) {
1949                         roots.add(dependent);
1950                     }
1951                 }
1952             }
1953 
1954             if (index < sorted.length) {
1955                 throw new IllegalStateException("Circular dependencies cannot exist"
1956                         + " in RelativeLayout");
1957             }
1958         }
1959 
1960         /**
1961          * Finds the roots of the graph. A root is a node with no dependency and
1962          * with [0..n] dependents.
1963          *
1964          * @param rulesFilter The list of rules to consider when building the
1965          *        dependencies
1966          *
1967          * @return A list of node, each being a root of the graph
1968          */
findRoots(int[] rulesFilter)1969         private ArrayDeque<Node> findRoots(int[] rulesFilter) {
1970             final SparseArray<Node> keyNodes = mKeyNodes;
1971             final ArrayList<Node> nodes = mNodes;
1972             final int count = nodes.size();
1973 
1974             // Find roots can be invoked several times, so make sure to clear
1975             // all dependents and dependencies before running the algorithm
1976             for (int i = 0; i < count; i++) {
1977                 final Node node = nodes.get(i);
1978                 node.dependents.clear();
1979                 node.dependencies.clear();
1980             }
1981 
1982             // Builds up the dependents and dependencies for each node of the graph
1983             for (int i = 0; i < count; i++) {
1984                 final Node node = nodes.get(i);
1985 
1986                 final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
1987                 final int[] rules = layoutParams.mRules;
1988                 final int rulesCount = rulesFilter.length;
1989 
1990                 // Look only the the rules passed in parameter, this way we build only the
1991                 // dependencies for a specific set of rules
1992                 for (int j = 0; j < rulesCount; j++) {
1993                     final int rule = rules[rulesFilter[j]];
1994                     if (rule > 0 || ResourceId.isValid(rule)) {
1995                         // The node this node depends on
1996                         final Node dependency = keyNodes.get(rule);
1997                         // Skip unknowns and self dependencies
1998                         if (dependency == null || dependency == node) {
1999                             continue;
2000                         }
2001                         // Add the current node as a dependent
2002                         dependency.dependents.put(node, this);
2003                         // Add a dependency to the current node
2004                         node.dependencies.put(rule, dependency);
2005                     }
2006                 }
2007             }
2008 
2009             final ArrayDeque<Node> roots = mRoots;
2010             roots.clear();
2011 
2012             // Finds all the roots in the graph: all nodes with no dependencies
2013             for (int i = 0; i < count; i++) {
2014                 final Node node = nodes.get(i);
2015                 if (node.dependencies.size() == 0) roots.addLast(node);
2016             }
2017 
2018             return roots;
2019         }
2020 
2021         /**
2022          * A node in the dependency graph. A node is a view, its list of dependencies
2023          * and its list of dependents.
2024          *
2025          * A node with no dependent is considered a root of the graph.
2026          */
2027         static class Node {
2028 
2029             @UnsupportedAppUsage
Node()2030             Node() {
2031             }
2032 
2033             /**
2034              * The view representing this node in the layout.
2035              */
2036             View view;
2037 
2038             /**
2039              * The list of dependents for this node; a dependent is a node
2040              * that needs this node to be processed first.
2041              */
2042             final ArrayMap<Node, DependencyGraph> dependents =
2043                     new ArrayMap<Node, DependencyGraph>();
2044 
2045             /**
2046              * The list of dependencies for this node.
2047              */
2048             final SparseArray<Node> dependencies = new SparseArray<Node>();
2049 
2050             /*
2051              * START POOL IMPLEMENTATION
2052              */
2053             // The pool is static, so all nodes instances are shared across
2054             // activities, that's why we give it a rather high limit
2055             private static final int POOL_LIMIT = 100;
2056             private static final SynchronizedPool<Node> sPool =
2057                     new SynchronizedPool<Node>(POOL_LIMIT);
2058 
acquire(View view)2059             static Node acquire(View view) {
2060                 Node node = sPool.acquire();
2061                 if (node == null) {
2062                     node = new Node();
2063                 }
2064                 node.view = view;
2065                 return node;
2066             }
2067 
release()2068             void release() {
2069                 view = null;
2070                 dependents.clear();
2071                 dependencies.clear();
2072 
2073                 sPool.release(this);
2074             }
2075             /*
2076              * END POOL IMPLEMENTATION
2077              */
2078         }
2079     }
2080 }
2081