From 7419a17d9243ddb0629af24d0308797154e44925 Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Thu, 24 May 2018 18:20:51 +0200 Subject: [PATCH] WindowInsets: Never dispatch negative insets Bug: 80204753 Test: atest ViewRootImplTest Change-Id: Idace95ae57c6a3a1667b95ce8c3ac7d2bfe06f94 --- core/java/android/view/ViewRootImpl.java | 13 ++ .../src/android/view/ViewRootImplTest.java | 143 +++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 core/tests/coretests/src/android/view/ViewRootImplTest.java diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7c814f4a5773..ed67075d38d6 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -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 index 000000000000..c8e46fcdf3fd --- /dev/null +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -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); + } + } +} -- 2.11.0