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 39 import androidx.annotation.Nullable; 40 41 import com.android.compatibility.common.util.PollingCheck; 42 43 import org.junit.Test; 44 45 /** 46 * Test whether WindowManager performs the correct layout while the app applies the fit-window- 47 * insets APIs. 48 * 49 * Build/Install/Run: 50 * atest CtsWindowManagerDeviceTestCases:WindowInsetsLayoutTests 51 */ 52 @Presubmit 53 @android.server.wm.annotation.Group2 54 public class WindowInsetsLayoutTests extends WindowManagerTestBase { 55 56 private final static long TIMEOUT = 1000; // milliseconds 57 58 @Test testSetFitInsetsTypes()59 public void testSetFitInsetsTypes() { 60 // Start the Activity in fullscreen windowing mode for its bounds to match display bounds. 61 final TestActivity activity = 62 startActivityInWindowingMode(TestActivity.class, WINDOWING_MODE_FULLSCREEN); 63 64 // Make sure the main window has been laid out. 65 final View mainWindowRoot = activity.getWindow().getDecorView(); 66 PollingCheck.waitFor(TIMEOUT, () -> mainWindowRoot.getWidth() > 0); 67 68 getInstrumentation().runOnMainSync(() -> { 69 activity.assertMatchesWindowBounds(); 70 }); 71 72 testSetFitInsetsTypesInner(Type.statusBars(), activity); 73 testSetFitInsetsTypesInner(Type.navigationBars(), activity); 74 testSetFitInsetsTypesInner(Type.systemBars(), activity); 75 } 76 testSetFitInsetsTypesInner(int types, TestActivity activity)77 private void testSetFitInsetsTypesInner(int types, TestActivity activity) { 78 getInstrumentation().runOnMainSync(() -> { 79 activity.addChildWindow(types, Side.all(), false); 80 }); 81 82 // Make sure the child window has been laid out. 83 final View childWindowRoot = activity.getChildWindowRoot(); 84 PollingCheck.waitFor(TIMEOUT, () -> childWindowRoot.getWidth() > 0); 85 86 getInstrumentation().runOnMainSync(() -> { 87 final WindowInsets windowInsets = childWindowRoot.getRootWindowInsets(); 88 final Insets insets = windowInsets.getInsets(types); 89 assertEquals(Insets.NONE, insets); 90 activity.removeChildWindow(); 91 }); 92 } 93 94 @Test testSetFitInsetsSides()95 public void testSetFitInsetsSides() { 96 // Start the Activity in fullscreen windowing mode for its bounds to match display bounds. 97 final TestActivity activity = 98 startActivityInWindowingMode(TestActivity.class, WINDOWING_MODE_FULLSCREEN); 99 100 // Make sure the main window has been laid out. 101 final View mainWindowRoot = activity.getWindow().getDecorView(); 102 PollingCheck.waitFor(TIMEOUT, () -> mainWindowRoot.getWidth() > 0); 103 104 getInstrumentation().runOnMainSync(() -> { 105 activity.assertMatchesWindowBounds(); 106 }); 107 108 testSetFitInsetsSidesInner(Side.LEFT, activity); 109 testSetFitInsetsSidesInner(Side.TOP, activity); 110 testSetFitInsetsSidesInner(Side.RIGHT, activity); 111 testSetFitInsetsSidesInner(Side.BOTTOM, activity); 112 } 113 testSetFitInsetsSidesInner(int sides, TestActivity activity)114 private void testSetFitInsetsSidesInner(int sides, TestActivity activity) { 115 final int types = Type.systemBars(); 116 getInstrumentation().runOnMainSync(() -> { 117 activity.addChildWindow(types, sides, false); 118 }); 119 120 // Make sure the child window has been laid out. 121 final View childWindowRoot = activity.getChildWindowRoot(); 122 PollingCheck.waitFor(TIMEOUT, () -> childWindowRoot.getWidth() > 0); 123 124 getInstrumentation().runOnMainSync(() -> { 125 final WindowInsets windowInsets = childWindowRoot.getRootWindowInsets(); 126 final Insets insets = windowInsets.getInsets(types); 127 if ((sides & Side.LEFT) != 0) { 128 assertEquals(0, insets.left); 129 } 130 if ((sides & Side.TOP) != 0) { 131 assertEquals(0, insets.top); 132 } 133 if ((sides & Side.RIGHT) != 0) { 134 assertEquals(0, insets.right); 135 } 136 if ((sides & Side.BOTTOM) != 0) { 137 assertEquals(0, insets.bottom); 138 } 139 activity.removeChildWindow(); 140 }); 141 } 142 143 @Test testSetFitInsetsIgnoringVisibility()144 public void testSetFitInsetsIgnoringVisibility() { 145 // Start the Activity in fullscreen windowing mode for its bounds to match display bounds. 146 final TestActivity activity = 147 startActivityInWindowingMode(TestActivity.class, WINDOWING_MODE_FULLSCREEN); 148 149 // Make sure the main window has been laid out. 150 final View mainWindowRoot = activity.getWindow().getDecorView(); 151 PollingCheck.waitFor(TIMEOUT, () -> mainWindowRoot.getWidth() > 0); 152 153 final int types = Type.systemBars(); 154 final int sides = Side.all(); 155 final int[] locationAndSize1 = new int[4]; 156 final int[] locationAndSize2 = new int[4]; 157 158 getInstrumentation().runOnMainSync(() -> { 159 activity.assertMatchesWindowBounds(); 160 activity.addChildWindow(types, sides, false); 161 }); 162 163 // Make sure the 1st child window has been laid out. 164 final View childWindowRoot1 = activity.getChildWindowRoot(); 165 PollingCheck.waitFor(TIMEOUT, () -> childWindowRoot1.getWidth() > 0); 166 167 getInstrumentation().runOnMainSync(() -> { 168 childWindowRoot1.getLocationOnScreen(locationAndSize1); 169 locationAndSize1[2] = childWindowRoot1.getWidth(); 170 locationAndSize1[3] = childWindowRoot1.getHeight(); 171 activity.removeChildWindow(); 172 173 mainWindowRoot.getWindowInsetsController().hide(types); 174 175 activity.addChildWindow(types, sides, true); 176 }); 177 178 // Make sure the 2nd child window has been laid out. 179 final View childWindowRoot2 = activity.getChildWindowRoot(); 180 PollingCheck.waitFor(TIMEOUT, () -> childWindowRoot2.getWidth() > 0); 181 182 getInstrumentation().runOnMainSync(() -> { 183 childWindowRoot2.getLocationOnScreen(locationAndSize2); 184 locationAndSize2[2] = childWindowRoot2.getWidth(); 185 locationAndSize2[3] = childWindowRoot2.getHeight(); 186 activity.removeChildWindow(); 187 }); 188 189 for (int i = 0; i < 4; i++) { 190 assertEquals(locationAndSize1[i], locationAndSize2[i]); 191 } 192 } 193 194 public static class TestActivity extends FocusableActivity { 195 196 private View mChildWindowRoot; 197 198 @Override onCreate(@ullable Bundle savedInstanceState)199 protected void onCreate(@Nullable Bundle savedInstanceState) { 200 super.onCreate(savedInstanceState); 201 final View view = new View(this); 202 setContentView(view); 203 WindowManager.LayoutParams lp = getWindow().getAttributes(); 204 lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 205 getWindow().setAttributes(lp); 206 } 207 addChildWindow(int types, int sides, boolean ignoreVis)208 void addChildWindow(int types, int sides, boolean ignoreVis) { 209 final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(); 210 attrs.type = TYPE_APPLICATION_PANEL; 211 attrs.width = MATCH_PARENT; 212 attrs.height = MATCH_PARENT; 213 attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 214 attrs.flags = FLAG_NOT_FOCUSABLE; 215 attrs.setFitInsetsTypes(types); 216 attrs.setFitInsetsSides(sides); 217 attrs.setFitInsetsIgnoringVisibility(ignoreVis); 218 mChildWindowRoot = new View(this); 219 getWindowManager().addView(mChildWindowRoot, attrs); 220 } 221 removeChildWindow()222 void removeChildWindow() { 223 getWindowManager().removeViewImmediate(mChildWindowRoot); 224 } 225 getChildWindowRoot()226 View getChildWindowRoot() { 227 return mChildWindowRoot; 228 } 229 assertMatchesWindowBounds()230 void assertMatchesWindowBounds() { 231 final View rootView = getWindow().getDecorView(); 232 final Rect windowMetricsBounds = 233 getWindowManager().getCurrentWindowMetrics().getBounds(); 234 assertEquals(windowMetricsBounds.width(), rootView.getWidth()); 235 assertEquals(windowMetricsBounds.height(), rootView.getHeight()); 236 final int[] locationOnScreen = new int[2]; 237 rootView.getLocationOnScreen(locationOnScreen); 238 assertEquals(locationOnScreen[0] /* expected x */, windowMetricsBounds.left); 239 assertEquals(locationOnScreen[1] /* expected y */, windowMetricsBounds.top); 240 } 241 } 242 } 243