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.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN; 20 import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_WINDOW; 21 import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangedWithChangeTypes; 22 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen; 23 import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS; 24 import static android.accessibilityservice.cts.utils.CtsTestUtils.isAutomotive; 25 import static android.content.pm.PackageManager.FEATURE_WINDOW_MAGNIFICATION; 26 import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ADDED; 27 28 import static org.junit.Assert.assertEquals; 29 import static org.junit.Assert.assertFalse; 30 import static org.junit.Assert.assertNotNull; 31 import static org.junit.Assert.assertTrue; 32 import static org.junit.Assume.assumeFalse; 33 import static org.mockito.Mockito.any; 34 import static org.mockito.Mockito.anyFloat; 35 import static org.mockito.Mockito.eq; 36 import static org.mockito.Mockito.mock; 37 import static org.mockito.Mockito.reset; 38 import static org.mockito.Mockito.timeout; 39 import static org.mockito.Mockito.verify; 40 41 import android.accessibility.cts.common.AccessibilityDumpOnFailureRule; 42 import android.accessibility.cts.common.InstrumentedAccessibilityService; 43 import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule; 44 import android.accessibility.cts.common.ShellCommandBuilder; 45 import android.accessibilityservice.AccessibilityService.MagnificationController; 46 import android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener; 47 import android.accessibilityservice.AccessibilityServiceInfo; 48 import android.accessibilityservice.MagnificationConfig; 49 import android.accessibilityservice.cts.activities.AccessibilityWindowQueryActivity; 50 import android.app.Activity; 51 import android.app.Instrumentation; 52 import android.app.UiAutomation; 53 import android.content.Context; 54 import android.content.pm.PackageManager; 55 import android.graphics.PixelFormat; 56 import android.graphics.Point; 57 import android.graphics.Rect; 58 import android.graphics.Region; 59 import android.os.SystemClock; 60 import android.platform.test.annotations.AppModeFull; 61 import android.platform.test.annotations.Presubmit; 62 import android.platform.test.annotations.RequiresFlagsDisabled; 63 import android.platform.test.annotations.RequiresFlagsEnabled; 64 import android.platform.test.flag.junit.CheckFlagsRule; 65 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 66 import android.util.DisplayMetrics; 67 import android.util.Log; 68 import android.view.Gravity; 69 import android.view.View; 70 import android.view.ViewGroup; 71 import android.view.WindowManager; 72 import android.view.accessibility.AccessibilityNodeInfo; 73 import android.view.accessibility.AccessibilityWindowInfo; 74 import android.widget.Button; 75 76 import androidx.test.filters.FlakyTest; 77 import androidx.test.platform.app.InstrumentationRegistry; 78 import androidx.test.rule.ActivityTestRule; 79 import androidx.test.runner.AndroidJUnit4; 80 81 import com.android.compatibility.common.util.CddTest; 82 import com.android.compatibility.common.util.SystemUtil; 83 import com.android.compatibility.common.util.TestUtils; 84 import com.android.window.flags.Flags; 85 86 import org.junit.After; 87 import org.junit.AfterClass; 88 import org.junit.Assume; 89 import org.junit.Before; 90 import org.junit.BeforeClass; 91 import org.junit.Rule; 92 import org.junit.Test; 93 import org.junit.rules.RuleChain; 94 import org.junit.runner.RunWith; 95 import org.mockito.ArgumentCaptor; 96 97 import java.util.concurrent.TimeoutException; 98 import java.util.concurrent.atomic.AtomicBoolean; 99 import java.util.concurrent.atomic.AtomicReference; 100 import java.util.function.Consumer; 101 102 /** 103 * Class for testing {@link MagnificationController} and the magnification overlay window. 104 */ 105 @AppModeFull 106 @RunWith(AndroidJUnit4.class) 107 @CddTest(requirements = {"3.10/C-1-1,C-1-2"}) 108 @Presubmit 109 public class AccessibilityMagnificationTest { 110 111 /** Maximum timeout when waiting for a magnification callback. */ 112 public static final int LISTENER_TIMEOUT_MILLIS = 500; 113 /** Maximum animation timeout when waiting for a magnification callback. */ 114 public static final int LISTENER_ANIMATION_TIMEOUT_MILLIS = 1000; 115 public static final int BOUNDS_TOLERANCE = 1; 116 public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = 117 "accessibility_display_magnification_enabled"; 118 119 /** Maximum timeout while waiting for a config to be updated */ 120 public static final int TIMEOUT_CONFIG_SECONDS = 15; 121 122 // From WindowManager.java. 123 public static final int TYPE_NAVIGATION_BAR_PANEL = 2024; 124 125 private static UiAutomation sUiAutomation; 126 127 private static final String TAG = "AccessibilityMagnificationTest"; 128 129 private StubMagnificationAccessibilityService mService; 130 private Instrumentation mInstrumentation; 131 132 private AccessibilityDumpOnFailureRule mDumpOnFailureRule = 133 new AccessibilityDumpOnFailureRule(); 134 135 private final ActivityTestRule<AccessibilityWindowQueryActivity> mActivityRule = 136 new ActivityTestRule<>(AccessibilityWindowQueryActivity.class, false, false); 137 138 private InstrumentedAccessibilityServiceTestRule<InstrumentedAccessibilityService> 139 mInstrumentedAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>( 140 InstrumentedAccessibilityService.class, false); 141 142 private InstrumentedAccessibilityServiceTestRule<StubMagnificationAccessibilityService> 143 mMagnificationAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>( 144 StubMagnificationAccessibilityService.class, false); 145 146 private final CheckFlagsRule mCheckFlagsRule = 147 DeviceFlagsValueProvider.createCheckFlagsRule(sUiAutomation); 148 149 @Rule 150 public final RuleChain mRuleChain = RuleChain 151 .outerRule(mActivityRule) 152 .around(mMagnificationAccessibilityServiceRule) 153 .around(mInstrumentedAccessibilityServiceRule) 154 .around(mDumpOnFailureRule) 155 .around(mCheckFlagsRule); 156 157 @BeforeClass oneTimeSetUp()158 public static void oneTimeSetUp() { 159 sUiAutomation = InstrumentationRegistry.getInstrumentation() 160 .getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES); 161 final AccessibilityServiceInfo info = sUiAutomation.getServiceInfo(); 162 info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS; 163 sUiAutomation.setServiceInfo(info); 164 } 165 166 @AfterClass postTestTearDown()167 public static void postTestTearDown() { 168 sUiAutomation.destroy(); 169 } 170 171 @Before setUp()172 public void setUp() throws Exception { 173 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 174 assumeFalse("Magnification is not supported on Automotive.", 175 isAutomotive(mInstrumentation.getTargetContext())); 176 ShellCommandBuilder.create(sUiAutomation) 177 .deleteSecureSetting(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED) 178 .run(); 179 // Starting the service will force the accessibility subsystem to examine its settings, so 180 // it will update magnification in the process to disable it. 181 mService = mMagnificationAccessibilityServiceRule.enableService(); 182 } 183 184 @After tearDown()185 public void tearDown() { 186 // Ensure the magnification is deactivated after each test case. For some test cases that 187 // would disable mService during the test, they still need to reset magnification themselves 188 // after the test. 189 if (mService != null) { 190 mService.runOnServiceSync(() -> { 191 mService.getMagnificationController().resetCurrentMagnification(/* animate= */ 192 false); 193 }); 194 } 195 196 // Since window magnification may need times to remove the magnification window, we would 197 // like to wait and ensure the overlays for magnification are removed here. 198 if (isMagnificationOverlayExisting() || doesAccessibilityMagnificationOverlayExist()) { 199 // Do nothing, we just want to wait for the event and check the 200 // overlays for magnification are removed 201 try { 202 sUiAutomation.executeAndWaitForEvent(() -> {}, 203 event -> !(isMagnificationOverlayExisting() 204 || doesAccessibilityMagnificationOverlayExist()), 205 5000); 206 } catch (TimeoutException timeoutException) { 207 // Double check the overlay is not exists in case there is no event sent 208 assertTrue(!(isMagnificationOverlayExisting() 209 || doesAccessibilityMagnificationOverlayExist())); 210 } 211 } 212 } 213 214 @Test testSetScale()215 public void testSetScale() { 216 final MagnificationController controller = mService.getMagnificationController(); 217 final float scale = 2.0f; 218 final AtomicBoolean result = new AtomicBoolean(); 219 220 mService.runOnServiceSync(() -> result.set(controller.setScale(scale, false))); 221 222 assertTrue(getSetterErrorMessage("Failed to set scale"), result.get()); 223 assertEquals("Failed to apply scale", scale, controller.getScale(), 0f); 224 225 mService.runOnServiceSync(() -> result.set(controller.reset(false))); 226 227 assertTrue("Failed to reset", result.get()); 228 assertEquals("Failed to apply reset", 1.0f, controller.getScale(), 0f); 229 } 230 231 @Test testSetScaleAndCenter()232 public void testSetScaleAndCenter() { 233 final MagnificationController controller = mService.getMagnificationController(); 234 final Region region = controller.getMagnificationRegion(); 235 final Rect bounds = region.getBounds(); 236 final float scale = 2.0f; 237 final float x = bounds.left + (bounds.width() / 4.0f); 238 final float y = bounds.top + (bounds.height() / 4.0f); 239 final AtomicBoolean setScale = new AtomicBoolean(); 240 final AtomicBoolean setCenter = new AtomicBoolean(); 241 final AtomicBoolean result = new AtomicBoolean(); 242 243 mService.runOnServiceSync(() -> { 244 setScale.set(controller.setScale(scale, false)); 245 setCenter.set(controller.setCenter(x, y, false)); 246 }); 247 248 assertTrue(getSetterErrorMessage("Failed to set scale"), setScale.get()); 249 assertEquals("Failed to apply scale", scale, controller.getScale(), 0f); 250 251 assertTrue(getSetterErrorMessage("Failed to set center"), setCenter.get()); 252 assertEquals("Failed to apply center X", x, controller.getCenterX(), 5.0f); 253 assertEquals("Failed to apply center Y", y, controller.getCenterY(), 5.0f); 254 255 mService.runOnServiceSync(() -> result.set(controller.reset(false))); 256 257 assertTrue("Failed to reset", result.get()); 258 assertEquals("Failed to apply reset", 1.0f, controller.getScale(), 0f); 259 } 260 261 @Test testSetMagnificationConfig_expectedConfig()262 public void testSetMagnificationConfig_expectedConfig() throws Exception { 263 final MagnificationController controller = mService.getMagnificationController(); 264 final Rect rect = controller.getMagnificationRegion().getBounds(); 265 final float scale = 2.0f; 266 final float x = rect.centerX(); 267 final float y = rect.centerY(); 268 final AtomicBoolean setConfig = new AtomicBoolean(); 269 270 final int targetMode = isWindowModeSupported(mInstrumentation.getContext()) 271 ? MAGNIFICATION_MODE_WINDOW : MAGNIFICATION_MODE_FULLSCREEN; 272 final MagnificationConfig config = new MagnificationConfig.Builder() 273 .setMode(targetMode) 274 .setScale(scale) 275 .setCenterX(x) 276 .setCenterY(y).build(); 277 278 mService.runOnServiceSync(() -> { 279 setConfig.set(controller.setMagnificationConfig(config, false)); 280 }); 281 assertTrue(getSetterErrorMessage("Failed to set config"), setConfig.get()); 282 283 waitUntilMagnificationConfig(controller, config); 284 assertConfigEquals(config, controller.getMagnificationConfig()); 285 286 final float newScale = scale + 1; 287 final Region region = controller.getMagnificationRegion(); 288 final Rect bounds = region.getBounds(); 289 final float newX = bounds.left + (bounds.width() / 4.0f); 290 final float newY = bounds.top + (bounds.height() / 4.0f); 291 final MagnificationConfig newConfig = new MagnificationConfig.Builder() 292 .setMode(MAGNIFICATION_MODE_FULLSCREEN).setScale(newScale).setCenterX( 293 newX).setCenterY( 294 newY).build(); 295 mService.runOnServiceSync(() -> { 296 controller.setMagnificationConfig(newConfig, false); 297 }); 298 assertTrue(getSetterErrorMessage("Failed to set config"), setConfig.get()); 299 300 waitUntilMagnificationConfig(controller, newConfig); 301 assertConfigEquals(newConfig, controller.getMagnificationConfig()); 302 } 303 304 @Test testSetConfigWithDefaultModeAndCenter_expectedConfig()305 public void testSetConfigWithDefaultModeAndCenter_expectedConfig() throws Exception { 306 final MagnificationController controller = mService.getMagnificationController(); 307 final Rect bounds = controller.getMagnificationRegion().getBounds(); 308 final float scale = 3.0f; 309 final float x = bounds.centerX(); 310 final float y = bounds.centerY(); 311 final AtomicBoolean setConfig = new AtomicBoolean(); 312 313 final int targetMode = isWindowModeSupported(mInstrumentation.getContext()) 314 ? MAGNIFICATION_MODE_WINDOW : MAGNIFICATION_MODE_FULLSCREEN; 315 final MagnificationConfig config = new MagnificationConfig.Builder() 316 .setMode(targetMode) 317 .setScale(scale) 318 .setCenterX(x) 319 .setCenterY(y) 320 .build(); 321 322 mService.runOnServiceSync( 323 () -> setConfig.set(controller.setMagnificationConfig(config, false))); 324 assertTrue(getSetterErrorMessage("Failed to set config"), setConfig.get()); 325 326 waitUntilMagnificationConfig(controller, config); 327 assertConfigEquals(config, controller.getMagnificationConfig()); 328 329 final float newScale = scale + 1; 330 final MagnificationConfig newConfig = new MagnificationConfig.Builder() 331 .setScale(newScale).build(); 332 final MagnificationConfig expectedConfig = obtainConfigBuilder(config).setScale( 333 newScale).build(); 334 335 mService.runOnServiceSync( 336 () -> setConfig.set(controller.setMagnificationConfig(newConfig, false))); 337 assertTrue(getSetterErrorMessage("Failed to set config"), setConfig.get()); 338 339 waitUntilMagnificationConfig(controller, expectedConfig); 340 assertConfigEquals(expectedConfig, controller.getMagnificationConfig()); 341 342 mService.runOnServiceSync( 343 () -> controller.resetCurrentMagnification(/* animate= */ false)); 344 } 345 346 @Test testSetConfigWithActivatedFalse_expectedConfig()347 public void testSetConfigWithActivatedFalse_expectedConfig() throws Exception { 348 final MagnificationController controller = mService.getMagnificationController(); 349 final Rect bounds = controller.getMagnificationRegion().getBounds(); 350 final float scale = 3.0f; 351 final float x = bounds.centerX(); 352 final float y = bounds.centerY(); 353 final AtomicBoolean setConfig = new AtomicBoolean(); 354 355 final int targetMode = isWindowModeSupported(mInstrumentation.getContext()) 356 ? MAGNIFICATION_MODE_WINDOW : MAGNIFICATION_MODE_FULLSCREEN; 357 final MagnificationConfig config = new MagnificationConfig.Builder() 358 .setMode(targetMode) 359 .setScale(scale) 360 .setCenterX(x) 361 .setCenterY(y) 362 .build(); 363 364 mService.runOnServiceSync( 365 () -> setConfig.set(controller.setMagnificationConfig(config, 366 /* animate= */ false))); 367 assertTrue(getSetterErrorMessage("Failed to set config"), setConfig.get()); 368 369 waitUntilMagnificationConfig(controller, config); 370 assertConfigEquals(config, controller.getMagnificationConfig()); 371 372 final MagnificationConfig newConfig = new MagnificationConfig.Builder() 373 .setActivated(false).build(); 374 final MagnificationConfig expectedConfig = obtainConfigBuilder(config).setActivated( 375 false).build(); 376 377 mService.runOnServiceSync( 378 () -> setConfig.set(controller.setMagnificationConfig(newConfig, 379 /* animate= */ false))); 380 assertTrue(getSetterErrorMessage("Failed to set config"), setConfig.get()); 381 382 waitUntilMagnificationConfig(controller, expectedConfig); 383 assertConfigEquals(expectedConfig, controller.getMagnificationConfig()); 384 } 385 386 @Test testSetConfigWithActivatedFalse_magnificationDisabled_expectedReturnedValue()387 public void testSetConfigWithActivatedFalse_magnificationDisabled_expectedReturnedValue() 388 throws Exception { 389 final MagnificationController controller = mService.getMagnificationController(); 390 final AtomicBoolean setConfig = new AtomicBoolean(); 391 392 final MagnificationConfig config = new MagnificationConfig.Builder() 393 .setActivated(false) 394 .build(); 395 396 mService.runOnServiceSync( 397 () -> setConfig.set(controller.setMagnificationConfig(config, 398 /* animate= */ false))); 399 assertFalse(getSetterErrorMessage("Failed to set config"), setConfig.get()); 400 401 waitUntilMagnificationConfig(controller, config); 402 } 403 404 @Test testSetFullScreenConfigWithDefaultValues_windowModeEnabled_expectedConfig()405 public void testSetFullScreenConfigWithDefaultValues_windowModeEnabled_expectedConfig() 406 throws Exception { 407 final boolean windowModeSupported = isWindowModeSupported(mInstrumentation.getContext()); 408 Assume.assumeTrue("window mode is not available", windowModeSupported); 409 410 final MagnificationController controller = mService.getMagnificationController(); 411 final Rect bounds = controller.getMagnificationRegion().getBounds(); 412 final float scale = 3.0f; 413 final float x = bounds.centerX(); 414 final float y = bounds.centerY(); 415 final AtomicBoolean setConfig = new AtomicBoolean(); 416 417 final MagnificationConfig config = new MagnificationConfig.Builder() 418 .setMode(MAGNIFICATION_MODE_WINDOW) 419 .setScale(scale) 420 .setCenterX(x) 421 .setCenterY(y).build(); 422 423 mService.runOnServiceSync( 424 () -> setConfig.set(controller.setMagnificationConfig(config, false))); 425 assertTrue(getSetterErrorMessage("Failed to set config"), setConfig.get()); 426 427 waitUntilMagnificationConfig(controller, config); 428 assertConfigEquals(config, controller.getMagnificationConfig()); 429 430 final MagnificationConfig newConfig = new MagnificationConfig.Builder() 431 .setMode(MAGNIFICATION_MODE_FULLSCREEN) 432 .build(); 433 434 mService.runOnServiceSync( 435 () -> setConfig.set(controller.setMagnificationConfig(newConfig, false))); 436 assertTrue("Set config should have failed but didn't", setConfig.get()); 437 438 final MagnificationConfig expectedConfig = obtainConfigBuilder(config).setMode( 439 MAGNIFICATION_MODE_FULLSCREEN).build(); 440 441 waitUntilMagnificationConfig(controller, expectedConfig); 442 assertConfigEquals(expectedConfig, controller.getMagnificationConfig()); 443 } 444 445 @Test testSetFullScreenConfigWithScaleOne_expectedConfig()446 public void testSetFullScreenConfigWithScaleOne_expectedConfig() { 447 final MagnificationController controller = mService.getMagnificationController(); 448 final float scale = 1.0f; 449 final AtomicBoolean setConfig = new AtomicBoolean(); 450 451 final MagnificationConfig config = new MagnificationConfig.Builder() 452 .setMode(MAGNIFICATION_MODE_FULLSCREEN) 453 .setScale(scale) 454 .build(); 455 456 mService.runOnServiceSync( 457 () -> setConfig.set(controller.setMagnificationConfig(config, 458 /* animate= */ false))); 459 assertTrue(getSetterErrorMessage("Failed to set config"), setConfig.get()); 460 assertTrue(controller.getMagnificationConfig().isActivated()); 461 assertEquals(1.0f, controller.getMagnificationConfig().getScale(), 0); 462 } 463 464 @Test testResetCurrentMagnification_fullScreenEnabled_expectedConfig()465 public void testResetCurrentMagnification_fullScreenEnabled_expectedConfig() 466 throws Exception { 467 final MagnificationController controller = mService.getMagnificationController(); 468 final Rect bounds = controller.getMagnificationRegion().getBounds(); 469 final float scale = 3.0f; 470 final float x = bounds.centerX(); 471 final float y = bounds.centerY(); 472 final AtomicBoolean setConfig = new AtomicBoolean(); 473 474 final MagnificationConfig config = new MagnificationConfig.Builder() 475 .setMode(MAGNIFICATION_MODE_FULLSCREEN) 476 .setScale(scale) 477 .setCenterX(x) 478 .setCenterY(y) 479 .build(); 480 481 mService.runOnServiceSync( 482 () -> setConfig.set(controller.setMagnificationConfig(config, 483 /* animate= */ false))); 484 assertTrue(getSetterErrorMessage("Failed to set config"), setConfig.get()); 485 486 waitUntilMagnificationConfig(controller, config); 487 assertConfigEquals(config, controller.getMagnificationConfig()); 488 489 mService.runOnServiceSync(() -> { 490 controller.resetCurrentMagnification(/* animate= */ false); 491 }); 492 493 assertFalse(controller.getMagnificationConfig().isActivated()); 494 assertEquals(1.0f, controller.getMagnificationConfig().getScale(), 0); 495 } 496 497 @Test testSetMagnificationConfig_legacyApiExpectedResult()498 public void testSetMagnificationConfig_legacyApiExpectedResult() { 499 final MagnificationController controller = mService.getMagnificationController(); 500 final Region region = controller.getMagnificationRegion(); 501 final Rect bounds = region.getBounds(); 502 final float scale = 2.0f; 503 final float x = bounds.left + (bounds.width() / 4.0f); 504 final float y = bounds.top + (bounds.height() / 4.0f); 505 final AtomicBoolean setConfig = new AtomicBoolean(); 506 final MagnificationConfig config = new MagnificationConfig.Builder() 507 .setMode(MAGNIFICATION_MODE_FULLSCREEN) 508 .setScale(scale) 509 .setCenterX(x) 510 .setCenterY(y).build(); 511 try { 512 mService.runOnServiceSync(() -> { 513 setConfig.set(controller.setMagnificationConfig(config, false)); 514 }); 515 assertTrue(getSetterErrorMessage("Failed to set config"), setConfig.get()); 516 517 assertEquals("Failed to apply scale", scale, controller.getScale(), 0f); 518 assertEquals("Failed to apply center X", x, controller.getCenterX(), 5.0f); 519 assertEquals("Failed to apply center Y", y, controller.getCenterY(), 5.0f); 520 } finally { 521 mService.runOnServiceSync(() -> controller.resetCurrentMagnification(false)); 522 } 523 } 524 525 @Test testSetWindowModeConfig_connectionReset_expectedResult()526 public void testSetWindowModeConfig_connectionReset_expectedResult() throws Exception { 527 Assume.assumeTrue(isWindowModeSupported(mInstrumentation.getContext())); 528 529 final MagnificationController controller = mService.getMagnificationController(); 530 final Rect bounds = controller.getMagnificationRegion().getBounds(); 531 final float scale = 2.0f; 532 final float x = bounds.centerX(); 533 final float y = bounds.centerY(); 534 535 final MagnificationConfig config = new MagnificationConfig.Builder() 536 .setMode(MAGNIFICATION_MODE_WINDOW) 537 .setScale(scale) 538 .setCenterX(x) 539 .setCenterY(y).build(); 540 541 mService.runOnServiceSync( 542 () -> controller.setMagnificationConfig(config, /* animate= */ false)); 543 544 waitUntilMagnificationConfig(controller, config); 545 546 // Test service is disabled and enabled to make the connection reset. 547 mService.runOnServiceSync(() -> mService.disableSelfAndRemove()); 548 mService = null; 549 InstrumentedAccessibilityService service = 550 mMagnificationAccessibilityServiceRule.enableService(); 551 MagnificationController controller2 = service.getMagnificationController(); 552 try { 553 final float newScale = scale + 1; 554 final float newX = x + bounds.width() / 4.0f; 555 final float newY = y + bounds.height() / 4.0f; 556 final MagnificationConfig newConfig = new MagnificationConfig.Builder() 557 .setMode(MAGNIFICATION_MODE_WINDOW) 558 .setScale(newScale) 559 .setCenterX(newX) 560 .setCenterY(newY).build(); 561 562 service.runOnServiceSync( 563 () -> controller2.setMagnificationConfig(newConfig, /* animate= */ false)); 564 565 waitUntilMagnificationConfig(controller2, newConfig); 566 } finally { 567 service.runOnServiceSync( 568 () -> controller2.resetCurrentMagnification(false)); 569 } 570 } 571 572 @Test 573 @RequiresFlagsDisabled(com.android.systemui.Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER) testSetWindowModeConfig_hasMagnificationOverlay()574 public void testSetWindowModeConfig_hasMagnificationOverlay() throws TimeoutException { 575 Assume.assumeTrue(isWindowModeSupported(mInstrumentation.getContext())); 576 577 final MagnificationController controller = mService.getMagnificationController(); 578 final MagnificationConfig config = new MagnificationConfig.Builder() 579 .setMode(MAGNIFICATION_MODE_WINDOW) 580 .setScale(2.0f) 581 .build(); 582 583 try { 584 sUiAutomation.executeAndWaitForEvent( 585 () -> controller.setMagnificationConfig(config, false), 586 event -> isMagnificationOverlayExisting(), 5000); 587 } finally { 588 mService.runOnServiceSync(() -> controller.resetCurrentMagnification(false)); 589 } 590 } 591 592 @Test 593 @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER) testSetWindowModeConfig_hasAccessibilityOverlay()594 public void testSetWindowModeConfig_hasAccessibilityOverlay() throws TimeoutException { 595 Assume.assumeTrue(isWindowModeSupported(mInstrumentation.getContext())); 596 597 final MagnificationController controller = mService.getMagnificationController(); 598 final MagnificationConfig config = new MagnificationConfig.Builder() 599 .setMode(MAGNIFICATION_MODE_WINDOW) 600 .setScale(2.0f) 601 .build(); 602 603 try { 604 sUiAutomation.executeAndWaitForEvent( 605 () -> controller.setMagnificationConfig(config, false), 606 event -> doesAccessibilityMagnificationOverlayExist(), 5000); 607 } finally { 608 mService.runOnServiceSync(() -> controller.resetCurrentMagnification(false)); 609 } 610 } 611 612 @Test 613 @RequiresFlagsDisabled(com.android.systemui.Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER) testServiceConnectionDisconnected_hasNoMagnificationOverlay()614 public void testServiceConnectionDisconnected_hasNoMagnificationOverlay() 615 throws TimeoutException { 616 Assume.assumeTrue(isWindowModeSupported(mInstrumentation.getContext())); 617 618 final MagnificationController controller = mService.getMagnificationController(); 619 final MagnificationConfig config = new MagnificationConfig.Builder() 620 .setMode(MAGNIFICATION_MODE_WINDOW) 621 .setScale(2.0f) 622 .build(); 623 624 try { 625 sUiAutomation.executeAndWaitForEvent( 626 () -> controller.setMagnificationConfig(config, false), 627 event -> isMagnificationOverlayExisting(), 5000); 628 629 sUiAutomation.executeAndWaitForEvent( 630 () -> mService.runOnServiceSync(() -> mService.disableSelfAndRemove()), 631 event -> !isMagnificationOverlayExisting(), 5000); 632 } finally { 633 mService.runOnServiceSync(() -> controller.resetCurrentMagnification(false)); 634 } 635 } 636 637 @Test 638 @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER) testServiceConnectionDisconnected_hasNoAccessibilityOverlay()639 public void testServiceConnectionDisconnected_hasNoAccessibilityOverlay() 640 throws TimeoutException { 641 Assume.assumeTrue(isWindowModeSupported(mInstrumentation.getContext())); 642 643 final MagnificationController controller = mService.getMagnificationController(); 644 final MagnificationConfig config = new MagnificationConfig.Builder() 645 .setMode(MAGNIFICATION_MODE_WINDOW) 646 .setScale(2.0f) 647 .build(); 648 649 try { 650 sUiAutomation.executeAndWaitForEvent( 651 () -> controller.setMagnificationConfig(config, false), 652 event -> doesAccessibilityMagnificationOverlayExist(), 5000); 653 654 sUiAutomation.executeAndWaitForEvent( 655 () -> mService.runOnServiceSync(() -> mService.disableSelfAndRemove()), 656 event -> !doesAccessibilityMagnificationOverlayExist(), 5000); 657 } finally { 658 mService.runOnServiceSync(() -> controller.resetCurrentMagnification(false)); 659 } 660 } 661 662 @Test testGetMagnificationConfig_setConfigByLegacyApi_expectedResult()663 public void testGetMagnificationConfig_setConfigByLegacyApi_expectedResult() { 664 final MagnificationController controller = mService.getMagnificationController(); 665 final Region region = controller.getMagnificationRegion(); 666 final Rect bounds = region.getBounds(); 667 final float scale = 2.0f; 668 final float x = bounds.left + (bounds.width() / 4.0f); 669 final float y = bounds.top + (bounds.height() / 4.0f); 670 mService.runOnServiceSync(() -> { 671 controller.setScale(scale, false); 672 controller.setCenter(x, y, false); 673 }); 674 675 final MagnificationConfig config = controller.getMagnificationConfig(); 676 677 assertTrue(config.isActivated()); 678 assertEquals("Failed to apply scale", scale, config.getScale(), 0f); 679 assertEquals("Failed to apply center X", x, config.getCenterX(), 5.0f); 680 assertEquals("Failed to apply center Y", y, config.getCenterY(), 5.0f); 681 } 682 683 @Test testGetMagnificationConfig_setConfigByLegacyApiAndReset_expectedResult()684 public void testGetMagnificationConfig_setConfigByLegacyApiAndReset_expectedResult() { 685 final MagnificationController controller = mService.getMagnificationController(); 686 final Region region = controller.getMagnificationRegion(); 687 final Rect bounds = region.getBounds(); 688 final float scale = 2.0f; 689 final float x = bounds.left + (bounds.width() / 4.0f); 690 final float y = bounds.top + (bounds.height() / 4.0f); 691 mService.runOnServiceSync(() -> { 692 controller.setScale(scale, /* animate= */ false); 693 controller.setCenter(x, y, /* animate= */ false); 694 controller.reset(/* animate= */ false); 695 }); 696 697 final MagnificationConfig config = controller.getMagnificationConfig(); 698 699 assertFalse(config.isActivated()); 700 } 701 702 @Test testListener()703 public void testListener() { 704 final MagnificationController controller = mService.getMagnificationController(); 705 final OnMagnificationChangedListener spyListener = mock( 706 OnMagnificationChangedListener.class); 707 final OnMagnificationChangedListener listener = 708 (controller1, region, scale, centerX, centerY) -> 709 spyListener.onMagnificationChanged(controller1, region, scale, centerX, 710 centerY); 711 controller.addListener(listener); 712 713 try { 714 final float scale = 2.0f; 715 final AtomicBoolean result = new AtomicBoolean(); 716 717 mService.runOnServiceSync(() -> result.set(controller.setScale(scale, false))); 718 719 assertTrue(getSetterErrorMessage("Failed to set scale"), result.get()); 720 verify(spyListener, timeout(LISTENER_TIMEOUT_MILLIS)).onMagnificationChanged( 721 eq(controller), any(Region.class), eq(scale), anyFloat(), anyFloat()); 722 723 mService.runOnServiceSync(() -> result.set(controller.reset(false))); 724 725 assertTrue("Failed to reset", result.get()); 726 verify(spyListener, timeout(LISTENER_TIMEOUT_MILLIS)).onMagnificationChanged( 727 eq(controller), any(Region.class), eq(1.0f), anyFloat(), anyFloat()); 728 } finally { 729 controller.removeListener(listener); 730 } 731 } 732 733 @Test testListener_changeConfigByLegacyApi_notifyConfigChanged()734 public void testListener_changeConfigByLegacyApi_notifyConfigChanged() { 735 final MagnificationController controller = mService.getMagnificationController(); 736 final OnMagnificationChangedListener listener = mock(OnMagnificationChangedListener.class); 737 controller.addListener(listener); 738 739 try { 740 final float scale = 2.0f; 741 final AtomicBoolean result = new AtomicBoolean(); 742 743 mService.runOnServiceSync(() -> result.set(controller.setScale(scale, false))); 744 745 assertTrue(getSetterErrorMessage("Failed to set scale"), result.get()); 746 final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass( 747 MagnificationConfig.class); 748 verify(listener, timeout(LISTENER_TIMEOUT_MILLIS)).onMagnificationChanged( 749 eq(controller), any(Region.class), configCaptor.capture()); 750 assertTrue(configCaptor.getValue().isActivated()); 751 assertEquals(scale, configCaptor.getValue().getScale(), 0); 752 753 reset(listener); 754 mService.runOnServiceSync(() -> result.set(controller.reset(false))); 755 756 assertTrue("Failed to reset", result.get()); 757 verify(listener, timeout(LISTENER_TIMEOUT_MILLIS)).onMagnificationChanged( 758 eq(controller), any(Region.class), configCaptor.capture()); 759 assertFalse(configCaptor.getValue().isActivated()); 760 assertEquals(1.0f, configCaptor.getValue().getScale(), 0); 761 } finally { 762 controller.removeListener(listener); 763 } 764 } 765 766 @Test testListener_magnificationConfigChangedWithoutAnimation_notifyConfigChanged()767 public void testListener_magnificationConfigChangedWithoutAnimation_notifyConfigChanged() 768 throws Exception { 769 final MagnificationController controller = mService.getMagnificationController(); 770 final OnMagnificationChangedListener listener = mock(OnMagnificationChangedListener.class); 771 controller.addListener(listener); 772 final int targetMode = isWindowModeSupported(mInstrumentation.getContext()) 773 ? MAGNIFICATION_MODE_WINDOW : MAGNIFICATION_MODE_FULLSCREEN; 774 final Rect bounds = controller.getMagnificationRegion().getBounds(); 775 final float scale = 2.0f; 776 final float x = bounds.centerX(); 777 final float y = bounds.centerY(); 778 final MagnificationConfig config = new MagnificationConfig.Builder() 779 .setMode(targetMode) 780 .setScale(scale) 781 .setCenterX(x) 782 .setCenterY(y) 783 .build(); 784 785 try { 786 mService.runOnServiceSync(() -> controller.setMagnificationConfig(config, false)); 787 waitUntilMagnificationConfig(controller, config); 788 789 final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass( 790 MagnificationConfig.class); 791 verify(listener, timeout(LISTENER_TIMEOUT_MILLIS)).onMagnificationChanged( 792 eq(controller), any(Region.class), configCaptor.capture()); 793 assertConfigEquals(config, configCaptor.getValue()); 794 795 final float newScale = scale + 1; 796 final float newX = x + bounds.width() / 4.0f; 797 final float newY = y + bounds.height() / 4.0f; 798 final MagnificationConfig fullscreenConfig = new MagnificationConfig.Builder() 799 .setMode(MAGNIFICATION_MODE_FULLSCREEN) 800 .setScale(newScale) 801 .setCenterX(newX) 802 .setCenterY(newY).build(); 803 804 reset(listener); 805 mService.runOnServiceSync(() -> { 806 controller.setMagnificationConfig(fullscreenConfig, false); 807 }); 808 waitUntilMagnificationConfig(controller, fullscreenConfig); 809 810 verify(listener, timeout(LISTENER_TIMEOUT_MILLIS)).onMagnificationChanged( 811 eq(controller), any(Region.class), configCaptor.capture()); 812 assertConfigEquals(fullscreenConfig, configCaptor.getValue()); 813 } finally { 814 mService.runOnServiceSync(() -> { 815 controller.resetCurrentMagnification(false); 816 controller.removeListener(listener); 817 }); 818 } 819 } 820 821 @Test testListener_magnificationConfigChangedWithAnimation_notifyConfigChanged()822 public void testListener_magnificationConfigChangedWithAnimation_notifyConfigChanged() { 823 final MagnificationController controller = mService.getMagnificationController(); 824 final OnMagnificationChangedListener listener = mock(OnMagnificationChangedListener.class); 825 controller.addListener(listener); 826 final int targetMode = isWindowModeSupported(mInstrumentation.getContext()) 827 ? MAGNIFICATION_MODE_WINDOW : MAGNIFICATION_MODE_FULLSCREEN; 828 final float scale = 2.0f; 829 final MagnificationConfig config = new MagnificationConfig.Builder() 830 .setMode(targetMode) 831 .setScale(scale).build(); 832 833 try { 834 mService.runOnServiceSync( 835 () -> controller.setMagnificationConfig(config, /* animate= */ true)); 836 837 verify(listener, timeout(LISTENER_ANIMATION_TIMEOUT_MILLIS)).onMagnificationChanged( 838 eq(controller), any(Region.class), any(MagnificationConfig.class)); 839 840 final float newScale = scale + 1; 841 final MagnificationConfig fullscreenConfig = new MagnificationConfig.Builder() 842 .setMode(MAGNIFICATION_MODE_FULLSCREEN) 843 .setScale(newScale).build(); 844 845 reset(listener); 846 mService.runOnServiceSync(() -> { 847 controller.setMagnificationConfig(fullscreenConfig, /* animate= */ true); 848 }); 849 850 verify(listener, timeout(LISTENER_ANIMATION_TIMEOUT_MILLIS)).onMagnificationChanged( 851 eq(controller), any(Region.class), any(MagnificationConfig.class)); 852 } finally { 853 mService.runOnServiceSync(() -> { 854 controller.resetCurrentMagnification(false); 855 controller.removeListener(listener); 856 }); 857 } 858 } 859 860 @Test testListener_transitionFromFullScreenToWindow_notifyConfigChanged()861 public void testListener_transitionFromFullScreenToWindow_notifyConfigChanged() 862 throws Exception { 863 Assume.assumeTrue(isWindowModeSupported(mInstrumentation.getContext())); 864 865 final MagnificationController controller = mService.getMagnificationController(); 866 final OnMagnificationChangedListener listener = mock(OnMagnificationChangedListener.class); 867 final Rect bounds = controller.getMagnificationRegion().getBounds(); 868 final float scale = 2.0f; 869 final float x = bounds.centerX(); 870 final float y = bounds.centerY(); 871 final MagnificationConfig windowConfig = new MagnificationConfig.Builder() 872 .setMode(MAGNIFICATION_MODE_WINDOW) 873 .setScale(scale) 874 .setCenterX(x) 875 .setCenterY(y) 876 .build(); 877 final float newScale = scale + 1; 878 final float newX = x + bounds.width() / 4.0f; 879 final float newY = y + bounds.height() / 4.0f; 880 final MagnificationConfig fullscreenConfig = new MagnificationConfig.Builder() 881 .setMode(MAGNIFICATION_MODE_FULLSCREEN) 882 .setScale(newScale) 883 .setCenterX(newX) 884 .setCenterY(newY).build(); 885 886 try { 887 final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass( 888 MagnificationConfig.class); 889 890 mService.runOnServiceSync(() -> { 891 controller.setMagnificationConfig(fullscreenConfig, false); 892 }); 893 waitUntilMagnificationConfig(controller, fullscreenConfig); 894 895 controller.addListener(listener); 896 mService.runOnServiceSync(() -> controller.setMagnificationConfig(windowConfig, false)); 897 waitUntilMagnificationConfig(controller, windowConfig); 898 899 verify(listener, timeout(LISTENER_TIMEOUT_MILLIS)).onMagnificationChanged( 900 eq(controller), any(Region.class), configCaptor.capture()); 901 assertConfigEquals(windowConfig, configCaptor.getValue()); 902 } finally { 903 mService.runOnServiceSync(() -> { 904 controller.resetCurrentMagnification(false); 905 controller.removeListener(listener); 906 }); 907 } 908 } 909 910 @Test testListener_resetCurrentMagnification_notifyConfigChanged()911 public void testListener_resetCurrentMagnification_notifyConfigChanged() throws Exception { 912 final MagnificationController controller = mService.getMagnificationController(); 913 final OnMagnificationChangedListener listener = mock(OnMagnificationChangedListener.class); 914 final int targetMode = isWindowModeSupported(mInstrumentation.getContext()) 915 ? MAGNIFICATION_MODE_WINDOW : MAGNIFICATION_MODE_FULLSCREEN; 916 final Rect bounds = controller.getMagnificationRegion().getBounds(); 917 final float scale = 2.0f; 918 final float x = bounds.centerX(); 919 final float y = bounds.centerY(); 920 final MagnificationConfig config = new MagnificationConfig.Builder() 921 .setMode(targetMode) 922 .setScale(scale) 923 .setCenterX(x) 924 .setCenterY(y) 925 .build(); 926 927 try { 928 mService.runOnServiceSync( 929 () -> controller.setMagnificationConfig(config, /* animate= */ false)); 930 waitUntilMagnificationConfig(controller, config); 931 932 controller.addListener(listener); 933 controller.resetCurrentMagnification(false); 934 935 final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass( 936 MagnificationConfig.class); 937 verify(listener, timeout(LISTENER_TIMEOUT_MILLIS)).onMagnificationChanged( 938 eq(controller), any(Region.class), configCaptor.capture()); 939 assertFalse(configCaptor.getValue().isActivated()); 940 assertEquals(1.0f, configCaptor.getValue().getScale(), 0); 941 } finally { 942 controller.removeListener(listener); 943 } 944 } 945 946 @Test testMagnificationServiceShutsDownWhileMagnifying_fullscreen_shouldReturnTo1x()947 public void testMagnificationServiceShutsDownWhileMagnifying_fullscreen_shouldReturnTo1x() { 948 final MagnificationController controller = mService.getMagnificationController(); 949 mService.runOnServiceSync(() -> controller.setScale(2.0f, false)); 950 951 mService.runOnServiceSync(() -> mService.disableSelf()); 952 mService = null; 953 InstrumentedAccessibilityService service = 954 mInstrumentedAccessibilityServiceRule.enableService(); 955 final MagnificationController controller2 = service.getMagnificationController(); 956 assertEquals("Magnification must reset when a service dies", 957 1.0f, controller2.getScale(), 0f); 958 assertFalse(controller2.getMagnificationConfig().isActivated()); 959 } 960 961 @Test testMagnificationServiceShutsDownWhileMagnifying_windowMode_shouldReturnTo1x()962 public void testMagnificationServiceShutsDownWhileMagnifying_windowMode_shouldReturnTo1x() 963 throws Exception { 964 Assume.assumeTrue(isWindowModeSupported(mInstrumentation.getContext())); 965 966 final MagnificationController controller = mService.getMagnificationController(); 967 final Rect bounds = controller.getMagnificationRegion().getBounds(); 968 final float scale = 2.0f; 969 final float x = bounds.centerX(); 970 final float y = bounds.centerY(); 971 972 final MagnificationConfig config = new MagnificationConfig.Builder() 973 .setMode(MAGNIFICATION_MODE_WINDOW) 974 .setScale(scale) 975 .setCenterX(x) 976 .setCenterY(y).build(); 977 978 mService.runOnServiceSync(() -> { 979 controller.setMagnificationConfig(config, false); 980 }); 981 waitUntilMagnificationConfig(controller, config); 982 983 mService.runOnServiceSync(() -> mService.disableSelf()); 984 mService = null; 985 InstrumentedAccessibilityService service = 986 mInstrumentedAccessibilityServiceRule.enableService(); 987 final MagnificationController controller2 = service.getMagnificationController(); 988 assertEquals("Magnification must reset when a service dies", 989 1.0f, controller2.getMagnificationConfig().getScale(), 0f); 990 assertFalse(controller2.getMagnificationConfig().isActivated()); 991 } 992 993 @Test testGetMagnificationRegion_whenCanControlMagnification_shouldNotBeEmpty()994 public void testGetMagnificationRegion_whenCanControlMagnification_shouldNotBeEmpty() { 995 final MagnificationController controller = mService.getMagnificationController(); 996 Region magnificationRegion = controller.getMagnificationRegion(); 997 assertFalse("Magnification region should not be empty when " 998 + "magnification is being actively controlled", magnificationRegion.isEmpty()); 999 } 1000 1001 @Test testGetMagnificationRegion_whenCantControlMagnification_shouldBeEmpty()1002 public void testGetMagnificationRegion_whenCantControlMagnification_shouldBeEmpty() { 1003 mService.runOnServiceSync(() -> mService.disableSelf()); 1004 mService = null; 1005 InstrumentedAccessibilityService service = 1006 mInstrumentedAccessibilityServiceRule.enableService(); 1007 final MagnificationController controller = service.getMagnificationController(); 1008 Region magnificationRegion = controller.getMagnificationRegion(); 1009 assertTrue("Magnification region should be empty when magnification " 1010 + "is not being actively controlled", magnificationRegion.isEmpty()); 1011 } 1012 1013 @Test testGetMagnificationRegion_whenMagnificationGesturesEnabled_shouldNotBeEmpty()1014 public void testGetMagnificationRegion_whenMagnificationGesturesEnabled_shouldNotBeEmpty() { 1015 ShellCommandBuilder.create(sUiAutomation) 1016 .putSecureSetting(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, "1") 1017 .run(); 1018 mService.runOnServiceSync(() -> mService.disableSelf()); 1019 mService = null; 1020 InstrumentedAccessibilityService service = 1021 mInstrumentedAccessibilityServiceRule.enableService(); 1022 try { 1023 final MagnificationController controller = service.getMagnificationController(); 1024 Region magnificationRegion = controller.getMagnificationRegion(); 1025 assertFalse("Magnification region should not be empty when magnification " 1026 + "gestures are active", magnificationRegion.isEmpty()); 1027 } finally { 1028 ShellCommandBuilder.create(sUiAutomation) 1029 .deleteSecureSetting(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED) 1030 .run(); 1031 } 1032 } 1033 1034 @Test testGetCurrentMagnificationRegion_fullscreen_exactRegionCenter()1035 public void testGetCurrentMagnificationRegion_fullscreen_exactRegionCenter() throws Exception { 1036 final MagnificationController controller = mService.getMagnificationController(); 1037 final Region region = controller.getMagnificationRegion(); 1038 final Rect bounds = region.getBounds(); 1039 final float scale = 2.0f; 1040 final float x = bounds.left + (bounds.width() / 4.0f); 1041 final float y = bounds.top + (bounds.height() / 4.0f); 1042 1043 final MagnificationConfig config = new MagnificationConfig.Builder() 1044 .setMode(MAGNIFICATION_MODE_FULLSCREEN) 1045 .setScale(scale) 1046 .setCenterX(x) 1047 .setCenterY(y).build(); 1048 try { 1049 mService.runOnServiceSync(() -> { 1050 controller.setMagnificationConfig(config, false); 1051 }); 1052 waitUntilMagnificationConfig(controller, config); 1053 1054 final Region magnificationRegion = controller.getCurrentMagnificationRegion(); 1055 assertFalse(magnificationRegion.isEmpty()); 1056 } finally { 1057 mService.runOnServiceSync(() -> { 1058 controller.resetCurrentMagnification(false); 1059 }); 1060 } 1061 } 1062 1063 @Test testMagnificationRegion_hasNonMagnifiableWindow_excludeWindowExactTouchableRegion()1064 public void testMagnificationRegion_hasNonMagnifiableWindow_excludeWindowExactTouchableRegion() 1065 throws Exception { 1066 final MagnificationController controller = mService.getMagnificationController(); 1067 final Region expectedMagnificationRegion = controller.getMagnificationRegion(); 1068 final Rect magnifyBounds = controller.getMagnificationRegion().getBounds(); 1069 1070 mService.runOnServiceSync(() -> controller.setScale(4.0f, /* animate =*/ false)); 1071 1072 Button createdButton = null; 1073 try { 1074 // We should ensure all the button area is in magnifyBounds for the following tests 1075 Button button = addNonMagnifiableWindow(R.string.button1, "button1", params -> { 1076 params.gravity = Gravity.LEFT | Gravity.TOP; 1077 params.x = magnifyBounds.left; 1078 params.y = magnifyBounds.top; 1079 params.width = magnifyBounds.width(); 1080 params.height = magnifyBounds.height() / 8; 1081 }); 1082 createdButton = button; 1083 1084 // Set two small rects at the top-left and right-bottom corner as the touchable region 1085 Rect buttonBounds = new Rect(); 1086 button.getBoundsOnScreen(buttonBounds, false); 1087 int touchableRegionWidth = buttonBounds.width() / 4; 1088 int touchableRegionHeight = buttonBounds.height() / 4; 1089 Rect touchableRect1 = new Rect(0, 0, touchableRegionWidth, touchableRegionHeight); 1090 Rect touchableRect2 = new Rect( 1091 buttonBounds.width() - touchableRegionWidth, 1092 buttonBounds.height() - touchableRegionHeight, 1093 buttonBounds.width(), 1094 buttonBounds.height()); 1095 Region touchableRegion = new Region(); 1096 touchableRegion.op(touchableRect1, Region.Op.UNION); 1097 touchableRegion.op(touchableRect2, Region.Op.UNION); 1098 mInstrumentation.runOnMainSync(() -> 1099 button.getRootSurfaceControl().setTouchableRegion(touchableRegion)); 1100 1101 // Offset the touchable rects to screen coordinates 1102 touchableRect1.offset(buttonBounds.left, buttonBounds.top); 1103 touchableRect2.offset(buttonBounds.left, buttonBounds.top); 1104 TestUtils.waitUntil( 1105 "The updated magnification region is not expected. expected: " 1106 + expectedMagnificationRegion + " , actual: " 1107 + controller.getMagnificationRegion() + ", touchableRect1: " 1108 + touchableRect1 + ", touchableRect2: " + touchableRect2, 1109 TIMEOUT_CONFIG_SECONDS, 1110 () -> { 1111 final Region magnificationRegion = controller.getMagnificationRegion(); 1112 1113 Region tmpRegion = new Region(); 1114 1115 // magnificationRegion should not intersect with real touchable rects 1116 tmpRegion.set(magnificationRegion); 1117 tmpRegion.op(touchableRect1, Region.Op.INTERSECT); 1118 if (!tmpRegion.isEmpty()) { 1119 return false; 1120 } 1121 tmpRegion.set(magnificationRegion); 1122 tmpRegion.op(touchableRect2, Region.Op.INTERSECT); 1123 if (!tmpRegion.isEmpty()) { 1124 return false; 1125 } 1126 1127 // The region united by magnificationRegion and touchable rects 1128 // should equal to the original region 1129 tmpRegion.set(magnificationRegion); 1130 tmpRegion.union(touchableRect1); 1131 tmpRegion.union(touchableRect2); 1132 return expectedMagnificationRegion.equals(tmpRegion); 1133 }); 1134 } finally { 1135 if (createdButton != null) { 1136 mInstrumentation.getContext().getSystemService(WindowManager.class).removeView( 1137 createdButton); 1138 } 1139 // After the non-magnifiable window is removed, the magnificationRegion should restore 1140 // to the original region. 1141 waitUntilMagnificationRegion(controller, expectedMagnificationRegion); 1142 1143 mService.runOnServiceSync(() -> controller.reset(/* animate =*/ false)); 1144 } 1145 } 1146 1147 @Test testGetCurrentMagnificationRegion_windowMode_exactRegionCenter()1148 public void testGetCurrentMagnificationRegion_windowMode_exactRegionCenter() throws Exception { 1149 Assume.assumeTrue(isWindowModeSupported(mInstrumentation.getContext())); 1150 1151 final MagnificationController controller = mService.getMagnificationController(); 1152 final Rect bounds = controller.getMagnificationRegion().getBounds(); 1153 final float scale = 2.0f; 1154 final float x = bounds.centerX(); 1155 final float y = bounds.centerY(); 1156 1157 final MagnificationConfig config = new MagnificationConfig.Builder() 1158 .setMode(MAGNIFICATION_MODE_WINDOW) 1159 .setScale(scale) 1160 .setCenterX(x) 1161 .setCenterY(y).build(); 1162 try { 1163 mService.runOnServiceSync(() -> { 1164 controller.setMagnificationConfig(config, false); 1165 }); 1166 waitUntilMagnificationConfig(controller, config); 1167 1168 final Region magnificationRegion = controller.getCurrentMagnificationRegion(); 1169 final Rect magnificationBounds = magnificationRegion.getBounds(); 1170 assertEquals(magnificationBounds.exactCenterX(), x, BOUNDS_TOLERANCE); 1171 assertEquals(magnificationBounds.exactCenterY(), y, BOUNDS_TOLERANCE); 1172 } finally { 1173 mService.runOnServiceSync(() -> { 1174 controller.resetCurrentMagnification(false); 1175 }); 1176 } 1177 } 1178 1179 @Test testResetCurrentMagnificationRegion_WindowMode_regionIsEmpty()1180 public void testResetCurrentMagnificationRegion_WindowMode_regionIsEmpty() throws Exception { 1181 Assume.assumeTrue(isWindowModeSupported(mInstrumentation.getContext())); 1182 1183 final MagnificationController controller = mService.getMagnificationController(); 1184 final Rect bounds = controller.getMagnificationRegion().getBounds(); 1185 final float scale = 2.0f; 1186 final float x = bounds.centerX(); 1187 final float y = bounds.centerY(); 1188 1189 final MagnificationConfig config = new MagnificationConfig.Builder() 1190 .setMode(MAGNIFICATION_MODE_WINDOW) 1191 .setScale(scale) 1192 .setCenterX(x) 1193 .setCenterY(y).build(); 1194 1195 mService.runOnServiceSync(() -> { 1196 controller.setMagnificationConfig(config, false); 1197 }); 1198 waitUntilMagnificationConfig(controller, config); 1199 1200 assertEquals(scale, controller.getMagnificationConfig().getScale(), 0); 1201 1202 mService.runOnServiceSync(() -> { 1203 controller.resetCurrentMagnification(false); 1204 }); 1205 1206 assertFalse(controller.getMagnificationConfig().isActivated()); 1207 assertEquals(1.0f, controller.getMagnificationConfig().getScale(), 0); 1208 assertTrue(controller.getCurrentMagnificationRegion().isEmpty()); 1209 } 1210 1211 @Test testAnimatingMagnification()1212 public void testAnimatingMagnification() throws InterruptedException { 1213 final MagnificationController controller = mService.getMagnificationController(); 1214 final int timeBetweenAnimationChanges = 100; 1215 1216 final float scale1 = 5.0f; 1217 final float x1 = 500; 1218 final float y1 = 1000; 1219 1220 final float scale2 = 4.0f; 1221 final float x2 = 500; 1222 final float y2 = 1500; 1223 1224 final float scale3 = 2.1f; 1225 final float x3 = 700; 1226 final float y3 = 700; 1227 1228 for (int i = 0; i < 5; i++) { 1229 mService.runOnServiceSync(() -> { 1230 controller.setScale(scale1, true); 1231 controller.setCenter(x1, y1, true); 1232 }); 1233 1234 Thread.sleep(timeBetweenAnimationChanges); 1235 1236 mService.runOnServiceSync(() -> { 1237 controller.setScale(scale2, true); 1238 controller.setCenter(x2, y2, true); 1239 }); 1240 1241 Thread.sleep(timeBetweenAnimationChanges); 1242 1243 mService.runOnServiceSync(() -> { 1244 controller.setScale(scale3, true); 1245 controller.setCenter(x3, y3, true); 1246 }); 1247 1248 Thread.sleep(timeBetweenAnimationChanges); 1249 } 1250 1251 mService.runOnServiceSync(() -> { 1252 controller.resetCurrentMagnification(/* animate= */ false); 1253 }); 1254 } 1255 1256 @Test 1257 @FlakyTest testA11yNodeInfoVisibility_whenOutOfMagnifiedArea_shouldVisible()1258 public void testA11yNodeInfoVisibility_whenOutOfMagnifiedArea_shouldVisible() 1259 throws Exception{ 1260 Activity activity = launchActivityAndWaitForItToBeOnscreen( 1261 mInstrumentation, sUiAutomation, mActivityRule); 1262 final MagnificationController controller = mService.getMagnificationController(); 1263 final Rect magnifyBounds = controller.getMagnificationRegion().getBounds(); 1264 final float scale = 8.0f; 1265 final Button button = activity.findViewById(R.id.button1); 1266 adjustViewBoundsIfNeeded(button, scale, magnifyBounds); 1267 1268 final AccessibilityNodeInfo buttonNode = sUiAutomation.getRootInActiveWindow() 1269 .findAccessibilityNodeInfosByViewId( 1270 "android.accessibilityservice.cts:id/button1").get(0); 1271 assertNotNull("Can't find button on the screen", buttonNode); 1272 assertTrue("Button should be visible", buttonNode.isVisibleToUser()); 1273 1274 // Get right-bottom center position 1275 final float centerX = magnifyBounds.left + (((float) magnifyBounds.width() / (2.0f * scale)) 1276 * ((2.0f * scale) - 1.0f)); 1277 final float centerY = magnifyBounds.top + (((float) magnifyBounds.height() / (2.0f * scale)) 1278 * ((2.0f * scale) - 1.0f)); 1279 final Rect boundsBeforeMagnify = new Rect(); 1280 buttonNode.getBoundsInScreen(boundsBeforeMagnify); 1281 final Rect boundsAfterMagnify = new Rect(); 1282 try { 1283 waitOnMagnificationChanged(controller, scale, centerX, centerY); 1284 1285 final DisplayMetrics displayMetrics = new DisplayMetrics(); 1286 activity.getDisplay().getMetrics(displayMetrics); 1287 final Rect displayRect = new Rect(0, 0, 1288 displayMetrics.widthPixels, displayMetrics.heightPixels); 1289 1290 // The boundsInScreen of button is adjusted to outside of screen by framework, 1291 // for example, Rect(-xxx, -xxx, -xxx, -xxx). Intersection of button and screen 1292 // should be empty. 1293 TestUtils.waitUntil("Button shouldn't be on the screen, screen is " + displayRect 1294 + ", button bounds before magnified is " + boundsBeforeMagnify 1295 + ", button bounds after layout is " + boundsAfterMagnify, 1296 TIMEOUT_CONFIG_SECONDS, 1297 () -> { 1298 buttonNode.refresh(); 1299 buttonNode.getBoundsInScreen(boundsAfterMagnify); 1300 return !Rect.intersects(displayRect, boundsAfterMagnify); 1301 }); 1302 assertTrue("Button should be visible", buttonNode.isVisibleToUser()); 1303 } finally { 1304 mService.runOnServiceSync(() -> controller.reset(false)); 1305 } 1306 } 1307 1308 @Test testShowMagnifiableWindow_outOfTheMagnifiedRegion_moveMagnification()1309 public void testShowMagnifiableWindow_outOfTheMagnifiedRegion_moveMagnification() 1310 throws Exception { 1311 Activity activity = launchActivityAndWaitForItToBeOnscreen( 1312 mInstrumentation, sUiAutomation, mActivityRule); 1313 1314 final MagnificationController controller = mService.getMagnificationController(); 1315 final Rect magnifyBounds = controller.getMagnificationRegion().getBounds(); 1316 final float scale = 4.0f; 1317 // Get right-bottom center position 1318 final float centerX = magnifyBounds.left + (((float) magnifyBounds.width() / (2.0f * scale)) 1319 * ((2.0f * scale) - 1.0f)); 1320 final float centerY = magnifyBounds.top + (((float) magnifyBounds.height() / (2.0f * scale)) 1321 * ((2.0f * scale) - 1.0f)); 1322 1323 Button createdButton = null; 1324 1325 try { 1326 waitOnMagnificationChanged(controller, scale, centerX, centerY); 1327 1328 // Add window at left-top position 1329 Button button = addMagnifiableWindowInActivity(R.string.button1, params -> { 1330 params.width = magnifyBounds.width() / 4; 1331 params.height = magnifyBounds.height() / 4; 1332 params.gravity = Gravity.TOP | Gravity.LEFT; 1333 }, activity); 1334 createdButton = button; 1335 1336 TestUtils.waitUntil("bounds are not intersected:", TIMEOUT_CONFIG_SECONDS, 1337 () -> { 1338 Rect magnifiedArea = getMagnifiedArea(controller); 1339 Rect buttonBounds = new Rect(); 1340 button.getBoundsOnScreen(buttonBounds, false); 1341 // magnification should be moved to make the button visible on screen 1342 return magnifiedArea.intersect(buttonBounds); 1343 }); 1344 1345 } finally { 1346 if (createdButton != null) { 1347 activity.getWindowManager().removeView(createdButton); 1348 } 1349 mService.runOnServiceSync(() -> controller.reset(false)); 1350 } 1351 } 1352 1353 @Test testShowNonMagnifiableWindow_outOfTheMagnifiedRegion_shouldNotMoveMagnification()1354 public void testShowNonMagnifiableWindow_outOfTheMagnifiedRegion_shouldNotMoveMagnification() 1355 throws Exception { 1356 final MagnificationController controller = mService.getMagnificationController(); 1357 final Rect magnifyBounds = controller.getMagnificationRegion().getBounds(); 1358 final float scale = 4.0f; 1359 // Get right-bottom center position 1360 final float centerX = magnifyBounds.left + (((float) magnifyBounds.width() / (2.0f * scale)) 1361 * ((2.0f * scale) - 1.0f)); 1362 final float centerY = magnifyBounds.top + (((float) magnifyBounds.height() / (2.0f * scale)) 1363 * ((2.0f * scale) - 1.0f)); 1364 1365 Button createdButton = null; 1366 1367 try { 1368 waitOnMagnificationChanged(controller, scale, centerX, centerY); 1369 1370 // Add window at left-top position 1371 Button button = addNonMagnifiableWindow(R.string.button1, "button1", params -> { 1372 params.width = magnifyBounds.width() / 4; 1373 params.height = magnifyBounds.height() / 4; 1374 params.gravity = Gravity.TOP | Gravity.LEFT; 1375 }); 1376 createdButton = button; 1377 // wait 1 second to check if the magnification would move. 1378 SystemClock.sleep(1000); 1379 Rect magnifiedArea = getMagnifiedArea(controller); 1380 Rect buttonBounds = new Rect(); 1381 button.getBoundsOnScreen(buttonBounds, false); 1382 // magnification should not be moved since the button is already visible on screen 1383 assertFalse(magnifiedArea.intersect(buttonBounds)); 1384 assertEquals(centerX, controller.getCenterX(), 0f); 1385 assertEquals(centerY, controller.getCenterY(), 0f); 1386 } finally { 1387 if (createdButton != null) { 1388 mInstrumentation.getContext().getSystemService(WindowManager.class).removeView( 1389 createdButton); 1390 } 1391 mService.runOnServiceSync(() -> controller.reset(false)); 1392 } 1393 } 1394 isMagnificationOverlayExisting()1395 private boolean isMagnificationOverlayExisting() { 1396 return sUiAutomation.getWindows().stream().anyMatch( 1397 accessibilityWindowInfo -> accessibilityWindowInfo.getType() 1398 == AccessibilityWindowInfo.TYPE_MAGNIFICATION_OVERLAY); 1399 } 1400 doesAccessibilityMagnificationOverlayExist()1401 private boolean doesAccessibilityMagnificationOverlayExist() { 1402 return sUiAutomation.getWindows().stream().anyMatch( 1403 // TODO: b/335440685 - Move to TYPE_ACCESSIBILITY_OVERLAY after the issues with 1404 // that type preventing swipe to navigate are resolved. 1405 accessibilityWindowInfo -> accessibilityWindowInfo.getType() 1406 == AccessibilityWindowInfo.TYPE_MAGNIFICATION_OVERLAY); 1407 } 1408 getMagnifiedArea(MagnificationController magnificationController)1409 private Rect getMagnifiedArea(MagnificationController magnificationController) { 1410 final Rect magnifyBounds = magnificationController.getMagnificationRegion().getBounds(); 1411 final float scale = magnificationController.getScale(); 1412 final float centerX = magnificationController.getCenterX(); 1413 final float centerY = magnificationController.getCenterX(); 1414 1415 float halfWidth = magnifyBounds.width() / (2.0f * scale); 1416 float halfHeight = magnifyBounds.height() / (2.0f * scale); 1417 return new Rect( 1418 (int) (centerX - halfWidth), 1419 (int) (centerY - halfHeight), 1420 (int) (centerX + halfWidth), 1421 (int) (centerY + halfHeight)); 1422 } 1423 addNonMagnifiableWindow(int btnTextRes, String title, Consumer<WindowManager.LayoutParams> configure)1424 private Button addNonMagnifiableWindow(int btnTextRes, String title, 1425 Consumer<WindowManager.LayoutParams> configure) throws Exception { 1426 final WindowManager wm = 1427 mInstrumentation.getContext().getSystemService(WindowManager.class); 1428 AtomicReference<Button> result = new AtomicReference<>(); 1429 1430 sUiAutomation.executeAndWaitForEvent(() -> { 1431 mInstrumentation.runOnMainSync(() -> { 1432 final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); 1433 params.width = WindowManager.LayoutParams.MATCH_PARENT; 1434 params.height = WindowManager.LayoutParams.WRAP_CONTENT; 1435 params.type = TYPE_NAVIGATION_BAR_PANEL; 1436 params.accessibilityTitle = title; 1437 params.format = PixelFormat.TRANSPARENT; 1438 configure.accept(params); 1439 1440 final Button button = new Button(mInstrumentation.getContext()); 1441 button.setText(btnTextRes); 1442 result.set(button); 1443 SystemUtil.runWithShellPermissionIdentity(sUiAutomation, 1444 () -> wm.addView(button, params), 1445 "android.permission.INTERNAL_SYSTEM_WINDOW", 1446 "android.permission.STATUS_BAR_SERVICE"); 1447 }); 1448 }, filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_ADDED), DEFAULT_TIMEOUT_MS); 1449 return result.get(); 1450 } 1451 addMagnifiableWindowInActivity(int btnTextRes, Consumer<WindowManager.LayoutParams> configure, Activity activity)1452 private Button addMagnifiableWindowInActivity(int btnTextRes, 1453 Consumer<WindowManager.LayoutParams> configure, 1454 Activity activity) throws TimeoutException { 1455 AtomicReference<Button> result = new AtomicReference<>(); 1456 sUiAutomation.executeAndWaitForEvent(() -> { 1457 mInstrumentation.runOnMainSync(() -> { 1458 final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); 1459 params.width = WindowManager.LayoutParams.MATCH_PARENT; 1460 params.height = WindowManager.LayoutParams.WRAP_CONTENT; 1461 params.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 1462 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR 1463 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 1464 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; 1465 params.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; 1466 params.token = activity.getWindow().getDecorView().getWindowToken(); 1467 configure.accept(params); 1468 1469 final Button button = new Button(activity); 1470 button.setText(btnTextRes); 1471 result.set(button); 1472 activity.getWindowManager().addView(button, params); 1473 }); 1474 }, filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_ADDED), DEFAULT_TIMEOUT_MS); 1475 return result.get(); 1476 } 1477 waitOnMagnificationChanged(MagnificationController controller, float newScale, float newCenterX, float newCenterY)1478 private void waitOnMagnificationChanged(MagnificationController controller, float newScale, 1479 float newCenterX, float newCenterY) { 1480 final OnMagnificationChangedListener spyListener = mock( 1481 OnMagnificationChangedListener.class); 1482 final OnMagnificationChangedListener listener = 1483 (controller1, region, scale, centerX, centerY) -> 1484 spyListener.onMagnificationChanged(controller1, region, scale, centerX, 1485 centerY); 1486 controller.addListener(listener); 1487 try { 1488 final AtomicBoolean setScale = new AtomicBoolean(); 1489 final AtomicBoolean setCenter = new AtomicBoolean(); 1490 mService.runOnServiceSync(() -> { 1491 setScale.set(controller.setScale(newScale, false)); 1492 }); 1493 1494 assertTrue(getSetterErrorMessage("Failed to set scale"), setScale.get()); 1495 verify(spyListener, timeout(LISTENER_TIMEOUT_MILLIS)).onMagnificationChanged( 1496 eq(controller), any(Region.class), eq(newScale), anyFloat(), anyFloat()); 1497 1498 reset(spyListener); 1499 mService.runOnServiceSync(() -> { 1500 setCenter.set(controller.setCenter(newCenterX, newCenterY, false)); 1501 }); 1502 1503 assertTrue(getSetterErrorMessage("Failed to set center"), setCenter.get()); 1504 verify(spyListener, timeout(LISTENER_TIMEOUT_MILLIS)).onMagnificationChanged( 1505 eq(controller), any(Region.class), anyFloat(), eq(newCenterX), eq(newCenterY)); 1506 } finally { 1507 controller.removeListener(listener); 1508 } 1509 } 1510 1511 /** 1512 * Adjust top-left view bounds if it's still in the magnified viewport after sets magnification 1513 * scale and move centers to bottom-right. 1514 */ adjustViewBoundsIfNeeded(View topLeftview, float scale, Rect magnifyBounds)1515 private void adjustViewBoundsIfNeeded(View topLeftview, float scale, Rect magnifyBounds) { 1516 final Point magnifyViewportTopLeft = new Point(); 1517 magnifyViewportTopLeft.x = (int)((scale - 1.0f) * ((float) magnifyBounds.width() / scale)); 1518 magnifyViewportTopLeft.y = (int)((scale - 1.0f) * ((float) magnifyBounds.height() / scale)); 1519 magnifyViewportTopLeft.offset(magnifyBounds.left, magnifyBounds.top); 1520 1521 final int[] viewLocation = new int[2]; 1522 topLeftview.getLocationOnScreen(viewLocation); 1523 final Rect viewBounds = new Rect(viewLocation[0], viewLocation[1], 1524 viewLocation[0] + topLeftview.getWidth(), 1525 viewLocation[1] + topLeftview.getHeight()); 1526 if (viewBounds.right < magnifyViewportTopLeft.x 1527 && viewBounds.bottom < magnifyViewportTopLeft.y) { 1528 // no need 1529 return; 1530 } 1531 1532 final ViewGroup.LayoutParams layoutParams = topLeftview.getLayoutParams(); 1533 if (viewBounds.right >= magnifyViewportTopLeft.x) { 1534 layoutParams.width = topLeftview.getWidth() - 1 1535 - (viewBounds.right - magnifyViewportTopLeft.x); 1536 assertTrue("Needs to fix layout", layoutParams.width > 0); 1537 } 1538 if (viewBounds.bottom >= magnifyViewportTopLeft.y) { 1539 layoutParams.height = topLeftview.getHeight() - 1 1540 - (viewBounds.bottom - magnifyViewportTopLeft.y); 1541 assertTrue("Needs to fix layout", layoutParams.height > 0); 1542 } 1543 mInstrumentation.runOnMainSync(() -> topLeftview.setLayoutParams(layoutParams)); 1544 // Waiting for UI refresh 1545 mInstrumentation.waitForIdleSync(); 1546 } 1547 waitUntilMagnificationConfig(MagnificationController controller, MagnificationConfig config)1548 private void waitUntilMagnificationConfig(MagnificationController controller, 1549 MagnificationConfig config) throws Exception { 1550 TestUtils.waitUntil( 1551 "Failed to apply the config. expected: " + config + " , actual: " 1552 + controller.getMagnificationConfig(), TIMEOUT_CONFIG_SECONDS, 1553 () -> { 1554 final MagnificationConfig actualConfig = controller.getMagnificationConfig(); 1555 Log.d(TAG, "Polling config: " + actualConfig.toString()); 1556 // If expected config activated is false, we just need to verify the activated 1557 // value is the same. Otherwise, we need to check all the actual values are 1558 // equal to the expected values. 1559 if (config.isActivated()) { 1560 return actualConfig.getMode() == config.getMode() 1561 && actualConfig.isActivated() == config.isActivated() 1562 && Float.compare(actualConfig.getScale(), config.getScale()) == 0 1563 && (Math.abs(actualConfig.getCenterX() - config.getCenterX()) 1564 <= BOUNDS_TOLERANCE) 1565 && (Math.abs(actualConfig.getCenterY() - config.getCenterY()) 1566 <= BOUNDS_TOLERANCE); 1567 } else { 1568 return actualConfig.isActivated() == config.isActivated(); 1569 } 1570 }); 1571 } 1572 waitUntilMagnificationRegion(MagnificationController controller, Region expectedRegion)1573 private void waitUntilMagnificationRegion(MagnificationController controller, 1574 Region expectedRegion) throws Exception { 1575 TestUtils.waitUntil( 1576 "Failed to apply the region. expected: " + expectedRegion + " , actual: " 1577 + controller.getMagnificationRegion(), TIMEOUT_CONFIG_SECONDS, 1578 () -> { 1579 final Region actualRegion = controller.getMagnificationRegion(); 1580 Log.d(TAG, "Polling region: " + actualRegion); 1581 return actualRegion.equals(expectedRegion); 1582 }); 1583 } 1584 assertConfigEquals(MagnificationConfig expected, MagnificationConfig result)1585 private void assertConfigEquals(MagnificationConfig expected, MagnificationConfig result) { 1586 // If expected config activated is false, we just need to verify the activated 1587 // value is the same. Otherwise, we need to check all the actual values are 1588 // equal to the expected values. 1589 if (expected.isActivated()) { 1590 assertEquals("Failed to apply mode", expected.getMode(), 1591 result.getMode(), 0f); 1592 assertEquals("Failed to apply activated", expected.isActivated(), 1593 result.isActivated()); 1594 assertEquals("Failed to apply scale", expected.getScale(), 1595 result.getScale(), 0f); 1596 assertEquals("Failed to apply center X", expected.getCenterX(), 1597 result.getCenterX(), 5.0f); 1598 assertEquals("Failed to apply center Y", expected.getCenterY(), 1599 result.getCenterY(), 5.0f); 1600 } else { 1601 assertEquals("Failed to apply activated", expected.isActivated(), 1602 result.isActivated()); 1603 } 1604 } 1605 isWindowModeSupported(Context context)1606 private static boolean isWindowModeSupported(Context context) { 1607 PackageManager pm = context.getPackageManager(); 1608 final boolean isWatch = pm.hasSystemFeature(PackageManager.FEATURE_WATCH); 1609 return pm.hasSystemFeature(FEATURE_WINDOW_MAGNIFICATION) && !isWatch; 1610 } 1611 obtainConfigBuilder(MagnificationConfig config)1612 private static MagnificationConfig.Builder obtainConfigBuilder(MagnificationConfig config) { 1613 MagnificationConfig.Builder builder = new MagnificationConfig.Builder(); 1614 builder.setMode(config.getMode()) 1615 .setScale(config.getScale()) 1616 .setCenterX(config.getCenterX()) 1617 .setCenterY(config.getCenterY()); 1618 return builder; 1619 } 1620 getSetterErrorMessage(String rawMessage)1621 private String getSetterErrorMessage(String rawMessage) { 1622 if (Flags.alwaysDrawMagnificationFullscreenBorder()) { 1623 String postfix = ". The failure may be because the service connection does not exist" 1624 + ", the given setter info does not make the magnification spec changed" 1625 + ", or the SystemUI connection does not exist." 1626 + " Please ensure your SystemUI implements the IMagnificationConnection AIDL."; 1627 return rawMessage + postfix; 1628 } else { 1629 return rawMessage; 1630 } 1631 } 1632 } 1633