Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.internal.widget;
     17 
     18 import com.android.internal.R;
     19 
     20 import android.util.TypedValue;
     21 import android.view.ContextThemeWrapper;
     22 import android.view.MotionEvent;
     23 import android.widget.ActionMenuPresenter;
     24 import android.widget.ActionMenuView;
     25 
     26 import android.animation.Animator;
     27 import android.animation.AnimatorSet;
     28 import android.animation.ObjectAnimator;
     29 import android.animation.TimeInterpolator;
     30 import android.content.Context;
     31 import android.content.res.Configuration;
     32 import android.content.res.TypedArray;
     33 import android.util.AttributeSet;
     34 import android.view.View;
     35 import android.view.ViewGroup;
     36 import android.view.animation.DecelerateInterpolator;
     37 
     38 public abstract class AbsActionBarView extends ViewGroup {
     39     private static final TimeInterpolator sAlphaInterpolator = new DecelerateInterpolator();
     40 
     41     private static final int FADE_DURATION = 200;
     42 
     43     protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
     44 
     45     /** Context against which to inflate popup menus. */
     46     protected final Context mPopupContext;
     47 
     48     protected ActionMenuView mMenuView;
     49     protected ActionMenuPresenter mActionMenuPresenter;
     50     protected ViewGroup mSplitView;
     51     protected boolean mSplitActionBar;
     52     protected boolean mSplitWhenNarrow;
     53     protected int mContentHeight;
     54 
     55     protected Animator mVisibilityAnim;
     56 
     57     private boolean mEatingTouch;
     58     private boolean mEatingHover;
     59 
     60     public AbsActionBarView(Context context) {
     61         this(context, null);
     62     }
     63 
     64     public AbsActionBarView(Context context, AttributeSet attrs) {
     65         this(context, attrs, 0);
     66     }
     67 
     68     public AbsActionBarView(Context context, AttributeSet attrs, int defStyleAttr) {
     69         this(context, attrs, defStyleAttr, 0);
     70     }
     71 
     72     public AbsActionBarView(
     73             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
     74         super(context, attrs, defStyleAttr, defStyleRes);
     75 
     76         final TypedValue tv = new TypedValue();
     77         if (context.getTheme().resolveAttribute(R.attr.actionBarPopupTheme, tv, true)
     78                 && tv.resourceId != 0) {
     79             mPopupContext = new ContextThemeWrapper(context, tv.resourceId);
     80         } else {
     81             mPopupContext = context;
     82         }
     83     }
     84 
     85     @Override
     86     protected void onConfigurationChanged(Configuration newConfig) {
     87         super.onConfigurationChanged(newConfig);
     88 
     89         // Action bar can change size on configuration changes.
     90         // Reread the desired height from the theme-specified style.
     91         TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.ActionBar,
     92                 com.android.internal.R.attr.actionBarStyle, 0);
     93         setContentHeight(a.getLayoutDimension(R.styleable.ActionBar_height, 0));
     94         a.recycle();
     95         if (mSplitWhenNarrow) {
     96             setSplitToolbar(getContext().getResources().getBoolean(
     97                     com.android.internal.R.bool.split_action_bar_is_narrow));
     98         }
     99         if (mActionMenuPresenter != null) {
    100             mActionMenuPresenter.onConfigurationChanged(newConfig);
    101         }
    102     }
    103 
    104     @Override
    105     public boolean onTouchEvent(MotionEvent ev) {
    106         // ActionBarViews always eat touch events, but should still respect the touch event dispatch
    107         // contract. If the normal View implementation doesn't want the events, we'll just silently
    108         // eat the rest of the gesture without reporting the events to the default implementation
    109         // since that's what it expects.
    110 
    111         final int action = ev.getActionMasked();
    112         if (action == MotionEvent.ACTION_DOWN) {
    113             mEatingTouch = false;
    114         }
    115 
    116         if (!mEatingTouch) {
    117             final boolean handled = super.onTouchEvent(ev);
    118             if (action == MotionEvent.ACTION_DOWN && !handled) {
    119                 mEatingTouch = true;
    120             }
    121         }
    122 
    123         if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
    124             mEatingTouch = false;
    125         }
    126 
    127         return true;
    128     }
    129 
    130     @Override
    131     public boolean onHoverEvent(MotionEvent ev) {
    132         // Same deal as onTouchEvent() above. Eat all hover events, but still
    133         // respect the touch event dispatch contract.
    134 
    135         final int action = ev.getActionMasked();
    136         if (action == MotionEvent.ACTION_HOVER_ENTER) {
    137             mEatingHover = false;
    138         }
    139 
    140         if (!mEatingHover) {
    141             final boolean handled = super.onHoverEvent(ev);
    142             if (action == MotionEvent.ACTION_HOVER_ENTER && !handled) {
    143                 mEatingHover = true;
    144             }
    145         }
    146 
    147         if (action == MotionEvent.ACTION_HOVER_EXIT
    148                 || action == MotionEvent.ACTION_CANCEL) {
    149             mEatingHover = false;
    150         }
    151 
    152         return true;
    153     }
    154 
    155     /**
    156      * Sets whether the bar should be split right now, no questions asked.
    157      * @param split true if the bar should split
    158      */
    159     public void setSplitToolbar(boolean split) {
    160         mSplitActionBar = split;
    161     }
    162 
    163     /**
    164      * Sets whether the bar should split if we enter a narrow screen configuration.
    165      * @param splitWhenNarrow true if the bar should check to split after a config change
    166      */
    167     public void setSplitWhenNarrow(boolean splitWhenNarrow) {
    168         mSplitWhenNarrow = splitWhenNarrow;
    169     }
    170 
    171     public void setContentHeight(int height) {
    172         mContentHeight = height;
    173         requestLayout();
    174     }
    175 
    176     public int getContentHeight() {
    177         return mContentHeight;
    178     }
    179 
    180     public void setSplitView(ViewGroup splitView) {
    181         mSplitView = splitView;
    182     }
    183 
    184     /**
    185      * @return Current visibility or if animating, the visibility being animated to.
    186      */
    187     public int getAnimatedVisibility() {
    188         if (mVisibilityAnim != null) {
    189             return mVisAnimListener.mFinalVisibility;
    190         }
    191         return getVisibility();
    192     }
    193 
    194     public Animator setupAnimatorToVisibility(int visibility, long duration) {
    195         if (mVisibilityAnim != null) {
    196             mVisibilityAnim.cancel();
    197         }
    198 
    199         if (visibility == VISIBLE) {
    200             if (getVisibility() != VISIBLE) {
    201                 setAlpha(0);
    202                 if (mSplitView != null && mMenuView != null) {
    203                     mMenuView.setAlpha(0);
    204                 }
    205             }
    206             ObjectAnimator anim = ObjectAnimator.ofFloat(this, View.ALPHA, 1);
    207             anim.setDuration(duration);
    208             anim.setInterpolator(sAlphaInterpolator);
    209             if (mSplitView != null && mMenuView != null) {
    210                 AnimatorSet set = new AnimatorSet();
    211                 ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, View.ALPHA, 1);
    212                 splitAnim.setDuration(duration);
    213                 set.addListener(mVisAnimListener.withFinalVisibility(visibility));
    214                 set.play(anim).with(splitAnim);
    215                 return set;
    216             } else {
    217                 anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
    218                 return anim;
    219             }
    220         } else {
    221             ObjectAnimator anim = ObjectAnimator.ofFloat(this, View.ALPHA, 0);
    222             anim.setDuration(duration);
    223             anim.setInterpolator(sAlphaInterpolator);
    224             if (mSplitView != null && mMenuView != null) {
    225                 AnimatorSet set = new AnimatorSet();
    226                 ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, View.ALPHA, 0);
    227                 splitAnim.setDuration(duration);
    228                 set.addListener(mVisAnimListener.withFinalVisibility(visibility));
    229                 set.play(anim).with(splitAnim);
    230                 return set;
    231             } else {
    232                 anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
    233                 return anim;
    234             }
    235         }
    236     }
    237 
    238     public void animateToVisibility(int visibility) {
    239         Animator anim = setupAnimatorToVisibility(visibility, FADE_DURATION);
    240         anim.start();
    241     }
    242 
    243     @Override
    244     public void setVisibility(int visibility) {
    245         if (visibility != getVisibility()) {
    246             if (mVisibilityAnim != null) {
    247                 mVisibilityAnim.end();
    248             }
    249             super.setVisibility(visibility);
    250         }
    251     }
    252 
    253     public boolean showOverflowMenu() {
    254         if (mActionMenuPresenter != null) {
    255             return mActionMenuPresenter.showOverflowMenu();
    256         }
    257         return false;
    258     }
    259 
    260     public void postShowOverflowMenu() {
    261         post(new Runnable() {
    262             public void run() {
    263                 showOverflowMenu();
    264             }
    265         });
    266     }
    267 
    268     public boolean hideOverflowMenu() {
    269         if (mActionMenuPresenter != null) {
    270             return mActionMenuPresenter.hideOverflowMenu();
    271         }
    272         return false;
    273     }
    274 
    275     public boolean isOverflowMenuShowing() {
    276         if (mActionMenuPresenter != null) {
    277             return mActionMenuPresenter.isOverflowMenuShowing();
    278         }
    279         return false;
    280     }
    281 
    282     public boolean isOverflowMenuShowPending() {
    283         if (mActionMenuPresenter != null) {
    284             return mActionMenuPresenter.isOverflowMenuShowPending();
    285         }
    286         return false;
    287     }
    288 
    289     public boolean isOverflowReserved() {
    290         return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved();
    291     }
    292 
    293     public boolean canShowOverflowMenu() {
    294         return isOverflowReserved() && getVisibility() == VISIBLE;
    295     }
    296 
    297     public void dismissPopupMenus() {
    298         if (mActionMenuPresenter != null) {
    299             mActionMenuPresenter.dismissPopupMenus();
    300         }
    301     }
    302 
    303     protected int measureChildView(View child, int availableWidth, int childSpecHeight,
    304             int spacing) {
    305         child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
    306                 childSpecHeight);
    307 
    308         availableWidth -= child.getMeasuredWidth();
    309         availableWidth -= spacing;
    310 
    311         return Math.max(0, availableWidth);
    312     }
    313 
    314     static protected int next(int x, int val, boolean isRtl) {
    315         return isRtl ? x - val : x + val;
    316     }
    317 
    318     protected int positionChild(View child, int x, int y, int contentHeight, boolean reverse) {
    319         int childWidth = child.getMeasuredWidth();
    320         int childHeight = child.getMeasuredHeight();
    321         int childTop = y + (contentHeight - childHeight) / 2;
    322 
    323         if (reverse) {
    324             child.layout(x - childWidth, childTop, x, childTop + childHeight);
    325         } else {
    326             child.layout(x, childTop, x + childWidth, childTop + childHeight);
    327         }
    328 
    329         return  (reverse ? -childWidth : childWidth);
    330     }
    331 
    332     protected class VisibilityAnimListener implements Animator.AnimatorListener {
    333         private boolean mCanceled = false;
    334         int mFinalVisibility;
    335 
    336         public VisibilityAnimListener withFinalVisibility(int visibility) {
    337             mFinalVisibility = visibility;
    338             return this;
    339         }
    340 
    341         @Override
    342         public void onAnimationStart(Animator animation) {
    343             setVisibility(VISIBLE);
    344             mVisibilityAnim = animation;
    345             mCanceled = false;
    346         }
    347 
    348         @Override
    349         public void onAnimationEnd(Animator animation) {
    350             if (mCanceled) return;
    351 
    352             mVisibilityAnim = null;
    353             setVisibility(mFinalVisibility);
    354             if (mSplitView != null && mMenuView != null) {
    355                 mMenuView.setVisibility(mFinalVisibility);
    356             }
    357         }
    358 
    359         @Override
    360         public void onAnimationCancel(Animator animation) {
    361             mCanceled = true;
    362         }
    363 
    364         @Override
    365         public void onAnimationRepeat(Animator animation) {
    366         }
    367     }
    368 }
    369