diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java index 1af38970ea4c0..69a0d65a69636 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java @@ -46,9 +46,15 @@ import com.android.systemui.R; */ class MagnificationModeSwitch { - private static final int DURATION_MS = 5000; - private static final int START_DELAY_MS = 3000; - private final Runnable mAnimationTask; + @VisibleForTesting + static final long FADING_ANIMATION_DURATION_MS = 300; + private static final int DEFAULT_FADE_OUT_ANIMATION_DELAY_MS = 3000; + // The button visible duration starting from the last showButton() called. + private int mVisibleDuration = DEFAULT_FADE_OUT_ANIMATION_DELAY_MS; + private final Runnable mFadeInAnimationTask; + private final Runnable mFadeOutAnimationTask; + @VisibleForTesting + boolean mIsFadeOutAnimating = false; private final Context mContext; private final WindowManager mWindowManager; @@ -100,12 +106,19 @@ class MagnificationModeSwitch { } }); - mAnimationTask = () -> { + mFadeInAnimationTask = () -> { + mImageView.animate() + .alpha(1f) + .setDuration(FADING_ANIMATION_DURATION_MS) + .start(); + }; + mFadeOutAnimationTask = () -> { mImageView.animate() .alpha(0f) - .setDuration(DURATION_MS) + .setDuration(FADING_ANIMATION_DURATION_MS) .withEndAction(() -> removeButton()) .start(); + mIsFadeOutAnimating = true; }; } @@ -128,7 +141,6 @@ class MagnificationModeSwitch { } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - mImageView.setAlpha(1.0f); mImageView.animate().cancel(); mLastDown.set(event.getRawX(), event.getRawY()); mLastDrag.set(event.getRawX(), event.getRawY()); @@ -169,9 +181,13 @@ class MagnificationModeSwitch { if (!mIsVisible) { return; } - mImageView.animate().cancel(); - mWindowManager.removeView(mImageView); // Reset button status. + mImageView.removeCallbacks(mFadeInAnimationTask); + mImageView.removeCallbacks(mFadeOutAnimationTask); + mImageView.animate().cancel(); + mIsFadeOutAnimating = false; + mImageView.setAlpha(0f); + mWindowManager.removeView(mImageView); mIsVisible = false; mParams.x = 0; mParams.y = 0; @@ -185,14 +201,15 @@ class MagnificationModeSwitch { if (!mIsVisible) { mWindowManager.addView(mImageView, mParams); mIsVisible = true; + mImageView.postOnAnimation(mFadeInAnimationTask); } - mImageView.setAlpha(1.0f); - // TODO(b/143852371): use accessibility timeout as a delay. - // Dismiss the magnification switch button after the button is displayed for a period of - // time. - mImageView.animate().cancel(); - mImageView.removeCallbacks(mAnimationTask); - mImageView.postDelayed(mAnimationTask, START_DELAY_MS); + if (mIsFadeOutAnimating) { + mImageView.animate().cancel(); + mImageView.setAlpha(1f); + } + // Refresh the time slot of the fade-out task whenever this method is called. + mImageView.removeCallbacks(mFadeOutAnimationTask); + mImageView.postOnAnimationDelayed(mFadeOutAnimationTask, mVisibleDuration); } void onConfigurationChanged(int configDiff) { @@ -222,6 +239,7 @@ class MagnificationModeSwitch { imageView.setClickable(true); imageView.setFocusable(true); imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); + imageView.setAlpha(0f); return imageView; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java index 0a51b26f4a85f..3c248c73ab718 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java @@ -24,6 +24,7 @@ import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_UP; import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK; +import static com.android.systemui.accessibility.MagnificationModeSwitch.FADING_ANIMATION_DURATION_MS; import static com.android.systemui.accessibility.MagnificationModeSwitch.getIconResId; import static junit.framework.Assert.assertEquals; @@ -35,7 +36,6 @@ import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -69,6 +69,9 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) public class MagnificationModeSwitchTest extends SysuiTestCase { + private static final float FADE_IN_ALPHA = 1f; + private static final float FADE_OUT_ALPHA = 0f; + private ImageView mSpyImageView; @Mock private WindowManager mWindowManager; @@ -87,49 +90,47 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { ).when(mWindowManager).getMaximumWindowMetrics(); mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager); mSpyImageView = Mockito.spy(new ImageView(mContext)); - doAnswer(invocation -> null).when(mSpyImageView).setOnTouchListener( - mTouchListenerCaptor.capture()); - initMockImageViewAndAnimator(); + resetMockImageViewAndAnimator(); mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView); } @Test - public void removeButton_removeView() { + public void removeButton_buttonIsShowing_removeView() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); mMagnificationModeSwitch.removeButton(); verify(mWindowManager).removeView(mSpyImageView); - // First invocation is in showButton. - verify(mViewPropertyAnimator, times(2)).cancel(); + verify(mViewPropertyAnimator).cancel(); } @Test public void showWindowModeButton_fullscreenMode_addViewAndSetImageResource() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); - - verify(mSpyImageView).setAlpha(1.0f); verify(mSpyImageView).setImageResource( getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)); - assertShowButtonAnimation(); + assertShowFadingAnimation(FADE_IN_ALPHA); + assertShowFadingAnimation(FADE_OUT_ALPHA); + ArgumentCaptor captor = ArgumentCaptor.forClass(Runnable.class); verify(mViewPropertyAnimator).withEndAction(captor.capture()); verify(mWindowManager).addView(eq(mSpyImageView), any(WindowManager.LayoutParams.class)); captor.getValue().run(); - // First invocation is in showButton. - verify(mViewPropertyAnimator, times(2)).cancel(); + verify(mViewPropertyAnimator).cancel(); verify(mWindowManager).removeView(mSpyImageView); } @Test - public void onConfigurationChanged_setImageResource() { + public void onConfigurationChanged_buttonIsShowing_setImageResource() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); + resetMockImageViewAndAnimator(); + mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY); - verify(mSpyImageView, times(2)).setImageResource( + verify(mSpyImageView).setImageResource( getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)); } @@ -162,7 +163,6 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0); listener.onTouch(mSpyImageView, MotionEvent.obtain( 0, 0, ACTION_DOWN, 100, 100, 0)); - verify(mSpyImageView).setAlpha(1.0f); verify(mViewPropertyAnimator).cancel(); listener.onTouch(mSpyImageView, MotionEvent.obtain( @@ -173,9 +173,8 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { resetMockImageViewAndAnimator(); listener.onTouch(mSpyImageView, MotionEvent.obtain( 0, ViewConfiguration.getTapTimeout() + 10, ACTION_UP, 100 + offset, 100, 0)); - verify(mSpyImageView).setAlpha(1.0f); assertModeUnchanged(previousMode); - assertShowButtonAnimation(); + assertShowFadingAnimation(FADE_OUT_ALPHA); } @Test @@ -193,9 +192,8 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { resetMockImageViewAndAnimator(); listener.onTouch(mSpyImageView, MotionEvent.obtain( 0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100, 100, 0)); - verify(mSpyImageView).setAlpha(1.0f); assertModeUnchanged(previousMode); - assertShowButtonAnimation(); + assertShowFadingAnimation(FADE_OUT_ALPHA); } @Test @@ -216,9 +214,8 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { resetMockImageViewAndAnimator(); listener.onTouch(mSpyImageView, MotionEvent.obtain( 0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100 + offset, 100, 0)); - verify(mSpyImageView).setAlpha(1.0f); assertModeUnchanged(previousMode); - assertShowButtonAnimation(); + assertShowFadingAnimation(FADE_OUT_ALPHA); } @Test @@ -249,37 +246,55 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { verifyTapAction(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); } + @Test + public void showButton_showFadeOutAnimation_fadeOutAnimationCanceled() { + mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); + assertShowFadingAnimation(FADE_OUT_ALPHA); + resetMockImageViewAndAnimator(); + + mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); + + verify(mViewPropertyAnimator).cancel(); + assertEquals(1f, mSpyImageView.getAlpha()); + assertShowFadingAnimation(FADE_OUT_ALPHA); + } + private void assertModeUnchanged(int expectedMode) { final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0); assertEquals(expectedMode, actualMode); } - private void assertShowButtonAnimation() { - verify(mViewPropertyAnimator).cancel(); - verify(mViewPropertyAnimator).setDuration(anyLong()); - verify(mViewPropertyAnimator).alpha(anyFloat()); + private void assertShowFadingAnimation(float alpha) { + ArgumentCaptor runnableCaptor = ArgumentCaptor.forClass(Runnable.class); + if (alpha == FADE_IN_ALPHA) { // Fade-in + verify(mSpyImageView).postOnAnimation(runnableCaptor.capture()); + } else { // Fade-out + verify(mSpyImageView).postOnAnimationDelayed(runnableCaptor.capture(), anyLong()); + } + resetMockAnimator(); + + runnableCaptor.getValue().run(); + + verify(mViewPropertyAnimator).setDuration(eq(FADING_ANIMATION_DURATION_MS)); + verify(mViewPropertyAnimator).alpha(alpha); verify(mViewPropertyAnimator).start(); } - private void initMockImageViewAndAnimator() { + private void resetMockImageViewAndAnimator() { + Mockito.reset(mSpyImageView); + doAnswer(invocation -> null).when(mSpyImageView).setOnTouchListener( + mTouchListenerCaptor.capture()); + resetMockAnimator(); + } + + private void resetMockAnimator() { + Mockito.reset(mViewPropertyAnimator); when(mViewPropertyAnimator.setDuration(anyLong())).thenReturn(mViewPropertyAnimator); when(mViewPropertyAnimator.alpha(anyFloat())).thenReturn(mViewPropertyAnimator); when(mViewPropertyAnimator.withEndAction(any(Runnable.class))).thenReturn( mViewPropertyAnimator); - when(mSpyImageView.animate()).thenReturn(mViewPropertyAnimator); - doAnswer(invocation -> { - Runnable run = invocation.getArgument(0); - run.run(); - return null; - }).when(mSpyImageView).postDelayed(any(), anyLong()); - } - - private void resetMockImageViewAndAnimator() { - Mockito.reset(mViewPropertyAnimator); - Mockito.reset(mSpyImageView); - initMockImageViewAndAnimator(); } /**