Merge "feat(#AlwaysOnMagnifier)!: Supports magnification zooming to 100% [2/2]"

This commit is contained in:
Roy Chou
2023-01-31 01:32:30 +00:00
committed by Android (Google) Code Review
8 changed files with 212 additions and 44 deletions

View File

@@ -50,9 +50,9 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
@interface MagnificationState {}
// The window magnification is disabled.
private static final int STATE_DISABLED = 0;
@VisibleForTesting static final int STATE_DISABLED = 0;
// The window magnification is enabled.
private static final int STATE_ENABLED = 1;
@VisibleForTesting static final int STATE_ENABLED = 1;
// 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.
@@ -151,7 +151,7 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
}
mController.enableWindowMagnificationInternal(scale, centerX, centerY,
mMagnificationFrameOffsetRatioX, mMagnificationFrameOffsetRatioY);
setState(STATE_ENABLED);
updateState();
return;
}
mAnimationCallback = animationCallback;
@@ -165,7 +165,7 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
mValueAnimator.cancel();
}
sendAnimationCallback(true);
setState(STATE_ENABLED);
updateState();
} else {
if (mState == STATE_DISABLING) {
mValueAnimator.reverse();
@@ -254,7 +254,7 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
mValueAnimator.cancel();
}
mController.deleteWindowMagnification();
setState(STATE_DISABLED);
updateState();
return;
}
@@ -272,6 +272,14 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
setState(STATE_DISABLING);
}
private void updateState() {
if (Float.isNaN(mController.getScale())) {
setState(STATE_DISABLED);
} else {
setState(STATE_ENABLED);
}
}
private void setState(@MagnificationState int state) {
if (DEBUG) {
Log.d(TAG, "setState from " + mState + " to " + state);
@@ -279,6 +287,11 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
mState = state;
}
@VisibleForTesting
@MagnificationState int getState() {
return mState;
}
@Override
public void onAnimationStart(Animator animation) {
mEndAnimationCanceled = false;
@@ -289,11 +302,10 @@ class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUp
if (mEndAnimationCanceled || mController == null) {
return;
}
if (Float.isNaN(mController.getScale())) {
setState(STATE_DISABLED);
} else {
setState(STATE_ENABLED);
if (mState == STATE_DISABLING) {
mController.deleteWindowMagnification();
}
updateState();
sendAnimationCallback(true);
// We reset the duration to config_longAnimTime
mValueAnimator.setDuration(mContext.getResources()

View File

@@ -97,7 +97,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
// Delay to avoid updating state description too frequently.
private static final int UPDATE_STATE_DESCRIPTION_DELAY_MS = 100;
// It should be consistent with the value defined in WindowMagnificationGestureHandler.
private static final Range<Float> A11Y_ACTION_SCALE_RANGE = new Range<>(2.0f, 8.0f);
private static final Range<Float> A11Y_ACTION_SCALE_RANGE = new Range<>(1.0f, 8.0f);
private static final float A11Y_CHANGE_SCALE_DIFFERENCE = 1.0f;
private static final float ANIMATION_BOUNCE_EFFECT_SCALE = 1.05f;
private static final float[] MAGNIFICATION_SCALE_OPTIONS = {1.0f, 1.4f, 1.8f, 2.5f};
@@ -213,7 +213,9 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
private static final int MAX_HORIZONTAL_MOVE_ANGLE = 50;
private static final int HORIZONTAL = 1;
private static final int VERTICAL = 0;
private static final double HORIZONTAL_LOCK_BASE =
@VisibleForTesting
static final double HORIZONTAL_LOCK_BASE =
Math.tan(Math.toRadians(MAX_HORIZONTAL_MOVE_ANGLE));
private boolean mAllowDiagonalScrolling = false;
@@ -704,6 +706,11 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
}
}
@VisibleForTesting
WindowMagnificationSettings getMagnificationSettings() {
return mWindowMagnificationSettings;
}
/**
* Sets the window size with given width and height in pixels without changing the
* window center. The width or the height will be clamped in the range
@@ -1075,7 +1082,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
/**
* Enables window magnification with specified parameters. If the given scale is <strong>less
* than or equal to 1.0f<strong>, then
* than 1.0f<strong>, then
* {@link WindowMagnificationController#deleteWindowMagnification()} will be called instead to
* be consistent with the behavior of display magnification.
*
@@ -1093,7 +1100,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
*/
void enableWindowMagnificationInternal(float scale, float centerX, float centerY,
float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY) {
if (Float.compare(scale, 1.0f) <= 0) {
if (Float.compare(scale, 1.0f) < 0) {
deleteWindowMagnification();
return;
}

View File

@@ -92,7 +92,7 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest
private ImageButton mChangeModeButton;
private boolean mAllowDiagonalScrolling = false;
private static final float A11Y_CHANGE_SCALE_DIFFERENCE = 1.0f;
private static final float A11Y_SCALE_MIN_VALUE = 2.0f;
private static final float A11Y_SCALE_MIN_VALUE = 1.0f;
private WindowMagnificationSettingsCallback mCallback;
@Retention(RetentionPolicy.SOURCE)
@@ -136,9 +136,12 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
float scale = progress * A11Y_CHANGE_SCALE_DIFFERENCE + A11Y_SCALE_MIN_VALUE;
mSecureSettings.putFloatForUser(
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, scale,
UserHandle.USER_CURRENT);
// update persisted scale only when scale >= 2.0
if (scale >= 2.0f) {
Settings.Secure.putFloatForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, scale,
UserHandle.USER_CURRENT);
}
mCallback.onMagnifierScale(scale);
}
@@ -516,12 +519,16 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest
boolean enabled = mSecureSettings.getIntForUser(
Settings.Secure.ACCESSIBILITY_ALLOW_DIAGONAL_SCROLLING, 0,
UserHandle.USER_CURRENT) == 1;
setDiagonalScrolling(!enabled);
}
mSecureSettings.putIntForUser(
Settings.Secure.ACCESSIBILITY_ALLOW_DIAGONAL_SCROLLING, enabled ? 0 : 1,
@VisibleForTesting
void setDiagonalScrolling(boolean enabled) {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_ALLOW_DIAGONAL_SCROLLING, enabled ? 1 : 0,
UserHandle.USER_CURRENT);
mCallback.onSetDiagonalScrolling(!enabled);
mCallback.onSetDiagonalScrolling(enabled);
}
private void setEditMagnifierSizeMode(boolean enable) {

View File

@@ -251,10 +251,51 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
throws RemoteException {
enableWindowMagnificationWithoutAnimation();
final float targetScale = 1.0f;
final float targetCenterX = DEFAULT_CENTER_X + 100;
final float targetCenterY = DEFAULT_CENTER_Y + 100;
mInstrumentation.runOnMainSync(() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(1.0f,
DEFAULT_CENTER_X + 100, DEFAULT_CENTER_Y + 100, mAnimationCallback);
mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
targetCenterX, targetCenterY, mAnimationCallback);
mCurrentScale.set(mController.getScale());
mCurrentCenterX.set(mController.getCenterX());
mCurrentCenterY.set(mController.getCenterY());
});
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture(),
mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
verifyStartValue(mScaleCaptor, mCurrentScale.get());
verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
verifyStartValue(mOffsetXCaptor, 0f);
verifyStartValue(mOffsetYCaptor, 0f);
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
verify(mAnimationCallback).onResult(true);
assertEquals(WindowMagnificationAnimationController.STATE_ENABLED,
mWindowMagnificationAnimationController.getState());
}
@Test
public void enableWindowMagnificationWithScaleLessThanOne_enabled_AnimationAndInvokeCallback()
throws RemoteException {
enableWindowMagnificationWithoutAnimation();
final float targetScale = 0.99f;
final float targetCenterX = DEFAULT_CENTER_X + 100;
final float targetCenterY = DEFAULT_CENTER_Y + 100;
mInstrumentation.runOnMainSync(() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
targetCenterX, targetCenterY, mAnimationCallback);
mCurrentScale.set(mController.getScale());
mCurrentCenterX.set(mController.getCenterX());
mCurrentCenterY.set(mController.getCenterY());
@@ -275,6 +316,29 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
verify(mAnimationCallback).onResult(true);
assertEquals(WindowMagnificationAnimationController.STATE_DISABLED,
mWindowMagnificationAnimationController.getState());
}
@Test
public void
enableWindowMagnificationWithScaleLessThanOneAndWithoutCallBack_enabled_expectedValues()
throws RemoteException {
enableWindowMagnificationWithoutAnimation();
final float targetScale = 0.99f;
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(Float.NaN, Float.NaN, Float.NaN);
assertEquals(WindowMagnificationAnimationController.STATE_DISABLED,
mWindowMagnificationAnimationController.getState());
}
@Test
@@ -684,14 +748,54 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
}
@Test
public void moveWindowMagnifier_enabled() {
public void moveWindowMagnifier_enabled_vertical_only_expectedValue() {
enableWindowMagnificationWithoutAnimation();
// should move vertically since offsetY/offsetX > HORIZONTAL_LOCK_BASE
final float offsetX = 50.0f;
final float offsetY =
(float) Math.ceil(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE)
+ 1.0f;
mInstrumentation.runOnMainSync(
() -> mController.moveWindowMagnifier(100f, 200f));
() -> mController.moveWindowMagnifier(offsetX, offsetY));
verify(mSpyController).moveWindowMagnifier(100f, 200f);
verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + 100f, DEFAULT_CENTER_Y + 100f);
verify(mSpyController).moveWindowMagnifier(offsetX, offsetY);
verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y + offsetY);
}
@Test
public void moveWindowMagnifier_enabled_horinzontal_only_expectedValue() {
enableWindowMagnificationWithoutAnimation();
// should move vertically since offsetY/offsetX <= HORIZONTAL_LOCK_BASE
final float offsetX = 50.0f;
final float offsetY =
(float) Math.floor(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE)
- 1.0f;
mInstrumentation.runOnMainSync(
() -> mController.moveWindowMagnifier(offsetX, offsetY));
verify(mSpyController).moveWindowMagnifier(offsetX, offsetY);
verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + offsetX, DEFAULT_CENTER_Y);
}
@Test
public void moveWindowMagnifier_enabled_setDiagonalEnabled_expectedValues() {
enableWindowMagnificationWithoutAnimation();
final float offsetX = 50.0f;
final float offsetY =
(float) Math.ceil(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE);
// while diagonal scrolling enabled,
// should move with both offsetX and offsetY without regrading offsetY/offsetX
mInstrumentation.runOnMainSync(
() -> {
mController.getMagnificationSettings().setDiagonalScrolling(true);
mController.moveWindowMagnifier(offsetX, offsetY);
});
verify(mSpyController).moveWindowMagnifier(offsetX, offsetY);
verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + offsetX, DEFAULT_CENTER_Y + offsetY);
}
@Test
@@ -807,7 +911,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
@Override
void moveWindowMagnifier(float offsetX, float offsetY) {
super.moveWindowMagnifier(offsetX, offsetX);
super.moveWindowMagnifier(offsetX, offsetY);
mSpyController.moveWindowMagnifier(offsetX, offsetY);
}

