Magnification button applies fade-in and fade-out animation
We use runnable task to handle the UI animation to prevent the case that "Disable animation" settings disables all the animation showing. The default showing duration of magnification button is 3 secs and the fade-in & fade-out animation durations are both 300ms. The showing step: 1.fade-in > 2.show magnification button > 3.fade-out And the demo video is attached in the bug Bug: 168867867 Test: atest MagnificationModeSwitchTest Change-Id: If2d9a93987555b947e36e09b5345aaf588c02927
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Runnable> 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<Runnable> 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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user