content: /*
   * Copyright (C) 2006 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.widget;
  
  import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
  
  import android.annotation.NonNull;
  import android.annotation.UnsupportedAppUsage;
  import android.content.Context;
  import android.content.res.ResourceId;
  import android.content.res.TypedArray;
  import android.graphics.Rect;
  import android.os.Build;
  import android.util.ArrayMap;
  import android.util.AttributeSet;
  import android.util.Pools.SynchronizedPool;
  import android.util.SparseArray;
  import android.view.Gravity;
  import android.view.View;
  import android.view.ViewDebug;
  import android.view.ViewGroup;
  import android.view.ViewHierarchyEncoder;
  import android.view.accessibility.AccessibilityEvent;
  import android.view.inspector.InspectableProperty;
  import android.view.inspector.InspectionCompanion;
  import android.view.inspector.PropertyMapper;
  import android.view.inspector.PropertyReader;
  import android.widget.RemoteViews.RemoteView;
  
  import com.android.internal.R;
  
  import java.util.ArrayDeque;
  import java.util.ArrayList;
  import java.util.Comparator;
  import java.util.SortedSet;
  import java.util.TreeSet;
  
  /**
   * A Layout where the positions of the children can be described in relation to each other or to the
   * parent.
   *
   * <p>
   * Note that you cannot have a circular dependency between the size of the RelativeLayout and the
   * position of its children. For example, you cannot have a RelativeLayout whose height is set to
   * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT} and a child set to
   * {@link #ALIGN_PARENT_BOTTOM}.
   * </p>
   *
   * <p><strong>Note:</strong> In platform version 17 and lower, RelativeLayout was affected by
   * a measurement bug that could cause child views to be measured with incorrect
   * {@link android.view.View.MeasureSpec MeasureSpec} values. (See
   * {@link android.view.View.MeasureSpec#makeMeasureSpec(int, int) MeasureSpec.makeMeasureSpec}
   * for more details.) This was triggered when a RelativeLayout container was placed in
   * a scrolling container, such as a ScrollView or HorizontalScrollView. If a custom view
   * not equipped to properly measure with the MeasureSpec mode
   * {@link android.view.View.MeasureSpec#UNSPECIFIED UNSPECIFIED} was placed in a RelativeLayout,
   * this would silently work anyway as RelativeLayout would pass a very large
   * {@link android.view.View.MeasureSpec#AT_MOST AT_MOST} MeasureSpec instead.</p>
   *
   * <p>This behavior has been preserved for apps that set <code>android:targetSdkVersion="17"</code>
   * or older in their manifest's <code>uses-sdk</code> tag for compatibility. Apps targeting SDK
   * version 18 or newer will receive the correct behavior.</p>
   *
   * <p>See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative
   * Layout</a> guide.</p>
   *
   * <p>
   * Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for
   * layout attributes
   * </p>
   *
   * @attr ref android.R.styleable#RelativeLayout_gravity
   * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
   */
  @RemoteView
  public class RelativeLayout extends ViewGroup {
      public static final int TRUE = -1;
  
      /**
       * Rule that aligns a child's right edge with another child's left edge.
       */
      public static final int LEFT_OF                  = 0;
      /**
       * Rule that aligns a child's left edge with another child's right edge.
       */
      public static final int RIGHT_OF                 = 1;
      /**
       * Rule that aligns a child's bottom edge with another child's top edge.
       */
      public static final int ABOVE                    = 2;
      /**
       * Rule that aligns a child's top edge with another child's bottom edge.
       */
      public static final int BELOW                    = 3;
  
      /**
       * Rule that aligns a child's baseline with another child's baseline.
       */
      public static final int ALIGN_BASELINE           = 4;
      /**
       * Rule that aligns a child's left edge with another child's left edge.
       */
      public static final int ALIGN_LEFT               = 5;
      /**
       * Rule that aligns a child's top edge with another child's top edge.
       */
      public static final int ALIGN_TOP                = 6;
      /**
       * Rule that aligns a child's right edge with another child's right edge.
       */
      public static final int ALIGN_RIGHT              = 7;
      /**
       * Rule that aligns a child's bottom edge with another child's bottom edge.
       */
      public static final int ALIGN_BOTTOM             = 8;
  
      /**
       * Rule that aligns the child's left edge with its RelativeLayout
       * parent's left edge.
       */
      public static final int ALIGN_PARENT_LEFT        = 9;
      /**
       * Rule that aligns the child's top edge with its RelativeLayout
       * parent's top edge.
       */
      public static final int ALIGN_PARENT_TOP         = 10;
      /**
       * Rule that aligns the child's right edge with its RelativeLayout
       * parent's right edge.
       */
      public static final int ALIGN_PARENT_RIGHT       = 11;
      /**
       * Rule that aligns the child's bottom edge with its RelativeLayout
       * parent's bottom edge.
       */
      public static final int ALIGN_PARENT_BOTTOM      = 12;
  
      /**
       * Rule that centers the child with respect to the bounds of its
       * RelativeLayout parent.
       */
      public static final int CENTER_IN_PARENT         = 13;
      /**
       * Rule that centers the child horizontally with respect to the
       * bounds of its RelativeLayout parent.
       */
      public static final int CENTER_HORIZONTAL        = 14;
      /**
       * Rule that centers the child vertically with respect to the
       * bounds of its RelativeLayout parent.
       */
      public static final int CENTER_VERTICAL          = 15;
      /**
       * Rule that aligns a child's end edge with another child's start edge.
       */
      public static final int START_OF                 = 16;
      /**
       * Rule that aligns a child's start edge with another child's end edge.
       */
      public static final int END_OF                   = 17;
      /**
       * Rule that aligns a child's start edge with another child's start edge.
       */
      public static final int ALIGN_START              = 18;
      /**
       * Rule that aligns a child's end edge with another child's end edge.
       */
      public static final int ALIGN_END                = 19;
      /**
       * Rule that aligns the child's start edge with its RelativeLayout
       * parent's start edge.
       */
      public static final int ALIGN_PARENT_START       = 20;
      /**
       * Rule that aligns the child's end edge with its RelativeLayout
       * parent's end edge.
       */
      public static final int ALIGN_PARENT_END         = 21;
  
      private static final int VERB_COUNT              = 22;
  
  
      private static final int[] RULES_VERTICAL = {
              ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM
      };
  
      private static final int[] RULES_HORIZONTAL = {
              LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT, START_OF, END_OF, ALIGN_START, ALIGN_END
      };
  
      /**
       * Used to indicate left/right/top/bottom should be inferred from constraints
       */
      private static final int VALUE_NOT_SET = Integer.MIN_VALUE;
  
      private View mBaselineView = null;
  
      @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
      private int mGravity = Gravity.START | Gravity.TOP;
      private final Rect mContentBounds = new Rect();
      private final Rect mSelfBounds = new Rect();
      private int mIgnoreGravity;
  
      private SortedSet<View> mTopToBottomLeftToRightSet = null;
  
      private boolean mDirtyHierarchy;
      private View[] mSortedHorizontalChildren;
      private View[] mSortedVerticalChildren;
      private final DependencyGraph mGraph = new DependencyGraph();
  
      // Compatibility hack. Old versions of the platform had problems
      // with MeasureSpec value overflow and RelativeLayout was one source of them.
      // Some apps came to rely on them. :(
      private boolean mAllowBrokenMeasureSpecs = false;
      // Compatibility hack. Old versions of the platform would not take
      // margins and padding into account when generating the height measure spec
      // for children during the horizontal measure pass.
      private boolean mMeasureVerticalWithPaddingMargin = false;
  
      // A default width used for RTL measure pass
      /**
       * Value reduced so as not to interfere with View's measurement spec. flags. See:
       * {@link View#MEASURED_SIZE_MASK}.
       * {@link View#MEASURED_STATE_TOO_SMALL}.
       **/
      private static final int DEFAULT_WIDTH = 0x00010000;
  
      public RelativeLayout(Context context) {
          this(context, null);
      }
  
      public RelativeLayout(Context context, AttributeSet attrs) {
          this(context, attrs, 0);
      }
  
      public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
          this(context, attrs, defStyleAttr, 0);
      }
  
      public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
          super(context, attrs, defStyleAttr, defStyleRes);
          initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
          queryCompatibilityModes(context);
      }
  
      private void initFromAttributes(
              Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
          final TypedArray a = context.obtainStyledAttributes(
                  attrs, R.styleable.RelativeLayout, defStyleAttr, defStyleRes);
          saveAttributeDataForStyleable(context, R.styleable.RelativeLayout,
                  attrs, a, defStyleAttr, defStyleRes);
          mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, View.NO_ID);
          mGravity = a.getInt(R.styleable.RelativeLayout_gravity, mGravity);
          a.recycle();
      }
  
      private void queryCompatibilityModes(Context context) {
          int version = context.getApplicationInfo().targetSdkVersion;
          mAllowBrokenMeasureSpecs = version <= Build.VERSION_CODES.JELLY_BEAN_MR1;
          mMeasureVerticalWithPaddingMargin = version >= Build.VERSION_CODES.JELLY_BEAN_MR2;
      }
  
      @Override
      public boolean shouldDelayChildPressedState() {
          return false;
      }
  
      /**
       * Defines which View is ignored when the gravity is applied. This setting has no
       * effect if the gravity is <code>Gravity.START | Gravity.TOP</code>.
       *
       * @param viewId The id of the View to be ignored by gravity, or 0 if no View
       *        should be ignored.
       *
       * @see #setGravity(int)
       *
       * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
       */
      @android.view.RemotableViewMethod
      public void setIgnoreGravity(int viewId) {
          mIgnoreGravity = viewId;
      }
  
      /**
       * Get the id of the View to be ignored by gravity
       *
       * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
       */
      @InspectableProperty
      public int getIgnoreGravity() {
          return mIgnoreGravity;
      }
  
      /**
       * Describes how the child views are positioned.
       *
       * @return the gravity.
       *
       * @see #setGravity(int)
       * @see android.view.Gravity
       *
       * @attr ref android.R.styleable#RelativeLayout_gravity
       */
      @InspectableProperty(valueType = InspectableProperty.ValueType.GRAVITY)
      public int getGravity() {
          return mGravity;
      }
  
      /**
       * Describes how the child views are positioned. Defaults to
       * <code>Gravity.START | Gravity.TOP</code>.
       *
       * <p>Note that since RelativeLayout considers the positioning of each child
       * relative to one another to be significant, setting gravity will affect
       * the positioning of all children as a single unit within the parent.
       * This happens after children have been relatively positioned.</p>
       *
       * @param gravity See {@link android.view.Gravity}
       *
       * @see #setHorizontalGravity(int)
       * @see #setVerticalGravity(int)
       *
       * @attr ref android.R.styleable#RelativeLayout_gravity
       */
      @android.view.RemotableViewMethod
      public void setGravity(int gravity) {
          if (mGravity != gravity) {
              if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
                  gravity |= Gravity.START;
              }
  
              if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
                  gravity |= Gravity.TOP;
              }
  
              mGravity = gravity;
              requestLayout();
          }
      }
  
      @android.view.RemotableViewMethod
      public void setHorizontalGravity(int horizontalGravity) {
          final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
          if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
              mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
              requestLayout();
          }
      }
  
      @android.view.RemotableViewMethod
      public void setVerticalGravity(int verticalGravity) {
          final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
          if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
              mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
              requestLayout();
          }
      }
  
      @Override
      public int getBaseline() {
          return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline();
      }
  
      @Override
      public void requestLayout() {
          super.requestLayout();
          mDirtyHierarchy = true;
      }
  
      private void sortChildren() {
          final int count = getChildCount();
          if (mSortedVerticalChildren == null || mSortedVerticalChildren.length != count) {
              mSortedVerticalChildren = new View[count];
          }
  
          if (mSortedHorizontalChildren == null || mSortedHorizontalChildren.length != count) {
              mSortedHorizontalChildren = new View[count];
          }
  
          final DependencyGraph graph = mGraph;
          graph.clear();
  
          for (int i = 0; i < count; i++) {
              graph.add(getChildAt(i));
          }
  
          graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL);
          graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL);
      }
  
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          if (mDirtyHierarchy) {
              mDirtyHierarchy = false;
              sortChildren();
          }
  
          int myWidth = -1;
          int myHeight = -1;
  
          int width = 0;
          int height = 0;
  
          final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
          final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
          final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
          final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
  
          // Record our dimensions if they are known;
          if (widthMode != MeasureSpec.UNSPECIFIED) {
              myWidth = widthSize;
          }
  
          if (heightMode != MeasureSpec.UNSPECIFIED) {
              myHeight = heightSize;
          }
  
          if (widthMode == MeasureSpec.EXACTLY) {
              width = myWidth;
          }
  
          if (heightMode == MeasureSpec.EXACTLY) {
              height = myHeight;
          }
  
          View ignore = null;
          int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
          final boolean horizontalGravity = gravity != Gravity.START && gravity != 0;
          gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
          final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;
  
          int left = Integer.MAX_VALUE;
          int top = Integer.MAX_VALUE;
          int right = Integer.MIN_VALUE;
          int bottom = Integer.MIN_VALUE;
  
          boolean offsetHorizontalAxis = false;
          boolean offsetVerticalAxis = false;
  
          if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
              ignore = findViewById(mIgnoreGravity);
          }
  
          final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
          final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
  
          // We need to know our size for doing the correct computation of children positioning in RTL
          // mode but there is no practical way to get it instead of running the code below.
          // So, instead of running the code twice, we just set the width to a "default display width"
          // before the computation and then, as a last pass, we will update their real position with
          // an offset equals to "DEFAULT_WIDTH - width".
          final int layoutDirection = getLayoutDirection();
          if (isLayoutRtl() && myWidth == -1) {
              myWidth = DEFAULT_WIDTH;
          }
  
          View[] views = mSortedHorizontalChildren;
          int count = views.length;
  
          for (int i = 0; i < count; i++) {
              View child = views[i];
              if (child.getVisibility() != GONE) {
                  LayoutParams params = (LayoutParams) child.getLayoutParams();
                  int[] rules = params.getRules(layoutDirection);
  
                  applyHorizontalSizeRules(params, myWidth, rules);
                  measureChildHorizontal(child, params, myWidth, myHeight);
  
                  if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
                      offsetHorizontalAxis = true;
                  }
              }
          }
  
          views = mSortedVerticalChildren;
          count = views.length;
          final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
  
          for (int i = 0; i < count; i++) {
              final View child = views[i];
              if (child.getVisibility() != GONE) {
                  final LayoutParams params = (LayoutParams) child.getLayoutParams();
  
                  applyVerticalSizeRules(params, myHeight, child.getBaseline());
                  measureChild(child, params, myWidth, myHeight);
                  if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
                      offsetVerticalAxis = true;
                  }
  
                  if (isWrapContentWidth) {
                      if (isLayoutRtl()) {
                          if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                              width = Math.max(width, myWidth - params.mLeft);
                          } else {
                              width = Math.max(width, myWidth - params.mLeft + params.leftMargin);
                          }
                      } else {
                          if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                              width = Math.max(width, params.mRight);
                          } else {
                              width = Math.max(width, params.mRight + params.rightMargin);
                          }
                      }
                  }
  
                  if (isWrapContentHeight) {
                      if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                          height = Math.max(height, params.mBottom);
                      } else {
                          height = Math.max(height, params.mBottom + params.bottomMargin);
                      }
                  }
  
                  if (child != ignore || verticalGravity) {
                      left = Math.min(left, params.mLeft - params.leftMargin);
                      top = Math.min(top, params.mTop - params.topMargin);
                  }
  
                  if (child != ignore || horizontalGravity) {
                      right = Math.max(right, params.mRight + params.rightMargin);
                      bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
                  }
              }
          }
  
          // Use the top-start-most laid out view as the baseline. RTL offsets are
          // applied later, so we can use the left-most edge as the starting edge.
          View baselineView = null;
          LayoutParams baselineParams = null;
          for (int i = 0; i < count; i++) {
              final View child = views[i];
              if (child.getVisibility() != GONE) {
                  final LayoutParams childParams = (LayoutParams) child.getLayoutParams();
                  if (baselineView == null || baselineParams == null
                          || compareLayoutPosition(childParams, baselineParams) < 0) {
                      baselineView = child;
                      baselineParams = childParams;
                  }
              }
          }
          mBaselineView = baselineView;
  
          if (isWrapContentWidth) {
              // Width already has left padding in it since it was calculated by looking at
              // the right of each child view
              width += mPaddingRight;
  
              if (mLayoutParams != null && mLayoutParams.width >= 0) {
                  width = Math.max(width, mLayoutParams.width);
              }
  
              width = Math.max(width, getSuggestedMinimumWidth());
              width = resolveSize(width, widthMeasureSpec);
  
              if (offsetHorizontalAxis) {
                  for (int i = 0; i < count; i++) {
                      final View child = views[i];
                      if (child.getVisibility() != GONE) {
                          final LayoutParams params = (LayoutParams) child.getLayoutParams();
                          final int[] rules = params.getRules(layoutDirection);
                          if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
                              centerHorizontal(child, params, width);
                          } else if (rules[ALIGN_PARENT_RIGHT] != 0) {
                              final int childWidth = child.getMeasuredWidth();
                              params.mLeft = width - mPaddingRight - childWidth;
                              params.mRight = params.mLeft + childWidth;
                          }
                      }
                  }
              }
          }
  
          if (isWrapContentHeight) {
              // Height already has top padding in it since it was calculated by looking at
              // the bottom of each child view
              height += mPaddingBottom;
  
              if (mLayoutParams != null && mLayoutParams.height >= 0) {
                  height = Math.max(height, mLayoutParams.height);
              }
  
              height = Math.max(height, getSuggestedMinimumHeight());
              height = resolveSize(height, heightMeasureSpec);
  
              if (offsetVerticalAxis) {
                  for (int i = 0; i < count; i++) {
                      final View child = views[i];
                      if (child.getVisibility() != GONE) {
                          final LayoutParams params = (LayoutParams) child.getLayoutParams();
                          final int[] rules = params.getRules(layoutDirection);
                          if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
                              centerVertical(child, params, height);
                          } else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
                              final int childHeight = child.getMeasuredHeight();
                              params.mTop = height - mPaddingBottom - childHeight;
                              params.mBottom = params.mTop + childHeight;
                          }
                      }
                  }
              }
          }
  
          if (horizontalGravity || verticalGravity) {
              final Rect selfBounds = mSelfBounds;
              selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
                      height - mPaddingBottom);
  
              final Rect contentBounds = mContentBounds;
              Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,
                      layoutDirection);
  
              final int horizontalOffset = contentBounds.left - left;
              final int verticalOffset = contentBounds.top - top;
              if (horizontalOffset != 0 || verticalOffset != 0) {
                  for (int i = 0; i < count; i++) {
                      final View child = views[i];
                      if (child.getVisibility() != GONE && child != ignore) {
                          final LayoutParams params = (LayoutParams) child.getLayoutParams();
                          if (horizontalGravity) {
                              params.mLeft += horizontalOffset;
                              params.mRight += horizontalOffset;
                          }
                          if (verticalGravity) {
                              params.mTop += verticalOffset;
                              params.mBottom += verticalOffset;
                          }
                      }
                  }
              }
          }
  
          if (isLayoutRtl()) {
              final int offsetWidth = myWidth - width;
              for (int i = 0; i < count; i++) {
                  final View child = views[i];
                  if (child.getVisibility() != GONE) {
                      final LayoutParams params = (LayoutParams) child.getLayoutParams();
                      params.mLeft -= offsetWidth;
                      params.mRight -= offsetWidth;
                  }
              }
          }
  
          setMeasuredDimension(width, height);
      }
  
      /**
       * @return a negative number if the top of {@code p1} is above the top of
       *         {@code p2} or if they have identical top values and the left of
       *         {@code p1} is to the left of {@code p2}, or a positive number
       *         otherwise
       */
      private int compareLayoutPosition(LayoutParams p1, LayoutParams p2) {
          final int topDiff = p1.mTop - p2.mTop;
          if (topDiff != 0) {
              return topDiff;
          }
          return p1.mLeft - p2.mLeft;
      }
  
      /**
       * Measure a child. The child should have left, top, right and bottom information
       * stored in its LayoutParams. If any of these values is VALUE_NOT_SET it means
       * that the view can extend up to the corresponding edge.
       *
       * @param child Child to measure
       * @param params LayoutParams associated with child
       * @param myWidth Width of the the RelativeLayout
       * @param myHeight Height of the RelativeLayout
       */
      private void measureChild(View child, LayoutParams params, int myWidth, int myHeight) {
          int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
                  params.mRight, params.width,
                  params.leftMargin, params.rightMargin,
                  mPaddingLeft, mPaddingRight,
                  myWidth);
          int childHeightMeasureSpec = getChildMeasureSpec(params.mTop,
                  params.mBottom, params.height,
                  params.topMargin, params.bottomMargin,
                  mPaddingTop, mPaddingBottom,
                  myHeight);
          child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
      }
  
      private void measureChildHorizontal(
              View child, LayoutParams params, int myWidth, int myHeight) {
          final int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, params.mRight,
                  params.width, params.leftMargin, params.rightMargin, mPaddingLeft, mPaddingRight,
                  myWidth);
  
          final int childHeightMeasureSpec;
          if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
              if (params.height >= 0) {
                  childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                          params.height, MeasureSpec.EXACTLY);
              } else {
                  // Negative values in a mySize/myWidth/myWidth value in
                  // RelativeLayout measurement is code for, "we got an
                  // unspecified mode in the RelativeLayout's measure spec."
                  // Carry it forward.
                  childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
              }
          } else {
              final int maxHeight;
              if (mMeasureVerticalWithPaddingMargin) {
                  maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom
                          - params.topMargin - params.bottomMargin);
              } else {
                  maxHeight = Math.max(0, myHeight);
              }
  
              final int heightMode;
              if (params.height == LayoutParams.MATCH_PARENT) {
                  heightMode = MeasureSpec.EXACTLY;
              } else {
                  heightMode = MeasureSpec.AT_MOST;
              }
              childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, heightMode);
          }
  
          child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
      }
  
      /**
       * Get a measure spec that accounts for all of the constraints on this view.
       * This includes size constraints imposed by the RelativeLayout as well as
       * the View's desired dimension.
       *
       * @param childStart The left or top field of the child's layout params
       * @param childEnd The right or bottom field of the child's layout params
       * @param childSize The child's desired size (the width or height field of
       *        the child's layout params)
       * @param startMargin The left or top margin
       * @param endMargin The right or bottom margin
       * @param startPadding mPaddingLeft or mPaddingTop
       * @param endPadding mPaddingRight or mPaddingBottom
       * @param mySize The width or height of this view (the RelativeLayout)
       * @return MeasureSpec for the child
       */
      private int getChildMeasureSpec(int childStart, int childEnd,
              int childSize, int startMargin, int endMargin, int startPadding,
              int endPadding, int mySize) {
          int childSpecMode = 0;
          int childSpecSize = 0;
  
          // Negative values in a mySize value in RelativeLayout
          // measurement is code for, "we got an unspecified mode in the
          // RelativeLayout's measure spec."
          final boolean isUnspecified = mySize < 0;
          if (isUnspecified && !mAllowBrokenMeasureSpecs) {
              if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
                  // Constraints fixed both edges, so child has an exact size.
                  childSpecSize = Math.max(0, childEnd - childStart);
                  childSpecMode = MeasureSpec.EXACTLY;
              } else if (childSize >= 0) {
                  // The child specified an exact size.
                  childSpecSize = childSize;
                  childSpecMode = MeasureSpec.EXACTLY;
              } else {
                  // Allow the child to be whatever size it wants.
                  childSpecSize = 0;
                  childSpecMode = MeasureSpec.UNSPECIFIED;
              }
  
              return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
          }
  
          // Figure out start and end bounds.
          int tempStart = childStart;
          int tempEnd = childEnd;
  
          // If the view did not express a layout constraint for an edge, use
          // view's margins and our padding
          if (tempStart == VALUE_NOT_SET) {
              tempStart = startPadding + startMargin;
          }
          if (tempEnd == VALUE_NOT_SET) {
              tempEnd = mySize - endPadding - endMargin;
          }
  
          // Figure out maximum size available to this view
          final int maxAvailable = tempEnd - tempStart;
  
          if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
              // Constraints fixed both edges, so child must be an exact size.
              childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
              childSpecSize = Math.max(0, maxAvailable);
          } else {
              if (childSize >= 0) {
                  // Child wanted an exact size. Give as much as possible.
                  childSpecMode = MeasureSpec.EXACTLY;
  
                  if (maxAvailable >= 0) {
                      // We have a maximum size in this dimension.
                      childSpecSize = Math.min(maxAvailable, childSize);
                  } else {
                      // We can grow in this dimension.
                      childSpecSize = childSize;
                  }
              } else if (childSize == LayoutParams.MATCH_PARENT) {
                  // Child wanted to be as big as possible. Give all available
                  // space.
                  childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
                  childSpecSize = Math.max(0, maxAvailable);
              } else if (childSize == LayoutParams.WRAP_CONTENT) {
                  // Child wants to wrap content. Use AT_MOST to communicate
                  // available space if we know our max size.
                  if (maxAvailable >= 0) {
                      // We have a maximum size in this dimension.
                      childSpecMode = MeasureSpec.AT_MOST;
                      childSpecSize = maxAvailable;
                  } else {
                      // We can grow in this dimension. Child can be as big as it
                      // wants.
                      childSpecMode = MeasureSpec.UNSPECIFIED;
                      childSpecSize = 0;
                  }
              }
          }
  
          return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
      }
  
      private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth,
              boolean wrapContent) {
  
          final int layoutDirection = getLayoutDirection();
          int[] rules = params.getRules(layoutDirection);
  
          if (params.mLeft == VALUE_NOT_SET && params.mRight != VALUE_NOT_SET) {
              // Right is fixed, but left varies
              params.mLeft = params.mRight - child.getMeasuredWidth();
          } else if (params.mLeft != VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
              // Left is fixed, but right varies
              params.mRight = params.mLeft + child.getMeasuredWidth();
          } else if (params.mLeft == VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
              // Both left and right vary
              if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
                  if (!wrapContent) {
                      centerHorizontal(child, params, myWidth);
                  } else {
                      positionAtEdge(child, params, myWidth);
                  }
                  return true;
              } else {
                  // This is the default case. For RTL we start from the right and for LTR we start
                  // from the left. This will give LEFT/TOP for LTR and RIGHT/TOP for RTL.
                  positionAtEdge(child, params, myWidth);
              }
          }
          return rules[ALIGN_PARENT_END] != 0;
      }
  
      private void positionAtEdge(View child, LayoutParams params, int myWidth) {
          if (isLayoutRtl()) {
              params.mRight = myWidth - mPaddingRight - params.rightMargin;
              params.mLeft = params.mRight - child.getMeasuredWidth();
          } else {
              params.mLeft = mPaddingLeft + params.leftMargin;
              params.mRight = params.mLeft + child.getMeasuredWidth();
          }
      }
  
      private boolean positionChildVertical(View child, LayoutParams params, int myHeight,
              boolean wrapContent) {
  
          int[] rules = params.getRules();
  
          if (params.mTop == VALUE_NOT_SET && params.mBottom != VALUE_NOT_SET) {
              // Bottom is fixed, but top varies
              params.mTop = params.mBottom - child.getMeasuredHeight();
          } else if (params.mTop != VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) {
              // Top is fixed, but bottom varies
              params.mBottom = params.mTop + child.getMeasuredHeight();
          } else if (params.mTop == VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) {
              // Both top and bottom vary
              if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
                  if (!wrapContent) {
                      centerVertical(child, params, myHeight);
                  } else {
                      params.mTop = mPaddingTop + params.topMargin;
                      params.mBottom = params.mTop + child.getMeasuredHeight();
                  }
                  return true;
              } else {
                  params.mTop = mPaddingTop + params.topMargin;
                  params.mBottom = params.mTop + child.getMeasuredHeight();
              }
          }
          return rules[ALIGN_PARENT_BOTTOM] != 0;
      }
  
      private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) {
          RelativeLayout.LayoutParams anchorParams;
  
          // VALUE_NOT_SET indicates a "soft requirement" in that direction. For example:
          // left=10, right=VALUE_NOT_SET means the view must start at 10, but can go as far as it
          // wants to the right
          // left=VALUE_NOT_SET, right=10 means the view must end at 10, but can go as far as it
          // wants to the left
          // left=10, right=20 means the left and right ends are both fixed
          childParams.mLeft = VALUE_NOT_SET;
          childParams.mRight = VALUE_NOT_SET;
  
          anchorParams = getRelatedViewParams(rules, LEFT_OF);
          if (anchorParams != null) {
              childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
                      childParams.rightMargin);
          } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
              if (myWidth >= 0) {
                  childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
              }
          }
  
          anchorParams = getRelatedViewParams(rules, RIGHT_OF);
          if (anchorParams != null) {
              childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
                      childParams.leftMargin);
          } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
              childParams.mLeft = mPaddingLeft + childParams.leftMargin;
          }
  
          anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
          if (anchorParams != null) {
              childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
          } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
              childParams.mLeft = mPaddingLeft + childParams.leftMargin;
          }
  
          anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
          if (anchorParams != null) {
              childParams.mRight = anchorParams.mRight - childParams.rightMargin;
          } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
              if (myWidth >= 0) {
                  childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
              }
          }
  
          if (0 != rules[ALIGN_PARENT_LEFT]) {
              childParams.mLeft = mPaddingLeft + childParams.leftMargin;
          }
  
          if (0 != rules[ALIGN_PARENT_RIGHT]) {
              if (myWidth >= 0) {
                  childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
              }
          }
      }
  
      private void applyVerticalSizeRules(LayoutParams childParams, int myHeight, int myBaseline) {
          final int[] rules = childParams.getRules();
  
          // Baseline alignment overrides any explicitly specified top or bottom.
          int baselineOffset = getRelatedViewBaselineOffset(rules);
          if (baselineOffset != -1) {
              if (myBaseline != -1) {
                  baselineOffset -= myBaseline;
              }
              childParams.mTop = baselineOffset;
              childParams.mBottom = VALUE_NOT_SET;
              return;
          }
  
          RelativeLayout.LayoutParams anchorParams;
  
          childParams.mTop = VALUE_NOT_SET;
          childParams.mBottom = VALUE_NOT_SET;
  
          anchorParams = getRelatedViewParams(rules, ABOVE);
          if (anchorParams != null) {
              childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin +
                      childParams.bottomMargin);
          } else if (childParams.alignWithParent && rules[ABOVE] != 0) {
              if (myHeight >= 0) {
                  childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
              }
          }
  
          anchorParams = getRelatedViewParams(rules, BELOW);
          if (anchorParams != null) {
              childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin +
                      childParams.topMargin);
          } else if (childParams.alignWithParent && rules[BELOW] != 0) {
              childParams.mTop = mPaddingTop + childParams.topMargin;
          }
  
          anchorParams = getRelatedViewParams(rules, ALIGN_TOP);
          if (anchorParams != null) {
              childParams.mTop = anchorParams.mTop + childParams.topMargin;
          } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) {
              childParams.mTop = mPaddingTop + childParams.topMargin;
          }
  
          anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM);
          if (anchorParams != null) {
              childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin;
          } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) {
              if (myHeight >= 0) {
                  childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
              }
          }
  
          if (0 != rules[ALIGN_PARENT_TOP]) {
              childParams.mTop = mPaddingTop + childParams.topMargin;
          }
  
          if (0 != rules[ALIGN_PARENT_BOTTOM]) {
              if (myHeight >= 0) {
                  childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
              }
          }
      }
  
      private View getRelatedView(int[] rules, int relation) {
          int id = rules[relation];
          if (id != 0) {
              DependencyGraph.Node node = mGraph.mKeyNodes.get(id);
              if (node == null) return null;
              View v = node.view;
  
              // Find the first non-GONE view up the chain
              while (v.getVisibility() == View.GONE) {
                  rules = ((LayoutParams) v.getLayoutParams()).getRules(v.getLayoutDirection());
                  node = mGraph.mKeyNodes.get((rules[relation]));
                  // ignore self dependency. for more info look in git commit: da3003
                  if (node == null || v == node.view) return null;
                  v = node.view;
              }
  
              return v;
          }
  
          return null;
      }
  
      private LayoutParams getRelatedViewParams(int[] rules, int relation) {
          View v = getRelatedView(rules, relation);
          if (v != null) {
              ViewGroup.LayoutParams params = v.getLayoutParams();
              if (params instanceof LayoutParams) {
                  return (LayoutParams) v.getLayoutParams();
              }
          }
          return null;
      }
  
      private int getRelatedViewBaselineOffset(int[] rules) {
          final View v = getRelatedView(rules, ALIGN_BASELINE);
          if (v != null) {
              final int baseline = v.getBaseline();
              if (baseline != -1) {
                  final ViewGroup.LayoutParams params = v.getLayoutParams();
                  if (params instanceof LayoutParams) {
                      final LayoutParams anchorParams = (LayoutParams) v.getLayoutParams();
                      return anchorParams.mTop + baseline;
                  }
              }
          }
          return -1;
      }
  
      private static void centerHorizontal(View child, LayoutParams params, int myWidth) {
          int childWidth = child.getMeasuredWidth();
          int left = (myWidth - childWidth) / 2;
  
          params.mLeft = left;
          params.mRight = left + childWidth;
      }
  
      private static void centerVertical(View child, LayoutParams params, int myHeight) {
          int childHeight = child.getMeasuredHeight();
          int top = (myHeight - childHeight) / 2;
  
          params.mTop = top;
          params.mBottom = top + childHeight;
      }
  
      @Override
      protected void onLayout(boolean changed, int l, int t, int r, int b) {
          //  The layout has actually already been performed and the positions
          //  cached.  Apply the cached values to the children.
          final int count = getChildCount();
  
          for (int i = 0; i < count; i++) {
              View child = getChildAt(i);
              if (child.getVisibility() != GONE) {
                  RelativeLayout.LayoutParams st =
                          (RelativeLayout.LayoutParams) child.getLayoutParams();
                  child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
              }
          }
      }
  
      @Override
      public LayoutParams generateLayoutParams(AttributeSet attrs) {
          return new RelativeLayout.LayoutParams(getContext(), attrs);
      }
  
      /**
       * Returns a set of layout parameters with a width of
       * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT},
       * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning.
       */
      @Override
      protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
          return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
      }
  
      // Override to allow type-checking of LayoutParams.
      @Override
      protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
          return p instanceof RelativeLayout.LayoutParams;
      }
  
      @Override
      protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
          if (sPreserveMarginParamsInLayoutParamConversion) {
              if (lp instanceof LayoutParams) {
                  return new LayoutParams((LayoutParams) lp);
              } else if (lp instanceof MarginLayoutParams) {
                  return new LayoutParams((MarginLayoutParams) lp);
              }
          }
          return new LayoutParams(lp);
      }
  
      /** @hide */
      @Override
      public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
          if (mTopToBottomLeftToRightSet == null) {
              mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator());
          }
  
          // sort children top-to-bottom and left-to-right
          for (int i = 0, count = getChildCount(); i < count; i++) {
              mTopToBottomLeftToRightSet.add(getChildAt(i));
          }
  
          for (View view : mTopToBottomLeftToRightSet) {
              if (view.getVisibility() == View.VISIBLE
                      && view.dispatchPopulateAccessibilityEvent(event)) {
                  mTopToBottomLeftToRightSet.clear();
                  return true;
              }
          }
  
          mTopToBottomLeftToRightSet.clear();
          return false;
      }
  
      @Override
      public CharSequence getAccessibilityClassName() {
          return RelativeLayout.class.getName();
      }
  
      /**
       * Compares two views in left-to-right and top-to-bottom fashion.
       */
       private class TopToBottomLeftToRightComparator implements Comparator<View> {
          public int compare(View first, View second) {
              // top - bottom
              int topDifference = first.getTop() - second.getTop();
              if (topDifference != 0) {
                  return topDifference;
              }
              // left - right
              int leftDifference = first.getLeft() - second.getLeft();
              if (leftDifference != 0) {
                  return leftDifference;
              }
              // break tie by height
              int heightDiference = first.getHeight() - second.getHeight();
              if (heightDiference != 0) {
                  return heightDiference;
              }
              // break tie by width
              int widthDiference = first.getWidth() - second.getWidth();
              if (widthDiference != 0) {
                  return widthDiference;
              }
              return 0;
          }
      }
  
      /**
       * Specifies how a view is positioned within a {@link RelativeLayout}.
       * The relative layout containing the view uses the value of these layout parameters to
       * determine where to position the view on the screen.  If the view is not contained
       * within a relative layout, these attributes are ignored.
       *
       * See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative
       * Layout</a> guide for example code demonstrating how to use relative layout's
       * layout parameters in a layout XML.
       *
       * To learn more about layout parameters and how they differ from typical view attributes,
       * see the <a href="{@docRoot}guide/topics/ui/declaring-layout.html#attributes">Layouts
       * guide</a>.
       *
       *
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toLeftOf
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toRightOf
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_above
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_below
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBaseline
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignLeft
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignTop
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignRight
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBottom
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentLeft
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentTop
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentRight
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentBottom
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerInParent
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerHorizontal
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toStartOf
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toEndOf
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignStart
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignEnd
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentStart
       * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentEnd
       */
      public static class LayoutParams extends ViewGroup.MarginLayoutParams {
          @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
              @ViewDebug.IntToString(from = ABOVE,               to = "above"),
              @ViewDebug.IntToString(from = ALIGN_BASELINE,      to = "alignBaseline"),
              @ViewDebug.IntToString(from = ALIGN_BOTTOM,        to = "alignBottom"),
              @ViewDebug.IntToString(from = ALIGN_LEFT,          to = "alignLeft"),
              @ViewDebug.IntToString(from = ALIGN_PARENT_BOTTOM, to = "alignParentBottom"),
              @ViewDebug.IntToString(from = ALIGN_PARENT_LEFT,   to = "alignParentLeft"),
              @ViewDebug.IntToString(from = ALIGN_PARENT_RIGHT,  to = "alignParentRight"),
              @ViewDebug.IntToString(from = ALIGN_PARENT_TOP,    to = "alignParentTop"),
              @ViewDebug.IntToString(from = ALIGN_RIGHT,         to = "alignRight"),
              @ViewDebug.IntToString(from = ALIGN_TOP,           to = "alignTop"),
              @ViewDebug.IntToString(from = BELOW,               to = "below"),
              @ViewDebug.IntToString(from = CENTER_HORIZONTAL,   to = "centerHorizontal"),
              @ViewDebug.IntToString(from = CENTER_IN_PARENT,    to = "center"),
              @ViewDebug.IntToString(from = CENTER_VERTICAL,     to = "centerVertical"),
              @ViewDebug.IntToString(from = LEFT_OF,             to = "leftOf"),
              @ViewDebug.IntToString(from = RIGHT_OF,            to = "rightOf"),
              @ViewDebug.IntToString(from = ALIGN_START,         to = "alignStart"),
              @ViewDebug.IntToString(from = ALIGN_END,           to = "alignEnd"),
              @ViewDebug.IntToString(from = ALIGN_PARENT_START,  to = "alignParentStart"),
              @ViewDebug.IntToString(from = ALIGN_PARENT_END,    to = "alignParentEnd"),
              @ViewDebug.IntToString(from = START_OF,            to = "startOf"),
              @ViewDebug.IntToString(from = END_OF,              to = "endOf")
          }, mapping = {
              @ViewDebug.IntToString(from = TRUE, to = "true"),
              @ViewDebug.IntToString(from = 0,    to = "false/NO_ID")
          })
  
          private int[] mRules = new int[VERB_COUNT];
          private int[] mInitialRules = new int[VERB_COUNT];
  
          @UnsupportedAppUsage
          private int mLeft;
          @UnsupportedAppUsage
          private int mTop;
          @UnsupportedAppUsage
          private int mRight;
          @UnsupportedAppUsage
          private int mBottom;
  
          /**
           * Whether this view had any relative rules modified following the most
           * recent resolution of layout direction.
           */
          private boolean mNeedsLayoutResolution;
  
          private boolean mRulesChanged = false;
          private boolean mIsRtlCompatibilityMode = false;
  
          /**
           * When true, uses the parent as the anchor if the anchor doesn't exist or if
           * the anchor's visibility is GONE.
           */
          @ViewDebug.ExportedProperty(category = "layout")
          public boolean alignWithParent;
  
          public LayoutParams(Context c, AttributeSet attrs) {
              super(c, attrs);
  
              TypedArray a = c.obtainStyledAttributes(attrs,
                      com.android.internal.R.styleable.RelativeLayout_Layout);
  
              final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
              mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 ||
                      !c.getApplicationInfo().hasRtlSupport());
  
              final int[] rules = mRules;
              //noinspection MismatchedReadAndWriteOfArray
              final int[] initialRules = mInitialRules;
  
              final int N = a.getIndexCount();
              for (int i = 0; i < N; i++) {
                  int attr = a.getIndex(i);
                  switch (attr) {
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing:
                          alignWithParent = a.getBoolean(attr, false);
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf:
                          rules[LEFT_OF] = a.getResourceId(attr, 0);
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf:
                          rules[RIGHT_OF] = a.getResourceId(attr, 0);
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above:
                          rules[ABOVE] = a.getResourceId(attr, 0);
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below:
                          rules[BELOW] = a.getResourceId(attr, 0);
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline:
                          rules[ALIGN_BASELINE] = a.getResourceId(attr, 0);
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft:
                          rules[ALIGN_LEFT] = a.getResourceId(attr, 0);
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop:
                          rules[ALIGN_TOP] = a.getResourceId(attr, 0);
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight:
                          rules[ALIGN_RIGHT] = a.getResourceId(attr, 0);
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom:
                          rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0);
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft:
                          rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0;
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop:
                          rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0;
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight:
                          rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0;
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom:
                          rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0;
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent:
                          rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0;
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal:
                          rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0;
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical:
                          rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0;
                         break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf:
                          rules[START_OF] = a.getResourceId(attr, 0);
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf:
                          rules[END_OF] = a.getResourceId(attr, 0);
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart:
                          rules[ALIGN_START] = a.getResourceId(attr, 0);
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd:
                          rules[ALIGN_END] = a.getResourceId(attr, 0);
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart:
                          rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0;
                          break;
                      case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd:
                          rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0;
                          break;
                  }
              }
              mRulesChanged = true;
              System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT);
  
              a.recycle();
          }
  
          public LayoutParams(int w, int h) {
              super(w, h);
          }
  
          /**
           * {@inheritDoc}
           */
          public LayoutParams(ViewGroup.LayoutParams source) {
              super(source);
          }
  
          /**
           * {@inheritDoc}
           */
          public LayoutParams(ViewGroup.MarginLayoutParams source) {
              super(source);
          }
  
          /**
           * Copy constructor. Clones the width, height, margin values, and rules
           * of the source.
           *
           * @param source The layout params to copy from.
           */
          public LayoutParams(LayoutParams source) {
              super(source);
  
              this.mIsRtlCompatibilityMode = source.mIsRtlCompatibilityMode;
              this.mRulesChanged = source.mRulesChanged;
              this.alignWithParent = source.alignWithParent;
  
              System.arraycopy(source.mRules, LEFT_OF, this.mRules, LEFT_OF, VERB_COUNT);
              System.arraycopy(
                      source.mInitialRules, LEFT_OF, this.mInitialRules, LEFT_OF, VERB_COUNT);
          }
  
          @Override
          public String debug(String output) {
              return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) +
                      ", height=" + sizeToString(height) + " }";
          }
  
          /**
           * Adds a layout rule to be interpreted by the RelativeLayout.
           * <p>
           * This method should only be used for verbs that don't refer to a
           * sibling (ex. {@link #ALIGN_RIGHT}) or take a boolean
           * value ({@link #TRUE} for true or 0 for false). To
           * specify a verb that takes a subject, use {@link #addRule(int, int)}.
           * <p>
           * If the rule is relative to the layout direction (ex.
           * {@link #ALIGN_PARENT_START}), then the layout direction must be
           * resolved using {@link #resolveLayoutDirection(int)} before calling
           * {@link #getRule(int)} an absolute rule (ex.
           * {@link #ALIGN_PARENT_LEFT}.
           *
           * @param verb a layout verb, such as {@link #ALIGN_PARENT_LEFT}
           * @see #addRule(int, int)
           * @see #removeRule(int)
           * @see #getRule(int)
           */
          public void addRule(int verb) {
              addRule(verb, TRUE);
          }
  
          /**
           * Adds a layout rule to be interpreted by the RelativeLayout.
           * <p>
           * Use this for verbs that refer to a sibling (ex.
           * {@link #ALIGN_RIGHT}) or take a boolean value (ex.
           * {@link #CENTER_IN_PARENT}).
           * <p>
           * If the rule is relative to the layout direction (ex.
           * {@link #START_OF}), then the layout direction must be resolved using
           * {@link #resolveLayoutDirection(int)} before calling
           * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}.
           *
           * @param verb a layout verb, such as {@link #ALIGN_RIGHT}
           * @param subject the ID of another view to use as an anchor, or a
           *                boolean value (represented as {@link #TRUE} for true
           *                or 0 for false)
           * @see #addRule(int)
           * @see #removeRule(int)
           * @see #getRule(int)
           */
          public void addRule(int verb, int subject) {
              // If we're removing a relative rule, we'll need to force layout
              // resolution the next time it's requested.
              if (!mNeedsLayoutResolution && isRelativeRule(verb)
                      && mInitialRules[verb] != 0 && subject == 0) {
                  mNeedsLayoutResolution = true;
              }
  
              mRules[verb] = subject;
              mInitialRules[verb] = subject;
              mRulesChanged = true;
          }
  
          /**
           * Removes a layout rule to be interpreted by the RelativeLayout.
           * <p>
           * If the rule is relative to the layout direction (ex.
           * {@link #START_OF}, {@link #ALIGN_PARENT_START}, etc.) then the
           * layout direction must be resolved using
           * {@link #resolveLayoutDirection(int)} before before calling
           * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}.
           *
           * @param verb One of the verbs defined by
           *        {@link android.widget.RelativeLayout RelativeLayout}, such as
           *         ALIGN_WITH_PARENT_LEFT.
           * @see #addRule(int)
           * @see #addRule(int, int)
           * @see #getRule(int)
           */
          public void removeRule(int verb) {
              addRule(verb, 0);
          }
  
          /**
           * Returns the layout rule associated with a specific verb.
           *
           * @param verb one of the verbs defined by {@link RelativeLayout}, such
           *             as ALIGN_WITH_PARENT_LEFT
           * @return the id of another view to use as an anchor, a boolean value
           *         (represented as {@link RelativeLayout#TRUE} for true
           *         or 0 for false), or -1 for verbs that don't refer to another
           *         sibling (for example, ALIGN_WITH_PARENT_BOTTOM)
           * @see #addRule(int)
           * @see #addRule(int, int)
           */
          public int getRule(int verb) {
              return mRules[verb];
          }
  
          private boolean hasRelativeRules() {
              return (mInitialRules[START_OF] != 0 || mInitialRules[END_OF] != 0 ||
                      mInitialRules[ALIGN_START] != 0 || mInitialRules[ALIGN_END] != 0 ||
                      mInitialRules[ALIGN_PARENT_START] != 0 || mInitialRules[ALIGN_PARENT_END] != 0);
          }
  
          private boolean isRelativeRule(int rule) {
              return rule == START_OF || rule == END_OF
                      || rule == ALIGN_START || rule == ALIGN_END
                      || rule == ALIGN_PARENT_START || rule == ALIGN_PARENT_END;
          }
  
          // The way we are resolving rules depends on the layout direction and if we are pre JB MR1
          // or not.
          //
          // If we are pre JB MR1 (said as "RTL compatibility mode"), "left"/"right" rules are having
          // predominance over any "start/end" rules that could have been defined. A special case:
          // if no "left"/"right" rule has been defined and "start"/"end" rules are defined then we
          // resolve those "start"/"end" rules to "left"/"right" respectively.
          //
          // If we are JB MR1+, then "start"/"end" rules are having predominance over "left"/"right"
          // rules. If no "start"/"end" rule is defined then we use "left"/"right" rules.
          //
          // In all cases, the result of the resolution should clear the "start"/"end" rules to leave
          // only the "left"/"right" rules at the end.
          private void resolveRules(int layoutDirection) {
              final boolean isLayoutRtl = (layoutDirection == View.LAYOUT_DIRECTION_RTL);
  
              // Reset to initial state
              System.arraycopy(mInitialRules, LEFT_OF, mRules, LEFT_OF, VERB_COUNT);
  
              // Apply rules depending on direction and if we are in RTL compatibility mode
              if (mIsRtlCompatibilityMode) {
                  if (mRules[ALIGN_START] != 0) {
                      if (mRules[ALIGN_LEFT] == 0) {
                          // "left" rule is not defined but "start" rule is: use the "start" rule as
                          // the "left" rule
                          mRules[ALIGN_LEFT] = mRules[ALIGN_START];
                      }
                      mRules[ALIGN_START] = 0;
                  }
  
                  if (mRules[ALIGN_END] != 0) {
                      if (mRules[ALIGN_RIGHT] == 0) {
                          // "right" rule is not defined but "end" rule is: use the "end" rule as the
                          // "right" rule
                          mRules[ALIGN_RIGHT] = mRules[ALIGN_END];
                      }
                      mRules[ALIGN_END] = 0;
                  }
  
                  if (mRules[START_OF] != 0) {
                      if (mRules[LEFT_OF] == 0) {
                          // "left" rule is not defined but "start" rule is: use the "start" rule as
                          // the "left" rule
                          mRules[LEFT_OF] = mRules[START_OF];
                      }
                      mRules[START_OF] = 0;
                  }
  
                  if (mRules[END_OF] != 0) {
                      if (mRules[RIGHT_OF] == 0) {
                          // "right" rule is not defined but "end" rule is: use the "end" rule as the
                          // "right" rule
                          mRules[RIGHT_OF] = mRules[END_OF];
                      }
                      mRules[END_OF] = 0;
                  }
  
                  if (mRules[ALIGN_PARENT_START] != 0) {
                      if (mRules[ALIGN_PARENT_LEFT] == 0) {
                          // "left" rule is not defined but "start" rule is: use the "start" rule as
                          // the "left" rule
                          mRules[ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
                      }
                      mRules[ALIGN_PARENT_START] = 0;
                  }
  
                  if (mRules[ALIGN_PARENT_END] != 0) {
                      if (mRules[ALIGN_PARENT_RIGHT] == 0) {
                          // "right" rule is not defined but "end" rule is: use the "end" rule as the
                          // "right" rule
                          mRules[ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
                      }
                      mRules[ALIGN_PARENT_END] = 0;
                  }
              } else {
                  // JB MR1+ case
                  if ((mRules[ALIGN_START] != 0 || mRules[ALIGN_END] != 0) &&
                          (mRules[ALIGN_LEFT] != 0 || mRules[ALIGN_RIGHT] != 0)) {
                      // "start"/"end" rules take precedence over "left"/"right" rules
                      mRules[ALIGN_LEFT] = 0;
                      mRules[ALIGN_RIGHT] = 0;
                  }
                  if (mRules[ALIGN_START] != 0) {
                      // "start" rule resolved to "left" or "right" depending on the direction
                      mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START];
                      mRules[ALIGN_START] = 0;
                  }
                  if (mRules[ALIGN_END] != 0) {
                      // "end" rule resolved to "left" or "right" depending on the direction
                      mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END];
                      mRules[ALIGN_END] = 0;
                  }
  
                  if ((mRules[START_OF] != 0 || mRules[END_OF] != 0) &&
                          (mRules[LEFT_OF] != 0 || mRules[RIGHT_OF] != 0)) {
                      // "start"/"end" rules take precedence over "left"/"right" rules
                      mRules[LEFT_OF] = 0;
                      mRules[RIGHT_OF] = 0;
                  }
                  if (mRules[START_OF] != 0) {
                      // "start" rule resolved to "left" or "right" depending on the direction
                      mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF];
                      mRules[START_OF] = 0;
                  }
                  if (mRules[END_OF] != 0) {
                      // "end" rule resolved to "left" or "right" depending on the direction
                      mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF];
                      mRules[END_OF] = 0;
                  }
  
                  if ((mRules[ALIGN_PARENT_START] != 0 || mRules[ALIGN_PARENT_END] != 0) &&
                          (mRules[ALIGN_PARENT_LEFT] != 0 || mRules[ALIGN_PARENT_RIGHT] != 0)) {
                      // "start"/"end" rules take precedence over "left"/"right" rules
                      mRules[ALIGN_PARENT_LEFT] = 0;
                      mRules[ALIGN_PARENT_RIGHT] = 0;
                  }
                  if (mRules[ALIGN_PARENT_START] != 0) {
                      // "start" rule resolved to "left" or "right" depending on the direction
                      mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
                      mRules[ALIGN_PARENT_START] = 0;
                  }
                  if (mRules[ALIGN_PARENT_END] != 0) {
                      // "end" rule resolved to "left" or "right" depending on the direction
                      mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
                      mRules[ALIGN_PARENT_END] = 0;
                  }
              }
  
              mRulesChanged = false;
              mNeedsLayoutResolution = false;
          }
  
          /**
           * Retrieves a complete list of all supported rules, where the index is the rule
           * verb, and the element value is the value specified, or "false" if it was never
           * set. If there are relative rules defined (*_START / *_END), they will be resolved
           * depending on the layout direction.
           *
           * @param layoutDirection the direction of the layout.
           *                        Should be either {@link View#LAYOUT_DIRECTION_LTR}
           *                        or {@link View#LAYOUT_DIRECTION_RTL}
           * @return the supported rules
           * @see #addRule(int, int)
           *
           * @hide
           */
          public int[] getRules(int layoutDirection) {
              resolveLayoutDirection(layoutDirection);
              return mRules;
          }
  
          /**
           * Retrieves a complete list of all supported rules, where the index is the rule
           * verb, and the element value is the value specified, or "false" if it was never
           * set. There will be no resolution of relative rules done.
           *
           * @return the supported rules
           * @see #addRule(int, int)
           */
          public int[] getRules() {
              return mRules;
          }
  
          /**
           * This will be called by {@link android.view.View#requestLayout()} to
           * resolve layout parameters that are relative to the layout direction.
           * <p>
           * After this method is called, any rules using layout-relative verbs
           * (ex. {@link #START_OF}) previously added via {@link #addRule(int)}
           * may only be accessed via their resolved absolute verbs (ex.
           * {@link #LEFT_OF}).
           */
          @Override
          public void resolveLayoutDirection(int layoutDirection) {
              if (shouldResolveLayoutDirection(layoutDirection)) {
                  resolveRules(layoutDirection);
              }
  
              // This will set the layout direction.
              super.resolveLayoutDirection(layoutDirection);
          }
  
          private boolean shouldResolveLayoutDirection(int layoutDirection) {
              return (mNeedsLayoutResolution || hasRelativeRules())
                      && (mRulesChanged || layoutDirection != getLayoutDirection());
          }
  
          /** @hide */
          @Override
          protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
              super.encodeProperties(encoder);
              encoder.addProperty("layout:alignWithParent", alignWithParent);
          }
  
          /** @hide */
          public static final class InspectionCompanion
                  implements android.view.inspector.InspectionCompanion<LayoutParams> {
              private boolean mPropertiesMapped;
  
              private int mAboveId;
              private int mAlignBaselineId;
              private int mAlignBottomId;
              private int mAlignEndId;
              private int mAlignLeftId;
              private int mAlignParentBottomId;
              private int mAlignParentEndId;
              private int mAlignParentLeftId;
              private int mAlignParentRightId;
              private int mAlignParentStartId;
              private int mAlignParentTopId;
              private int mAlignRightId;
              private int mAlignStartId;
              private int mAlignTopId;
              private int mAlignWithParentIfMissingId;
              private int mBelowId;
              private int mCenterHorizontalId;
              private int mCenterInParentId;
              private int mCenterVerticalId;
              private int mToEndOfId;
              private int mToLeftOfId;
              private int mToRightOfId;
              private int mToStartOfId;
  
              @Override
              public void mapProperties(@NonNull PropertyMapper propertyMapper) {
                  mPropertiesMapped = true;
  
                  mAboveId = propertyMapper.mapResourceId("layout_above", R.attr.layout_above);
  
                  mAlignBaselineId = propertyMapper.mapResourceId(
                          "layout_alignBaseline", R.attr.layout_alignBaseline);
  
                  mAlignBottomId = propertyMapper.mapResourceId(
                          "layout_alignBottom", R.attr.layout_alignBottom);
  
                  mAlignEndId = propertyMapper.mapResourceId(
                          "layout_alignEnd", R.attr.layout_alignEnd);
  
                  mAlignLeftId = propertyMapper.mapResourceId(
                          "layout_alignLeft", R.attr.layout_alignLeft);
  
                  mAlignParentBottomId = propertyMapper.mapBoolean(
                          "layout_alignParentBottom", R.attr.layout_alignParentBottom);
  
                  mAlignParentEndId = propertyMapper.mapBoolean(
                          "layout_alignParentEnd", R.attr.layout_alignParentEnd);
  
                  mAlignParentLeftId = propertyMapper.mapBoolean(
                          "layout_alignParentLeft", R.attr.layout_alignParentLeft);
  
                  mAlignParentRightId = propertyMapper.mapBoolean(
                          "layout_alignParentRight", R.attr.layout_alignParentRight);
  
                  mAlignParentStartId = propertyMapper.mapBoolean(
                          "layout_alignParentStart", R.attr.layout_alignParentStart);
  
                  mAlignParentTopId = propertyMapper.mapBoolean(
                          "layout_alignParentTop", R.attr.layout_alignParentTop);
  
                  mAlignRightId = propertyMapper.mapResourceId(
                          "layout_alignRight", R.attr.layout_alignRight);
  
                  mAlignStartId = propertyMapper.mapResourceId(
                          "layout_alignStart", R.attr.layout_alignStart);
  
                  mAlignTopId = propertyMapper.mapResourceId(
                          "layout_alignTop", R.attr.layout_alignTop);
  
                  mAlignWithParentIfMissingId = propertyMapper.mapBoolean(
                          "layout_alignWithParentIfMissing",
                          R.attr.layout_alignWithParentIfMissing);
  
                  mBelowId = propertyMapper.mapResourceId("layout_below", R.attr.layout_below);
  
                  mCenterHorizontalId = propertyMapper.mapBoolean(
                          "layout_centerHorizontal", R.attr.layout_centerHorizontal);
  
                  mCenterInParentId = propertyMapper.mapBoolean(
                          "layout_centerInParent", R.attr.layout_centerInParent);
  
                  mCenterVerticalId = propertyMapper.mapBoolean(
                          "layout_centerVertical", R.attr.layout_centerVertical);
  
                  mToEndOfId = propertyMapper.mapResourceId(
                          "layout_toEndOf", R.attr.layout_toEndOf);
  
                  mToLeftOfId = propertyMapper.mapResourceId(
                          "layout_toLeftOf", R.attr.layout_toLeftOf);
  
                  mToRightOfId = propertyMapper.mapResourceId(
                          "layout_toRightOf", R.attr.layout_toRightOf);
  
                  mToStartOfId = propertyMapper.mapResourceId(
                          "layout_toStartOf", R.attr.layout_toStartOf);
              }
  
              @Override
              public void readProperties(
                      @NonNull LayoutParams node,
                      @NonNull PropertyReader propertyReader
              ) {
                  if (!mPropertiesMapped) {
                      throw new UninitializedPropertyMapException();
                  }
  
                  final int[] rules = node.getRules();
  
                  propertyReader.readResourceId(mAboveId, rules[ABOVE]);
                  propertyReader.readResourceId(mAlignBaselineId, rules[ALIGN_BASELINE]);
                  propertyReader.readResourceId(mAlignBottomId, rules[ALIGN_BOTTOM]);
                  propertyReader.readResourceId(mAlignEndId, rules[ALIGN_END]);
                  propertyReader.readResourceId(mAlignLeftId, rules[ALIGN_LEFT]);
                  propertyReader.readBoolean(
                          mAlignParentBottomId, rules[ALIGN_PARENT_BOTTOM] == TRUE);
                  propertyReader.readBoolean(mAlignParentEndId, rules[ALIGN_PARENT_END] == TRUE);
                  propertyReader.readBoolean(mAlignParentLeftId, rules[ALIGN_PARENT_LEFT] == TRUE);
                  propertyReader.readBoolean(mAlignParentRightId, rules[ALIGN_PARENT_RIGHT] == TRUE);
                  propertyReader.readBoolean(mAlignParentStartId, rules[ALIGN_PARENT_START] == TRUE);
                  propertyReader.readBoolean(mAlignParentTopId, rules[ALIGN_PARENT_TOP] == TRUE);
                  propertyReader.readResourceId(mAlignRightId, rules[ALIGN_RIGHT]);
                  propertyReader.readResourceId(mAlignStartId, rules[ALIGN_START]);
                  propertyReader.readResourceId(mAlignTopId, rules[ALIGN_TOP]);
                  propertyReader.readBoolean(mAlignWithParentIfMissingId, node.alignWithParent);
                  propertyReader.readResourceId(mBelowId, rules[BELOW]);
                  propertyReader.readBoolean(mCenterHorizontalId, rules[CENTER_HORIZONTAL] == TRUE);
                  propertyReader.readBoolean(mCenterInParentId, rules[CENTER_IN_PARENT] == TRUE);
                  propertyReader.readBoolean(mCenterVerticalId, rules[CENTER_VERTICAL] == TRUE);
                  propertyReader.readResourceId(mToEndOfId, rules[END_OF]);
                  propertyReader.readResourceId(mToLeftOfId, rules[LEFT_OF]);
                  propertyReader.readResourceId(mToRightOfId, rules[RIGHT_OF]);
                  propertyReader.readResourceId(mToStartOfId, rules[START_OF]);
              }
          }
      }
  
      private static class DependencyGraph {
          /**
           * List of all views in the graph.
           */
          private ArrayList<Node> mNodes = new ArrayList<Node>();
  
          /**
           * List of nodes in the graph. Each node is identified by its
           * view id (see View#getId()).
           */
          private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
  
          /**
           * Temporary data structure used to build the list of roots
           * for this graph.
           */
          private ArrayDeque<Node> mRoots = new ArrayDeque<Node>();
  
          /**
           * Clears the graph.
           */
          void clear() {
              final ArrayList<Node> nodes = mNodes;
              final int count = nodes.size();
  
              for (int i = 0; i < count; i++) {
                  nodes.get(i).release();
              }
              nodes.clear();
  
              mKeyNodes.clear();
              mRoots.clear();
          }
  
          /**
           * Adds a view to the graph.
           *
           * @param view The view to be added as a node to the graph.
           */
          void add(View view) {
              final int id = view.getId();
              final Node node = Node.acquire(view);
  
              if (id != View.NO_ID) {
                  mKeyNodes.put(id, node);
              }
  
              mNodes.add(node);
          }
  
          /**
           * Builds a sorted list of views. The sorting order depends on the dependencies
           * between the view. For instance, if view C needs view A to be processed first
           * and view A needs view B to be processed first, the dependency graph
           * is: B -> A -> C. The sorted array will contain views B, A and C in this order.
           *
           * @param sorted The sorted list of views. The length of this array must
           *        be equal to getChildCount().
           * @param rules The list of rules to take into account.
           */
          void getSortedViews(View[] sorted, int... rules) {
              final ArrayDeque<Node> roots = findRoots(rules);
              int index = 0;
  
              Node node;
              while ((node = roots.pollLast()) != null) {
                  final View view = node.view;
                  final int key = view.getId();
  
                  sorted[index++] = view;
  
                  final ArrayMap<Node, DependencyGraph> dependents = node.dependents;
                  final int count = dependents.size();
                  for (int i = 0; i < count; i++) {
                      final Node dependent = dependents.keyAt(i);
                      final SparseArray<Node> dependencies = dependent.dependencies;
  
                      dependencies.remove(key);
                      if (dependencies.size() == 0) {
                          roots.add(dependent);
                      }
                  }
              }
  
              if (index < sorted.length) {
                  throw new IllegalStateException("Circular dependencies cannot exist"
                          + " in RelativeLayout");
              }
          }
  
          /**
           * Finds the roots of the graph. A root is a node with no dependency and
           * with [0..n] dependents.
           *
           * @param rulesFilter The list of rules to consider when building the
           *        dependencies
           *
           * @return A list of node, each being a root of the graph
           */
          private ArrayDeque<Node> findRoots(int[] rulesFilter) {
              final SparseArray<Node> keyNodes = mKeyNodes;
              final ArrayList<Node> nodes = mNodes;
              final int count = nodes.size();
  
              // Find roots can be invoked several times, so make sure to clear
              // all dependents and dependencies before running the algorithm
              for (int i = 0; i < count; i++) {
                  final Node node = nodes.get(i);
                  node.dependents.clear();
                  node.dependencies.clear();
              }
  
              // Builds up the dependents and dependencies for each node of the graph
              for (int i = 0; i < count; i++) {
                  final Node node = nodes.get(i);
  
                  final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
                  final int[] rules = layoutParams.mRules;
                  final int rulesCount = rulesFilter.length;
  
                  // Look only the the rules passed in parameter, this way we build only the
                  // dependencies for a specific set of rules
                  for (int j = 0; j < rulesCount; j++) {
                      final int rule = rules[rulesFilter[j]];
                      if (rule > 0 || ResourceId.isValid(rule)) {
                          // The node this node depends on
                          final Node dependency = keyNodes.get(rule);
                          // Skip unknowns and self dependencies
                          if (dependency == null || dependency == node) {
                              continue;
                          }
                          // Add the current node as a dependent
                          dependency.dependents.put(node, this);
                          // Add a dependency to the current node
                          node.dependencies.put(rule, dependency);
                      }
                  }
              }
  
              final ArrayDeque<Node> roots = mRoots;
              roots.clear();
  
              // Finds all the roots in the graph: all nodes with no dependencies
              for (int i = 0; i < count; i++) {
                  final Node node = nodes.get(i);
                  if (node.dependencies.size() == 0) roots.addLast(node);
              }
  
              return roots;
          }
  
          /**
           * A node in the dependency graph. A node is a view, its list of dependencies
           * and its list of dependents.
           *
           * A node with no dependent is considered a root of the graph.
           */
          static class Node {
              /**
               * The view representing this node in the layout.
               */
              View view;
  
              /**
               * The list of dependents for this node; a dependent is a node
               * that needs this node to be processed first.
               */
              final ArrayMap<Node, DependencyGraph> dependents =
                      new ArrayMap<Node, DependencyGraph>();
  
              /**
               * The list of dependencies for this node.
               */
              final SparseArray<Node> dependencies = new SparseArray<Node>();
  
              /*
               * START POOL IMPLEMENTATION
               */
              // The pool is static, so all nodes instances are shared across
              // activities, that's why we give it a rather high limit
              private static final int POOL_LIMIT = 100;
              private static final SynchronizedPool<Node> sPool =
                      new SynchronizedPool<Node>(POOL_LIMIT);
  
              static Node acquire(View view) {
                  Node node = sPool.acquire();
                  if (node == null) {
                      node = new Node();
                  }
                  node.view = view;
                  return node;
              }
  
              void release() {
                  view = null;
                  dependents.clear();
                  dependencies.clear();
  
                  sPool.release(this);
              }
              /*
               * END POOL IMPLEMENTATION
               */
          }
      }
  }
  