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