OSDN Git Service

WindowInsets: Never dispatch negative insets
authorAdrian Roos <roosa@google.com>
Thu, 24 May 2018 16:20:51 +0000 (18:20 +0200)
committerAdrian Roos <roosa@google.com>
Fri, 25 May 2018 12:54:09 +0000 (14:54 +0200)
Bug: 80204753
Test: atest ViewRootImplTest
Change-Id: Idace95ae57c6a3a1667b95ce8c3ac7d2bfe06f94

core/java/android/view/ViewRootImpl.java
core/tests/coretests/src/android/view/ViewRootImplTest.java [new file with mode: 0644]

index 7c814f4..ed67075 100644 (file)
@@ -1630,6 +1630,8 @@ public final class ViewRootImpl implements ViewParent,
                         contentInsets.top + outsets.top, contentInsets.right + outsets.right,
                         contentInsets.bottom + outsets.bottom);
             }
+            contentInsets = ensureInsetsNonNegative(contentInsets, "content");
+            stableInsets = ensureInsetsNonNegative(stableInsets, "stable");
             mLastWindowInsets = new WindowInsets(contentInsets,
                     null /* windowDecorInsets */, stableInsets,
                     mContext.getResources().getConfiguration().isScreenRound(),
@@ -1638,6 +1640,17 @@ public final class ViewRootImpl implements ViewParent,
         return mLastWindowInsets;
     }
 
+    private Rect ensureInsetsNonNegative(Rect insets, String kind) {
+        if (insets.left < 0  || insets.top < 0  || insets.right < 0  || insets.bottom < 0) {
+            Log.wtf(mTag, "Negative " + kind + "Insets: " + insets + ", mFirst=" + mFirst);
+            return new Rect(Math.max(0, insets.left),
+                    Math.max(0, insets.top),
+                    Math.max(0, insets.right),
+                    Math.max(0, insets.bottom));
+        }
+        return insets;
+    }
+
     void dispatchApplyInsets(View host) {
         WindowInsets insets = getWindowInsets(true /* forceConstruct */);
         final boolean dispatchCutout = (mWindowAttributes.layoutInDisplayCutoutMode
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
new file mode 100644 (file)
index 0000000..c8e46fc
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ViewRootImplTest {
+
+    private Context mContext;
+    private ViewRootImplAccessor mViewRootImpl;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mViewRootImpl = new ViewRootImplAccessor(
+                    new ViewRootImpl(mContext, mContext.getDisplay()));
+        });
+    }
+
+    @Test
+    public void negativeInsets_areSetToZero() throws Exception {
+        mViewRootImpl.getAttachInfo().getContentInsets().set(-10, -20, -30 , -40);
+        mViewRootImpl.getAttachInfo().getStableInsets().set(-10, -20, -30 , -40);
+        final WindowInsets insets = mViewRootImpl.getWindowInsets(true /* forceConstruct */);
+
+        assertThat(insets.getSystemWindowInsets(), equalTo(new Rect()));
+        assertThat(new Rect(insets.getStableInsetLeft(), insets.getStableInsetTop(),
+                insets.getStableInsetRight(), insets.getStableInsetBottom()), equalTo(new Rect()));
+    }
+
+    @Test
+    public void negativeInsets_areSetToZero_positiveAreLeftAsIs() throws Exception {
+        mViewRootImpl.getAttachInfo().getContentInsets().set(-10, 20, -30 , 40);
+        mViewRootImpl.getAttachInfo().getStableInsets().set(10, -20, 30 , -40);
+        final WindowInsets insets = mViewRootImpl.getWindowInsets(true /* forceConstruct */);
+
+        assertThat(insets.getSystemWindowInsets(), equalTo(new Rect(0, 20, 0, 40)));
+        assertThat(new Rect(insets.getStableInsetLeft(), insets.getStableInsetTop(),
+                insets.getStableInsetRight(), insets.getStableInsetBottom()),
+                equalTo(new Rect(10, 0, 30, 0)));
+    }
+
+    @Test
+    public void positiveInsets_areLeftAsIs() throws Exception {
+        mViewRootImpl.getAttachInfo().getContentInsets().set(10, 20, 30 , 40);
+        mViewRootImpl.getAttachInfo().getStableInsets().set(10, 20, 30 , 40);
+        final WindowInsets insets = mViewRootImpl.getWindowInsets(true /* forceConstruct */);
+
+        assertThat(insets.getSystemWindowInsets(), equalTo(new Rect(10, 20, 30, 40)));
+        assertThat(new Rect(insets.getStableInsetLeft(), insets.getStableInsetTop(),
+                insets.getStableInsetRight(), insets.getStableInsetBottom()),
+                equalTo(new Rect(10, 20, 30, 40)));
+    }
+
+    private static class ViewRootImplAccessor {
+
+        private final ViewRootImpl mViewRootImpl;
+
+        ViewRootImplAccessor(ViewRootImpl viewRootImpl) {
+            mViewRootImpl = viewRootImpl;
+        }
+
+        public ViewRootImpl get() {
+            return mViewRootImpl;
+        }
+
+        AttachInfoAccessor getAttachInfo() throws Exception {
+            return new AttachInfoAccessor(
+                    getField(mViewRootImpl, ViewRootImpl.class.getDeclaredField("mAttachInfo")));
+        }
+
+        WindowInsets getWindowInsets(boolean forceConstruct) throws Exception {
+            return (WindowInsets) invokeMethod(mViewRootImpl,
+                    ViewRootImpl.class.getDeclaredMethod("getWindowInsets", boolean.class),
+                    forceConstruct);
+        }
+
+        class AttachInfoAccessor {
+
+            private final Class<?> mClass;
+            private final Object mAttachInfo;
+
+            AttachInfoAccessor(Object attachInfo) throws Exception {
+                mAttachInfo = attachInfo;
+                mClass = ViewRootImpl.class.getClassLoader().loadClass(
+                        "android.view.View$AttachInfo");
+            }
+
+            Rect getContentInsets() throws Exception {
+                return (Rect) getField(mAttachInfo, mClass.getDeclaredField("mContentInsets"));
+            }
+
+            Rect getStableInsets() throws Exception {
+                return (Rect) getField(mAttachInfo, mClass.getDeclaredField("mStableInsets"));
+            }
+        }
+
+        private static Object getField(Object o, Field field) throws Exception {
+            field.setAccessible(true);
+            return field.get(o);
+        }
+
+        private static Object invokeMethod(Object o, Method method, Object... args)
+                throws Exception {
+            method.setAccessible(true);
+            return method.invoke(o, args);
+        }
+    }
+}