Merge "Display Cutout: Fix ActionBarOverlayLayout to properly dispatch cutout" into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
177ee3d7a1
@@ -6449,6 +6449,7 @@ Landroid/view/WindowContentFrameStats;->init(J[J[J[J)V
|
||||
Landroid/view/WindowInsets;-><init>(Landroid/graphics/Rect;)V
|
||||
Landroid/view/WindowInsets;->CONSUMED:Landroid/view/WindowInsets;
|
||||
Landroid/view/WindowInsets;->getSystemWindowInsets()Landroid/graphics/Rect;
|
||||
Landroid/view/WindowInsets;->inset(IIII)Landroid/view/WindowInsets;
|
||||
Landroid/view/WindowLeaked;-><init>(Ljava/lang/String;)V
|
||||
Landroid/view/WindowManager$LayoutParams;->backup()V
|
||||
Landroid/view/WindowManager$LayoutParams;->FLAG_SLIPPERY:I
|
||||
|
||||
@@ -9838,26 +9838,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
||||
/**
|
||||
* @hide Compute the insets that should be consumed by this view and the ones
|
||||
* that should propagate to those under it.
|
||||
*
|
||||
* Note: This is used by appcompat's ActionBarOverlayLayout through reflection.
|
||||
*
|
||||
* @param inoutInsets the insets given to this view
|
||||
* @param outLocalInsets the insets that should be applied to this view
|
||||
* @deprecated use {@link #computeSystemWindowInsets}
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
protected boolean computeFitSystemWindows(Rect inoutInsets, Rect outLocalInsets) {
|
||||
if ((mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) == 0
|
||||
|| mAttachInfo == null
|
||||
|| ((mAttachInfo.mSystemUiVisibility & SYSTEM_UI_LAYOUT_FLAGS) == 0
|
||||
&& !mAttachInfo.mOverscanRequested)) {
|
||||
outLocalInsets.set(inoutInsets);
|
||||
inoutInsets.set(0, 0, 0, 0);
|
||||
return true;
|
||||
} else {
|
||||
// The application wants to take care of fitting system window for
|
||||
// the content... however we still need to take care of any overscan here.
|
||||
final Rect overscan = mAttachInfo.mOverscanInsets;
|
||||
outLocalInsets.set(overscan);
|
||||
inoutInsets.left -= overscan.left;
|
||||
inoutInsets.top -= overscan.top;
|
||||
inoutInsets.right -= overscan.right;
|
||||
inoutInsets.bottom -= overscan.bottom;
|
||||
return false;
|
||||
}
|
||||
WindowInsets innerInsets = computeSystemWindowInsets(new WindowInsets(inoutInsets),
|
||||
outLocalInsets);
|
||||
inoutInsets.set(innerInsets.getSystemWindowInsets());
|
||||
return innerInsets.isSystemWindowInsetsConsumed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9873,12 +9867,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
||||
public WindowInsets computeSystemWindowInsets(WindowInsets in, Rect outLocalInsets) {
|
||||
if ((mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) == 0
|
||||
|| mAttachInfo == null
|
||||
|| (mAttachInfo.mSystemUiVisibility & SYSTEM_UI_LAYOUT_FLAGS) == 0) {
|
||||
|| ((mAttachInfo.mSystemUiVisibility & SYSTEM_UI_LAYOUT_FLAGS) == 0
|
||||
&& !mAttachInfo.mOverscanRequested)) {
|
||||
outLocalInsets.set(in.getSystemWindowInsets());
|
||||
return in.consumeSystemWindowInsets();
|
||||
return in.consumeSystemWindowInsets().inset(outLocalInsets);
|
||||
} else {
|
||||
outLocalInsets.set(0, 0, 0, 0);
|
||||
return in;
|
||||
// The application wants to take care of fitting system window for
|
||||
// the content... however we still need to take care of any overscan here.
|
||||
final Rect overscan = mAttachInfo.mOverscanInsets;
|
||||
outLocalInsets.set(overscan);
|
||||
return in.inset(outLocalInsets);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@ package android.view;
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Describes a set of insets for window content.
|
||||
*
|
||||
@@ -27,6 +31,12 @@ import android.graphics.Rect;
|
||||
* To adjust insets, use one of the supplied clone methods to obtain a new WindowInsets instance
|
||||
* with the adjusted properties.</p>
|
||||
*
|
||||
* <p>Note: Before {@link android.os.Build.VERSION_CODES#P P}, WindowInsets instances were only
|
||||
* immutable during a single layout pass (i.e. would return the same values between
|
||||
* {@link View#onApplyWindowInsets} and {@link View#onLayout}, but could return other values
|
||||
* otherwise). Starting with {@link android.os.Build.VERSION_CODES#P P}, WindowInsets are
|
||||
* always immutable and implement equality.
|
||||
*
|
||||
* @see View.OnApplyWindowInsetsListener
|
||||
* @see View#onApplyWindowInsets(WindowInsets)
|
||||
*/
|
||||
@@ -69,13 +79,14 @@ public final class WindowInsets {
|
||||
public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, Rect stableInsets,
|
||||
boolean isRound, boolean alwaysConsumeNavBar, DisplayCutout displayCutout) {
|
||||
mSystemWindowInsetsConsumed = systemWindowInsets == null;
|
||||
mSystemWindowInsets = mSystemWindowInsetsConsumed ? EMPTY_RECT : systemWindowInsets;
|
||||
mSystemWindowInsets = mSystemWindowInsetsConsumed
|
||||
? EMPTY_RECT : new Rect(systemWindowInsets);
|
||||
|
||||
mWindowDecorInsetsConsumed = windowDecorInsets == null;
|
||||
mWindowDecorInsets = mWindowDecorInsetsConsumed ? EMPTY_RECT : windowDecorInsets;
|
||||
mWindowDecorInsets = mWindowDecorInsetsConsumed ? EMPTY_RECT : new Rect(windowDecorInsets);
|
||||
|
||||
mStableInsetsConsumed = stableInsets == null;
|
||||
mStableInsets = mStableInsetsConsumed ? EMPTY_RECT : stableInsets;
|
||||
mStableInsets = mStableInsetsConsumed ? EMPTY_RECT : new Rect(stableInsets);
|
||||
|
||||
mIsRound = isRound;
|
||||
mAlwaysConsumeNavBar = alwaysConsumeNavBar;
|
||||
@@ -535,4 +546,104 @@ public final class WindowInsets {
|
||||
+ (isRound() ? " round" : "")
|
||||
+ "}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this instance inset in the given directions.
|
||||
*
|
||||
* @see #inset(int, int, int, int)
|
||||
* @hide
|
||||
*/
|
||||
public WindowInsets inset(Rect r) {
|
||||
return inset(r.left, r.top, r.right, r.bottom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this instance inset in the given directions.
|
||||
*
|
||||
* This is intended for dispatching insets to areas of the window that are smaller than the
|
||||
* current area.
|
||||
*
|
||||
* <p>Example:
|
||||
* <pre>
|
||||
* childView.dispatchApplyWindowInsets(insets.inset(
|
||||
* childMarginLeft, childMarginTop, childMarginBottom, childMarginRight));
|
||||
* </pre>
|
||||
*
|
||||
* @param left the amount of insets to remove from the left. Must be non-negative.
|
||||
* @param top the amount of insets to remove from the top. Must be non-negative.
|
||||
* @param right the amount of insets to remove from the right. Must be non-negative.
|
||||
* @param bottom the amount of insets to remove from the bottom. Must be non-negative.
|
||||
*
|
||||
* @return the inset insets
|
||||
*
|
||||
* @hide pending API
|
||||
*/
|
||||
public WindowInsets inset(int left, int top, int right, int bottom) {
|
||||
Preconditions.checkArgumentNonnegative(left);
|
||||
Preconditions.checkArgumentNonnegative(top);
|
||||
Preconditions.checkArgumentNonnegative(right);
|
||||
Preconditions.checkArgumentNonnegative(bottom);
|
||||
|
||||
WindowInsets result = new WindowInsets(this);
|
||||
if (!result.mSystemWindowInsetsConsumed) {
|
||||
result.mSystemWindowInsets =
|
||||
insetInsets(result.mSystemWindowInsets, left, top, right, bottom);
|
||||
}
|
||||
if (!result.mWindowDecorInsetsConsumed) {
|
||||
result.mWindowDecorInsets =
|
||||
insetInsets(result.mWindowDecorInsets, left, top, right, bottom);
|
||||
}
|
||||
if (!result.mStableInsetsConsumed) {
|
||||
result.mStableInsets = insetInsets(result.mStableInsets, left, top, right, bottom);
|
||||
}
|
||||
if (mDisplayCutout != null) {
|
||||
result.mDisplayCutout = result.mDisplayCutout.inset(left, top, right, bottom);
|
||||
if (result.mDisplayCutout.isEmpty()) {
|
||||
result.mDisplayCutout = null;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || !(o instanceof WindowInsets)) return false;
|
||||
WindowInsets that = (WindowInsets) o;
|
||||
return mIsRound == that.mIsRound
|
||||
&& mAlwaysConsumeNavBar == that.mAlwaysConsumeNavBar
|
||||
&& mSystemWindowInsetsConsumed == that.mSystemWindowInsetsConsumed
|
||||
&& mWindowDecorInsetsConsumed == that.mWindowDecorInsetsConsumed
|
||||
&& mStableInsetsConsumed == that.mStableInsetsConsumed
|
||||
&& mDisplayCutoutConsumed == that.mDisplayCutoutConsumed
|
||||
&& Objects.equals(mSystemWindowInsets, that.mSystemWindowInsets)
|
||||
&& Objects.equals(mWindowDecorInsets, that.mWindowDecorInsets)
|
||||
&& Objects.equals(mStableInsets, that.mStableInsets)
|
||||
&& Objects.equals(mDisplayCutout, that.mDisplayCutout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mSystemWindowInsets, mWindowDecorInsets, mStableInsets, mIsRound,
|
||||
mDisplayCutout, mAlwaysConsumeNavBar, mSystemWindowInsetsConsumed,
|
||||
mWindowDecorInsetsConsumed, mStableInsetsConsumed, mDisplayCutoutConsumed);
|
||||
}
|
||||
|
||||
private static Rect insetInsets(Rect insets, int left, int top, int right, int bottom) {
|
||||
int newLeft = Math.max(0, insets.left - left);
|
||||
int newTop = Math.max(0, insets.top - top);
|
||||
int newRight = Math.max(0, insets.right - right);
|
||||
int newBottom = Math.max(0, insets.bottom - bottom);
|
||||
if (newLeft == left && newTop == top && newRight == right && newBottom == bottom) {
|
||||
return insets;
|
||||
}
|
||||
return new Rect(newLeft, newTop, newRight, newBottom);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether system window insets have been consumed.
|
||||
*/
|
||||
boolean isSystemWindowInsetsConsumed() {
|
||||
return mSystemWindowInsetsConsumed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -983,14 +983,14 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
|
||||
if (attrs.height == WindowManager.LayoutParams.WRAP_CONTENT) {
|
||||
mFloatingInsets.top = insets.getSystemWindowInsetTop();
|
||||
mFloatingInsets.bottom = insets.getSystemWindowInsetBottom();
|
||||
insets = insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), 0,
|
||||
insets.getSystemWindowInsetRight(), 0);
|
||||
insets = insets.inset(0, insets.getSystemWindowInsetTop(),
|
||||
0, insets.getSystemWindowInsetBottom());
|
||||
}
|
||||
if (mWindow.getAttributes().width == WindowManager.LayoutParams.WRAP_CONTENT) {
|
||||
mFloatingInsets.left = insets.getSystemWindowInsetTop();
|
||||
mFloatingInsets.right = insets.getSystemWindowInsetBottom();
|
||||
insets = insets.replaceSystemWindowInsets(0, insets.getSystemWindowInsetTop(),
|
||||
0, insets.getSystemWindowInsetBottom());
|
||||
insets = insets.inset(insets.getSystemWindowInsetLeft(), 0,
|
||||
insets.getSystemWindowInsetRight(), 0);
|
||||
}
|
||||
}
|
||||
mFrameOffsets.set(insets.getSystemWindowInsets());
|
||||
@@ -1158,11 +1158,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
|
||||
}
|
||||
}
|
||||
if (insets != null) {
|
||||
insets = insets.replaceSystemWindowInsets(
|
||||
insets.getSystemWindowInsetLeft() - consumedLeft,
|
||||
insets.getSystemWindowInsetTop() - consumedTop,
|
||||
insets.getSystemWindowInsetRight() - consumedRight,
|
||||
insets.getSystemWindowInsetBottom() - consumedBottom);
|
||||
insets = insets.inset(consumedLeft, consumedTop, consumedRight, consumedBottom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1383,8 +1379,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
|
||||
// screen_simple_overlay_action_mode.xml).
|
||||
final boolean nonOverlay = (mWindow.getLocalFeaturesPrivate()
|
||||
& (1 << Window.FEATURE_ACTION_MODE_OVERLAY)) == 0;
|
||||
insets = insets.consumeSystemWindowInsets(
|
||||
false, nonOverlay && showStatusGuard /* top */, false, false);
|
||||
if (nonOverlay && showStatusGuard) {
|
||||
insets = insets.inset(0, insets.getSystemWindowInsetTop(), 0, 0);
|
||||
}
|
||||
} else {
|
||||
// reset top margin
|
||||
if (mlp.topMargin != 0) {
|
||||
|
||||
@@ -75,10 +75,10 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar
|
||||
private final Rect mBaseContentInsets = new Rect();
|
||||
private final Rect mLastBaseContentInsets = new Rect();
|
||||
private final Rect mContentInsets = new Rect();
|
||||
private final Rect mBaseInnerInsets = new Rect();
|
||||
private final Rect mLastBaseInnerInsets = new Rect();
|
||||
private final Rect mInnerInsets = new Rect();
|
||||
private final Rect mLastInnerInsets = new Rect();
|
||||
private WindowInsets mBaseInnerInsets = WindowInsets.CONSUMED;
|
||||
private WindowInsets mLastBaseInnerInsets = WindowInsets.CONSUMED;
|
||||
private WindowInsets mInnerInsets = WindowInsets.CONSUMED;
|
||||
private WindowInsets mLastInnerInsets = WindowInsets.CONSUMED;
|
||||
|
||||
private ActionBarVisibilityCallback mActionBarVisibilityCallback;
|
||||
|
||||
@@ -322,11 +322,14 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar
|
||||
changed |= applyInsets(mActionBarBottom, systemInsets, true, false, true, true);
|
||||
}
|
||||
|
||||
mBaseInnerInsets.set(systemInsets);
|
||||
computeFitSystemWindows(mBaseInnerInsets, mBaseContentInsets);
|
||||
// Cannot use the result of computeSystemWindowInsets, because that consumes the
|
||||
// systemWindowInsets. Instead, we do the insetting by the local insets ourselves.
|
||||
computeSystemWindowInsets(insets, mBaseContentInsets);
|
||||
mBaseInnerInsets = insets.inset(mBaseContentInsets);
|
||||
|
||||
if (!mLastBaseInnerInsets.equals(mBaseInnerInsets)) {
|
||||
changed = true;
|
||||
mLastBaseContentInsets.set(mBaseContentInsets);
|
||||
mLastBaseInnerInsets = mBaseInnerInsets;
|
||||
}
|
||||
if (!mLastBaseContentInsets.equals(mBaseContentInsets)) {
|
||||
changed = true;
|
||||
@@ -430,22 +433,29 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar
|
||||
// will still be covered by the action bar if they have requested it to
|
||||
// overlay.
|
||||
mContentInsets.set(mBaseContentInsets);
|
||||
mInnerInsets.set(mBaseInnerInsets);
|
||||
mInnerInsets = mBaseInnerInsets;
|
||||
if (!mOverlayMode && !stable) {
|
||||
mContentInsets.top += topInset;
|
||||
mContentInsets.bottom += bottomInset;
|
||||
// Content view has been shrunk, shrink all insets to match.
|
||||
mInnerInsets = mInnerInsets.inset(0 /* left */, topInset, 0 /* right */, bottomInset);
|
||||
} else {
|
||||
mInnerInsets.top += topInset;
|
||||
mInnerInsets.bottom += bottomInset;
|
||||
// Add ActionBar to system window inset, but leave other insets untouched.
|
||||
mInnerInsets = mInnerInsets.replaceSystemWindowInsets(
|
||||
mInnerInsets.getSystemWindowInsetLeft(),
|
||||
mInnerInsets.getSystemWindowInsetTop() + topInset,
|
||||
mInnerInsets.getSystemWindowInsetRight(),
|
||||
mInnerInsets.getSystemWindowInsetBottom() + bottomInset
|
||||
);
|
||||
}
|
||||
applyInsets(mContent, mContentInsets, true, true, true, true);
|
||||
|
||||
if (!mLastInnerInsets.equals(mInnerInsets)) {
|
||||
// If the inner insets have changed, we need to dispatch this down to
|
||||
// the app's fitSystemWindows(). We do this before measuring the content
|
||||
// the app's onApplyWindowInsets(). We do this before measuring the content
|
||||
// view to keep the same semantics as the normal fitSystemWindows() call.
|
||||
mLastInnerInsets.set(mInnerInsets);
|
||||
mContent.dispatchApplyWindowInsets(new WindowInsets(mInnerInsets));
|
||||
mLastInnerInsets = mInnerInsets;
|
||||
mContent.dispatchApplyWindowInsets(mInnerInsets);
|
||||
}
|
||||
|
||||
measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec, 0);
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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 com.android.internal.widget;
|
||||
|
||||
import static android.view.DisplayCutout.NO_CUTOUT;
|
||||
import static android.view.View.MeasureSpec.EXACTLY;
|
||||
import static android.view.View.MeasureSpec.makeMeasureSpec;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.View;
|
||||
import android.view.View.OnApplyWindowInsetsListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class ActionBarOverlayLayoutTest {
|
||||
|
||||
private static final Rect TOP_INSET_5 = new Rect(0, 5, 0, 0);
|
||||
private static final Rect TOP_INSET_25 = new Rect(0, 25, 0, 0);
|
||||
private static final Rect ZERO_INSET = new Rect(0, 0, 0, 0);
|
||||
private static final DisplayCutout CONSUMED_CUTOUT = null;
|
||||
private static final DisplayCutout CUTOUT_5 = new DisplayCutout(TOP_INSET_5, Arrays.asList(
|
||||
new Rect(100, 0, 200, 5)));
|
||||
private static final int EXACTLY_1000 = makeMeasureSpec(1000, EXACTLY);
|
||||
|
||||
private Context mContext;
|
||||
private TestActionBarOverlayLayout mLayout;
|
||||
|
||||
private ViewGroup mContent;
|
||||
private ViewGroup mActionBarTop;
|
||||
private Toolbar mToolbar;
|
||||
private FakeOnApplyWindowListener mContentInsetsListener;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mContext = InstrumentationRegistry.getContext();
|
||||
mLayout = new TestActionBarOverlayLayout(mContext);
|
||||
mLayout.makeOptionalFitsSystemWindows();
|
||||
|
||||
mContent = createViewGroupWithId(com.android.internal.R.id.content);
|
||||
mContent.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
||||
mLayout.addView(mContent);
|
||||
|
||||
mContentInsetsListener = new FakeOnApplyWindowListener();
|
||||
mContent.setOnApplyWindowInsetsListener(mContentInsetsListener);
|
||||
|
||||
mActionBarTop = new ActionBarContainer(mContext);
|
||||
mActionBarTop.setId(com.android.internal.R.id.action_bar_container);
|
||||
mActionBarTop.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, 20));
|
||||
mLayout.addView(mActionBarTop);
|
||||
mLayout.setActionBarHeight(20);
|
||||
|
||||
mToolbar = new Toolbar(mContext);
|
||||
mToolbar.setId(com.android.internal.R.id.action_bar);
|
||||
mActionBarTop.addView(mToolbar);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void topInset_consumedCutout_stable() {
|
||||
mLayout.setStable(true);
|
||||
mLayout.dispatchApplyWindowInsets(insetsWith(TOP_INSET_5, CONSUMED_CUTOUT));
|
||||
|
||||
assertThat(mContentInsetsListener.captured, nullValue());
|
||||
|
||||
mLayout.measure(EXACTLY_1000, EXACTLY_1000);
|
||||
|
||||
// Action bar height is added to the top inset
|
||||
assertThat(mContentInsetsListener.captured, is(insetsWith(TOP_INSET_25, CONSUMED_CUTOUT)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void topInset_consumedCutout_notStable() {
|
||||
mLayout.dispatchApplyWindowInsets(insetsWith(TOP_INSET_5, CONSUMED_CUTOUT));
|
||||
|
||||
assertThat(mContentInsetsListener.captured, nullValue());
|
||||
|
||||
mLayout.measure(EXACTLY_1000, EXACTLY_1000);
|
||||
|
||||
assertThat(mContentInsetsListener.captured, is(insetsWith(ZERO_INSET, CONSUMED_CUTOUT)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void topInset_noCutout_stable() {
|
||||
mLayout.setStable(true);
|
||||
mLayout.dispatchApplyWindowInsets(insetsWith(TOP_INSET_5, NO_CUTOUT));
|
||||
|
||||
assertThat(mContentInsetsListener.captured, nullValue());
|
||||
|
||||
mLayout.measure(EXACTLY_1000, EXACTLY_1000);
|
||||
|
||||
// Action bar height is added to the top inset
|
||||
assertThat(mContentInsetsListener.captured, is(insetsWith(TOP_INSET_25, NO_CUTOUT)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void topInset_noCutout_notStable() {
|
||||
mLayout.dispatchApplyWindowInsets(insetsWith(TOP_INSET_5, NO_CUTOUT));
|
||||
|
||||
assertThat(mContentInsetsListener.captured, nullValue());
|
||||
|
||||
mLayout.measure(EXACTLY_1000, EXACTLY_1000);
|
||||
|
||||
assertThat(mContentInsetsListener.captured, is(insetsWith(ZERO_INSET, NO_CUTOUT)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void topInset_cutout_stable() {
|
||||
mLayout.setStable(true);
|
||||
mLayout.dispatchApplyWindowInsets(insetsWith(TOP_INSET_5, CUTOUT_5));
|
||||
|
||||
assertThat(mContentInsetsListener.captured, nullValue());
|
||||
|
||||
mLayout.measure(EXACTLY_1000, EXACTLY_1000);
|
||||
|
||||
// Action bar height is added to the top inset
|
||||
assertThat(mContentInsetsListener.captured, is(insetsWith(TOP_INSET_25, CUTOUT_5)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void topInset_cutout_notStable() {
|
||||
mLayout.dispatchApplyWindowInsets(insetsWith(TOP_INSET_5, CUTOUT_5));
|
||||
|
||||
assertThat(mContentInsetsListener.captured, nullValue());
|
||||
|
||||
mLayout.measure(EXACTLY_1000, EXACTLY_1000);
|
||||
|
||||
assertThat(mContentInsetsListener.captured, is(insetsWith(ZERO_INSET, NO_CUTOUT)));
|
||||
}
|
||||
|
||||
private WindowInsets insetsWith(Rect content, DisplayCutout cutout) {
|
||||
return new WindowInsets(content, null, null, false, false, cutout);
|
||||
}
|
||||
|
||||
private ViewGroup createViewGroupWithId(int id) {
|
||||
final FrameLayout v = new FrameLayout(mContext);
|
||||
v.setId(id);
|
||||
return v;
|
||||
}
|
||||
|
||||
static class TestActionBarOverlayLayout extends ActionBarOverlayLayout {
|
||||
private boolean mStable;
|
||||
|
||||
public TestActionBarOverlayLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowInsets computeSystemWindowInsets(WindowInsets in, Rect outLocalInsets) {
|
||||
if (mStable) {
|
||||
// Emulate the effect of makeOptionalFitsSystemWindows, because we can't do that
|
||||
// without being attached to a window.
|
||||
outLocalInsets.setEmpty();
|
||||
return in;
|
||||
}
|
||||
return super.computeSystemWindowInsets(in, outLocalInsets);
|
||||
}
|
||||
|
||||
void setStable(boolean stable) {
|
||||
mStable = stable;
|
||||
setSystemUiVisibility(stable ? SYSTEM_UI_FLAG_LAYOUT_STABLE : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWindowSystemUiVisibility() {
|
||||
return getSystemUiVisibility();
|
||||
}
|
||||
|
||||
void setActionBarHeight(int height) {
|
||||
try {
|
||||
final Field field = ActionBarOverlayLayout.class.getDeclaredField(
|
||||
"mActionBarHeight");
|
||||
field.setAccessible(true);
|
||||
field.setInt(this, height);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class FakeOnApplyWindowListener implements OnApplyWindowInsetsListener {
|
||||
WindowInsets captured;
|
||||
|
||||
@Override
|
||||
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
|
||||
assertNotNull(insets);
|
||||
captured = insets;
|
||||
return v.onApplyWindowInsets(insets);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,7 +159,12 @@ public class ScreenDecorWindowTests {
|
||||
|
||||
updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness,
|
||||
0, PRIVATE_FLAG_IS_SCREEN_DECOR);
|
||||
assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop());
|
||||
|
||||
// TODO: fix test and re-enable assertion.
|
||||
// initialInsets was not actually immutable and just updated to the current insets,
|
||||
// meaning this assertion never actually tested anything. Now that WindowInsets actually is
|
||||
// immutable, it turns out the test was broken.
|
||||
// assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop());
|
||||
|
||||
updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness,
|
||||
PRIVATE_FLAG_IS_SCREEN_DECOR, PRIVATE_FLAG_IS_SCREEN_DECOR);
|
||||
|
||||
Reference in New Issue
Block a user