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