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.widget.ActionMenuPresenter;
23 import android.widget.ActionMenuView;
24 
25 import android.animation.Animator;
26 import android.animation.AnimatorSet;
27 import android.animation.ObjectAnimator;
28 import android.animation.TimeInterpolator;
29 import android.content.Context;
30 import android.content.res.Configuration;
31 import android.content.res.TypedArray;
32 import android.util.AttributeSet;
33 import android.view.View;
34 import android.view.ViewGroup;
35 import android.view.animation.DecelerateInterpolator;
36 
37 public abstract class AbsActionBarView extends ViewGroup {
38     private static final TimeInterpolator sAlphaInterpolator = new DecelerateInterpolator();
39 
40     private static final int FADE_DURATION = 200;
41 
42     protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
43 
44     /** Context against which to inflate popup menus. */
45     protected final Context mPopupContext;
46 
47     protected ActionMenuView mMenuView;
48     protected ActionMenuPresenter mActionMenuPresenter;
49     protected ViewGroup mSplitView;
50     protected boolean mSplitActionBar;
51     protected boolean mSplitWhenNarrow;
52     protected int mContentHeight;
53 
54     protected Animator mVisibilityAnim;
55 
AbsActionBarView(Context context)56     public AbsActionBarView(Context context) {
57         this(context, null);
58     }
59 
AbsActionBarView(Context context, AttributeSet attrs)60     public AbsActionBarView(Context context, AttributeSet attrs) {
61         this(context, attrs, 0);
62     }
63 
AbsActionBarView(Context context, AttributeSet attrs, int defStyleAttr)64     public AbsActionBarView(Context context, AttributeSet attrs, int defStyleAttr) {
65         this(context, attrs, defStyleAttr, 0);
66     }
67 
AbsActionBarView( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)68     public AbsActionBarView(
69             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
70         super(context, attrs, defStyleAttr, defStyleRes);
71 
72         final TypedValue tv = new TypedValue();
73         if (context.getTheme().resolveAttribute(R.attr.actionBarPopupTheme, tv, true)
74                 && tv.resourceId != 0) {
75             mPopupContext = new ContextThemeWrapper(context, tv.resourceId);
76         } else {
77             mPopupContext = context;
78         }
79     }
80 
81     @Override
onConfigurationChanged(Configuration newConfig)82     protected void onConfigurationChanged(Configuration newConfig) {
83         super.onConfigurationChanged(newConfig);
84 
85         // Action bar can change size on configuration changes.
86         // Reread the desired height from the theme-specified style.
87         TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.ActionBar,
88                 com.android.internal.R.attr.actionBarStyle, 0);
89         setContentHeight(a.getLayoutDimension(R.styleable.ActionBar_height, 0));
90         a.recycle();
91         if (mSplitWhenNarrow) {
92             setSplitToolbar(getContext().getResources().getBoolean(
93                     com.android.internal.R.bool.split_action_bar_is_narrow));
94         }
95         if (mActionMenuPresenter != null) {
96             mActionMenuPresenter.onConfigurationChanged(newConfig);
97         }
98     }
99 
100     /**
101      * Sets whether the bar should be split right now, no questions asked.
102      * @param split true if the bar should split
103      */
setSplitToolbar(boolean split)104     public void setSplitToolbar(boolean split) {
105         mSplitActionBar = split;
106     }
107 
108     /**
109      * Sets whether the bar should split if we enter a narrow screen configuration.
110      * @param splitWhenNarrow true if the bar should check to split after a config change
111      */
setSplitWhenNarrow(boolean splitWhenNarrow)112     public void setSplitWhenNarrow(boolean splitWhenNarrow) {
113         mSplitWhenNarrow = splitWhenNarrow;
114     }
115 
setContentHeight(int height)116     public void setContentHeight(int height) {
117         mContentHeight = height;
118         requestLayout();
119     }
120 
getContentHeight()121     public int getContentHeight() {
122         return mContentHeight;
123     }
124 
setSplitView(ViewGroup splitView)125     public void setSplitView(ViewGroup splitView) {
126         mSplitView = splitView;
127     }
128 
129     /**
130      * @return Current visibility or if animating, the visibility being animated to.
131      */
getAnimatedVisibility()132     public int getAnimatedVisibility() {
133         if (mVisibilityAnim != null) {
134             return mVisAnimListener.mFinalVisibility;
135         }
136         return getVisibility();
137     }
138 
setupAnimatorToVisibility(int visibility, long duration)139     public Animator setupAnimatorToVisibility(int visibility, long duration) {
140         if (mVisibilityAnim != null) {
141             mVisibilityAnim.cancel();
142         }
143 
144         if (visibility == VISIBLE) {
145             if (getVisibility() != VISIBLE) {
146                 setAlpha(0);
147                 if (mSplitView != null && mMenuView != null) {
148                     mMenuView.setAlpha(0);
149                 }
150             }
151             ObjectAnimator anim = ObjectAnimator.ofFloat(this, View.ALPHA, 1);
152             anim.setDuration(duration);
153             anim.setInterpolator(sAlphaInterpolator);
154             if (mSplitView != null && mMenuView != null) {
155                 AnimatorSet set = new AnimatorSet();
156                 ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, View.ALPHA, 1);
157                 splitAnim.setDuration(duration);
158                 set.addListener(mVisAnimListener.withFinalVisibility(visibility));
159                 set.play(anim).with(splitAnim);
160                 return set;
161             } else {
162                 anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
163                 return anim;
164             }
165         } else {
166             ObjectAnimator anim = ObjectAnimator.ofFloat(this, View.ALPHA, 0);
167             anim.setDuration(duration);
168             anim.setInterpolator(sAlphaInterpolator);
169             if (mSplitView != null && mMenuView != null) {
170                 AnimatorSet set = new AnimatorSet();
171                 ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, View.ALPHA, 0);
172                 splitAnim.setDuration(duration);
173                 set.addListener(mVisAnimListener.withFinalVisibility(visibility));
174                 set.play(anim).with(splitAnim);
175                 return set;
176             } else {
177                 anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
178                 return anim;
179             }
180         }
181     }
182 
animateToVisibility(int visibility)183     public void animateToVisibility(int visibility) {
184         Animator anim = setupAnimatorToVisibility(visibility, FADE_DURATION);
185         anim.start();
186     }
187 
188     @Override
setVisibility(int visibility)189     public void setVisibility(int visibility) {
190         if (visibility != getVisibility()) {
191             if (mVisibilityAnim != null) {
192                 mVisibilityAnim.end();
193             }
194             super.setVisibility(visibility);
195         }
196     }
197 
showOverflowMenu()198     public boolean showOverflowMenu() {
199         if (mActionMenuPresenter != null) {
200             return mActionMenuPresenter.showOverflowMenu();
201         }
202         return false;
203     }
204 
postShowOverflowMenu()205     public void postShowOverflowMenu() {
206         post(new Runnable() {
207             public void run() {
208                 showOverflowMenu();
209             }
210         });
211     }
212 
hideOverflowMenu()213     public boolean hideOverflowMenu() {
214         if (mActionMenuPresenter != null) {
215             return mActionMenuPresenter.hideOverflowMenu();
216         }
217         return false;
218     }
219 
isOverflowMenuShowing()220     public boolean isOverflowMenuShowing() {
221         if (mActionMenuPresenter != null) {
222             return mActionMenuPresenter.isOverflowMenuShowing();
223         }
224         return false;
225     }
226 
isOverflowMenuShowPending()227     public boolean isOverflowMenuShowPending() {
228         if (mActionMenuPresenter != null) {
229             return mActionMenuPresenter.isOverflowMenuShowPending();
230         }
231         return false;
232     }
233 
isOverflowReserved()234     public boolean isOverflowReserved() {
235         return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved();
236     }
237 
canShowOverflowMenu()238     public boolean canShowOverflowMenu() {
239         return isOverflowReserved() && getVisibility() == VISIBLE;
240     }
241 
dismissPopupMenus()242     public void dismissPopupMenus() {
243         if (mActionMenuPresenter != null) {
244             mActionMenuPresenter.dismissPopupMenus();
245         }
246     }
247 
measureChildView(View child, int availableWidth, int childSpecHeight, int spacing)248     protected int measureChildView(View child, int availableWidth, int childSpecHeight,
249             int spacing) {
250         child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
251                 childSpecHeight);
252 
253         availableWidth -= child.getMeasuredWidth();
254         availableWidth -= spacing;
255 
256         return Math.max(0, availableWidth);
257     }
258 
next(int x, int val, boolean isRtl)259     static protected int next(int x, int val, boolean isRtl) {
260         return isRtl ? x - val : x + val;
261     }
262 
positionChild(View child, int x, int y, int contentHeight, boolean reverse)263     protected int positionChild(View child, int x, int y, int contentHeight, boolean reverse) {
264         int childWidth = child.getMeasuredWidth();
265         int childHeight = child.getMeasuredHeight();
266         int childTop = y + (contentHeight - childHeight) / 2;
267 
268         if (reverse) {
269             child.layout(x - childWidth, childTop, x, childTop + childHeight);
270         } else {
271             child.layout(x, childTop, x + childWidth, childTop + childHeight);
272         }
273 
274         return  (reverse ? -childWidth : childWidth);
275     }
276 
277     protected class VisibilityAnimListener implements Animator.AnimatorListener {
278         private boolean mCanceled = false;
279         int mFinalVisibility;
280 
withFinalVisibility(int visibility)281         public VisibilityAnimListener withFinalVisibility(int visibility) {
282             mFinalVisibility = visibility;
283             return this;
284         }
285 
286         @Override
onAnimationStart(Animator animation)287         public void onAnimationStart(Animator animation) {
288             setVisibility(VISIBLE);
289             mVisibilityAnim = animation;
290             mCanceled = false;
291         }
292 
293         @Override
onAnimationEnd(Animator animation)294         public void onAnimationEnd(Animator animation) {
295             if (mCanceled) return;
296 
297             mVisibilityAnim = null;
298             setVisibility(mFinalVisibility);
299             if (mSplitView != null && mMenuView != null) {
300                 mMenuView.setVisibility(mFinalVisibility);
301             }
302         }
303 
304         @Override
onAnimationCancel(Animator animation)305         public void onAnimationCancel(Animator animation) {
306             mCanceled = true;
307         }
308 
309         @Override
onAnimationRepeat(Animator animation)310         public void onAnimationRepeat(Animator animation) {
311         }
312     }
313 }
314