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