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