1 /* 2 * Copyright (C) 2016 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.server.wm.ComponentNameUtils.getWindowName; 20 import static android.server.wm.DialogFrameTestActivity.DIALOG_WINDOW_NAME; 21 import static android.server.wm.DialogFrameTestActivity.TEST_EXPLICIT_POSITION_MATCH_PARENT; 22 import static android.server.wm.DialogFrameTestActivity.TEST_EXPLICIT_POSITION_MATCH_PARENT_NO_LIMITS; 23 import static android.server.wm.DialogFrameTestActivity.TEST_EXPLICIT_SIZE; 24 import static android.server.wm.DialogFrameTestActivity.TEST_EXPLICIT_SIZE_BOTTOM_RIGHT_GRAVITY; 25 import static android.server.wm.DialogFrameTestActivity.TEST_EXPLICIT_SIZE_TOP_LEFT_GRAVITY; 26 import static android.server.wm.DialogFrameTestActivity.TEST_MATCH_PARENT; 27 import static android.server.wm.DialogFrameTestActivity.TEST_NO_FOCUS; 28 import static android.server.wm.DialogFrameTestActivity.TEST_OVER_SIZED_DIMENSIONS; 29 import static android.server.wm.DialogFrameTestActivity.TEST_OVER_SIZED_DIMENSIONS_NO_LIMITS; 30 import static android.server.wm.DialogFrameTestActivity.TEST_WITH_MARGINS; 31 32 import static androidx.test.InstrumentationRegistry.getInstrumentation; 33 34 import static org.hamcrest.MatcherAssert.assertThat; 35 import static org.hamcrest.Matchers.greaterThan; 36 import static org.junit.Assert.assertEquals; 37 38 import android.content.ComponentName; 39 import android.graphics.Insets; 40 import android.graphics.Rect; 41 import android.platform.test.annotations.AppModeFull; 42 import android.platform.test.annotations.Presubmit; 43 import android.server.wm.WindowManagerState.WindowState; 44 import android.view.WindowInsets; 45 46 import androidx.test.rule.ActivityTestRule; 47 48 import org.junit.Ignore; 49 import org.junit.Rule; 50 import org.junit.Test; 51 52 import java.util.List; 53 54 /** 55 * Build/Install/Run: 56 * atest CtsWindowManagerDeviceTestCases:DialogFrameTests 57 * 58 * TODO: Consolidate this class with {@link ParentChildTestBase}. 59 */ 60 @AppModeFull(reason = "Requires android.permission.MANAGE_ACTIVITY_TASKS") 61 @Presubmit 62 public class DialogFrameTests extends ParentChildTestBase<DialogFrameTestActivity> { 63 64 private static final ComponentName DIALOG_FRAME_TEST_ACTIVITY = new ComponentName( 65 getInstrumentation().getContext(), DialogFrameTestActivity.class); 66 private Insets mContentInsets; 67 68 @Rule 69 public final ActivityTestRule<DialogFrameTestActivity> mDialogTestActivity = 70 new ActivityTestRule<>(DialogFrameTestActivity.class, false /* initialTOuchMode */, 71 false /* launchActivity */); 72 73 @Override activityName()74 ComponentName activityName() { 75 return DIALOG_FRAME_TEST_ACTIVITY; 76 } 77 78 @Override activityRule()79 ActivityTestRule<DialogFrameTestActivity> activityRule() { 80 return mDialogTestActivity; 81 } 82 getSingleWindow(final String windowName)83 private WindowState getSingleWindow(final String windowName) { 84 final List<WindowState> windowList = 85 mWmState.getMatchingVisibleWindowState(windowName); 86 assertThat(windowList.size(), greaterThan(0)); 87 return windowList.get(0); 88 } 89 90 @Override doSingleTest(ParentChildTest t)91 void doSingleTest(ParentChildTest t) throws Exception { 92 mWmState.computeState(WaitForValidActivityState.forWindow(DIALOG_WINDOW_NAME)); 93 WindowState dialog = getSingleWindow(DIALOG_WINDOW_NAME); 94 WindowState parent = getSingleWindow(getWindowName(activityName())); 95 96 t.doTest(parent, dialog); 97 } 98 99 // With Width and Height as MATCH_PARENT we should fill 100 // the same content frame as the main activity window 101 @Test testMatchParentDialog()102 public void testMatchParentDialog() throws Exception { 103 doParentChildTest(TEST_MATCH_PARENT, (parent, dialog) -> { ; 104 assertEquals(getParentFrameWithInsets(parent), dialog.getFrame()); 105 }); 106 } 107 108 private static final int explicitDimension = 200; 109 110 // The default gravity for dialogs should center them. 111 @Test testExplicitSizeDefaultGravity()112 public void testExplicitSizeDefaultGravity() throws Exception { 113 doParentChildTest(TEST_EXPLICIT_SIZE, (parent, dialog) -> { 114 Rect parentFrame = getParentFrameWithInsets(parent); 115 Rect expectedFrame = new Rect( 116 parentFrame.left + (parentFrame.width() - explicitDimension) / 2, 117 parentFrame.top + (parentFrame.height() - explicitDimension) / 2, 118 parentFrame.left + (parentFrame.width() + explicitDimension) / 2, 119 parentFrame.top + (parentFrame.height() + explicitDimension) / 2); 120 assertEquals(expectedFrame, dialog.getFrame()); 121 }); 122 } 123 124 @Test testExplicitSizeTopLeftGravity()125 public void testExplicitSizeTopLeftGravity() throws Exception { 126 doParentChildTest(TEST_EXPLICIT_SIZE_TOP_LEFT_GRAVITY, (parent, dialog) -> { 127 Rect parentFrame = getParentFrameWithInsets(parent); 128 Rect expectedFrame = new Rect( 129 parentFrame.left, 130 parentFrame.top, 131 parentFrame.left + explicitDimension, 132 parentFrame.top + explicitDimension); 133 assertEquals(expectedFrame, dialog.getFrame()); 134 }); 135 } 136 137 @Test testExplicitSizeBottomRightGravity()138 public void testExplicitSizeBottomRightGravity() throws Exception { 139 doParentChildTest(TEST_EXPLICIT_SIZE_BOTTOM_RIGHT_GRAVITY, (parent, dialog) -> { 140 Rect parentFrame = getParentFrameWithInsets(parent); 141 Rect expectedFrame = new Rect( 142 parentFrame.left + parentFrame.width() - explicitDimension, 143 parentFrame.top + parentFrame.height() - explicitDimension, 144 parentFrame.left + parentFrame.width(), 145 parentFrame.top + parentFrame.height()); 146 assertEquals(expectedFrame, dialog.getFrame()); 147 }); 148 } 149 150 // TODO(b/30127373): Commented out for now because it doesn't work. We end up insetting the 151 // decor on the bottom. I think this is a bug probably in the default dialog flags: 152 @Ignore 153 @Test testOversizedDimensions()154 public void testOversizedDimensions() throws Exception { 155 doParentChildTest(TEST_OVER_SIZED_DIMENSIONS, (parent, dialog) -> 156 // With the default flags oversize should result in clipping to 157 // parent frame. 158 assertEquals(getParentFrameWithInsets(parent), dialog.getFrame()) 159 ); 160 } 161 162 // TODO(b/63993863) : Disabled pending public API to fetch maximum surface size. 163 static final int oversizedDimension = 5000; 164 // With FLAG_LAYOUT_NO_LIMITS we should get the size we request, even if its much larger than 165 // the screen. 166 @Ignore 167 @Test testOversizedDimensionsNoLimits()168 public void testOversizedDimensionsNoLimits() throws Exception { 169 // TODO(b/36890978): We only run this in fullscreen because of the 170 // unclear status of NO_LIMITS for non-child surfaces in MW modes 171 doFullscreenTest(TEST_OVER_SIZED_DIMENSIONS_NO_LIMITS, (parent, dialog) -> { 172 Rect parentFrame = getParentFrameWithInsets(parent); 173 Rect expectedFrame = new Rect(parentFrame.left, parentFrame.top, 174 parentFrame.left + oversizedDimension, 175 parentFrame.top + oversizedDimension); 176 assertEquals(expectedFrame, dialog.getFrame()); 177 }); 178 } 179 180 // If we request the MATCH_PARENT and a non-zero position, we wouldn't be 181 // able to fit all of our content, so we should be adjusted to just fit the 182 // content frame. 183 @Test testExplicitPositionMatchParent()184 public void testExplicitPositionMatchParent() throws Exception { 185 doParentChildTest(TEST_EXPLICIT_POSITION_MATCH_PARENT, (parent, dialog) -> 186 assertEquals(getParentFrameWithInsets(parent), dialog.getFrame()) 187 ); 188 } 189 190 // Unless we pass NO_LIMITS in which case our requested position should 191 // be honored. 192 @Test testExplicitPositionMatchParentNoLimits()193 public void testExplicitPositionMatchParentNoLimits() throws Exception { 194 final int explicitPosition = 100; 195 doParentChildTest(TEST_EXPLICIT_POSITION_MATCH_PARENT_NO_LIMITS, (parent, dialog) -> { 196 Rect parentFrame = getParentFrameWithInsets(parent); 197 Rect expectedFrame = new Rect(parentFrame); 198 expectedFrame.offset(explicitPosition, explicitPosition); 199 assertEquals(expectedFrame, dialog.getFrame()); 200 }); 201 } 202 203 // We run the two focus tests fullscreen only because switching to the 204 // docked stack will strip away focus from the task anyway. 205 @Test testDialogReceivesFocus()206 public void testDialogReceivesFocus() throws Exception { 207 doFullscreenTest(TEST_MATCH_PARENT, (parent, dialog) -> 208 assertEquals(dialog.getName(), mWmState.getFocusedWindow()) 209 ); 210 } 211 212 @Test testNoFocusDialog()213 public void testNoFocusDialog() throws Exception { 214 doFullscreenTest(TEST_NO_FOCUS, (parent, dialog) -> 215 assertEquals(parent.getName(), mWmState.getFocusedWindow()) 216 ); 217 } 218 219 @Test testMarginsArePercentagesOfContentFrame()220 public void testMarginsArePercentagesOfContentFrame() throws Exception { 221 float horizontalMargin = .10f; 222 float verticalMargin = .15f; 223 doParentChildTest(TEST_WITH_MARGINS, (parent, dialog) -> { 224 Rect frame = getParentFrameWithInsets(parent); 225 Rect expectedFrame = new Rect( 226 (int) (horizontalMargin * frame.width() + frame.left), 227 (int) (verticalMargin * frame.height() + frame.top), 228 (int) (horizontalMargin * frame.width() + frame.left) + explicitDimension, 229 (int) (verticalMargin * frame.height() + frame.top) + explicitDimension); 230 assertEquals(expectedFrame, dialog.getFrame()); 231 }); 232 } 233 234 @Test testDialogPlacedAboveParent()235 public void testDialogPlacedAboveParent() throws Exception { 236 final WindowManagerState wmState = mWmState; 237 doParentChildTest(TEST_MATCH_PARENT, (parent, dialog) -> 238 // Not only should the dialog be higher, but it should be leave multiple layers of 239 // space in between for DimLayers, etc... 240 assertThat(wmState.getZOrder(dialog), greaterThan(wmState.getZOrder(parent))) 241 ); 242 } 243 getParentFrameWithInsets(WindowState parent)244 private Rect getParentFrameWithInsets(WindowState parent) { 245 Rect parentFrame = parent.getFrame(); 246 return inset(parentFrame, getActivitySystemInsets()); 247 } 248 getActivitySystemInsets()249 private Insets getActivitySystemInsets() { 250 getInstrumentation().waitForIdleSync(); 251 getInstrumentation().runOnMainSync(() -> { 252 final Insets insets = mDialogTestActivity 253 .getActivity() 254 .getWindow() 255 .getDecorView() 256 .getRootWindowInsets() 257 .getInsets(WindowInsets.Type.systemBars()); 258 mContentInsets = Insets.of(insets.left, insets.top, insets.right, insets.bottom); 259 }); 260 return mContentInsets; 261 } 262 inset(Rect original, Insets insets)263 private static Rect inset(Rect original, Insets insets) { 264 final int left = original.left + insets.left; 265 final int top = original.top + insets.top; 266 final int right = original.right - insets.right; 267 final int bottom = original.bottom - insets.bottom; 268 return new Rect(left, top, right, bottom); 269 } 270 } 271