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