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