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 android.support.v7.internal.widget;
18 
19 import android.content.Context;
20 import android.content.res.TypedArray;
21 import android.graphics.drawable.Drawable;
22 import android.os.Build;
23 import android.support.v7.appcompat.R;
24 import android.support.v7.internal.VersionUtils;
25 import android.support.v7.view.ActionMode;
26 import android.util.AttributeSet;
27 import android.view.MotionEvent;
28 import android.view.View;
29 import android.view.ViewGroup;
30 import 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  */
37 public 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 
ActionBarContainer(Context context)50     public ActionBarContainer(Context context) {
51         this(context, null);
52     }
53 
ActionBarContainer(Context context, AttributeSet attrs)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
onFinishInflate()81     public void onFinishInflate() {
82         super.onFinishInflate();
83         mActionBarView = findViewById(R.id.action_bar);
84         mContextView = findViewById(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                 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 
setStackedBackground(Drawable bg)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 
setSplitBackground(Drawable bg)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
setVisibility(int visibility)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
verifyDrawable(Drawable who)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
drawableStateChanged()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 
jumpDrawablesToCurrentState()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      */
setTransitioning(boolean isTransitioning)191     public void setTransitioning(boolean isTransitioning) {
192         mIsTransitioning = isTransitioning;
193         setDescendantFocusability(isTransitioning ? FOCUS_BLOCK_DESCENDANTS
194                 : FOCUS_AFTER_DESCENDANTS);
195     }
196 
197     @Override
onInterceptTouchEvent(MotionEvent ev)198     public boolean onInterceptTouchEvent(MotionEvent ev) {
199         return mIsTransitioning || super.onInterceptTouchEvent(ev);
200     }
201 
202     @Override
onTouchEvent(MotionEvent ev)203     public boolean onTouchEvent(MotionEvent ev) {
204         super.onTouchEvent(ev);
205 
206         // An action bar always eats touch events.
207         return true;
208     }
209 
setTabContainer(ScrollingTabContainerView tabView)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 
getTabContainer()224     public View getTabContainer() {
225         return mTabContainer;
226     }
227 
228     //@Override
startActionModeForChild(View child, ActionMode.Callback callback)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
startActionModeForChild(View originalView, android.view.ActionMode.Callback callback)235     public android.view.ActionMode startActionModeForChild(View originalView,
236             android.view.ActionMode.Callback callback) {
237         return null;
238     }
239 
isCollapsed(View view)240     private boolean isCollapsed(View view) {
241         return view == null || view.getVisibility() == GONE || view.getMeasuredHeight() == 0;
242     }
243 
getMeasuredHeightWithMargins(View view)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
onMeasure(int widthMeasureSpec, int heightMeasureSpec)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
onLayout(boolean changed, int l, int t, int r, int b)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