1 /* 2 * Copyright (C) 2021 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.app.Components.KEEP_CLEAR_RECTS_ACTIVITY; 20 import static android.server.wm.app.Components.KEEP_CLEAR_RECTS_ACTIVITY2; 21 import static android.server.wm.app.Components.KeepClearRectsActivity.EXTRA_KEEP_CLEAR_RECTS; 22 import static android.view.Display.DEFAULT_DISPLAY; 23 24 import static org.junit.Assert.assertFalse; 25 import static org.junit.Assert.assertTrue; 26 import static org.junit.Assume.assumeTrue; 27 28 import static java.util.Collections.EMPTY_LIST; 29 30 import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule; 31 import android.app.Activity; 32 import android.content.ComponentName; 33 import android.content.Intent; 34 import android.graphics.Color; 35 import android.graphics.Rect; 36 import android.os.Bundle; 37 import android.platform.test.annotations.Presubmit; 38 import android.server.wm.cts.R; 39 import android.view.Gravity; 40 import android.view.View; 41 import android.view.ViewConfiguration; 42 import android.view.ViewGroup; 43 import android.view.WindowManager; 44 import android.view.accessibility.AccessibilityNodeInfo; 45 import android.widget.RelativeLayout; 46 import android.widget.RelativeLayout.LayoutParams; 47 48 import com.android.compatibility.common.util.ApiTest; 49 import com.android.compatibility.common.util.PollingCheck; 50 51 import org.junit.After; 52 import org.junit.Before; 53 import org.junit.Rule; 54 import org.junit.Test; 55 56 import java.util.ArrayList; 57 import java.util.Arrays; 58 import java.util.Collections; 59 import java.util.List; 60 import java.util.concurrent.Callable; 61 62 @Presubmit 63 public class KeepClearRectsTests extends WindowManagerTestBase { 64 private static final long SAME_ELEMENT_ASSERTION_TIMEOUT = 3000; 65 private static final List<Rect> TEST_KEEP_CLEAR_RECTS = 66 Arrays.asList(new Rect(0, 0, 25, 25), 67 new Rect(30, 0, 50, 25), 68 new Rect(25, 25, 50, 50), 69 new Rect(10, 30, 20, 50)); 70 private static final List<Rect> TEST_KEEP_CLEAR_RECTS_2 = 71 Arrays.asList(new Rect(55, 0, 75, 15), 72 new Rect(50, 15, 60, 25), 73 new Rect(75, 25, 90, 50), 74 new Rect(90, 0, 100, 10)); 75 private static final Rect TEST_VIEW_BOUNDS = new Rect(0, 0, 100, 100); 76 private static final String USE_KEEP_CLEAR_ATTR_LAYOUT = "use_keep_clear_attr_layout"; 77 78 private TestActivitySession<TestActivity> mTestSession; 79 80 @Rule 81 public InstrumentedAccessibilityServiceTestRule<AccessibilityTestService> 82 mAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>( 83 AccessibilityTestService.class, false); 84 85 @Before setUp()86 public void setUp() throws Exception { 87 super.setUp(); 88 mTestSession = createManagedTestActivitySession(); 89 } 90 91 @After tearDown()92 public void tearDown() throws Exception { 93 mWmState.setSuppressAccessibilityServices(true); 94 } 95 96 @Test 97 @ApiTest(apis = {"android.view.View#attr_android:preferKeepClear"}) testSetPreferKeepClearAttr()98 public void testSetPreferKeepClearAttr() throws Exception { 99 final Intent intent = new Intent(mContext, TestActivity.class); 100 intent.putExtra(USE_KEEP_CLEAR_ATTR_LAYOUT, true); 101 mTestSession.launchTestActivityOnDisplaySync(null, intent, DEFAULT_DISPLAY); 102 final TestActivity activity = mTestSession.getActivity(); 103 104 // To be kept in sync with res/layout/keep_clear_attr_activity 105 final Rect keepClearRect = new Rect(0, 0, 25, 25); 106 assertSameElementsEventually(Arrays.asList(keepClearRect), 107 () -> getKeepClearRectsForActivity(activity)); 108 } 109 110 @Test 111 @ApiTest(apis = {"android.view.View#setPreferKeepClear"}) testSetPreferKeepClearSingleView()112 public void testSetPreferKeepClearSingleView() throws Exception { 113 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 114 final TestActivity activity = mTestSession.getActivity(); 115 116 final Rect keepClearRect = new Rect(0, 0, 25, 25); 117 final View v = createTestViewInActivity(activity, keepClearRect); 118 mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(true)); 119 120 assertSameElementsEventually(Arrays.asList(keepClearRect), 121 () -> getKeepClearRectsForActivity(activity)); 122 } 123 124 @Test 125 @ApiTest(apis = {"android.view.View#setPreferKeepClear"}) testSetPreferKeepClearTwoViews()126 public void testSetPreferKeepClearTwoViews() throws Exception { 127 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 128 final TestActivity activity = mTestSession.getActivity(); 129 130 final Rect keepClearRect = new Rect(0, 0, 25, 25); 131 final View v = createTestViewInActivity(activity, keepClearRect); 132 mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(true)); 133 134 final List<Rect> expected = new ArrayList(Arrays.asList(keepClearRect)); 135 assertSameElementsEventually(expected, () -> getKeepClearRectsForActivity(activity)); 136 137 final Rect keepClearRect2 = new Rect(25, 25, 50, 50); 138 final View v2 = createTestViewInActivity(activity, keepClearRect2); 139 mTestSession.runOnMainSyncAndWait(() -> v2.setPreferKeepClear(true)); 140 141 expected.addAll(Arrays.asList(keepClearRect2)); 142 assertSameElementsEventually(expected, () -> getKeepClearRectsForActivity(activity)); 143 } 144 145 @Test 146 @ApiTest(apis = {"android.view.View#setPreferKeepClearRects"}) testSetMultipleKeepClearRectsSingleView()147 public void testSetMultipleKeepClearRectsSingleView() throws Exception { 148 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 149 final TestActivity activity = mTestSession.getActivity(); 150 151 final View v = createTestViewInActivity(activity); 152 mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS)); 153 154 assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, 155 () -> getKeepClearRectsForActivity(activity)); 156 } 157 158 @Test 159 @ApiTest(apis = {"android.view.View#setPreferKeepClearRects"}) testSetMultipleKeepClearRectsTwoViews()160 public void testSetMultipleKeepClearRectsTwoViews() throws Exception { 161 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 162 final TestActivity activity = mTestSession.getActivity(); 163 164 final View v1 = createTestViewInActivity(activity); 165 final View v2 = createTestViewInActivity(activity); 166 mTestSession.runOnMainSyncAndWait(() -> { 167 v1.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS); 168 v2.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS_2); 169 }); 170 171 final List<Rect> expected = new ArrayList(TEST_KEEP_CLEAR_RECTS); 172 expected.addAll(TEST_KEEP_CLEAR_RECTS_2); 173 assertSameElementsEventually(expected, () -> getKeepClearRectsForActivity(activity)); 174 } 175 176 @Test 177 @ApiTest(apis = {"android.view.View#setPreferKeepClear", 178 "android.view.View#isPreferKeepClear"}) testIsPreferKeepClearSingleView()179 public void testIsPreferKeepClearSingleView() { 180 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 181 final TestActivity activity = mTestSession.getActivity(); 182 183 final Rect viewBounds = new Rect(0, 0, 60, 60); 184 final View v = createTestViewInActivity(activity, viewBounds); 185 assertFalse(v.isPreferKeepClear()); 186 mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(true)); 187 assertTrue(v.isPreferKeepClear()); 188 189 mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(false)); 190 assertFalse(v.isPreferKeepClear()); 191 } 192 193 @Test 194 @ApiTest(apis = {"android.view.View#getPreferKeepClearRects", 195 "android.view.View#setPreferKeepClearRects"}) testGetPreferKeepClearRectsSingleView()196 public void testGetPreferKeepClearRectsSingleView() throws Exception { 197 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 198 final TestActivity activity = mTestSession.getActivity(); 199 200 final Rect viewBounds = new Rect(0, 0, 60, 60); 201 final View v = createTestViewInActivity(activity, viewBounds); 202 assertSameElementsEventually(EMPTY_LIST, () -> v.getPreferKeepClearRects()); 203 mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS)); 204 assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, () -> v.getPreferKeepClearRects()); 205 206 mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS_2)); 207 assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS_2, () -> v.getPreferKeepClearRects()); 208 209 mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClearRects(EMPTY_LIST)); 210 assertSameElementsEventually(EMPTY_LIST, () -> v.getPreferKeepClearRects()); 211 } 212 213 @Test 214 @ApiTest(apis = {"android.view.View#setPreferKeepClearRects", 215 "android.view.View#setPreferKeepClear", 216 "android.view.View#getPreferKeepClearRects", 217 "android.view.View#isPreferKeepClear"}) testGettersPreferKeepClearRectsTwoViews()218 public void testGettersPreferKeepClearRectsTwoViews() throws Exception { 219 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 220 final TestActivity activity = mTestSession.getActivity(); 221 222 final Rect viewBounds1 = new Rect(0, 0, 60, 60); 223 final Rect viewBounds2 = new Rect(0, 0, 90, 90); 224 final View v1 = createTestViewInActivity(activity, viewBounds1); 225 final View v2 = createTestViewInActivity(activity, viewBounds2); 226 mTestSession.runOnMainSyncAndWait(() -> { 227 v1.setPreferKeepClear(true); 228 v2.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS); 229 }); 230 231 assertTrue(v1.isPreferKeepClear()); 232 assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, () -> v2.getPreferKeepClearRects()); 233 234 mTestSession.runOnMainSyncAndWait(() -> v1.setPreferKeepClear(false)); 235 assertFalse(v1.isPreferKeepClear()); 236 assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, () -> v2.getPreferKeepClearRects()); 237 238 mTestSession.runOnMainSyncAndWait(() -> { 239 v1.setPreferKeepClear(true); 240 v2.setPreferKeepClearRects(EMPTY_LIST); 241 }); 242 assertTrue(v1.isPreferKeepClear()); 243 assertSameElementsEventually(EMPTY_LIST, () -> v2.getPreferKeepClearRects()); 244 } 245 246 @Test 247 @ApiTest(apis = {"android.view.View#setPreferKeepClearRects", 248 "android.view.View#setPreferKeepClear"}) testSetPreferKeepClearCombinesWithMultipleRects()249 public void testSetPreferKeepClearCombinesWithMultipleRects() throws Exception { 250 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 251 final TestActivity activity = mTestSession.getActivity(); 252 253 final Rect viewBounds = new Rect(0, 0, 60, 60); 254 final View v = createTestViewInActivity(activity, viewBounds); 255 mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS)); 256 assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, 257 () -> getKeepClearRectsForActivity(activity)); 258 259 mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(true)); 260 final List<Rect> combinedRects = new ArrayList<>(TEST_KEEP_CLEAR_RECTS); 261 combinedRects.add(viewBounds); 262 assertSameElementsEventually(combinedRects, () -> getKeepClearRectsForActivity(activity)); 263 264 mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(false)); 265 assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, 266 () -> getKeepClearRectsForActivity(activity)); 267 } 268 269 @Test 270 @ApiTest(apis = {"android.view.View#setPreferKeepClearRects", 271 "android.view.View#setPreferKeepClear"}) testIgnoreKeepClearRectsFromGoneViews()272 public void testIgnoreKeepClearRectsFromGoneViews() throws Exception { 273 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 274 final TestActivity activity = mTestSession.getActivity(); 275 276 final Rect viewBounds = new Rect(0, 0, 60, 60); 277 final View v = createTestViewInActivity(activity, viewBounds); 278 mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(true)); 279 assertSameElementsEventually(Arrays.asList(viewBounds), 280 () -> getKeepClearRectsForActivity(activity)); 281 282 mTestSession.runOnMainSyncAndWait(() -> v.setVisibility(View.GONE)); 283 assertSameElementsEventually(EMPTY_LIST, () -> getKeepClearRectsForActivity(activity)); 284 285 mTestSession.runOnMainSyncAndWait(() -> v.setVisibility(View.VISIBLE)); 286 assertSameElementsEventually(Arrays.asList(viewBounds), 287 () -> getKeepClearRectsForActivity(activity)); 288 289 mTestSession.runOnMainSyncAndWait(() -> { 290 v.setPreferKeepClear(false); 291 v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS); 292 }); 293 assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, 294 () -> getKeepClearRectsForActivity(activity)); 295 296 mTestSession.runOnMainSyncAndWait(() -> v.setVisibility(View.GONE)); 297 assertSameElementsEventually(EMPTY_LIST, () -> getKeepClearRectsForActivity(activity)); 298 299 mTestSession.runOnMainSyncAndWait(() -> v.setVisibility(View.VISIBLE)); 300 assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, 301 () -> getKeepClearRectsForActivity(activity)); 302 303 final Rect viewBounds2 = new Rect(60, 60, 90, 90); 304 final View v2 = createTestViewInActivity(activity, viewBounds2); 305 mTestSession.runOnMainSyncAndWait(() -> v2.setPreferKeepClear(true)); 306 307 final List<Rect> expected = new ArrayList(TEST_KEEP_CLEAR_RECTS); 308 expected.add(viewBounds2); 309 assertSameElementsEventually(expected, () -> getKeepClearRectsForActivity(activity)); 310 311 mTestSession.runOnMainSyncAndWait(() -> v.setVisibility(View.GONE)); 312 assertSameElementsEventually(Arrays.asList(viewBounds2), 313 () -> getKeepClearRectsForActivity(activity)); 314 315 mTestSession.runOnMainSyncAndWait(() -> { 316 v.setVisibility(View.VISIBLE); 317 v2.setVisibility(View.GONE); 318 }); 319 assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, 320 () -> getKeepClearRectsForActivity(activity)); 321 322 mTestSession.runOnMainSyncAndWait(() -> { 323 v.setVisibility(View.VISIBLE); 324 v2.setVisibility(View.VISIBLE); 325 }); 326 assertSameElementsEventually(expected, () -> getKeepClearRectsForActivity(activity)); 327 } 328 329 @Test 330 @ApiTest(apis = {"android.view.View#setPreferKeepClear"}) testIgnoreKeepClearRectsFromDetachedViews()331 public void testIgnoreKeepClearRectsFromDetachedViews() throws Exception { 332 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 333 final TestActivity activity = mTestSession.getActivity(); 334 335 final Rect viewBounds = new Rect(0, 0, 60, 60); 336 final View v = createTestViewInActivity(activity, viewBounds); 337 mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(true)); 338 assertSameElementsEventually(Arrays.asList(viewBounds), 339 () -> getKeepClearRectsForActivity(activity)); 340 341 mTestSession.runOnMainSyncAndWait(() -> ((ViewGroup) v.getParent()).removeView(v)); 342 assertSameElementsEventually(EMPTY_LIST, () -> getKeepClearRectsForActivity(activity)); 343 } 344 345 @Test testFocusedViewDeclaredAsKeepClearArea()346 public void testFocusedViewDeclaredAsKeepClearArea() throws Exception { 347 assumeTrue(ViewConfiguration.get(mContext).isPreferKeepClearForFocusEnabled()); 348 349 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 350 final TestActivity activity = mTestSession.getActivity(); 351 352 final Rect viewBounds = new Rect(0, 0, 60, 60); 353 final View v = createTestViewInActivity(activity, viewBounds); 354 assertSameElementsEventually(EMPTY_LIST, () -> getKeepClearRectsForActivity(activity)); 355 356 mTestSession.runOnMainSyncAndWait(() -> { 357 v.setFocusableInTouchMode(true); 358 v.setFocusable(true); 359 v.requestFocus(); 360 }); 361 362 assertSameElementsEventually(Arrays.asList(viewBounds), 363 () -> getKeepClearRectsForActivity(activity)); 364 365 mTestSession.runOnMainSyncAndWait(() -> v.setFocusable(false)); 366 assertSameElementsEventually(EMPTY_LIST, () -> getKeepClearRectsForActivity(activity)); 367 } 368 369 @Test 370 @ApiTest(apis = {"android.view.View#setPreferKeepClearRects"}) testKeepClearRectsGetTranslatedToWindowSpace()371 public void testKeepClearRectsGetTranslatedToWindowSpace() throws Exception { 372 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 373 final TestActivity activity = mTestSession.getActivity(); 374 375 final Rect viewBounds = new Rect(30, 30, 60, 60); 376 final View v = createTestViewInActivity(activity, viewBounds); 377 mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS)); 378 final List<Rect> expected = new ArrayList(); 379 for (Rect r : TEST_KEEP_CLEAR_RECTS) { 380 Rect newRect = new Rect(r); 381 newRect.offset(viewBounds.left, viewBounds.top); 382 expected.add(newRect); 383 } 384 385 assertSameElementsEventually(expected, () -> getKeepClearRectsForActivity(activity)); 386 } 387 388 @Test 389 @ApiTest(apis = {"android.view.View#setPreferKeepClear", 390 "android.view.View#setPreferKeepClearRects"}) testSetKeepClearRectsOnDisplaySingleWindow()391 public void testSetKeepClearRectsOnDisplaySingleWindow() throws Exception { 392 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 393 final TestActivity activity = mTestSession.getActivity(); 394 395 final Rect keepClearRect = new Rect(0, 0, 25, 25); 396 final View v = createTestViewInActivity(activity, keepClearRect); 397 final List<Rect> prevKeepClearRectsOnDisplay = getKeepClearRectsOnDefaultDisplay(); 398 mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(true)); 399 assertSameElementsEventually(Arrays.asList(keepClearRect), 400 () -> getKeepClearRectsForActivity(activity)); 401 402 mTestSession.runOnMainSyncAndWait(() -> { 403 v.setPreferKeepClear(false); 404 v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS); 405 }); 406 assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, 407 () -> getKeepClearRectsForActivity(activity)); 408 409 final List<Rect> expectedRectsOnDisplay = new ArrayList<Rect>(); 410 expectedRectsOnDisplay.addAll(prevKeepClearRectsOnDisplay); 411 expectedRectsOnDisplay.addAll( 412 getRectsInScreenSpace(TEST_KEEP_CLEAR_RECTS, activity.getComponentName())); 413 assertSameElementsEventually(expectedRectsOnDisplay, 414 () -> getKeepClearRectsOnDefaultDisplay()); 415 } 416 417 @Test 418 @ApiTest(apis = {"android.view.View#setPreferKeepClearRects"}) testFinishingActivityRemovesItsKeepClearRects()419 public void testFinishingActivityRemovesItsKeepClearRects() throws Exception { 420 final List<Rect> prevKeepClearRectsOnDisplay = getKeepClearRectsOnDefaultDisplay(); 421 422 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 423 final TestActivity activity = mTestSession.getActivity(); 424 425 final Rect viewBounds = new Rect(0, 0, 60, 60); 426 final View v = createTestViewInActivity(activity, viewBounds); 427 mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS)); 428 assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, 429 () -> getKeepClearRectsForActivity(activity)); 430 431 activity.finishAndRemoveTask(); 432 assertSameElementsEventually(prevKeepClearRectsOnDisplay, 433 () -> getKeepClearRectsOnDefaultDisplay()); 434 } 435 436 @Test 437 @ApiTest(apis = {"android.view.View#setPreferKeepClear"}) testKeepClearRectsOnDisplayTwoWindows()438 public void testKeepClearRectsOnDisplayTwoWindows() throws Exception { 439 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 440 final TestActivity activity = mTestSession.getActivity(); 441 442 final Rect viewBounds = new Rect(0, 0, 25, 25); 443 final View v1 = createTestViewInActivity(activity, viewBounds); 444 mTestSession.runOnMainSyncAndWait(() -> v1.setPreferKeepClear(true)); 445 assertSameElementsEventually(Arrays.asList(viewBounds), 446 () -> getKeepClearRectsForActivity(activity)); 447 448 final String title = "KeepClearRectsTestWindow"; 449 mTestSession.runOnMainSyncAndWait(() -> { 450 final View testView = new View(activity); 451 testView.setPreferKeepClear(true); 452 testView.setBackgroundColor(Color.argb(20, 255, 0, 0)); 453 WindowManager.LayoutParams params = new WindowManager.LayoutParams(); 454 params.gravity = Gravity.TOP | Gravity.START; 455 params.width = 50; 456 params.height = 50; 457 params.setTitle(title); 458 activity.getWindowManager().addView(testView, params); 459 }); 460 mWmState.waitAndAssertWindowSurfaceShown(title, true); 461 462 assertSameElementsEventually(Arrays.asList(viewBounds), 463 () -> getKeepClearRectsForActivity(activity)); 464 } 465 466 @Test 467 @ApiTest(apis = {"android.view.View#setPreferKeepClear", 468 "android.view.View#setPreferKeepClearRects"}) testKeepClearRectsOnDisplayTwoFullscreenActivities()469 public void testKeepClearRectsOnDisplayTwoFullscreenActivities() throws Exception { 470 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 471 final TestActivity activity1 = mTestSession.getActivity(); 472 473 final Rect viewBounds = new Rect(0, 0, 25, 25); 474 final View v1 = createTestViewInActivity(activity1, viewBounds); 475 final List<Rect> prevKeepClearRectsOnDisplay = getKeepClearRectsOnDefaultDisplay(); 476 mTestSession.runOnMainSyncAndWait(() -> v1.setPreferKeepClear(true)); 477 assertSameElementsEventually(Arrays.asList(viewBounds), 478 () -> getKeepClearRectsForActivity(activity1)); 479 480 final TestActivitySession<TranslucentTestActivity> translucentTestSession = 481 createManagedTestActivitySession(); 482 translucentTestSession.launchTestActivityOnDisplaySync( 483 TranslucentTestActivity.class, DEFAULT_DISPLAY); 484 final TestActivity activity2 = translucentTestSession.getActivity(); 485 486 final View v2 = createTestViewInActivity(activity2); 487 mTestSession.runOnMainSyncAndWait(() -> v2.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS)); 488 assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, 489 () -> getKeepClearRectsForActivity(activity2)); 490 491 mWmState.assertVisibility(activity1.getComponentName(), true); 492 mWmState.assertVisibility(activity2.getComponentName(), true); 493 494 // Since both activities are fullscreen, WM only takes the keep clear areas from the top one 495 final List<Rect> expectedRectsOnDisplay = new ArrayList<Rect>(); 496 expectedRectsOnDisplay.addAll(prevKeepClearRectsOnDisplay); 497 expectedRectsOnDisplay.addAll(getRectsInScreenSpace(TEST_KEEP_CLEAR_RECTS, 498 activity2.getComponentName())); 499 assertSameElementsEventually(expectedRectsOnDisplay, 500 () -> getKeepClearRectsOnDefaultDisplay()); 501 } 502 503 @Test 504 @ApiTest(apis = {"android.view.View#setPreferKeepClear", 505 "android.view.View#setPreferKeepClearRects"}) testDisplayHasKeepClearRectsOnlyFromVisibleWindows()506 public void testDisplayHasKeepClearRectsOnlyFromVisibleWindows() throws Exception { 507 final TestActivitySession<TranslucentTestActivity> translucentTestSession = 508 createManagedTestActivitySession(); 509 translucentTestSession.launchTestActivityOnDisplaySync( 510 TranslucentTestActivity.class, DEFAULT_DISPLAY); 511 final TestActivity activity1 = translucentTestSession.getActivity(); 512 final Rect viewBounds = new Rect(0, 0, 25, 25); 513 final View v1 = createTestViewInActivity(activity1, viewBounds); 514 final List<Rect> prevKeepClearRectsOnDisplay = getKeepClearRectsOnDefaultDisplay(); 515 translucentTestSession.runOnMainSyncAndWait(() -> v1.setPreferKeepClear(true)); 516 517 // Add keep-clear rects in the activity 518 final List<Rect> expectedRectsOnDisplay = new ArrayList<Rect>(); 519 expectedRectsOnDisplay.addAll(prevKeepClearRectsOnDisplay); 520 expectedRectsOnDisplay.addAll(getRectsInScreenSpace(Arrays.asList(viewBounds), 521 activity1.getComponentName())); 522 assertSameElementsEventually(expectedRectsOnDisplay, 523 () -> getKeepClearRectsOnDefaultDisplay()); 524 525 // Start an opaque activity on top 526 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 527 final TestActivity activity2 = mTestSession.getActivity(); 528 529 // Add keep-clear rects in the opaque activity 530 final View v2 = createTestViewInActivity(activity2); 531 mTestSession.runOnMainSyncAndWait(() -> v2.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS)); 532 assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, 533 () -> getKeepClearRectsForActivity(activity2)); 534 535 mWmState.waitAndAssertVisibilityGone(activity1.getComponentName()); 536 mWmState.assertVisibility(activity2.getComponentName(), true); 537 538 // Only the opaque activity's keep-clear areas should be reported on the display 539 expectedRectsOnDisplay.clear(); 540 expectedRectsOnDisplay.addAll(prevKeepClearRectsOnDisplay); 541 expectedRectsOnDisplay.addAll(getRectsInScreenSpace( 542 TEST_KEEP_CLEAR_RECTS, activity2.getComponentName())); 543 assertSameElementsEventually(expectedRectsOnDisplay, 544 () -> getKeepClearRectsOnDefaultDisplay()); 545 } 546 547 @Test 548 @ApiTest(apis = {"android.view.View#setPreferKeepClearRects"}) testDisplayHasKeepClearAreasFromTwoActivitiesInSplitscreen()549 public void testDisplayHasKeepClearAreasFromTwoActivitiesInSplitscreen() throws Exception { 550 assumeTrue("Skipping test: no split multi-window support", 551 supportsSplitScreenMultiWindow()); 552 553 startKeepClearActivitiesInSplitscreen(KEEP_CLEAR_RECTS_ACTIVITY, 554 KEEP_CLEAR_RECTS_ACTIVITY2, Collections.emptyList(), Collections.emptyList()); 555 final List<Rect> prevKeepClearRectsOnDisplay = getKeepClearRectsOnDefaultDisplay(); 556 557 removeRootTask(mWmState.getTaskByActivity(KEEP_CLEAR_RECTS_ACTIVITY).mTaskId); 558 removeRootTask(mWmState.getTaskByActivity(KEEP_CLEAR_RECTS_ACTIVITY2).mTaskId); 559 560 startKeepClearActivitiesInSplitscreen(KEEP_CLEAR_RECTS_ACTIVITY, 561 KEEP_CLEAR_RECTS_ACTIVITY2, TEST_KEEP_CLEAR_RECTS, TEST_KEEP_CLEAR_RECTS_2); 562 563 assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, 564 () -> getKeepClearRectsForActivity(KEEP_CLEAR_RECTS_ACTIVITY)); 565 assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS_2, 566 () -> getKeepClearRectsForActivity(KEEP_CLEAR_RECTS_ACTIVITY2)); 567 568 final List<Rect> expected = new ArrayList(); 569 expected.addAll(prevKeepClearRectsOnDisplay); 570 expected.addAll(getRectsInScreenSpace(TEST_KEEP_CLEAR_RECTS, KEEP_CLEAR_RECTS_ACTIVITY)); 571 expected.addAll(getRectsInScreenSpace(TEST_KEEP_CLEAR_RECTS_2, KEEP_CLEAR_RECTS_ACTIVITY2)); 572 assertSameElementsEventually(expected, () -> getKeepClearRectsOnDefaultDisplay()); 573 } 574 startKeepClearActivitiesInSplitscreen(ComponentName activity1, ComponentName activity2, List<Rect> keepClearRects1, List<Rect> keepClearRects2)575 private void startKeepClearActivitiesInSplitscreen(ComponentName activity1, 576 ComponentName activity2, List<Rect> keepClearRects1, List<Rect> keepClearRects2) { 577 final LaunchActivityBuilder activityBuilder1 = getLaunchActivityBuilder() 578 .setUseInstrumentation() 579 .setTargetActivity(activity1) 580 .setIntentExtra(extra -> { 581 extra.putParcelableArrayList(EXTRA_KEEP_CLEAR_RECTS, 582 new ArrayList(keepClearRects1)); 583 }); 584 585 final LaunchActivityBuilder activityBuilder2 = getLaunchActivityBuilder() 586 .setUseInstrumentation() 587 .setTargetActivity(activity2) 588 .setIntentExtra(extra -> { 589 extra.putParcelableArrayList(EXTRA_KEEP_CLEAR_RECTS, 590 new ArrayList(keepClearRects2)); 591 }); 592 593 launchActivitiesInSplitScreen(activityBuilder1, activityBuilder2); 594 595 waitAndAssertResumedActivity(activity1, activity1 + " must be resumed"); 596 waitAndAssertResumedActivity(activity2, activity2 + " must be resumed"); 597 mWmState.assertVisibility(activity1, true); 598 mWmState.assertVisibility(activity2, true); 599 } 600 601 @Test 602 @ApiTest(apis = {"android.view.View#setUnrestrictedPreferKeepClearRects", 603 "android.view.View#setPreferKeepClearRects"}) testUnrestrictedKeepClearRects()604 public void testUnrestrictedKeepClearRects() throws Exception { 605 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 606 final TestActivity activity = mTestSession.getActivity(); 607 608 final View v = createTestViewInActivity(activity); 609 mTestSession.runOnMainSyncAndWait(() -> { 610 v.setUnrestrictedPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS); 611 }); 612 613 assertSameElementsEventually(EMPTY_LIST, () -> getKeepClearRectsForActivity(activity)); 614 assertSameElementsEventually(EMPTY_LIST, 615 () -> getUnrestrictedKeepClearRectsForActivity(activity)); 616 617 mTestSession.runOnMainSyncAndWait(() -> { 618 v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS); 619 }); 620 621 assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, 622 () -> getKeepClearRectsForActivity(activity)); 623 assertSameElementsEventually(EMPTY_LIST, 624 () -> getUnrestrictedKeepClearRectsForActivity(activity)); 625 } 626 627 @Test testAccessibilityFocusCreatesKeepClearRect()628 public void testAccessibilityFocusCreatesKeepClearRect() throws Exception { 629 assumeTrue(ViewConfiguration.get(mContext).isPreferKeepClearForFocusEnabled()); 630 631 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 632 final TestActivity activity = mTestSession.getActivity(); 633 634 mWmState.setSuppressAccessibilityServices(false); 635 mAccessibilityServiceRule.enableService(); 636 637 final View v = createTestViewInActivity(activity, TEST_VIEW_BOUNDS); 638 v.setFocusable(false); 639 640 mTestSession.runOnMainSyncAndWait(() -> { 641 v.performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); 642 }); 643 644 assertSameElementsEventually(Collections.singletonList(TEST_VIEW_BOUNDS), 645 () -> getKeepClearRectsForActivity(activity)); 646 } 647 648 @Test testAccessibilityAndInputFocusCreateKeepClearRects()649 public void testAccessibilityAndInputFocusCreateKeepClearRects() throws Exception { 650 assumeTrue(ViewConfiguration.get(mContext).isPreferKeepClearForFocusEnabled()); 651 652 mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); 653 final TestActivity activity = mTestSession.getActivity(); 654 655 mWmState.setSuppressAccessibilityServices(false); 656 mAccessibilityServiceRule.enableService(); 657 658 final Rect a11yFocusViewBounds = new Rect(30, 0, 50, 25); 659 final Rect inputFocusViewBounds = new Rect(0, 0, 25, 25); 660 661 final View a11yFocusView = createTestViewInActivity(activity, a11yFocusViewBounds); 662 final View inputFocusView = createTestViewInActivity(activity, inputFocusViewBounds); 663 664 mTestSession.runOnMainSyncAndWait(() -> { 665 a11yFocusView.performAccessibilityAction( 666 AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); 667 668 inputFocusView.setFocusableInTouchMode(true); 669 inputFocusView.setFocusable(true); 670 inputFocusView.requestFocus(); 671 }); 672 673 assertSameElementsEventually(Arrays.asList(a11yFocusViewBounds, inputFocusViewBounds), 674 () -> getKeepClearRectsForActivity(activity)); 675 } 676 createTestViewInActivity(TestActivity activity)677 private View createTestViewInActivity(TestActivity activity) { 678 return createTestViewInActivity(activity, TEST_VIEW_BOUNDS); 679 } 680 createTestViewInActivity(TestActivity activity, Rect viewBounds)681 private View createTestViewInActivity(TestActivity activity, Rect viewBounds) { 682 final View newView = new View(activity); 683 final LayoutParams params = new LayoutParams(viewBounds.width(), viewBounds.height()); 684 params.leftMargin = viewBounds.left; 685 params.topMargin = viewBounds.top; 686 mTestSession.runOnMainSyncAndWait(() -> { 687 activity.addView(newView, params); 688 }); 689 waitForIdle(); 690 return newView; 691 } 692 getKeepClearRectsForActivity(Activity activity)693 private List<Rect> getKeepClearRectsForActivity(Activity activity) { 694 return getKeepClearRectsForActivity(activity.getComponentName()); 695 } 696 getKeepClearRectsForActivity(ComponentName activityComponent)697 private List<Rect> getKeepClearRectsForActivity(ComponentName activityComponent) { 698 mWmState.computeState(); 699 return mWmState.getWindowState(activityComponent).getKeepClearRects(); 700 } 701 getKeepClearRectsOnDefaultDisplay()702 private List<Rect> getKeepClearRectsOnDefaultDisplay() { 703 mWmState.computeState(); 704 return mWmState.getDisplay(DEFAULT_DISPLAY).getKeepClearRects(); 705 } 706 getUnrestrictedKeepClearRectsForActivity(Activity activity)707 private List<Rect> getUnrestrictedKeepClearRectsForActivity(Activity activity) { 708 mWmState.computeState(); 709 return mWmState.getWindowState(activity.getComponentName()).getUnrestrictedKeepClearRects(); 710 } 711 712 public static class TestActivity extends FocusableActivity { 713 private RelativeLayout mRootView; 714 addView(View v, LayoutParams params)715 public void addView(View v, LayoutParams params) { 716 mRootView.addView(v, params); 717 } 718 719 @Override onCreate(Bundle savedInstanceState)720 protected void onCreate(Bundle savedInstanceState) { 721 super.onCreate(savedInstanceState); 722 if (getIntent().getBooleanExtra(USE_KEEP_CLEAR_ATTR_LAYOUT, false)) { 723 setContentView(R.layout.keep_clear_attr_activity); 724 } else { 725 setContentView(R.layout.keep_clear_rects_activity); 726 } 727 mRootView = findViewById(R.id.root); 728 729 getWindow().setDecorFitsSystemWindows(false); 730 } 731 } 732 733 public static class TranslucentTestActivity extends TestActivity {} 734 getRectsInScreenSpace(List<Rect> rects, ComponentName componentName)735 private List<Rect> getRectsInScreenSpace(List<Rect> rects, ComponentName componentName) { 736 mWmState.computeState(); 737 final WindowManagerState.WindowState windowState = 738 mWmState.getWindowState(componentName); 739 final List<Rect> result = new ArrayList<>(); 740 for (Rect r : rects) { 741 Rect rectInScreenSpace = new Rect(r); 742 rectInScreenSpace.offset(windowState.getFrame().left, windowState.getFrame().top); 743 result.add(rectInScreenSpace); 744 } 745 return result; 746 } 747 assertSameElementsEventually(List<T> expected, Callable<List<T>> actual)748 private static <T> void assertSameElementsEventually(List<T> expected, Callable<List<T>> actual) 749 throws Exception { 750 PollingCheck.check("Lists do not have the same elements." 751 + "Expected=" + expected + ", actual=" + actual.call(), 752 SAME_ELEMENT_ASSERTION_TIMEOUT, 753 () -> hasSameElements(expected, actual.call())); 754 } 755 hasSameElements(List<T> fst, List<T> snd)756 private static <T> boolean hasSameElements(List<T> fst, List<T> snd) { 757 if (fst.size() != snd.size()) return false; 758 759 for (T a : fst) { 760 if (!snd.contains(a)) { 761 return false; 762 } 763 } 764 return true; 765 } 766 } 767