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