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