Loading services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +106 −17 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ package com.android.server.accessibility.magnification; import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; Loading Loading @@ -61,6 +63,8 @@ public class FullScreenMagnificationController { private static final boolean DEBUG = false; private static final String LOG_TAG = "FullScreenMagnificationController"; private static final Runnable STUB_RUNNABLE = () -> { }; public static final float MIN_SCALE = 1.0f; public static final float MAX_SCALE = 8.0f; Loading Loading @@ -292,7 +296,7 @@ public class FullScreenMagnificationController { // Adjust the current spec's offsets if necessary. if (updateCurrentSpecWithOffsetsLocked( mCurrentMagnificationSpec.offsetX, mCurrentMagnificationSpec.offsetY)) { sendSpecToAnimation(mCurrentMagnificationSpec, false); sendSpecToAnimation(mCurrentMagnificationSpec, null); } onMagnificationChangedLocked(); } Loading @@ -300,17 +304,18 @@ public class FullScreenMagnificationController { } } void sendSpecToAnimation(MagnificationSpec spec, boolean animate) { void sendSpecToAnimation(MagnificationSpec spec, Runnable endCallback) { if (DEBUG) { Slog.i(LOG_TAG, "sendSpecToAnimation(spec = " + spec + ", animate = " + animate + ")"); "sendSpecToAnimation(spec = " + spec + ", endCallback = " + endCallback + ")"); } if (Thread.currentThread().getId() == mMainThreadId) { mSpecAnimationBridge.updateSentSpecMainThread(spec, animate); mSpecAnimationBridge.updateSentSpecMainThread(spec, endCallback); } else { final Message m = PooledLambda.obtainMessage( SpecAnimationBridge::updateSentSpecMainThread, mSpecAnimationBridge, spec, animate); mSpecAnimationBridge, spec, endCallback); mControllerCtx.getHandler().sendMessage(m); } } Loading Loading @@ -410,6 +415,11 @@ public class FullScreenMagnificationController { @GuardedBy("mLock") boolean reset(boolean animate) { return reset(transformToStubRunnable(animate)); } @GuardedBy("mLock") boolean reset(Runnable endCallback) { if (!mRegistered) { return false; } Loading @@ -420,7 +430,7 @@ public class FullScreenMagnificationController { onMagnificationChangedLocked(); } mIdOfLastServiceToMagnify = INVALID_ID; sendSpecToAnimation(spec, animate); sendSpecToAnimation(spec, endCallback); return changed; } Loading Loading @@ -448,24 +458,24 @@ public class FullScreenMagnificationController { final float centerX = normPivotX + offsetX; final float centerY = normPivotY + offsetY; mIdOfLastServiceToMagnify = id; return setScaleAndCenter(scale, centerX, centerY, animate, id); return setScaleAndCenter(scale, centerX, centerY, transformToStubRunnable(animate), id); } @GuardedBy("mLock") boolean setScaleAndCenter(float scale, float centerX, float centerY, boolean animate, int id) { Runnable endCallback, int id) { if (!mRegistered) { return false; } if (DEBUG) { Slog.i(LOG_TAG, "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX + ", centerY = " + centerY + ", animate = " + animate + ", centerY = " + centerY + ", endCallback = " + endCallback + ", id = " + id + ")"); } final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY); sendSpecToAnimation(mCurrentMagnificationSpec, animate); sendSpecToAnimation(mCurrentMagnificationSpec, endCallback); if (isMagnifying() && (id != INVALID_ID)) { mIdOfLastServiceToMagnify = id; } Loading Loading @@ -531,7 +541,7 @@ public class FullScreenMagnificationController { if (id != INVALID_ID) { mIdOfLastServiceToMagnify = id; } sendSpecToAnimation(mCurrentMagnificationSpec, false); sendSpecToAnimation(mCurrentMagnificationSpec, null); } boolean updateCurrentSpecWithOffsetsLocked(float nonNormOffsetX, float nonNormOffsetY) { Loading Loading @@ -865,12 +875,26 @@ public class FullScreenMagnificationController { * the spec did not change */ public boolean reset(int displayId, boolean animate) { return reset(displayId, animate ? STUB_RUNNABLE : null); } /** * Resets the magnification scale and center, optionally animating the * transition. * * @param displayId The logical display id. * @param endCallback Called when the animation is ended or the spec did not change. * {@code null} to transition immediately * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ public boolean reset(int displayId, Runnable endCallback) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); if (display == null) { return false; } return display.reset(animate); return display.reset(endCallback); } } Loading Loading @@ -921,7 +945,8 @@ public class FullScreenMagnificationController { if (display == null) { return false; } return display.setScaleAndCenter(Float.NaN, centerX, centerY, animate, id); return display.setScaleAndCenter(Float.NaN, centerX, centerY, animate ? STUB_RUNNABLE : null, id); } } Loading @@ -944,12 +969,35 @@ public class FullScreenMagnificationController { */ public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY, boolean animate, int id) { return setScaleAndCenter(displayId, scale, centerX, centerY, transformToStubRunnable(animate), id); } /** * Sets the scale and center of the magnified region, optionally * animating the transition. If animation is disabled, the transition * is immediate. * * @param displayId The logical display id. * @param scale the target scale, or {@link Float#NaN} to leave unchanged * @param centerX the screen-relative X coordinate around which to * center and scale, or {@link Float#NaN} to leave unchanged * @param centerY the screen-relative Y coordinate around which to * center and scale, or {@link Float#NaN} to leave unchanged * @param endCallback called when the transition is finished successfully or the spec did not * change. {@code null} to transition immediately. * @param id the ID of the service requesting the change * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY, Runnable endCallback, int id) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); if (display == null) { return false; } return display.setScaleAndCenter(scale, centerX, centerY, animate, id); return display.setScaleAndCenter(scale, centerX, centerY, endCallback, id); } } Loading Loading @@ -1160,7 +1208,8 @@ public class FullScreenMagnificationController { * Class responsible for animating spec on the main thread and sending spec * updates to the window manager. */ private static class SpecAnimationBridge implements ValueAnimator.AnimatorUpdateListener { private static class SpecAnimationBridge implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener { private final ControllerContext mControllerCtx; /** Loading @@ -1180,6 +1229,8 @@ public class FullScreenMagnificationController { */ private final ValueAnimator mValueAnimator; // Called when the callee wants animating and the sent spec matches the target spec. private Runnable mEndCallback; private final Object mLock; private final int mDisplayId; Loading @@ -1197,6 +1248,7 @@ public class FullScreenMagnificationController { mValueAnimator.setInterpolator(new DecelerateInterpolator(2.5f)); mValueAnimator.setFloatValues(0.0f, 1.0f); mValueAnimator.addUpdateListener(this); mValueAnimator.addListener(this); } /** Loading @@ -1216,21 +1268,33 @@ public class FullScreenMagnificationController { } } public void updateSentSpecMainThread(MagnificationSpec spec, boolean animate) { void updateSentSpecMainThread(MagnificationSpec spec, Runnable endCallback) { if (mValueAnimator.isRunning()) { // Avoid AnimationEnd Callback. mEndCallback = null; mValueAnimator.cancel(); } mEndCallback = endCallback; // If the current and sent specs don't match, update the sent spec. synchronized (mLock) { final boolean changed = !mSentMagnificationSpec.equals(spec); if (changed) { if (animate) { if (mEndCallback != null) { animateMagnificationSpecLocked(spec); } else { setMagnificationSpecLocked(spec); } } else { sendEndCallbackMainThread(); } } } private void sendEndCallbackMainThread() { if (mEndCallback != null) { mEndCallback.run(); mEndCallback = null; } } Loading Loading @@ -1270,6 +1334,26 @@ public class FullScreenMagnificationController { } } } @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { sendEndCallbackMainThread(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } } private static class ScreenStateObserver extends BroadcastReceiver { Loading Loading @@ -1395,4 +1479,9 @@ public class FullScreenMagnificationController { return mAnimationDuration; } } @Nullable private static Runnable transformToStubRunnable(boolean animate) { return animate ? STUB_RUNNABLE : null; } } services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +99 −1 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; Loading Loading @@ -95,12 +96,15 @@ public class FullScreenMagnificationControllerTest { ValueAnimator mMockValueAnimator; ValueAnimator.AnimatorUpdateListener mTargetAnimationListener; ValueAnimator.AnimatorListener mStateListener; FullScreenMagnificationController mFullScreenMagnificationController; Runnable mEndCallback; @Before public void setUp() { Looper looper = InstrumentationRegistry.getContext().getMainLooper(); mEndCallback = Mockito.mock(Runnable.class); // Pretending ID of the Thread associated with looper as main thread ID in controller when(mMockContext.getMainLooper()).thenReturn(looper); when(mMockControllerCtx.getContext()).thenReturn(mMockContext); Loading Loading @@ -319,6 +323,7 @@ public class FullScreenMagnificationControllerTest { for (int i = 0; i < DISPLAY_COUNT; i++) { setScaleAndCenter_animated_stateChangesAndAnimationHappens(i); resetMockWindowManager(); Mockito.reset(mEndCallback); } } Loading @@ -331,7 +336,7 @@ public class FullScreenMagnificationControllerTest { MagnificationSpec endSpec = getMagnificationSpec(scale, offsets); assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale, newCenter.x, newCenter.y, true, SERVICE_ID_1)); newCenter.x, newCenter.y, mEndCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5); Loading @@ -358,7 +363,33 @@ public class FullScreenMagnificationControllerTest { Mockito.reset(mMockWindowManager); when(mMockValueAnimator.getAnimatedFraction()).thenReturn(1.0f); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); mStateListener.onAnimationEnd(mMockValueAnimator); verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec))); verify(mEndCallback).run(); } @Test public void testSetScaleAndCenterWithAnimation_sameSpec_noAnimationButInvokeEndCallback() { for (int i = 0; i < DISPLAY_COUNT; i++) { setScaleAndCenter_sameSpec_noAnimationButInvokeEndCallback(i); Mockito.reset(mEndCallback); } } private void setScaleAndCenter_sameSpec_noAnimationButInvokeEndCallback(int displayId) { register(displayId); final PointF center = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; final float targetScale = 2.0f; assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, targetScale, center.x, center.y, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertFalse(mFullScreenMagnificationController.setScaleAndCenter(displayId, targetScale, center.x, center.y, mEndCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); verify(mMockValueAnimator, never()).start(); verify(mEndCallback).run(); } @Test Loading Loading @@ -638,6 +669,69 @@ public class FullScreenMagnificationControllerTest { assertFalse(mFullScreenMagnificationController.resetIfNeeded(displayId, false)); } @Test public void testReset_notMagnifying_noStateChangeButInvokeCallback() { for (int i = 0; i < DISPLAY_COUNT; i++) { reset_notMagnifying_noStateChangeButInvokeCallback(i); Mockito.reset(mEndCallback); } } private void reset_notMagnifying_noStateChangeButInvokeCallback(int displayId) { register(displayId); assertFalse(mFullScreenMagnificationController.reset(displayId, mEndCallback)); mMessageCapturingHandler.sendAllMessages(); verify(mMockAms, never()).notifyMagnificationChanged(eq(displayId), any(Region.class), anyFloat(), anyFloat(), anyFloat()); verify(mEndCallback).run(); } @Test public void testReset_Magnifying_resetsMagnificationAndInvokeLastEndCallback() { for (int i = 0; i < DISPLAY_COUNT; i++) { reset_Magnifying_resetsMagnificationAndInvokeLastEndCallback(i); } } private void reset_Magnifying_resetsMagnificationAndInvokeLastEndCallback(int displayId) { register(displayId); float scale = 2.5f; PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale, firstCenter.x, firstCenter.y, mEndCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); Mockito.reset(mMockValueAnimator); // Stubs the logic after the animation is started. doAnswer(invocation -> { mStateListener.onAnimationEnd(mMockValueAnimator); return null; }).when(mMockValueAnimator).cancel(); when(mMockValueAnimator.isRunning()).thenReturn(true); // Intermediate point float fraction = 0.33f; when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); Runnable lastEndCallback = Mockito.mock(Runnable.class); assertTrue(mFullScreenMagnificationController.reset(displayId, lastEndCallback)); mMessageCapturingHandler.sendAllMessages(); // Verify expected actions. verify(mEndCallback, never()).run(); verify(mMockValueAnimator).start(); verify(mMockValueAnimator).cancel(); // Fast-forward the animation to the end. when(mMockValueAnimator.getAnimatedFraction()).thenReturn(1.0f); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); mStateListener.onAnimationEnd(mMockValueAnimator); assertFalse(mFullScreenMagnificationController.isMagnifying(DISPLAY_0)); verify(lastEndCallback).run(); } @Test public void testTurnScreenOff_resetsMagnification() { register(DISPLAY_0); Loading Loading @@ -1043,6 +1137,10 @@ public class FullScreenMagnificationControllerTest { ArgumentCaptor.forClass(ValueAnimator.AnimatorUpdateListener.class); verify(mMockValueAnimator).addUpdateListener(listenerArgumentCaptor.capture()); mTargetAnimationListener = listenerArgumentCaptor.getValue(); ArgumentCaptor<ValueAnimator.AnimatorListener> animatorListenerArgumentCaptor = ArgumentCaptor.forClass(ValueAnimator.AnimatorListener.class); verify(mMockValueAnimator).addListener(animatorListenerArgumentCaptor.capture()); mStateListener = animatorListenerArgumentCaptor.getValue(); Mockito.reset(mMockValueAnimator); // Ignore other initialization } Loading Loading
services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +106 −17 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ package com.android.server.accessibility.magnification; import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; Loading Loading @@ -61,6 +63,8 @@ public class FullScreenMagnificationController { private static final boolean DEBUG = false; private static final String LOG_TAG = "FullScreenMagnificationController"; private static final Runnable STUB_RUNNABLE = () -> { }; public static final float MIN_SCALE = 1.0f; public static final float MAX_SCALE = 8.0f; Loading Loading @@ -292,7 +296,7 @@ public class FullScreenMagnificationController { // Adjust the current spec's offsets if necessary. if (updateCurrentSpecWithOffsetsLocked( mCurrentMagnificationSpec.offsetX, mCurrentMagnificationSpec.offsetY)) { sendSpecToAnimation(mCurrentMagnificationSpec, false); sendSpecToAnimation(mCurrentMagnificationSpec, null); } onMagnificationChangedLocked(); } Loading @@ -300,17 +304,18 @@ public class FullScreenMagnificationController { } } void sendSpecToAnimation(MagnificationSpec spec, boolean animate) { void sendSpecToAnimation(MagnificationSpec spec, Runnable endCallback) { if (DEBUG) { Slog.i(LOG_TAG, "sendSpecToAnimation(spec = " + spec + ", animate = " + animate + ")"); "sendSpecToAnimation(spec = " + spec + ", endCallback = " + endCallback + ")"); } if (Thread.currentThread().getId() == mMainThreadId) { mSpecAnimationBridge.updateSentSpecMainThread(spec, animate); mSpecAnimationBridge.updateSentSpecMainThread(spec, endCallback); } else { final Message m = PooledLambda.obtainMessage( SpecAnimationBridge::updateSentSpecMainThread, mSpecAnimationBridge, spec, animate); mSpecAnimationBridge, spec, endCallback); mControllerCtx.getHandler().sendMessage(m); } } Loading Loading @@ -410,6 +415,11 @@ public class FullScreenMagnificationController { @GuardedBy("mLock") boolean reset(boolean animate) { return reset(transformToStubRunnable(animate)); } @GuardedBy("mLock") boolean reset(Runnable endCallback) { if (!mRegistered) { return false; } Loading @@ -420,7 +430,7 @@ public class FullScreenMagnificationController { onMagnificationChangedLocked(); } mIdOfLastServiceToMagnify = INVALID_ID; sendSpecToAnimation(spec, animate); sendSpecToAnimation(spec, endCallback); return changed; } Loading Loading @@ -448,24 +458,24 @@ public class FullScreenMagnificationController { final float centerX = normPivotX + offsetX; final float centerY = normPivotY + offsetY; mIdOfLastServiceToMagnify = id; return setScaleAndCenter(scale, centerX, centerY, animate, id); return setScaleAndCenter(scale, centerX, centerY, transformToStubRunnable(animate), id); } @GuardedBy("mLock") boolean setScaleAndCenter(float scale, float centerX, float centerY, boolean animate, int id) { Runnable endCallback, int id) { if (!mRegistered) { return false; } if (DEBUG) { Slog.i(LOG_TAG, "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX + ", centerY = " + centerY + ", animate = " + animate + ", centerY = " + centerY + ", endCallback = " + endCallback + ", id = " + id + ")"); } final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY); sendSpecToAnimation(mCurrentMagnificationSpec, animate); sendSpecToAnimation(mCurrentMagnificationSpec, endCallback); if (isMagnifying() && (id != INVALID_ID)) { mIdOfLastServiceToMagnify = id; } Loading Loading @@ -531,7 +541,7 @@ public class FullScreenMagnificationController { if (id != INVALID_ID) { mIdOfLastServiceToMagnify = id; } sendSpecToAnimation(mCurrentMagnificationSpec, false); sendSpecToAnimation(mCurrentMagnificationSpec, null); } boolean updateCurrentSpecWithOffsetsLocked(float nonNormOffsetX, float nonNormOffsetY) { Loading Loading @@ -865,12 +875,26 @@ public class FullScreenMagnificationController { * the spec did not change */ public boolean reset(int displayId, boolean animate) { return reset(displayId, animate ? STUB_RUNNABLE : null); } /** * Resets the magnification scale and center, optionally animating the * transition. * * @param displayId The logical display id. * @param endCallback Called when the animation is ended or the spec did not change. * {@code null} to transition immediately * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ public boolean reset(int displayId, Runnable endCallback) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); if (display == null) { return false; } return display.reset(animate); return display.reset(endCallback); } } Loading Loading @@ -921,7 +945,8 @@ public class FullScreenMagnificationController { if (display == null) { return false; } return display.setScaleAndCenter(Float.NaN, centerX, centerY, animate, id); return display.setScaleAndCenter(Float.NaN, centerX, centerY, animate ? STUB_RUNNABLE : null, id); } } Loading @@ -944,12 +969,35 @@ public class FullScreenMagnificationController { */ public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY, boolean animate, int id) { return setScaleAndCenter(displayId, scale, centerX, centerY, transformToStubRunnable(animate), id); } /** * Sets the scale and center of the magnified region, optionally * animating the transition. If animation is disabled, the transition * is immediate. * * @param displayId The logical display id. * @param scale the target scale, or {@link Float#NaN} to leave unchanged * @param centerX the screen-relative X coordinate around which to * center and scale, or {@link Float#NaN} to leave unchanged * @param centerY the screen-relative Y coordinate around which to * center and scale, or {@link Float#NaN} to leave unchanged * @param endCallback called when the transition is finished successfully or the spec did not * change. {@code null} to transition immediately. * @param id the ID of the service requesting the change * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY, Runnable endCallback, int id) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); if (display == null) { return false; } return display.setScaleAndCenter(scale, centerX, centerY, animate, id); return display.setScaleAndCenter(scale, centerX, centerY, endCallback, id); } } Loading Loading @@ -1160,7 +1208,8 @@ public class FullScreenMagnificationController { * Class responsible for animating spec on the main thread and sending spec * updates to the window manager. */ private static class SpecAnimationBridge implements ValueAnimator.AnimatorUpdateListener { private static class SpecAnimationBridge implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener { private final ControllerContext mControllerCtx; /** Loading @@ -1180,6 +1229,8 @@ public class FullScreenMagnificationController { */ private final ValueAnimator mValueAnimator; // Called when the callee wants animating and the sent spec matches the target spec. private Runnable mEndCallback; private final Object mLock; private final int mDisplayId; Loading @@ -1197,6 +1248,7 @@ public class FullScreenMagnificationController { mValueAnimator.setInterpolator(new DecelerateInterpolator(2.5f)); mValueAnimator.setFloatValues(0.0f, 1.0f); mValueAnimator.addUpdateListener(this); mValueAnimator.addListener(this); } /** Loading @@ -1216,21 +1268,33 @@ public class FullScreenMagnificationController { } } public void updateSentSpecMainThread(MagnificationSpec spec, boolean animate) { void updateSentSpecMainThread(MagnificationSpec spec, Runnable endCallback) { if (mValueAnimator.isRunning()) { // Avoid AnimationEnd Callback. mEndCallback = null; mValueAnimator.cancel(); } mEndCallback = endCallback; // If the current and sent specs don't match, update the sent spec. synchronized (mLock) { final boolean changed = !mSentMagnificationSpec.equals(spec); if (changed) { if (animate) { if (mEndCallback != null) { animateMagnificationSpecLocked(spec); } else { setMagnificationSpecLocked(spec); } } else { sendEndCallbackMainThread(); } } } private void sendEndCallbackMainThread() { if (mEndCallback != null) { mEndCallback.run(); mEndCallback = null; } } Loading Loading @@ -1270,6 +1334,26 @@ public class FullScreenMagnificationController { } } } @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { sendEndCallbackMainThread(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } } private static class ScreenStateObserver extends BroadcastReceiver { Loading Loading @@ -1395,4 +1479,9 @@ public class FullScreenMagnificationController { return mAnimationDuration; } } @Nullable private static Runnable transformToStubRunnable(boolean animate) { return animate ? STUB_RUNNABLE : null; } }
services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +99 −1 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; Loading Loading @@ -95,12 +96,15 @@ public class FullScreenMagnificationControllerTest { ValueAnimator mMockValueAnimator; ValueAnimator.AnimatorUpdateListener mTargetAnimationListener; ValueAnimator.AnimatorListener mStateListener; FullScreenMagnificationController mFullScreenMagnificationController; Runnable mEndCallback; @Before public void setUp() { Looper looper = InstrumentationRegistry.getContext().getMainLooper(); mEndCallback = Mockito.mock(Runnable.class); // Pretending ID of the Thread associated with looper as main thread ID in controller when(mMockContext.getMainLooper()).thenReturn(looper); when(mMockControllerCtx.getContext()).thenReturn(mMockContext); Loading Loading @@ -319,6 +323,7 @@ public class FullScreenMagnificationControllerTest { for (int i = 0; i < DISPLAY_COUNT; i++) { setScaleAndCenter_animated_stateChangesAndAnimationHappens(i); resetMockWindowManager(); Mockito.reset(mEndCallback); } } Loading @@ -331,7 +336,7 @@ public class FullScreenMagnificationControllerTest { MagnificationSpec endSpec = getMagnificationSpec(scale, offsets); assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale, newCenter.x, newCenter.y, true, SERVICE_ID_1)); newCenter.x, newCenter.y, mEndCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5); Loading @@ -358,7 +363,33 @@ public class FullScreenMagnificationControllerTest { Mockito.reset(mMockWindowManager); when(mMockValueAnimator.getAnimatedFraction()).thenReturn(1.0f); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); mStateListener.onAnimationEnd(mMockValueAnimator); verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec))); verify(mEndCallback).run(); } @Test public void testSetScaleAndCenterWithAnimation_sameSpec_noAnimationButInvokeEndCallback() { for (int i = 0; i < DISPLAY_COUNT; i++) { setScaleAndCenter_sameSpec_noAnimationButInvokeEndCallback(i); Mockito.reset(mEndCallback); } } private void setScaleAndCenter_sameSpec_noAnimationButInvokeEndCallback(int displayId) { register(displayId); final PointF center = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; final float targetScale = 2.0f; assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, targetScale, center.x, center.y, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); assertFalse(mFullScreenMagnificationController.setScaleAndCenter(displayId, targetScale, center.x, center.y, mEndCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); verify(mMockValueAnimator, never()).start(); verify(mEndCallback).run(); } @Test Loading Loading @@ -638,6 +669,69 @@ public class FullScreenMagnificationControllerTest { assertFalse(mFullScreenMagnificationController.resetIfNeeded(displayId, false)); } @Test public void testReset_notMagnifying_noStateChangeButInvokeCallback() { for (int i = 0; i < DISPLAY_COUNT; i++) { reset_notMagnifying_noStateChangeButInvokeCallback(i); Mockito.reset(mEndCallback); } } private void reset_notMagnifying_noStateChangeButInvokeCallback(int displayId) { register(displayId); assertFalse(mFullScreenMagnificationController.reset(displayId, mEndCallback)); mMessageCapturingHandler.sendAllMessages(); verify(mMockAms, never()).notifyMagnificationChanged(eq(displayId), any(Region.class), anyFloat(), anyFloat(), anyFloat()); verify(mEndCallback).run(); } @Test public void testReset_Magnifying_resetsMagnificationAndInvokeLastEndCallback() { for (int i = 0; i < DISPLAY_COUNT; i++) { reset_Magnifying_resetsMagnificationAndInvokeLastEndCallback(i); } } private void reset_Magnifying_resetsMagnificationAndInvokeLastEndCallback(int displayId) { register(displayId); float scale = 2.5f; PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale, firstCenter.x, firstCenter.y, mEndCallback, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); Mockito.reset(mMockValueAnimator); // Stubs the logic after the animation is started. doAnswer(invocation -> { mStateListener.onAnimationEnd(mMockValueAnimator); return null; }).when(mMockValueAnimator).cancel(); when(mMockValueAnimator.isRunning()).thenReturn(true); // Intermediate point float fraction = 0.33f; when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); Runnable lastEndCallback = Mockito.mock(Runnable.class); assertTrue(mFullScreenMagnificationController.reset(displayId, lastEndCallback)); mMessageCapturingHandler.sendAllMessages(); // Verify expected actions. verify(mEndCallback, never()).run(); verify(mMockValueAnimator).start(); verify(mMockValueAnimator).cancel(); // Fast-forward the animation to the end. when(mMockValueAnimator.getAnimatedFraction()).thenReturn(1.0f); mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator); mStateListener.onAnimationEnd(mMockValueAnimator); assertFalse(mFullScreenMagnificationController.isMagnifying(DISPLAY_0)); verify(lastEndCallback).run(); } @Test public void testTurnScreenOff_resetsMagnification() { register(DISPLAY_0); Loading Loading @@ -1043,6 +1137,10 @@ public class FullScreenMagnificationControllerTest { ArgumentCaptor.forClass(ValueAnimator.AnimatorUpdateListener.class); verify(mMockValueAnimator).addUpdateListener(listenerArgumentCaptor.capture()); mTargetAnimationListener = listenerArgumentCaptor.getValue(); ArgumentCaptor<ValueAnimator.AnimatorListener> animatorListenerArgumentCaptor = ArgumentCaptor.forClass(ValueAnimator.AnimatorListener.class); verify(mMockValueAnimator).addListener(animatorListenerArgumentCaptor.capture()); mStateListener = animatorListenerArgumentCaptor.getValue(); Mockito.reset(mMockValueAnimator); // Ignore other initialization } Loading