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 */
16package com.android.internal.widget;
17
18import com.android.internal.R;
19
20import android.util.TypedValue;
21import android.view.ContextThemeWrapper;
22import android.widget.ActionMenuPresenter;
23import android.widget.ActionMenuView;
24
25import android.animation.Animator;
26import android.animation.AnimatorSet;
27import android.animation.ObjectAnimator;
28import android.animation.TimeInterpolator;
29import android.content.Context;
30import android.content.res.Configuration;
31import android.content.res.TypedArray;
32import android.util.AttributeSet;
33import android.view.View;
34import android.view.ViewGroup;
35import android.view.animation.DecelerateInterpolator;
36
37public 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
56    public AbsActionBarView(Context context) {
57        this(context, null);
58    }
59
60    public AbsActionBarView(Context context, AttributeSet attrs) {
61        this(context, attrs, 0);
62    }
63
64    public AbsActionBarView(Context context, AttributeSet attrs, int defStyleAttr) {
65        this(context, attrs, defStyleAttr, 0);
66    }
67
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
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     */
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     */
112    public void setSplitWhenNarrow(boolean splitWhenNarrow) {
113        mSplitWhenNarrow = splitWhenNarrow;
114    }
115
116    public void setContentHeight(int height) {
117        mContentHeight = height;
118        requestLayout();
119    }
120
121    public int getContentHeight() {
122        return mContentHeight;
123    }
124
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     */
132    public int getAnimatedVisibility() {
133        if (mVisibilityAnim != null) {
134            return mVisAnimListener.mFinalVisibility;
135        }
136        return getVisibility();
137    }
138
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
183    public void animateToVisibility(int visibility) {
184        Animator anim = setupAnimatorToVisibility(visibility, FADE_DURATION);
185        anim.start();
186    }
187
188    @Override
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
198    public boolean showOverflowMenu() {
199        if (mActionMenuPresenter != null) {
200            return mActionMenuPresenter.showOverflowMenu();
201        }
202        return false;
203    }
204
205    public void postShowOverflowMenu() {
206        post(new Runnable() {
207            public void run() {
208                showOverflowMenu();
209            }
210        });
211    }
212
213    public boolean hideOverflowMenu() {
214        if (mActionMenuPresenter != null) {
215            return mActionMenuPresenter.hideOverflowMenu();
216        }
217        return false;
218    }
219
220    public boolean isOverflowMenuShowing() {
221        if (mActionMenuPresenter != null) {
222            return mActionMenuPresenter.isOverflowMenuShowing();
223        }
224        return false;
225    }
226
227    public boolean isOverflowMenuShowPending() {
228        if (mActionMenuPresenter != null) {
229            return mActionMenuPresenter.isOverflowMenuShowPending();
230        }
231        return false;
232    }
233
234    public boolean isOverflowReserved() {
235        return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved();
236    }
237
238    public boolean canShowOverflowMenu() {
239        return isOverflowReserved() && getVisibility() == VISIBLE;
240    }
241
242    public void dismissPopupMenus() {
243        if (mActionMenuPresenter != null) {
244            mActionMenuPresenter.dismissPopupMenus();
245        }
246    }
247
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
259    static protected int next(int x, int val, boolean isRtl) {
260        return isRtl ? x - val : x + val;
261    }
262
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
281        public VisibilityAnimListener withFinalVisibility(int visibility) {
282            mFinalVisibility = visibility;
283            return this;
284        }
285
286        @Override
287        public void onAnimationStart(Animator animation) {
288            setVisibility(VISIBLE);
289            mVisibilityAnim = animation;
290            mCanceled = false;
291        }
292
293        @Override
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
305        public void onAnimationCancel(Animator animation) {
306            mCanceled = true;
307        }
308
309        @Override
310        public void onAnimationRepeat(Animator animation) {
311        }
312    }
313}
314