• 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.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 
AbsActionBarView(Context context)60     public AbsActionBarView(Context context) {
61         this(context, null);
62     }
63 
AbsActionBarView(Context context, AttributeSet attrs)64     public AbsActionBarView(Context context, AttributeSet attrs) {
65         this(context, attrs, 0);
66     }
67 
AbsActionBarView(Context context, AttributeSet attrs, int defStyleAttr)68     public AbsActionBarView(Context context, AttributeSet attrs, int defStyleAttr) {
69         this(context, attrs, defStyleAttr, 0);
70     }
71 
AbsActionBarView( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)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
onConfigurationChanged(Configuration newConfig)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
onTouchEvent(MotionEvent ev)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
onHoverEvent(MotionEvent ev)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      */
setSplitToolbar(boolean split)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      */
setSplitWhenNarrow(boolean splitWhenNarrow)167     public void setSplitWhenNarrow(boolean splitWhenNarrow) {
168         mSplitWhenNarrow = splitWhenNarrow;
169     }
170 
setContentHeight(int height)171     public void setContentHeight(int height) {
172         mContentHeight = height;
173         requestLayout();
174     }
175 
getContentHeight()176     public int getContentHeight() {
177         return mContentHeight;
178     }
179 
setSplitView(ViewGroup splitView)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      */
getAnimatedVisibility()187     public int getAnimatedVisibility() {
188         if (mVisibilityAnim != null) {
189             return mVisAnimListener.mFinalVisibility;
190         }
191         return getVisibility();
192     }
193 
setupAnimatorToVisibility(int visibility, long duration)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 
animateToVisibility(int visibility)238     public void animateToVisibility(int visibility) {
239         Animator anim = setupAnimatorToVisibility(visibility, FADE_DURATION);
240         anim.start();
241     }
242 
243     @Override
setVisibility(int visibility)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 
showOverflowMenu()253     public boolean showOverflowMenu() {
254         if (mActionMenuPresenter != null) {
255             return mActionMenuPresenter.showOverflowMenu();
256         }
257         return false;
258     }
259 
postShowOverflowMenu()260     public void postShowOverflowMenu() {
261         post(new Runnable() {
262             public void run() {
263                 showOverflowMenu();
264             }
265         });
266     }
267 
hideOverflowMenu()268     public boolean hideOverflowMenu() {
269         if (mActionMenuPresenter != null) {
270             return mActionMenuPresenter.hideOverflowMenu();
271         }
272         return false;
273     }
274 
isOverflowMenuShowing()275     public boolean isOverflowMenuShowing() {
276         if (mActionMenuPresenter != null) {
277             return mActionMenuPresenter.isOverflowMenuShowing();
278         }
279         return false;
280     }
281 
isOverflowMenuShowPending()282     public boolean isOverflowMenuShowPending() {
283         if (mActionMenuPresenter != null) {
284             return mActionMenuPresenter.isOverflowMenuShowPending();
285         }
286         return false;
287     }
288 
isOverflowReserved()289     public boolean isOverflowReserved() {
290         return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved();
291     }
292 
canShowOverflowMenu()293     public boolean canShowOverflowMenu() {
294         return isOverflowReserved() && getVisibility() == VISIBLE;
295     }
296 
dismissPopupMenus()297     public void dismissPopupMenus() {
298         if (mActionMenuPresenter != null) {
299             mActionMenuPresenter.dismissPopupMenus();
300         }
301     }
302 
measureChildView(View child, int availableWidth, int childSpecHeight, int spacing)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 
next(int x, int val, boolean isRtl)314     static protected int next(int x, int val, boolean isRtl) {
315         return isRtl ? x - val : x + val;
316     }
317 
positionChild(View child, int x, int y, int contentHeight, boolean reverse)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 
withFinalVisibility(int visibility)336         public VisibilityAnimListener withFinalVisibility(int visibility) {
337             mFinalVisibility = visibility;
338             return this;
339         }
340 
341         @Override
onAnimationStart(Animator animation)342         public void onAnimationStart(Animator animation) {
343             setVisibility(VISIBLE);
344             mVisibilityAnim = animation;
345             mCanceled = false;
346         }
347 
348         @Override
onAnimationEnd(Animator animation)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
onAnimationCancel(Animator animation)360         public void onAnimationCancel(Animator animation) {
361             mCanceled = true;
362         }
363 
364         @Override
onAnimationRepeat(Animator animation)365         public void onAnimationRepeat(Animator animation) {
366         }
367     }
368 }
369