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 android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; 20 import static android.view.MotionEvent.ACTION_CANCEL; 21 import static android.view.MotionEvent.ACTION_DOWN; 22 import static android.view.MotionEvent.ACTION_MOVE; 23 import static android.view.MotionEvent.ACTION_UP; 24 import static android.view.WindowInsets.Type.displayCutout; 25 import static android.view.WindowInsets.Type.systemBars; 26 import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK; 27 28 import static com.android.systemui.accessibility.MagnificationModeSwitch.DEFAULT_FADE_OUT_ANIMATION_DELAY_MS; 29 import static com.android.systemui.accessibility.MagnificationModeSwitch.FADING_ANIMATION_DURATION_MS; 30 import static com.android.systemui.accessibility.MagnificationModeSwitch.getIconResId; 31 32 import static junit.framework.Assert.assertEquals; 33 import static junit.framework.Assert.assertNotNull; 34 35 import static org.hamcrest.CoreMatchers.hasItems; 36 import static org.hamcrest.MatcherAssert.assertThat; 37 import static org.junit.Assert.assertNotEquals; 38 import static org.junit.Assert.assertNull; 39 import static org.mockito.ArgumentMatchers.any; 40 import static org.mockito.ArgumentMatchers.anyInt; 41 import static org.mockito.ArgumentMatchers.anyLong; 42 import static org.mockito.ArgumentMatchers.eq; 43 import static org.mockito.Mockito.doAnswer; 44 import static org.mockito.Mockito.doNothing; 45 import static org.mockito.Mockito.doReturn; 46 import static org.mockito.Mockito.mock; 47 import static org.mockito.Mockito.never; 48 import static org.mockito.Mockito.spy; 49 import static org.mockito.Mockito.verify; 50 import static org.mockito.Mockito.when; 51 52 import android.content.Context; 53 import android.content.pm.ActivityInfo; 54 import android.graphics.Insets; 55 import android.graphics.Rect; 56 import android.os.Handler; 57 import android.os.SystemClock; 58 import android.testing.AndroidTestingRunner; 59 import android.testing.TestableLooper; 60 import android.view.Choreographer; 61 import android.view.MotionEvent; 62 import android.view.View; 63 import android.view.ViewConfiguration; 64 import android.view.ViewPropertyAnimator; 65 import android.view.WindowInsets; 66 import android.view.WindowManager; 67 import android.view.accessibility.AccessibilityManager; 68 import android.view.accessibility.AccessibilityNodeInfo; 69 import android.widget.ImageView; 70 71 import androidx.test.filters.SmallTest; 72 73 import com.android.internal.graphics.SfVsyncFrameCallbackProvider; 74 import com.android.systemui.R; 75 import com.android.systemui.SysuiTestCase; 76 77 import org.junit.After; 78 import org.junit.Before; 79 import org.junit.Test; 80 import org.junit.runner.RunWith; 81 import org.mockito.ArgumentCaptor; 82 import org.mockito.InOrder; 83 import org.mockito.Mock; 84 import org.mockito.Mockito; 85 import org.mockito.MockitoAnnotations; 86 87 import java.util.List; 88 89 @SmallTest 90 @RunWith(AndroidTestingRunner.class) 91 @TestableLooper.RunWithLooper(setAsMainLooper = true) 92 public class MagnificationModeSwitchTest extends SysuiTestCase { 93 94 private static final float FADE_IN_ALPHA = 1f; 95 private static final float FADE_OUT_ALPHA = 0f; 96 97 private ImageView mSpyImageView; 98 @Mock 99 private AccessibilityManager mAccessibilityManager; 100 @Mock 101 private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; 102 @Mock 103 private MagnificationModeSwitch.ClickListener mClickListener; 104 private TestableWindowManager mWindowManager; 105 private ViewPropertyAnimator mViewPropertyAnimator; 106 private MagnificationModeSwitch mMagnificationModeSwitch; 107 private View.OnTouchListener mTouchListener; 108 private Runnable mFadeOutAnimation; 109 private MotionEventHelper mMotionEventHelper = new MotionEventHelper(); 110 111 @Before setUp()112 public void setUp() throws Exception { 113 MockitoAnnotations.initMocks(this); 114 mContext = Mockito.spy(getContext()); 115 final WindowManager wm = mContext.getSystemService(WindowManager.class); 116 mWindowManager = spy(new TestableWindowManager(wm)); 117 mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager); 118 mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager); 119 mSpyImageView = Mockito.spy(new ImageView(mContext)); 120 mViewPropertyAnimator = Mockito.spy(mSpyImageView.animate()); 121 resetAndStubMockImageViewAndAnimator(); 122 doAnswer((invocation) -> { 123 mTouchListener = invocation.getArgument(0); 124 return null; 125 }).when(mSpyImageView).setOnTouchListener( 126 any(View.OnTouchListener.class)); 127 doAnswer(invocation -> { 128 Choreographer.FrameCallback callback = invocation.getArgument(0); 129 callback.doFrame(0); 130 return null; 131 }).when(mSfVsyncFrameProvider).postFrameCallback( 132 any(Choreographer.FrameCallback.class)); 133 mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView, 134 mSfVsyncFrameProvider, mClickListener); 135 assertNotNull(mTouchListener); 136 } 137 138 @After tearDown()139 public void tearDown() { 140 mFadeOutAnimation = null; 141 mMotionEventHelper.recycleEvents(); 142 mMagnificationModeSwitch.removeButton(); 143 } 144 145 @Test removeButton_buttonIsShowing_removeViewAndUnregisterComponentCallbacks()146 public void removeButton_buttonIsShowing_removeViewAndUnregisterComponentCallbacks() { 147 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 148 149 mMagnificationModeSwitch.removeButton(); 150 151 verify(mWindowManager).removeView(mSpyImageView); 152 verify(mViewPropertyAnimator).cancel(); 153 verify(mContext).unregisterComponentCallbacks(mMagnificationModeSwitch); 154 } 155 156 @Test showFullscreenModeButton_addViewAndSetImageResource()157 public void showFullscreenModeButton_addViewAndSetImageResource() { 158 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 159 160 verify(mSpyImageView).setImageResource( 161 getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)); 162 assertEquals(mSpyImageView, mWindowManager.getAttachedView()); 163 assertShowFadingAnimation(FADE_IN_ALPHA); 164 assertShowFadingAnimation(FADE_OUT_ALPHA); 165 } 166 167 @Test showButton_excludeSystemGestureArea()168 public void showButton_excludeSystemGestureArea() { 169 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 170 171 verify(mSpyImageView).setSystemGestureExclusionRects(any(List.class)); 172 } 173 174 @Test showMagnificationButton_setA11yTimeout_postDelayedAnimationWithA11yTimeout()175 public void showMagnificationButton_setA11yTimeout_postDelayedAnimationWithA11yTimeout() { 176 final int a11yTimeout = 12345; 177 when(mAccessibilityManager.getRecommendedTimeoutMillis(anyInt(), anyInt())).thenReturn( 178 a11yTimeout); 179 180 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 181 182 verify(mAccessibilityManager).getRecommendedTimeoutMillis( 183 DEFAULT_FADE_OUT_ANIMATION_DELAY_MS, AccessibilityManager.FLAG_CONTENT_ICONS 184 | AccessibilityManager.FLAG_CONTENT_CONTROLS); 185 verify(mSpyImageView).postOnAnimationDelayed(any(Runnable.class), eq((long) a11yTimeout)); 186 } 187 188 @Test showMagnificationButton_windowModeAndFadingOut_verifyAnimationEndAction()189 public void showMagnificationButton_windowModeAndFadingOut_verifyAnimationEndAction() { 190 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 191 executeFadeOutAnimation(); 192 193 // Verify the end action after fade-out. 194 final ArgumentCaptor<Runnable> endActionCaptor = ArgumentCaptor.forClass(Runnable.class); 195 verify(mViewPropertyAnimator).withEndAction(endActionCaptor.capture()); 196 197 endActionCaptor.getValue().run(); 198 199 verify(mViewPropertyAnimator).cancel(); 200 verify(mWindowManager).removeView(mSpyImageView); 201 } 202 203 @Test onDensityChanged_buttonIsShowing_updateResourcesAndLayout()204 public void onDensityChanged_buttonIsShowing_updateResourcesAndLayout() { 205 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 206 resetAndStubMockImageViewAndAnimator(); 207 208 mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY); 209 210 InOrder inOrder = Mockito.inOrder(mWindowManager); 211 inOrder.verify(mWindowManager).updateViewLayout(eq(mSpyImageView), any()); 212 inOrder.verify(mWindowManager).removeView(eq(mSpyImageView)); 213 inOrder.verify(mWindowManager).addView(eq(mSpyImageView), any()); 214 verify(mSpyImageView).setSystemGestureExclusionRects(any(List.class)); 215 } 216 217 @Test onApplyWindowInsetsWithBoundsChange_buttonIsShowing_updateLayoutPosition()218 public void onApplyWindowInsetsWithBoundsChange_buttonIsShowing_updateLayoutPosition() { 219 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 220 221 mMagnificationModeSwitch.mDraggableWindowBounds.inset(10, 10); 222 mSpyImageView.onApplyWindowInsets(WindowInsets.CONSUMED); 223 224 verify(mWindowManager).updateViewLayout(eq(mSpyImageView), 225 any(WindowManager.LayoutParams.class)); 226 assertLayoutPosition(/* toLeftScreenEdge= */ false); 227 } 228 229 @Test onSystemBarsInsetsChanged_buttonIsShowing_draggableBoundsChanged()230 public void onSystemBarsInsetsChanged_buttonIsShowing_draggableBoundsChanged() { 231 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 232 final Rect oldDraggableBounds = new Rect(mMagnificationModeSwitch.mDraggableWindowBounds); 233 234 mWindowManager.setWindowInsets(new WindowInsets.Builder() 235 .setInsetsIgnoringVisibility(systemBars(), Insets.of(0, 20, 0, 20)) 236 .build()); 237 mSpyImageView.onApplyWindowInsets(WindowInsets.CONSUMED); 238 239 assertNotEquals(oldDraggableBounds, mMagnificationModeSwitch.mDraggableWindowBounds); 240 } 241 242 @Test onDisplayCutoutInsetsChanged_buttonIsShowing_draggableBoundsChanged()243 public void onDisplayCutoutInsetsChanged_buttonIsShowing_draggableBoundsChanged() { 244 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 245 final Rect oldDraggableBounds = new Rect(mMagnificationModeSwitch.mDraggableWindowBounds); 246 247 mWindowManager.setWindowInsets(new WindowInsets.Builder() 248 .setInsetsIgnoringVisibility(displayCutout(), Insets.of(20, 30, 20, 30)) 249 .build()); 250 mSpyImageView.onApplyWindowInsets(WindowInsets.CONSUMED); 251 252 assertNotEquals(oldDraggableBounds, mMagnificationModeSwitch.mDraggableWindowBounds); 253 } 254 255 @Test onWindowBoundsChanged_buttonIsShowing_draggableBoundsChanged()256 public void onWindowBoundsChanged_buttonIsShowing_draggableBoundsChanged() { 257 mWindowManager.setWindowBounds(new Rect(0, 0, 800, 1000)); 258 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 259 final Rect oldDraggableBounds = new Rect(mMagnificationModeSwitch.mDraggableWindowBounds); 260 261 mWindowManager.setWindowBounds(new Rect(0, 0, 1000, 800)); 262 mSpyImageView.onApplyWindowInsets(WindowInsets.CONSUMED); 263 264 assertNotEquals(oldDraggableBounds, mMagnificationModeSwitch.mDraggableWindowBounds); 265 } 266 267 @Test onDraggingGestureFinish_buttonIsShowing_stickToRightEdge()268 public void onDraggingGestureFinish_buttonIsShowing_stickToRightEdge() { 269 final int windowHalfWidth = 270 mWindowManager.getCurrentWindowMetrics().getBounds().width() / 2; 271 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 272 273 // Drag button to right side on screen 274 final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop() + 10; 275 final long downTime = SystemClock.uptimeMillis(); 276 mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( 277 downTime, 0, ACTION_DOWN, 100, 100)); 278 mTouchListener.onTouch(mSpyImageView, 279 obtainMotionEvent(downTime, downTime, ACTION_MOVE, windowHalfWidth - offset, 100)); 280 281 mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( 282 downTime, downTime, ACTION_UP, windowHalfWidth - offset, 100)); 283 284 assertLayoutPosition(/* toLeftScreenEdge= */false); 285 } 286 287 @Test performSingleTap_fullscreenMode_callbackTriggered()288 public void performSingleTap_fullscreenMode_callbackTriggered() { 289 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 290 resetAndStubMockImageViewAndAnimator(); 291 292 // Perform a single-tap 293 final long downTime = SystemClock.uptimeMillis(); 294 mTouchListener.onTouch(mSpyImageView, 295 obtainMotionEvent(downTime, 0, ACTION_DOWN, 100, 100)); 296 resetAndStubMockImageViewAndAnimator(); 297 mTouchListener.onTouch(mSpyImageView, 298 obtainMotionEvent(downTime, downTime, ACTION_UP, 100, 100)); 299 300 verify(mClickListener).onClick(eq(mContext.getDisplayId())); 301 } 302 303 @Test sendDownEvent_fullscreenMode_fadeOutAnimationIsNull()304 public void sendDownEvent_fullscreenMode_fadeOutAnimationIsNull() { 305 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 306 resetAndStubMockImageViewAndAnimator(); 307 308 final long downTime = SystemClock.uptimeMillis(); 309 mTouchListener.onTouch(mSpyImageView, 310 obtainMotionEvent(downTime, 0, ACTION_DOWN, 100, 100)); 311 312 assertNull(mFadeOutAnimation); 313 } 314 315 @Test sendDownEvent_fullscreenModeAndFadingOut_cancelAnimation()316 public void sendDownEvent_fullscreenModeAndFadingOut_cancelAnimation() { 317 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 318 executeFadeOutAnimation(); 319 resetAndStubMockImageViewAndAnimator(); 320 321 final long downTime = SystemClock.uptimeMillis(); 322 mTouchListener.onTouch(mSpyImageView, 323 obtainMotionEvent(downTime, 0, ACTION_DOWN, 100, 100)); 324 325 verify(mViewPropertyAnimator).cancel(); 326 } 327 328 @Test performDragging_showMagnificationButton_updateViewLayout()329 public void performDragging_showMagnificationButton_updateViewLayout() { 330 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 331 resetAndStubMockImageViewAndAnimator(); 332 333 // Perform dragging 334 final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop() + 10; 335 final long downTime = SystemClock.uptimeMillis(); 336 mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( 337 downTime, 0, ACTION_DOWN, 100, 100)); 338 339 mTouchListener.onTouch(mSpyImageView, 340 obtainMotionEvent(downTime, downTime, ACTION_MOVE, 100 + offset, 341 100)); 342 verify(mWindowManager).updateViewLayout(eq(mSpyImageView), 343 any(WindowManager.LayoutParams.class)); 344 345 resetAndStubMockImageViewAndAnimator(); 346 mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( 347 downTime, downTime, ACTION_UP, 100 + offset, 100)); 348 349 verify(mClickListener, never()).onClick(anyInt()); 350 assertShowFadingAnimation(FADE_OUT_ALPHA); 351 } 352 353 @Test performSingleTapActionCanceled_showButtonAnimation()354 public void performSingleTapActionCanceled_showButtonAnimation() { 355 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 356 resetAndStubMockImageViewAndAnimator(); 357 358 final long downTime = SystemClock.uptimeMillis(); 359 mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( 360 downTime, downTime, ACTION_DOWN, 100, 100)); 361 resetAndStubMockImageViewAndAnimator(); 362 mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( 363 downTime, downTime, ACTION_CANCEL, 100, 100)); 364 365 verify(mClickListener, never()).onClick(anyInt()); 366 assertShowFadingAnimation(FADE_OUT_ALPHA); 367 } 368 369 @Test performDraggingActionCanceled_showButtonAnimation()370 public void performDraggingActionCanceled_showButtonAnimation() { 371 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 372 resetAndStubMockImageViewAndAnimator(); 373 374 // Perform dragging 375 final long downTime = SystemClock.uptimeMillis(); 376 final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop() + 10; 377 mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( 378 0, 0, ACTION_DOWN, 100, 100)); 379 mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( 380 downTime, downTime, ACTION_MOVE, 100 + offset, 100)); 381 resetAndStubMockImageViewAndAnimator(); 382 mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( 383 downTime, downTime, ACTION_CANCEL, 100 + offset, 100)); 384 385 verify(mClickListener, never()).onClick(anyInt()); 386 assertShowFadingAnimation(FADE_OUT_ALPHA); 387 } 388 389 @Test initializeA11yNode_showWindowModeButton_expectedValues()390 public void initializeA11yNode_showWindowModeButton_expectedValues() { 391 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 392 final AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo(); 393 394 mSpyImageView.onInitializeAccessibilityNodeInfo(nodeInfo); 395 396 assertEquals(mContext.getString(R.string.magnification_mode_switch_description), 397 nodeInfo.getContentDescription()); 398 assertEquals(mContext.getString(R.string.magnification_mode_switch_state_full_screen), 399 nodeInfo.getStateDescription().toString()); 400 assertThat(nodeInfo.getActionList(), 401 hasItems(new AccessibilityNodeInfo.AccessibilityAction( 402 ACTION_CLICK.getId(), mContext.getResources().getString( 403 R.string.magnification_open_settings_click_label)))); 404 assertThat(nodeInfo.getActionList(), 405 hasItems(new AccessibilityNodeInfo.AccessibilityAction( 406 R.id.accessibility_action_move_up, mContext.getResources().getString( 407 R.string.accessibility_control_move_up)))); 408 assertThat(nodeInfo.getActionList(), 409 hasItems(new AccessibilityNodeInfo.AccessibilityAction( 410 R.id.accessibility_action_move_down, mContext.getResources().getString( 411 R.string.accessibility_control_move_down)))); 412 assertThat(nodeInfo.getActionList(), 413 hasItems(new AccessibilityNodeInfo.AccessibilityAction( 414 R.id.accessibility_action_move_left, mContext.getResources().getString( 415 R.string.accessibility_control_move_left)))); 416 assertThat(nodeInfo.getActionList(), 417 hasItems(new AccessibilityNodeInfo.AccessibilityAction( 418 R.id.accessibility_action_move_right, mContext.getResources().getString( 419 R.string.accessibility_control_move_right)))); 420 } 421 422 @Test performClickA11yActions_showWindowModeButton_callbackTriggered()423 public void performClickA11yActions_showWindowModeButton_callbackTriggered() { 424 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 425 resetAndStubMockImageViewAndAnimator(); 426 427 mSpyImageView.performAccessibilityAction( 428 ACTION_CLICK.getId(), null); 429 430 verify(mClickListener).onClick(mContext.getDisplayId()); 431 } 432 433 @Test performMoveLeftA11yAction_showButtonAtRightEdge_moveToLeftEdge()434 public void performMoveLeftA11yAction_showButtonAtRightEdge_moveToLeftEdge() { 435 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 436 437 mSpyImageView.performAccessibilityAction( 438 R.id.accessibility_action_move_left, null); 439 440 assertLayoutPosition(/* toLeftScreenEdge= */true); 441 } 442 443 @Test showButton_showFadeOutAnimation_fadeOutAnimationCanceled()444 public void showButton_showFadeOutAnimation_fadeOutAnimationCanceled() { 445 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 446 assertShowFadingAnimation(FADE_OUT_ALPHA); 447 resetAndStubMockImageViewAndAnimator(); 448 449 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 450 451 verify(mViewPropertyAnimator).cancel(); 452 assertEquals(1f, mSpyImageView.getAlpha()); 453 assertShowFadingAnimation(FADE_OUT_ALPHA); 454 } 455 456 @Test showButton_hasAccessibilityWindowTitle()457 public void showButton_hasAccessibilityWindowTitle() { 458 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 459 460 final WindowManager.LayoutParams layoutPrams = 461 mWindowManager.getLayoutParamsFromAttachedView(); 462 assertNotNull(layoutPrams); 463 assertEquals(getContext().getResources().getString( 464 com.android.internal.R.string.android_system_label), 465 layoutPrams.accessibilityTitle); 466 } 467 468 @Test showButton_registerComponentCallbacks()469 public void showButton_registerComponentCallbacks() { 470 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 471 472 verify(mContext).registerComponentCallbacks(mMagnificationModeSwitch); 473 } 474 475 @Test onLocaleChanged_buttonIsShowing_updateA11yWindowTitle()476 public void onLocaleChanged_buttonIsShowing_updateA11yWindowTitle() { 477 final String newA11yWindowTitle = "new a11y window title"; 478 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 479 480 getContext().getOrCreateTestableResources().addOverride( 481 com.android.internal.R.string.android_system_label, newA11yWindowTitle); 482 mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE); 483 484 final WindowManager.LayoutParams layoutParams = 485 mWindowManager.getLayoutParamsFromAttachedView(); 486 assertNotNull(layoutParams); 487 assertEquals(newA11yWindowTitle, layoutParams.accessibilityTitle); 488 } 489 490 @Test onRotationChanged_buttonIsShowing_expectedYPosition()491 public void onRotationChanged_buttonIsShowing_expectedYPosition() { 492 final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds(); 493 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 494 final Rect oldDraggableBounds = new Rect(mMagnificationModeSwitch.mDraggableWindowBounds); 495 final float windowHeightFraction = 496 (float) (mWindowManager.getLayoutParamsFromAttachedView().y 497 - oldDraggableBounds.top) / oldDraggableBounds.height(); 498 499 // The window bounds and the draggable bounds are changed due to the rotation change. 500 final Rect newWindowBounds = new Rect(0, 0, windowBounds.height(), windowBounds.width()); 501 mWindowManager.setWindowBounds(newWindowBounds); 502 mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION); 503 504 int expectedY = (int) (windowHeightFraction 505 * mMagnificationModeSwitch.mDraggableWindowBounds.height()) 506 + mMagnificationModeSwitch.mDraggableWindowBounds.top; 507 assertEquals( 508 "The Y position does not keep the same height ratio after the rotation changed.", 509 expectedY, mWindowManager.getLayoutParamsFromAttachedView().y); 510 } 511 512 @Test onScreenSizeChanged_buttonIsShowingOnTheRightSide_expectedPosition()513 public void onScreenSizeChanged_buttonIsShowingOnTheRightSide_expectedPosition() { 514 final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds(); 515 mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); 516 final Rect oldDraggableBounds = new Rect(mMagnificationModeSwitch.mDraggableWindowBounds); 517 final float windowHeightFraction = 518 (float) (mWindowManager.getLayoutParamsFromAttachedView().y 519 - oldDraggableBounds.top) / oldDraggableBounds.height(); 520 521 // The window bounds and the draggable bounds are changed due to the screen size change. 522 final Rect tmpRect = new Rect(windowBounds); 523 tmpRect.scale(2); 524 final Rect newWindowBounds = new Rect(tmpRect); 525 mWindowManager.setWindowBounds(newWindowBounds); 526 mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE); 527 528 final int expectedX = mMagnificationModeSwitch.mDraggableWindowBounds.right; 529 final int expectedY = (int) (windowHeightFraction 530 * mMagnificationModeSwitch.mDraggableWindowBounds.height()) 531 + mMagnificationModeSwitch.mDraggableWindowBounds.top; 532 assertEquals(expectedX, mWindowManager.getLayoutParamsFromAttachedView().x); 533 assertEquals(expectedY, mWindowManager.getLayoutParamsFromAttachedView().y); 534 } 535 assertShowFadingAnimation(float alpha)536 private void assertShowFadingAnimation(float alpha) { 537 final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); 538 if (alpha == FADE_IN_ALPHA) { // Fade-in 539 verify(mSpyImageView).postOnAnimation(runnableCaptor.capture()); 540 } else { // Fade-out 541 verify(mSpyImageView).postOnAnimationDelayed(runnableCaptor.capture(), anyLong()); 542 } 543 resetAndStubMockAnimator(); 544 545 runnableCaptor.getValue().run(); 546 547 verify(mViewPropertyAnimator).setDuration(eq(FADING_ANIMATION_DURATION_MS)); 548 verify(mViewPropertyAnimator).alpha(alpha); 549 verify(mViewPropertyAnimator).start(); 550 } 551 resetAndStubMockImageViewAndAnimator()552 private void resetAndStubMockImageViewAndAnimator() { 553 resetAndStubMockAnimator(); 554 Mockito.reset(mSpyImageView); 555 final Handler handler = mock(Handler.class); 556 when(mSpyImageView.getHandler()).thenReturn(handler); 557 doAnswer(invocation -> { 558 final Runnable runnable = invocation.getArgument(0); 559 runnable.run(); 560 return null; 561 }).when(handler).post(any(Runnable.class)); 562 doAnswer(invocation -> { 563 final Runnable runnable = invocation.getArgument(0); 564 runnable.run(); 565 return null; 566 }).when(mSpyImageView).post(any(Runnable.class)); 567 doReturn(mViewPropertyAnimator).when(mSpyImageView).animate(); 568 doAnswer((invocation) -> { 569 mFadeOutAnimation = invocation.getArgument(0); 570 return null; 571 }).when(mSpyImageView).postOnAnimationDelayed(any(Runnable.class), anyLong()); 572 doAnswer((invocation) -> { 573 if (mFadeOutAnimation == invocation.getArgument(0)) { 574 mFadeOutAnimation = null; 575 } 576 return null; 577 }).when(mSpyImageView).removeCallbacks(any(Runnable.class)); 578 } 579 resetAndStubMockAnimator()580 private void resetAndStubMockAnimator() { 581 Mockito.reset(mViewPropertyAnimator); 582 doNothing().when(mViewPropertyAnimator).start(); 583 } 584 obtainMotionEvent(long downTime, long eventTime, int action, float x, float y)585 private MotionEvent obtainMotionEvent(long downTime, long eventTime, int action, float x, 586 float y) { 587 return mMotionEventHelper.obtainMotionEvent(downTime, eventTime, action, x, y); 588 } 589 executeFadeOutAnimation()590 private void executeFadeOutAnimation() { 591 assertNotNull(mFadeOutAnimation); 592 mFadeOutAnimation.run(); 593 mFadeOutAnimation = null; 594 } 595 assertLayoutPosition(boolean toLeftScreenEdge)596 private void assertLayoutPosition(boolean toLeftScreenEdge) { 597 final int expectedX = 598 toLeftScreenEdge ? mMagnificationModeSwitch.mDraggableWindowBounds.left 599 : mMagnificationModeSwitch.mDraggableWindowBounds.right; 600 final int expectedY = mMagnificationModeSwitch.mDraggableWindowBounds.bottom; 601 final WindowManager.LayoutParams layoutParams = 602 mWindowManager.getLayoutParamsFromAttachedView(); 603 assertNotNull(layoutParams); 604 assertEquals(expectedX, layoutParams.x); 605 assertEquals(expectedY, layoutParams.y); 606 } 607 } 608