content: /*
   * Copyright (C) 2011 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.android.internal.widget;
  
  import com.android.internal.R;
  
  import android.util.TypedValue;
  import android.view.ContextThemeWrapper;
  import android.view.MotionEvent;
  import android.widget.ActionMenuPresenter;
  import android.widget.ActionMenuView;
  
  import android.animation.Animator;
  import android.animation.AnimatorSet;
  import android.animation.ObjectAnimator;
  import android.animation.TimeInterpolator;
  import android.annotation.UnsupportedAppUsage;
  import android.content.Context;
  import android.content.res.Configuration;
  import android.content.res.TypedArray;
  import android.util.AttributeSet;
  import android.view.View;
  import android.view.ViewGroup;
  import android.view.animation.DecelerateInterpolator;
  
  public abstract class AbsActionBarView extends ViewGroup {
      private static final TimeInterpolator sAlphaInterpolator = new DecelerateInterpolator();
  
      private static final int FADE_DURATION = 200;
  
      protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
  
      /** Context against which to inflate popup menus. */
      protected final Context mPopupContext;
  
      protected ActionMenuView mMenuView;
      protected ActionMenuPresenter mActionMenuPresenter;
      protected ViewGroup mSplitView;
      protected boolean mSplitActionBar;
      protected boolean mSplitWhenNarrow;
      protected int mContentHeight;
  
      protected Animator mVisibilityAnim;
  
      private boolean mEatingTouch;
      private boolean mEatingHover;
  
      public AbsActionBarView(Context context) {
          this(context, null);
      }
  
      public AbsActionBarView(Context context, AttributeSet attrs) {
          this(context, attrs, 0);
      }
  
      public AbsActionBarView(Context context, AttributeSet attrs, int defStyleAttr) {
          this(context, attrs, defStyleAttr, 0);
      }
  
      public AbsActionBarView(
              Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
          super(context, attrs, defStyleAttr, defStyleRes);
  
          final TypedValue tv = new TypedValue();
          if (context.getTheme().resolveAttribute(R.attr.actionBarPopupTheme, tv, true)
                  && tv.resourceId != 0) {
              mPopupContext = new ContextThemeWrapper(context, tv.resourceId);
          } else {
              mPopupContext = context;
          }
      }
  
      @Override
      protected void onConfigurationChanged(Configuration newConfig) {
          super.onConfigurationChanged(newConfig);
  
          // Action bar can change size on configuration changes.
          // Reread the desired height from the theme-specified style.
          TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.ActionBar,
                  com.android.internal.R.attr.actionBarStyle, 0);
          setContentHeight(a.getLayoutDimension(R.styleable.ActionBar_height, 0));
          a.recycle();
          if (mSplitWhenNarrow) {
              setSplitToolbar(getContext().getResources().getBoolean(
                      com.android.internal.R.bool.split_action_bar_is_narrow));
          }
          if (mActionMenuPresenter != null) {
              mActionMenuPresenter.onConfigurationChanged(newConfig);
          }
      }
  
      @Override
      public boolean onTouchEvent(MotionEvent ev) {
          // ActionBarViews always eat touch events, but should still respect the touch event dispatch
          // contract. If the normal View implementation doesn't want the events, we'll just silently
          // eat the rest of the gesture without reporting the events to the default implementation
          // since that's what it expects.
  
          final int action = ev.getActionMasked();
          if (action == MotionEvent.ACTION_DOWN) {
              mEatingTouch = false;
          }
  
          if (!mEatingTouch) {
              final boolean handled = super.onTouchEvent(ev);
              if (action == MotionEvent.ACTION_DOWN && !handled) {
                  mEatingTouch = true;
              }
          }
  
          if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
              mEatingTouch = false;
          }
  
          return true;
      }
  
      @Override
      public boolean onHoverEvent(MotionEvent ev) {
          // Same deal as onTouchEvent() above. Eat all hover events, but still
          // respect the touch event dispatch contract.
  
          final int action = ev.getActionMasked();
          if (action == MotionEvent.ACTION_HOVER_ENTER) {
              mEatingHover = false;
          }
  
          if (!mEatingHover) {
              final boolean handled = super.onHoverEvent(ev);
              if (action == MotionEvent.ACTION_HOVER_ENTER && !handled) {
                  mEatingHover = true;
              }
          }
  
          if (action == MotionEvent.ACTION_HOVER_EXIT
                  || action == MotionEvent.ACTION_CANCEL) {
              mEatingHover = false;
          }
  
          return true;
      }
  
      /**
       * Sets whether the bar should be split right now, no questions asked.
       * @param split true if the bar should split
       */
      public void setSplitToolbar(boolean split) {
          mSplitActionBar = split;
      }
  
      /**
       * Sets whether the bar should split if we enter a narrow screen configuration.
       * @param splitWhenNarrow true if the bar should check to split after a config change
       */
      public void setSplitWhenNarrow(boolean splitWhenNarrow) {
          mSplitWhenNarrow = splitWhenNarrow;
      }
  
      public void setContentHeight(int height) {
          mContentHeight = height;
          requestLayout();
      }
  
      public int getContentHeight() {
          return mContentHeight;
      }
  
      public void setSplitView(ViewGroup splitView) {
          mSplitView = splitView;
      }
  
      /**
       * @return Current visibility or if animating, the visibility being animated to.
       */
      public int getAnimatedVisibility() {
          if (mVisibilityAnim != null) {
              return mVisAnimListener.mFinalVisibility;
          }
          return getVisibility();
      }
  
      public Animator setupAnimatorToVisibility(int visibility, long duration) {
          if (mVisibilityAnim != null) {
              mVisibilityAnim.cancel();
          }
  
          if (visibility == VISIBLE) {
              if (getVisibility() != VISIBLE) {
                  setAlpha(0);
                  if (mSplitView != null && mMenuView != null) {
                      mMenuView.setAlpha(0);
                  }
              }
              ObjectAnimator anim = ObjectAnimator.ofFloat(this, View.ALPHA, 1);
              anim.setDuration(duration);
              anim.setInterpolator(sAlphaInterpolator);
              if (mSplitView != null && mMenuView != null) {
                  AnimatorSet set = new AnimatorSet();
                  ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, View.ALPHA, 1);
                  splitAnim.setDuration(duration);
                  set.addListener(mVisAnimListener.withFinalVisibility(visibility));
                  set.play(anim).with(splitAnim);
                  return set;
              } else {
                  anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
                  return anim;
              }
          } else {
              ObjectAnimator anim = ObjectAnimator.ofFloat(this, View.ALPHA, 0);
              anim.setDuration(duration);
              anim.setInterpolator(sAlphaInterpolator);
              if (mSplitView != null && mMenuView != null) {
                  AnimatorSet set = new AnimatorSet();
                  ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, View.ALPHA, 0);
                  splitAnim.setDuration(duration);
                  set.addListener(mVisAnimListener.withFinalVisibility(visibility));
                  set.play(anim).with(splitAnim);
                  return set;
              } else {
                  anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
                  return anim;
              }
          }
      }
  
      public void animateToVisibility(int visibility) {
          Animator anim = setupAnimatorToVisibility(visibility, FADE_DURATION);
          anim.start();
      }
  
      @Override
      public void setVisibility(int visibility) {
          if (visibility != getVisibility()) {
              if (mVisibilityAnim != null) {
                  mVisibilityAnim.end();
              }
              super.setVisibility(visibility);
          }
      }
  
      public boolean showOverflowMenu() {
          if (mActionMenuPresenter != null) {
              return mActionMenuPresenter.showOverflowMenu();
          }
          return false;
      }
  
      public void postShowOverflowMenu() {
          post(new Runnable() {
              public void run() {
                  showOverflowMenu();
              }
          });
      }
  
      public boolean hideOverflowMenu() {
          if (mActionMenuPresenter != null) {
              return mActionMenuPresenter.hideOverflowMenu();
          }
          return false;
      }
  
      public boolean isOverflowMenuShowing() {
          if (mActionMenuPresenter != null) {
              return mActionMenuPresenter.isOverflowMenuShowing();
          }
          return false;
      }
  
      public boolean isOverflowMenuShowPending() {
          if (mActionMenuPresenter != null) {
              return mActionMenuPresenter.isOverflowMenuShowPending();
          }
          return false;
      }
  
      public boolean isOverflowReserved() {
          return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved();
      }
  
      public boolean canShowOverflowMenu() {
          return isOverflowReserved() && getVisibility() == VISIBLE;
      }
  
      @UnsupportedAppUsage
      public void dismissPopupMenus() {
          if (mActionMenuPresenter != null) {
              mActionMenuPresenter.dismissPopupMenus();
          }
      }
  
      protected int measureChildView(View child, int availableWidth, int childSpecHeight,
              int spacing) {
          child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
                  childSpecHeight);
  
          availableWidth -= child.getMeasuredWidth();
          availableWidth -= spacing;
  
          return Math.max(0, availableWidth);
      }
  
      static protected int next(int x, int val, boolean isRtl) {
          return isRtl ? x - val : x + val;
      }
  
      protected int positionChild(View child, int x, int y, int contentHeight, boolean reverse) {
          int childWidth = child.getMeasuredWidth();
          int childHeight = child.getMeasuredHeight();
          int childTop = y + (contentHeight - childHeight) / 2;
  
          if (reverse) {
              child.layout(x - childWidth, childTop, x, childTop + childHeight);
          } else {
              child.layout(x, childTop, x + childWidth, childTop + childHeight);
          }
  
          return  (reverse ? -childWidth : childWidth);
      }
  
      protected class VisibilityAnimListener implements Animator.AnimatorListener {
          private boolean mCanceled = false;
          int mFinalVisibility;
  
          public VisibilityAnimListener withFinalVisibility(int visibility) {
              mFinalVisibility = visibility;
              return this;
          }
  
          @Override
          public void onAnimationStart(Animator animation) {
              setVisibility(VISIBLE);
              mVisibilityAnim = animation;
              mCanceled = false;
          }
  
          @Override
          public void onAnimationEnd(Animator animation) {
              if (mCanceled) return;
  
              mVisibilityAnim = null;
              setVisibility(mFinalVisibility);
              if (mSplitView != null && mMenuView != null) {
                  mMenuView.setVisibility(mFinalVisibility);
              }
          }
  
          @Override
          public void onAnimationCancel(Animator animation) {
              mCanceled = true;
          }
  
          @Override
          public void onAnimationRepeat(Animator animation) {
          }
      }
  }
  