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        requestLayout();
99    }
100
101    public int getContentHeight() {
102        return mContentHeight;
103    }
104
105    public void setSplitView(ActionBarContainer splitView) {
106        mSplitView = splitView;
107    }
108
109    /**
110     * @return Current visibility or if animating, the visibility being animated to.
111     */
112    public int getAnimatedVisibility() {
113        if (mVisibilityAnim != null) {
114            return mVisAnimListener.mFinalVisibility;
115        }
116        return getVisibility();
117    }
118
119    public void animateToVisibility(int visibility) {
120        if (mVisibilityAnim != null) {
121            mVisibilityAnim.cancel();
122        }
123        if (visibility == VISIBLE) {
124            if (getVisibility() != VISIBLE) {
125                setAlpha(0);
126                if (mSplitView != null && mMenuView != null) {
127                    mMenuView.setAlpha(0);
128                }
129            }
130            ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1);
131            anim.setDuration(FADE_DURATION);
132            anim.setInterpolator(sAlphaInterpolator);
133            if (mSplitView != null && mMenuView != null) {
134                AnimatorSet set = new AnimatorSet();
135                ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, "alpha", 1);
136                splitAnim.setDuration(FADE_DURATION);
137                set.addListener(mVisAnimListener.withFinalVisibility(visibility));
138                set.play(anim).with(splitAnim);
139                set.start();
140            } else {
141                anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
142                anim.start();
143            }
144        } else {
145            ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0);
146            anim.setDuration(FADE_DURATION);
147            anim.setInterpolator(sAlphaInterpolator);
148            if (mSplitView != null && mMenuView != null) {
149                AnimatorSet set = new AnimatorSet();
150                ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, "alpha", 0);
151                splitAnim.setDuration(FADE_DURATION);
152                set.addListener(mVisAnimListener.withFinalVisibility(visibility));
153                set.play(anim).with(splitAnim);
154                set.start();
155            } else {
156                anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
157                anim.start();
158            }
159        }
160    }
161
162    @Override
163    public void setVisibility(int visibility) {
164        if (mVisibilityAnim != null) {
165            mVisibilityAnim.end();
166        }
167        super.setVisibility(visibility);
168    }
169
170    public boolean showOverflowMenu() {
171        if (mActionMenuPresenter != null) {
172            return mActionMenuPresenter.showOverflowMenu();
173        }
174        return false;
175    }
176
177    public void postShowOverflowMenu() {
178        post(new Runnable() {
179            public void run() {
180                showOverflowMenu();
181            }
182        });
183    }
184
185    public boolean hideOverflowMenu() {
186        if (mActionMenuPresenter != null) {
187            return mActionMenuPresenter.hideOverflowMenu();
188        }
189        return false;
190    }
191
192    public boolean isOverflowMenuShowing() {
193        if (mActionMenuPresenter != null) {
194            return mActionMenuPresenter.isOverflowMenuShowing();
195        }
196        return false;
197    }
198
199    public boolean isOverflowReserved() {
200        return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved();
201    }
202
203    public void dismissPopupMenus() {
204        if (mActionMenuPresenter != null) {
205            mActionMenuPresenter.dismissPopupMenus();
206        }
207    }
208
209    protected int measureChildView(View child, int availableWidth, int childSpecHeight,
210            int spacing) {
211        child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
212                childSpecHeight);
213
214        availableWidth -= child.getMeasuredWidth();
215        availableWidth -= spacing;
216
217        return Math.max(0, availableWidth);
218    }
219
220    protected int positionChild(View child, int x, int y, int contentHeight) {
221        int childWidth = child.getMeasuredWidth();
222        int childHeight = child.getMeasuredHeight();
223        int childTop = y + (contentHeight - childHeight) / 2;
224
225        child.layout(x, childTop, x + childWidth, childTop + childHeight);
226
227        return childWidth;
228    }
229
230    protected int positionChildInverse(View child, int x, int y, int contentHeight) {
231        int childWidth = child.getMeasuredWidth();
232        int childHeight = child.getMeasuredHeight();
233        int childTop = y + (contentHeight - childHeight) / 2;
234
235        child.layout(x - childWidth, childTop, x, childTop + childHeight);
236
237        return childWidth;
238    }
239
240    protected class VisibilityAnimListener implements Animator.AnimatorListener {
241        private boolean mCanceled = false;
242        int mFinalVisibility;
243
244        public VisibilityAnimListener withFinalVisibility(int visibility) {
245            mFinalVisibility = visibility;
246            return this;
247        }
248
249        @Override
250        public void onAnimationStart(Animator animation) {
251            setVisibility(VISIBLE);
252            mVisibilityAnim = animation;
253            mCanceled = false;
254        }
255
256        @Override
257        public void onAnimationEnd(Animator animation) {
258            if (mCanceled) return;
259
260            mVisibilityAnim = null;
261            setVisibility(mFinalVisibility);
262            if (mSplitView != null && mMenuView != null) {
263                mMenuView.setVisibility(mFinalVisibility);
264            }
265        }
266
267        @Override
268        public void onAnimationCancel(Animator animation) {
269            mCanceled = true;
270        }
271
272        @Override
273        public void onAnimationRepeat(Animator animation) {
274        }
275    }
276}
277