diff --git a/core/java/android/view/accessibility/MagnificationAnimationCallback.java b/core/java/android/view/accessibility/MagnificationAnimationCallback.java index bc9fb0a3e5e08..72518db3c60e0 100644 --- a/core/java/android/view/accessibility/MagnificationAnimationCallback.java +++ b/core/java/android/view/accessibility/MagnificationAnimationCallback.java @@ -21,6 +21,9 @@ package android.view.accessibility; * @hide */ public interface MagnificationAnimationCallback { + MagnificationAnimationCallback STUB_ANIMATION_CALLBACK = success -> { + }; + /** * Called when the animation is finished or interrupted during animating. * diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java index 36fef3ed6b512..8cb608f768eb1 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java @@ -106,6 +106,15 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp void enableWindowMagnification(float scale, float centerX, float centerY, @Nullable IRemoteMagnificationAnimationCallback animationCallback) { sendAnimationCallback(false); + // Enable window magnification without animation immediately. + if (animationCallback == null) { + if (mState == STATE_ENABLING || mState == STATE_DISABLING) { + mValueAnimator.cancel(); + } + mController.enableWindowMagnification(scale, centerX, centerY); + setState(STATE_ENABLED); + return; + } mAnimationCallback = animationCallback; setupEnableAnimationSpecs(scale, centerX, centerY); if (mEndSpec.equals(mStartSpec)) { @@ -173,6 +182,16 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp void deleteWindowMagnification( @Nullable IRemoteMagnificationAnimationCallback animationCallback) { sendAnimationCallback(false); + // Delete window magnification without animation. + if (animationCallback == null) { + if (mState == STATE_ENABLING || mState == STATE_DISABLING) { + mValueAnimator.cancel(); + } + mController.deleteWindowMagnification(); + setState(STATE_DISABLED); + return; + } + mAnimationCallback = animationCallback; if (mState == STATE_DISABLED || mState == STATE_DISABLING) { if (mState == STATE_DISABLED) { @@ -223,8 +242,7 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp if (mEndAnimationCanceled) { return; } - if (isReverse) { - mController.deleteWindowMagnification(); + if (Float.isNaN(mController.getScale())) { setState(STATE_DISABLED); } else { setState(STATE_ENABLED); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index 0893e895b102c..b48def2b948c6 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -262,6 +262,9 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold * Deletes the magnification window. */ void deleteWindowMagnification() { + if (!isWindowVisible()) { + return; + } if (mMirrorSurface != null) { mTransaction.remove(mMirrorSurface).apply(); mMirrorSurface = null; @@ -690,7 +693,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } /** - * Enables window magnification with specified parameters. + * Enables window magnification with specified parameters. If the given scale is less + * than or equal to 1.0f, then + * {@link WindowMagnificationController#deleteWindowMagnification()} will be called instead to + * be consistent with the behavior of display magnification. * * @param scale the target scale, or {@link Float#NaN} to leave unchanged * @param centerX the screen-relative X coordinate around which to center, @@ -699,6 +705,11 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold * or {@link Float#NaN} to leave unchanged. */ void enableWindowMagnification(float scale, float centerX, float centerY) { + if (Float.compare(scale, 1.0f) <= 0) { + deleteWindowMagnification(); + return; + } + final float offsetX = Float.isNaN(centerX) ? 0 : centerX - mMagnificationFrame.exactCenterX(); final float offsetY = Float.isNaN(centerY) ? 0 diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java index 045fb57f11f92..148c6ef0be536 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java @@ -48,6 +48,7 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; @@ -61,11 +62,11 @@ import java.util.concurrent.atomic.AtomicReference; @RunWith(AndroidTestingRunner.class) public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { - private static final float DEFAULT_SCALE = 3.0f; + private static final float DEFAULT_SCALE = 4.0f; private static final float DEFAULT_CENTER_X = 400.0f; private static final float DEFAULT_CENTER_Y = 500.0f; // The duration couldn't too short, otherwise the ValueAnimator won't work in expectation. - private static final long ANIMATION_DURATION_MS = 200; + private static final long ANIMATION_DURATION_MS = 300; private AtomicReference mCurrentScale = new AtomicReference<>((float) 0); private AtomicReference mCurrentCenterX = new AtomicReference<>((float) 0); @@ -84,7 +85,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { IRemoteMagnificationAnimationCallback mAnimationCallback; @Mock IRemoteMagnificationAnimationCallback mAnimationCallback2; - @Mock + @Mock(answer = Answers.RETURNS_SELF) SysUiState mSysUiState; private SpyWindowMagnificationController mController; private WindowMagnificationController mSpyController; @@ -127,6 +128,28 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { verify(mAnimationCallback).onResult(true); } + @Test + public void enableWindowMagnificationWithoutCallback_disabled_expectedValues() { + enableWindowMagnificationWithoutAnimation(); + + verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y); + } + + @Test + public void enableWindowMagnificationWithoutCallback_enabled_expectedValues() { + enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); + final float targetScale = DEFAULT_SCALE + 1.0f; + final float targetCenterX = DEFAULT_CENTER_X + 100; + final float targetCenterY = DEFAULT_CENTER_Y + 100; + + mInstrumentation.runOnMainSync( + () -> { + mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, + targetCenterX, targetCenterY, null); + }); + + verifyFinalSpec(targetScale, targetCenterX, targetCenterY); + } @Test public void enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback() @@ -173,6 +196,52 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { verify(mAnimationCallback2).onResult(true); } + @Test + public void enableWindowMagnificationWithScaleOne_enabled_AnimationAndInvokeCallback() + throws RemoteException { + enableWindowMagnificationWithoutAnimation(); + + mInstrumentation.runOnMainSync(() -> { + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(1.0f, + DEFAULT_CENTER_X + 100, DEFAULT_CENTER_Y + 100, mAnimationCallback); + mCurrentScale.set(mController.getScale()); + mCurrentCenterX.set(mController.getCenterX()); + mCurrentCenterY.set(mController.getCenterY()); + }); + + SystemClock.sleep(mWaitingAnimationPeriod); + + verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(), + mCenterXCaptor.capture(), mCenterYCaptor.capture()); + verifyStartValue(mScaleCaptor, mCurrentScale.get()); + verifyStartValue(mCenterXCaptor, mCurrentCenterX.get()); + verifyStartValue(mCenterYCaptor, mCurrentCenterY.get()); + // It presents the window magnification is disabled. + verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); + + verify(mAnimationCallback).onResult(true); + } + + @Test + public void + enableMagnificationWithoutCallback_enabling_expectedValuesAndInvokeFormerCallback() + throws RemoteException { + enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, + mAnimationCallback); + final float targetScale = DEFAULT_SCALE - 1.0f; + final float targetCenterX = DEFAULT_CENTER_X + 100; + final float targetCenterY = DEFAULT_CENTER_Y + 100; + + mInstrumentation.runOnMainSync(() -> { + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, + targetCenterX, targetCenterY, null); + }); + verifyFinalSpec(targetScale, targetCenterX, targetCenterY); + verify(mAnimationCallback).onResult(false); + } + @Test public void enableWindowMagnificationWithSameSpec_enabling_NoAnimationAndInvokeCallback() throws RemoteException { @@ -230,6 +299,28 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { verify(mAnimationCallback2).onResult(true); } + @Test + public void + enableMagnificationWithoutCallback_disabling_expectedValuesAndInvokeFormerCallback() + throws RemoteException { + enableWindowMagnificationWithoutAnimation(); + deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, + mAnimationCallback); + final float targetScale = DEFAULT_SCALE + 1.0f; + final float targetCenterX = DEFAULT_CENTER_X + 100; + final float targetCenterY = DEFAULT_CENTER_Y + 100; + + mInstrumentation.runOnMainSync( + () -> { + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, + targetCenterX, targetCenterY, null); + }); + + verify(mAnimationCallback).onResult(false); + verifyFinalSpec(targetScale, targetCenterX, targetCenterY); + } + @Test public void enableWindowMagnificationWithSameSpec_disabling_NoAnimationAndInvokeCallback() throws RemoteException { @@ -254,8 +345,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @Test public void enableWindowMagnification_enabled_expectedValuesAndInvokeCallback() throws RemoteException { - enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, - mAnimationCallback); + enableWindowMagnificationWithoutAnimation(); final float targetScale = DEFAULT_SCALE + 1.0f; final float targetCenterX = DEFAULT_CENTER_X + 100; final float targetCenterY = DEFAULT_CENTER_Y + 100; @@ -277,7 +367,6 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { verifyStartValue(mCenterXCaptor, mCurrentCenterX.get()); verifyStartValue(mCenterYCaptor, mCurrentCenterY.get()); verifyFinalSpec(targetScale, targetCenterX, targetCenterY); - verify(mAnimationCallback).onResult(false); verify(mAnimationCallback2).onResult(true); } @@ -295,7 +384,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @Test public void setScale_enabled_expectedScale() { - enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); + enableWindowMagnificationWithoutAnimation(); mInstrumentation.runOnMainSync( () -> mWindowMagnificationAnimationController.setScale(DEFAULT_SCALE + 1)); @@ -307,13 +396,12 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { @Test public void deleteWindowMagnification_enabled_expectedValuesAndInvokeCallback() throws RemoteException { - enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); + enableWindowMagnificationWithoutAnimation(); deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback); verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(), mCenterXCaptor.capture(), mCenterYCaptor.capture()); - verify(mSpyController).deleteWindowMagnification(); verifyStartValue(mScaleCaptor, DEFAULT_SCALE); verifyStartValue(mCenterXCaptor, Float.NaN); verifyStartValue(mCenterYCaptor, Float.NaN); @@ -321,6 +409,15 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { verify(mAnimationCallback).onResult(true); } + @Test + public void deleteWindowMagnificationWithoutCallback_enabled_expectedValues() { + enableWindowMagnificationWithoutAnimation(); + + deleteWindowMagnificationAndWaitAnimating(0, null); + + verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); + } + @Test public void deleteWindowMagnification_disabled_doNothingAndInvokeCallback() throws RemoteException { @@ -346,10 +443,8 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { mCurrentCenterY.set(mController.getCenterY()); }); SystemClock.sleep(mWaitingAnimationPeriod); - verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(), mCenterXCaptor.capture(), mCenterYCaptor.capture()); - verify(mSpyController).deleteWindowMagnification(); //The animation is in verse, so we only check the start values should no be greater than // the current one. @@ -362,6 +457,22 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { verify(mAnimationCallback2).onResult(true); } + @Test + public void deleteWindowMagnificationWithoutCallback_enabling_expectedValuesAndInvokeCallback() + throws RemoteException { + enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, + mAnimationCallback); + + mInstrumentation.runOnMainSync( + () -> { + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.deleteWindowMagnification(null); + }); + + verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); + verify(mAnimationCallback).onResult(false); + } + @Test public void deleteWindowMagnification_disabling_checkStartAndValues() throws RemoteException { enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); @@ -372,7 +483,6 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(), mCenterXCaptor.capture(), mCenterYCaptor.capture()); - verify(mSpyController).deleteWindowMagnification(); assertEquals(1.0f, mScaleCaptor.getValue(), 0f); verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); verify(mAnimationCallback).onResult(false); @@ -380,8 +490,22 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { } @Test - public void moveWindowMagnifier_enabled() { + public void deleteWindowMagnificationWithoutCallback_disabling_checkStartAndValues() + throws RemoteException { enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null); + deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod, + mAnimationCallback); + + deleteWindowMagnificationAndWaitAnimating(0, null); + + verify(mSpyController).deleteWindowMagnification(); + verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); + verify(mAnimationCallback).onResult(false); + } + + @Test + public void moveWindowMagnifier_enabled() { + enableWindowMagnificationWithoutAnimation(); mInstrumentation.runOnMainSync( () -> mWindowMagnificationAnimationController.moveWindowMagnifier(100f, 200f)); @@ -411,6 +535,15 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { assertEquals(expectedCenterY, mController.getCenterY(), 0f); } + private void enableWindowMagnificationWithoutAnimation() { + mInstrumentation.runOnMainSync( + () -> { + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE, + DEFAULT_CENTER_X, DEFAULT_CENTER_Y, null); + }); + } + private void enableWindowMagnificationAndWaitAnimating(long duration, @Nullable IRemoteMagnificationAnimationCallback callback) { mInstrumentation.runOnMainSync( @@ -484,12 +617,17 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { mSpyController.setScale(scale); } + @Override + public void updateSysUIStateFlag() { + super.updateSysUIStateFlag(); + mSpyController.updateSysUIStateFlag(); + } + @Override void onConfigurationChanged(int configDiff) { super.onConfigurationChanged(configDiff); mSpyController.onConfigurationChanged(configDiff); } - } private static ValueAnimator newValueAnimator() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index 14a60be364721..70457cf46e0db 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -468,6 +468,21 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { com.android.internal.R.string.android_system_label), getAccessibilityWindowTitle()); } + @Test + public void enableWindowMagnificationWithScaleLessThanOne_enabled_disabled() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + Float.NaN); + }); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnification(0.9f, Float.NaN, + Float.NaN); + }); + + assertEquals(Float.NaN, mWindowMagnificationController.getScale(), 0); + } + @Test public void onLocaleChanged_enabled_updateA11yWindowTitle() { final String newA11yWindowTitle = "new a11y window title"; diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index c62473db948c3..51e34178e80cd 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -17,6 +17,7 @@ package com.android.server.accessibility.magnification; import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL; +import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK; import android.animation.Animator; import android.animation.ValueAnimator; @@ -66,9 +67,6 @@ public class FullScreenMagnificationController { private static final boolean DEBUG = false; private static final String LOG_TAG = "FullScreenMagnificationController"; - private static final MagnificationAnimationCallback STUB_ANIMATION_CALLBACK = success -> { - }; - private static final boolean DEBUG_SET_MAGNIFICATION_SPEC = false; private final Object mLock; diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java index ce7ba7568b6e4..25205b2f9cf30 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java @@ -18,6 +18,7 @@ package com.android.server.accessibility.magnification; import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION; import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK; +import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK; import android.annotation.NonNull; import android.annotation.Nullable; @@ -74,7 +75,7 @@ public class WindowMagnificationManager implements public void onReceive(Context context, Intent intent) { final int displayId = context.getDisplayId(); removeMagnificationButton(displayId); - disableWindowMagnification(displayId, false); + disableWindowMagnification(displayId, false, null); } }; @@ -263,7 +264,7 @@ public class WindowMagnificationManager implements * or {@link Float#NaN} to leave unchanged. */ void enableWindowMagnification(int displayId, float scale, float centerX, float centerY) { - enableWindowMagnification(displayId, scale, centerX, centerY, null); + enableWindowMagnification(displayId, scale, centerX, centerY, STUB_ANIMATION_CALLBACK); } /** @@ -305,7 +306,7 @@ public class WindowMagnificationManager implements * @param clear {@true} Clears the state of window magnification. */ void disableWindowMagnification(int displayId, boolean clear) { - disableWindowMagnification(displayId, clear, null); + disableWindowMagnification(displayId, clear, STUB_ANIMATION_CALLBACK); } /** diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java index da881c4e64942..15ba358f146e3 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java @@ -19,6 +19,7 @@ package com.android.server.accessibility.magnification; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -169,13 +170,14 @@ public class WindowMagnificationManagerTest { } @Test - public void enable_hasConnection_enableWindowMagnification() throws RemoteException { + public void enableWithAnimation_hasConnection_enableWindowMagnification() + throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f); - verify(mMockConnection.getConnection()).enableWindowMagnification(TEST_DISPLAY, 2f, - 200f, 300f, null); + verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f), + eq(200f), eq(300f), notNull()); } @Test @@ -199,7 +201,8 @@ public class WindowMagnificationManagerTest { mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false); - verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null); + verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY), + notNull()); } @Test