1/*
2 * Copyright (C) 2010 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 */
16
17package android.support.v7.internal.widget;
18
19import android.content.Context;
20import android.content.res.TypedArray;
21import android.graphics.drawable.Drawable;
22import android.os.Build;
23import android.support.v7.appcompat.R;
24import android.support.v7.internal.VersionUtils;
25import android.support.v7.view.ActionMode;
26import android.util.AttributeSet;
27import android.view.MotionEvent;
28import android.view.View;
29import android.view.ViewGroup;
30import android.widget.FrameLayout;
31
32/**
33 * This class acts as a container for the action bar view and action mode context views.
34 * It applies special styles as needed to help handle animated transitions between them.
35 * @hide
36 */
37public class ActionBarContainer extends FrameLayout {
38    private boolean mIsTransitioning;
39    private View mTabContainer;
40    private View mActionBarView;
41    private View mContextView;
42
43    Drawable mBackground;
44    Drawable mStackedBackground;
45    Drawable mSplitBackground;
46    boolean mIsSplit;
47    boolean mIsStacked;
48    private int mHeight;
49
50    public ActionBarContainer(Context context) {
51        this(context, null);
52    }
53
54    public ActionBarContainer(Context context, AttributeSet attrs) {
55        super(context, attrs);
56
57        // Set a transparent background so that we project appropriately.
58        final Drawable bg = VersionUtils.isAtLeastL()
59                ? new ActionBarBackgroundDrawableV21(this)
60                : new ActionBarBackgroundDrawable(this);
61        setBackgroundDrawable(bg);
62
63        TypedArray a = context.obtainStyledAttributes(attrs,
64                R.styleable.ActionBar);
65        mBackground = a.getDrawable(R.styleable.ActionBar_background);
66        mStackedBackground = a.getDrawable(
67                R.styleable.ActionBar_backgroundStacked);
68        mHeight = a.getDimensionPixelSize(R.styleable.ActionBar_height, -1);
69
70        if (getId() == R.id.split_action_bar) {
71            mIsSplit = true;
72            mSplitBackground = a.getDrawable(R.styleable.ActionBar_backgroundSplit);
73        }
74        a.recycle();
75
76        setWillNotDraw(mIsSplit ? mSplitBackground == null :
77                mBackground == null && mStackedBackground == null);
78    }
79
80    @Override
81    public void onFinishInflate() {
82        super.onFinishInflate();
83        mActionBarView = findViewById(R.id.action_bar);
84        mContextView = findViewById(R.id.action_context_bar);
85    }
86
87    public void setPrimaryBackground(Drawable bg) {
88        if (mBackground != null) {
89            mBackground.setCallback(null);
90            unscheduleDrawable(mBackground);
91        }
92        mBackground = bg;
93        if (bg != null) {
94            bg.setCallback(this);
95            if (mActionBarView != null) {
96                mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(),
97                        mActionBarView.getRight(), mActionBarView.getBottom());
98            }
99        }
100        setWillNotDraw(mIsSplit ? mSplitBackground == null :
101                mBackground == null && mStackedBackground == null);
102        invalidate();
103    }
104
105    public void setStackedBackground(Drawable bg) {
106        if (mStackedBackground != null) {
107            mStackedBackground.setCallback(null);
108            unscheduleDrawable(mStackedBackground);
109        }
110        mStackedBackground = bg;
111        if (bg != null) {
112            bg.setCallback(this);
113            if ((mIsStacked && mStackedBackground != null)) {
114                mStackedBackground.setBounds(mTabContainer.getLeft(), mTabContainer.getTop(),
115                        mTabContainer.getRight(), mTabContainer.getBottom());
116            }
117        }
118        setWillNotDraw(mIsSplit ? mSplitBackground == null :
119                mBackground == null && mStackedBackground == null);
120        invalidate();
121    }
122
123    public void setSplitBackground(Drawable bg) {
124        if (mSplitBackground != null) {
125            mSplitBackground.setCallback(null);
126            unscheduleDrawable(mSplitBackground);
127        }
128        mSplitBackground = bg;
129        if (bg != null) {
130            bg.setCallback(this);
131            if (mIsSplit && mSplitBackground != null) {
132                mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
133            }
134        }
135        setWillNotDraw(mIsSplit ? mSplitBackground == null :
136                mBackground == null && mStackedBackground == null);
137        invalidate();
138    }
139
140    @Override
141    public void setVisibility(int visibility) {
142        super.setVisibility(visibility);
143        final boolean isVisible = visibility == VISIBLE;
144        if (mBackground != null) mBackground.setVisible(isVisible, false);
145        if (mStackedBackground != null) mStackedBackground.setVisible(isVisible, false);
146        if (mSplitBackground != null) mSplitBackground.setVisible(isVisible, false);
147    }
148
149    @Override
150    protected boolean verifyDrawable(Drawable who) {
151        return (who == mBackground && !mIsSplit) || (who == mStackedBackground && mIsStacked) ||
152                (who == mSplitBackground && mIsSplit) || super.verifyDrawable(who);
153    }
154
155    @Override
156    protected void drawableStateChanged() {
157        super.drawableStateChanged();
158        if (mBackground != null && mBackground.isStateful()) {
159            mBackground.setState(getDrawableState());
160        }
161        if (mStackedBackground != null && mStackedBackground.isStateful()) {
162            mStackedBackground.setState(getDrawableState());
163        }
164        if (mSplitBackground != null && mSplitBackground.isStateful()) {
165            mSplitBackground.setState(getDrawableState());
166        }
167    }
168
169    public void jumpDrawablesToCurrentState() {
170        if (Build.VERSION.SDK_INT >= 11) {
171            super.jumpDrawablesToCurrentState();
172            if (mBackground != null) {
173                mBackground.jumpToCurrentState();
174            }
175            if (mStackedBackground != null) {
176                mStackedBackground.jumpToCurrentState();
177            }
178            if (mSplitBackground != null) {
179                mSplitBackground.jumpToCurrentState();
180            }
181        }
182    }
183
184    /**
185     * Set the action bar into a "transitioning" state. While transitioning the bar will block focus
186     * and touch from all of its descendants. This prevents the user from interacting with the bar
187     * while it is animating in or out.
188     *
189     * @param isTransitioning true if the bar is currently transitioning, false otherwise.
190     */
191    public void setTransitioning(boolean isTransitioning) {
192        mIsTransitioning = isTransitioning;
193        setDescendantFocusability(isTransitioning ? FOCUS_BLOCK_DESCENDANTS
194                : FOCUS_AFTER_DESCENDANTS);
195    }
196
197    @Override
198    public boolean onInterceptTouchEvent(MotionEvent ev) {
199        return mIsTransitioning || super.onInterceptTouchEvent(ev);
200    }
201
202    @Override
203    public boolean onTouchEvent(MotionEvent ev) {
204        super.onTouchEvent(ev);
205
206        // An action bar always eats touch events.
207        return true;
208    }
209
210    public void setTabContainer(ScrollingTabContainerView tabView) {
211        if (mTabContainer != null) {
212            removeView(mTabContainer);
213        }
214        mTabContainer = tabView;
215        if (tabView != null) {
216            addView(tabView);
217            final ViewGroup.LayoutParams lp = tabView.getLayoutParams();
218            lp.width = LayoutParams.MATCH_PARENT;
219            lp.height = LayoutParams.WRAP_CONTENT;
220            tabView.setAllowCollapse(false);
221        }
222    }
223
224    public View getTabContainer() {
225        return mTabContainer;
226    }
227
228    //@Override
229    public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
230        // No starting an action mode for an action bar child! (Where would it go?)
231        return null;
232    }
233
234    @Override
235    public android.view.ActionMode startActionModeForChild(View originalView,
236            android.view.ActionMode.Callback callback) {
237        return null;
238    }
239
240    private boolean isCollapsed(View view) {
241        return view == null || view.getVisibility() == GONE || view.getMeasuredHeight() == 0;
242    }
243
244    private int getMeasuredHeightWithMargins(View view) {
245        final LayoutParams lp = (LayoutParams) view.getLayoutParams();
246        return view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
247    }
248
249    @Override
250    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
251        if (mActionBarView == null &&
252                MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST && mHeight >= 0) {
253            heightMeasureSpec = MeasureSpec.makeMeasureSpec(
254                    Math.min(mHeight, MeasureSpec.getSize(heightMeasureSpec)), MeasureSpec.AT_MOST);
255        }
256        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
257
258        if (mActionBarView == null) return;
259
260        final int mode = MeasureSpec.getMode(heightMeasureSpec);
261        if (mTabContainer != null && mTabContainer.getVisibility() != GONE
262                && mode != MeasureSpec.EXACTLY) {
263            final int topMarginForTabs;
264            if (!isCollapsed(mActionBarView)) {
265                topMarginForTabs = getMeasuredHeightWithMargins(mActionBarView);
266            } else if (!isCollapsed(mContextView)) {
267                topMarginForTabs = getMeasuredHeightWithMargins(mContextView);
268            } else {
269                topMarginForTabs = 0;
270            }
271            final int maxHeight = mode == MeasureSpec.AT_MOST ?
272                    MeasureSpec.getSize(heightMeasureSpec) : Integer.MAX_VALUE;
273            setMeasuredDimension(getMeasuredWidth(),
274                    Math.min(topMarginForTabs + getMeasuredHeightWithMargins(mTabContainer),
275                            maxHeight));
276        }
277    }
278
279    @Override
280    public void onLayout(boolean changed, int l, int t, int r, int b) {
281        super.onLayout(changed, l, t, r, b);
282
283        final View tabContainer = mTabContainer;
284        final boolean hasTabs = tabContainer != null && tabContainer.getVisibility() != GONE;
285
286        if (tabContainer != null && tabContainer.getVisibility() != GONE) {
287            final int containerHeight = getMeasuredHeight();
288            final LayoutParams lp = (LayoutParams) tabContainer.getLayoutParams();
289            final int tabHeight = tabContainer.getMeasuredHeight();
290            tabContainer.layout(l, containerHeight - tabHeight - lp.bottomMargin, r,
291                    containerHeight - lp.bottomMargin);
292        }
293
294        boolean needsInvalidate = false;
295        if (mIsSplit) {
296            if (mSplitBackground != null) {
297                mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
298                needsInvalidate = true;
299            }
300        } else {
301            if (mBackground != null) {
302                if (mActionBarView.getVisibility() == View.VISIBLE) {
303                    mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(),
304                            mActionBarView.getRight(), mActionBarView.getBottom());
305                } else if (mContextView != null &&
306                        mContextView.getVisibility() == View.VISIBLE) {
307                    mBackground.setBounds(mContextView.getLeft(), mContextView.getTop(),
308                            mContextView.getRight(), mContextView.getBottom());
309                } else {
310                    mBackground.setBounds(0, 0, 0, 0);
311                }
312                needsInvalidate = true;
313            }
314            mIsStacked = hasTabs;
315            if (hasTabs && mStackedBackground != null) {
316                mStackedBackground.setBounds(tabContainer.getLeft(), tabContainer.getTop(),
317                        tabContainer.getRight(), tabContainer.getBottom());
318                needsInvalidate = true;
319            }
320        }
321
322        if (needsInvalidate) {
323            invalidate();
324        }
325    }
326}
327