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 isOverflowMenuShowPending() {
205        if (mActionMenuPresenter != null) {
206            return mActionMenuPresenter.isOverflowMenuShowPending();
207        }
208        return false;
209    }
210
211    public boolean isOverflowReserved() {
212        return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved();
213    }
214
215    public void dismissPopupMenus() {
216        if (mActionMenuPresenter != null) {
217            mActionMenuPresenter.dismissPopupMenus();
218        }
219    }
220
221    protected int measureChildView(View child, int availableWidth, int childSpecHeight,
222            int spacing) {
223        child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
224                childSpecHeight);
225
226        availableWidth -= child.getMeasuredWidth();
227        availableWidth -= spacing;
228
229        return Math.max(0, availableWidth);
230    }
231
232    static protected int next(int x, int val, boolean isRtl) {
233        return isRtl ? x - val : x + val;
234    }
235
236    protected int positionChild(View child, int x, int y, int contentHeight, boolean reverse) {
237        int childWidth = child.getMeasuredWidth();
238        int childHeight = child.getMeasuredHeight();
239        int childTop = y + (contentHeight - childHeight) / 2;
240
241        if (reverse) {
242            child.layout(x - childWidth, childTop, x, childTop + childHeight);
243        } else {
244            child.layout(x, childTop, x + childWidth, childTop + childHeight);
245        }
246
247        return  (reverse ? -childWidth : childWidth);
248    }
249
250    protected class VisibilityAnimListener implements Animator.AnimatorListener {
251        private boolean mCanceled = false;
252        int mFinalVisibility;
253
254        public VisibilityAnimListener withFinalVisibility(int visibility) {
255            mFinalVisibility = visibility;
256            return this;
257        }
258
259        @Override
260        public void onAnimationStart(Animator animation) {
261            setVisibility(VISIBLE);
262            mVisibilityAnim = animation;
263            mCanceled = false;
264        }
265
266        @Override
267        public void onAnimationEnd(Animator animation) {
268            if (mCanceled) return;
269
270            mVisibilityAnim = null;
271            setVisibility(mFinalVisibility);
272            if (mSplitView != null && mMenuView != null) {
273                mMenuView.setVisibility(mFinalVisibility);
274            }
275        }
276
277        @Override
278        public void onAnimationCancel(Animator animation) {
279            mCanceled = true;
280        }
281
282        @Override
283        public void onAnimationRepeat(Animator animation) {
284        }
285    }
286}
287