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.accessibilityservice.cts; 18 19 import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWaitForAll; 20 import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangeTypesAndWindowTitle; 21 import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangedWithChangeTypes; 22 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.findWindowByTitle; 23 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.findWindowByTitleAndDisplay; 24 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.getActivityTitle; 25 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen; 26 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen; 27 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.supportsMultiDisplay; 28 import static android.accessibilityservice.cts.utils.CtsTestUtils.isAutomotive; 29 import static android.accessibilityservice.cts.utils.DisplayUtils.VirtualDisplaySession; 30 import static android.accessibilityservice.cts.utils.WindowCreationUtils.TOP_WINDOW_TITLE; 31 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; 32 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOWS_CHANGED; 33 import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED; 34 import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACTIVE; 35 import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ADDED; 36 import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_BOUNDS; 37 import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_CHILDREN; 38 import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_FOCUSED; 39 import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_TITLE; 40 41 import static com.google.common.truth.Truth.assertThat; 42 import static com.google.common.truth.Truth.assertWithMessage; 43 44 import static org.junit.Assume.assumeFalse; 45 import static org.junit.Assume.assumeTrue; 46 47 import android.Manifest; 48 import android.accessibility.cts.common.AccessibilityDumpOnFailureRule; 49 import android.accessibilityservice.AccessibilityServiceInfo; 50 import android.accessibilityservice.cts.activities.AccessibilityWindowReportingActivity; 51 import android.accessibilityservice.cts.activities.NonDefaultDisplayActivity; 52 import android.accessibilityservice.cts.activities.NotTouchableWindowTestActivity; 53 import android.accessibilityservice.cts.utils.ActivityLaunchUtils; 54 import android.accessibilityservice.cts.utils.DisplayUtils; 55 import android.accessibilityservice.cts.utils.WindowCreationUtils; 56 import android.app.Activity; 57 import android.app.Instrumentation; 58 import android.app.UiAutomation; 59 import android.content.ComponentName; 60 import android.content.Context; 61 import android.content.Intent; 62 import android.platform.test.annotations.AppModeFull; 63 import android.platform.test.annotations.Presubmit; 64 import android.provider.Settings; 65 import android.view.Gravity; 66 import android.view.View; 67 import android.view.WindowManager; 68 import android.view.accessibility.AccessibilityNodeInfo; 69 import android.view.accessibility.AccessibilityWindowInfo; 70 import android.widget.ArrayAdapter; 71 import android.widget.AutoCompleteTextView; 72 import android.widget.Button; 73 74 import androidx.test.InstrumentationRegistry; 75 import androidx.test.rule.ActivityTestRule; 76 import androidx.test.runner.AndroidJUnit4; 77 78 import com.android.compatibility.common.util.CddTest; 79 import com.android.compatibility.common.util.SystemUtil; 80 81 import org.junit.AfterClass; 82 import org.junit.Before; 83 import org.junit.BeforeClass; 84 import org.junit.Ignore; 85 import org.junit.Rule; 86 import org.junit.Test; 87 import org.junit.rules.RuleChain; 88 import org.junit.runner.RunWith; 89 90 import java.io.IOException; 91 import java.util.List; 92 import java.util.concurrent.TimeoutException; 93 94 /** 95 * Tests that window changes produce the correct events and that AccessibilityWindowInfos are 96 * properly populated 97 */ 98 @RunWith(AndroidJUnit4.class) 99 @CddTest(requirements = {"3.10/C-1-1,C-1-2"}) 100 public class AccessibilityWindowReportingTest { 101 private static final int TIMEOUT_ASYNC_PROCESSING = 5000; 102 private static Instrumentation sInstrumentation; 103 private static UiAutomation sUiAutomation; 104 private Activity mActivity; 105 private CharSequence mActivityTitle; 106 107 private final ActivityTestRule<AccessibilityWindowReportingActivity> mActivityRule = 108 new ActivityTestRule<>(AccessibilityWindowReportingActivity.class, false, false); 109 110 private final AccessibilityDumpOnFailureRule mDumpOnFailureRule = 111 new AccessibilityDumpOnFailureRule(); 112 113 @Rule 114 public final RuleChain mRuleChain = RuleChain 115 .outerRule(mActivityRule) 116 .around(mDumpOnFailureRule); 117 118 @BeforeClass oneTimeSetup()119 public static void oneTimeSetup() throws Exception { 120 sInstrumentation = InstrumentationRegistry.getInstrumentation(); 121 sUiAutomation = sInstrumentation.getUiAutomation(); 122 AccessibilityServiceInfo info = sUiAutomation.getServiceInfo(); 123 info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS; 124 sUiAutomation.setServiceInfo(info); 125 } 126 127 @AfterClass finalTearDown()128 public static void finalTearDown() { 129 sUiAutomation.destroy(); 130 } 131 132 @Before setUp()133 public void setUp() throws Exception { 134 mActivity = launchActivityAndWaitForItToBeOnscreen( 135 sInstrumentation, sUiAutomation, mActivityRule); 136 mActivityTitle = getActivityTitle(sInstrumentation, mActivity); 137 } 138 perDisplayFocusEnabled()139 private static boolean perDisplayFocusEnabled() { 140 return sInstrumentation.getTargetContext().getResources() 141 .getBoolean(android.R.bool.config_perDisplayFocusEnabled); 142 } 143 144 @Test 145 @Presubmit testUpdatedWindowTitle_generatesEventAndIsReturnedByGetTitle()146 public void testUpdatedWindowTitle_generatesEventAndIsReturnedByGetTitle() { 147 final String updatedTitle = "Updated Title"; 148 try { 149 sUiAutomation.executeAndWaitForEvent( 150 () -> sInstrumentation.runOnMainSync(() -> mActivity.setTitle(updatedTitle)), 151 filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_TITLE), 152 TIMEOUT_ASYNC_PROCESSING); 153 } catch (TimeoutException exception) { 154 throw new RuntimeException( 155 "Failed to get windows changed event for title update", exception); 156 } 157 final AccessibilityWindowInfo window = findWindowByTitle(sUiAutomation, updatedTitle); 158 assertWithMessage("Updated window title not reported to accessibility") 159 .that(window).isNotNull(); 160 window.recycle(); 161 } 162 163 @Test 164 @Presubmit testWindowAddedMovedAndRemoved_generatesEventsForAllThree()165 public void testWindowAddedMovedAndRemoved_generatesEventsForAllThree() throws Exception { 166 final WindowManager.LayoutParams paramsForTop = 167 WindowCreationUtils.layoutParamsForWindowOnTop( 168 sInstrumentation, mActivity, TOP_WINDOW_TITLE); 169 final WindowManager.LayoutParams paramsForBottom = layoutParamsForWindowOnBottom(); 170 final Button button = new Button(mActivity); 171 button.setText(R.string.button1); 172 173 WindowCreationUtils.addWindowAndWaitForEvent(sUiAutomation, sInstrumentation, mActivity, 174 button, paramsForTop, filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_ADDED)); 175 176 // Move window from top to bottom 177 sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync( 178 () -> mActivity.getWindowManager().updateViewLayout(button, paramsForBottom)), 179 filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_BOUNDS), 180 TIMEOUT_ASYNC_PROCESSING); 181 // Remove the view 182 WindowCreationUtils.removeWindow(sUiAutomation, sInstrumentation, mActivity, button); 183 } 184 185 @Test 186 @Ignore("b/325640120") putWindowInPictureInPicture_generatesEventAndReportsProperty()187 public void putWindowInPictureInPicture_generatesEventAndReportsProperty() throws Exception { 188 if (!sInstrumentation.getContext().getPackageManager() 189 .hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { 190 return; 191 } 192 sUiAutomation.executeAndWaitForEvent( 193 () -> sInstrumentation.runOnMainSync(() -> mActivity.enterPictureInPictureMode()), 194 (event) -> { 195 if (event.getEventType() != TYPE_WINDOWS_CHANGED) return false; 196 // Look for a picture-in-picture window 197 final List<AccessibilityWindowInfo> windows = sUiAutomation.getWindows(); 198 final int windowCount = windows.size(); 199 for (int i = 0; i < windowCount; i++) { 200 if (windows.get(i).isInPictureInPictureMode()) { 201 return true; 202 } 203 } 204 return false; 205 }, TIMEOUT_ASYNC_PROCESSING); 206 207 // There should be exactly one picture-in-picture window now 208 int numPictureInPictureWindows = 0; 209 final List<AccessibilityWindowInfo> windows = sUiAutomation.getWindows(); 210 final int windowCount = windows.size(); 211 for (int i = 0; i < windowCount; i++) { 212 final AccessibilityWindowInfo window = windows.get(i); 213 if (window.isInPictureInPictureMode()) { 214 numPictureInPictureWindows++; 215 } 216 } 217 assertThat(numPictureInPictureWindows).isAtLeast(1); 218 } 219 220 @Test 221 @Presubmit moveFocusToAnotherWindow_generatesEventsAndMovesActiveAndFocus()222 public void moveFocusToAnotherWindow_generatesEventsAndMovesActiveAndFocus() throws Exception { 223 final View topWindowView = showTopWindowAndWaitForItToShowUp(); 224 final AccessibilityWindowInfo topWindow = 225 findWindowByTitle(sUiAutomation, TOP_WINDOW_TITLE); 226 227 AccessibilityWindowInfo activityWindow = findWindowByTitle(sUiAutomation, mActivityTitle); 228 final AccessibilityNodeInfo buttonNode = 229 topWindow.getRoot().findAccessibilityNodeInfosByText( 230 sInstrumentation.getContext().getString(R.string.button1)).get(0); 231 232 // Make sure activityWindow is not focused 233 if (activityWindow.isFocused()) { 234 sUiAutomation.executeAndWaitForEvent( 235 () -> buttonNode.performAction(AccessibilityNodeInfo.ACTION_FOCUS), 236 filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_FOCUSED), 237 TIMEOUT_ASYNC_PROCESSING); 238 } 239 240 // Windows may have changed - refresh 241 activityWindow = findWindowByTitle(sUiAutomation, mActivityTitle); 242 assertThat(activityWindow.isActive()).isFalse(); 243 assertThat(activityWindow.isFocused()).isFalse(); 244 245 // Find a focusable view in the main activity menu 246 final AccessibilityNodeInfo autoCompleteTextInfo = activityWindow.getRoot() 247 .findAccessibilityNodeInfosByViewId( 248 "android.accessibilityservice.cts:id/autoCompleteLayout") 249 .get(0); 250 assertThat(autoCompleteTextInfo).isNotNull(); 251 252 // Remove the top window and focus on the main activity 253 sUiAutomation.executeAndWaitForEvent( 254 () -> { 255 sInstrumentation.runOnMainSync( 256 () -> mActivity.getWindowManager().removeView(topWindowView)); 257 buttonNode.performAction(AccessibilityNodeInfo.ACTION_FOCUS); 258 }, 259 filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_FOCUSED | WINDOWS_CHANGE_ACTIVE), 260 TIMEOUT_ASYNC_PROCESSING); 261 } 262 263 @Test 264 @Presubmit moveFocusToAnotherDisplay_movesActiveAndFocusWindow()265 public void moveFocusToAnotherDisplay_movesActiveAndFocusWindow() throws Exception { 266 assumeTrue(supportsMultiDisplay(sInstrumentation.getContext())); 267 268 // Makes sure activityWindow on default display is focused 269 AccessibilityWindowInfo activityWindow = findWindowByTitle(sUiAutomation, mActivityTitle); 270 assertThat(activityWindow.isActive()).isTrue(); 271 assertThat(activityWindow.isFocused()).isTrue(); 272 273 // Creates a virtual display. 274 try (VirtualDisplaySession displaySession = new VirtualDisplaySession()) { 275 final int virtualDisplayId = 276 displaySession.createDisplayWithDefaultDisplayMetricsAndWait( 277 sInstrumentation.getContext(), false).getDisplayId(); 278 // Launches an activity on virtual display. 279 final Activity activityOnVirtualDisplay = 280 launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen(sInstrumentation, 281 sUiAutomation, 282 NonDefaultDisplayActivity.class, 283 virtualDisplayId); 284 285 final CharSequence activityTitle = getActivityTitle(sInstrumentation, 286 activityOnVirtualDisplay); 287 288 // Window manager changed the behavior of focused window at a virtual display. A window 289 // at virtual display needs to be touched then it becomes to be focused one. Adding this 290 // touch event on the activity window of the virtual display to pass this test case. 291 sUiAutomation.executeAndWaitForEvent( 292 () -> DisplayUtils.touchDisplay(sUiAutomation, virtualDisplayId, activityTitle), 293 filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_FOCUSED | 294 WINDOWS_CHANGE_ACTIVE), 295 TIMEOUT_ASYNC_PROCESSING); 296 297 // Make sure activityWindow on virtual display is focused. 298 AccessibilityWindowInfo activityWindowOnVirtualDisplay = 299 findWindowByTitleAndDisplay(sUiAutomation, activityTitle, virtualDisplayId); 300 // Windows may have changed - refresh. 301 activityWindow = findWindowByTitle(sUiAutomation, mActivityTitle); 302 try { 303 if (!perDisplayFocusEnabled()) { 304 assertThat(activityWindow.isActive()).isFalse(); 305 assertThat(activityWindow.isFocused()).isFalse(); 306 } else { 307 assertThat(activityWindow.isActive()).isTrue(); 308 assertThat(activityWindow.isFocused()).isTrue(); 309 } 310 assertThat(activityWindowOnVirtualDisplay.isActive()).isTrue(); 311 assertThat(activityWindowOnVirtualDisplay.isFocused()).isTrue(); 312 } finally { 313 sUiAutomation.executeAndWaitForEvent( 314 () -> sInstrumentation.runOnMainSync(activityOnVirtualDisplay::finish), 315 filterWaitForAll( 316 filterWindowsChangedWithChangeTypes( 317 WINDOWS_CHANGE_FOCUSED | WINDOWS_CHANGE_ACTIVE), 318 event -> { 319 // The focused window should be returned to activity at 320 // default display after 321 // the activity at virtual display is destroyed. 322 AccessibilityWindowInfo window = findWindowByTitle( 323 sUiAutomation, mActivityTitle); 324 return window.isActive() && window.isFocused(); 325 }), 326 TIMEOUT_ASYNC_PROCESSING); 327 } 328 } 329 } 330 331 @Test 332 @Presubmit testChangeAccessibilityFocusWindow_getEvent()333 public void testChangeAccessibilityFocusWindow_getEvent() throws Exception { 334 final AccessibilityServiceInfo info = sUiAutomation.getServiceInfo(); 335 info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE; 336 sUiAutomation.setServiceInfo(info); 337 View topWindowView = null; 338 try { 339 topWindowView = showTopWindowAndWaitForItToShowUp(); 340 341 final AccessibilityWindowInfo activityWindow = 342 findWindowByTitle(sUiAutomation, mActivityTitle); 343 final AccessibilityWindowInfo topWindow = 344 findWindowByTitle(sUiAutomation, TOP_WINDOW_TITLE); 345 final AccessibilityNodeInfo win2Node = 346 topWindow.getRoot().findAccessibilityNodeInfosByText( 347 sInstrumentation.getContext().getString(R.string.button1)).get(0); 348 final AccessibilityNodeInfo win1Node = activityWindow.getRoot() 349 .findAccessibilityNodeInfosByViewId( 350 "android.accessibilityservice.cts:id/autoCompleteLayout") 351 .get(0); 352 353 sUiAutomation.executeAndWaitForEvent( 354 () -> { 355 win2Node.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); 356 win1Node.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); 357 }, 358 filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED), 359 TIMEOUT_ASYNC_PROCESSING); 360 } finally { 361 info.flags &= ~AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE; 362 sUiAutomation.setServiceInfo(info); 363 // Remove the view 364 if (topWindowView != null) { 365 WindowCreationUtils.removeWindow(sUiAutomation, sInstrumentation, mActivity, 366 topWindowView); 367 } 368 } 369 } 370 371 @Test testGetAnchorForDropDownForAutoCompleteTextView_returnsTextViewNode()372 public void testGetAnchorForDropDownForAutoCompleteTextView_returnsTextViewNode() { 373 final AutoCompleteTextView autoCompleteTextView = 374 mActivity.findViewById(R.id.autoCompleteLayout); 375 final AccessibilityNodeInfo autoCompleteTextInfo = sUiAutomation.getRootInActiveWindow() 376 .findAccessibilityNodeInfosByViewId( 377 "android.accessibilityservice.cts:id/autoCompleteLayout") 378 .get(0); 379 380 // For the drop-down 381 final String[] countries = new String[]{"Belgium", "France", "Italy", "Germany", "Spain"}; 382 383 try { 384 sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync( 385 () -> { 386 final ArrayAdapter<String> adapter = new ArrayAdapter<>( 387 mActivity, android.R.layout.simple_dropdown_item_1line, countries); 388 autoCompleteTextView.setAdapter(adapter); 389 autoCompleteTextView.showDropDown(); 390 }), 391 filterWindowsChangeTypesAndWindowTitle(sUiAutomation, WINDOWS_CHANGE_CHILDREN, 392 mActivityTitle.toString()), TIMEOUT_ASYNC_PROCESSING); 393 } catch (TimeoutException exception) { 394 throw new RuntimeException( 395 "Failed to get window changed event when showing dropdown", exception); 396 } 397 398 // Find the pop-up window 399 boolean foundPopup = false; 400 final List<AccessibilityWindowInfo> windows = sUiAutomation.getWindows(); 401 for (int i = 0; i < windows.size(); i++) { 402 final AccessibilityWindowInfo window = windows.get(i); 403 if (window.getAnchor() == null) { 404 continue; 405 } 406 assertThat(window.getAnchor()).isEqualTo(autoCompleteTextInfo); 407 assertWithMessage("Found multiple pop-ups anchored to one text view") 408 .that(foundPopup).isFalse(); 409 foundPopup = true; 410 } 411 assertWithMessage("Failed to find accessibility window for auto-complete pop-up") 412 .that(foundPopup).isTrue(); 413 } 414 415 @AppModeFull 416 @Test showNotTouchableWindow_activityWindowIsNotVisible()417 public void showNotTouchableWindow_activityWindowIsNotVisible() throws TimeoutException { 418 // TODO: b/336552993 - Investigate and re-enable this test on Android Auto. 419 assumeFalse(isAutomotive(sInstrumentation.getTargetContext())); 420 try { 421 launchNotTouchableWindowTestActivityFromShell(); 422 423 Intent intent = new Intent(); 424 intent.setAction(NotTouchableWindowTestActivity.ADD_WINDOW); 425 intent.setPackage(sInstrumentation.getContext().getPackageName()); 426 427 // Waits for two events, whose order is nondeterministic: 428 // (1) the test activity is covered by the untrusted non-touchable window. 429 // (2) the untrusted non-touchable window is added. 430 sendIntentAndWaitForEvent(intent, 431 filterWaitForAll( 432 event -> { 433 final AccessibilityWindowInfo coveredWindow = 434 findWindowByTitle(sUiAutomation, 435 NotTouchableWindowTestActivity.TITLE); 436 return coveredWindow == null; 437 }, 438 filterWindowsChangeTypesAndWindowTitle(sUiAutomation, 439 WINDOWS_CHANGE_ADDED, 440 NotTouchableWindowTestActivity.NON_TOUCHABLE_WINDOW_TITLE) 441 )); 442 } finally { 443 closeNotTouchableWindowTestActivity(); 444 } 445 } 446 447 @AppModeFull 448 @Test showNotTouchableTrustedWindow_activityWindowIsVisible()449 public void showNotTouchableTrustedWindow_activityWindowIsVisible() { 450 // TODO: b/336552993 - Investigate and re-enable this test on Android Auto. 451 assumeFalse(isAutomotive(sInstrumentation.getTargetContext())); 452 try { 453 launchNotTouchableWindowTestActivityFromShell(); 454 455 Intent intent = new Intent(); 456 intent.setAction(NotTouchableWindowTestActivity.ADD_TRUSTED_WINDOW); 457 intent.setPackage(sInstrumentation.getContext().getPackageName()); 458 459 SystemUtil.runWithShellPermissionIdentity(sUiAutomation, 460 () -> sendIntentAndWaitForEvent(intent, 461 filterWindowsChangeTypesAndWindowTitle(sUiAutomation, 462 WINDOWS_CHANGE_ADDED, 463 NotTouchableWindowTestActivity.NON_TOUCHABLE_WINDOW_TITLE)), 464 Manifest.permission.INTERNAL_SYSTEM_WINDOW); 465 466 assertThat(findWindowByTitle(sUiAutomation, NotTouchableWindowTestActivity.TITLE)) 467 .isNotNull(); 468 } finally { 469 closeNotTouchableWindowTestActivity(); 470 } 471 } 472 473 // We want to test WindowState#isTrustedOverlay which refers to flag stored in the 474 // Session class and is not updated since the Session is created. 475 // Use shell command instead of ActivityLaunchUtils to get INTERNAL_SYSTEM_WINDOW 476 // permission when the Session is created. launchNotTouchableWindowTestActivityFromShell()477 private void launchNotTouchableWindowTestActivityFromShell() { 478 SystemUtil.runWithShellPermissionIdentity(sUiAutomation, 479 () -> sUiAutomation.executeAndWaitForEvent( 480 () -> { 481 final ComponentName componentName = new ComponentName( 482 sInstrumentation.getContext(), 483 NotTouchableWindowTestActivity.class); 484 485 String command = "am start -n " + componentName.flattenToString(); 486 try { 487 SystemUtil.runShellCommand(sInstrumentation, command); 488 } catch (IOException e) { 489 throw new RuntimeException(e); 490 } 491 }, 492 (event) -> { 493 final AccessibilityWindowInfo window = 494 findWindowByTitleAndDisplay(sUiAutomation, 495 NotTouchableWindowTestActivity.TITLE, 0); 496 return window != null; 497 }, TIMEOUT_ASYNC_PROCESSING), Manifest.permission.INTERNAL_SYSTEM_WINDOW); 498 } 499 closeNotTouchableWindowTestActivity()500 private void closeNotTouchableWindowTestActivity() { 501 final Intent intent = new Intent(); 502 intent.setAction(NotTouchableWindowTestActivity.FINISH_ACTIVITY); 503 intent.setPackage(sInstrumentation.getContext().getPackageName()); 504 // Call finish() on the window. This is required to launch more activities in any subsequent 505 // tests from this same app process. 506 sInstrumentation.runOnMainSync(() -> sInstrumentation.getContext().sendBroadcast(intent)); 507 // Ensure we're at the home screen before continuing to other tests. 508 // finish() should do this, but sometimes takes longer than expected. 509 ActivityLaunchUtils.homeScreenOrBust(sInstrumentation.getContext(), sUiAutomation); 510 } 511 512 /** 513 * Test whether we can successfully enable and disable window animations. 514 */ 515 @Test testDisableWindowAnimations()516 public void testDisableWindowAnimations() { 517 setAndAssertAnimationScale(0.0f); 518 setAndAssertAnimationScale(0.5f); 519 setAndAssertAnimationScale(1.0f); 520 } 521 522 /** Sets the animation scale to a specified value and asserts that the value has been set. */ setAndAssertAnimationScale(float value)523 private void setAndAssertAnimationScale(float value) { 524 Context context = sInstrumentation.getContext(); 525 sUiAutomation.setAnimationScale(value); 526 assertThat(getGlobalFloat(context, Settings.Global.WINDOW_ANIMATION_SCALE)) 527 .isEqualTo(value); 528 assertThat(getGlobalFloat(context, Settings.Global.TRANSITION_ANIMATION_SCALE)) 529 .isEqualTo(value); 530 assertThat(getGlobalFloat(context, Settings.Global.ANIMATOR_DURATION_SCALE)) 531 .isEqualTo(value); 532 } 533 534 /** Returns value of constants in Settings.Global. */ getGlobalFloat(Context context, String constantName)535 private static float getGlobalFloat(Context context, String constantName) { 536 return Settings.Global.getFloat(context.getContentResolver(), constantName, -1); 537 } 538 showTopWindowAndWaitForItToShowUp()539 private View showTopWindowAndWaitForItToShowUp() throws TimeoutException { 540 final WindowManager.LayoutParams paramsForTop = 541 WindowCreationUtils.layoutParamsForWindowOnTop( 542 sInstrumentation, mActivity, TOP_WINDOW_TITLE); 543 final Button button = new Button(mActivity); 544 button.setText(R.string.button1); 545 546 WindowCreationUtils.addWindowAndWaitForEvent(sUiAutomation, sInstrumentation, mActivity, 547 button, paramsForTop, (event) -> (event.getEventType() == TYPE_WINDOWS_CHANGED) 548 && (findWindowByTitle(sUiAutomation, mActivityTitle) != null) 549 && (findWindowByTitle(sUiAutomation, TOP_WINDOW_TITLE) != null)); 550 return button; 551 } 552 layoutParamsForWindowOnBottom()553 private WindowManager.LayoutParams layoutParamsForWindowOnBottom() { 554 final WindowManager.LayoutParams params = WindowCreationUtils.layoutParamsForTestWindow( 555 sInstrumentation, mActivity); 556 params.gravity = Gravity.BOTTOM; 557 return params; 558 } 559 sendIntentAndWaitForEvent(Intent intent, UiAutomation.AccessibilityEventFilter filter)560 private void sendIntentAndWaitForEvent(Intent intent, 561 UiAutomation.AccessibilityEventFilter filter) throws TimeoutException { 562 sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync( 563 () -> sInstrumentation.getContext().sendBroadcast(intent)), 564 filter, 565 TIMEOUT_ASYNC_PROCESSING); 566 } 567 568 } 569