Support enable window magnification without animation

To extend the magnification API to window magnification mode,
we support enabling the window magnification without animation.
Besides, we also disable the window magnification if the given scale
is less than or equal to 1.0f.

For the screen off scenario, we change to disable the window
magnification without animation.

Bug: 204844820
Test: atest com.android.systemui.accessibility
      atest com.android.server.accessibility.magnification
Change-Id: I8a35492776f08ecee353f035a94a16fec8e61218
This commit is contained in:
ryanlwlin
2021-11-01 23:40:38 +08:00
parent 4d021782fe
commit 855260af7d
8 changed files with 214 additions and 27 deletions

View File

@@ -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.
*

View File

@@ -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);

View File

@@ -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 <strong>less
* than or equal to 1.0f<strong>, 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

View File

@@ -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<Float> mCurrentScale = new AtomicReference<>((float) 0);
private AtomicReference<Float> 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() {

View File

@@ -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";

View File

@@ -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;

View File

@@ -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);
}
/**

View File

@@ -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