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.

Bug 15341653

Change-Id: I55b33b7dfbf4cae1e906a82140537156cffdbf47
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 117fe8e..7c70ee4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6105,7 +6105,7 @@
             // 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 0f40ee7..eef09ae 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -5574,11 +5574,11 @@
     @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 294f472..3a1e826 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -35,6 +35,9 @@
     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);
 
     /**
@@ -52,13 +55,17 @@
 
     /** @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 +77,14 @@
     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);
     }
 
     /**
@@ -238,6 +247,24 @@
     }
 
     /**
+     * Check if these insets have been fully consumed.
+     *
+     * <p>Insets are considered "consumed" if the applicable <code>consume*</code> 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.</p>
+     *
+     * <p>The result of this method is equivalent to the return value of
+     * {@link View#fitSystemWindows(android.graphics.Rect)}.</p>
+     *
+     * @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.
      *
      * <p>A round window's left, top, right and bottom edges reach all the way to the
@@ -258,7 +285,8 @@
      */
     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;
     }
 
@@ -276,10 +304,12 @@
             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;
@@ -299,6 +329,7 @@
             int right, int bottom) {
         final WindowInsets result = new WindowInsets(this);
         result.mSystemWindowInsets = new Rect(left, top, right, bottom);
+        result.mSystemWindowInsetsConsumed = !hasSystemWindowInsets();
         return result;
     }
 
@@ -308,6 +339,7 @@
     public WindowInsets consumeWindowDecorInsets() {
         final WindowInsets result = new WindowInsets(this);
         result.mWindowDecorInsets.set(0, 0, 0, 0);
+        result.mWindowDecorInsetsConsumed = true;
         return result;
     }
 
@@ -322,6 +354,7 @@
                     top ? 0 : mWindowDecorInsets.top,
                     right ? 0 : mWindowDecorInsets.right,
                     bottom ? 0 : mWindowDecorInsets.bottom);
+            result.mWindowDecorInsetsConsumed = !hasWindowDecorInsets();
             return result;
         }
         return this;
@@ -333,6 +366,7 @@
     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;
     }