blob: 5469b635b5ae5948f53513d2f97b1b2500181519 [file] [log] [blame]
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -07001/*
2 * Copyright (C) 2012 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
Adam Powell9b0dc282013-07-31 13:58:43 -070019import android.graphics.Canvas;
20import android.graphics.drawable.Drawable;
21import android.os.Build;
Dianne Hackborn1dc5f922013-03-04 18:17:50 -080022import android.view.ViewGroup;
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -070023import com.android.internal.app.ActionBarImpl;
24
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -070025import android.content.Context;
26import android.content.res.TypedArray;
27import android.graphics.Rect;
28import android.util.AttributeSet;
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -070029import android.view.View;
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -070030
31/**
32 * Special layout for the containing of an overlay action bar (and its
33 * content) to correctly handle fitting system windows when the content
34 * has request that its layout ignore them.
35 */
Dianne Hackborn1dc5f922013-03-04 18:17:50 -080036public class ActionBarOverlayLayout extends ViewGroup {
Adam Powell9b0dc282013-07-31 13:58:43 -070037 private static final String TAG = "ActionBarOverlayLayout";
38
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -070039 private int mActionBarHeight;
40 private ActionBarImpl mActionBar;
41 private int mWindowVisibility = View.VISIBLE;
Dianne Hackborn1dc5f922013-03-04 18:17:50 -080042
43 // The main UI elements that we handle the layout of.
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -070044 private View mContent;
Dianne Hackborn1dc5f922013-03-04 18:17:50 -080045 private View mActionBarBottom;
Adam Powell9b0dc282013-07-31 13:58:43 -070046 private ActionBarContainer mActionBarTop;
Dianne Hackborn1dc5f922013-03-04 18:17:50 -080047
48 // Some interior UI elements.
Adam Powell9b0dc282013-07-31 13:58:43 -070049 private ActionBarView mActionBarView;
50
51 // Content overlay drawable - generally the action bar's shadow
52 private Drawable mWindowContentOverlay;
53 private boolean mIgnoreWindowContentOverlay;
Dianne Hackborn1dc5f922013-03-04 18:17:50 -080054
Dianne Hackborndf7221c2013-02-26 14:53:55 -080055 private boolean mOverlayMode;
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -070056 private int mLastSystemUiVisibility;
Dianne Hackborn1dc5f922013-03-04 18:17:50 -080057 private final Rect mBaseContentInsets = new Rect();
58 private final Rect mLastBaseContentInsets = new Rect();
59 private final Rect mContentInsets = new Rect();
60 private final Rect mBaseInnerInsets = new Rect();
61 private final Rect mInnerInsets = new Rect();
62 private final Rect mLastInnerInsets = new Rect();
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -070063
Adam Powell9b0dc282013-07-31 13:58:43 -070064 static final int[] ATTRS = new int [] {
65 com.android.internal.R.attr.actionBarSize,
66 com.android.internal.R.attr.windowContentOverlay
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -070067 };
68
69 public ActionBarOverlayLayout(Context context) {
70 super(context);
71 init(context);
72 }
73
74 public ActionBarOverlayLayout(Context context, AttributeSet attrs) {
75 super(context, attrs);
76 init(context);
77 }
78
79 private void init(Context context) {
Adam Powell9b0dc282013-07-31 13:58:43 -070080 TypedArray ta = getContext().getTheme().obtainStyledAttributes(ATTRS);
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -070081 mActionBarHeight = ta.getDimensionPixelSize(0, 0);
Adam Powell9b0dc282013-07-31 13:58:43 -070082 mWindowContentOverlay = ta.getDrawable(1);
83 setWillNotDraw(mWindowContentOverlay == null);
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -070084 ta.recycle();
Adam Powell9b0dc282013-07-31 13:58:43 -070085
86 mIgnoreWindowContentOverlay = context.getApplicationInfo().targetSdkVersion <
Chet Haasee8222dd2013-09-05 07:44:18 -070087 Build.VERSION_CODES.KITKAT;
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -070088 }
89
Adam Powell9b0dc282013-07-31 13:58:43 -070090 public void setActionBar(ActionBarImpl impl) {
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -070091 mActionBar = impl;
92 if (getWindowToken() != null) {
93 // This is being initialized after being added to a window;
94 // make sure to update all state now.
95 mActionBar.setWindowVisibility(mWindowVisibility);
96 if (mLastSystemUiVisibility != 0) {
97 int newVis = mLastSystemUiVisibility;
98 onWindowSystemUiVisibilityChanged(newVis);
99 requestFitSystemWindows();
100 }
101 }
102 }
103
Adam Powell9b0dc282013-07-31 13:58:43 -0700104 public void setOverlayMode(boolean overlayMode) {
105 mOverlayMode = overlayMode;
106
107 /*
108 * Drawing the window content overlay was broken before K so starting to draw it
109 * again unexpectedly will cause artifacts in some apps. They should fix it.
110 */
111 mIgnoreWindowContentOverlay = overlayMode &&
112 getContext().getApplicationInfo().targetSdkVersion <
Chet Haasee8222dd2013-09-05 07:44:18 -0700113 Build.VERSION_CODES.KITKAT;
Adam Powell9b0dc282013-07-31 13:58:43 -0700114 }
115
Dianne Hackborn139e5aa2012-05-05 20:36:38 -0700116 public void setShowingForActionMode(boolean showing) {
117 if (showing) {
118 // Here's a fun hack: if the status bar is currently being hidden,
119 // and the application has asked for stable content insets, then
120 // we will end up with the action mode action bar being shown
121 // without the status bar, but moved below where the status bar
122 // would be. Not nice. Trying to have this be positioned
123 // correctly is not easy (basically we need yet *another* content
124 // inset from the window manager to know where to put it), so
125 // instead we will just temporarily force the status bar to be shown.
126 if ((getWindowSystemUiVisibility() & (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
127 | SYSTEM_UI_FLAG_LAYOUT_STABLE))
128 == (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_LAYOUT_STABLE)) {
129 setDisabledSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN);
130 }
131 } else {
132 setDisabledSystemUiVisibility(0);
133 }
134 }
135
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700136 @Override
137 public void onWindowSystemUiVisibilityChanged(int visible) {
138 super.onWindowSystemUiVisibilityChanged(visible);
139 pullChildren();
140 final int diff = mLastSystemUiVisibility ^ visible;
141 mLastSystemUiVisibility = visible;
142 final boolean barVisible = (visible&SYSTEM_UI_FLAG_FULLSCREEN) == 0;
Dianne Hackborn139e5aa2012-05-05 20:36:38 -0700143 final boolean wasVisible = mActionBar != null ? mActionBar.isSystemShowing() : true;
Dianne Hackborndf7221c2013-02-26 14:53:55 -0800144 final boolean stable = (visible&SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
Dianne Hackborn139e5aa2012-05-05 20:36:38 -0700145 if (mActionBar != null) {
Dianne Hackborndf7221c2013-02-26 14:53:55 -0800146 // We want the bar to be visible if it is not being hidden,
147 // or the app has not turned on a stable UI mode (meaning they
148 // are performing explicit layout around the action bar).
149 mActionBar.enableContentAnimations(!stable);
150 if (barVisible || !stable) mActionBar.showForSystem();
Dianne Hackborn139e5aa2012-05-05 20:36:38 -0700151 else mActionBar.hideForSystem();
152 }
153 if ((diff&SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700154 if (mActionBar != null) {
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700155 requestFitSystemWindows();
156 }
157 }
158 }
159
160 @Override
161 protected void onWindowVisibilityChanged(int visibility) {
162 super.onWindowVisibilityChanged(visibility);
163 mWindowVisibility = visibility;
164 if (mActionBar != null) {
165 mActionBar.setWindowVisibility(visibility);
166 }
167 }
168
169 private boolean applyInsets(View view, Rect insets, boolean left, boolean top,
170 boolean bottom, boolean right) {
171 boolean changed = false;
Dianne Hackborn1dc5f922013-03-04 18:17:50 -0800172 LayoutParams lp = (LayoutParams)view.getLayoutParams();
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700173 if (left && lp.leftMargin != insets.left) {
174 changed = true;
175 lp.leftMargin = insets.left;
176 }
177 if (top && lp.topMargin != insets.top) {
178 changed = true;
179 lp.topMargin = insets.top;
180 }
181 if (right && lp.rightMargin != insets.right) {
182 changed = true;
183 lp.rightMargin = insets.right;
184 }
185 if (bottom && lp.bottomMargin != insets.bottom) {
186 changed = true;
187 lp.bottomMargin = insets.bottom;
188 }
189 return changed;
190 }
191
192 @Override
193 protected boolean fitSystemWindows(Rect insets) {
194 pullChildren();
195
196 final int vis = getWindowSystemUiVisibility();
197 final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
198
199 // The top and bottom action bars are always within the content area.
200 boolean changed = applyInsets(mActionBarTop, insets, true, true, false, true);
Dianne Hackborn474690c2013-03-06 11:33:26 -0800201 if (mActionBarBottom != null) {
202 changed |= applyInsets(mActionBarBottom, insets, true, false, true, true);
203 }
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700204
Dianne Hackborn1dc5f922013-03-04 18:17:50 -0800205 mBaseInnerInsets.set(insets);
206 computeFitSystemWindows(mBaseInnerInsets, mBaseContentInsets);
207 if (!mLastBaseContentInsets.equals(mBaseContentInsets)) {
208 changed = true;
209 mLastBaseContentInsets.set(mBaseContentInsets);
Dianne Hackborndf7221c2013-02-26 14:53:55 -0800210 }
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700211
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700212 if (changed) {
213 requestLayout();
214 }
215
Dianne Hackborn1dc5f922013-03-04 18:17:50 -0800216 // We don't do any more at this point. To correctly compute the content/inner
217 // insets in all cases, we need to know the measured size of the various action
218 // bar elements. fitSystemWindows() happens before the measure pass, so we can't
219 // do that here. Instead we will take this up in onMeasure().
Dianne Hackbornc4aad012013-02-22 15:05:25 -0800220 return true;
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700221 }
222
Dianne Hackborn1dc5f922013-03-04 18:17:50 -0800223 @Override
224 protected LayoutParams generateDefaultLayoutParams() {
225 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
226 }
227
228 @Override
229 public LayoutParams generateLayoutParams(AttributeSet attrs) {
230 return new LayoutParams(getContext(), attrs);
231 }
232
233 @Override
Dianne Hackborn7cf71fb2013-03-05 18:00:12 -0800234 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
235 return new LayoutParams(p);
236 }
237
238 @Override
239 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
240 return p instanceof LayoutParams;
241 }
242
243 @Override
Dianne Hackborn1dc5f922013-03-04 18:17:50 -0800244 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Dianne Hackborn474690c2013-03-06 11:33:26 -0800245 pullChildren();
246
Dianne Hackborn1dc5f922013-03-04 18:17:50 -0800247 int maxHeight = 0;
248 int maxWidth = 0;
249 int childState = 0;
250
251 int topInset = 0;
252 int bottomInset = 0;
253
254 measureChildWithMargins(mActionBarTop, widthMeasureSpec, 0, heightMeasureSpec, 0);
255 LayoutParams lp = (LayoutParams) mActionBarTop.getLayoutParams();
256 maxWidth = Math.max(maxWidth,
257 mActionBarTop.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
258 maxHeight = Math.max(maxHeight,
259 mActionBarTop.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
260 childState = combineMeasuredStates(childState, mActionBarTop.getMeasuredState());
261
Dianne Hackborn474690c2013-03-06 11:33:26 -0800262 // xlarge screen layout doesn't have bottom action bar.
263 if (mActionBarBottom != null) {
264 measureChildWithMargins(mActionBarBottom, widthMeasureSpec, 0, heightMeasureSpec, 0);
265 lp = (LayoutParams) mActionBarBottom.getLayoutParams();
266 maxWidth = Math.max(maxWidth,
267 mActionBarBottom.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
268 maxHeight = Math.max(maxHeight,
269 mActionBarBottom.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
270 childState = combineMeasuredStates(childState, mActionBarBottom.getMeasuredState());
271 }
Dianne Hackborn1dc5f922013-03-04 18:17:50 -0800272
273 final int vis = getWindowSystemUiVisibility();
274 final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
275
276 if (stable) {
277 // This is the standard space needed for the action bar. For stable measurement,
278 // we can't depend on the size currently reported by it -- this must remain constant.
279 topInset = mActionBarHeight;
280 if (mActionBar != null && mActionBar.hasNonEmbeddedTabs()) {
Adam Powell9b0dc282013-07-31 13:58:43 -0700281 View tabs = mActionBarTop.getTabContainer();
Dianne Hackborn1dc5f922013-03-04 18:17:50 -0800282 if (tabs != null) {
283 // If tabs are not embedded, increase space on top to account for them.
284 topInset += mActionBarHeight;
285 }
286 }
287 } else if (mActionBarTop.getVisibility() == VISIBLE) {
288 // This is the space needed on top of the window for all of the action bar
289 // and tabs.
290 topInset = mActionBarTop.getMeasuredHeight();
291 }
292
Adam Powell9b0dc282013-07-31 13:58:43 -0700293 if (mActionBarView.isSplitActionBar()) {
Dianne Hackborn1dc5f922013-03-04 18:17:50 -0800294 // If action bar is split, adjust bottom insets for it.
Dianne Hackborn474690c2013-03-06 11:33:26 -0800295 if (mActionBarBottom != null) {
296 if (stable) {
297 bottomInset = mActionBarHeight;
298 } else {
299 bottomInset = mActionBarBottom.getMeasuredHeight();
300 }
Dianne Hackborn1dc5f922013-03-04 18:17:50 -0800301 }
302 }
303
304 // If the window has not requested system UI layout flags, we need to
305 // make sure its content is not being covered by system UI... though it
306 // will still be covered by the action bar if they have requested it to
307 // overlay.
308 mContentInsets.set(mBaseContentInsets);
309 mInnerInsets.set(mBaseInnerInsets);
310 if (!mOverlayMode && !stable) {
311 mContentInsets.top += topInset;
312 mContentInsets.bottom += bottomInset;
313 } else {
314 mInnerInsets.top += topInset;
315 mInnerInsets.bottom += bottomInset;
316 }
317 applyInsets(mContent, mContentInsets, true, true, true, true);
318
319 if (!mLastInnerInsets.equals(mInnerInsets)) {
320 // If the inner insets have changed, we need to dispatch this down to
321 // the app's fitSystemWindows(). We do this before measuring the content
322 // view to keep the same semantics as the normal fitSystemWindows() call.
323 mLastInnerInsets.set(mInnerInsets);
324 super.fitSystemWindows(mInnerInsets);
325 }
326
327 measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec, 0);
328 lp = (LayoutParams) mContent.getLayoutParams();
329 maxWidth = Math.max(maxWidth,
330 mContent.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
331 maxHeight = Math.max(maxHeight,
332 mContent.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
333 childState = combineMeasuredStates(childState, mContent.getMeasuredState());
334
335 // Account for padding too
336 maxWidth += getPaddingLeft() + getPaddingRight();
337 maxHeight += getPaddingTop() + getPaddingBottom();
338
339 // Check against our minimum height and width
340 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
341 maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
342
343 setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
344 resolveSizeAndState(maxHeight, heightMeasureSpec,
345 childState << MEASURED_HEIGHT_STATE_SHIFT));
346 }
347
348 @Override
349 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
350 final int count = getChildCount();
351
352 final int parentLeft = getPaddingLeft();
353 final int parentRight = right - left - getPaddingRight();
354
355 final int parentTop = getPaddingTop();
356 final int parentBottom = bottom - top - getPaddingBottom();
357
358 for (int i = 0; i < count; i++) {
359 final View child = getChildAt(i);
360 if (child.getVisibility() != GONE) {
361 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
362
363 final int width = child.getMeasuredWidth();
364 final int height = child.getMeasuredHeight();
365
366 int childLeft = parentLeft + lp.leftMargin;
367 int childTop;
368 if (child == mActionBarBottom) {
369 childTop = parentBottom - height - lp.bottomMargin;
370 } else {
371 childTop = parentTop + lp.topMargin;
372 }
373
374 child.layout(childLeft, childTop, childLeft + width, childTop + height);
375 }
376 }
377 }
378
Dianne Hackborn7cf71fb2013-03-05 18:00:12 -0800379 @Override
Adam Powell9b0dc282013-07-31 13:58:43 -0700380 public void draw(Canvas c) {
381 super.draw(c);
382 if (mWindowContentOverlay != null && !mIgnoreWindowContentOverlay) {
383 final int top = mActionBarTop.getVisibility() == VISIBLE ?
384 (int) (mActionBarTop.getBottom() + mActionBarTop.getTranslationY() + 0.5f) : 0;
385 mWindowContentOverlay.setBounds(0, top, getWidth(),
386 top + mWindowContentOverlay.getIntrinsicHeight());
387 mWindowContentOverlay.draw(c);
388 }
389 }
390
391 @Override
Dianne Hackborn7cf71fb2013-03-05 18:00:12 -0800392 public boolean shouldDelayChildPressedState() {
393 return false;
394 }
395
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700396 void pullChildren() {
397 if (mContent == null) {
398 mContent = findViewById(com.android.internal.R.id.content);
Adam Powell9b0dc282013-07-31 13:58:43 -0700399 mActionBarTop = (ActionBarContainer)findViewById(
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700400 com.android.internal.R.id.action_bar_container);
Adam Powell9b0dc282013-07-31 13:58:43 -0700401 mActionBarView = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700402 mActionBarBottom = findViewById(com.android.internal.R.id.split_action_bar);
403 }
404 }
Dianne Hackborn1dc5f922013-03-04 18:17:50 -0800405
406
407 public static class LayoutParams extends MarginLayoutParams {
408 public LayoutParams(Context c, AttributeSet attrs) {
409 super(c, attrs);
410 }
411
412 public LayoutParams(int width, int height) {
413 super(width, height);
414 }
415
416 public LayoutParams(ViewGroup.LayoutParams source) {
417 super(source);
418 }
419
420 public LayoutParams(ViewGroup.MarginLayoutParams source) {
421 super(source);
422 }
423 }
Dianne Hackborn3a3a6cf2012-03-26 10:24:04 -0700424}