1 /* 2 * Copyright (C) 2020 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 com.android.systemui.accessibility; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertTrue; 22 import static org.mockito.ArgumentMatchers.anyBoolean; 23 import static org.mockito.ArgumentMatchers.anyFloat; 24 import static org.mockito.Mockito.atLeast; 25 import static org.mockito.Mockito.never; 26 import static org.mockito.Mockito.spy; 27 import static org.mockito.Mockito.verify; 28 29 import android.animation.ValueAnimator; 30 import android.annotation.Nullable; 31 import android.app.Instrumentation; 32 import android.content.Context; 33 import android.graphics.Rect; 34 import android.os.Handler; 35 import android.os.RemoteException; 36 import android.os.SystemClock; 37 import android.testing.AndroidTestingRunner; 38 import android.view.SurfaceControl; 39 import android.view.View; 40 import android.view.WindowManager; 41 import android.view.WindowManagerGlobal; 42 import android.view.accessibility.IRemoteMagnificationAnimationCallback; 43 import android.view.animation.AccelerateInterpolator; 44 45 import androidx.test.InstrumentationRegistry; 46 import androidx.test.filters.LargeTest; 47 48 import com.android.internal.graphics.SfVsyncFrameCallbackProvider; 49 import com.android.systemui.R; 50 import com.android.systemui.SysuiTestCase; 51 import com.android.systemui.model.SysUiState; 52 import com.android.systemui.util.settings.SecureSettings; 53 54 import org.junit.After; 55 import org.junit.Before; 56 import org.junit.Ignore; 57 import org.junit.Test; 58 import org.junit.runner.RunWith; 59 import org.mockito.Answers; 60 import org.mockito.ArgumentCaptor; 61 import org.mockito.Mock; 62 import org.mockito.Mockito; 63 import org.mockito.MockitoAnnotations; 64 65 import java.util.concurrent.CountDownLatch; 66 import java.util.concurrent.TimeUnit; 67 import java.util.concurrent.atomic.AtomicReference; 68 69 @Ignore 70 @LargeTest 71 @RunWith(AndroidTestingRunner.class) 72 public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { 73 74 private static final float DEFAULT_SCALE = 4.0f; 75 private static final float DEFAULT_CENTER_X = 400.0f; 76 private static final float DEFAULT_CENTER_Y = 500.0f; 77 // The duration couldn't too short, otherwise the ValueAnimator won't work in expectation. 78 private static final long ANIMATION_DURATION_MS = 300; 79 80 private AtomicReference<Float> mCurrentScale = new AtomicReference<>((float) 0); 81 private AtomicReference<Float> mCurrentCenterX = new AtomicReference<>((float) 0); 82 private AtomicReference<Float> mCurrentCenterY = new AtomicReference<>((float) 0); 83 private ArgumentCaptor<Float> mScaleCaptor = ArgumentCaptor.forClass(Float.class); 84 private ArgumentCaptor<Float> mCenterXCaptor = ArgumentCaptor.forClass(Float.class); 85 private ArgumentCaptor<Float> mCenterYCaptor = ArgumentCaptor.forClass(Float.class); 86 private final ArgumentCaptor<Float> mOffsetXCaptor = ArgumentCaptor.forClass(Float.class); 87 private final ArgumentCaptor<Float> mOffsetYCaptor = ArgumentCaptor.forClass(Float.class); 88 89 @Mock 90 Handler mHandler; 91 @Mock 92 SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; 93 @Mock 94 WindowMagnifierCallback mWindowMagnifierCallback; 95 @Mock 96 IRemoteMagnificationAnimationCallback mAnimationCallback; 97 @Mock 98 IRemoteMagnificationAnimationCallback mAnimationCallback2; 99 @Mock(answer = Answers.RETURNS_SELF) 100 SysUiState mSysUiState; 101 @Mock 102 SecureSettings mSecureSettings; 103 private SpyWindowMagnificationController mController; 104 private WindowMagnificationController mSpyController; 105 private WindowMagnificationAnimationController mWindowMagnificationAnimationController; 106 private Instrumentation mInstrumentation; 107 private long mWaitingAnimationPeriod; 108 private long mWaitIntermediateAnimationPeriod; 109 110 private TestableWindowManager mWindowManager; 111 112 @Before setUp()113 public void setUp() throws Exception { 114 MockitoAnnotations.initMocks(this); 115 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 116 final WindowManager wm = mContext.getSystemService(WindowManager.class); 117 mWindowManager = spy(new TestableWindowManager(wm)); 118 mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager); 119 120 mWaitingAnimationPeriod = 2 * ANIMATION_DURATION_MS; 121 mWaitIntermediateAnimationPeriod = ANIMATION_DURATION_MS / 2; 122 mWindowMagnificationAnimationController = new WindowMagnificationAnimationController( 123 mContext, newValueAnimator()); 124 mController = new SpyWindowMagnificationController(mContext, mHandler, 125 mWindowMagnificationAnimationController, 126 mSfVsyncFrameProvider, null, new SurfaceControl.Transaction(), 127 mWindowMagnifierCallback, mSysUiState, mSecureSettings); 128 mSpyController = mController.getSpyController(); 129 } 130 131 @After tearDown()132 public void tearDown() throws Exception { 133 mInstrumentation.runOnMainSync(() -> mController.deleteWindowMagnification()); 134 } 135 136 @Test enableWindowMagnification_disabled_expectedValuesAndInvokeCallback()137 public void enableWindowMagnification_disabled_expectedValuesAndInvokeCallback() 138 throws RemoteException { 139 enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); 140 141 verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( 142 mScaleCaptor.capture(), 143 mCenterXCaptor.capture(), mCenterYCaptor.capture(), 144 mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); 145 verifyStartValue(mScaleCaptor, 1.0f); 146 verifyStartValue(mCenterXCaptor, DEFAULT_CENTER_X); 147 verifyStartValue(mCenterYCaptor, DEFAULT_CENTER_Y); 148 verifyStartValue(mOffsetXCaptor, 0f); 149 verifyStartValue(mOffsetYCaptor, 0f); 150 verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y); 151 verify(mAnimationCallback).onResult(true); 152 } 153 154 @Test enableWindowMagnificationWithoutCallback_disabled_expectedValues()155 public void enableWindowMagnificationWithoutCallback_disabled_expectedValues() { 156 enableWindowMagnificationWithoutAnimation(); 157 158 verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y); 159 } 160 161 @Test enableWindowMagnificationWithoutCallback_enabled_expectedValues()162 public void enableWindowMagnificationWithoutCallback_enabled_expectedValues() { 163 enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); 164 final float targetScale = DEFAULT_SCALE + 1.0f; 165 final float targetCenterX = DEFAULT_CENTER_X + 100; 166 final float targetCenterY = DEFAULT_CENTER_Y + 100; 167 168 mInstrumentation.runOnMainSync( 169 () -> { 170 mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, 171 targetCenterX, targetCenterY, null); 172 }); 173 174 verifyFinalSpec(targetScale, targetCenterX, targetCenterY); 175 } 176 177 @Test enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback()178 public void enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback() 179 throws RemoteException { 180 mInstrumentation.runOnMainSync( 181 () -> { 182 mWindowMagnificationAnimationController.enableWindowMagnification(1, 183 DEFAULT_CENTER_X, DEFAULT_CENTER_Y, mAnimationCallback); 184 }); 185 SystemClock.sleep(mWaitingAnimationPeriod); 186 187 verify(mSpyController).enableWindowMagnificationInternal(1, DEFAULT_CENTER_X, 188 DEFAULT_CENTER_Y, 0f, 0f); 189 verify(mAnimationCallback).onResult(true); 190 } 191 192 @Test enableWindowMagnification_enabling_expectedValuesAndInvokeCallback()193 public void enableWindowMagnification_enabling_expectedValuesAndInvokeCallback() 194 throws RemoteException { 195 enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, 196 mAnimationCallback); 197 final float targetScale = DEFAULT_SCALE + 1.0f; 198 final float targetCenterX = DEFAULT_CENTER_X + 100; 199 final float targetCenterY = DEFAULT_CENTER_Y + 100; 200 201 mInstrumentation.runOnMainSync(() -> { 202 Mockito.reset(mSpyController); 203 mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, 204 targetCenterX, targetCenterY, mAnimationCallback2); 205 mCurrentScale.set(mController.getScale()); 206 mCurrentCenterX.set(mController.getCenterX()); 207 mCurrentCenterY.set(mController.getCenterY()); 208 }); 209 210 SystemClock.sleep(mWaitingAnimationPeriod); 211 212 verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( 213 mScaleCaptor.capture(), 214 mCenterXCaptor.capture(), mCenterYCaptor.capture(), 215 mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); 216 verifyStartValue(mScaleCaptor, mCurrentScale.get()); 217 verifyStartValue(mCenterXCaptor, mCurrentCenterX.get()); 218 verifyStartValue(mCenterYCaptor, mCurrentCenterY.get()); 219 verifyStartValue(mOffsetXCaptor, 0f); 220 verifyStartValue(mOffsetYCaptor, 0f); 221 verifyFinalSpec(targetScale, targetCenterX, targetCenterY); 222 verify(mAnimationCallback).onResult(false); 223 verify(mAnimationCallback2).onResult(true); 224 } 225 226 @Test enableWindowMagnificationWithUnchanged_enabling_expectedValuesToDefault()227 public void enableWindowMagnificationWithUnchanged_enabling_expectedValuesToDefault() 228 throws InterruptedException { 229 final CountDownLatch countDownLatch = new CountDownLatch(2); 230 final MockMagnificationAnimationCallback animationCallback = 231 new MockMagnificationAnimationCallback(countDownLatch); 232 233 enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, 234 animationCallback); 235 mInstrumentation.runOnMainSync( 236 () -> { 237 mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN, 238 Float.NaN, Float.NaN, animationCallback); 239 }); 240 241 assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); 242 // The callback in 2nd enableWindowMagnification will return true 243 assertEquals(1, animationCallback.getSuccessCount()); 244 // The callback in 1st enableWindowMagnification will return false 245 assertEquals(1, animationCallback.getFailedCount()); 246 verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y); 247 } 248 249 @Test enableWindowMagnificationWithScaleOne_enabled_AnimationAndInvokeCallback()250 public void enableWindowMagnificationWithScaleOne_enabled_AnimationAndInvokeCallback() 251 throws RemoteException { 252 enableWindowMagnificationWithoutAnimation(); 253 254 final float targetScale = 1.0f; 255 final float targetCenterX = DEFAULT_CENTER_X + 100; 256 final float targetCenterY = DEFAULT_CENTER_Y + 100; 257 258 mInstrumentation.runOnMainSync(() -> { 259 Mockito.reset(mSpyController); 260 mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, 261 targetCenterX, targetCenterY, mAnimationCallback); 262 mCurrentScale.set(mController.getScale()); 263 mCurrentCenterX.set(mController.getCenterX()); 264 mCurrentCenterY.set(mController.getCenterY()); 265 }); 266 267 SystemClock.sleep(mWaitingAnimationPeriod); 268 269 verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( 270 mScaleCaptor.capture(), 271 mCenterXCaptor.capture(), mCenterYCaptor.capture(), 272 mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); 273 verifyStartValue(mScaleCaptor, mCurrentScale.get()); 274 verifyStartValue(mCenterXCaptor, mCurrentCenterX.get()); 275 verifyStartValue(mCenterYCaptor, mCurrentCenterY.get()); 276 verifyStartValue(mOffsetXCaptor, 0f); 277 verifyStartValue(mOffsetYCaptor, 0f); 278 279 verifyFinalSpec(targetScale, targetCenterX, targetCenterY); 280 281 verify(mAnimationCallback).onResult(true); 282 assertEquals(WindowMagnificationAnimationController.STATE_ENABLED, 283 mWindowMagnificationAnimationController.getState()); 284 } 285 286 @Test enableWindowMagnificationWithScaleLessThanOne_enabled_AnimationAndInvokeCallback()287 public void enableWindowMagnificationWithScaleLessThanOne_enabled_AnimationAndInvokeCallback() 288 throws RemoteException { 289 enableWindowMagnificationWithoutAnimation(); 290 291 final float targetScale = 0.99f; 292 final float targetCenterX = DEFAULT_CENTER_X + 100; 293 final float targetCenterY = DEFAULT_CENTER_Y + 100; 294 295 mInstrumentation.runOnMainSync(() -> { 296 Mockito.reset(mSpyController); 297 mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, 298 targetCenterX, targetCenterY, mAnimationCallback); 299 mCurrentScale.set(mController.getScale()); 300 mCurrentCenterX.set(mController.getCenterX()); 301 mCurrentCenterY.set(mController.getCenterY()); 302 }); 303 304 SystemClock.sleep(mWaitingAnimationPeriod); 305 306 verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( 307 mScaleCaptor.capture(), 308 mCenterXCaptor.capture(), mCenterYCaptor.capture(), 309 mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); 310 verifyStartValue(mScaleCaptor, mCurrentScale.get()); 311 verifyStartValue(mCenterXCaptor, mCurrentCenterX.get()); 312 verifyStartValue(mCenterYCaptor, mCurrentCenterY.get()); 313 verifyStartValue(mOffsetXCaptor, 0f); 314 verifyStartValue(mOffsetYCaptor, 0f); 315 // It presents the window magnification is disabled. 316 verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); 317 318 verify(mAnimationCallback).onResult(true); 319 assertEquals(WindowMagnificationAnimationController.STATE_DISABLED, 320 mWindowMagnificationAnimationController.getState()); 321 } 322 323 @Test 324 public void enableWindowMagnificationWithScaleLessThanOneAndWithoutCallBack_enabled_expectedValues()325 enableWindowMagnificationWithScaleLessThanOneAndWithoutCallBack_enabled_expectedValues() 326 throws RemoteException { 327 enableWindowMagnificationWithoutAnimation(); 328 329 final float targetScale = 0.99f; 330 final float targetCenterX = DEFAULT_CENTER_X + 100; 331 final float targetCenterY = DEFAULT_CENTER_Y + 100; 332 333 mInstrumentation.runOnMainSync(() -> { 334 Mockito.reset(mSpyController); 335 mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, 336 targetCenterX, targetCenterY, null); 337 }); 338 339 verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); 340 assertEquals(WindowMagnificationAnimationController.STATE_DISABLED, 341 mWindowMagnificationAnimationController.getState()); 342 } 343 344 @Test 345 public void enableMagnificationWithoutCallback_enabling_expectedValuesAndInvokeFormerCallback()346 enableMagnificationWithoutCallback_enabling_expectedValuesAndInvokeFormerCallback() 347 throws RemoteException { 348 enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, 349 mAnimationCallback); 350 final float targetScale = DEFAULT_SCALE - 1.0f; 351 final float targetCenterX = DEFAULT_CENTER_X + 100; 352 final float targetCenterY = DEFAULT_CENTER_Y + 100; 353 354 mInstrumentation.runOnMainSync(() -> { 355 Mockito.reset(mSpyController); 356 mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, 357 targetCenterX, targetCenterY, null); 358 }); 359 verifyFinalSpec(targetScale, targetCenterX, targetCenterY); 360 verify(mAnimationCallback).onResult(false); 361 } 362 363 @Test enableWindowMagnificationWithSameSpec_enabling_NoAnimationAndInvokeCallback()364 public void enableWindowMagnificationWithSameSpec_enabling_NoAnimationAndInvokeCallback() 365 throws RemoteException { 366 enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, 367 mAnimationCallback); 368 369 mInstrumentation.runOnMainSync(() -> { 370 Mockito.reset(mSpyController); 371 mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN, 372 Float.NaN, Float.NaN, mAnimationCallback2); 373 }); 374 SystemClock.sleep(mWaitingAnimationPeriod); 375 376 verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(), 377 anyFloat()); 378 verify(mAnimationCallback).onResult(false); 379 verify(mAnimationCallback2).onResult(true); 380 } 381 382 @Test enableWindowMagnification_disabling_expectedValuesAndInvokeCallback()383 public void enableWindowMagnification_disabling_expectedValuesAndInvokeCallback() 384 throws RemoteException { 385 enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); 386 deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, 387 mAnimationCallback); 388 final float targetScale = DEFAULT_SCALE + 1.0f; 389 final float targetCenterX = DEFAULT_CENTER_X + 100; 390 final float targetCenterY = DEFAULT_CENTER_Y + 100; 391 392 mInstrumentation.runOnMainSync( 393 () -> { 394 Mockito.reset(mSpyController); 395 mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, 396 targetCenterX, targetCenterY, mAnimationCallback2); 397 mCurrentScale.set(mController.getScale()); 398 mCurrentCenterX.set(mController.getCenterX()); 399 mCurrentCenterY.set(mController.getCenterY()); 400 }); 401 // Current spec shouldn't match given spec. 402 verify(mAnimationCallback2, never()).onResult(anyBoolean()); 403 verify(mAnimationCallback).onResult(false); 404 SystemClock.sleep(mWaitingAnimationPeriod); 405 406 verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( 407 mScaleCaptor.capture(), 408 mCenterXCaptor.capture(), mCenterYCaptor.capture(), 409 mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); 410 //Animating in reverse, so we only check if the start values are greater than current. 411 assertTrue(mScaleCaptor.getAllValues().get(0) > mCurrentScale.get()); 412 assertEquals(targetScale, mScaleCaptor.getValue(), 0f); 413 assertTrue(mCenterXCaptor.getAllValues().get(0) > mCurrentCenterX.get()); 414 assertEquals(targetCenterX, mCenterXCaptor.getValue(), 0f); 415 assertTrue(mCenterYCaptor.getAllValues().get(0) > mCurrentCenterY.get()); 416 assertEquals(targetCenterY, mCenterYCaptor.getValue(), 0f); 417 verifyFinalSpec(targetScale, targetCenterX, targetCenterY); 418 verify(mAnimationCallback2).onResult(true); 419 } 420 421 @Test 422 public void enableMagnificationWithoutCallback_disabling_expectedValuesAndInvokeFormerCallback()423 enableMagnificationWithoutCallback_disabling_expectedValuesAndInvokeFormerCallback() 424 throws RemoteException { 425 enableWindowMagnificationWithoutAnimation(); 426 deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, 427 mAnimationCallback); 428 final float targetScale = DEFAULT_SCALE + 1.0f; 429 final float targetCenterX = DEFAULT_CENTER_X + 100; 430 final float targetCenterY = DEFAULT_CENTER_Y + 100; 431 432 mInstrumentation.runOnMainSync( 433 () -> { 434 Mockito.reset(mSpyController); 435 mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, 436 targetCenterX, targetCenterY, null); 437 }); 438 439 verify(mAnimationCallback).onResult(false); 440 verifyFinalSpec(targetScale, targetCenterX, targetCenterY); 441 } 442 443 @Test enableWindowMagnificationWithSameSpec_disabling_NoAnimationAndInvokeCallback()444 public void enableWindowMagnificationWithSameSpec_disabling_NoAnimationAndInvokeCallback() 445 throws RemoteException { 446 enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); 447 deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, 448 mAnimationCallback); 449 450 mInstrumentation.runOnMainSync(() -> { 451 Mockito.reset(mSpyController); 452 mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN, 453 Float.NaN, Float.NaN, mAnimationCallback2); 454 }); 455 SystemClock.sleep(mWaitingAnimationPeriod); 456 457 verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(), 458 anyFloat()); 459 verify(mSpyController, never()).deleteWindowMagnification(); 460 verify(mAnimationCallback).onResult(false); 461 verify(mAnimationCallback2).onResult(true); 462 } 463 464 @Test enableWindowMagnification_enabled_expectedValuesAndInvokeCallback()465 public void enableWindowMagnification_enabled_expectedValuesAndInvokeCallback() 466 throws RemoteException { 467 enableWindowMagnificationWithoutAnimation(); 468 final float targetScale = DEFAULT_SCALE + 1.0f; 469 final float targetCenterX = DEFAULT_CENTER_X + 100; 470 final float targetCenterY = DEFAULT_CENTER_Y + 100; 471 472 mInstrumentation.runOnMainSync(() -> { 473 Mockito.reset(mSpyController); 474 mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, 475 targetCenterX, targetCenterY, mAnimationCallback2); 476 mCurrentScale.set(mController.getScale()); 477 mCurrentCenterX.set(mController.getCenterX()); 478 mCurrentCenterY.set(mController.getCenterY()); 479 }); 480 481 SystemClock.sleep(mWaitingAnimationPeriod); 482 483 verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( 484 mScaleCaptor.capture(), 485 mCenterXCaptor.capture(), mCenterYCaptor.capture(), 486 mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); 487 verifyStartValue(mScaleCaptor, mCurrentScale.get()); 488 verifyStartValue(mCenterXCaptor, mCurrentCenterX.get()); 489 verifyStartValue(mCenterYCaptor, mCurrentCenterY.get()); 490 verifyStartValue(mOffsetXCaptor, 0f); 491 verifyStartValue(mOffsetYCaptor, 0f); 492 verifyFinalSpec(targetScale, targetCenterX, targetCenterY); 493 verify(mAnimationCallback2).onResult(true); 494 } 495 496 @Test enableWindowMagnificationWithOffset_expectedValues()497 public void enableWindowMagnificationWithOffset_expectedValues() { 498 final float offsetRatio = -0.1f; 499 final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds()); 500 mInstrumentation.runOnMainSync(() -> { 501 Mockito.reset(mSpyController); 502 mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE, 503 windowBounds.exactCenterX(), windowBounds.exactCenterY(), 504 offsetRatio, offsetRatio, mAnimationCallback); 505 }); 506 SystemClock.sleep(mWaitingAnimationPeriod); 507 final View attachedView = mWindowManager.getAttachedView(); 508 assertNotNull(attachedView); 509 final Rect mirrorViewBound = new Rect(); 510 final View mirrorView = attachedView.findViewById(R.id.surface_view); 511 assertNotNull(mirrorView); 512 mirrorView.getBoundsOnScreen(mirrorViewBound); 513 514 assertEquals(mirrorViewBound.exactCenterX() - windowBounds.exactCenterX(), 515 Math.round(offsetRatio * mirrorViewBound.width() / 2), 0.1f); 516 assertEquals(mirrorViewBound.exactCenterY() - windowBounds.exactCenterY(), 517 Math.round(offsetRatio * mirrorViewBound.height() / 2), 0.1f); 518 } 519 520 @Test moveWindowMagnifierToPosition_enabled_expectedValues()521 public void moveWindowMagnifierToPosition_enabled_expectedValues() 522 throws InterruptedException { 523 final CountDownLatch countDownLatch = new CountDownLatch(1); 524 final MockMagnificationAnimationCallback animationCallback = 525 new MockMagnificationAnimationCallback(countDownLatch); 526 final float targetCenterX = DEFAULT_CENTER_X + 100; 527 final float targetCenterY = DEFAULT_CENTER_Y + 100; 528 enableWindowMagnificationWithoutAnimation(); 529 530 mInstrumentation.runOnMainSync(() -> { 531 mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( 532 targetCenterX, targetCenterY, animationCallback); 533 }); 534 535 assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); 536 assertEquals(1, animationCallback.getSuccessCount()); 537 assertEquals(0, animationCallback.getFailedCount()); 538 verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY); 539 } 540 541 @Test moveWindowMagnifierToPositionMultipleTimes_enabled_expectedValuesToLastOne()542 public void moveWindowMagnifierToPositionMultipleTimes_enabled_expectedValuesToLastOne() 543 throws InterruptedException { 544 final CountDownLatch countDownLatch = new CountDownLatch(4); 545 final MockMagnificationAnimationCallback animationCallback = 546 new MockMagnificationAnimationCallback(countDownLatch); 547 enableWindowMagnificationWithoutAnimation(); 548 549 mInstrumentation.runOnMainSync(() -> { 550 mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( 551 DEFAULT_CENTER_X + 10, DEFAULT_CENTER_Y + 10, animationCallback); 552 mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( 553 DEFAULT_CENTER_X + 20, DEFAULT_CENTER_Y + 20, animationCallback); 554 mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( 555 DEFAULT_CENTER_X + 30, DEFAULT_CENTER_Y + 30, animationCallback); 556 mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( 557 DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40, animationCallback); 558 }); 559 560 assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); 561 // only the last one callback will return true 562 assertEquals(1, animationCallback.getSuccessCount()); 563 // the others will return false 564 assertEquals(3, animationCallback.getFailedCount()); 565 verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40); 566 } 567 568 @Test moveWindowMagnifierToPosition_enabling_expectedValuesToLastOne()569 public void moveWindowMagnifierToPosition_enabling_expectedValuesToLastOne() 570 throws InterruptedException { 571 final CountDownLatch countDownLatch = new CountDownLatch(2); 572 final MockMagnificationAnimationCallback animationCallback = 573 new MockMagnificationAnimationCallback(countDownLatch); 574 final float targetCenterX = DEFAULT_CENTER_X + 100; 575 final float targetCenterY = DEFAULT_CENTER_Y + 100; 576 577 enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, 578 animationCallback); 579 mInstrumentation.runOnMainSync( 580 () -> { 581 mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( 582 targetCenterX, targetCenterY, animationCallback); 583 }); 584 585 assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); 586 // The callback in moveWindowMagnifierToPosition will return true 587 assertEquals(1, animationCallback.getSuccessCount()); 588 // The callback in enableWindowMagnification will return false 589 assertEquals(1, animationCallback.getFailedCount()); 590 verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY); 591 } 592 593 @Test moveWindowMagnifierToPositionWithCenterUnchanged_enabling_expectedValuesToDefault()594 public void moveWindowMagnifierToPositionWithCenterUnchanged_enabling_expectedValuesToDefault() 595 throws InterruptedException { 596 final CountDownLatch countDownLatch = new CountDownLatch(2); 597 final MockMagnificationAnimationCallback animationCallback = 598 new MockMagnificationAnimationCallback(countDownLatch); 599 600 enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, 601 animationCallback); 602 mInstrumentation.runOnMainSync( 603 () -> { 604 mWindowMagnificationAnimationController.moveWindowMagnifierToPosition( 605 Float.NaN, Float.NaN, animationCallback); 606 }); 607 608 assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS)); 609 // The callback in moveWindowMagnifierToPosition will return true 610 assertEquals(1, animationCallback.getSuccessCount()); 611 // The callback in enableWindowMagnification will return false 612 assertEquals(1, animationCallback.getFailedCount()); 613 verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y); 614 } 615 616 @Test enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback()617 public void enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback() 618 throws RemoteException { 619 enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); 620 621 enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); 622 623 verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(), 624 anyFloat()); 625 verify(mAnimationCallback).onResult(true); 626 } 627 628 @Test deleteWindowMagnification_enabled_expectedValuesAndInvokeCallback()629 public void deleteWindowMagnification_enabled_expectedValuesAndInvokeCallback() 630 throws RemoteException { 631 enableWindowMagnificationWithoutAnimation(); 632 633 deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); 634 635 verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( 636 mScaleCaptor.capture(), 637 mCenterXCaptor.capture(), mCenterYCaptor.capture(), 638 mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); 639 verifyStartValue(mScaleCaptor, DEFAULT_SCALE); 640 verifyStartValue(mCenterXCaptor, Float.NaN); 641 verifyStartValue(mCenterYCaptor, Float.NaN); 642 verifyStartValue(mOffsetXCaptor, 0f); 643 verifyStartValue(mOffsetYCaptor, 0f); 644 verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); 645 verify(mAnimationCallback).onResult(true); 646 } 647 648 @Test deleteWindowMagnificationWithoutCallback_enabled_expectedValues()649 public void deleteWindowMagnificationWithoutCallback_enabled_expectedValues() { 650 enableWindowMagnificationWithoutAnimation(); 651 652 deleteWindowMagnificationAndWaitAnimating(0, null); 653 654 verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); 655 } 656 657 @Test deleteWindowMagnification_disabled_doNothingAndInvokeCallback()658 public void deleteWindowMagnification_disabled_doNothingAndInvokeCallback() 659 throws RemoteException { 660 deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); 661 662 Mockito.verifyNoMoreInteractions(mSpyController); 663 verify(mAnimationCallback).onResult(true); 664 } 665 666 @Test deleteWindowMagnification_enabling_expectedValuesAndInvokeCallback()667 public void deleteWindowMagnification_enabling_expectedValuesAndInvokeCallback() 668 throws RemoteException { 669 enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, 670 mAnimationCallback); 671 672 mInstrumentation.runOnMainSync( 673 () -> { 674 Mockito.reset(mSpyController); 675 mWindowMagnificationAnimationController.deleteWindowMagnification( 676 mAnimationCallback2); 677 mCurrentScale.set(mController.getScale()); 678 mCurrentCenterX.set(mController.getCenterX()); 679 mCurrentCenterY.set(mController.getCenterY()); 680 }); 681 SystemClock.sleep(mWaitingAnimationPeriod); 682 verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( 683 mScaleCaptor.capture(), 684 mCenterXCaptor.capture(), mCenterYCaptor.capture(), 685 mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); 686 687 //The animation is in verse, so we only check the start values should no be greater than 688 // the current one. 689 assertTrue(mScaleCaptor.getAllValues().get(0) <= mCurrentScale.get()); 690 assertEquals(1.0f, mScaleCaptor.getValue(), 0f); 691 verifyStartValue(mCenterXCaptor, Float.NaN); 692 verifyStartValue(mCenterYCaptor, Float.NaN); 693 verifyStartValue(mOffsetXCaptor, 0f); 694 verifyStartValue(mOffsetYCaptor, 0f); 695 verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); 696 verify(mAnimationCallback).onResult(false); 697 verify(mAnimationCallback2).onResult(true); 698 } 699 700 @Test deleteWindowMagnificationWithoutCallback_enabling_expectedValuesAndInvokeCallback()701 public void deleteWindowMagnificationWithoutCallback_enabling_expectedValuesAndInvokeCallback() 702 throws RemoteException { 703 enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, 704 mAnimationCallback); 705 706 mInstrumentation.runOnMainSync( 707 () -> { 708 Mockito.reset(mSpyController); 709 mWindowMagnificationAnimationController.deleteWindowMagnification(null); 710 }); 711 712 verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); 713 verify(mAnimationCallback).onResult(false); 714 } 715 716 @Test deleteWindowMagnification_disabling_checkStartAndValues()717 public void deleteWindowMagnification_disabling_checkStartAndValues() throws RemoteException { 718 enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); 719 deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, 720 mAnimationCallback); 721 722 deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback2); 723 724 verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal( 725 mScaleCaptor.capture(), 726 mCenterXCaptor.capture(), mCenterYCaptor.capture(), 727 mOffsetXCaptor.capture(), mOffsetYCaptor.capture()); 728 assertEquals(1.0f, mScaleCaptor.getValue(), 0f); 729 verifyStartValue(mOffsetXCaptor, 0f); 730 verifyStartValue(mOffsetYCaptor, 0f); 731 verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); 732 verify(mAnimationCallback).onResult(false); 733 verify(mAnimationCallback2).onResult(true); 734 } 735 736 @Test deleteWindowMagnificationWithoutCallback_disabling_checkStartAndValues()737 public void deleteWindowMagnificationWithoutCallback_disabling_checkStartAndValues() 738 throws RemoteException { 739 enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); 740 deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, 741 mAnimationCallback); 742 743 deleteWindowMagnificationAndWaitAnimating(0, null); 744 745 verify(mSpyController).deleteWindowMagnification(); 746 verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); 747 verify(mAnimationCallback).onResult(false); 748 } 749 750 @Test moveWindowMagnifier_enabled_vertical_only_expectedValue()751 public void moveWindowMagnifier_enabled_vertical_only_expectedValue() { 752 enableWindowMagnificationWithoutAnimation(); 753 754 // should move vertically since offsetY/offsetX > HORIZONTAL_LOCK_BASE 755 final float offsetX = 50.0f; 756 final float offsetY = 757 (float) Math.ceil(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE) 758 + 1.0f; 759 mInstrumentation.runOnMainSync( 760 () -> mController.moveWindowMagnifier(offsetX, offsetY)); 761 762 verify(mSpyController).moveWindowMagnifier(offsetX, offsetY); 763 verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y + offsetY); 764 } 765 766 @Test moveWindowMagnifier_enabled_horinzontal_only_expectedValue()767 public void moveWindowMagnifier_enabled_horinzontal_only_expectedValue() { 768 enableWindowMagnificationWithoutAnimation(); 769 770 // should move vertically since offsetY/offsetX <= HORIZONTAL_LOCK_BASE 771 final float offsetX = 50.0f; 772 final float offsetY = 773 (float) Math.floor(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE) 774 - 1.0f; 775 mInstrumentation.runOnMainSync( 776 () -> mController.moveWindowMagnifier(offsetX, offsetY)); 777 778 verify(mSpyController).moveWindowMagnifier(offsetX, offsetY); 779 verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + offsetX, DEFAULT_CENTER_Y); 780 } 781 782 @Test moveWindowMagnifier_enabled_setDiagonalEnabled_expectedValues()783 public void moveWindowMagnifier_enabled_setDiagonalEnabled_expectedValues() { 784 enableWindowMagnificationWithoutAnimation(); 785 786 final float offsetX = 50.0f; 787 final float offsetY = 788 (float) Math.ceil(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE); 789 // while diagonal scrolling enabled, 790 // should move with both offsetX and offsetY without regrading offsetY/offsetX 791 mInstrumentation.runOnMainSync( 792 () -> { 793 mController.setDiagonalScrolling(true); 794 mController.moveWindowMagnifier(offsetX, offsetY); 795 }); 796 797 verify(mSpyController).moveWindowMagnifier(offsetX, offsetY); 798 verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + offsetX, DEFAULT_CENTER_Y + offsetY); 799 } 800 801 @Test moveWindowMagnifierToPosition_enabled()802 public void moveWindowMagnifierToPosition_enabled() { 803 final float targetCenterX = DEFAULT_CENTER_X + 100; 804 final float targetCenterY = DEFAULT_CENTER_Y + 100; 805 enableWindowMagnificationWithoutAnimation(); 806 807 mInstrumentation.runOnMainSync( 808 () -> mController.moveWindowMagnifierToPosition(targetCenterX, targetCenterY, 809 mAnimationCallback)); 810 SystemClock.sleep(mWaitingAnimationPeriod); 811 812 verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY); 813 } 814 verifyFinalSpec(float expectedScale, float expectedCenterX, float expectedCenterY)815 private void verifyFinalSpec(float expectedScale, float expectedCenterX, 816 float expectedCenterY) { 817 assertEquals(expectedScale, mController.getScale(), 0f); 818 assertEquals(expectedCenterX, mController.getCenterX(), 0f); 819 assertEquals(expectedCenterY, mController.getCenterY(), 0f); 820 } 821 enableWindowMagnificationWithoutAnimation()822 private void enableWindowMagnificationWithoutAnimation() { 823 mInstrumentation.runOnMainSync( 824 () -> { 825 Mockito.reset(mSpyController); 826 mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE, 827 DEFAULT_CENTER_X, DEFAULT_CENTER_Y, null); 828 }); 829 } 830 enableWindowMagnificationAndWaitAnimating(long duration, @Nullable IRemoteMagnificationAnimationCallback callback)831 private void enableWindowMagnificationAndWaitAnimating(long duration, 832 @Nullable IRemoteMagnificationAnimationCallback callback) { 833 mInstrumentation.runOnMainSync( 834 () -> { 835 Mockito.reset(mSpyController); 836 mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE, 837 DEFAULT_CENTER_X, DEFAULT_CENTER_Y, callback); 838 }); 839 SystemClock.sleep(duration); 840 } 841 deleteWindowMagnificationAndWaitAnimating(long duration, @Nullable IRemoteMagnificationAnimationCallback callback)842 private void deleteWindowMagnificationAndWaitAnimating(long duration, 843 @Nullable IRemoteMagnificationAnimationCallback callback) { 844 mInstrumentation.runOnMainSync( 845 () -> { 846 resetMockObjects(); 847 mWindowMagnificationAnimationController.deleteWindowMagnification(callback); 848 }); 849 SystemClock.sleep(duration); 850 } 851 verifyStartValue(ArgumentCaptor<Float> captor, float startValue)852 private void verifyStartValue(ArgumentCaptor<Float> captor, float startValue) { 853 assertEquals(startValue, captor.getAllValues().get(0), 0f); 854 } 855 resetMockObjects()856 private void resetMockObjects() { 857 Mockito.reset(mSpyController); 858 } 859 860 /** 861 * It observes the methods in {@link WindowMagnificationController} since we couldn't spy it 862 * directly. 863 */ 864 private static class SpyWindowMagnificationController extends WindowMagnificationController { 865 private WindowMagnificationController mSpyController; 866 SpyWindowMagnificationController(Context context, Handler handler, WindowMagnificationAnimationController animationController, SfVsyncFrameCallbackProvider sfVsyncFrameProvider, MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction, WindowMagnifierCallback callback, SysUiState sysUiState, SecureSettings secureSettings)867 SpyWindowMagnificationController(Context context, Handler handler, 868 WindowMagnificationAnimationController animationController, 869 SfVsyncFrameCallbackProvider sfVsyncFrameProvider, 870 MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction, 871 WindowMagnifierCallback callback, SysUiState sysUiState, 872 SecureSettings secureSettings) { 873 super( 874 context, 875 handler, 876 animationController, 877 sfVsyncFrameProvider, 878 mirrorWindowControl, 879 transaction, 880 callback, 881 sysUiState, 882 WindowManagerGlobal::getWindowSession, 883 secureSettings); 884 mSpyController = Mockito.mock(WindowMagnificationController.class); 885 } 886 getSpyController()887 WindowMagnificationController getSpyController() { 888 return mSpyController; 889 } 890 891 @Override enableWindowMagnificationInternal(float scale, float centerX, float centerY)892 void enableWindowMagnificationInternal(float scale, float centerX, float centerY) { 893 super.enableWindowMagnificationInternal(scale, centerX, centerY); 894 mSpyController.enableWindowMagnificationInternal(scale, centerX, centerY); 895 } 896 897 @Override enableWindowMagnificationInternal(float scale, float centerX, float centerY, float magnificationOffsetFrameRatioX, float magnificationOffsetFrameRatioY)898 void enableWindowMagnificationInternal(float scale, float centerX, float centerY, 899 float magnificationOffsetFrameRatioX, float magnificationOffsetFrameRatioY) { 900 super.enableWindowMagnificationInternal(scale, centerX, centerY, 901 magnificationOffsetFrameRatioX, magnificationOffsetFrameRatioY); 902 mSpyController.enableWindowMagnificationInternal(scale, centerX, centerY, 903 magnificationOffsetFrameRatioX, magnificationOffsetFrameRatioY); 904 } 905 906 @Override deleteWindowMagnification()907 void deleteWindowMagnification() { 908 super.deleteWindowMagnification(); 909 mSpyController.deleteWindowMagnification(); 910 } 911 912 @Override moveWindowMagnifier(float offsetX, float offsetY)913 void moveWindowMagnifier(float offsetX, float offsetY) { 914 super.moveWindowMagnifier(offsetX, offsetY); 915 mSpyController.moveWindowMagnifier(offsetX, offsetY); 916 } 917 918 @Override moveWindowMagnifierToPosition(float positionX, float positionY, IRemoteMagnificationAnimationCallback callback)919 void moveWindowMagnifierToPosition(float positionX, float positionY, 920 IRemoteMagnificationAnimationCallback callback) { 921 super.moveWindowMagnifierToPosition(positionX, positionY, callback); 922 mSpyController.moveWindowMagnifierToPosition(positionX, positionY, callback); 923 } 924 925 @Override setScale(float scale)926 void setScale(float scale) { 927 super.setScale(scale); 928 mSpyController.setScale(scale); 929 } 930 931 @Override updateSysUIStateFlag()932 public void updateSysUIStateFlag() { 933 super.updateSysUIStateFlag(); 934 mSpyController.updateSysUIStateFlag(); 935 } 936 } 937 newValueAnimator()938 private static ValueAnimator newValueAnimator() { 939 final ValueAnimator valueAnimator = new ValueAnimator(); 940 valueAnimator.setDuration(ANIMATION_DURATION_MS); 941 valueAnimator.setInterpolator(new AccelerateInterpolator(2.5f)); 942 valueAnimator.setFloatValues(0.0f, 1.0f); 943 return valueAnimator; 944 } 945 } 946