MagnificationControllerTest.java revision 7a6cc9b5a70217029a08bdaae701f072bf5410de
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 17package com.android.server.accessibility; 18 19import static org.junit.Assert.assertEquals; 20import static org.junit.Assert.assertThat; 21import static org.junit.Assert.assertTrue; 22import static org.junit.Assert.assertFalse; 23import static org.mockito.ArgumentMatchers.anyFloat; 24import static org.mockito.Matchers.anyObject; 25import static org.mockito.Matchers.eq; 26import static org.mockito.Mockito.doAnswer; 27import static org.mockito.Mockito.mock; 28import static org.mockito.Mockito.reset; 29import static org.mockito.Mockito.times; 30import static org.mockito.Mockito.verify; 31import static org.mockito.Mockito.verifyNoMoreInteractions; 32import static org.mockito.Mockito.when; 33import static org.mockito.hamcrest.MockitoHamcrest.argThat; 34 35import android.animation.ValueAnimator; 36import android.content.BroadcastReceiver; 37import android.content.Context; 38import android.content.IntentFilter; 39import android.content.res.Resources; 40import android.graphics.PointF; 41import android.graphics.Rect; 42import android.graphics.Region; 43import android.os.Handler; 44import android.os.Looper; 45import android.os.Message; 46import android.support.test.runner.AndroidJUnit4; 47import android.view.MagnificationSpec; 48import android.view.WindowManagerInternal; 49import android.view.WindowManagerInternal.MagnificationCallbacks; 50 51import com.android.internal.R; 52 53import org.hamcrest.CoreMatchers; 54import org.hamcrest.Description; 55import org.hamcrest.TypeSafeMatcher; 56import org.junit.Before; 57import org.junit.BeforeClass; 58import org.junit.Test; 59import org.junit.runner.RunWith; 60import org.mockito.ArgumentCaptor; 61import org.mockito.Mockito; 62import org.mockito.invocation.InvocationOnMock; 63import org.mockito.stubbing.Answer; 64 65import java.util.Locale; 66 67@RunWith(AndroidJUnit4.class) 68public class MagnificationControllerTest { 69 static final Rect INITIAL_MAGNIFICATION_BOUNDS = new Rect(0, 0, 100, 200); 70 static final PointF INITIAL_MAGNIFICATION_BOUNDS_CENTER = new PointF( 71 INITIAL_MAGNIFICATION_BOUNDS.centerX(), INITIAL_MAGNIFICATION_BOUNDS.centerY()); 72 static final PointF INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER = new PointF(25, 50); 73 static final PointF INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER = new PointF(75, 150); 74 static final Rect OTHER_MAGNIFICATION_BOUNDS = new Rect(100, 200, 500, 600); 75 static final PointF OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER = new PointF(400, 500); 76 static final Region INITIAL_MAGNIFICATION_REGION = new Region(INITIAL_MAGNIFICATION_BOUNDS); 77 static final Region OTHER_REGION = new Region(OTHER_MAGNIFICATION_BOUNDS); 78 static final int SERVICE_ID_1 = 1; 79 static final int SERVICE_ID_2 = 2; 80 81 final Context mMockContext = mock(Context.class); 82 final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class); 83 final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class); 84 final MessageCapturingHandler mMessageCapturingHandler = 85 new MessageCapturingHandler(new Handler.Callback() { 86 @Override 87 public boolean handleMessage(Message msg) { 88 return mMagnificationController.handleMessage(msg); 89 } 90 }); 91 final ArgumentCaptor<MagnificationSpec> mMagnificationSpecCaptor = 92 ArgumentCaptor.forClass(MagnificationSpec.class); 93 final ValueAnimator mMockValueAnimator = mock(ValueAnimator.class); 94 MagnificationController.SettingsBridge mMockSettingsBridge; 95 96 97 MagnificationController mMagnificationController; 98 ValueAnimator.AnimatorUpdateListener mTargetAnimationListener; 99 100 @BeforeClass 101 public static void oneTimeInitialization() { 102 if (Looper.myLooper() == null) { 103 Looper.prepare(); 104 } 105 } 106 107 @Before 108 public void setUp() { 109 when(mMockContext.getMainLooper()).thenReturn(Looper.myLooper()); 110 Resources mockResources = mock(Resources.class); 111 when(mMockContext.getResources()).thenReturn(mockResources); 112 when(mockResources.getInteger(R.integer.config_longAnimTime)) 113 .thenReturn(1000); 114 mMockSettingsBridge = mock(MagnificationController.SettingsBridge.class); 115 mMagnificationController = new MagnificationController(mMockContext, mMockAms, new Object(), 116 mMessageCapturingHandler, mMockWindowManager, mMockValueAnimator, 117 mMockSettingsBridge); 118 119 doAnswer(new Answer<Void>() { 120 @Override 121 public Void answer(InvocationOnMock invocationOnMock) throws Throwable { 122 Object[] args = invocationOnMock.getArguments(); 123 Region regionArg = (Region) args[0]; 124 regionArg.set(INITIAL_MAGNIFICATION_REGION); 125 return null; 126 } 127 }).when(mMockWindowManager).getMagnificationRegion((Region) anyObject()); 128 129 ArgumentCaptor<ValueAnimator.AnimatorUpdateListener> listenerArgumentCaptor = 130 ArgumentCaptor.forClass(ValueAnimator.AnimatorUpdateListener.class); 131 verify(mMockValueAnimator).addUpdateListener(listenerArgumentCaptor.capture()); 132 mTargetAnimationListener = listenerArgumentCaptor.getValue(); 133 Mockito.reset(mMockValueAnimator); // Ignore other initialization 134 } 135 136 @Test 137 public void testRegister_WindowManagerAndContextRegisterListeners() { 138 mMagnificationController.register(); 139 verify(mMockContext).registerReceiver( 140 (BroadcastReceiver) anyObject(), (IntentFilter) anyObject()); 141 verify(mMockWindowManager).setMagnificationCallbacks((MagnificationCallbacks) anyObject()); 142 assertTrue(mMagnificationController.isRegisteredLocked()); 143 } 144 145 @Test 146 public void testRegister_WindowManagerAndContextUnregisterListeners() { 147 mMagnificationController.register(); 148 mMagnificationController.unregister(); 149 150 verify(mMockContext).unregisterReceiver((BroadcastReceiver) anyObject()); 151 verify(mMockWindowManager).setMagnificationCallbacks(null); 152 assertFalse(mMagnificationController.isRegisteredLocked()); 153 } 154 155 @Test 156 public void testInitialState_noMagnificationAndMagnificationRegionReadFromWindowManager() { 157 mMagnificationController.register(); 158 MagnificationSpec expectedInitialSpec = getMagnificationSpec(1.0f, 0.0f, 0.0f); 159 Region initialMagRegion = new Region(); 160 Rect initialBounds = new Rect(); 161 162 assertEquals(expectedInitialSpec, getCurrentMagnificationSpec()); 163 mMagnificationController.getMagnificationRegion(initialMagRegion); 164 mMagnificationController.getMagnificationBounds(initialBounds); 165 assertEquals(INITIAL_MAGNIFICATION_REGION, initialMagRegion); 166 assertEquals(INITIAL_MAGNIFICATION_BOUNDS, initialBounds); 167 assertEquals(INITIAL_MAGNIFICATION_BOUNDS.centerX(), 168 mMagnificationController.getCenterX(), 0.0f); 169 assertEquals(INITIAL_MAGNIFICATION_BOUNDS.centerY(), 170 mMagnificationController.getCenterY(), 0.0f); 171 } 172 173 @Test 174 public void testNotRegistered_publicMethodsShouldBeBenign() { 175 assertFalse(mMagnificationController.isMagnifying()); 176 assertFalse(mMagnificationController.magnificationRegionContains(100, 100)); 177 assertFalse(mMagnificationController.reset(true)); 178 assertFalse(mMagnificationController.setScale(2, 100, 100, true, 0)); 179 assertFalse(mMagnificationController.setCenter(100, 100, false, 1)); 180 assertFalse(mMagnificationController.setScaleAndCenter(1.5f, 100, 100, false, 2)); 181 assertTrue(mMagnificationController.getIdOfLastServiceToMagnify() < 0); 182 183 mMagnificationController.getMagnificationRegion(new Region()); 184 mMagnificationController.getMagnificationBounds(new Rect()); 185 mMagnificationController.getScale(); 186 mMagnificationController.getOffsetX(); 187 mMagnificationController.getOffsetY(); 188 mMagnificationController.getCenterX(); 189 mMagnificationController.getCenterY(); 190 mMagnificationController.offsetMagnifiedRegion(50, 50, 1); 191 mMagnificationController.unregister(); 192 } 193 194 @Test 195 public void testSetScale_noAnimation_shouldGoStraightToWindowManagerAndUpdateState() { 196 mMagnificationController.register(); 197 final float scale = 2.0f; 198 final PointF center = INITIAL_MAGNIFICATION_BOUNDS_CENTER; 199 final PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, center, scale); 200 assertTrue(mMagnificationController 201 .setScale(scale, center.x, center.y, false, SERVICE_ID_1)); 202 203 final MagnificationSpec expectedSpec = getMagnificationSpec(scale, offsets); 204 verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedSpec))); 205 assertThat(getCurrentMagnificationSpec(), closeTo(expectedSpec)); 206 assertEquals(center.x, mMagnificationController.getCenterX(), 0.0); 207 assertEquals(center.y, mMagnificationController.getCenterY(), 0.0); 208 verify(mMockValueAnimator, times(0)).start(); 209 } 210 211 @Test 212 public void testSetScale_withPivotAndAnimation_stateChangesAndAnimationHappens() { 213 mMagnificationController.register(); 214 MagnificationSpec startSpec = getCurrentMagnificationSpec(); 215 float scale = 2.0f; 216 PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; 217 assertTrue(mMagnificationController 218 .setScale(scale, pivotPoint.x, pivotPoint.y, true, SERVICE_ID_1)); 219 220 // New center should be halfway between original center and pivot 221 PointF newCenter = new PointF((pivotPoint.x + INITIAL_MAGNIFICATION_BOUNDS.centerX()) / 2, 222 (pivotPoint.y + INITIAL_MAGNIFICATION_BOUNDS.centerY()) / 2); 223 PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale); 224 MagnificationSpec endSpec = getMagnificationSpec(scale, offsets); 225 226 assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5); 227 assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5); 228 assertThat(getCurrentMagnificationSpec(), closeTo(endSpec)); 229 verify(mMockValueAnimator).start(); 230 231 // Initial value 232 when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f); 233 mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); 234 verify(mMockWindowManager).setMagnificationSpec(startSpec); 235 236 // Intermediate point 237 Mockito.reset(mMockWindowManager); 238 float fraction = 0.5f; 239 when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction); 240 mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); 241 verify(mMockWindowManager).setMagnificationSpec( 242 argThat(closeTo(getInterpolatedMagSpec(startSpec, endSpec, fraction)))); 243 244 // Final value 245 Mockito.reset(mMockWindowManager); 246 when(mMockValueAnimator.getAnimatedFraction()).thenReturn(1.0f); 247 mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); 248 verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec))); 249 } 250 251 @Test 252 public void testSetCenter_whileMagnifying_noAnimation_centerMoves() { 253 mMagnificationController.register(); 254 // First zoom in 255 float scale = 2.0f; 256 assertTrue(mMagnificationController.setScale(scale, 257 INITIAL_MAGNIFICATION_BOUNDS.centerX(), INITIAL_MAGNIFICATION_BOUNDS.centerY(), 258 false, SERVICE_ID_1)); 259 Mockito.reset(mMockWindowManager); 260 261 PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; 262 assertTrue(mMagnificationController 263 .setCenter(newCenter.x, newCenter.y, false, SERVICE_ID_1)); 264 PointF expectedOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale); 265 MagnificationSpec expectedSpec = getMagnificationSpec(scale, expectedOffsets); 266 267 verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedSpec))); 268 assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.0); 269 assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.0); 270 verify(mMockValueAnimator, times(0)).start(); 271 } 272 273 @Test 274 public void testSetScaleAndCenter_animated_stateChangesAndAnimationHappens() { 275 mMagnificationController.register(); 276 MagnificationSpec startSpec = getCurrentMagnificationSpec(); 277 float scale = 2.5f; 278 PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; 279 PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale); 280 MagnificationSpec endSpec = getMagnificationSpec(scale, offsets); 281 282 assertTrue(mMagnificationController.setScaleAndCenter(scale, newCenter.x, newCenter.y, 283 true, SERVICE_ID_1)); 284 285 assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5); 286 assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5); 287 assertThat(getCurrentMagnificationSpec(), closeTo(endSpec)); 288 verify(mMockAms).notifyMagnificationChanged( 289 INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y); 290 verify(mMockValueAnimator).start(); 291 292 // Initial value 293 when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f); 294 mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); 295 verify(mMockWindowManager).setMagnificationSpec(startSpec); 296 297 // Intermediate point 298 Mockito.reset(mMockWindowManager); 299 float fraction = 0.33f; 300 when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction); 301 mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); 302 verify(mMockWindowManager).setMagnificationSpec( 303 argThat(closeTo(getInterpolatedMagSpec(startSpec, endSpec, fraction)))); 304 305 // Final value 306 Mockito.reset(mMockWindowManager); 307 when(mMockValueAnimator.getAnimatedFraction()).thenReturn(1.0f); 308 mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); 309 verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec))); 310 } 311 312 @Test 313 public void testSetScaleAndCenter_scaleOutOfBounds_cappedAtLimits() { 314 mMagnificationController.register(); 315 MagnificationSpec startSpec = getCurrentMagnificationSpec(); 316 PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; 317 PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, 318 MagnificationController.MAX_SCALE); 319 MagnificationSpec endSpec = getMagnificationSpec( 320 MagnificationController.MAX_SCALE, offsets); 321 322 assertTrue(mMagnificationController.setScaleAndCenter( 323 MagnificationController.MAX_SCALE + 1.0f, 324 newCenter.x, newCenter.y, false, SERVICE_ID_1)); 325 326 assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5); 327 assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5); 328 verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec))); 329 Mockito.reset(mMockWindowManager); 330 331 // Verify that we can't zoom below 1x 332 assertTrue(mMagnificationController.setScaleAndCenter(0.5f, 333 INITIAL_MAGNIFICATION_BOUNDS_CENTER.x, INITIAL_MAGNIFICATION_BOUNDS_CENTER.y, 334 false, SERVICE_ID_1)); 335 336 assertEquals(INITIAL_MAGNIFICATION_BOUNDS_CENTER.x, 337 mMagnificationController.getCenterX(), 0.5); 338 assertEquals(INITIAL_MAGNIFICATION_BOUNDS_CENTER.y, 339 mMagnificationController.getCenterY(), 0.5); 340 verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(startSpec))); 341 } 342 343 @Test 344 public void testSetScaleAndCenter_centerOutOfBounds_cappedAtLimits() { 345 mMagnificationController.register(); 346 float scale = 2.0f; 347 348 // Off the edge to the top and left 349 assertTrue(mMagnificationController.setScaleAndCenter( 350 scale, -100f, -200f, false, SERVICE_ID_1)); 351 352 PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER; 353 PointF newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale); 354 assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5); 355 assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5); 356 verify(mMockWindowManager).setMagnificationSpec( 357 argThat(closeTo(getMagnificationSpec(scale, newOffsets)))); 358 Mockito.reset(mMockWindowManager); 359 360 // Off the edge to the bottom and right 361 assertTrue(mMagnificationController.setScaleAndCenter(scale, 362 INITIAL_MAGNIFICATION_BOUNDS.right + 1, INITIAL_MAGNIFICATION_BOUNDS.bottom + 1, 363 false, SERVICE_ID_1)); 364 newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; 365 newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale); 366 assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5); 367 assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5); 368 verify(mMockWindowManager).setMagnificationSpec( 369 argThat(closeTo(getMagnificationSpec(scale, newOffsets)))); 370 } 371 372 @Test 373 public void testMagnificationRegionChanged_serviceNotified() { 374 mMagnificationController.register(); 375 MagnificationCallbacks callbacks = getMagnificationCallbacks(); 376 callbacks.onMagnificationRegionChanged(OTHER_REGION); 377 mMessageCapturingHandler.sendAllMessages(); 378 verify(mMockAms).notifyMagnificationChanged(OTHER_REGION, 1.0f, 379 OTHER_MAGNIFICATION_BOUNDS.centerX(), OTHER_MAGNIFICATION_BOUNDS.centerY()); 380 } 381 382 @Test 383 public void testOffsetMagnifiedRegion_whileMagnifying_offsetsMove() { 384 mMagnificationController.register(); 385 PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; 386 float scale = 2.0f; 387 PointF startOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, startCenter, scale); 388 // First zoom in 389 assertTrue(mMagnificationController 390 .setScaleAndCenter(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1)); 391 Mockito.reset(mMockWindowManager); 392 393 PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; 394 PointF newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale); 395 mMagnificationController.offsetMagnifiedRegion( 396 startOffsets.x - newOffsets.x, startOffsets.y - newOffsets.y, SERVICE_ID_1); 397 398 MagnificationSpec expectedSpec = getMagnificationSpec(scale, newOffsets); 399 verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedSpec))); 400 assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.0); 401 assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.0); 402 verify(mMockValueAnimator, times(0)).start(); 403 } 404 405 @Test 406 public void testOffsetMagnifiedRegion_whileNotMagnifying_hasNoEffect() { 407 mMagnificationController.register(); 408 Mockito.reset(mMockWindowManager); 409 MagnificationSpec startSpec = getCurrentMagnificationSpec(); 410 mMagnificationController.offsetMagnifiedRegion(10, 10, SERVICE_ID_1); 411 assertThat(getCurrentMagnificationSpec(), closeTo(startSpec)); 412 mMagnificationController.offsetMagnifiedRegion(-10, -10, SERVICE_ID_1); 413 assertThat(getCurrentMagnificationSpec(), closeTo(startSpec)); 414 verifyNoMoreInteractions(mMockWindowManager); 415 } 416 417 @Test 418 public void testOffsetMagnifiedRegion_whileMagnifyingButAtEdge_hasNoEffect() { 419 mMagnificationController.register(); 420 float scale = 2.0f; 421 422 // Upper left edges 423 PointF ulCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER; 424 assertTrue(mMagnificationController 425 .setScaleAndCenter(scale, ulCenter.x, ulCenter.y, false, SERVICE_ID_1)); 426 Mockito.reset(mMockWindowManager); 427 MagnificationSpec ulSpec = getCurrentMagnificationSpec(); 428 mMagnificationController.offsetMagnifiedRegion(-10, -10, SERVICE_ID_1); 429 assertThat(getCurrentMagnificationSpec(), closeTo(ulSpec)); 430 verifyNoMoreInteractions(mMockWindowManager); 431 432 // Lower right edges 433 PointF lrCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; 434 assertTrue(mMagnificationController 435 .setScaleAndCenter(scale, lrCenter.x, lrCenter.y, false, SERVICE_ID_1)); 436 Mockito.reset(mMockWindowManager); 437 MagnificationSpec lrSpec = getCurrentMagnificationSpec(); 438 mMagnificationController.offsetMagnifiedRegion(10, 10, SERVICE_ID_1); 439 assertThat(getCurrentMagnificationSpec(), closeTo(lrSpec)); 440 verifyNoMoreInteractions(mMockWindowManager); 441 } 442 443 @Test 444 public void testGetIdOfLastServiceToChange_returnsCorrectValue() { 445 mMagnificationController.register(); 446 PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; 447 assertTrue(mMagnificationController 448 .setScale(2.0f, startCenter.x, startCenter.y, false, SERVICE_ID_1)); 449 assertEquals(SERVICE_ID_1, mMagnificationController.getIdOfLastServiceToMagnify()); 450 assertTrue(mMagnificationController 451 .setScale(1.5f, startCenter.x, startCenter.y, false, SERVICE_ID_2)); 452 assertEquals(SERVICE_ID_2, mMagnificationController.getIdOfLastServiceToMagnify()); 453 } 454 455 @Test 456 public void testSetUserId_resetsOnlyIfIdChanges() { 457 final int userId1 = 1; 458 final int userId2 = 2; 459 460 mMagnificationController.register(); 461 mMagnificationController.setUserId(userId1); 462 PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; 463 float scale = 2.0f; 464 mMagnificationController.setScale(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1); 465 466 mMagnificationController.setUserId(userId1); 467 assertTrue(mMagnificationController.isMagnifying()); 468 mMagnificationController.setUserId(userId2); 469 assertFalse(mMagnificationController.isMagnifying()); 470 } 471 472 @Test 473 public void testResetIfNeeded_doesWhatItSays() { 474 mMagnificationController.register(); 475 zoomIn2xToMiddle(); 476 reset(mMockAms); 477 assertTrue(mMagnificationController.resetIfNeeded(false)); 478 verify(mMockAms).notifyMagnificationChanged( 479 eq(INITIAL_MAGNIFICATION_REGION), eq(1.0f), anyFloat(), anyFloat()); 480 assertFalse(mMagnificationController.isMagnifying()); 481 assertFalse(mMagnificationController.resetIfNeeded(false)); 482 } 483 484 @Test 485 public void testTurnScreenOff_resetsMagnification() { 486 mMagnificationController.register(); 487 ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = 488 ArgumentCaptor.forClass(BroadcastReceiver.class); 489 verify(mMockContext).registerReceiver( 490 broadcastReceiverCaptor.capture(), (IntentFilter) anyObject()); 491 BroadcastReceiver br = broadcastReceiverCaptor.getValue(); 492 zoomIn2xToMiddle(); 493 br.onReceive(mMockContext, null); 494 mMessageCapturingHandler.sendAllMessages(); 495 assertFalse(mMagnificationController.isMagnifying()); 496 } 497 498 @Test 499 public void testUserContextChange_resetsMagnification() { 500 mMagnificationController.register(); 501 MagnificationCallbacks callbacks = getMagnificationCallbacks(); 502 zoomIn2xToMiddle(); 503 callbacks.onUserContextChanged(); 504 mMessageCapturingHandler.sendAllMessages(); 505 assertFalse(mMagnificationController.isMagnifying()); 506 } 507 508 @Test 509 public void testRotation_resetsMagnification() { 510 mMagnificationController.register(); 511 MagnificationCallbacks callbacks = getMagnificationCallbacks(); 512 zoomIn2xToMiddle(); 513 mMessageCapturingHandler.sendAllMessages(); 514 assertTrue(mMagnificationController.isMagnifying()); 515 callbacks.onRotationChanged(0); 516 mMessageCapturingHandler.sendAllMessages(); 517 assertFalse(mMagnificationController.isMagnifying()); 518 } 519 520 @Test 521 public void testBoundsChange_whileMagnifyingWithCompatibleSpec_noSpecChange() { 522 // Going from a small region to a large one leads to no issues 523 mMagnificationController.register(); 524 zoomIn2xToMiddle(); 525 MagnificationSpec startSpec = getCurrentMagnificationSpec(); 526 MagnificationCallbacks callbacks = getMagnificationCallbacks(); 527 Mockito.reset(mMockWindowManager); 528 callbacks.onMagnificationRegionChanged(OTHER_REGION); 529 mMessageCapturingHandler.sendAllMessages(); 530 assertThat(getCurrentMagnificationSpec(), closeTo(startSpec)); 531 verifyNoMoreInteractions(mMockWindowManager); 532 } 533 534 @Test 535 public void testBoundsChange_whileZoomingWithCompatibleSpec_noSpecChange() { 536 mMagnificationController.register(); 537 PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; 538 float scale = 2.0f; 539 mMagnificationController.setScale(scale, startCenter.x, startCenter.y, true, SERVICE_ID_1); 540 MagnificationSpec startSpec = getCurrentMagnificationSpec(); 541 MagnificationCallbacks callbacks = getMagnificationCallbacks(); 542 Mockito.reset(mMockWindowManager); 543 callbacks.onMagnificationRegionChanged(OTHER_REGION); 544 mMessageCapturingHandler.sendAllMessages(); 545 assertThat(getCurrentMagnificationSpec(), closeTo(startSpec)); 546 verifyNoMoreInteractions(mMockWindowManager); 547 } 548 549 @Test 550 public void testBoundsChange_whileMagnifyingWithIncompatibleSpec_offsetsConstrained() { 551 // In a large region, pan to the farthest point possible 552 mMagnificationController.register(); 553 MagnificationCallbacks callbacks = getMagnificationCallbacks(); 554 callbacks.onMagnificationRegionChanged(OTHER_REGION); 555 mMessageCapturingHandler.sendAllMessages(); 556 PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER; 557 float scale = 2.0f; 558 mMagnificationController.setScale(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1); 559 MagnificationSpec startSpec = getCurrentMagnificationSpec(); 560 verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(startSpec))); 561 Mockito.reset(mMockWindowManager); 562 563 callbacks.onMagnificationRegionChanged(INITIAL_MAGNIFICATION_REGION); 564 mMessageCapturingHandler.sendAllMessages(); 565 566 MagnificationSpec endSpec = getCurrentMagnificationSpec(); 567 assertThat(endSpec, CoreMatchers.not(closeTo(startSpec))); 568 PointF expectedOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, 569 INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER, scale); 570 assertThat(endSpec, closeTo(getMagnificationSpec(scale, expectedOffsets))); 571 verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec))); 572 } 573 574 @Test 575 public void testBoundsChange_whileZoomingWithIncompatibleSpec_jumpsToCompatibleSpec() { 576 mMagnificationController.register(); 577 MagnificationCallbacks callbacks = getMagnificationCallbacks(); 578 callbacks.onMagnificationRegionChanged(OTHER_REGION); 579 mMessageCapturingHandler.sendAllMessages(); 580 PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER; 581 float scale = 2.0f; 582 mMagnificationController.setScale(scale, startCenter.x, startCenter.y, true, SERVICE_ID_1); 583 MagnificationSpec startSpec = getCurrentMagnificationSpec(); 584 when (mMockValueAnimator.isRunning()).thenReturn(true); 585 586 callbacks.onMagnificationRegionChanged(INITIAL_MAGNIFICATION_REGION); 587 mMessageCapturingHandler.sendAllMessages(); 588 verify(mMockValueAnimator).cancel(); 589 590 MagnificationSpec endSpec = getCurrentMagnificationSpec(); 591 assertThat(endSpec, CoreMatchers.not(closeTo(startSpec))); 592 PointF expectedOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, 593 INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER, scale); 594 assertThat(endSpec, closeTo(getMagnificationSpec(scale, expectedOffsets))); 595 verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec))); 596 } 597 598 @Test 599 public void testRequestRectOnScreen_rectAlreadyOnScreen_doesNothing() { 600 mMagnificationController.register(); 601 zoomIn2xToMiddle(); 602 MagnificationSpec startSpec = getCurrentMagnificationSpec(); 603 MagnificationCallbacks callbacks = getMagnificationCallbacks(); 604 Mockito.reset(mMockWindowManager); 605 int centerX = (int) INITIAL_MAGNIFICATION_BOUNDS_CENTER.x; 606 int centerY = (int) INITIAL_MAGNIFICATION_BOUNDS_CENTER.y; 607 callbacks.onRectangleOnScreenRequested(centerX - 1, centerY - 1, centerX + 1, centerY - 1); 608 mMessageCapturingHandler.sendAllMessages(); 609 assertThat(getCurrentMagnificationSpec(), closeTo(startSpec)); 610 verifyNoMoreInteractions(mMockWindowManager); 611 } 612 613 @Test 614 public void testRequestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen() { 615 mMagnificationController.register(); 616 zoomIn2xToMiddle(); 617 MagnificationCallbacks callbacks = getMagnificationCallbacks(); 618 Mockito.reset(mMockWindowManager); 619 callbacks.onRectangleOnScreenRequested(0, 0, 1, 1); 620 mMessageCapturingHandler.sendAllMessages(); 621 MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, 0, 0); 622 assertThat(getCurrentMagnificationSpec(), closeTo(expectedEndSpec)); 623 verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedEndSpec))); 624 } 625 626 @Test 627 public void testRequestRectOnScreen_garbageInput_doesNothing() { 628 mMagnificationController.register(); 629 zoomIn2xToMiddle(); 630 MagnificationSpec startSpec = getCurrentMagnificationSpec(); 631 MagnificationCallbacks callbacks = getMagnificationCallbacks(); 632 Mockito.reset(mMockWindowManager); 633 callbacks.onRectangleOnScreenRequested(0, 0, -50, -50); 634 mMessageCapturingHandler.sendAllMessages(); 635 assertThat(getCurrentMagnificationSpec(), closeTo(startSpec)); 636 verifyNoMoreInteractions(mMockWindowManager); 637 } 638 639 640 @Test 641 public void testRequestRectOnScreen_rectTooWide_pansToGetStartOnScreenBasedOnLocale() { 642 Locale.setDefault(new Locale("en", "us")); 643 mMagnificationController.register(); 644 zoomIn2xToMiddle(); 645 MagnificationCallbacks callbacks = getMagnificationCallbacks(); 646 MagnificationSpec startSpec = getCurrentMagnificationSpec(); 647 Mockito.reset(mMockWindowManager); 648 Rect wideRect = new Rect(0, 50, 100, 51); 649 callbacks.onRectangleOnScreenRequested( 650 wideRect.left, wideRect.top, wideRect.right, wideRect.bottom); 651 mMessageCapturingHandler.sendAllMessages(); 652 MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, 0, startSpec.offsetY); 653 assertThat(getCurrentMagnificationSpec(), closeTo(expectedEndSpec)); 654 verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedEndSpec))); 655 Mockito.reset(mMockWindowManager); 656 657 // Repeat with RTL 658 Locale.setDefault(new Locale("he", "il")); 659 callbacks.onRectangleOnScreenRequested( 660 wideRect.left, wideRect.top, wideRect.right, wideRect.bottom); 661 mMessageCapturingHandler.sendAllMessages(); 662 expectedEndSpec = getMagnificationSpec(2.0f, -100, startSpec.offsetY); 663 assertThat(getCurrentMagnificationSpec(), closeTo(expectedEndSpec)); 664 verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedEndSpec))); 665 } 666 667 @Test 668 public void testRequestRectOnScreen_rectTooTall_pansMinimumToGetTopOnScreen() { 669 mMagnificationController.register(); 670 zoomIn2xToMiddle(); 671 MagnificationCallbacks callbacks = getMagnificationCallbacks(); 672 MagnificationSpec startSpec = getCurrentMagnificationSpec(); 673 Mockito.reset(mMockWindowManager); 674 Rect tallRect = new Rect(50, 0, 51, 100); 675 callbacks.onRectangleOnScreenRequested( 676 tallRect.left, tallRect.top, tallRect.right, tallRect.bottom); 677 mMessageCapturingHandler.sendAllMessages(); 678 MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, startSpec.offsetX, 0); 679 assertThat(getCurrentMagnificationSpec(), closeTo(expectedEndSpec)); 680 verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedEndSpec))); 681 } 682 683 @Test 684 public void testChangeMagnification_duringAnimation_animatesToNewValue() { 685 mMagnificationController.register(); 686 MagnificationSpec startSpec = getCurrentMagnificationSpec(); 687 float scale = 2.5f; 688 PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; 689 MagnificationSpec firstEndSpec = getMagnificationSpec( 690 scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, firstCenter, scale)); 691 692 assertTrue(mMagnificationController.setScaleAndCenter(scale, firstCenter.x, firstCenter.y, 693 true, SERVICE_ID_1)); 694 695 assertEquals(firstCenter.x, mMagnificationController.getCenterX(), 0.5); 696 assertEquals(firstCenter.y, mMagnificationController.getCenterY(), 0.5); 697 assertThat(getCurrentMagnificationSpec(), closeTo(firstEndSpec)); 698 verify(mMockValueAnimator, times(1)).start(); 699 700 // Initial value 701 when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f); 702 mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); 703 verify(mMockWindowManager).setMagnificationSpec(startSpec); 704 verify(mMockAms).notifyMagnificationChanged( 705 INITIAL_MAGNIFICATION_REGION, scale, firstCenter.x, firstCenter.y); 706 Mockito.reset(mMockWindowManager); 707 708 // Intermediate point 709 float fraction = 0.33f; 710 when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction); 711 mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); 712 MagnificationSpec intermediateSpec1 = 713 getInterpolatedMagSpec(startSpec, firstEndSpec, fraction); 714 verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(intermediateSpec1))); 715 Mockito.reset(mMockWindowManager); 716 717 PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER; 718 MagnificationSpec newEndSpec = getMagnificationSpec( 719 scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale)); 720 assertTrue(mMagnificationController.setCenter( 721 newCenter.x, newCenter.y, true, SERVICE_ID_1)); 722 723 // Animation should have been restarted 724 verify(mMockValueAnimator, times(2)).start(); 725 verify(mMockAms).notifyMagnificationChanged( 726 INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y); 727 728 // New starting point should be where we left off 729 when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f); 730 mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); 731 verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(intermediateSpec1))); 732 Mockito.reset(mMockWindowManager); 733 734 // Second intermediate point 735 fraction = 0.5f; 736 when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction); 737 mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); 738 verify(mMockWindowManager).setMagnificationSpec( 739 argThat(closeTo(getInterpolatedMagSpec(intermediateSpec1, newEndSpec, fraction)))); 740 Mockito.reset(mMockWindowManager); 741 742 // Final value should be the new center 743 Mockito.reset(mMockWindowManager); 744 when(mMockValueAnimator.getAnimatedFraction()).thenReturn(1.0f); 745 mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); 746 verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(newEndSpec))); 747 } 748 749 private void zoomIn2xToMiddle() { 750 PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; 751 float scale = 2.0f; 752 mMagnificationController.setScale(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1); 753 assertTrue(mMagnificationController.isMagnifying()); 754 } 755 756 private MagnificationCallbacks getMagnificationCallbacks() { 757 ArgumentCaptor<MagnificationCallbacks> magnificationCallbacksCaptor = 758 ArgumentCaptor.forClass(MagnificationCallbacks.class); 759 verify(mMockWindowManager) 760 .setMagnificationCallbacks(magnificationCallbacksCaptor.capture()); 761 return magnificationCallbacksCaptor.getValue(); 762 } 763 764 private PointF computeOffsets(Rect magnifiedBounds, PointF center, float scale) { 765 return new PointF( 766 magnifiedBounds.centerX() - scale * center.x, 767 magnifiedBounds.centerY() - scale * center.y); 768 } 769 770 private MagnificationSpec getInterpolatedMagSpec(MagnificationSpec start, MagnificationSpec end, 771 float fraction) { 772 MagnificationSpec interpolatedSpec = MagnificationSpec.obtain(); 773 interpolatedSpec.scale = start.scale + fraction * (end.scale - start.scale); 774 interpolatedSpec.offsetX = start.offsetX + fraction * (end.offsetX - start.offsetX); 775 interpolatedSpec.offsetY = start.offsetY + fraction * (end.offsetY - start.offsetY); 776 return interpolatedSpec; 777 } 778 779 private MagnificationSpec getMagnificationSpec(float scale, PointF offsets) { 780 return getMagnificationSpec(scale, offsets.x, offsets.y); 781 } 782 783 private MagnificationSpec getMagnificationSpec(float scale, float offsetX, float offsetY) { 784 MagnificationSpec spec = MagnificationSpec.obtain(); 785 spec.scale = scale; 786 spec.offsetX = offsetX; 787 spec.offsetY = offsetY; 788 return spec; 789 } 790 791 private MagnificationSpec getCurrentMagnificationSpec() { 792 return getMagnificationSpec(mMagnificationController.getScale(), 793 mMagnificationController.getOffsetX(), mMagnificationController.getOffsetY()); 794 } 795 796 private MagSpecMatcher closeTo(MagnificationSpec spec) { 797 return new MagSpecMatcher(spec, 0.01f, 0.5f); 798 } 799 800 private class MagSpecMatcher extends TypeSafeMatcher<MagnificationSpec> { 801 final MagnificationSpec mMagSpec; 802 final float mScaleTolerance; 803 final float mOffsetTolerance; 804 805 MagSpecMatcher(MagnificationSpec spec, float scaleTolerance, float offsetTolerance) { 806 mMagSpec = spec; 807 mScaleTolerance = scaleTolerance; 808 mOffsetTolerance = offsetTolerance; 809 } 810 811 @Override 812 protected boolean matchesSafely(MagnificationSpec magnificationSpec) { 813 if (Math.abs(mMagSpec.scale - magnificationSpec.scale) > mScaleTolerance) { 814 return false; 815 } 816 if (Math.abs(mMagSpec.offsetX - magnificationSpec.offsetX) > mOffsetTolerance) { 817 return false; 818 } 819 if (Math.abs(mMagSpec.offsetY - magnificationSpec.offsetY) > mOffsetTolerance) { 820 return false; 821 } 822 return true; 823 } 824 825 @Override 826 public void describeTo(Description description) { 827 description.appendText("Match spec: " + mMagSpec); 828 } 829 } 830} 831