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