content: /*
   * Copyright (C) 2010 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 com.actionbarsherlock.internal.view.menu;
  
  import android.content.Context;
  import android.content.res.Configuration;
  import android.graphics.Canvas;
  import android.os.Build;
  import android.util.AttributeSet;
  import android.view.Gravity;
  import android.view.View;
  import android.view.ViewGroup;
  import android.view.accessibility.AccessibilityEvent;
  import android.widget.LinearLayout;
  import com.actionbarsherlock.internal.widget.IcsLinearLayout;
  
  /**
   * @hide
   */
  public class ActionMenuView extends IcsLinearLayout implements MenuBuilder.ItemInvoker, MenuView {
      //UNUSED private static final String TAG = "ActionMenuView";
      private static final boolean IS_FROYO = Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;
  
      static final int MIN_CELL_SIZE = 56; // dips
      static final int GENERATED_ITEM_PADDING = 4; // dips
  
      private MenuBuilder mMenu;
  
      private boolean mReserveOverflow;
      private ActionMenuPresenter mPresenter;
      private boolean mFormatItems;
      private int mFormatItemsWidth;
      private int mMinCellSize;
      private int mGeneratedItemPadding;
      //UNUSED private int mMeasuredExtraWidth;
  
      private boolean mFirst = true;
  
      public ActionMenuView(Context context) {
          this(context, null);
      }
  
      public ActionMenuView(Context context, AttributeSet attrs) {
          super(context, attrs);
          setBaselineAligned(false);
          final float density = context.getResources().getDisplayMetrics().density;
          mMinCellSize = (int) (MIN_CELL_SIZE * density);
          mGeneratedItemPadding = (int) (GENERATED_ITEM_PADDING * density);
      }
  
      public void setPresenter(ActionMenuPresenter presenter) {
          mPresenter = presenter;
      }
  
      public boolean isExpandedFormat() {
          return mFormatItems;
      }
  
      @Override
      public void onConfigurationChanged(Configuration newConfig) {
          if (IS_FROYO) {
              super.onConfigurationChanged(newConfig);
          }
          mPresenter.updateMenuView(false);
  
          if (mPresenter != null && mPresenter.isOverflowMenuShowing()) {
              mPresenter.hideOverflowMenu();
              mPresenter.showOverflowMenu();
          }
      }
  
      @Override
      protected void onDraw(Canvas canvas) {
          //Need to trigger a relayout since we may have been added extremely
          //late in the initial rendering (e.g., when contained in a ViewPager).
          //See: https://github.com/JakeWharton/ActionBarSherlock/issues/272
          if (!IS_FROYO && mFirst) {
              mFirst = false;
              requestLayout();
              return;
          }
          super.onDraw(canvas);
      }
  
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          // If we've been given an exact size to match, apply special formatting during layout.
          final boolean wasFormatted = mFormatItems;
          mFormatItems = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY;
  
          if (wasFormatted != mFormatItems) {
              mFormatItemsWidth = 0; // Reset this when switching modes
          }
  
          // Special formatting can change whether items can fit as action buttons.
          // Kick the menu and update presenters when this changes.
          final int widthSize = MeasureSpec.getMode(widthMeasureSpec);
          if (mFormatItems && mMenu != null && widthSize != mFormatItemsWidth) {
              mFormatItemsWidth = widthSize;
              mMenu.onItemsChanged(true);
          }
  
          if (mFormatItems) {
              onMeasureExactFormat(widthMeasureSpec, heightMeasureSpec);
          } else {
              super.onMeasure(widthMeasureSpec, heightMeasureSpec);
          }
      }
  
      private void onMeasureExactFormat(int widthMeasureSpec, int heightMeasureSpec) {
          // We already know the width mode is EXACTLY if we're here.
          final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
          int widthSize = MeasureSpec.getSize(widthMeasureSpec);
          int heightSize = MeasureSpec.getSize(heightMeasureSpec);
  
          final int widthPadding = getPaddingLeft() + getPaddingRight();
          final int heightPadding = getPaddingTop() + getPaddingBottom();
  
          widthSize -= widthPadding;
  
          // Divide the view into cells.
          final int cellCount = widthSize / mMinCellSize;
          final int cellSizeRemaining = widthSize % mMinCellSize;
  
          if (cellCount == 0) {
              // Give up, nothing fits.
              setMeasuredDimension(widthSize, 0);
              return;
          }
  
          final int cellSize = mMinCellSize + cellSizeRemaining / cellCount;
  
          int cellsRemaining = cellCount;
          int maxChildHeight = 0;
          int maxCellsUsed = 0;
          int expandableItemCount = 0;
          int visibleItemCount = 0;
          boolean hasOverflow = false;
  
          // This is used as a bitfield to locate the smallest items present. Assumes childCount < 64.
          long smallestItemsAt = 0;
  
          final int childCount = getChildCount();
          for (int i = 0; i < childCount; i++) {
              final View child = getChildAt(i);
              if (child.getVisibility() == GONE) continue;
  
              final boolean isGeneratedItem = child instanceof ActionMenuItemView;
              visibleItemCount++;
  
              if (isGeneratedItem) {
                  // Reset padding for generated menu item views; it may change below
                  // and views are recycled.
                  child.setPadding(mGeneratedItemPadding, 0, mGeneratedItemPadding, 0);
              }
  
              final LayoutParams lp = (LayoutParams) child.getLayoutParams();
              lp.expanded = false;
              lp.extraPixels = 0;
              lp.cellsUsed = 0;
              lp.expandable = false;
              lp.leftMargin = 0;
              lp.rightMargin = 0;
              lp.preventEdgeOffset = isGeneratedItem && ((ActionMenuItemView) child).hasText();
  
              // Overflow always gets 1 cell. No more, no less.
              final int cellsAvailable = lp.isOverflowButton ? 1 : cellsRemaining;
  
              final int cellsUsed = measureChildForCells(child, cellSize, cellsAvailable,
                      heightMeasureSpec, heightPadding);
  
              maxCellsUsed = Math.max(maxCellsUsed, cellsUsed);
              if (lp.expandable) expandableItemCount++;
              if (lp.isOverflowButton) hasOverflow = true;
  
              cellsRemaining -= cellsUsed;
              maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
              if (cellsUsed == 1) smallestItemsAt |= (1 << i);
          }
  
          // When we have overflow and a single expanded (text) item, we want to try centering it
          // visually in the available space even though overflow consumes some of it.
          final boolean centerSingleExpandedItem = hasOverflow && visibleItemCount == 2;
  
          // Divide space for remaining cells if we have items that can expand.
          // Try distributing whole leftover cells to smaller items first.
  
          boolean needsExpansion = false;
          while (expandableItemCount > 0 && cellsRemaining > 0) {
              int minCells = Integer.MAX_VALUE;
              long minCellsAt = 0; // Bit locations are indices of relevant child views
              int minCellsItemCount = 0;
              for (int i = 0; i < childCount; i++) {
                  final View child = getChildAt(i);
                  final LayoutParams lp = (LayoutParams) child.getLayoutParams();
  
                  // Don't try to expand items that shouldn't.
                  if (!lp.expandable) continue;
  
                  // Mark indices of children that can receive an extra cell.
                  if (lp.cellsUsed < minCells) {
                      minCells = lp.cellsUsed;
                      minCellsAt = 1 << i;
                      minCellsItemCount = 1;
                  } else if (lp.cellsUsed == minCells) {
                      minCellsAt |= 1 << i;
                      minCellsItemCount++;
                  }
              }
  
              // Items that get expanded will always be in the set of smallest items when we're done.
              smallestItemsAt |= minCellsAt;
  
              if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop.
  
              // We have enough cells, all minimum size items will be incremented.
              minCells++;
  
              for (int i = 0; i < childCount; i++) {
                  final View child = getChildAt(i);
                  final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                  if ((minCellsAt & (1 << i)) == 0) {
                      // If this item is already at our small item count, mark it for later.
                      if (lp.cellsUsed == minCells) smallestItemsAt |= 1 << i;
                      continue;
                  }
  
                  if (centerSingleExpandedItem && lp.preventEdgeOffset && cellsRemaining == 1) {
                      // Add padding to this item such that it centers.
                      child.setPadding(mGeneratedItemPadding + cellSize, 0, mGeneratedItemPadding, 0);
                  }
                  lp.cellsUsed++;
                  lp.expanded = true;
                  cellsRemaining--;
              }
  
              needsExpansion = true;
          }
  
          // Divide any space left that wouldn't divide along cell boundaries
          // evenly among the smallest items
  
          final boolean singleItem = !hasOverflow && visibleItemCount == 1;
          if (cellsRemaining > 0 && smallestItemsAt != 0 &&
                  (cellsRemaining < visibleItemCount - 1 || singleItem || maxCellsUsed > 1)) {
              float expandCount = Long.bitCount(smallestItemsAt);
  
              if (!singleItem) {
                  // The items at the far edges may only expand by half in order to pin to either side.
                  if ((smallestItemsAt & 1) != 0) {
                      LayoutParams lp = (LayoutParams) getChildAt(0).getLayoutParams();
                      if (!lp.preventEdgeOffset) expandCount -= 0.5f;
                  }
                  if ((smallestItemsAt & (1 << (childCount - 1))) != 0) {
                      LayoutParams lp = ((LayoutParams) getChildAt(childCount - 1).getLayoutParams());
                      if (!lp.preventEdgeOffset) expandCount -= 0.5f;
                  }
              }
  
              final int extraPixels = expandCount > 0 ?
                      (int) (cellsRemaining * cellSize / expandCount) : 0;
  
              for (int i = 0; i < childCount; i++) {
                  if ((smallestItemsAt & (1 << i)) == 0) continue;
  
                  final View child = getChildAt(i);
                  final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                  if (child instanceof ActionMenuItemView) {
                      // If this is one of our views, expand and measure at the larger size.
                      lp.extraPixels = extraPixels;
                      lp.expanded = true;
                      if (i == 0 && !lp.preventEdgeOffset) {
                          // First item gets part of its new padding pushed out of sight.
                          // The last item will get this implicitly from layout.
                          lp.leftMargin = -extraPixels / 2;
                      }
                      needsExpansion = true;
                  } else if (lp.isOverflowButton) {
                      lp.extraPixels = extraPixels;
                      lp.expanded = true;
                      lp.rightMargin = -extraPixels / 2;
                      needsExpansion = true;
                  } else {
                      // If we don't know what it is, give it some margins instead
                      // and let it center within its space. We still want to pin
                      // against the edges.
                      if (i != 0) {
                          lp.leftMargin = extraPixels / 2;
                      }
                      if (i != childCount - 1) {
                          lp.rightMargin = extraPixels / 2;
                      }
                  }
              }
  
              cellsRemaining = 0;
          }
  
          // Remeasure any items that have had extra space allocated to them.
          if (needsExpansion) {
              int heightSpec = MeasureSpec.makeMeasureSpec(heightSize - heightPadding, heightMode);
              for (int i = 0; i < childCount; i++) {
                  final View child = getChildAt(i);
                  final LayoutParams lp = (LayoutParams) child.getLayoutParams();
  
                  if (!lp.expanded) continue;
  
                  final int width = lp.cellsUsed * cellSize + lp.extraPixels;
                  child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), heightSpec);
              }
          }
  
          if (heightMode != MeasureSpec.EXACTLY) {
              heightSize = maxChildHeight;
          }
  
          setMeasuredDimension(widthSize, heightSize);
          //UNUSED mMeasuredExtraWidth = cellsRemaining * cellSize;
      }
  
      /**
       * Measure a child view to fit within cell-based formatting. The child's width
       * will be measured to a whole multiple of cellSize.
       *
       * <p>Sets the expandable and cellsUsed fields of LayoutParams.
       *
       * @param child Child to measure
       * @param cellSize Size of one cell
       * @param cellsRemaining Number of cells remaining that this view can expand to fill
       * @param parentHeightMeasureSpec MeasureSpec used by the parent view
       * @param parentHeightPadding Padding present in the parent view
       * @return Number of cells this child was measured to occupy
       */
      static int measureChildForCells(View child, int cellSize, int cellsRemaining,
              int parentHeightMeasureSpec, int parentHeightPadding) {
          final LayoutParams lp = (LayoutParams) child.getLayoutParams();
  
          final int childHeightSize = MeasureSpec.getSize(parentHeightMeasureSpec) -
                  parentHeightPadding;
          final int childHeightMode = MeasureSpec.getMode(parentHeightMeasureSpec);
          final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode);
  
          int cellsUsed = 0;
          if (cellsRemaining > 0) {
              final int childWidthSpec = MeasureSpec.makeMeasureSpec(
                      cellSize * cellsRemaining, MeasureSpec.AT_MOST);
              child.measure(childWidthSpec, childHeightSpec);
  
              final int measuredWidth = child.getMeasuredWidth();
              cellsUsed = measuredWidth / cellSize;
              if (measuredWidth % cellSize != 0) cellsUsed++;
          }
  
          final ActionMenuItemView itemView = child instanceof ActionMenuItemView ?
                  (ActionMenuItemView) child : null;
          final boolean expandable = !lp.isOverflowButton && itemView != null && itemView.hasText();
          lp.expandable = expandable;
  
          lp.cellsUsed = cellsUsed;
          final int targetWidth = cellsUsed * cellSize;
          child.measure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
                  childHeightSpec);
          return cellsUsed;
      }
  
      @Override
      protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
          if (!mFormatItems) {
              super.onLayout(changed, left, top, right, bottom);
              return;
          }
  
          final int childCount = getChildCount();
          final int midVertical = (top + bottom) / 2;
          final int dividerWidth = 0;//getDividerWidth();
          int overflowWidth = 0;
          //UNUSED int nonOverflowWidth = 0;
          int nonOverflowCount = 0;
          int widthRemaining = right - left - getPaddingRight() - getPaddingLeft();
          boolean hasOverflow = false;
          for (int i = 0; i < childCount; i++) {
              final View v = getChildAt(i);
              if (v.getVisibility() == GONE) {
                  continue;
              }
  
              LayoutParams p = (LayoutParams) v.getLayoutParams();
              if (p.isOverflowButton) {
                  overflowWidth = v.getMeasuredWidth();
                  if (hasDividerBeforeChildAt(i)) {
                      overflowWidth += dividerWidth;
                  }
  
                  int height = v.getMeasuredHeight();
                  int r = getWidth() - getPaddingRight() - p.rightMargin;
                  int l = r - overflowWidth;
                  int t = midVertical - (height / 2);
                  int b = t + height;
                  v.layout(l, t, r, b);
  
                  widthRemaining -= overflowWidth;
                  hasOverflow = true;
              } else {
                  final int size = v.getMeasuredWidth() + p.leftMargin + p.rightMargin;
                  //UNUSED nonOverflowWidth += size;
                  widthRemaining -= size;
                  //if (hasDividerBeforeChildAt(i)) {
                      //UNUSED nonOverflowWidth += dividerWidth;
                  //}
                  nonOverflowCount++;
              }
          }
  
          if (childCount == 1 && !hasOverflow) {
              // Center a single child
              final View v = getChildAt(0);
              final int width = v.getMeasuredWidth();
              final int height = v.getMeasuredHeight();
              final int midHorizontal = (right - left) / 2;
              final int l = midHorizontal - width / 2;
              final int t = midVertical - height / 2;
              v.layout(l, t, l + width, t + height);
              return;
          }
  
          final int spacerCount = nonOverflowCount - (hasOverflow ? 0 : 1);
          final int spacerSize = Math.max(0, spacerCount > 0 ? widthRemaining / spacerCount : 0);
  
          int startLeft = getPaddingLeft();
          for (int i = 0; i < childCount; i++) {
              final View v = getChildAt(i);
              final LayoutParams lp = (LayoutParams) v.getLayoutParams();
              if (v.getVisibility() == GONE || lp.isOverflowButton) {
                  continue;
              }
  
              startLeft += lp.leftMargin;
              int width = v.getMeasuredWidth();
              int height = v.getMeasuredHeight();
              int t = midVertical - height / 2;
              v.layout(startLeft, t, startLeft + width, t + height);
              startLeft += width + lp.rightMargin + spacerSize;
          }
      }
  
      @Override
      public void onDetachedFromWindow() {
          super.onDetachedFromWindow();
          mPresenter.dismissPopupMenus();
      }
  
      public boolean isOverflowReserved() {
          return mReserveOverflow;
      }
  
      public void setOverflowReserved(boolean reserveOverflow) {
          mReserveOverflow = reserveOverflow;
      }
  
      @Override
      protected LayoutParams generateDefaultLayoutParams() {
          LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
                  LayoutParams.WRAP_CONTENT);
          params.gravity = Gravity.CENTER_VERTICAL;
          return params;
      }
  
      @Override
      public LayoutParams generateLayoutParams(AttributeSet attrs) {
          return new LayoutParams(getContext(), attrs);
      }
  
      @Override
      protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
          if (p instanceof LayoutParams) {
              LayoutParams result = new LayoutParams((LayoutParams) p);
              if (result.gravity <= Gravity.NO_GRAVITY) {
                  result.gravity = Gravity.CENTER_VERTICAL;
              }
              return result;
          }
          return generateDefaultLayoutParams();
      }
  
      @Override
      protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
          return p != null && p instanceof LayoutParams;
      }
  
      public LayoutParams generateOverflowButtonLayoutParams() {
          LayoutParams result = generateDefaultLayoutParams();
          result.isOverflowButton = true;
          return result;
      }
  
      public boolean invokeItem(MenuItemImpl item) {
          return mMenu.performItemAction(item, 0);
      }
  
      public int getWindowAnimations() {
          return 0;
      }
  
      public void initialize(MenuBuilder menu) {
          mMenu = menu;
      }
  
      //@Override
      protected boolean hasDividerBeforeChildAt(int childIndex) {
          if (childIndex == 0) {
              return false;
          }
          final View childBefore = getChildAt(childIndex - 1);
          final View child = getChildAt(childIndex);
          boolean result = false;
          if (childIndex < getChildCount() && childBefore instanceof ActionMenuChildView) {
              result |= ((ActionMenuChildView) childBefore).needsDividerAfter();
          }
          if (childIndex > 0 && child instanceof ActionMenuChildView) {
              result |= ((ActionMenuChildView) child).needsDividerBefore();
          }
          return result;
      }
  
      public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
          return false;
      }
  
      public interface ActionMenuChildView {
          public boolean needsDividerBefore();
          public boolean needsDividerAfter();
      }
  
      public static class LayoutParams extends LinearLayout.LayoutParams {
          public boolean isOverflowButton;
          public int cellsUsed;
          public int extraPixels;
          public boolean expandable;
          public boolean preventEdgeOffset;
  
          public boolean expanded;
  
          public LayoutParams(Context c, AttributeSet attrs) {
              super(c, attrs);
          }
  
          public LayoutParams(LayoutParams other) {
              super((LinearLayout.LayoutParams) other);
              isOverflowButton = other.isOverflowButton;
          }
  
          public LayoutParams(int width, int height) {
              super(width, height);
              isOverflowButton = false;
          }
  
          public LayoutParams(int width, int height, boolean isOverflowButton) {
              super(width, height);
              this.isOverflowButton = isOverflowButton;
          }
      }
  }
  