blob: ea18e58fe999abd47f5793845c5cda9279055cbd [file] [log] [blame]
/*
* Copyright (C) 2021 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.server.wm;
import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_RIGHT_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT;
import static android.view.WindowLayout.UNSPECIFIED_LENGTH;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
import static org.junit.Assert.assertEquals;
import android.app.WindowConfiguration;
import android.graphics.Insets;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.InsetsState;
import android.view.InsetsVisibilities;
import android.view.WindowInsets;
import android.view.WindowLayout;
import android.view.WindowManager;
import android.window.ClientWindowFrames;
import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
/**
* Tests for the {@link WindowLayout#computeFrames}.
*
* Build/Install/Run:
* atest WmTests:WindowLayoutTests
*/
@SmallTest
@Presubmit
public class WindowLayoutTests {
private static final int DISPLAY_WIDTH = 500;
private static final int DISPLAY_HEIGHT = 1000;
private static final int STATUS_BAR_HEIGHT = 10;
private static final int NAVIGATION_BAR_HEIGHT = 15;
private static final int IME_HEIGHT = 400;
private static final int DISPLAY_CUTOUT_HEIGHT = 8;
private static final Rect DISPLAY_CUTOUT_BOUNDS_TOP =
new Rect(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT);
private static final Insets WATERFALL_INSETS = Insets.of(6, 0, 12, 0);
private final WindowLayout mWindowLayout = new WindowLayout();
private final ClientWindowFrames mOutFrames = new ClientWindowFrames();
private WindowManager.LayoutParams mAttrs;
private InsetsState mState;
private final Rect mDisplayCutoutSafe = new Rect();
private Rect mWindowBounds;
private int mWindowingMode;
private int mRequestedWidth;
private int mRequestedHeight;
private InsetsVisibilities mRequestedVisibilities;
private Rect mAttachedWindowFrame;
private float mCompatScale;
@Before
public void setUp() {
mAttrs = new WindowManager.LayoutParams();
mState = new InsetsState();
mState.setDisplayFrame(new Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
mState.getSource(ITYPE_STATUS_BAR).setFrame(
0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT);
mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(
0, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT);
mState.getDisplayCutoutSafe(mDisplayCutoutSafe);
mWindowBounds = new Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
mWindowingMode = WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
mRequestedWidth = DISPLAY_WIDTH;
mRequestedHeight = DISPLAY_HEIGHT;
mRequestedVisibilities = new InsetsVisibilities();
mAttachedWindowFrame = null;
mCompatScale = 1f;
}
private void computeFrames() {
mWindowLayout.computeFrames(mAttrs, mState, mDisplayCutoutSafe, mWindowBounds,
mWindowingMode, mRequestedWidth, mRequestedHeight, mRequestedVisibilities,
mAttachedWindowFrame, mCompatScale, mOutFrames);
}
private void addDisplayCutout() {
mState.setDisplayCutout(new DisplayCutout(
Insets.of(WATERFALL_INSETS.left, DISPLAY_CUTOUT_HEIGHT, WATERFALL_INSETS.right, 0),
new Rect(),
DISPLAY_CUTOUT_BOUNDS_TOP,
new Rect(),
new Rect(),
WATERFALL_INSETS));
mState.getDisplayCutoutSafe(mDisplayCutoutSafe);
mState.getSource(ITYPE_LEFT_DISPLAY_CUTOUT).setFrame(
0, 0, mDisplayCutoutSafe.left, DISPLAY_HEIGHT);
mState.getSource(ITYPE_TOP_DISPLAY_CUTOUT).setFrame(
0, 0, DISPLAY_WIDTH, mDisplayCutoutSafe.top);
mState.getSource(ITYPE_RIGHT_DISPLAY_CUTOUT).setFrame(
mDisplayCutoutSafe.right, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
mState.getSource(ITYPE_BOTTOM_DISPLAY_CUTOUT).setFrame(
0, mDisplayCutoutSafe.bottom, DISPLAY_WIDTH, DISPLAY_HEIGHT);
}
private static void assertInsetByTopBottom(int top, int bottom, Rect actual) {
assertInsetBy(0, top, 0, bottom, actual);
}
private static void assertInsetBy(int left, int top, int right, int bottom, Rect actual) {
assertRect(left, top, DISPLAY_WIDTH - right, DISPLAY_HEIGHT - bottom, actual);
}
private static void assertRect(int left, int top, int right, int bottom, Rect actual) {
assertEquals(new Rect(left, top, right, bottom), actual);
}
@Test
public void defaultParams() {
computeFrames();
assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.displayFrame);
assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.parentFrame);
assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.frame);
}
@Test
public void unmeasured() {
mRequestedWidth = UNSPECIFIED_LENGTH;
mRequestedHeight = UNSPECIFIED_LENGTH;
computeFrames();
assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.displayFrame);
assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.parentFrame);
assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.frame);
}
@Test
public void unmeasuredWithSizeSpecifiedInLayoutParams() {
final int width = DISPLAY_WIDTH / 2;
final int height = DISPLAY_HEIGHT / 2;
mRequestedWidth = UNSPECIFIED_LENGTH;
mRequestedHeight = UNSPECIFIED_LENGTH;
mAttrs.width = width;
mAttrs.height = height;
mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
computeFrames();
assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.displayFrame);
assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.parentFrame);
assertRect(0, STATUS_BAR_HEIGHT, width, STATUS_BAR_HEIGHT + height, mOutFrames.frame);
}
@Test
public void nonFullscreenWindowBounds() {
final int top = Math.max(DISPLAY_HEIGHT / 2, STATUS_BAR_HEIGHT);
mWindowBounds.set(0, top, DISPLAY_WIDTH, DISPLAY_HEIGHT);
mRequestedWidth = UNSPECIFIED_LENGTH;
mRequestedHeight = UNSPECIFIED_LENGTH;
computeFrames();
assertRect(0, top, DISPLAY_WIDTH, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT,
mOutFrames.displayFrame);
assertRect(0, top, DISPLAY_WIDTH, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT,
mOutFrames.parentFrame);
assertRect(0, top, DISPLAY_WIDTH, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT,
mOutFrames.frame);
}
@Test
public void fitStatusBars() {
mAttrs.setFitInsetsTypes(WindowInsets.Type.statusBars());
computeFrames();
assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mOutFrames.displayFrame);
assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mOutFrames.parentFrame);
assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mOutFrames.frame);
}
@Test
public void fitNavigationBars() {
mAttrs.setFitInsetsTypes(WindowInsets.Type.navigationBars());
computeFrames();
assertInsetByTopBottom(0, NAVIGATION_BAR_HEIGHT, mOutFrames.displayFrame);
assertInsetByTopBottom(0, NAVIGATION_BAR_HEIGHT, mOutFrames.parentFrame);
assertInsetByTopBottom(0, NAVIGATION_BAR_HEIGHT, mOutFrames.frame);
}
@Test
public void fitZeroTypes() {
mAttrs.setFitInsetsTypes(0);
computeFrames();
assertInsetByTopBottom(0, 0, mOutFrames.displayFrame);
assertInsetByTopBottom(0, 0, mOutFrames.parentFrame);
assertInsetByTopBottom(0, 0, mOutFrames.frame);
}
@Test
public void fitAllSides() {
mAttrs.setFitInsetsSides(WindowInsets.Side.all());
computeFrames();
assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.displayFrame);
assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.parentFrame);
assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.frame);
}
@Test
public void fitTopOnly() {
mAttrs.setFitInsetsSides(WindowInsets.Side.TOP);
computeFrames();
assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mOutFrames.displayFrame);
assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mOutFrames.parentFrame);
assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mOutFrames.frame);
}
@Test
public void fitZeroSides() {
mAttrs.setFitInsetsSides(0);
computeFrames();
assertInsetByTopBottom(0, 0, mOutFrames.displayFrame);
assertInsetByTopBottom(0, 0, mOutFrames.parentFrame);
assertInsetByTopBottom(0, 0, mOutFrames.frame);
}
@Test
public void fitInvisibleInsets() {
mState.getSource(ITYPE_STATUS_BAR).setVisible(false);
mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(false);
computeFrames();
assertInsetByTopBottom(0, 0, mOutFrames.displayFrame);
assertInsetByTopBottom(0, 0, mOutFrames.parentFrame);
assertInsetByTopBottom(0, 0, mOutFrames.frame);
}
@Test
public void fitInvisibleInsetsIgnoringVisibility() {
mState.getSource(ITYPE_STATUS_BAR).setVisible(false);
mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(false);
mAttrs.setFitInsetsIgnoringVisibility(true);
computeFrames();
assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.displayFrame);
assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.parentFrame);
assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.frame);
}
@Test
public void insetParentFrameByIme() {
mState.getSource(InsetsState.ITYPE_IME).setVisible(true);
mState.getSource(InsetsState.ITYPE_IME).setFrame(
0, DISPLAY_HEIGHT - IME_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT);
mAttrs.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
computeFrames();
assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.displayFrame);
assertInsetByTopBottom(STATUS_BAR_HEIGHT, IME_HEIGHT, mOutFrames.parentFrame);
assertInsetByTopBottom(STATUS_BAR_HEIGHT, IME_HEIGHT, mOutFrames.frame);
}
@Test
public void fitDisplayCutout() {
addDisplayCutout();
mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mAttrs.setFitInsetsTypes(WindowInsets.Type.displayCutout());
computeFrames();
assertInsetBy(WATERFALL_INSETS.left, DISPLAY_CUTOUT_HEIGHT, WATERFALL_INSETS.right, 0,
mOutFrames.displayFrame);
assertInsetBy(WATERFALL_INSETS.left, DISPLAY_CUTOUT_HEIGHT, WATERFALL_INSETS.right, 0,
mOutFrames.parentFrame);
assertInsetBy(WATERFALL_INSETS.left, DISPLAY_CUTOUT_HEIGHT, WATERFALL_INSETS.right, 0,
mOutFrames.frame);
}
@Test
public void layoutInDisplayCutoutModeDefault() {
addDisplayCutout();
mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
mAttrs.setFitInsetsTypes(0);
computeFrames();
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
mOutFrames.displayFrame);
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
mOutFrames.parentFrame);
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
mOutFrames.frame);
}
@Test
public void layoutInDisplayCutoutModeDefaultWithLayoutInScreenAndLayoutInsetDecor() {
addDisplayCutout();
mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
mAttrs.setFitInsetsTypes(0);
computeFrames();
assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mOutFrames.displayFrame);
assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mOutFrames.parentFrame);
assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mOutFrames.frame);
}
@Test
public void layoutExtendedToDisplayCutout() {
addDisplayCutout();
final int height = DISPLAY_HEIGHT / 2;
mRequestedHeight = UNSPECIFIED_LENGTH;
mAttrs.height = height;
mAttrs.gravity = Gravity.TOP;
mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mAttrs.setFitInsetsTypes(0);
mAttrs.privateFlags |= PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
computeFrames();
assertRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, mOutFrames.displayFrame);
assertRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, mOutFrames.parentFrame);
assertRect(0, 0, DISPLAY_WIDTH, height + DISPLAY_CUTOUT_HEIGHT, mOutFrames.frame);
}
@Test
public void layoutInDisplayCutoutModeDefaultWithInvisibleSystemBars() {
addDisplayCutout();
mState.getSource(ITYPE_STATUS_BAR).setVisible(false);
mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(false);
mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
mAttrs.setFitInsetsTypes(0);
computeFrames();
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
mOutFrames.displayFrame);
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
mOutFrames.parentFrame);
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
mOutFrames.frame);
}
@Test
public void layoutInDisplayCutoutModeShortEdges() {
addDisplayCutout();
mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
mAttrs.setFitInsetsTypes(0);
computeFrames();
assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mOutFrames.displayFrame);
assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mOutFrames.parentFrame);
assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mOutFrames.frame);
}
@Test
public void layoutInDisplayCutoutModeNever() {
addDisplayCutout();
mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
mAttrs.setFitInsetsTypes(0);
computeFrames();
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
mOutFrames.displayFrame);
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
mOutFrames.parentFrame);
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
mOutFrames.frame);
}
@Test
public void layoutInDisplayCutoutModeAlways() {
addDisplayCutout();
mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mAttrs.setFitInsetsTypes(0);
computeFrames();
assertInsetByTopBottom(0, 0, mOutFrames.displayFrame);
assertInsetByTopBottom(0, 0, mOutFrames.parentFrame);
assertInsetByTopBottom(0, 0, mOutFrames.frame);
}
}