ActionBarContainer.java revision f849a5e16d016fb6ae081a4575ce67f4ce688e3a
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 com.android.internal.widget;
18
19import android.annotation.NonNull;
20import android.content.Context;
21import android.content.res.TypedArray;
22import android.graphics.Canvas;
23import android.graphics.ColorFilter;
24import android.graphics.Outline;
25import android.graphics.PixelFormat;
26import android.graphics.drawable.Drawable;
27import android.util.AttributeSet;
28import android.view.ActionMode;
29import android.view.MotionEvent;
30import android.view.View;
31import android.view.ViewGroup;
32import android.widget.FrameLayout;
33
34/**
35 * This class acts as a container for the action bar view and action mode context views.
36 * It applies special styles as needed to help handle animated transitions between them.
37 * @hide
38 */
39public class ActionBarContainer extends FrameLayout {
40    private boolean mIsTransitioning;
41    private View mTabContainer;
42    private View mActionBarView;
43
44    private Drawable mBackground;
45    private Drawable mStackedBackground;
46    private Drawable mSplitBackground;
47    private boolean mIsSplit;
48    private boolean mIsStacked;
49    private int mHeight;
50
51    public ActionBarContainer(Context context) {
52        this(context, null);
53    }
54
55    public ActionBarContainer(Context context, AttributeSet attrs) {
56        super(context, attrs);
57
58        // Set a transparent background so that we project appropriately.
59        setBackground(new ActionBarBackgroundDrawable());
60
61        TypedArray a = context.obtainStyledAttributes(attrs,
62                com.android.internal.R.styleable.ActionBar);
63        mBackground = a.getDrawable(com.android.internal.R.styleable.ActionBar_background);
64        mStackedBackground = a.getDrawable(
65                com.android.internal.R.styleable.ActionBar_backgroundStacked);
66        mHeight = a.getDimensionPixelSize(com.android.internal.R.styleable.ActionBar_height, -1);
67
68        if (getId() == com.android.internal.R.id.split_action_bar) {
69            mIsSplit = true;
70            mSplitBackground = a.getDrawable(
71                    com.android.internal.R.styleable.ActionBar_backgroundSplit);
72        }
73        a.recycle();
74
75        setWillNotDraw(mIsSplit ? mSplitBackground == null :
76                mBackground == null && mStackedBackground == null);
77    }
78
79    @Override
80    public void onFinishInflate() {
81        super.onFinishInflate();
82        mActionBarView = findViewById(com.android.internal.R.id.action_bar);
83    }
84
85    public void setPrimaryBackground(Drawable bg) {
86        if (mBackground != null) {
87            mBackground.setCallback(null);
88            unscheduleDrawable(mBackground);
89        }
90        mBackground = bg;
91        if (bg != null) {
92            bg.setCallback(this);
93            if (mActionBarView != null) {
94                mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(),
95                        mActionBarView.getRight(), mActionBarView.getBottom());
96            }
97        }
98        setWillNotDraw(mIsSplit ? mSplitBackground == null :
99                mBackground == null && mStackedBackground == null);
100        invalidate();
101    }
102
103    public void setStackedBackground(Drawable bg) {
104        if (mStackedBackground != null) {
105            mStackedBackground.setCallback(null);
106            unscheduleDrawable(mStackedBackground);
107        }
108        mStackedBackground = bg;
109        if (bg != null) {
110            bg.setCallback(this);
111            if ((mIsStacked && mStackedBackground != null)) {
112                mStackedBackground.setBounds(mTabContainer.getLeft(), mTabContainer.getTop(),
113                        mTabContainer.getRight(), mTabContainer.getBottom());
114            }
115        }
116        setWillNotDraw(mIsSplit ? mSplitBackground == null :
117                mBackground == null && mStackedBackground == null);
118        invalidate();
119    }
120
121    public void setSplitBackground(Drawable bg) {
122        if (mSplitBackground != null) {
123            mSplitBackground.setCallback(null);
124            unscheduleDrawable(mSplitBackground);
125        }
126        mSplitBackground = bg;
127        if (bg != null) {
128            bg.setCallback(this);
129            if (mIsSplit && mSplitBackground != null) {
130                mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
131            }
132        }
133        setWillNotDraw(mIsSplit ? mSplitBackground == null :
134                mBackground == null && mStackedBackground == null);
135        invalidate();
136    }
137
138    @Override
139    public void setVisibility(int visibility) {
140        super.setVisibility(visibility);
141        final boolean isVisible = visibility == VISIBLE;
142        if (mBackground != null) mBackground.setVisible(isVisible, false);
143        if (mStackedBackground != null) mStackedBackground.setVisible(isVisible, false);
144        if (mSplitBackground != null) mSplitBackground.setVisible(isVisible, false);
145    }
146
147    @Override
148    protected boolean verifyDrawable(Drawable who) {
149        return (who == mBackground && !mIsSplit) || (who == mStackedBackground && mIsStacked) ||
150                (who == mSplitBackground && mIsSplit) || super.verifyDrawable(who);
151    }
152
153    @Override
154    protected void drawableStateChanged() {
155        super.drawableStateChanged();
156        if (mBackground != null && mBackground.isStateful()) {
157            mBackground.setState(getDrawableState());
158        }
159        if (mStackedBackground != null && mStackedBackground.isStateful()) {
160            mStackedBackground.setState(getDrawableState());
161        }
162        if (mSplitBackground != null && mSplitBackground.isStateful()) {
163            mSplitBackground.setState(getDrawableState());
164        }
165    }
166
167    @Override
168    public void jumpDrawablesToCurrentState() {
169        super.jumpDrawablesToCurrentState();
170        if (mBackground != null) {
171            mBackground.jumpToCurrentState();
172        }
173        if (mStackedBackground != null) {
174            mStackedBackground.jumpToCurrentState();
175        }
176        if (mSplitBackground != null) {
177            mSplitBackground.jumpToCurrentState();
178        }
179    }
180
181    /**
182     * @hide
183     */
184    @Override
185    public void onResolveDrawables(int layoutDirection) {
186        super.onResolveDrawables(layoutDirection);
187        if (mBackground != null) {
188            mBackground.setLayoutDirection(layoutDirection);
189        }
190        if (mStackedBackground != null) {
191            mStackedBackground.setLayoutDirection(layoutDirection);
192        }
193        if (mSplitBackground != null) {
194            mSplitBackground.setLayoutDirection(layoutDirection);
195        }
196    }
197
198    /**
199     * Set the action bar into a "transitioning" state. While transitioning
200     * the bar will block focus and touch from all of its descendants. This
201     * prevents the user from interacting with the bar while it is animating
202     * in or out.
203     *
204     * @param isTransitioning true if the bar is currently transitioning, false otherwise.
205     */
206    public void setTransitioning(boolean isTransitioning) {
207        mIsTransitioning = isTransitioning;
208        setDescendantFocusability(isTransitioning ? FOCUS_BLOCK_DESCENDANTS
209                : FOCUS_AFTER_DESCENDANTS);
210    }
211
212    @Override
213    public boolean onInterceptTouchEvent(MotionEvent ev) {
214        return mIsTransitioning || super.onInterceptTouchEvent(ev);
215    }
216
217    @Override
218    public boolean onTouchEvent(MotionEvent ev) {
219        super.onTouchEvent(ev);
220
221        // An action bar always eats touch events.
222        return true;
223    }
224
225    @Override
226    public boolean onHoverEvent(MotionEvent ev) {
227        super.onHoverEvent(ev);
228
229        // An action bar always eats hover events.
230        return true;
231    }
232
233    public void setTabContainer(ScrollingTabContainerView tabView) {
234        if (mTabContainer != null) {
235            removeView(mTabContainer);
236        }
237        mTabContainer = tabView;
238        if (tabView != null) {
239            addView(tabView);
240            final ViewGroup.LayoutParams lp = tabView.getLayoutParams();
241            lp.width = LayoutParams.MATCH_PARENT;
242            lp.height = LayoutParams.WRAP_CONTENT;
243            tabView.setAllowCollapse(false);
244        }
245    }
246
247    public View getTabContainer() {
248        return mTabContainer;
249    }
250
251    @Override
252    public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
253        // No starting an action mode for an action bar child! (Where would it go?)
254        return null;
255    }
256
257    private static boolean isCollapsed(View view) {
258        return view == null || view.getVisibility() == GONE || view.getMeasuredHeight() == 0;
259    }
260
261    @Override
262    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
263        if (mActionBarView == null &&
264                MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST && mHeight >= 0) {
265            heightMeasureSpec = MeasureSpec.makeMeasureSpec(
266                    Math.min(mHeight, MeasureSpec.getSize(heightMeasureSpec)), MeasureSpec.AT_MOST);
267        }
268        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
269
270        if (mActionBarView == null) return;
271
272        int nonTabMaxHeight = 0;
273        final int childCount = getChildCount();
274        for (int i = 0; i < childCount; i++) {
275            final View child = getChildAt(i);
276            if (child == mTabContainer) {
277                continue;
278            }
279            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
280            nonTabMaxHeight = Math.max(nonTabMaxHeight, isCollapsed(child) ? 0 :
281                    child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
282        }
283
284        if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
285            final int mode = MeasureSpec.getMode(heightMeasureSpec);
286            if (mode == MeasureSpec.AT_MOST) {
287                final int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
288                setMeasuredDimension(getMeasuredWidth(),
289                        Math.min(nonTabMaxHeight + mTabContainer.getMeasuredHeight(),
290                                maxHeight));
291            }
292        }
293    }
294
295    @Override
296    public void onLayout(boolean changed, int l, int t, int r, int b) {
297        super.onLayout(changed, l, t, r, b);
298
299        final View tabContainer = mTabContainer;
300        final boolean hasTabs = tabContainer != null && tabContainer.getVisibility() != GONE;
301
302        if (tabContainer != null && tabContainer.getVisibility() != GONE) {
303            final int containerHeight = getMeasuredHeight();
304            final int tabHeight = tabContainer.getMeasuredHeight();
305            tabContainer.layout(l, containerHeight - tabHeight, r, containerHeight);
306        }
307
308        boolean needsInvalidate = false;
309        if (mIsSplit) {
310            if (mSplitBackground != null) {
311                mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
312                needsInvalidate = true;
313            }
314        } else {
315            if (mBackground != null) {
316                mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(),
317                        mActionBarView.getRight(), mActionBarView.getBottom());
318                needsInvalidate = true;
319            }
320            mIsStacked = hasTabs;
321            if (hasTabs && mStackedBackground != null) {
322                mStackedBackground.setBounds(tabContainer.getLeft(), tabContainer.getTop(),
323                        tabContainer.getRight(), tabContainer.getBottom());
324                needsInvalidate = true;
325            }
326        }
327
328        if (needsInvalidate) {
329            invalidate();
330        }
331    }
332
333    /**
334     * Dummy drawable so that we don't break background display lists and
335     * projection surfaces.
336     */
337    private class ActionBarBackgroundDrawable extends Drawable {
338        @Override
339        public void draw(Canvas canvas) {
340            if (mIsSplit) {
341                if (mSplitBackground != null) {
342                    mSplitBackground.draw(canvas);
343                }
344            } else {
345                if (mBackground != null) {
346                    mBackground.draw(canvas);
347                }
348                if (mStackedBackground != null && mIsStacked) {
349                    mStackedBackground.draw(canvas);
350                }
351            }
352        }
353
354        @Override
355        public void getOutline(@NonNull Outline outline) {
356            if (mIsSplit) {
357                if (mSplitBackground != null) {
358                    mSplitBackground.getOutline(outline);
359                }
360            } else {
361                // ignore the stacked background for shadow casting
362                if (mBackground != null) {
363                    mBackground.getOutline(outline);
364                }
365            }
366        }
367
368        @Override
369        public void setAlpha(int alpha) {
370        }
371
372        @Override
373        public void setColorFilter(ColorFilter cf) {
374        }
375
376        @Override
377        public int getOpacity() {
378            if (mIsSplit) {
379                if (mSplitBackground != null
380                        && mSplitBackground.getOpacity() == PixelFormat.OPAQUE) {
381                    return PixelFormat.OPAQUE;
382                }
383            } else {
384                if (mIsStacked && (mStackedBackground == null
385                        || mStackedBackground.getOpacity() != PixelFormat.OPAQUE)) {
386                    return PixelFormat.UNKNOWN;
387                }
388                if (!isCollapsed(mActionBarView) && mBackground != null
389                        && mBackground.getOpacity() == PixelFormat.OPAQUE) {
390                    return PixelFormat.OPAQUE;
391                }
392            }
393
394            return PixelFormat.UNKNOWN;
395        }
396    }
397}
398