Add callback for enabling/disabling window magnification

To support switching magnification mode, we add callback to know
when the animation is finished. We only invoke the newest callback
when the animation is end successfully or the spec didn't change

Bug: 161669184
Test: atest com.android.systemui.accessibility
      atest com.android.server.accessibility.magnification
      Enable window magnification to see if it works normally
Change-Id: I34ce39d133a000964d1089946b7bcf510a618b40
This commit is contained in:
ryanlwlin
2020-08-04 15:35:55 +08:00
parent 1be64f8a92
commit 6045db95e6
10 changed files with 365 additions and 115 deletions

View File

@@ -18,6 +18,7 @@ package android.view.accessibility;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.RemoteCallback;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
/**
@@ -37,8 +38,10 @@ oneway interface IWindowMagnificationConnection {
* or {@link Float#NaN} to leave unchanged.
* @param centerY the screen-relative Y coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
* @param endCallback The callback called when the animation is completed.
*/
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY);
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
in RemoteCallback endCallback);
/**
* Sets the scale of the window magnifier on specified display.
@@ -52,8 +55,9 @@ oneway interface IWindowMagnificationConnection {
* Disables window magnification on specified display with animation.
*
* @param displayId The logical display id.
* @param endCallback The callback called when the animation is completed.
*/
void disableWindowMagnification(int displayId);
void disableWindowMagnification(int displayId, in RemoteCallback endCallback);
/**
* Moves the window magnifier on the specified display. It has no effect while animating.

View File

@@ -18,11 +18,13 @@ package com.android.systemui.accessibility;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Log;
import android.view.SurfaceControl;
@@ -98,9 +100,11 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
}
@MainThread
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY) {
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
@Nullable RemoteCallback endCallback) {
//TODO: b/144080869 support multi-display.
mWindowMagnificationAnimationController.enableWindowMagnification(scale, centerX, centerY);
mWindowMagnificationAnimationController.enableWindowMagnification(scale, centerX, centerY,
endCallback != null ? () -> endCallback.sendResult(null) : null);
}
@MainThread
@@ -116,9 +120,10 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
}
@MainThread
void disableWindowMagnification(int displayId) {
void disableWindowMagnification(int displayId, @Nullable RemoteCallback endCallback) {
//TODO: b/144080869 support multi-display.
mWindowMagnificationAnimationController.deleteWindowMagnification();
mWindowMagnificationAnimationController.deleteWindowMagnification(
endCallback != null ? () -> endCallback.sendResult(null) : null);
}
@Override
@@ -177,10 +182,10 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
@Override
public void enableWindowMagnification(int displayId, float scale, float centerX,
float centerY) {
float centerY, RemoteCallback remoteCallback) {
mHandler.post(
() -> mWindowMagnification.enableWindowMagnification(displayId, scale, centerX,
centerY));
centerY, remoteCallback));
}
@Override
@@ -189,8 +194,9 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
}
@Override
public void disableWindowMagnification(int displayId) {
mHandler.post(() -> mWindowMagnification.disableWindowMagnification(displayId));
public void disableWindowMagnification(int displayId, RemoteCallback remoteCallback) {
mHandler.post(() -> mWindowMagnification.disableWindowMagnification(displayId,
remoteCallback));
}
@Override

View File

@@ -19,6 +19,7 @@ package com.android.systemui.accessibility;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.util.Log;
@@ -44,13 +45,13 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
@IntDef({STATE_DISABLED, STATE_ENABLED, STATE_DISABLING, STATE_ENABLING})
@interface MagnificationState {}
//The window magnification is disabled.
// The window magnification is disabled.
private static final int STATE_DISABLED = 0;
//The window magnification is enabled.
// The window magnification is enabled.
private static final int STATE_ENABLED = 1;
//The window magnification is going to be disabled when the animation is end.
// The window magnification is going to be disabled when the animation is end.
private static final int STATE_DISABLING = 2;
//The animation is running for enabling the window magnification.
// The animation is running for enabling the window magnification.
private static final int STATE_ENABLING = 3;
private final WindowMagnificationController mController;
@@ -58,7 +59,11 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
private final AnimationSpec mStartSpec = new AnimationSpec();
private final AnimationSpec mEndSpec = new AnimationSpec();
private final Context mContext;
// Called when the animation is ended successfully without cancelling or mStartSpec and
// mEndSpec are equal.
private Runnable mAnimationEndCallback;
// The flag to ignore the animation end callback.
private boolean mEndAnimationCanceled = false;
@MagnificationState
private int mState = STATE_DISABLED;
@@ -83,26 +88,35 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
* from 1.0 and the center won't be changed during the animation. If {@link #mState} is
* {@code STATE_DISABLING}, the animation runs in reverse.
*
* @param scale the target scale, or {@link Float#NaN} to leave unchanged.
* @param centerX the screen-relative X coordinate around which to center,
* @param scale The target scale, or {@link Float#NaN} to leave unchanged.
* @param centerX The screen-relative X coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
* @param centerY the screen-relative Y coordinate around which to center,
* @param centerY The screen-relative Y coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
* @param animationEndCallback Called when the transition is complete or the given arguments
* are as same as current values.
*
* @see #onAnimationUpdate(ValueAnimator)
*/
void enableWindowMagnification(float scale, float centerX, float centerY) {
if (mState == STATE_ENABLING) {
mValueAnimator.cancel();
}
void enableWindowMagnification(float scale, float centerX, float centerY,
@Nullable Runnable animationEndCallback) {
mAnimationEndCallback = animationEndCallback;
setupEnableAnimationSpecs(scale, centerX, centerY);
if (mEndSpec.equals(mStartSpec)) {
if (mState == STATE_DISABLED) {
mController.enableWindowMagnification(scale, centerX, centerY);
} else if (mState == STATE_ENABLING || mState == STATE_DISABLING) {
mValueAnimator.cancel();
}
sendCallbackIfNeeded();
setState(STATE_ENABLED);
} else {
if (mState == STATE_DISABLING) {
mValueAnimator.reverse();
} else {
if (mState == STATE_ENABLING) {
mValueAnimator.cancel();
}
mValueAnimator.start();
}
setState(STATE_ENABLING);
@@ -115,7 +129,7 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
final float currentCenterY = mController.getCenterY();
if (mState == STATE_DISABLED) {
//We don't need to offset the center during the animation.
// We don't need to offset the center during the animation.
mStartSpec.set(/* scale*/ 1.0f, centerX, centerY);
mEndSpec.set(Float.isNaN(scale) ? mContext.getResources().getInteger(
R.integer.magnification_default_scale) : scale, centerX, centerY);
@@ -145,9 +159,16 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
/**
* Wraps {@link WindowMagnificationController#deleteWindowMagnification()}} with transition
* animation. If the window magnification is enabling, it runs the animation in reverse.
*
* @param animationEndCallback Called when the transition is complete or the window
* magnification is disabled already.
*/
void deleteWindowMagnification() {
void deleteWindowMagnification(@Nullable Runnable animationEndCallback) {
mAnimationEndCallback = animationEndCallback;
if (mState == STATE_DISABLED || mState == STATE_DISABLING) {
if (mState == STATE_DISABLED) {
sendCallbackIfNeeded();
}
return;
}
mStartSpec.set(/* scale*/ 1.0f, Float.NaN, Float.NaN);
@@ -160,9 +181,9 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
/**
* Wraps {@link WindowMagnificationController#moveWindowMagnifier(float, float)}. If the
* animation is running, it has no effect.
* @param offsetX the amount in pixels to offset the window magnifier in the X direction, in
* @param offsetX The amount in pixels to offset the window magnifier in the X direction, in
* current screen pixels.
* @param offsetY the amount in pixels to offset the window magnifier in the Y direction, in
* @param offsetY The amount in pixels to offset the window magnifier in the Y direction, in
* current screen pixels.
*/
void moveWindowMagnifier(float offsetX, float offsetY) {
@@ -185,28 +206,43 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
@Override
public void onAnimationStart(Animator animation) {
mEndAnimationCanceled = false;
}
@Override
public void onAnimationEnd(Animator animation, boolean isReverse) {
if (mEndAnimationCanceled) {
return;
}
if (isReverse) {
mController.deleteWindowMagnification();
setState(STATE_DISABLED);
} else {
setState(STATE_ENABLED);
}
sendCallbackIfNeeded();
}
@Override
public void onAnimationEnd(Animator animation) {
if (mState == STATE_DISABLING) {
mController.deleteWindowMagnification();
setState(STATE_DISABLED);
} else if (mState == STATE_ENABLING) {
setState(STATE_ENABLED);
} else {
Log.w(TAG, "onAnimationEnd unexpected state:" + mState);
}
}
@Override
public void onAnimationCancel(Animator animation) {
mEndAnimationCanceled = true;
}
@Override
public void onAnimationRepeat(Animator animation) {
}
private void sendCallbackIfNeeded() {
if (mAnimationEndCallback != null) {
mAnimationEndCallback.run();
mAnimationEndCallback = null;
}
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
final float fract = animation.getAnimatedFraction();

View File

@@ -18,10 +18,12 @@ package com.android.systemui.accessibility;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
@@ -39,6 +41,7 @@ import com.android.systemui.statusbar.CommandQueue;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -62,6 +65,9 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
@Mock
private ModeSwitchesController mModeSwitchesController;
@Mock
private RemoteCallback mRemoteCallback;
private ArgumentCaptor<Runnable> mRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
private IWindowMagnificationConnection mIWindowMagnificationConnection;
private WindowMagnification mWindowMagnification;
@@ -84,25 +90,24 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
}
@Test
public void enableWindowMagnification() throws RemoteException {
public void enableWindowMagnification_passThrough() throws RemoteException {
mIWindowMagnificationConnection.enableWindowMagnification(TEST_DISPLAY, 3.0f, Float.NaN,
Float.NaN);
Float.NaN, mRemoteCallback);
waitForIdleSync();
verify(mWindowMagnificationAnimationController).enableWindowMagnification(3.0f, Float.NaN,
Float.NaN);
verify(mWindowMagnificationAnimationController).enableWindowMagnification(eq(3.0f),
eq(Float.NaN), eq(Float.NaN), mRunnableCaptor.capture());
verifyRunnableWrapsRemoteCallback(mRunnableCaptor.getValue());
}
@Test
public void disableWindowMagnification_deleteWindowMagnification() throws RemoteException {
mIWindowMagnificationConnection.enableWindowMagnification(TEST_DISPLAY, 3.0f, Float.NaN,
Float.NaN);
mIWindowMagnificationConnection.disableWindowMagnification(TEST_DISPLAY, mRemoteCallback);
waitForIdleSync();
mIWindowMagnificationConnection.disableWindowMagnification(TEST_DISPLAY);
waitForIdleSync();
verify(mWindowMagnificationAnimationController).deleteWindowMagnification();
verify(mWindowMagnificationAnimationController).deleteWindowMagnification(
mRunnableCaptor.capture());
verifyRunnableWrapsRemoteCallback(mRunnableCaptor.getValue());
}
@Test
@@ -138,5 +143,10 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase {
verify(mModeSwitchesController).removeButton(TEST_DISPLAY);
}
private void verifyRunnableWrapsRemoteCallback(Runnable runnable) {
runnable.run();
verify(mRemoteCallback).sendResult(null);
}
}

View File

@@ -24,6 +24,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.app.Instrumentation;
import android.content.Context;
import android.os.Handler;
@@ -73,7 +74,10 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
@Mock
WindowMagnifierCallback mWindowMagnifierCallback;
@Mock
Runnable mAnimationEndCallback;
@Mock
Runnable mAnimationEndCallback2;
private SpyWindowMagnificationController mController;
private WindowMagnificationController mSpyController;
private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
@@ -101,8 +105,8 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
}
@Test
public void enableWindowMagnification_disabled_expectedStartAndEndValues() {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
public void enableWindowMagnification_disabled_expectedValuesAndInvokeCallback() {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback);
verify(mSpyController, atLeast(2)).enableWindowMagnification(
mScaleCaptor.capture(),
@@ -111,11 +115,28 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
verifyStartValue(mCenterXCaptor, DEFAULT_CENTER_X);
verifyStartValue(mCenterYCaptor, DEFAULT_CENTER_Y);
verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
verify(mAnimationEndCallback).run();
}
@Test
public void enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback() {
mInstrumentation.runOnMainSync(
() -> {
mWindowMagnificationAnimationController.enableWindowMagnification(1,
DEFAULT_CENTER_X, DEFAULT_CENTER_Y, mAnimationEndCallback);
});
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController).enableWindowMagnification(1, DEFAULT_CENTER_X,
DEFAULT_CENTER_Y);
verify(mAnimationEndCallback).run();
}
@Test
public void enableWindowMagnification_enabling_expectedStartAndEndValues() {
enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
public void enableWindowMagnification_enabling_expectedValuesAndInvokeCallback() {
enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
mAnimationEndCallback);
final float targetScale = DEFAULT_SCALE + 1.0f;
final float targetCenterX = DEFAULT_CENTER_X + 100;
final float targetCenterY = DEFAULT_CENTER_Y + 100;
@@ -123,7 +144,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
mInstrumentation.runOnMainSync(() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
targetCenterX, targetCenterY);
targetCenterX, targetCenterY, mAnimationEndCallback2);
mCurrentScale.set(mController.getScale());
mCurrentCenterX.set(mController.getCenterX());
mCurrentCenterY.set(mController.getCenterY());
@@ -137,12 +158,33 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
verify(mAnimationEndCallback, never()).run();
verify(mAnimationEndCallback2).run();
}
@Test
public void enableWindowMagnification_disabling_expectedStartAndEndValues() {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
public void enableWindowMagnificationWithSameSpec_enabling_NoAnimationAndInvokeCallback() {
enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
mAnimationEndCallback);
mInstrumentation.runOnMainSync(() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
Float.NaN, Float.NaN, mAnimationEndCallback2);
});
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
anyFloat());
verify(mAnimationEndCallback, never()).run();
verify(mAnimationEndCallback2).run();
}
@Test
public void enableWindowMagnification_disabling_expectedValuesAndInvokeCallback() {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
mAnimationEndCallback);
final float targetScale = DEFAULT_SCALE + 1.0f;
final float targetCenterX = DEFAULT_CENTER_X + 100;
final float targetCenterY = DEFAULT_CENTER_Y + 100;
@@ -151,11 +193,13 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
targetCenterX, targetCenterY);
targetCenterX, targetCenterY, mAnimationEndCallback2);
mCurrentScale.set(mController.getScale());
mCurrentCenterX.set(mController.getCenterX());
mCurrentCenterY.set(mController.getCenterY());
});
// Current spec shouldn't match given spec.
verify(mAnimationEndCallback2, never()).run();
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController, atLeast(2)).enableWindowMagnification(
@@ -169,21 +213,73 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
assertTrue(mCenterYCaptor.getAllValues().get(0) > mCurrentCenterY.get());
assertEquals(targetCenterY, mCenterYCaptor.getValue(), 0f);
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
verify(mAnimationEndCallback, never()).run();
verify(mAnimationEndCallback2).run();
}
@Test
public void enableWindowMagnificationWithSameScale_doNothing() {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
public void enableWindowMagnificationWithSameSpec_disabling_NoAnimationAndInvokeCallback() {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
mAnimationEndCallback);
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
mInstrumentation.runOnMainSync(() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
Float.NaN, Float.NaN, mAnimationEndCallback2);
});
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
anyFloat());
verify(mSpyController, never()).deleteWindowMagnification();
verify(mAnimationEndCallback, never()).run();
verify(mAnimationEndCallback2).run();
}
@Test
public void enableWindowMagnification_enabled_expectedValuesAndInvokeCallback() {
enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
mAnimationEndCallback);
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, mAnimationEndCallback2);
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());
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
verify(mAnimationEndCallback, never()).run();
verify(mAnimationEndCallback2).run();
}
@Test
public void enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback() {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback);
verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
anyFloat());
verify(mAnimationEndCallback).run();
}
@Test
public void setScale_enabled_expectedScale() {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
mInstrumentation.runOnMainSync(
() -> mWindowMagnificationAnimationController.setScale(DEFAULT_SCALE + 1));
@@ -193,10 +289,10 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
}
@Test
public void deleteWindowMagnification_enabled_expectedStartAndEndValues() {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
public void deleteWindowMagnification_enabled_expectedValuesAndInvokeCallback() {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback);
verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture());
@@ -205,24 +301,27 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
verifyStartValue(mCenterXCaptor, Float.NaN);
verifyStartValue(mCenterYCaptor, Float.NaN);
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
verify(mAnimationEndCallback).run();
}
@Test
public void deleteWindowMagnification_disabled_doNothing() {
deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
public void deleteWindowMagnification_disabled_doNothingAndInvokeCallback() {
deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback);
Mockito.verifyNoMoreInteractions(mSpyController);
verify(mAnimationEndCallback).run();
}
@Test
public void deleteWindowMagnification_enabling_checkStartAndEndValues() {
enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
public void deleteWindowMagnification_enabling_expectedValuesAndInvokeCallback() {
enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
mAnimationEndCallback);
//It just reverse the animation, so we don't need to wait the whole duration.
mInstrumentation.runOnMainSync(
() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.deleteWindowMagnification();
mWindowMagnificationAnimationController.deleteWindowMagnification(
mAnimationEndCallback2);
mCurrentScale.set(mController.getScale());
mCurrentCenterX.set(mController.getCenterX());
mCurrentCenterY.set(mController.getCenterY());
@@ -240,25 +339,30 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
verifyStartValue(mCenterXCaptor, Float.NaN);
verifyStartValue(mCenterYCaptor, Float.NaN);
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
verify(mAnimationEndCallback, never()).run();
verify(mAnimationEndCallback2).run();
}
@Test
public void deleteWindowMagnification_disabling_checkStartAndValues() {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
mAnimationEndCallback);
deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback2);
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(mAnimationEndCallback, never()).run();
verify(mAnimationEndCallback2).run();
}
@Test
public void moveWindowMagnifier_enabled() {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
mInstrumentation.runOnMainSync(
() -> mWindowMagnificationAnimationController.moveWindowMagnifier(100f, 200f));
@@ -273,6 +377,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
verify(mSpyController).onConfigurationChanged(100);
}
private void verifyFinalSpec(float expectedScale, float expectedCenterX,
float expectedCenterY) {
assertEquals(expectedScale, mController.getScale(), 0f);
@@ -280,21 +385,23 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
assertEquals(expectedCenterY, mController.getCenterY(), 0f);
}
private void enableWindowMagnificationAndWaitAnimating(long duration) {
private void enableWindowMagnificationAndWaitAnimating(long duration,
@Nullable Runnable endCallback) {
mInstrumentation.runOnMainSync(
() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
DEFAULT_CENTER_X, DEFAULT_CENTER_Y, endCallback);
});
SystemClock.sleep(duration);
}
private void deleteWindowMagnificationAndWaitAnimating(long duration) {
private void deleteWindowMagnificationAndWaitAnimating(long duration,
@Nullable Runnable endCallback) {
mInstrumentation.runOnMainSync(
() -> {
resetMockObjects();
mWindowMagnificationAnimationController.deleteWindowMagnification();
mWindowMagnificationAnimationController.deleteWindowMagnification(endCallback);
});
SystemClock.sleep(duration);
}

View File

@@ -19,6 +19,8 @@ package com.android.server.accessibility.magnification;
import static android.os.IBinder.DeathRecipient;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Slog;
import android.view.accessibility.IWindowMagnificationConnection;
@@ -47,9 +49,10 @@ class WindowMagnificationConnectionWrapper {
mConnection.asBinder().linkToDeath(deathRecipient, 0);
}
boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY) {
boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
@Nullable RemoteCallback endCallback) {
try {
mConnection.enableWindowMagnification(displayId, scale, centerX, centerY);
mConnection.enableWindowMagnification(displayId, scale, centerX, centerY, endCallback);
} catch (RemoteException e) {
if (DBG) {
Slog.e(TAG, "Error calling enableWindowMagnification()", e);
@@ -71,9 +74,9 @@ class WindowMagnificationConnectionWrapper {
return true;
}
boolean disableWindowMagnification(int displayId) {
boolean disableWindowMagnification(int displayId, @Nullable RemoteCallback endCallback) {
try {
mConnection.disableWindowMagnification(displayId);
mConnection.disableWindowMagnification(displayId, endCallback);
} catch (RemoteException e) {
if (DBG) {
Slog.e(TAG, "Error calling disableWindowMagnification()", e);

View File

@@ -24,6 +24,7 @@ import android.content.IntentFilter;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.MathUtils;
@@ -84,6 +85,7 @@ public class WindowMagnificationManager implements
/**
* Sets {@link IWindowMagnificationConnection}.
*
* @param connection {@link IWindowMagnificationConnection}
*/
public void setConnection(@Nullable IWindowMagnificationConnection connection) {
@@ -124,7 +126,6 @@ public class WindowMagnificationManager implements
}
/**
*
* @return {@code true} if {@link IWindowMagnificationConnection} is available
*/
public boolean isConnected() {
@@ -139,7 +140,7 @@ public class WindowMagnificationManager implements
* destroys all window magnifications if necessary.
*
* @param connect {@code true} if needs connection, otherwise set the connection to null and
* destroy all window magnifications.
* destroy all window magnifications.
* @return {@code true} if {@link IWindowMagnificationConnection} state is going to change.
*/
public boolean requestConnection(boolean connect) {
@@ -171,7 +172,7 @@ public class WindowMagnificationManager implements
private void disableAllWindowMagnifiers() {
for (int i = 0; i < mWindowMagnifiers.size(); i++) {
final WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i);
magnifier.disableWindowMagnificationInternal();
magnifier.disableWindowMagnificationInternal(null);
}
mWindowMagnifiers.clear();
}
@@ -192,7 +193,7 @@ public class WindowMagnificationManager implements
}
/**
* Scales the magnified region on the specified display if the window magnifier is initiated.
* Scales the magnified region on the specified display if window magnification is initiated.
*
* @param displayId The logical display id.
* @param scale The target scale, must be >= 1
@@ -209,8 +210,10 @@ public class WindowMagnificationManager implements
}
/**
* Enables window magnification with specified center and scale on the specified display.
* @param displayId The logical display id.
* Enables window magnification with specified center and scale on the given display and
* animating the transition.
*
* @param displayId The logical display id.
* @param scale The target scale, must be >= 1.
* @param centerX The screen-relative X coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
@@ -218,28 +221,58 @@ 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);
}
/**
* Enables window magnification with specified center and scale on the specified display and
* animating the transition.
*
* @param displayId The logical display id.
* @param scale The target scale, must be >= 1.
* @param centerX The screen-relative X coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
* @param centerY The screen-relative Y coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
* @param endCallback Called when the animation is ended without any interruption or the
* window magnifier is disabled already.
*/
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
@Nullable Runnable endCallback) {
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
magnifier = createWindowMagnifier(displayId);
}
magnifier.enableWindowMagnificationInternal(scale, centerX, centerY);
magnifier.enableWindowMagnificationInternal(scale, centerX, centerY, endCallback);
}
}
/**
* Disables window magnification on the specified display.
* Disables window magnification on the given display.
*
* @param displayId The logical display id.
* @param clear {@true} Clears the state of the window magnifier
* @param clear {@true} Clears the state of window magnification.
*/
void disableWindowMagnification(int displayId, boolean clear) {
disableWindowMagnification(displayId, clear, null);
}
/**
* Disables window magnification on the specified display and animating the transition.
*
* @param displayId The logical display id.
* @param clear {@true} Clears the state of window magnification.
* @param endCallback Called when the animation is ended without any interruption or the
* window magnifier is disabled already.
*/
void disableWindowMagnification(int displayId, boolean clear, Runnable endCallback) {
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
return;
}
magnifier.disableWindowMagnificationInternal();
magnifier.disableWindowMagnificationInternal(endCallback);
if (clear) {
mWindowMagnifiers.delete(displayId);
}
@@ -366,6 +399,7 @@ public class WindowMagnificationManager implements
/**
* Creates the windowMagnifier based on the specified display and stores it.
*
* @param displayId logical display id.
*/
@GuardedBy("mLock")
@@ -439,28 +473,30 @@ public class WindowMagnificationManager implements
private final Rect mBounds = new Rect();
//The magnified bounds on the screen.
private final Rect mSourceBounds = new Rect();
WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) {
mDisplayId = displayId;
mWindowMagnificationManager = windowMagnificationManager;
}
@GuardedBy("mLock")
void enableWindowMagnificationInternal(float scale, float centerX, float centerY) {
void enableWindowMagnificationInternal(float scale, float centerX, float centerY,
@Nullable Runnable endCallback) {
if (mEnabled) {
return;
}
final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale,
centerX, centerY)) {
centerX, centerY, endCallback)) {
mScale = normScale;
mEnabled = true;
}
}
@GuardedBy("mLock")
void disableWindowMagnificationInternal() {
void disableWindowMagnificationInternal(@Nullable Runnable endCallback) {
if (mEnabled && mWindowMagnificationManager.disableWindowMagnificationInternal(
mDisplayId)) {
mDisplayId, endCallback)) {
mEnabled = false;
}
}
@@ -522,18 +558,20 @@ public class WindowMagnificationManager implements
}
private boolean enableWindowMagnificationInternal(int displayId, float scale, float centerX,
float centerY) {
float centerY, Runnable endCallback) {
return mConnectionWrapper != null && mConnectionWrapper.enableWindowMagnification(
displayId, scale, centerX, centerY);
displayId, scale, centerX, centerY,
endCallback != null ? new RemoteCallback(bundle -> endCallback.run()) : null);
}
private boolean setScaleInternal(int displayId, float scale) {
return mConnectionWrapper != null && mConnectionWrapper.setScale(displayId, scale);
}
private boolean disableWindowMagnificationInternal(int displayId) {
private boolean disableWindowMagnificationInternal(int displayId, Runnable endCallback) {
return mConnectionWrapper != null && mConnectionWrapper.disableWindowMagnification(
displayId);
displayId,
endCallback != null ? new RemoteCallback(bundle -> endCallback.run()) : null);
}
private boolean moveWindowMagnifierInternal(int displayId, float offsetX, float offsetY) {

View File

@@ -20,6 +20,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -27,6 +28,7 @@ import static org.mockito.Mockito.when;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.view.Display;
import android.view.accessibility.IWindowMagnificationConnection;
@@ -69,13 +71,28 @@ class MockWindowMagnificationConnection {
if (displayId != TEST_DISPLAY) {
throw new IllegalArgumentException("only support default display :" + displayId);
}
computeMirrorWindowFrame(invocation.getArgument(1), invocation.getArgument(2));
computeMirrorWindowFrame(invocation.getArgument(2), invocation.getArgument(3));
final RemoteCallback callback = invocation.getArgument(4);
if (callback != null) {
callback.sendResult(null);
}
mIMirrorWindowCallback.onWindowMagnifierBoundsChanged(TEST_DISPLAY,
mMirrorWindowFrame);
return null;
}).when(mConnection).enableWindowMagnification(anyInt(),
anyFloat(), anyFloat(), anyFloat());
anyFloat(), anyFloat(), anyFloat(), nullable(RemoteCallback.class));
doAnswer((invocation) -> {
final int displayId = invocation.getArgument(0);
if (displayId != TEST_DISPLAY) {
throw new IllegalArgumentException("only support default display :" + displayId);
}
final RemoteCallback callback = invocation.getArgument(1);
if (callback != null) {
callback.sendResult(null);
}
return null;
}).when(mConnection).disableWindowMagnification(anyInt(), nullable(RemoteCallback.class));
}
private void computeMirrorWindowFrame(float centerX, float centerY) {

View File

@@ -19,6 +19,7 @@ package com.android.server.accessibility.magnification;
import static org.mockito.Mockito.verify;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.provider.Settings;
import android.view.Display;
@@ -43,18 +44,22 @@ public class WindowMagnificationConnectionWrapperTest {
private IWindowMagnificationConnection mConnection;
@Mock
private IWindowMagnificationConnectionCallback mCallback;
@Mock
private RemoteCallback.OnResultListener mOnResultListener;
private RemoteCallback mRemoteCallback;
private WindowMagnificationConnectionWrapper mConnectionWrapper;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection);
mRemoteCallback = new RemoteCallback(mOnResultListener);
}
@Test
public void enableWindowMagnification() throws RemoteException {
mConnectionWrapper.enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f);
verify(mConnection).enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f);
mConnectionWrapper.enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f, mRemoteCallback);
verify(mConnection).enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f, mRemoteCallback);
}
@Test
@@ -65,8 +70,8 @@ public class WindowMagnificationConnectionWrapperTest {
@Test
public void disableWindowMagnification() throws RemoteException {
mConnectionWrapper.disableWindowMagnification(TEST_DISPLAY);
verify(mConnection).disableWindowMagnification(TEST_DISPLAY);
mConnectionWrapper.disableWindowMagnification(TEST_DISPLAY, mRemoteCallback);
verify(mConnection).disableWindowMagnification(TEST_DISPLAY, mRemoteCallback);
}
@Test

View File

@@ -72,6 +72,8 @@ public class WindowMagnificationManagerTest {
private Context mContext;
@Mock
private StatusBarManagerInternal mMockStatusBarManagerInternal;
@Mock
private Runnable mEndCallback;
private MockContentResolver mResolver;
private WindowMagnificationManager mWindowMagnificationManager;
@@ -160,29 +162,50 @@ public class WindowMagnificationManagerTest {
}
@Test
public void enable_TestDisplay_enableWindowMagnification() throws RemoteException {
public void enable_hasConnection_enableWindowMagnification() throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f);
verify(mMockConnection.getConnection()).enableWindowMagnification(TEST_DISPLAY, 2f,
200f, 300f);
200f, 300f, null);
}
@Test
public void disable_testDisplay_disableWindowMagnification() throws RemoteException {
public void enableWithCallback_hasConnection_enableWindowMagnification()
throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f,
mEndCallback);
verify(mEndCallback).run();
}
@Test
public void disable_hasConnectionAndEnabled_disableWindowMagnification()
throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY);
verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
}
@Test
public void isWindowMagnifierEnabled_returnExpectedValue() {
public void disableWithCallback_hasConnectionAndEnabled_disableWindowMagnification() {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false, mEndCallback);
verify(mEndCallback).run();
}
@Test
public void isWindowMagnifierEnabled_hasConnectionAndEnabled_returnExpectedValue() {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
@@ -232,7 +255,7 @@ public class WindowMagnificationManagerTest {
}
@Test
public void moveWindowMagnifier() throws RemoteException {
public void moveWindowMagnifier_enabled_invokeConnectionMethod() throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
@@ -241,7 +264,8 @@ public class WindowMagnificationManagerTest {
}
@Test
public void showMagnificationButton() throws RemoteException {
public void showMagnificationButton_hasConnection_invokeConnectionMethod()
throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.showMagnificationButton(TEST_DISPLAY,
@@ -254,7 +278,7 @@ public class WindowMagnificationManagerTest {
}
@Test
public void pointersInWindow_returnCorrectValue() throws RemoteException {
public void pointersInWindow_magnifierEnabled_returnCorrectValue() throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(TEST_DISPLAY,
@@ -286,7 +310,7 @@ public class WindowMagnificationManagerTest {
assertTrue(mWindowMagnificationManager.requestConnection(false));
verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY);
verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(false);
}
@@ -324,7 +348,7 @@ public class WindowMagnificationManagerTest {
new Intent(Intent.ACTION_SCREEN_OFF));
verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY);
verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
}