content: /*
   * Copyright (C) 2019 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 androidx.constraintlayout.helper.widget;
  
  import android.annotation.SuppressLint;
  import android.content.Context;
  import android.content.res.TypedArray;
  import android.os.Build;
  import android.util.AttributeSet;
  import android.util.SparseArray;
  
  import androidx.constraintlayout.core.widgets.ConstraintWidget;
  import androidx.constraintlayout.core.widgets.HelperWidget;
  import androidx.constraintlayout.widget.ConstraintLayout;
  import androidx.constraintlayout.widget.ConstraintSet;
  import androidx.constraintlayout.widget.R;
  import androidx.constraintlayout.widget.VirtualLayout;
  
  /**
   *
   * Flow VirtualLayout. <b>Added in 2.0</b>
   *
   * Allows positioning of referenced widgets horizontally or vertically, similar to a Chain.
   *
   * The elements referenced are indicated via constraint_referenced_ids, as with other
   * ConstraintHelper implementations.
   *
   * Those referenced widgets are then laid out by the Flow virtual layout in three possible ways:
   * <ul>
   *     <li><a href="#wrap_none">wrap none</a> : simply create a chain out of the
   *     referenced elements</li>
   *     <li><a href="#wrap_chain">wrap chain</a> : create multiple chains (one after the other)
   *     if the referenced elements do not fit</li>
   *     <li><a href="#wrap_aligned">wrap aligned</a> : similar to wrap chain, but will align the
   *     elements by creating rows and columns</li>
   * </ul>
   *
   * As VirtualLayouts are ConstraintHelpers, they are normal views; you can thus treat them as such,
   * and setting up constraints on them (position, dimension) or some view attributes
   * (background, padding) will work. The main difference between VirtualLayouts and ViewGroups is
   * that:
   * <ul>
   *     <li>VirtualLayout keep the hierarchy flat</li>
   *     <li>Other views can thus reference / constrain to not only the VirtualLayout, but also
   *     the views laid out by the VirtualLayout</li>
   *     <li>VirtualLayout allow on the fly behavior modifications
   *     (e.g. for Flow, changing the orientation)</li>
   * </ul>
   *
   * <h4 id="wrap_none">flow_wrapMode = "none"</h4>
   *
   * This will simply create an horizontal or vertical chain out of the referenced widgets.
   * This is the default behavior of Flow.
   *
   * XML attributes that are allowed in this mode:
   *
   * <ul>
   *     <li>flow_horizontalStyle = "spread|spread_inside|packed"</li>
   *     <li>flow_verticalStyle = "spread|spread_inside|packed"</li>
   *     <li>flow_horizontalBias = "<i>float</i>"</li>
   *     <li>flow_verticalBias = "<i>float</i>"</li>
   *     <li>flow_horizontalGap = "<i>dimension</i>"</li>
   *     <li>flow_verticalGap = "<i>dimension</i>"</li>
   *     <li>flow_horizontalAlign = "start|end"</li>
   *     <li>flow_verticalAlign = "top|bottom|center|baseline</li>
   * </ul>
   *
   * While the elements are laid out as a chain in the orientation defined, the way they are laid
   * out in the other dimension is controlled
   * by <i>flow_horizontalAlign</i> and <i>flow_verticalAlign</i> attributes.
   *
   * <h4 id="wrap_chain">flow_wrapMode = "chain"</h4>
   *
   * Similar to wrap none in terms of creating chains, but if the referenced widgets do not fit the
   * horizontal or vertical dimension (depending
   * on the orientation picked), they will wrap around to the next line / column.
   *
   * XML attributes are the same same as in wrap_none, with the addition of attributes specifying
   * chain style and chain bias applied to the first chain. This way, it is possible to specify
   * different chain behavior between the first chain and the rest of the chains eventually created.
   *
   * <ul>
   *     <li>flow_firstHorizontalStyle = "spread|spread_inside|packed"</li>
   *     <li>flow_firstVerticalStyle = "spread|spread_inside|packed"</li>
   *     <li>flow_firstHorizontalBias = "<i>float</i>"</li>
   *     <li>flow_firstVerticalBias = "<i>float</i>"</li>
   * </ul>
   *
   * One last important attribute is <i>flow_maxElementsWrap</i>, which specify the number
   * of elements before wrapping, regardless if they
   * fit or not in the available space.
   *
   * <h4 id="wrap_aligned">flow_wrapMode = "aligned"</h4>
   *
   * Same XML attributes as for WRAP_CHAIN, with the difference that the elements are going to be
   * laid out in a set of rows and columns instead of chains.
   * The attribute specifying chains style and bias are thus not going to be applied.
   */
  public class Flow extends VirtualLayout {
      private static final String TAG = "Flow";
  
      private androidx.constraintlayout.core.widgets.Flow mFlow;
  
      public static final int HORIZONTAL = androidx.constraintlayout.core.widgets.Flow.HORIZONTAL;
      public static final int VERTICAL = androidx.constraintlayout.core.widgets.Flow.VERTICAL;
      public static final int WRAP_NONE = androidx.constraintlayout.core.widgets.Flow.WRAP_NONE;
      public static final int WRAP_CHAIN = androidx.constraintlayout.core.widgets.Flow.WRAP_CHAIN;
      public static final int WRAP_ALIGNED = androidx.constraintlayout.core.widgets.Flow.WRAP_ALIGNED;
  
      public static final int CHAIN_SPREAD = ConstraintWidget.CHAIN_SPREAD;
      public static final int CHAIN_SPREAD_INSIDE = ConstraintWidget.CHAIN_SPREAD_INSIDE;
      public static final int CHAIN_PACKED = ConstraintWidget.CHAIN_PACKED;
  
      public static final int HORIZONTAL_ALIGN_START =
              androidx.constraintlayout.core.widgets.Flow.HORIZONTAL_ALIGN_START;
      public static final int HORIZONTAL_ALIGN_END =
              androidx.constraintlayout.core.widgets.Flow.HORIZONTAL_ALIGN_END;
      public static final int HORIZONTAL_ALIGN_CENTER =
              androidx.constraintlayout.core.widgets.Flow.HORIZONTAL_ALIGN_CENTER;
  
      public static final int VERTICAL_ALIGN_TOP =
              androidx.constraintlayout.core.widgets.Flow.VERTICAL_ALIGN_TOP;
      public static final int VERTICAL_ALIGN_BOTTOM =
              androidx.constraintlayout.core.widgets.Flow.VERTICAL_ALIGN_BOTTOM;
      public static final int VERTICAL_ALIGN_CENTER =
              androidx.constraintlayout.core.widgets.Flow.VERTICAL_ALIGN_CENTER;
      public static final int VERTICAL_ALIGN_BASELINE =
              androidx.constraintlayout.core.widgets.Flow.VERTICAL_ALIGN_BASELINE;
  
      public Flow(Context context) {
          super(context);
      }
  
      public Flow(Context context, AttributeSet attrs) {
          super(context, attrs);
      }
  
      public Flow(Context context, AttributeSet attrs, int defStyleAttr) {
          super(context, attrs, defStyleAttr);
      }
  
      /**
       *
       *
       * @param widget
       * @param isRtl
       */
      @Override
      public void resolveRtl(ConstraintWidget widget, boolean isRtl) {
          mFlow.applyRtl(isRtl);
      }
  
      @SuppressLint("WrongCall")
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          onMeasure(mFlow, widthMeasureSpec, heightMeasureSpec);
      }
  
      /**
       *
       *
       * @param layout
       * @param widthMeasureSpec
       * @param heightMeasureSpec
       */
      @Override
      public void onMeasure(androidx.constraintlayout.core.widgets.VirtualLayout layout,
                            int widthMeasureSpec,
                            int heightMeasureSpec) {
          int widthMode = MeasureSpec.getMode(widthMeasureSpec);
          int widthSize = MeasureSpec.getSize(widthMeasureSpec);
          int heightMode = MeasureSpec.getMode(heightMeasureSpec);
          int heightSize = MeasureSpec.getSize(heightMeasureSpec);
          if (layout != null) {
              layout.measure(widthMode, widthSize, heightMode, heightSize);
              setMeasuredDimension(layout.getMeasuredWidth(), layout.getMeasuredHeight());
          } else {
              setMeasuredDimension(0, 0);
          }
      }
  
      /**
       *
       *
       * @param constraint
       * @param child
       * @param layoutParams
       * @param mapIdToWidget
       */
      @Override
      public void loadParameters(ConstraintSet.Constraint constraint, HelperWidget child,
                                 ConstraintLayout.LayoutParams layoutParams,
                                 SparseArray<ConstraintWidget> mapIdToWidget) {
          super.loadParameters(constraint, child, layoutParams, mapIdToWidget);
          if (child instanceof androidx.constraintlayout.core.widgets.Flow) {
              androidx.constraintlayout.core.widgets.Flow flow =
                      (androidx.constraintlayout.core.widgets.Flow) child;
              if (layoutParams.orientation != -1) {
                  flow.setOrientation(layoutParams.orientation);
              }
          }
      }
  
      /**
       *
       *
       * @param attrs
       */
      @Override
      protected void init(AttributeSet attrs) {
          super.init(attrs);
          mFlow = new androidx.constraintlayout.core.widgets.Flow();
          if (attrs != null) {
              TypedArray a = getContext().obtainStyledAttributes(attrs,
                      R.styleable.ConstraintLayout_Layout);
              final int n = a.getIndexCount();
              for (int i = 0; i < n; i++) {
                  int attr = a.getIndex(i);
                  if (attr == R.styleable.ConstraintLayout_Layout_android_orientation) {
                      mFlow.setOrientation(a.getInt(attr, 0));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_android_padding) {
                      mFlow.setPadding(a.getDimensionPixelSize(attr, 0));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_android_paddingStart) {
                      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                          mFlow.setPaddingStart(a.getDimensionPixelSize(attr, 0));
                      }
                  } else if (attr == R.styleable.ConstraintLayout_Layout_android_paddingEnd) {
                      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                          mFlow.setPaddingEnd(a.getDimensionPixelSize(attr, 0));
                      }
                  } else if (attr == R.styleable.ConstraintLayout_Layout_android_paddingLeft) {
                      mFlow.setPaddingLeft(a.getDimensionPixelSize(attr, 0));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_android_paddingTop) {
                      mFlow.setPaddingTop(a.getDimensionPixelSize(attr, 0));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_android_paddingRight) {
                      mFlow.setPaddingRight(a.getDimensionPixelSize(attr, 0));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_android_paddingBottom) {
                      mFlow.setPaddingBottom(a.getDimensionPixelSize(attr, 0));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_wrapMode) {
                      mFlow.setWrapMode(a.getInt(attr,
                              androidx.constraintlayout.core.widgets.Flow.WRAP_NONE));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_horizontalStyle) {
                      mFlow.setHorizontalStyle(a.getInt(attr, 0));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_verticalStyle) {
                      mFlow.setVerticalStyle(a.getInt(attr, 0));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_firstHorizontalStyle) {
                      mFlow.setFirstHorizontalStyle(a.getInt(attr, 0));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_lastHorizontalStyle) {
                      mFlow.setLastHorizontalStyle(a.getInt(attr, 0));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_firstVerticalStyle) {
                      mFlow.setFirstVerticalStyle(a.getInt(attr, 0));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_lastVerticalStyle) {
                      mFlow.setLastVerticalStyle(a.getInt(attr, 0));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_horizontalBias) {
                      mFlow.setHorizontalBias(a.getFloat(attr, 0.5f));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_firstHorizontalBias) {
                      mFlow.setFirstHorizontalBias(a.getFloat(attr, 0.5f));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_lastHorizontalBias) {
                      mFlow.setLastHorizontalBias(a.getFloat(attr, 0.5f));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_firstVerticalBias) {
                      mFlow.setFirstVerticalBias(a.getFloat(attr, 0.5f));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_lastVerticalBias) {
                      mFlow.setLastVerticalBias(a.getFloat(attr, 0.5f));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_verticalBias) {
                      mFlow.setVerticalBias(a.getFloat(attr, 0.5f));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_horizontalAlign) {
                      mFlow.setHorizontalAlign(a.getInt(attr,
                              androidx.constraintlayout.core.widgets.Flow.HORIZONTAL_ALIGN_CENTER));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_verticalAlign) {
                      mFlow.setVerticalAlign(a.getInt(attr,
                              androidx.constraintlayout.core.widgets.Flow.VERTICAL_ALIGN_CENTER));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_horizontalGap) {
                      mFlow.setHorizontalGap(a.getDimensionPixelSize(attr, 0));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_verticalGap) {
                      mFlow.setVerticalGap(a.getDimensionPixelSize(attr, 0));
                  } else if (attr == R.styleable.ConstraintLayout_Layout_flow_maxElementsWrap) {
                      mFlow.setMaxElementsWrap(a.getInt(attr, -1));
                  }
              }
              a.recycle();
          }
  
          mHelperWidget = mFlow;
          validateParams();
      }
  
      /**
       * Set the orientation of the layout
       *
       * @param orientation either Flow.HORIZONTAL or FLow.VERTICAL
       */
      public void setOrientation(int orientation) {
          mFlow.setOrientation(orientation);
          requestLayout();
      }
  
      /**
       * Set padding around the content
       *
       * @param padding
       */
      public void setPadding(int padding) {
          mFlow.setPadding(padding);
          requestLayout();
      }
  
      /**
       * Set padding left around the content
       *
       * @param paddingLeft
       */
      public void setPaddingLeft(int paddingLeft) {
          mFlow.setPaddingLeft(paddingLeft);
          requestLayout();
      }
  
      /**
       * Set padding top around the content
       *
       * @param paddingTop
       */
      public void setPaddingTop(int paddingTop) {
          mFlow.setPaddingTop(paddingTop);
          requestLayout();
      }
  
      /**
       * Set padding right around the content
       *
       * @param paddingRight
       */
      public void setPaddingRight(int paddingRight) {
          mFlow.setPaddingRight(paddingRight);
          requestLayout();
      }
  
      /**
       * Set padding bottom around the content
       *
       * @param paddingBottom
       */
      public void setPaddingBottom(int paddingBottom) {
          mFlow.setPaddingBottom(paddingBottom);
          requestLayout();
      }
  
      /**
       * Set the style of the last Horizontal column.
       * @param style Flow.CHAIN_SPREAD, Flow.CHAIN_SPREAD_INSIDE, or Flow.CHAIN_PACKED
       */
      public void setLastHorizontalStyle(int style) {
          mFlow.setLastHorizontalStyle(style);
          requestLayout();
      }
  
      /**
       * Set the style of the last vertical row.
       * @param style  Flow.CHAIN_SPREAD, Flow.CHAIN_SPREAD_INSIDE, or Flow.CHAIN_PACKED
       */
      public void setLastVerticalStyle(int style) {
          mFlow.setLastVerticalStyle(style);
          requestLayout();
      }
  
      /**
       * Set the bias of the last Horizontal column.
       * @param bias
       */
      public void setLastHorizontalBias(float bias) {
          mFlow.setLastHorizontalBias(bias);
          requestLayout();
      }
  
      /**
       * Set the bias of the last vertical row.
       * @param bias
       */
      public void setLastVerticalBias(float bias) {
          mFlow.setLastVerticalBias(bias);
          requestLayout();
      }
  
      /**
       * Set wrap mode for the layout. Can be:
       *
       * Flow.WRAP_NONE (default) -- no wrap behavior, create a single chain
       * Flow.WRAP_CHAIN -- if not enough space to fit the referenced elements,
       * will create additional chains after the first one
       * Flow.WRAP_ALIGNED -- if not enough space to fit the referenced elements,
       * will wrap the elements, keeping them aligned (like a table)
       *
       * @param mode
       */
      public void setWrapMode(int mode) {
          mFlow.setWrapMode(mode);
          requestLayout();
      }
  
      /**
       * Set horizontal chain style. Can be:
       *
       * Flow.CHAIN_SPREAD
       * Flow.CHAIN_SPREAD_INSIDE
       * Flow.CHAIN_PACKED
       *
       * @param style
       */
      public void setHorizontalStyle(int style) {
          mFlow.setHorizontalStyle(style);
          requestLayout();
      }
  
      /**
       * Set vertical chain style. Can be:
       *
       * Flow.CHAIN_SPREAD
       * Flow.CHAIN_SPREAD_INSIDE
       * Flow.CHAIN_PACKED
       *
       * @param style
       */
      public void setVerticalStyle(int style) {
          mFlow.setVerticalStyle(style);
          requestLayout();
      }
  
      /**
       * Set the horizontal bias applied to the chain
       *
       * @param bias from 0 to 1
       */
      public void setHorizontalBias(float bias) {
          mFlow.setHorizontalBias(bias);
          requestLayout();
      }
  
      /**
       * Set the vertical bias applied to the chain
       *
       * @param bias from 0 to 1
       */
      public void setVerticalBias(float bias) {
          mFlow.setVerticalBias(bias);
          requestLayout();
      }
  
      /**
       * Similar to setHorizontalStyle(), but only applies to the first chain.
       *
       * @param style
       */
      public void setFirstHorizontalStyle(int style) {
          mFlow.setFirstHorizontalStyle(style);
          requestLayout();
      }
  
      /**
       * Similar to setVerticalStyle(), but only applies to the first chain.
       *
       * @param style
       */
      public void setFirstVerticalStyle(int style) {
          mFlow.setFirstVerticalStyle(style);
          requestLayout();
      }
  
      /**
       * Similar to setHorizontalBias(), but only applied to the first chain.
       *
       * @param bias
       */
      public void setFirstHorizontalBias(float bias) {
          mFlow.setFirstHorizontalBias(bias);
          requestLayout();
      }
  
      /**
       * Similar to setVerticalBias(), but only applied to the first chain.
       *
       * @param bias
       */
      public void setFirstVerticalBias(float bias) {
          mFlow.setFirstVerticalBias(bias);
          requestLayout();
      }
  
      /**
       * Set up the horizontal alignment of the elements in the layout,
       * if the layout orientation is set to Flow.VERTICAL
       *
       * Can be either:
       * Flow.HORIZONTAL_ALIGN_START
       * Flow.HORIZONTAL_ALIGN_END
       * Flow.HORIZONTAL_ALIGN_CENTER
       *
       * @param align
       */
      public void setHorizontalAlign(int align) {
          mFlow.setHorizontalAlign(align);
          requestLayout();
      }
  
      /**
       * Set up the vertical alignment of the elements in the layout,
       * if the layout orientation is set to Flow.HORIZONTAL
       *
       * Can be either:
       * Flow.VERTICAL_ALIGN_TOP
       * Flow.VERTICAL_ALIGN_BOTTOM
       * Flow.VERTICAL_ALIGN_CENTER
       * Flow.VERTICAL_ALIGN_BASELINE
       *
       * @param align
       */
      public void setVerticalAlign(int align) {
          mFlow.setVerticalAlign(align);
          requestLayout();
      }
  
      /**
       * Set up the horizontal gap between elements
       *
       * @param gap
       */
      public void setHorizontalGap(int gap) {
          mFlow.setHorizontalGap(gap);
          requestLayout();
      }
  
      /**
       * Set up the vertical gap between elements
       *
       * @param gap
       */
      public void setVerticalGap(int gap) {
          mFlow.setVerticalGap(gap);
          requestLayout();
      }
  
      /**
       * Set up the maximum number of elements before wrapping.
       *
       * @param max
       */
      public void setMaxElementsWrap(int max) {
          mFlow.setMaxElementsWrap(max);
          requestLayout();
      }
  }
  