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