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

There are 2 parts in this feature.
1. support fullscreen magnifier zooming to 100%.
2. support window magnifier zooming to 100%.

This CL is for the 1st part.

We change the minimum magnifiable scale to 100% in fullscreen magnifier, and prevent persistedScale from being 100%.

We adjust the flag mMagnificationActivated's usage and add new function isActivated(). Originally, The services check isMagnifying() to identify the fullscreen magnifier is activated. If the magnifier zooms to 100%, the magnifier state would become activated but not magnifying. Accordingly, the services cannot identify whether the magnifier is activated when zooming to 100%. Therefore, instead of isMagnifying(), the services change to check fullscreen magnifier is activated by isActivated().

We support pinching to zoom gesture after tapping on a11y shortcut to activate fullscreen magnifier. Originally, we can use pinching gesture only when we additionally perform one more tap in advance.

Besides, when magnifier is already activated, we support further zoom in temporary by triple tap and hold gesture. Originally, the gesture only changes the state to the viewport dragging mode.

Constrain the scale range in persisted scale getter/setter to >= 2.0 to prevent the presisted scale from being 100%. It results in zooming in with persisted scale would make context no changes.

Bug: 146504199, 242739712
Test: manually
      atest FullScreenMagnificationControllerTest
      atest magnification/FullScreenMagnificationGestureHandlerTest
      atest cts/FullScreenMagnificationGestureHandlerTest
      atest MagnificationControllerTest
      atest AccessibilityManagerServiceTest
      atest AccessibilityMagnificationTest
Change-Id: I3b0b119e9833eddb9a13681e4631b3ff47c6ce1e
This commit is contained in:
Roy Chou
2022-12-19 14:13:34 +00:00
parent f8ce15fa3b
commit 231437621c
9 changed files with 247 additions and 199 deletions

View File

