1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.server.wm; 18 19 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 20 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 21 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 22 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 23 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; 24 25 import static androidx.test.InstrumentationRegistry.getInstrumentation; 26 27 import static org.junit.Assert.assertEquals; 28 29 import android.graphics.Insets; 30 import android.graphics.Rect; 31 import android.os.Bundle; 32 import android.platform.test.annotations.Presubmit; 33 import android.view.View; 34 import android.view.WindowInsets; 35 import android.view.WindowInsets.Side; 36 import android.view.WindowInsets.Type; 37 import android.view.WindowManager; 38 import android.view.WindowMetrics; 39 40 import androidx.annotation.Nullable; 41 import androidx.test.filters.FlakyTest; 42 43 import com.android.compatibility.common.util.PollingCheck; 44 45 import org.junit.Test; 46 47 /** 48 * Test whether WindowManager performs the correct layout while the app applies the fit-window- 49 * insets APIs. 50 * 51 * Build/Install/Run: 52 * atest CtsWindowManagerDeviceTestCases:WindowInsetsLayoutTests 53 */ 54 @FlakyTest(detail = "Promote once confirmed non-flaky") 55 @Presubmit 56 public class WindowInsetsLayoutTests extends WindowManagerTestBase { 57 58 private final static long TIMEOUT = 1000; // milliseconds 59 60 @Test testSetFitInsetsTypes()61 public void testSetFitInsetsTypes() { 62 // Start the Activity in fullscreen windowing mode for its bounds to match display bounds. 63 final TestActivity activity = 64 startActivityInWindowingMode(TestActivity.class, WINDOWING_MODE_FULLSCREEN); 65 66 // Make sure the main window has been laid out. 67 final View mainWindowRoot = activity.getWindow().getDecorView(); 68 PollingCheck.waitFor(TIMEOUT, () -> mainWindowRoot.getWidth() > 0); 69 70 getInstrumentation().runOnMainSync(() -> { 71 activity.assertMatchesWindowBounds(); 72 }); 73 74 testSetFitInsetsTypesInner(Type.statusBars(), activity, mainWindowRoot); 75 testSetFitInsetsTypesInner(Type.navigationBars(), activity, mainWindowRoot); 76 testSetFitInsetsTypesInner(Type.systemBars(), activity, mainWindowRoot); 77 } 78 testSetFitInsetsTypesInner( int types, TestActivity activity, View mainWindowRoot)79 private void testSetFitInsetsTypesInner( 80 int types, TestActivity activity, View mainWindowRoot) { 81 getInstrumentation().runOnMainSync(() -> { 82 activity.addChildWindow(types, Side.all(), false); 83 }); 84 85 // Make sure the child window has been laid out. 86 final View childWindowRoot = activity.getChildWindowRoot(); 87 PollingCheck.waitFor(TIMEOUT, () -> childWindowRoot.getWidth() > 0); 88 89 getInstrumentation().runOnMainSync(() -> { 90 final WindowInsets windowInsets = mainWindowRoot.getRootWindowInsets(); 91 final Insets insets = windowInsets.getInsets(types); 92 final int[] locationOnScreen = new int[2]; 93 childWindowRoot.getLocationOnScreen(locationOnScreen); 94 assertEquals(insets.left, locationOnScreen[0]); 95 assertEquals(insets.top, locationOnScreen[1]); 96 assertEquals(insets.right, 97 mainWindowRoot.getWidth() - locationOnScreen[0] - childWindowRoot.getWidth()); 98 assertEquals(insets.bottom, 99 mainWindowRoot.getHeight()- locationOnScreen[1] - childWindowRoot.getHeight()); 100 activity.removeChildWindow(); 101 }); 102 } 103 104 @Test testSetFitInsetsSides()105 public void testSetFitInsetsSides() { 106 // Start the Activity in fullscreen windowing mode for its bounds to match display bounds. 107 final TestActivity activity = 108 startActivityInWindowingMode(TestActivity.class, WINDOWING_MODE_FULLSCREEN); 109 110 // Make sure the main window has been laid out. 111 final View mainWindowRoot = activity.getWindow().getDecorView(); 112 PollingCheck.waitFor(TIMEOUT, () -> mainWindowRoot.getWidth() > 0); 113 114 getInstrumentation().runOnMainSync(() -> { 115 activity.assertMatchesWindowBounds(); 116 }); 117 118 testSetFitInsetsSidesInner(Side.LEFT, activity, mainWindowRoot); 119 testSetFitInsetsSidesInner(Side.TOP, activity, mainWindowRoot); 120 testSetFitInsetsSidesInner(Side.RIGHT, activity, mainWindowRoot); 121 testSetFitInsetsSidesInner(Side.BOTTOM, activity, mainWindowRoot); 122 } 123 testSetFitInsetsSidesInner( int sides, TestActivity activity, View mainWindowRoot)124 private void testSetFitInsetsSidesInner( 125 int sides, TestActivity activity, View mainWindowRoot) { 126 final int types = Type.systemBars(); 127 getInstrumentation().runOnMainSync(() -> { 128 activity.addChildWindow(types, sides, false); 129 }); 130 131 // Make sure the child window has been laid out. 132 final View childWindowRoot = activity.getChildWindowRoot(); 133 PollingCheck.waitFor(TIMEOUT, () -> childWindowRoot.getWidth() > 0); 134 135 getInstrumentation().runOnMainSync(() -> { 136 final WindowInsets windowInsets = mainWindowRoot.getRootWindowInsets(); 137 final Insets insets = windowInsets.getInsets(types); 138 final int[] locationOnScreen = new int[2]; 139 childWindowRoot.getLocationOnScreen(locationOnScreen); 140 assertEquals((sides & Side.LEFT) != 0 ? insets.left : 0, locationOnScreen[0]); 141 assertEquals((sides & Side.TOP) != 0 ? insets.top : 0, locationOnScreen[1]); 142 assertEquals((sides & Side.RIGHT) != 0 ? insets.right : 0, 143 mainWindowRoot.getWidth() - locationOnScreen[0] - childWindowRoot.getWidth()); 144 assertEquals((sides & Side.BOTTOM) != 0 ? insets.bottom : 0, 145 mainWindowRoot.getHeight()- locationOnScreen[1] - childWindowRoot.getHeight()); 146 activity.removeChildWindow(); 147 }); 148 } 149 150 @Test testSetFitInsetsIgnoringVisibility()151 public void testSetFitInsetsIgnoringVisibility() { 152 // Start the Activity in fullscreen windowing mode for its bounds to match display bounds. 153 final TestActivity activity = 154 startActivityInWindowingMode(TestActivity.class, WINDOWING_MODE_FULLSCREEN); 155 156 // Make sure the main window has been laid out. 157 final View mainWindowRoot = activity.getWindow().getDecorView(); 158 PollingCheck.waitFor(TIMEOUT, () -> mainWindowRoot.getWidth() > 0); 159 160 final int types = Type.systemBars(); 161 final int sides = Side.all(); 162 final int[] locationAndSize1 = new int[4]; 163 final int[] locationAndSize2 = new int[4]; 164 165 getInstrumentation().runOnMainSync(() -> { 166 activity.assertMatchesWindowBounds(); 167 activity.addChildWindow(types, sides, false); 168 }); 169 170 // Make sure the 1st child window has been laid out. 171 final View childWindowRoot1 = activity.getChildWindowRoot(); 172 PollingCheck.waitFor(TIMEOUT, () -> childWindowRoot1.getWidth() > 0); 173 174 getInstrumentation().runOnMainSync(() -> { 175 childWindowRoot1.getLocationOnScreen(locationAndSize1); 176 locationAndSize1[2] = childWindowRoot1.getWidth(); 177 locationAndSize1[3] = childWindowRoot1.getHeight(); 178 activity.removeChildWindow(); 179 180 mainWindowRoot.getWindowInsetsController().hide(types); 181 182 activity.addChildWindow(types, sides, true); 183 }); 184 185 // Make sure the 2nd child window has been laid out. 186 final View childWindowRoot2 = activity.getChildWindowRoot(); 187 PollingCheck.waitFor(TIMEOUT, () -> childWindowRoot2.getWidth() > 0); 188 189 getInstrumentation().runOnMainSync(() -> { 190 childWindowRoot2.getLocationOnScreen(locationAndSize2); 191 locationAndSize2[2] = childWindowRoot2.getWidth(); 192 locationAndSize2[3] = childWindowRoot2.getHeight(); 193 activity.removeChildWindow(); 194 }); 195 196 for (int i = 0; i < 4; i++) { 197 assertEquals(locationAndSize1[i], locationAndSize2[i]); 198 } 199 } 200 201 public static class TestActivity extends FocusableActivity { 202 203 private View mChildWindowRoot; 204 205 @Override onCreate(@ullable Bundle savedInstanceState)206 protected void onCreate(@Nullable Bundle savedInstanceState) { 207 super.onCreate(savedInstanceState); 208 WindowManager.LayoutParams lp = getWindow().getAttributes(); 209 lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 210 getWindow().setAttributes(lp); 211 } 212 addChildWindow(int types, int sides, boolean ignoreVis)213 void addChildWindow(int types, int sides, boolean ignoreVis) { 214 final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(); 215 attrs.type = TYPE_APPLICATION_PANEL; 216 attrs.width = MATCH_PARENT; 217 attrs.height = MATCH_PARENT; 218 attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 219 attrs.flags = FLAG_NOT_FOCUSABLE; 220 attrs.setFitInsetsTypes(types); 221 attrs.setFitInsetsSides(sides); 222 attrs.setFitInsetsIgnoringVisibility(ignoreVis); 223 mChildWindowRoot = new View(this); 224 getWindowManager().addView(mChildWindowRoot, attrs); 225 } 226 removeChildWindow()227 void removeChildWindow() { 228 getWindowManager().removeViewImmediate(mChildWindowRoot); 229 } 230 getChildWindowRoot()231 View getChildWindowRoot() { 232 return mChildWindowRoot; 233 } 234 assertMatchesWindowBounds()235 void assertMatchesWindowBounds() { 236 final View rootView = getWindow().getDecorView(); 237 final Rect windowMetricsBounds = 238 getWindowManager().getCurrentWindowMetrics().getBounds(); 239 assertEquals(windowMetricsBounds.width(), rootView.getWidth()); 240 assertEquals(windowMetricsBounds.height(), rootView.getHeight()); 241 final int[] locationOnScreen = new int[2]; 242 rootView.getLocationOnScreen(locationOnScreen); 243 assertEquals(locationOnScreen[0] /* expected x */, windowMetricsBounds.left); 244 assertEquals(locationOnScreen[1] /* expected y */, windowMetricsBounds.top); 245 } 246 } 247 } 248