View File

@@ -618,18 +618,18 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
public void performA11yActions_visible_expectedResults() {
final int displayId = mContext.getDisplayId();
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnificationInternal(2.5f, Float.NaN,
mWindowMagnificationController.enableWindowMagnificationInternal(1.5f, Float.NaN,
Float.NaN);
});
final View mirrorView = mWindowManager.getAttachedView();
assertTrue(
mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_out, null));
// Minimum scale is 2.0.
verify(mWindowMagnifierCallback).onPerformScaleAction(eq(displayId), eq(2.0f));
// Minimum scale is 1.0.
verify(mWindowMagnifierCallback).onPerformScaleAction(eq(displayId), eq(1.0f));
assertTrue(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_in, null));
verify(mWindowMagnifierCallback).onPerformScaleAction(eq(displayId), eq(3.5f));
verify(mWindowMagnifierCallback).onPerformScaleAction(eq(displayId), eq(2.5f));
// TODO: Verify the final state when the mirror surface is visible.
assertTrue(mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null));

View File

@@ -71,7 +71,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
private static final boolean DEBUG_DETECTING = false | DEBUG_ALL;
//Ensure the range has consistency with FullScreenMagnificationGestureHandler.
private static final float MIN_SCALE = 2.0f;
private static final float MIN_SCALE = 1.0f;
private static final float MAX_SCALE = MagnificationScaleProvider.MAX_SCALE;
private final WindowMagnificationManager mWindowMagnificationMgr;
@@ -222,6 +222,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
if (DEBUG_DETECTING) {
Slog.i(mLogTag, "onTripleTapAndHold()");
}
mViewportDraggingState.mEnabledBeforeDrag =
mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId);
enableWindowMagnifier(up.getX(), up.getY(),
WindowMagnificationManager.WINDOW_POSITION_AT_TOP_LEFT);
mTripleTapAndHoldStartedTime = SystemClock.uptimeMillis();
@@ -230,7 +232,9 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
@VisibleForTesting
void releaseTripleTapAndHold() {
mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true);
if (!mViewportDraggingState.mEnabledBeforeDrag) {
mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true);
}
transitionTo(mDetectingState);
if (mTripleTapAndHoldStartedTime != 0) {
final long duration = SystemClock.uptimeMillis() - mTripleTapAndHoldStartedTime;
@@ -377,6 +381,9 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
*/
final class ViewportDraggingState implements State {
/** Whether to disable zoom after dragging ends */
boolean mEnabledBeforeDrag;
private float mLastX = Float.NaN;
private float mLastY = Float.NaN;

View File

@@ -37,6 +37,7 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.MathUtils;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -715,18 +716,21 @@ public class WindowMagnificationManager implements
* scale if none is available
*/
float getPersistedScale(int displayId) {
return mScaleProvider.getScale(displayId);
return MathUtils.constrain(mScaleProvider.getScale(displayId),
2.0f, MagnificationScaleProvider.MAX_SCALE);
}
/**
* Persists the default display magnification scale to the current user's settings. Only the
* Persists the default display magnification scale to the current user's settings
* <strong>if scale is >= 2.0</strong>. Only the
* value of the default display is persisted in user's settings.
*/
void persistScale(int displayId) {
float scale = getScale(displayId);
if (scale != 1.0f) {
mScaleProvider.putScale(scale, displayId);
if (scale < 2.0f) {
return;
}
mScaleProvider.putScale(scale, displayId);
}
/**

View File

@@ -63,11 +63,12 @@ public class WindowMagnificationGestureHandlerTest {
public static final int STATE_SHOW_MAGNIFIER_SHORTCUT = 2;
public static final int STATE_TWO_FINGERS_DOWN = 3;
public static final int STATE_SHOW_MAGNIFIER_TRIPLE_TAP = 4;
public static final int STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD = 5;
public static final int STATE_NOT_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD = 5;
public static final int STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD = 6;
//TODO: Test it after can injecting Handler to GestureMatcher is available.
public static final int FIRST_STATE = STATE_IDLE;
public static final int LAST_STATE = STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD;
public static final int LAST_STATE = STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD;
// Co-prime x and y, to potentially catch x-y-swapped errors
public static final float DEFAULT_TAP_X = 301;
@@ -164,15 +165,27 @@ public class WindowMagnificationGestureHandlerTest {
@Test
public void testTripleTapAndHold_logSessionDuration() {
// perform triple tap on spy gesture handler
goFromStateIdleTo(STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD);
goFromStateIdleTo(STATE_NOT_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD);
// perform up event on spy gesture handler
returnToNormalFrom(STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD);
returnToNormalFrom(STATE_NOT_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD);
verify(mMockWindowMagnificationGestureHandler)
.logMagnificationTripleTapAndHoldSession(anyLong());
}
@Test
public void testTripleTapAndHold_enabled_releaseHold_expectedValue() {
goFromStateIdleTo(STATE_SHOW_MAGNIFIER_TRIPLE_TAP);
tap();
tap();
tapAndHold();
send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
assertIn(STATE_SHOW_MAGNIFIER_TRIPLE_TAP);
returnToNormalFrom(STATE_SHOW_MAGNIFIER_TRIPLE_TAP);
}
private void forEachState(IntConsumer action) {
for (int state = FIRST_STATE; state <= LAST_STATE; state++) {
action.accept(state);
@@ -199,7 +212,8 @@ public class WindowMagnificationGestureHandlerTest {
== mWindowMagnificationGestureHandler.mDetectingState, state);
}
break;
case STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD:
case STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
check(isWindowMagnifierEnabled(DISPLAY_0), state);
check(mWindowMagnificationGestureHandler.mCurrentState
== mWindowMagnificationGestureHandler.mViewportDraggingState, state);
@@ -256,13 +270,21 @@ public class WindowMagnificationGestureHandlerTest {
tap();
}
break;
case STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
// Perform triple tap and hold gesture
tap();
tap();
tapAndHold();
}
break;
case STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
// enabled then perform triple tap and hold gesture
goFromStateIdleTo(STATE_SHOW_MAGNIFIER_SHORTCUT);
tap();
tap();
tapAndHold();
}
break;
default:
throw new IllegalArgumentException("Illegal state: " + state);
}
@@ -296,10 +318,15 @@ public class WindowMagnificationGestureHandlerTest {
tap();
}
break;
case STATE_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
}
break;
case STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
returnToNormalFrom(STATE_SHOW_MAGNIFIER_SHORTCUT);
}
break;
default:
throw new IllegalArgumentException("Illegal state: " + state);
}