@@ -3228,7 +3228,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (targetName.equals(MAGNIFICATION_CONTROLLER_NAME)) {
final boolean enabled =
!getMagnificationController().getFullScreenMagnificationController()
.isMagnifying(displayId);
.isActivated(displayId);
logAccessibilityShortcutActivated(mContext, MAGNIFICATION_COMPONENT_NAME, shortcutType,
enabled);
sendAccessibilityButtonToInputFilter(displayId);

View File

@@ -126,8 +126,6 @@ public class FullScreenMagnificationController implements
private boolean mUnregisterPending;
private boolean mDeleteAfterUnregister;
private boolean mForceShowMagnifiableBounds;
private final int mDisplayId;
private int mIdOfLastServiceToMagnify = INVALID_SERVICE_ID;
@@ -213,8 +211,8 @@ public class FullScreenMagnificationController implements
return mRegistered;
}
boolean isMagnifying() {
return mCurrentMagnificationSpec.scale > 1.0f;
boolean isActivated() {
return mMagnificationActivated;
}
float getScale() {
@@ -370,12 +368,6 @@ public class FullScreenMagnificationController implements
@GuardedBy("mLock")
void onMagnificationChangedLocked() {
final float scale = getScale();
final boolean lastMagnificationActivated = mMagnificationActivated;
mMagnificationActivated = scale > 1.0f;
if (mMagnificationActivated != lastMagnificationActivated) {
mMagnificationInfoChangedCallback.onFullScreenMagnificationActivationState(
mDisplayId, mMagnificationActivated);
}
final MagnificationConfig config = new MagnificationConfig.Builder()
.setMode(MAGNIFICATION_MODE_FULLSCREEN)
@@ -384,7 +376,7 @@ public class FullScreenMagnificationController implements
.setCenterY(getCenterY()).build();
mMagnificationInfoChangedCallback.onFullScreenMagnificationChanged(mDisplayId,
mMagnificationRegion, config);
if (mUnregisterPending && !isMagnifying()) {
if (mUnregisterPending && !isActivated()) {
unregister(mDeleteAfterUnregister);
}
}
@@ -476,21 +468,22 @@ public class FullScreenMagnificationController implements
}
@GuardedBy("mLock")
void setForceShowMagnifiableBounds(boolean show) {
if (mRegistered) {
mForceShowMagnifiableBounds = show;
if (traceEnabled()) {
logTrace("setForceShowMagnifiableBounds",
"displayID=" + mDisplayId + ";show=" + show);
}
mControllerCtx.getWindowManager().setForceShowMagnifiableBounds(
mDisplayId, show);
private boolean setActivated(boolean activated) {
if (DEBUG) {
Slog.i(LOG_TAG, "setActivated(activated = " + activated + ")");
}
}
@GuardedBy("mLock")
boolean isForceShowMagnifiableBounds() {
return mRegistered && mForceShowMagnifiableBounds;
final boolean changed = (mMagnificationActivated != activated);
if (changed) {
mMagnificationActivated = activated;
mMagnificationInfoChangedCallback.onFullScreenMagnificationActivationState(
mDisplayId, mMagnificationActivated);
mControllerCtx.getWindowManager().setForceShowMagnifiableBounds(
mDisplayId, activated);
}
return changed;
}
@GuardedBy("mLock")
@@ -504,13 +497,13 @@ public class FullScreenMagnificationController implements
return false;
}
final MagnificationSpec spec = mCurrentMagnificationSpec;
final boolean changed = !spec.isNop();
final boolean changed = isActivated();
setActivated(false);
if (changed) {
spec.clear();
onMagnificationChangedLocked();
}
mIdOfLastServiceToMagnify = INVALID_SERVICE_ID;
mForceShowMagnifiableBounds = false;
sendSpecToAnimation(spec, animationCallback);
return changed;
}
@@ -554,9 +547,10 @@ public class FullScreenMagnificationController implements
+ ", centerY = " + centerY + ", endCallback = "
+ animationCallback + ", id = " + id + ")");
}
final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY);
boolean changed = setActivated(true);
changed |= updateMagnificationSpecLocked(scale, centerX, centerY);
sendSpecToAnimation(mCurrentMagnificationSpec, animationCallback);
if (isMagnifying() && (id != INVALID_SERVICE_ID)) {
if (isActivated() && (id != INVALID_SERVICE_ID)) {
mIdOfLastServiceToMagnify = id;
mMagnificationInfoChangedCallback.onRequestMagnificationSpec(mDisplayId,
mIdOfLastServiceToMagnify);
@@ -779,7 +773,7 @@ public class FullScreenMagnificationController implements
if (display == null) {
return;
}
if (!display.isMagnifying()) {
if (!display.isActivated()) {
return;
}
final Rect magnifiedRegionBounds = mTempRect;
@@ -831,16 +825,16 @@ public class FullScreenMagnificationController implements
/**
* @param displayId The logical display id.
* @return {@code true} if magnification is active, e.g. the scale
* is > 1, {@code false} otherwise
* @return {@code true} if magnification is activated,
* {@code false} otherwise
*/
public boolean isMagnifying(int displayId) {
public boolean isActivated(int displayId) {
synchronized (mLock) {
final DisplayMagnification display = mDisplays.get(displayId);
if (display == null) {
return false;
}
return display.isMagnifying();
return display.isActivated();
}
}
@@ -1166,6 +1160,9 @@ public class FullScreenMagnificationController implements
*/
public void persistScale(int displayId) {
final float scale = getScale(Display.DEFAULT_DISPLAY);
if (scale < 2.0f) {
return;
}
mScaleProvider.putScale(scale, displayId);
}
@@ -1177,7 +1174,8 @@ public class FullScreenMagnificationController implements
* scale if none is available
*/
public float getPersistedScale(int displayId) {
return mScaleProvider.getScale(displayId);
return MathUtils.constrain(mScaleProvider.getScale(displayId),
2.0f, MagnificationScaleProvider.MAX_SCALE);
}
/**
@@ -1198,12 +1196,12 @@ public class FullScreenMagnificationController implements
*
* @param displayId The logical display id.
* @param animate whether the animate the transition
* @return whether was {@link #isMagnifying(int) magnifying}
* @return whether was {@link #isActivated(int)} activated}
*/
boolean resetIfNeeded(int displayId, boolean animate) {
synchronized (mLock) {
final DisplayMagnification display = mDisplays.get(displayId);
if (display == null || !display.isMagnifying()) {
if (display == null || !display.isActivated()) {
return false;
}
display.reset(animate);
@@ -1221,7 +1219,7 @@ public class FullScreenMagnificationController implements
boolean resetIfNeeded(int displayId, int connectionId) {
synchronized (mLock) {
final DisplayMagnification display = mDisplays.get(displayId);
if (display == null || !display.isMagnifying()
if (display == null || !display.isActivated()
|| connectionId != display.getIdOfLastServiceToMagnify()) {
return false;
}
@@ -1230,16 +1228,6 @@ public class FullScreenMagnificationController implements
}
}
void setForceShowMagnifiableBounds(int displayId, boolean show) {
synchronized (mLock) {
final DisplayMagnification display = mDisplays.get(displayId);
if (display == null) {
return;
}
display.setForceShowMagnifiableBounds(show);
}
}
/**
* Notifies that the IME window visibility changed.
*
@@ -1251,21 +1239,6 @@ public class FullScreenMagnificationController implements
mMagnificationInfoChangedCallback.onImeWindowVisibilityChanged(displayId, shown);
}
/**
* Returns {@code true} if the magnifiable regions of the display is forced to be shown.
*
* @param displayId The logical display id.
*/
public boolean isForceShowMagnifiableBounds(int displayId) {
synchronized (mLock) {
final DisplayMagnification display = mDisplays.get(displayId);
if (display == null) {
return false;
}
return display.isForceShowMagnifiableBounds();
}
}
private void onScreenTurnedOff() {
final Message m = PooledLambda.obtainMessage(
FullScreenMagnificationController::resetAllIfNeeded, this, false);
@@ -1295,7 +1268,7 @@ public class FullScreenMagnificationController implements
}
return;
}
if (!display.isMagnifying()) {
if (!display.isActivated()) {
display.unregister(delete);
} else {
display.unregisterPending(delete);

View File

@@ -122,7 +122,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
// The MIN_SCALE is different from MagnificationScaleProvider.MIN_SCALE due
// to AccessibilityService.MagnificationController#setScale() has
// different scale range
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;
@VisibleForTesting final FullScreenMagnificationController mFullScreenMagnificationController;
@@ -220,14 +220,19 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
@Override
public void handleShortcutTriggered() {
boolean wasMagnifying = mFullScreenMagnificationController.resetIfNeeded(mDisplayId,
/* animate */ true);
if (wasMagnifying) {
final boolean isActivated = mFullScreenMagnificationController.isActivated(mDisplayId);
if (isActivated) {
zoomOff();
clearAndTransitionToStateDetecting();
} else {
mPromptController.showNotificationIfNeeded();
mDetectingState.toggleShortcutTriggered();
}
if (mDetectingState.isShortcutTriggered()) {
mPromptController.showNotificationIfNeeded();
zoomToScale(1.0f, Float.NaN, Float.NaN);
}
}
@Override
@@ -441,7 +446,12 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
final class ViewportDraggingState implements State {
/** Whether to disable zoom after dragging ends */
boolean mZoomedInBeforeDrag;
@VisibleForTesting boolean mActivatedBeforeDrag;
/** Whether to restore scale after dragging ends */
private boolean mZoomedInTemporary;
/** The cached scale for recovering after dragging ends */
private float mScaleBeforeZoomedInTemporary;
private boolean mLastMoveOutsideMagnifiedRegion;
@Override
@@ -474,7 +484,13 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
case ACTION_UP:
case ACTION_CANCEL: {
if (!mZoomedInBeforeDrag) zoomOff();
if (mActivatedBeforeDrag) {
if (mZoomedInTemporary) {
zoomToScale(mScaleBeforeZoomedInTemporary, event.getX(), event.getY());
}
} else {
zoomOff();
}
clear();
transitionTo(mDetectingState);
}
@@ -488,15 +504,27 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
}
public void prepareForZoomInTemporary() {
mViewportDraggingState.mActivatedBeforeDrag =
mFullScreenMagnificationController.isActivated(mDisplayId);
mViewportDraggingState.mZoomedInTemporary = true;
mViewportDraggingState.mScaleBeforeZoomedInTemporary =
mFullScreenMagnificationController.getScale(mDisplayId);
}
@Override
public void clear() {
mLastMoveOutsideMagnifiedRegion = false;
mZoomedInTemporary = false;
mScaleBeforeZoomedInTemporary = 1.0f;
}
@Override
public String toString() {
return "ViewportDraggingState{"
+ "mZoomedInBeforeDrag=" + mZoomedInBeforeDrag
+ "mActivatedBeforeDrag=" + mActivatedBeforeDrag
+ ", mLastMoveOutsideMagnifiedRegion=" + mLastMoveOutsideMagnifiedRegion
+ '}';
}
@@ -625,10 +653,10 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
transitionToDelegatingStateAndClear();
} else if (mDetectTripleTap
// If magnified, delay an ACTION_DOWN for mMultiTapMaxDelay
// If activated, delay an ACTION_DOWN for mMultiTapMaxDelay
// to ensure reachability of
// STATE_PANNING_SCALING(triggerable with ACTION_POINTER_DOWN)
|| mFullScreenMagnificationController.isMagnifying(mDisplayId)) {
|| isActivated()) {
afterMultiTapTimeoutTransitionToDelegatingState();
@@ -640,8 +668,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
break;
case ACTION_POINTER_DOWN: {
if (mFullScreenMagnificationController.isMagnifying(mDisplayId)
&& event.getPointerCount() == 2) {
if (isActivated() && event.getPointerCount() == 2) {
storeSecondPointerDownLocation(event);
mHandler.sendEmptyMessageDelayed(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
ViewConfiguration.getTapTimeout());
@@ -665,13 +692,13 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
// (which is a rare combo to be used aside from magnification)
if (isMultiTapTriggered(2 /* taps */) && event.getPointerCount() == 1) {
transitionToViewportDraggingStateAndClear(event);
} else if (isMagnifying() && event.getPointerCount() == 2) {
} else if (isActivated() && event.getPointerCount() == 2) {
//Primary pointer is swiping, so transit to PanningScalingState
transitToPanningScalingStateAndClear();
} else {
transitionToDelegatingStateAndClear();
}
} else if (isMagnifying() && secondPointerDownValid()
} else if (isActivated() && secondPointerDownValid()
&& distanceClosestPointerToPoint(
mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance) {
//Second pointer is swiping, so transit to PanningScalingState
@@ -734,7 +761,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
// Only log the triple tap event, use numTaps to filter.
if (multitapTriggered && numTaps > 2) {
final boolean enabled = mFullScreenMagnificationController.isMagnifying(mDisplayId);
final boolean enabled = isActivated();
logMagnificationTripleTap(enabled);
}
return multitapTriggered;
@@ -862,24 +889,33 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
}
/**
* This method could be triggered by both 2 cases.
* 1. direct three tap gesture
* 2. one tap while shortcut triggered (it counts as two taps).
*/
private void onTripleTap(MotionEvent up) {
if (DEBUG_DETECTING) {
Slog.i(mLogTag, "onTripleTap(); delayed: "
+ MotionEventInfo.toString(mDelayedEventQueue));
}
clear();
// Toggle zoom
if (mFullScreenMagnificationController.isMagnifying(mDisplayId)) {
zoomOff();
} else {
// We put mShortcutTriggered into conditions.
// The reason is when the shortcut is triggered,
// the magnifier is activated and keeps in scale 1.0,
// and in this case, we still want to zoom on the magnifier.
if (!isActivated() || mShortcutTriggered) {
mPromptController.showNotificationIfNeeded();
zoomOn(up.getX(), up.getY());
} else {
zoomOff();
}
clear();
}
private boolean isMagnifying() {
return mFullScreenMagnificationController.isMagnifying(mDisplayId);
private boolean isActivated() {
return mFullScreenMagnificationController.isActivated(mDisplayId);
}
void transitionToViewportDraggingStateAndClear(MotionEvent down) {
@@ -887,14 +923,13 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
if (DEBUG_DETECTING) Slog.i(mLogTag, "onTripleTapAndHold()");
clear();
mViewportDraggingState.mZoomedInBeforeDrag =
mFullScreenMagnificationController.isMagnifying(mDisplayId);
// Triple tap and hold also belongs to triple tap event.
final boolean enabled = !mViewportDraggingState.mZoomedInBeforeDrag;
final boolean enabled = !isActivated();
logMagnificationTripleTap(enabled);
zoomOn(down.getX(), down.getY());
mViewportDraggingState.prepareForZoomInTemporary();
zoomInTemporary(down.getX(), down.getY());
transitionTo(mViewportDraggingState);
}
@@ -919,7 +954,10 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
if (DEBUG_DETECTING) Slog.i(mLogTag, "setShortcutTriggered(" + state + ")");
mShortcutTriggered = state;
mFullScreenMagnificationController.setForceShowMagnifiableBounds(mDisplayId, state);
}
private boolean isShortcutTriggered() {
return mShortcutTriggered;
}
/**
@@ -948,12 +986,29 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
}
private void zoomInTemporary(float centerX, float centerY) {
final float currentScale = mFullScreenMagnificationController.getScale(mDisplayId);
final float persistedScale = MathUtils.constrain(
mFullScreenMagnificationController.getPersistedScale(mDisplayId),
MIN_SCALE, MAX_SCALE);
final float scale = MathUtils.constrain(Math.max(currentScale + 1.0f, persistedScale),
MIN_SCALE, MAX_SCALE);
zoomToScale(scale, centerX, centerY);
}
private void zoomOn(float centerX, float centerY) {
if (DEBUG_DETECTING) Slog.i(mLogTag, "zoomOn(" + centerX + ", " + centerY + ")");
final float scale = MathUtils.constrain(
mFullScreenMagnificationController.getPersistedScale(mDisplayId),
MIN_SCALE, MAX_SCALE);
zoomToScale(scale, centerX, centerY);
}
private void zoomToScale(float scale, float centerX, float centerY) {
scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
mFullScreenMagnificationController.setScaleAndCenter(mDisplayId,
scale, centerX, centerY,
/* animate */ true,

View File

@@ -254,13 +254,15 @@ public class MagnificationController implements WindowMagnificationManager.Callb
final DisableMagnificationCallback animationEndCallback =
new DisableMagnificationCallback(transitionCallBack, displayId, targetMode,
scale, currentCenter, true);
setDisableMagnificationCallbackLocked(displayId, animationEndCallback);
if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
screenMagnificationController.reset(displayId, animationEndCallback);
} else {
windowMagnificationMgr.disableWindowMagnification(displayId, false,
animationEndCallback);
}
setDisableMagnificationCallbackLocked(displayId, animationEndCallback);
}
/**
@@ -481,17 +483,17 @@ public class MagnificationController implements WindowMagnificationManager.Callb
*/
private boolean shouldNotifyMagnificationChange(int displayId, int changeMode) {
synchronized (mLock) {
final boolean fullScreenMagnifying = mFullScreenMagnificationController != null
&& mFullScreenMagnificationController.isMagnifying(displayId);
final boolean fullScreenActivated = mFullScreenMagnificationController != null
&& mFullScreenMagnificationController.isActivated(displayId);
final boolean windowEnabled = mWindowMagnificationMgr != null
&& mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId);
final Integer transitionMode = mTransitionModes.get(displayId);
if (((changeMode == MAGNIFICATION_MODE_FULLSCREEN && fullScreenMagnifying)
if (((changeMode == MAGNIFICATION_MODE_FULLSCREEN && fullScreenActivated)
|| (changeMode == MAGNIFICATION_MODE_WINDOW && windowEnabled))
&& (transitionMode == null)) {
return true;
}
if ((!fullScreenMagnifying && !windowEnabled)
if ((!fullScreenActivated && !windowEnabled)
&& (transitionMode == null)) {
return true;
}
@@ -742,7 +744,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
mWindowMagnificationMgr.getCenterY(displayId));
} else {
if (mFullScreenMagnificationController == null
|| !mFullScreenMagnificationController.isMagnifying(displayId)) {
|| !mFullScreenMagnificationController.isActivated(displayId)) {
return null;
}
mTempPoint.set(mFullScreenMagnificationController.getCenterX(displayId),
@@ -766,9 +768,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
if (mFullScreenMagnificationController == null) {
return false;
}
isActivated = mFullScreenMagnificationController.isMagnifying(displayId)
|| mFullScreenMagnificationController.isForceShowMagnifiableBounds(
displayId);
isActivated = mFullScreenMagnificationController.isActivated(displayId);
}
} else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
synchronized (mLock) {
@@ -829,7 +829,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
final FullScreenMagnificationController screenMagnificationController =
getFullScreenMagnificationController();
if (mCurrentMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
&& !screenMagnificationController.isMagnifying(mDisplayId)) {
&& !screenMagnificationController.isActivated(mDisplayId)) {
MagnificationConfig.Builder configBuilder =
new MagnificationConfig.Builder();
Region region = new Region();

View File

@@ -313,13 +313,13 @@ public class MagnificationProcessor {
}
/**
* {@link FullScreenMagnificationController#isMagnifying(int)}
* {@link FullScreenMagnificationController#isActivated(int)}
* {@link WindowMagnificationManager#isWindowMagnifierEnabled(int)}
*/
public boolean isMagnifying(int displayId) {
int mode = getControllingMode(displayId);
if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
return mController.getFullScreenMagnificationController().isMagnifying(displayId);
return mController.getFullScreenMagnificationController().isActivated(displayId);
} else if (mode == MAGNIFICATION_MODE_WINDOW) {
return mController.getWindowMagnificationMgr().isWindowMagnifierEnabled(displayId);
}

View File

@@ -891,8 +891,7 @@ final class AccessibilityController {
// to show the border. We will do so when the pending message is handled.
if (!mHandler.hasMessages(
MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
setMagnifiedRegionBorderShown(
isMagnifying() || isForceShowingMagnifiableBounds(), true);
setMagnifiedRegionBorderShown(isForceShowingMagnifiableBounds(), true);
}
}
@@ -1057,7 +1056,7 @@ final class AccessibilityController {
// rotation or folding/unfolding the device. In the rotation case, the screenshot
// used for rotation already has the border. After the rotation is complete
// we will show the border.
if (isMagnifying() || isForceShowingMagnifiableBounds()) {
if (isForceShowingMagnifiableBounds()) {
setMagnifiedRegionBorderShown(false, false);
final long delay = (long) (mLongAnimationDuration
* mService.getWindowAnimationScaleLocked());
@@ -1398,8 +1397,7 @@ final class AccessibilityController {
case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
synchronized (mService.mGlobalLock) {
if (mMagnifedViewport.isMagnifying()
|| isForceShowingMagnifiableBounds()) {
if (isForceShowingMagnifiableBounds()) {
mMagnifedViewport.setMagnifiedRegionBorderShown(true, true);
mService.scheduleAnimationLocked();
}

View File

@@ -214,7 +214,8 @@ public class FullScreenMagnificationControllerTest {
}
private void notRegistered_publicMethodsShouldBeBenign(int displayId) {
assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
checkActivatedAndMagnifyingState(/* activated= */false, /* magnifying= */false, displayId);
assertFalse(
mFullScreenMagnificationController.magnificationRegionContains(displayId, 100,
100));
@@ -646,9 +647,9 @@ public class FullScreenMagnificationControllerTest {
.setScale(displayId, 1.5f, startCenter.x, startCenter.y, false,
SERVICE_ID_2);
assertFalse(mFullScreenMagnificationController.resetIfNeeded(displayId, SERVICE_ID_1));
assertTrue(mFullScreenMagnificationController.isMagnifying(displayId));
checkActivatedAndMagnifyingState(/* activated= */true, /* magnifying= */true, displayId);
assertTrue(mFullScreenMagnificationController.resetIfNeeded(displayId, SERVICE_ID_2));
assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
checkActivatedAndMagnifyingState(/* activated= */false, /* magnifying= */false, displayId);
}
@Test
@@ -667,7 +668,7 @@ public class FullScreenMagnificationControllerTest {
assertTrue(mFullScreenMagnificationController.resetIfNeeded(displayId, false));
verify(mRequestObserver).onFullScreenMagnificationChanged(eq(displayId),
eq(INITIAL_MAGNIFICATION_REGION), any(MagnificationConfig.class));
assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
checkActivatedAndMagnifyingState(/* activated= */false, /* magnifying= */false, displayId);
assertFalse(mFullScreenMagnificationController.resetIfNeeded(displayId, false));
}
@@ -731,7 +732,7 @@ public class FullScreenMagnificationControllerTest {
mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
mStateListener.onAnimationEnd(mMockValueAnimator);
assertFalse(mFullScreenMagnificationController.isMagnifying(DISPLAY_0));
checkActivatedAndMagnifyingState(/* activated= */false, /* magnifying= */false, displayId);
verify(lastAnimationCallback).onResult(true);
}
@@ -749,8 +750,8 @@ public class FullScreenMagnificationControllerTest {
mMessageCapturingHandler.sendAllMessages();
br.onReceive(mMockContext, null);
mMessageCapturingHandler.sendAllMessages();
assertFalse(mFullScreenMagnificationController.isMagnifying(DISPLAY_0));
assertFalse(mFullScreenMagnificationController.isMagnifying(DISPLAY_1));
checkActivatedAndMagnifyingState(/* activated= */false, /* magnifying= */false, DISPLAY_0);
checkActivatedAndMagnifyingState(/* activated= */false, /* magnifying= */false, DISPLAY_1);
}
@Test
@@ -768,7 +769,7 @@ public class FullScreenMagnificationControllerTest {
mMessageCapturingHandler.sendAllMessages();
callbacks.onUserContextChanged();
mMessageCapturingHandler.sendAllMessages();
assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
checkActivatedAndMagnifyingState(/* activated= */false, /* magnifying= */false, displayId);
}
@Test
@@ -784,10 +785,10 @@ public class FullScreenMagnificationControllerTest {
MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
zoomIn2xToMiddle(displayId);
mMessageCapturingHandler.sendAllMessages();
assertTrue(mFullScreenMagnificationController.isMagnifying(displayId));
checkActivatedAndMagnifyingState(/* activated= */true, /* magnifying= */true, displayId);
callbacks.onDisplaySizeChanged();
mMessageCapturingHandler.sendAllMessages();
assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
checkActivatedAndMagnifyingState(/* activated= */false, /* magnifying= */false, DISPLAY_0);
}
@Test
@@ -1133,22 +1134,16 @@ public class FullScreenMagnificationControllerTest {
}
@Test
public void testSetForceShowMagnifiableBounds() {
public void testZoomTo1x_shouldActivatedAndForceShowMagnifiableBounds() {
register(DISPLAY_0);
final float scale = 1.0f;
mFullScreenMagnificationController.setScaleAndCenter(
DISPLAY_0, scale, Float.NaN, Float.NaN, true, SERVICE_ID_1);
mFullScreenMagnificationController.setForceShowMagnifiableBounds(DISPLAY_0, true);
checkActivatedAndMagnifyingState(/* activated= */true, /* magnifying= */false, DISPLAY_0);
verify(mMockWindowManager).setForceShowMagnifiableBounds(DISPLAY_0, true);
}
@Test
public void testIsForceShowMagnifiableBounds() {
register(DISPLAY_0);
mFullScreenMagnificationController.setForceShowMagnifiableBounds(DISPLAY_0, true);
assertTrue(mFullScreenMagnificationController.isForceShowMagnifiableBounds(DISPLAY_0));
}
@Test
public void testSetScale_toMagnifying_shouldNotifyActivatedState() {
setScaleToMagnifying();
@@ -1220,7 +1215,15 @@ public class FullScreenMagnificationControllerTest {
float scale = 2.0f;
mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y,
false, SERVICE_ID_1);
assertTrue(mFullScreenMagnificationController.isMagnifying(displayId));
checkActivatedAndMagnifyingState(/* activated= */true, /* magnifying= */true, displayId);
}
private void checkActivatedAndMagnifyingState(
boolean activated, boolean magnifying, int displayId) {
final boolean isActivated = mFullScreenMagnificationController.isActivated(displayId);
final boolean isMagnifying = mFullScreenMagnificationController.getScale(displayId) > 1.0f;
assertTrue(isActivated == activated);
assertTrue(isMagnifying == magnifying);
}
private MagnificationCallbacks getMagnificationCallbacks(int displayId) {

View File

@@ -19,6 +19,7 @@ package com.android.server.accessibility.magnification;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT;
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
@@ -78,24 +79,25 @@ import java.util.function.IntConsumer;
* {@code
* digraph {
* IDLE -> SHORTCUT_TRIGGERED [label="a11y\nbtn"]
* SHORTCUT_TRIGGERED -> IDLE [label="a11y\nbtn"]
* IDLE -> DOUBLE_TAP [label="2tap"]
* DOUBLE_TAP -> IDLE [label="timeout"]
* DOUBLE_TAP -> TRIPLE_TAP_AND_HOLD [label="down"]
* SHORTCUT_TRIGGERED -> TRIPLE_TAP_AND_HOLD [label="down"]
* TRIPLE_TAP_AND_HOLD -> ZOOMED [label="up"]
* TRIPLE_TAP_AND_HOLD -> DRAGGING_TMP [label="hold/\nswipe"]
* DRAGGING_TMP -> IDLE [label="release"]
* DOUBLE_TAP -> ZOOMED [label="tap"]
* DOUBLE_TAP -> NON_ACTIVATED_ZOOMED_TMP [label="hold"]
* NON_ACTIVATED_ZOOMED_TMP -> IDLE [label="release"]
* SHORTCUT_TRIGGERED -> IDLE [label="a11y\nbtn"]
* SHORTCUT_TRIGGERED -> ZOOMED[label="tap"]
* SHORTCUT_TRIGGERED -> ACTIVATED_ZOOMED_TMP [label="hold"]
* SHORTCUT_TRIGGERED -> PANNING [label="2hold]
* ZOOMED -> ZOOMED_DOUBLE_TAP [label="2tap"]
* ZOOMED_DOUBLE_TAP -> ZOOMED [label="timeout"]
* ZOOMED_DOUBLE_TAP -> DRAGGING [label="hold"]
* ZOOMED_DOUBLE_TAP -> IDLE [label="tap"]
* DRAGGING -> ZOOMED [label="release"]
* ZOOMED -> IDLE [label="a11y\nbtn"]
* ZOOMED -> PANNING [label="2hold"]
* ZOOMED_DOUBLE_TAP -> ZOOMED [label="timeout"]
* ZOOMED_DOUBLE_TAP -> ACTIVATED_ZOOMED_TMP [label="hold"]
* ZOOMED_DOUBLE_TAP -> IDLE [label="tap"]
* ACTIVATED_ZOOMED_TMP -> ZOOMED [label="release"]
* PANNING -> ZOOMED [label="release"]
* PANNING -> PANNING_SCALING [label="pinch"]
* PANNING_SCALING -> ZOOMED [label="release"]
* PANNING -> ZOOMED [label="release"]
* }
* }
*/
@@ -107,12 +109,11 @@ public class FullScreenMagnificationGestureHandlerTest {
public static final int STATE_2TAPS = 3;
public static final int STATE_ZOOMED_2TAPS = 4;
public static final int STATE_SHORTCUT_TRIGGERED = 5;
public static final int STATE_DRAGGING_TMP = 6;
public static final int STATE_DRAGGING = 7;
public static final int STATE_NON_ACTIVATED_ZOOMED_TMP = 6;
public static final int STATE_ACTIVATED_ZOOMED_TMP = 7;
public static final int STATE_PANNING = 8;
public static final int STATE_SCALING_AND_PANNING = 9;
public static final int FIRST_STATE = STATE_IDLE;
public static final int LAST_STATE = STATE_SCALING_AND_PANNING;
@@ -164,10 +165,6 @@ public class FullScreenMagnificationGestureHandlerTest {
public boolean magnificationRegionContains(int displayId, float x, float y) {
return true;
}
@Override
void setForceShowMagnifiableBounds(int displayId, boolean show) {
}
};
mFullScreenMagnificationController.register(DISPLAY_0);
mClock = new OffsettableClock.Stopped();
@@ -266,11 +263,11 @@ public class FullScreenMagnificationGestureHandlerTest {
@SuppressWarnings("Convert2MethodRef")
@Test
public void testAlternativeTransitions_areWorking() {
// A11y button followed by a tap&hold turns temporary "viewport dragging" zoom on
// A11y button followed by a tap&hold turns temporary "viewport dragging" zoom in
assertTransition(STATE_SHORTCUT_TRIGGERED, () -> {
send(downEvent());
fastForward1sec();
}, STATE_DRAGGING_TMP);
}, STATE_ACTIVATED_ZOOMED_TMP);
// A11y button followed by a tap turns zoom on
assertTransition(STATE_SHORTCUT_TRIGGERED, () -> tap(), STATE_ZOOMED);
@@ -281,7 +278,6 @@ public class FullScreenMagnificationGestureHandlerTest {
// A11y button turns zoom off
assertTransition(STATE_ZOOMED, () -> triggerShortcut(), STATE_IDLE);
// Double tap times out while zoomed
assertTransition(STATE_ZOOMED_2TAPS, () -> {
allowEventDelegation();
@@ -291,8 +287,11 @@ public class FullScreenMagnificationGestureHandlerTest {
// tap+tap+swipe doesn't get delegated
assertTransition(STATE_2TAPS, () -> swipe(), STATE_IDLE);
// tap+tap+swipe initiates viewport dragging immediately
assertTransition(STATE_2TAPS, () -> swipeAndHold(), STATE_DRAGGING_TMP);
// tap+tap+swipe&hold initiates temporary viewport dragging zoom in immediately
assertTransition(STATE_2TAPS, () -> swipeAndHold(), STATE_NON_ACTIVATED_ZOOMED_TMP);
// release when activated temporary zoom in back to zoomed
assertTransition(STATE_ACTIVATED_ZOOMED_TMP, () -> upEvent(), STATE_ZOOMED);
}
@Test
@@ -337,8 +336,10 @@ public class FullScreenMagnificationGestureHandlerTest {
@Test
public void testTripleTapAndHold_zoomsImmediately() {
assertZoomsImmediatelyOnSwipeFrom(STATE_2TAPS);
assertZoomsImmediatelyOnSwipeFrom(STATE_SHORTCUT_TRIGGERED);
assertZoomsImmediatelyOnSwipeFrom(STATE_2TAPS, STATE_NON_ACTIVATED_ZOOMED_TMP);
assertZoomsImmediatelyOnSwipeFrom(STATE_SHORTCUT_TRIGGERED, STATE_ACTIVATED_ZOOMED_TMP);
assertZoomsImmediatelyOnSwipeFrom(STATE_ZOOMED_2TAPS, STATE_ACTIVATED_ZOOMED_TMP);
}
@Test
@@ -391,10 +392,10 @@ public class FullScreenMagnificationGestureHandlerTest {
PointF pointer3 = new PointF(DEFAULT_X * 2, DEFAULT_Y);
send(downEvent());
send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2}));
send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2, pointer3}));
send(pointerEvent(ACTION_POINTER_UP, new PointF[] {pointer1, pointer2, pointer3}));
send(pointerEvent(ACTION_POINTER_UP, new PointF[] {pointer1, pointer2, pointer3}));
send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2}, 1));
send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2, pointer3}, 2));
send(pointerEvent(ACTION_POINTER_UP, new PointF[] {pointer1, pointer2, pointer3}, 2));
send(pointerEvent(ACTION_POINTER_UP, new PointF[] {pointer1, pointer2, pointer3}, 2));
send(upEvent());
assertIn(STATE_ZOOMED);
@@ -411,38 +412,53 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
public void testFirstFingerSwipe_TwoPinterDownAndZoomedState_panningState() {
public void testFirstFingerSwipe_twoPointerDownAndZoomedState_panningState() {
goFromStateIdleTo(STATE_ZOOMED);
PointF pointer1 = DEFAULT_POINT;
PointF pointer2 = new PointF(DEFAULT_X * 1.5f, DEFAULT_Y);
send(downEvent());
send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2}));
send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2}, 1));
//The minimum movement to transit to panningState.
final float sWipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop();
pointer1.offset(sWipeMinDistance + 1, 0);
send(pointerEvent(ACTION_MOVE, new PointF[] {pointer1, pointer2}));
send(pointerEvent(ACTION_MOVE, new PointF[] {pointer1, pointer2}, 0));
assertIn(STATE_PANNING);
assertIn(STATE_PANNING);
returnToNormalFrom(STATE_PANNING);
}
@Test
public void testSecondFingerSwipe_TwoPinterDownAndZoomedState_panningState() {
public void testSecondFingerSwipe_twoPointerDownAndZoomedState_panningState() {
goFromStateIdleTo(STATE_ZOOMED);
PointF pointer1 = DEFAULT_POINT;
PointF pointer2 = new PointF(DEFAULT_X * 1.5f, DEFAULT_Y);
send(downEvent());
send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2}));
send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2}, 1));
//The minimum movement to transit to panningState.
final float sWipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop();
pointer2.offset(sWipeMinDistance + 1, 0);
send(pointerEvent(ACTION_MOVE, new PointF[] {pointer1, pointer2}));
send(pointerEvent(ACTION_MOVE, new PointF[] {pointer1, pointer2}, 1));
assertIn(STATE_PANNING);
returnToNormalFrom(STATE_PANNING);
}
@Test
public void testSecondFingerSwipe_twoPointerDownAndShortcutTriggeredState_panningState() {
goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
PointF pointer1 = DEFAULT_POINT;
PointF pointer2 = new PointF(DEFAULT_X * 1.5f, DEFAULT_Y);
send(downEvent());
send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2}, 1));
//The minimum movement to transit to panningState.
final float sWipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop();
pointer2.offset(sWipeMinDistance + 1, 0);
send(pointerEvent(ACTION_MOVE, new PointF[] {pointer1, pointer2}, 1));
assertIn(STATE_PANNING);
returnToNormalFrom(STATE_PANNING);
}
@@ -474,11 +490,11 @@ public class FullScreenMagnificationGestureHandlerTest {
}
}
private void assertZoomsImmediatelyOnSwipeFrom(int state) {
goFromStateIdleTo(state);
private void assertZoomsImmediatelyOnSwipeFrom(int fromState, int toState) {
goFromStateIdleTo(fromState);
swipeAndHold();
assertIn(STATE_DRAGGING_TMP);
returnToNormalFrom(STATE_DRAGGING_TMP);
assertIn(toState);
returnToNormalFrom(toState);
}
private void assertTransition(int fromState, Runnable transitionAction, int toState) {
@@ -522,44 +538,51 @@ public class FullScreenMagnificationGestureHandlerTest {
case STATE_IDLE: {
check(tapCount() < 2, state);
check(!mMgh.mDetectingState.mShortcutTriggered, state);
check(!isActivated(), state);
check(!isZoomed(), state);
} break;
case STATE_ZOOMED: {
check(isActivated(), state);
check(isZoomed(), state);
check(tapCount() < 2, state);
} break;
case STATE_2TAPS: {
check(!isActivated(), state);
check(!isZoomed(), state);
check(tapCount() == 2, state);
} break;
case STATE_ZOOMED_2TAPS: {
check(isActivated(), state);
check(isZoomed(), state);
check(tapCount() == 2, state);
} break;
case STATE_DRAGGING: {
case STATE_NON_ACTIVATED_ZOOMED_TMP: {
check(isActivated(), state);
check(isZoomed(), state);
check(mMgh.mCurrentState == mMgh.mViewportDraggingState,
state);
check(mMgh.mViewportDraggingState.mZoomedInBeforeDrag, state);
check(!mMgh.mViewportDraggingState.mActivatedBeforeDrag, state);
} break;
case STATE_DRAGGING_TMP: {
case STATE_ACTIVATED_ZOOMED_TMP: {
check(isActivated(), state);
check(isZoomed(), state);
check(mMgh.mCurrentState == mMgh.mViewportDraggingState,
state);
check(!mMgh.mViewportDraggingState.mZoomedInBeforeDrag, state);
check(mMgh.mViewportDraggingState.mActivatedBeforeDrag, state);
} break;
case STATE_SHORTCUT_TRIGGERED: {
check(mMgh.mDetectingState.mShortcutTriggered, state);
check(isActivated(), state);
check(!isZoomed(), state);
} break;
case STATE_PANNING: {
check(isZoomed(), state);
check(isActivated(), state);
check(mMgh.mCurrentState == mMgh.mPanningScalingState,
state);
check(!mMgh.mPanningScalingState.mScaling, state);
} break;
case STATE_SCALING_AND_PANNING: {
check(isZoomed(), state);
check(isActivated(), state);
check(mMgh.mCurrentState == mMgh.mPanningScalingState,
state);
check(mMgh.mPanningScalingState.mScaling, state);
@@ -596,13 +619,13 @@ public class FullScreenMagnificationGestureHandlerTest {
tap();
tap();
} break;
case STATE_DRAGGING: {
goFromStateIdleTo(STATE_ZOOMED_2TAPS);
case STATE_NON_ACTIVATED_ZOOMED_TMP: {
goFromStateIdleTo(STATE_2TAPS);
send(downEvent());
fastForward1sec();
} break;
case STATE_DRAGGING_TMP: {
goFromStateIdleTo(STATE_2TAPS);
case STATE_ACTIVATED_ZOOMED_TMP: {
goFromStateIdleTo(STATE_ZOOMED_2TAPS);
send(downEvent());
fastForward1sec();
} break;
@@ -654,13 +677,13 @@ public class FullScreenMagnificationGestureHandlerTest {
case STATE_ZOOMED_2TAPS: {
tap();
} break;
case STATE_DRAGGING: {
case STATE_NON_ACTIVATED_ZOOMED_TMP: {
send(upEvent());
} break;
case STATE_ACTIVATED_ZOOMED_TMP: {
send(upEvent());
returnToNormalFrom(STATE_ZOOMED);
} break;
case STATE_DRAGGING_TMP: {
send(upEvent());
} break;
case STATE_SHORTCUT_TRIGGERED: {
triggerShortcut();
} break;
@@ -682,8 +705,12 @@ public class FullScreenMagnificationGestureHandlerTest {
}
}
private boolean isActivated() {
return mMgh.mFullScreenMagnificationController.isActivated(DISPLAY_0);
}
private boolean isZoomed() {
return mMgh.mFullScreenMagnificationController.isMagnifying(DISPLAY_0);
return mMgh.mFullScreenMagnificationController.getScale(DISPLAY_0) > 1.0f;
}
private int tapCount() {
@@ -770,10 +797,10 @@ public class FullScreenMagnificationGestureHandlerTest {
private MotionEvent pointerEvent(int action, float x, float y) {
return pointerEvent(action, new PointF[] {DEFAULT_POINT, new PointF(x, y)});
return pointerEvent(action, new PointF[] {DEFAULT_POINT, new PointF(x, y)}, 1);
}
private MotionEvent pointerEvent(int action, PointF[] pointersPosition) {
private MotionEvent pointerEvent(int action, PointF[] pointersPosition, int changedIndex) {
final MotionEvent.PointerProperties[] PointerPropertiesArray =
new MotionEvent.PointerProperties[pointersPosition.length];
for (int i = 0; i < pointersPosition.length; i++) {
@@ -792,6 +819,8 @@ public class FullScreenMagnificationGestureHandlerTest {
pointerCoordsArray[i] = pointerCoords;
}
action += (changedIndex << ACTION_POINTER_INDEX_SHIFT);
return MotionEvent.obtain(
/* downTime */ mClock.now(),
/* eventTime */ mClock.now(),

View File

@@ -494,16 +494,6 @@ public class MagnificationControllerTest {
eq(MODE_FULLSCREEN));
}
@Test
public void setScaleOneThroughExternalRequest_fullScreenEnabled_removeMagnificationButton()
throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, 1.0f,
MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, false, TEST_SERVICE_ID);
verify(mWindowMagnificationManager).removeMagnificationButton(eq(TEST_DISPLAY));
}
@Test
public void onPerformScaleAction_magnifierEnabled_handleScaleChange() throws RemoteException {
final float newScale = 4.0f;
@@ -756,7 +746,7 @@ public class MagnificationControllerTest {
mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, true);
assertFalse(mScreenMagnificationController.isMagnifying(TEST_DISPLAY));
verify(mScreenMagnificationController).reset(eq(TEST_DISPLAY), eq(false));
}
@Test