From 11335f14b9a3fd0a672a0fa0206e1f58b688dd36 Mon Sep 17 00:00:00 2001 From: Adam Powell Date: Wed, 11 Jun 2014 15:33:08 -0700 Subject: [PATCH] DO NOT MERGE Cherry picking 3 CLs to fix CTS testFitSystemWindows ---- Explicitly track consumed state for WindowInsets Treating 0-insets as fully consumed is incorrect since it means that you can't dispatch empty insets down the view hierarchy - traversal terminates immediately. Track consumed state independent of actual values. Replacing a given set of insets with all zeroes will mark it consumed. ---- Fix incorrect dispatch of empty WindowInsets from ActionBarOverlayLayout Fix a bug where ActionBarOverlayLayout was using a private constructor of WindowInsets to return empty insets that should have been marked fully consumed. This caused dispatch to further child views not to stop appropriately, corrupting application layout in some cases. --- Fix CTS regression in fitSystemWindows Don't attempt to apply null insets from a call to fitSystemWindows. Immediately return false since null insets cannot be applied. ---- Bug: 15452706 Change-Id: I34276a90305b141b4653aef0048f70350c69d02a --- core/java/android/view/View.java | 8 ++- core/java/android/view/ViewGroup.java | 4 +- core/java/android/view/WindowInsets.java | 54 ++++++++++++++++--- .../widget/ActionBarOverlayLayout.java | 10 +++- 4 files changed, 65 insertions(+), 11 deletions(-) diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 4c53df7723fda..d7dc6a45f8039 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5923,12 +5923,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ protected boolean fitSystemWindows(Rect insets) { if ((mPrivateFlags3 & PFLAG3_APPLYING_INSETS) == 0) { + if (insets == null) { + // Null insets by definition have already been consumed. + // This call cannot apply insets since there are none to apply, + // so return false. + return false; + } // If we're not in the process of dispatching the newer apply insets call, // that means we're not in the compatibility path. Dispatch into the newer // apply insets path and take things from there. try { mPrivateFlags3 |= PFLAG3_FITTING_SYSTEM_WINDOWS; - return !dispatchApplyWindowInsets(new WindowInsets(insets)).hasInsets(); + return dispatchApplyWindowInsets(new WindowInsets(insets)).isConsumed(); } finally { mPrivateFlags3 &= PFLAG3_FITTING_SYSTEM_WINDOWS; } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index dda5a608d0d19..cc6f9ab3e1eae 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -5433,11 +5433,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @Override public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { insets = super.dispatchApplyWindowInsets(insets); - if (insets.hasInsets()) { + if (!insets.isConsumed()) { final int count = getChildCount(); for (int i = 0; i < count; i++) { insets = getChildAt(i).dispatchApplyWindowInsets(insets); - if (!insets.hasInsets()) { + if (insets.isConsumed()) { break; } } diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 57e774eb00622..1d2f1bfcbe06d 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -35,6 +35,9 @@ public final class WindowInsets { private Rect mTempRect; private boolean mIsRound; + private boolean mSystemWindowInsetsConsumed = false; + private boolean mWindowDecorInsetsConsumed = false; + private static final Rect EMPTY_RECT = new Rect(0, 0, 0, 0); /** @@ -43,7 +46,13 @@ public final class WindowInsets { * since it would allow them to inadvertently consume unknown insets by returning it. * @hide */ - public static final WindowInsets EMPTY = new WindowInsets(EMPTY_RECT, EMPTY_RECT); + public static final WindowInsets CONSUMED; + + static { + CONSUMED = new WindowInsets(EMPTY_RECT, EMPTY_RECT); + CONSUMED.mSystemWindowInsetsConsumed = true; + CONSUMED.mWindowDecorInsetsConsumed = true; + } /** @hide */ public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets) { @@ -52,13 +61,17 @@ public final class WindowInsets { /** @hide */ public WindowInsets(Rect systemWindowInsets, boolean isRound) { - this(systemWindowInsets, EMPTY_RECT, isRound); + this(systemWindowInsets, null, isRound); } /** @hide */ public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, boolean isRound) { - mSystemWindowInsets = systemWindowInsets; - mWindowDecorInsets = windowDecorInsets; + mSystemWindowInsetsConsumed = systemWindowInsets == null; + mSystemWindowInsets = mSystemWindowInsetsConsumed ? EMPTY_RECT : systemWindowInsets; + + mWindowDecorInsetsConsumed = windowDecorInsets == null; + mWindowDecorInsets = mWindowDecorInsetsConsumed ? EMPTY_RECT : windowDecorInsets; + mIsRound = isRound; } @@ -70,12 +83,14 @@ public final class WindowInsets { public WindowInsets(WindowInsets src) { mSystemWindowInsets = src.mSystemWindowInsets; mWindowDecorInsets = src.mWindowDecorInsets; + mSystemWindowInsetsConsumed = src.mSystemWindowInsetsConsumed; + mWindowDecorInsetsConsumed = src.mWindowDecorInsetsConsumed; mIsRound = src.mIsRound; } /** @hide */ public WindowInsets(Rect systemWindowInsets) { - this(systemWindowInsets, EMPTY_RECT); + this(systemWindowInsets, null); } /** @@ -242,6 +257,24 @@ public final class WindowInsets { return hasSystemWindowInsets() || hasWindowDecorInsets(); } + /** + * Check if these insets have been fully consumed. + * + *

Insets are considered "consumed" if the applicable consume* methods + * have been called such that all insets have been set to zero. This affects propagation of + * insets through the view hierarchy; insets that have not been fully consumed will continue + * to propagate down to child views.

+ * + *

The result of this method is equivalent to the return value of + * {@link View#fitSystemWindows(android.graphics.Rect)}.

+ * + * @return true if the insets have been fully consumed. + * @hide Pending API + */ + public boolean isConsumed() { + return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed; + } + /** * Returns true if the associated window has a round shape. * @@ -263,7 +296,8 @@ public final class WindowInsets { */ public WindowInsets consumeSystemWindowInsets() { final WindowInsets result = new WindowInsets(this); - result.mSystemWindowInsets = new Rect(0, 0, 0, 0); + result.mSystemWindowInsets = EMPTY_RECT; + result.mSystemWindowInsetsConsumed = true; return result; } @@ -281,10 +315,12 @@ public final class WindowInsets { boolean right, boolean bottom) { if (left || top || right || bottom) { final WindowInsets result = new WindowInsets(this); - result.mSystemWindowInsets = new Rect(left ? 0 : mSystemWindowInsets.left, + result.mSystemWindowInsets = new Rect( + left ? 0 : mSystemWindowInsets.left, top ? 0 : mSystemWindowInsets.top, right ? 0 : mSystemWindowInsets.right, bottom ? 0 : mSystemWindowInsets.bottom); + result.mSystemWindowInsetsConsumed = !hasSystemWindowInsets(); return result; } return this; @@ -304,6 +340,7 @@ public final class WindowInsets { int right, int bottom) { final WindowInsets result = new WindowInsets(this); result.mSystemWindowInsets = new Rect(left, top, right, bottom); + result.mSystemWindowInsetsConsumed = !hasSystemWindowInsets(); return result; } @@ -313,6 +350,7 @@ public final class WindowInsets { public WindowInsets consumeWindowDecorInsets() { final WindowInsets result = new WindowInsets(this); result.mWindowDecorInsets.set(0, 0, 0, 0); + result.mWindowDecorInsetsConsumed = true; return result; } @@ -327,6 +365,7 @@ public final class WindowInsets { top ? 0 : mWindowDecorInsets.top, right ? 0 : mWindowDecorInsets.right, bottom ? 0 : mWindowDecorInsets.bottom); + result.mWindowDecorInsetsConsumed = !hasWindowDecorInsets(); return result; } return this; @@ -338,6 +377,7 @@ public final class WindowInsets { public WindowInsets replaceWindowDecorInsets(int left, int top, int right, int bottom) { final WindowInsets result = new WindowInsets(this); result.mWindowDecorInsets = new Rect(left, top, right, bottom); + result.mWindowDecorInsetsConsumed = !hasWindowDecorInsets(); return result; } diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java index c957b673ff597..d4adee19f4bac 100644 --- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java +++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java @@ -16,6 +16,7 @@ package com.android.internal.widget; +import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.Build; @@ -134,6 +135,13 @@ public class ActionBarOverlayLayout extends ViewGroup { } } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + init(getContext()); + requestApplyInsets(); + } + @Override public void onWindowSystemUiVisibilityChanged(int visible) { super.onWindowSystemUiVisibilityChanged(visible); @@ -219,7 +227,7 @@ public class ActionBarOverlayLayout extends ViewGroup { // insets in all cases, we need to know the measured size of the various action // bar elements. onApplyWindowInsets() happens before the measure pass, so we can't // do that here. Instead we will take this up in onMeasure(). - return WindowInsets.EMPTY; + return WindowInsets.CONSUMED; } @Override