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 android.annotation.AttrRes;
  import android.annotation.NonNull;
  import android.annotation.Nullable;
  import android.annotation.StyleRes;
  import android.annotation.UnsupportedAppUsage;
  import android.content.Context;
  import android.content.res.TypedArray;
  import android.graphics.Rect;
  import android.graphics.drawable.Drawable;
  import android.util.AttributeSet;
  import android.view.Gravity;
  import android.view.View;
  import android.view.ViewDebug;
  import android.view.ViewGroup;
  import android.view.ViewHierarchyEncoder;
  import android.view.inspector.InspectableProperty;
  import android.widget.RemoteViews.RemoteView;
  
  import com.android.internal.R;
  
  import java.util.ArrayList;
  
  /**
   * FrameLayout is designed to block out an area on the screen to display
   * a single item. Generally, FrameLayout should be used to hold a single child view, because it can
   * be difficult to organize child views in a way that's scalable to different screen sizes without
   * the children overlapping each other. You can, however, add multiple children to a FrameLayout
   * and control their position within the FrameLayout by assigning gravity to each child, using the
   * <a href="FrameLayout.LayoutParams.html#attr_android:layout_gravity">{@code
   * android:layout_gravity}</a> attribute.
   * <p>Child views are drawn in a stack, with the most recently added child on top.
   * The size of the FrameLayout is the size of its largest child (plus padding), visible
   * or not (if the FrameLayout's parent permits). Views that are {@link android.view.View#GONE} are
   * used for sizing
   * only if {@link #setMeasureAllChildren(boolean) setConsiderGoneChildrenWhenMeasuring()}
   * is set to true.
   *
   * @attr ref android.R.styleable#FrameLayout_measureAllChildren
   */
  @RemoteView
  public class FrameLayout extends ViewGroup {
      private static final int DEFAULT_CHILD_GRAVITY = Gravity.TOP | Gravity.START;
  
      @ViewDebug.ExportedProperty(category = "measurement")
      @UnsupportedAppUsage
      boolean mMeasureAllChildren = false;
  
      @ViewDebug.ExportedProperty(category = "padding")
      @UnsupportedAppUsage
      private int mForegroundPaddingLeft = 0;
  
      @ViewDebug.ExportedProperty(category = "padding")
      @UnsupportedAppUsage
      private int mForegroundPaddingTop = 0;
  
      @ViewDebug.ExportedProperty(category = "padding")
      @UnsupportedAppUsage
      private int mForegroundPaddingRight = 0;
  
      @ViewDebug.ExportedProperty(category = "padding")
      @UnsupportedAppUsage
      private int mForegroundPaddingBottom = 0;
  
      private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
  
      public FrameLayout(@NonNull Context context) {
          super(context);
      }
  
      public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
          this(context, attrs, 0);
      }
  
      public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs,
              @AttrRes int defStyleAttr) {
          this(context, attrs, defStyleAttr, 0);
      }
  
      public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs,
              @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
          super(context, attrs, defStyleAttr, defStyleRes);
  
          final TypedArray a = context.obtainStyledAttributes(
                  attrs, R.styleable.FrameLayout, defStyleAttr, defStyleRes);
          saveAttributeDataForStyleable(context, R.styleable.FrameLayout,
                  attrs, a, defStyleAttr, defStyleRes);
  
          if (a.getBoolean(R.styleable.FrameLayout_measureAllChildren, false)) {
              setMeasureAllChildren(true);
          }
  
          a.recycle();
      }
  
      /**
       * Describes how the foreground is positioned. Defaults to START and TOP.
       *
       * @param foregroundGravity See {@link android.view.Gravity}
       *
       * @see #getForegroundGravity()
       *
       * @attr ref android.R.styleable#View_foregroundGravity
       */
      @android.view.RemotableViewMethod
      public void setForegroundGravity(int foregroundGravity) {
          if (getForegroundGravity() != foregroundGravity) {
              super.setForegroundGravity(foregroundGravity);
  
              // calling get* again here because the set above may apply default constraints
              final Drawable foreground = getForeground();
              if (getForegroundGravity() == Gravity.FILL && foreground != null) {
                  Rect padding = new Rect();
                  if (foreground.getPadding(padding)) {
                      mForegroundPaddingLeft = padding.left;
                      mForegroundPaddingTop = padding.top;
                      mForegroundPaddingRight = padding.right;
                      mForegroundPaddingBottom = padding.bottom;
                  }
              } else {
                  mForegroundPaddingLeft = 0;
                  mForegroundPaddingTop = 0;
                  mForegroundPaddingRight = 0;
                  mForegroundPaddingBottom = 0;
              }
  
              requestLayout();
          }
      }
  
      /**
       * Returns a set of layout parameters with a width of
       * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT},
       * and a height of {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}.
       */
      @Override
      protected LayoutParams generateDefaultLayoutParams() {
          return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
      }
  
      int getPaddingLeftWithForeground() {
          return isForegroundInsidePadding() ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
              mPaddingLeft + mForegroundPaddingLeft;
      }
  
      int getPaddingRightWithForeground() {
          return isForegroundInsidePadding() ? Math.max(mPaddingRight, mForegroundPaddingRight) :
              mPaddingRight + mForegroundPaddingRight;
      }
  
      private int getPaddingTopWithForeground() {
          return isForegroundInsidePadding() ? Math.max(mPaddingTop, mForegroundPaddingTop) :
              mPaddingTop + mForegroundPaddingTop;
      }
  
      private int getPaddingBottomWithForeground() {
          return isForegroundInsidePadding() ? Math.max(mPaddingBottom, mForegroundPaddingBottom) :
              mPaddingBottom + mForegroundPaddingBottom;
      }
  
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          int count = getChildCount();
  
          final boolean measureMatchParentChildren =
                  MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
                  MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
          mMatchParentChildren.clear();
  
          int maxHeight = 0;
          int maxWidth = 0;
          int childState = 0;
  
          for (int i = 0; i < count; i++) {
              final View child = getChildAt(i);
              if (mMeasureAllChildren || child.getVisibility() != GONE) {
                  measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
                  final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                  maxWidth = Math.max(maxWidth,
                          child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                  maxHeight = Math.max(maxHeight,
                          child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
                  childState = combineMeasuredStates(childState, child.getMeasuredState());
                  if (measureMatchParentChildren) {
                      if (lp.width == LayoutParams.MATCH_PARENT ||
                              lp.height == LayoutParams.MATCH_PARENT) {
                          mMatchParentChildren.add(child);
                      }
                  }
              }
          }
  
          // Account for padding too
          maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
          maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
  
          // Check against our minimum height and width
          maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
          maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
  
          // Check against our foreground's minimum height and width
          final Drawable drawable = getForeground();
          if (drawable != null) {
              maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
              maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
          }
  
          setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                  resolveSizeAndState(maxHeight, heightMeasureSpec,
                          childState << MEASURED_HEIGHT_STATE_SHIFT));
  
          count = mMatchParentChildren.size();
          if (count > 1) {
              for (int i = 0; i < count; i++) {
                  final View child = mMatchParentChildren.get(i);
                  final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
  
                  final int childWidthMeasureSpec;
                  if (lp.width == LayoutParams.MATCH_PARENT) {
                      final int width = Math.max(0, getMeasuredWidth()
                              - getPaddingLeftWithForeground() - getPaddingRightWithForeground()
                              - lp.leftMargin - lp.rightMargin);
                      childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                              width, MeasureSpec.EXACTLY);
                  } else {
                      childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                              getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
                              lp.leftMargin + lp.rightMargin,
                              lp.width);
                  }
  
                  final int childHeightMeasureSpec;
                  if (lp.height == LayoutParams.MATCH_PARENT) {
                      final int height = Math.max(0, getMeasuredHeight()
                              - getPaddingTopWithForeground() - getPaddingBottomWithForeground()
                              - lp.topMargin - lp.bottomMargin);
                      childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                              height, MeasureSpec.EXACTLY);
                  } else {
                      childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                              getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
                              lp.topMargin + lp.bottomMargin,
                              lp.height);
                  }
  
                  child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
              }
          }
      }
  
      @Override
      protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
          layoutChildren(left, top, right, bottom, false /* no force left gravity */);
      }
  
      void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
          final int count = getChildCount();
  
          final int parentLeft = getPaddingLeftWithForeground();
          final int parentRight = right - left - getPaddingRightWithForeground();
  
          final int parentTop = getPaddingTopWithForeground();
          final int parentBottom = bottom - top - getPaddingBottomWithForeground();
  
          for (int i = 0; i < count; i++) {
              final View child = getChildAt(i);
              if (child.getVisibility() != GONE) {
                  final LayoutParams lp = (LayoutParams) child.getLayoutParams();
  
                  final int width = child.getMeasuredWidth();
                  final int height = child.getMeasuredHeight();
  
                  int childLeft;
                  int childTop;
  
                  int gravity = lp.gravity;
                  if (gravity == -1) {
                      gravity = DEFAULT_CHILD_GRAVITY;
                  }
  
                  final int layoutDirection = getLayoutDirection();
                  final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
                  final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
  
                  switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                      case Gravity.CENTER_HORIZONTAL:
                          childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
                          lp.leftMargin - lp.rightMargin;
                          break;
                      case Gravity.RIGHT:
                          if (!forceLeftGravity) {
                              childLeft = parentRight - width - lp.rightMargin;
                              break;
                          }
                      case Gravity.LEFT:
                      default:
                          childLeft = parentLeft + lp.leftMargin;
                  }
  
                  switch (verticalGravity) {
                      case Gravity.TOP:
                          childTop = parentTop + lp.topMargin;
                          break;
                      case Gravity.CENTER_VERTICAL:
                          childTop = parentTop + (parentBottom - parentTop - height) / 2 +
                          lp.topMargin - lp.bottomMargin;
                          break;
                      case Gravity.BOTTOM:
                          childTop = parentBottom - height - lp.bottomMargin;
                          break;
                      default:
                          childTop = parentTop + lp.topMargin;
                  }
  
                  child.layout(childLeft, childTop, childLeft + width, childTop + height);
              }
          }
      }
  
      /**
       * Sets whether to consider all children, or just those in
       * the VISIBLE or INVISIBLE state, when measuring. Defaults to false.
       *
       * @param measureAll true to consider children marked GONE, false otherwise.
       * Default value is false.
       *
       * @attr ref android.R.styleable#FrameLayout_measureAllChildren
       */
      @android.view.RemotableViewMethod
      public void setMeasureAllChildren(boolean measureAll) {
          mMeasureAllChildren = measureAll;
      }
  
      /**
       * Determines whether all children, or just those in the VISIBLE or
       * INVISIBLE state, are considered when measuring.
       *
       * @return Whether all children are considered when measuring.
       *
       * @deprecated This method is deprecated in favor of
       * {@link #getMeasureAllChildren() getMeasureAllChildren()}, which was
       * renamed for consistency with
       * {@link #setMeasureAllChildren(boolean) setMeasureAllChildren()}.
       */
      @Deprecated
      public boolean getConsiderGoneChildrenWhenMeasuring() {
          return getMeasureAllChildren();
      }
  
      /**
       * Determines whether all children, or just those in the VISIBLE or
       * INVISIBLE state, are considered when measuring.
       *
       * @return Whether all children are considered when measuring.
       */
      @InspectableProperty
      public boolean getMeasureAllChildren() {
          return mMeasureAllChildren;
      }
  
      @Override
      public LayoutParams generateLayoutParams(AttributeSet attrs) {
          return new FrameLayout.LayoutParams(getContext(), attrs);
      }
  
      @Override
      public boolean shouldDelayChildPressedState() {
          return false;
      }
  
      @Override
      protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
          return p instanceof 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);
      }
  
      @Override
      public CharSequence getAccessibilityClassName() {
          return FrameLayout.class.getName();
      }
  
      /** @hide */
      @Override
      protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
          super.encodeProperties(encoder);
  
          encoder.addProperty("measurement:measureAllChildren", mMeasureAllChildren);
          encoder.addProperty("padding:foregroundPaddingLeft", mForegroundPaddingLeft);
          encoder.addProperty("padding:foregroundPaddingTop", mForegroundPaddingTop);
          encoder.addProperty("padding:foregroundPaddingRight", mForegroundPaddingRight);
          encoder.addProperty("padding:foregroundPaddingBottom", mForegroundPaddingBottom);
      }
  
      /**
       * Per-child layout information for layouts that support margins.
       * See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes}
       * for a list of all child view attributes that this class supports.
       *
       * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
       */
      public static class LayoutParams extends MarginLayoutParams {
          /**
           * Value for {@link #gravity} indicating that a gravity has not been
           * explicitly specified.
           */
          public static final int UNSPECIFIED_GRAVITY = -1;
  
          /**
           * The gravity to apply with the View to which these layout parameters
           * are associated.
           * <p>
           * The default value is {@link #UNSPECIFIED_GRAVITY}, which is treated
           * by FrameLayout as {@code Gravity.TOP | Gravity.START}.
           *
           * @see android.view.Gravity
           * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
           */
          @InspectableProperty(
                  name = "layout_gravity",
                  valueType = InspectableProperty.ValueType.GRAVITY)
          public int gravity = UNSPECIFIED_GRAVITY;
  
          public LayoutParams(@NonNull Context c, @Nullable AttributeSet attrs) {
              super(c, attrs);
  
              final TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.FrameLayout_Layout);
              gravity = a.getInt(R.styleable.FrameLayout_Layout_layout_gravity, UNSPECIFIED_GRAVITY);
              a.recycle();
          }
  
          public LayoutParams(int width, int height) {
              super(width, height);
          }
  
          /**
           * Creates a new set of layout parameters with the specified width, height
           * and weight.
           *
           * @param width the width, either {@link #MATCH_PARENT},
           *              {@link #WRAP_CONTENT} or a fixed size in pixels
           * @param height the height, either {@link #MATCH_PARENT},
           *               {@link #WRAP_CONTENT} or a fixed size in pixels
           * @param gravity the gravity
           *
           * @see android.view.Gravity
           */
          public LayoutParams(int width, int height, int gravity) {
              super(width, height);
              this.gravity = gravity;
          }
  
          public LayoutParams(@NonNull ViewGroup.LayoutParams source) {
              super(source);
          }
  
          public LayoutParams(@NonNull ViewGroup.MarginLayoutParams source) {
              super(source);
          }
  
          /**
           * Copy constructor. Clones the width, height, margin values, and
           * gravity of the source.
           *
           * @param source The layout params to copy from.
           */
          public LayoutParams(@NonNull LayoutParams source) {
              super(source);
  
              this.gravity = source.gravity;
          }
      }
  }
  