diff options
author | 2025-02-06 12:33:02 -0800 | |
---|---|---|
committer | 2025-02-06 12:33:02 -0800 | |
commit | 302a9fb2cc2513a9000d7a3190feb382c0005abb (patch) | |
tree | 1721d94059a8211d9cd6ba44b7ba4049c1eec9f6 | |
parent | adbf70a70fd7dcba719e9882a96e12aedeca3a13 (diff) | |
parent | 3c730b712e7b421722101da4d9b3df527be0c028 (diff) |
Merge "Magnification pans diagonally with same radius as straight." into main
4 files changed, 769 insertions, 123 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index e757dd5a77b7..396ea33208d1 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -23,6 +23,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; +import static android.util.MathUtils.sqrt; import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID; @@ -38,7 +39,6 @@ import android.graphics.Region; import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.Looper; -import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.util.DisplayMetrics; @@ -116,8 +116,14 @@ public class MagnificationController implements MagnificationConnectionManager.C private final Executor mBackgroundExecutor; private final Handler mHandler; - private @PanDirection int mActivePanDirection = PAN_DIRECTION_DOWN; + // Prefer this to SystemClock, because it allows for tests to influence behavior. + private SystemClock mSystemClock; + private boolean[] mActivePanDirections = {false, false, false, false}; private int mActivePanDisplay = Display.INVALID_DISPLAY; + // The time that panning by keyboard last took place. Since users can pan + // in multiple directions at once (for example, up + left), tracking last + // panned time ensures that panning doesn't occur too frequently. + private long mLastPannedTime = 0; private boolean mRepeatKeysEnabled = true; private @ZoomDirection int mActiveZoomDirection = ZOOM_DIRECTION_IN; @@ -186,6 +192,25 @@ public class MagnificationController implements MagnificationConnectionManager.C void onResult(int displayId, boolean success); } + /** + * Functional interface for providing time. Tests may extend this interface to "control time". + */ + @VisibleForTesting + interface SystemClock { + /** + * Returns current time in milliseconds since boot, not counting time spent in deep sleep. + */ + long uptimeMillis(); + } + + /** The real system clock for use in production. */ + private static class SystemClockImpl implements SystemClock { + @Override + public long uptimeMillis() { + return android.os.SystemClock.uptimeMillis(); + } + } + /** * An interface to configure how much the magnification scale should be affected when moving in @@ -311,6 +336,7 @@ public class MagnificationController implements MagnificationConnectionManager.C mScaleProvider = scaleProvider; mBackgroundExecutor = backgroundExecutor; mHandler = new Handler(looper); + mSystemClock = new SystemClockImpl(); LocalServices.getService(WindowManagerInternal.class) .getAccessibilityController().setUiChangesForAccessibilityCallbacks(this); mSupportWindowMagnification = context.getPackageManager().hasSystemFeature( @@ -327,10 +353,12 @@ public class MagnificationController implements MagnificationConnectionManager.C public MagnificationController(AccessibilityManagerService ams, Object lock, Context context, FullScreenMagnificationController fullScreenMagnificationController, MagnificationConnectionManager magnificationConnectionManager, - MagnificationScaleProvider scaleProvider, Executor backgroundExecutor, Looper looper) { + MagnificationScaleProvider scaleProvider, Executor backgroundExecutor, Looper looper, + SystemClock systemClock) { this(ams, lock, context, scaleProvider, backgroundExecutor, looper); mFullScreenMagnificationController = fullScreenMagnificationController; mMagnificationConnectionManager = magnificationConnectionManager; + mSystemClock = systemClock; } @Override @@ -368,13 +396,13 @@ public class MagnificationController implements MagnificationConnectionManager.C @Override public void onPanMagnificationStart(int displayId, @MagnificationController.PanDirection int direction) { - // TODO(b/355499907): Handle multiple pan gestures at the same time (e.g. user may try to - // pan diagonally) by decreasing diagonal movement by sqrt(2) to make it appear the same - // speed as non-diagonal movement. - panMagnificationByStep(displayId, direction); - mActivePanDirection = direction; + // Update the current panning state for any callbacks. + boolean isAlreadyPanning = mActivePanDisplay != Display.INVALID_DISPLAY; mActivePanDisplay = displayId; - if (mRepeatKeysEnabled) { + mActivePanDirections[direction] = true; + // React immediately to any new key press by panning in the new composite direction. + panMagnificationByStep(mActivePanDisplay, mActivePanDirections); + if (!isAlreadyPanning && mRepeatKeysEnabled) { mHandler.sendMessageDelayed( PooledLambda.obtainMessage(MagnificationController::maybeContinuePan, this), mInitialKeyboardRepeatIntervalMs); @@ -382,9 +410,14 @@ public class MagnificationController implements MagnificationConnectionManager.C } @Override - public void onPanMagnificationStop(int displayId, - @MagnificationController.PanDirection int direction) { - if (direction == mActivePanDirection) { + public void onPanMagnificationStop(@MagnificationController.PanDirection int direction) { + // Stop panning in this direction. + mActivePanDirections[direction] = false; + if (!mActivePanDirections[PAN_DIRECTION_LEFT] + && !mActivePanDirections[PAN_DIRECTION_RIGHT] + && !mActivePanDirections[PAN_DIRECTION_UP] + && !mActivePanDirections[PAN_DIRECTION_DOWN]) { + // Stop all panning if no more pan directions were in started. mActivePanDisplay = Display.INVALID_DISPLAY; } } @@ -392,9 +425,14 @@ public class MagnificationController implements MagnificationConnectionManager.C @Override public void onScaleMagnificationStart(int displayId, @MagnificationController.ZoomDirection int direction) { - scaleMagnificationByStep(displayId, direction); + if (mActiveZoomDisplay != Display.INVALID_DISPLAY) { + // Only allow one zoom direction at a time (even if the other keyboard + // shortcut has been pressed). Return early if we are already zooming. + return; + } mActiveZoomDirection = direction; mActiveZoomDisplay = displayId; + scaleMagnificationByStep(displayId, direction); if (mRepeatKeysEnabled) { mHandler.sendMessageDelayed( PooledLambda.obtainMessage(MagnificationController::maybeContinueZoom, this), @@ -403,16 +441,27 @@ public class MagnificationController implements MagnificationConnectionManager.C } @Override - public void onScaleMagnificationStop(int displayId, - @MagnificationController.ZoomDirection int direction) { + public void onScaleMagnificationStop(@MagnificationController.ZoomDirection int direction) { if (direction == mActiveZoomDirection) { mActiveZoomDisplay = Display.INVALID_DISPLAY; } } + @Override + public void onKeyboardInteractionStop() { + mActiveZoomDisplay = Display.INVALID_DISPLAY; + mActivePanDisplay = Display.INVALID_DISPLAY; + mActivePanDirections = new boolean[]{false, false, false, false}; + } + private void maybeContinuePan() { - if (mActivePanDisplay != Display.INVALID_DISPLAY) { - panMagnificationByStep(mActivePanDisplay, mActivePanDirection); + if (mActivePanDisplay == Display.INVALID_DISPLAY) { + return; + } + if (mSystemClock.uptimeMillis() - mLastPannedTime >= KEYBOARD_REPEAT_INTERVAL_MS) { + panMagnificationByStep(mActivePanDisplay, mActivePanDirections); + } + if (mRepeatKeysEnabled) { mHandler.sendMessageDelayed( PooledLambda.obtainMessage(MagnificationController::maybeContinuePan, this), KEYBOARD_REPEAT_INTERVAL_MS); @@ -422,9 +471,12 @@ public class MagnificationController implements MagnificationConnectionManager.C private void maybeContinueZoom() { if (mActiveZoomDisplay != Display.INVALID_DISPLAY) { scaleMagnificationByStep(mActiveZoomDisplay, mActiveZoomDirection); - mHandler.sendMessageDelayed( - PooledLambda.obtainMessage(MagnificationController::maybeContinueZoom, this), - KEYBOARD_REPEAT_INTERVAL_MS); + if (mRepeatKeysEnabled) { + mHandler.sendMessageDelayed( + PooledLambda.obtainMessage(MagnificationController::maybeContinueZoom, + this), + KEYBOARD_REPEAT_INTERVAL_MS); + } } } @@ -719,7 +771,7 @@ public class MagnificationController implements MagnificationConnectionManager.C public void onWindowMagnificationActivationState(int displayId, boolean activated) { if (activated) { synchronized (mLock) { - mWindowModeEnabledTimeArray.put(displayId, SystemClock.uptimeMillis()); + mWindowModeEnabledTimeArray.put(displayId, mSystemClock.uptimeMillis()); setCurrentMagnificationModeAndSwitchDelegate(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); mLastMagnificationActivatedModeArray.put(displayId, @@ -733,7 +785,7 @@ public class MagnificationController implements MagnificationConnectionManager.C synchronized (mLock) { setCurrentMagnificationModeAndSwitchDelegate(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_NONE); - duration = SystemClock.uptimeMillis() - mWindowModeEnabledTimeArray.get(displayId); + duration = mSystemClock.uptimeMillis() - mWindowModeEnabledTimeArray.get(displayId); scale = mMagnificationConnectionManager.getLastActivatedScale(displayId); } logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, duration, scale); @@ -830,7 +882,7 @@ public class MagnificationController implements MagnificationConnectionManager.C if (activated) { synchronized (mLock) { - mFullScreenModeEnabledTimeArray.put(displayId, SystemClock.uptimeMillis()); + mFullScreenModeEnabledTimeArray.put(displayId, mSystemClock.uptimeMillis()); setCurrentMagnificationModeAndSwitchDelegate(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); mLastMagnificationActivatedModeArray.put(displayId, @@ -844,7 +896,7 @@ public class MagnificationController implements MagnificationConnectionManager.C synchronized (mLock) { setCurrentMagnificationModeAndSwitchDelegate(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_NONE); - duration = SystemClock.uptimeMillis() + duration = mSystemClock.uptimeMillis() - mFullScreenModeEnabledTimeArray.get(displayId); scale = mFullScreenMagnificationController.getLastActivatedScale(displayId); } @@ -1132,7 +1184,7 @@ public class MagnificationController implements MagnificationConnectionManager.C * @param displayId The logical display id. * @param direction Whether the scale should be zoomed in or out. */ - public void scaleMagnificationByStep(int displayId, @ZoomDirection int direction) { + private void scaleMagnificationByStep(int displayId, @ZoomDirection int direction) { if (getFullScreenMagnificationController().isActivated(displayId)) { final float magnificationScale = getFullScreenMagnificationController().getScale( displayId); @@ -1157,9 +1209,14 @@ public class MagnificationController implements MagnificationConnectionManager.C * param. * * @param displayId The logical display id. - * @param direction Whether the direction should be left/right/up/down. + * @param directions The directions to pan, indexed by {@code PanDirection}. If two or more + * are active, panning may be diagonal. */ - public void panMagnificationByStep(int displayId, @PanDirection int direction) { + private void panMagnificationByStep(int displayId, boolean[] directions) { + if (directions.length != 4) { + Slog.d(TAG, "Invalid number of panning directions"); + return; + } final boolean fullscreenActivated = getFullScreenMagnificationController().isActivated(displayId); final boolean windowActivated = @@ -1168,21 +1225,43 @@ public class MagnificationController implements MagnificationConnectionManager.C return; } + int numDirections = (directions[PAN_DIRECTION_LEFT] ? 1 : 0) + + (directions[PAN_DIRECTION_RIGHT] ? 1 : 0) + + (directions[PAN_DIRECTION_UP] ? 1 : 0) + + (directions[PAN_DIRECTION_DOWN] ? 1 : 0); + if (numDirections == 0) { + return; + } + final float scale = fullscreenActivated ? getFullScreenMagnificationController().getScale(displayId) : getMagnificationConnectionManager().getScale(displayId); - final float step = mPanStepProvider.nextPanStep(scale, displayId); + float step = mPanStepProvider.nextPanStep(scale, displayId); + + // If the user is trying to pan diagonally (2 directions), divide by the sqrt(2) + // so that the apparent step length (the radius of the step) is the same as + // panning in just one direction. + // Note that if numDirections is 3 or 4, opposite directions will cancel and + // there's no need to rescale {@code step}. + if (numDirections == 2) { + step /= sqrt(2); + } + // If two directions cancel out, they will be added and subtracted below for net change 0. + // This makes the logic simpler than removing out opposite directions manually. float offsetX = 0; float offsetY = 0; - if (direction == PAN_DIRECTION_LEFT) { - offsetX = -step; - } else if (direction == PAN_DIRECTION_RIGHT) { - offsetX = step; - } else if (direction == PAN_DIRECTION_UP) { - offsetY = -step; - } else if (direction == PAN_DIRECTION_DOWN) { - offsetY = step; + if (directions[PAN_DIRECTION_LEFT]) { + offsetX -= step; + } + if (directions[PAN_DIRECTION_RIGHT]) { + offsetX += step; + } + if (directions[PAN_DIRECTION_UP]) { + offsetY -= step; + } + if (directions[PAN_DIRECTION_DOWN]) { + offsetY += step; } if (fullscreenActivated) { @@ -1194,6 +1273,8 @@ public class MagnificationController implements MagnificationConnectionManager.C getMagnificationConnectionManager().moveWindowMagnification(displayId, offsetX, offsetY); } + + mLastPannedTime = mSystemClock.uptimeMillis(); } private final class DisableMagnificationCallback implements diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationKeyHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationKeyHandler.java index f20755328479..dab5411a3173 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationKeyHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationKeyHandler.java @@ -46,10 +46,8 @@ public class MagnificationKeyHandler extends BaseEventStreamTransformation { * arrows had been pressed at the same time (e.g. diagonal panning). * * @param displayId The logical display ID - * @param direction The direction in which panning stopped */ - void onPanMagnificationStop(int displayId, - @MagnificationController.PanDirection int direction); + void onPanMagnificationStop(int displayId); /** * Called when a keyboard shortcut to scale magnification in direction `direction` is @@ -65,14 +63,18 @@ public class MagnificationKeyHandler extends BaseEventStreamTransformation { * Called when a keyboard shortcut to scale magnification in direction `direction` is * unpressed by a user. * - * @param displayId The logical display ID * @param direction The direction in which scaling stopped */ - void onScaleMagnificationStop(int displayId, - @MagnificationController.ZoomDirection int direction); + void onScaleMagnificationStop(@MagnificationController.ZoomDirection int direction); + + /** + * Called when all keyboard interaction with magnification should be stopped. + */ + void onKeyboardInteractionStop(); } protected final MagnificationKeyHandler.Callback mCallback; + private boolean mIsKeyboardInteracting = false; public MagnificationKeyHandler(Callback callback) { mCallback = callback; @@ -88,6 +90,12 @@ public class MagnificationKeyHandler extends BaseEventStreamTransformation { boolean modifiersPressed = event.isAltPressed() && event.isMetaPressed(); if (!modifiersPressed) { super.onKeyEvent(event, policyFlags); + if (mIsKeyboardInteracting) { + // When modifier keys are no longer pressed, ensure that scaling and + // panning are fully stopped. + mCallback.onKeyboardInteractionStop(); + mIsKeyboardInteracting = false; + } return; } boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN; @@ -102,8 +110,9 @@ public class MagnificationKeyHandler extends BaseEventStreamTransformation { }; if (isDown) { mCallback.onPanMagnificationStart(getDisplayId(event), panDirection); + mIsKeyboardInteracting = true; } else { - mCallback.onPanMagnificationStop(getDisplayId(event), panDirection); + mCallback.onPanMagnificationStop(panDirection); } return; } else if (keyCode == KeyEvent.KEYCODE_EQUALS || keyCode == KeyEvent.KEYCODE_MINUS) { @@ -113,8 +122,9 @@ public class MagnificationKeyHandler extends BaseEventStreamTransformation { } if (isDown) { mCallback.onScaleMagnificationStart(getDisplayId(event), zoomDirection); + mIsKeyboardInteracting = true; } else { - mCallback.onScaleMagnificationStop(getDisplayId(event), zoomDirection); + mCallback.onScaleMagnificationStop(zoomDirection); } return; } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index cd6b36dbc1c6..6607054e13d3 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -18,6 +18,7 @@ package com.android.server.accessibility.magnification; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; +import static android.util.MathUtils.sqrt; import static com.android.internal.accessibility.common.MagnificationConstants.SCALE_MAX_VALUE; import static com.android.internal.accessibility.common.MagnificationConstants.SCALE_MIN_VALUE; @@ -175,6 +176,20 @@ public class MagnificationControllerTest { private TestLooper mTestLooper; + private static class FakeSystemClock implements MagnificationController.SystemClock { + private long mUptimeMillis = 1984; + + @Override + public long uptimeMillis() { + return mUptimeMillis; + } + + public void advanceTime(long ms) { + mUptimeMillis += ms; + } + } + private FakeSystemClock mSystemClock; + // To mock package-private class @Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = @@ -201,6 +216,7 @@ public class MagnificationControllerTest { mMockResolver = new MockContentResolver(); mMockResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + mSystemClock = new FakeSystemClock(); mTestLooper = new TestLooper(); when(mContext.getMainLooper()).thenReturn( InstrumentationRegistry.getContext().getMainLooper()); @@ -255,7 +271,7 @@ public class MagnificationControllerTest { mMagnificationController = spy(new MagnificationController(mService, globalLock, mContext, mScreenMagnificationController, mMagnificationConnectionManager, mScaleProvider, - ConcurrentUtils.DIRECT_EXECUTOR, mTestLooper.getLooper())); + ConcurrentUtils.DIRECT_EXECUTOR, mTestLooper.getLooper(), mSystemClock)); mMagnificationController.setMagnificationCapabilities( Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL); @@ -680,7 +696,7 @@ public class MagnificationControllerTest { } @Test - public void scaleMagnificationByStep_fullscreenMode_stepInAndOut() throws RemoteException { + public void scaleMagnificationStep_fullscreenMode_stepInAndOut() throws RemoteException { setMagnificationEnabled(MODE_FULLSCREEN); mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 1.0f, false); reset(mScreenMagnificationController); @@ -689,67 +705,79 @@ public class MagnificationControllerTest { // {@code MagnificationController.DefaultMagnificationScaleStepProvider // .ZOOM_STEP_SCALE_FACTOR} and the center coordinates are // unchanged (Float.NaN as values denotes unchanged center). - mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + mMagnificationController.onScaleMagnificationStart(TEST_DISPLAY, MagnificationController.ZOOM_DIRECTION_IN); verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), eq(MagnificationController .DefaultMagnificationScaleStepProvider.ZOOM_STEP_SCALE_FACTOR), eq(Float.NaN), eq(Float.NaN), anyBoolean(), anyInt()); + mMagnificationController.onScaleMagnificationStop( + MagnificationController.ZOOM_DIRECTION_IN); - mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + mMagnificationController.onScaleMagnificationStart(TEST_DISPLAY, MagnificationController.ZOOM_DIRECTION_OUT); verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), eq(SCALE_MIN_VALUE), eq(Float.NaN), eq(Float.NaN), anyBoolean(), anyInt()); + mMagnificationController.onScaleMagnificationStop( + MagnificationController.ZOOM_DIRECTION_OUT); } @Test - public void scaleMagnificationByStep_testMaxScaling() throws RemoteException { + public void scaleMagnificationStep_testMaxScaling() throws RemoteException { setMagnificationEnabled(MODE_FULLSCREEN); mMagnificationController.onPerformScaleAction(TEST_DISPLAY, SCALE_MIN_VALUE, false); reset(mScreenMagnificationController); float currentScale = mScreenMagnificationController.getScale(TEST_DISPLAY); while (currentScale < SCALE_MAX_VALUE) { - mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + mMagnificationController.onScaleMagnificationStart(TEST_DISPLAY, MagnificationController.ZOOM_DIRECTION_IN); final float nextScale = mScreenMagnificationController.getScale(TEST_DISPLAY); assertThat(nextScale).isGreaterThan(currentScale); currentScale = nextScale; + mMagnificationController.onScaleMagnificationStop( + MagnificationController.ZOOM_DIRECTION_IN); } assertThat(currentScale).isEqualTo(SCALE_MAX_VALUE); // Trying to scale further does not change the scale. - mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + mMagnificationController.onScaleMagnificationStart(TEST_DISPLAY, MagnificationController.ZOOM_DIRECTION_IN); final float finalScale = mScreenMagnificationController.getScale(TEST_DISPLAY); assertThat(finalScale).isEqualTo(currentScale); + mMagnificationController.onScaleMagnificationStop( + MagnificationController.ZOOM_DIRECTION_IN); } @Test - public void scaleMagnificationByStep_testMinScaling() throws RemoteException { + public void scaleMagnificationStep_testMinScaling() throws RemoteException { setMagnificationEnabled(MODE_FULLSCREEN); mMagnificationController.onPerformScaleAction(TEST_DISPLAY, SCALE_MAX_VALUE, false); reset(mScreenMagnificationController); float currentScale = mScreenMagnificationController.getScale(TEST_DISPLAY); while (currentScale > SCALE_MIN_VALUE) { - mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + mMagnificationController.onScaleMagnificationStart(TEST_DISPLAY, MagnificationController.ZOOM_DIRECTION_OUT); final float nextScale = mScreenMagnificationController.getScale(TEST_DISPLAY); assertThat(nextScale).isLessThan(currentScale); currentScale = nextScale; + mMagnificationController.onScaleMagnificationStop( + MagnificationController.ZOOM_DIRECTION_OUT); } assertThat(currentScale).isEqualTo(SCALE_MIN_VALUE); // Trying to scale further does not change the scale. - mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + mMagnificationController.onScaleMagnificationStart(TEST_DISPLAY, MagnificationController.ZOOM_DIRECTION_OUT); final float finalScale = mScreenMagnificationController.getScale(TEST_DISPLAY); assertThat(finalScale).isEqualTo(currentScale); + mMagnificationController.onScaleMagnificationStop( + MagnificationController.ZOOM_DIRECTION_OUT); } @Test - public void scaleMagnificationByStep_windowedMode_stepInAndOut() throws RemoteException { + public void scaleMagnificationStep_windowedMode_stepInAndOut() throws RemoteException { setMagnificationEnabled(MODE_WINDOW); mMagnificationController.onPerformScaleAction(TEST_DISPLAY, SCALE_MIN_VALUE, false); reset(mMagnificationConnectionManager); @@ -757,22 +785,26 @@ public class MagnificationControllerTest { // Verify the zoom scale factor increases by // {@code MagnificationController.DefaultMagnificationScaleStepProvider // .ZOOM_STEP_SCALE_FACTOR}. - mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + mMagnificationController.onScaleMagnificationStart(TEST_DISPLAY, MagnificationController.ZOOM_DIRECTION_IN); verify(mMagnificationConnectionManager).setScale(eq(TEST_DISPLAY), eq(MagnificationController .DefaultMagnificationScaleStepProvider.ZOOM_STEP_SCALE_FACTOR)); + mMagnificationController.onScaleMagnificationStop( + MagnificationController.ZOOM_DIRECTION_IN); - mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + mMagnificationController.onScaleMagnificationStart(TEST_DISPLAY, MagnificationController.ZOOM_DIRECTION_OUT); verify(mMagnificationConnectionManager).setScale(eq(TEST_DISPLAY), eq(SCALE_MIN_VALUE)); + mMagnificationController.onScaleMagnificationStop( + MagnificationController.ZOOM_DIRECTION_OUT); } @Test - public void panMagnificationByStep_fullscreenMode_stepSizeAtScale2() throws RemoteException { + public void panMagnificationStep_fullscreenMode_stepSizeAtScale2() throws RemoteException { setMagnificationEnabled(MODE_FULLSCREEN); - // At scale 2.0f, each step should be about 40 dpi. + // At scale 8.0f, each step should be about 27 dip. mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 2.0f, false); reset(mScreenMagnificationController); @@ -780,9 +812,9 @@ public class MagnificationControllerTest { } @Test - public void panMagnificationByStep_fullscreenMode_stepSizeAtScale8() throws RemoteException { + public void panMagnificationStep_fullscreenMode_stepSizeAtScale8() throws RemoteException { setMagnificationEnabled(MODE_FULLSCREEN); - // At scale 8.0f, each step should be about 27 dpi. + // At scale 8.0f, each step should be about 27 dip. mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 8.0f, false); reset(mScreenMagnificationController); @@ -790,16 +822,16 @@ public class MagnificationControllerTest { } @Test - public void panMagnificationByStep_windowMode_stepSizeAtScale2() throws RemoteException { + public void panMagnificationStep_windowMode_stepSizeAtScale2() throws RemoteException { mMagnificationConnectionManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, 100f, 200f); testWindowMagnificationPanWithStepSize(40.0f); } @Test - public void panMagnificationByStep_windowMode_stepSizeAtScale8() throws RemoteException { + public void panMagnificationStep_windowMode_stepSizeAtScale8() throws RemoteException { setMagnificationEnabled(MODE_WINDOW); - // At scale 8.0f, each step should be about 27. + // At scale 8.0f, each step should be about 27 dip. mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 8.0f, false); reset(mMagnificationConnectionManager); @@ -807,10 +839,10 @@ public class MagnificationControllerTest { } @Test - public void panMagnificationByStep_fullscreenMode_reachesRightEdgeOfScreen() + public void panMagnificationStep_fullscreenMode_reachesRightEdgeOfScreen() throws RemoteException { setMagnificationEnabled(MODE_FULLSCREEN); - // At scale 2.0f, each step should be about 40. + // At scale 2.0f, each step should be about 40 dip. mMagnificationController.onPerformScaleAction(TEST_DISPLAY, DEFAULT_SCALE, false); reset(mScreenMagnificationController); @@ -825,7 +857,9 @@ public class MagnificationControllerTest { int maxNumSteps = (int) (metrics.widthPixels / expectedStep) + 1; int numSteps = 0; while (numSteps < maxNumSteps) { - mMagnificationController.panMagnificationByStep(TEST_DISPLAY, + mMagnificationController.onPanMagnificationStart(TEST_DISPLAY, + MagnificationController.PAN_DIRECTION_RIGHT); + mMagnificationController.onPanMagnificationStop( MagnificationController.PAN_DIRECTION_RIGHT); float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); @@ -846,10 +880,10 @@ public class MagnificationControllerTest { } @Test - public void panMagnificationByStep_fullscreenMode_reachesBottomEdgeOfScreen() + public void panMagnificationStep_fullscreenMode_reachesBottomEdgeOfScreen() throws RemoteException { setMagnificationEnabled(MODE_FULLSCREEN); - // At scale 2.0f, each step should be about 40. + // At scale 2.0f, each step should be about 40 dip. mMagnificationController.onPerformScaleAction(TEST_DISPLAY, DEFAULT_SCALE, false); reset(mScreenMagnificationController); @@ -864,7 +898,9 @@ public class MagnificationControllerTest { int maxNumSteps = (int) (metrics.heightPixels / expectedStep) + 1; int numSteps = 0; while (numSteps < maxNumSteps) { - mMagnificationController.panMagnificationByStep(TEST_DISPLAY, + mMagnificationController.onPanMagnificationStart(TEST_DISPLAY, + MagnificationController.PAN_DIRECTION_DOWN); + mMagnificationController.onPanMagnificationStop( MagnificationController.PAN_DIRECTION_DOWN); float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); @@ -885,6 +921,47 @@ public class MagnificationControllerTest { } @Test + public void panMagnificationStep_windowMode_reachesRightEdgeOfScreen() + throws RemoteException { + setMagnificationEnabled(MODE_WINDOW); + // At scale 8.0f, each step should be about 27 dip. + mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 8.0f, false); + reset(mMagnificationConnectionManager); + + float currentCenterX = mMagnificationConnectionManager.getCenterX(TEST_DISPLAY); + float currentCenterY = mMagnificationConnectionManager.getCenterY(TEST_DISPLAY); + + DisplayMetrics metrics = new DisplayMetrics(); + mDisplay.getMetrics(metrics); + float expectedStep = 40.0f * metrics.density; + + // Move right, eventually we should reach the edge. + int maxNumSteps = (int) (metrics.widthPixels / expectedStep) + 1; + int numSteps = 0; + while (numSteps < maxNumSteps) { + mMagnificationController.onPanMagnificationStart(TEST_DISPLAY, + MagnificationController.PAN_DIRECTION_RIGHT); + mMagnificationController.onPanMagnificationStop( + MagnificationController.PAN_DIRECTION_RIGHT); + float newCenterX = mMagnificationConnectionManager.getCenterX(TEST_DISPLAY); + float newCenterY = mMagnificationConnectionManager.getCenterY(TEST_DISPLAY); + assertThat(currentCenterY).isEqualTo(newCenterY); + + assertThat(newCenterX).isAtLeast(currentCenterX); + if (newCenterX == currentCenterX) { + break; + } + + currentCenterX = newCenterX; + currentCenterY = newCenterY; + numSteps++; + } + assertWithMessage("Still not at edge after panning right " + numSteps + + " steps. Current position: " + currentCenterX + "," + currentCenterY) + .that(numSteps).isLessThan(maxNumSteps); + } + + @Test public void magnificationCallbacks_scaleMagnificationContinuous() throws RemoteException { setMagnificationEnabled(MODE_FULLSCREEN); float currentScale = 2.0f; @@ -910,7 +987,8 @@ public class MagnificationControllerTest { currentScale = newScale; // Wait for the initial delay to occur. - advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs() + 1); + int initialMs = mMagnificationController.getInitialKeyboardRepeatIntervalMs() + 1; + advanceTime(initialMs); // It should have scaled again after the handler was triggered. newScale = mScreenMagnificationController.getScale(TEST_DISPLAY); @@ -928,7 +1006,7 @@ public class MagnificationControllerTest { } // Stop magnification scale. - mMagnificationController.onScaleMagnificationStop(TEST_DISPLAY, + mMagnificationController.onScaleMagnificationStop( MagnificationController.ZOOM_DIRECTION_IN); // It should not scale again, even after the appropriate delay. @@ -975,10 +1053,10 @@ public class MagnificationControllerTest { currentCenterX = newCenterX; currentCenterY = newCenterY; - for (int i = 0; i < 3; i++) { - // Wait for the initial delay to occur. - advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs() + 1); + // Wait for the initial delay to occur. + advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs() + 1); + for (int i = 0; i < 3; i++) { // It should not have moved again because repeat keys is disabled. newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); @@ -986,9 +1064,13 @@ public class MagnificationControllerTest { expect.that(currentCenterY).isEqualTo(newCenterY); currentCenterX = newCenterX; currentCenterY = newCenterY; + + // Try waiting even longer. Nothing should ever happen. + advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs() + + MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1); } - mMagnificationController.onPanMagnificationStop(TEST_DISPLAY, + mMagnificationController.onPanMagnificationStop( MagnificationController.PAN_DIRECTION_DOWN); } @@ -1010,20 +1092,457 @@ public class MagnificationControllerTest { currentScale = newScale; - for (int i = 0; i < 3; i++) { - // Wait for the initial delay to occur. - advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs() + 1); + // Wait for the initial delay to occur. + advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs() + 1); + for (int i = 0; i < 3; i++) { // It should not have scaled again because repeat keys is disabled. newScale = mScreenMagnificationController.getScale(TEST_DISPLAY); expect.that(currentScale).isEqualTo(newScale); + + // Try waiting even longer. Nothing should ever happen. + advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs() + + MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1); } - mMagnificationController.onScaleMagnificationStop(TEST_DISPLAY, + mMagnificationController.onScaleMagnificationStop( MagnificationController.ZOOM_DIRECTION_OUT); } @Test + public void panMagnification_continuousDiagonalPanning_rightDown() throws RemoteException { + setMagnificationEnabled(MODE_FULLSCREEN); + mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 8.0f, false); + reset(mScreenMagnificationController); + + // At scale 8.0f, each step should be about 27 dip. + float expectedStep = 27.0f; + + DisplayMetrics metrics = new DisplayMetrics(); + mDisplay.getMetrics(metrics); + expectedStep *= metrics.density; + + float expectedDiagonalStep = expectedStep / sqrt(2); + + float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + + // Start panning right. + mMagnificationController.onPanMagnificationStart(TEST_DISPLAY, + MagnificationController.PAN_DIRECTION_RIGHT); + + // A step right should be taken immediately. + float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isLessThan(newCenterX); + expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep); + expect.that(currentCenterY).isEqualTo(newCenterY); + + currentCenterX = newCenterX; + currentCenterY = newCenterY; + + // Start panning down. + mMagnificationController.onPanMagnificationStart(TEST_DISPLAY, + MagnificationController.PAN_DIRECTION_DOWN); + + // The diagonal step should be taken immediately. + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isLessThan(newCenterX); + expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedDiagonalStep); + expect.that(newCenterY).isGreaterThan(currentCenterY); + expect.that(newCenterY - currentCenterY).isWithin(0.01f).of(expectedDiagonalStep); + + currentCenterX = newCenterX; + currentCenterY = newCenterY; + + // If we wait for the timeout from the initial pan start, we will move diagonally again. + advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs()); + + for (int i = 0; i < 3; i++) { + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isLessThan(newCenterX); + expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedDiagonalStep); + expect.that(newCenterY).isGreaterThan(currentCenterY); + expect.that(newCenterY - currentCenterY).isWithin(0.01f).of(expectedDiagonalStep); + + currentCenterX = newCenterX; + currentCenterY = newCenterY; + + // Wait for next animation step. + if (i < 2) { + advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1); + } + } + + // Release the "right" key. Should continue panning down only at the full step size. + mMagnificationController.onPanMagnificationStop( + MagnificationController.PAN_DIRECTION_RIGHT); + advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1); + + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isEqualTo(newCenterX); + expect.that(newCenterY).isGreaterThan(currentCenterY); + expect.that(newCenterY - currentCenterY).isWithin(0.01f).of(expectedStep); + + currentCenterX = newCenterX; + currentCenterY = newCenterY; + + // Release the "down" key. No more panning. + mMagnificationController.onPanMagnificationStop(MagnificationController.PAN_DIRECTION_DOWN); + + advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1); + + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isEqualTo(newCenterX); + expect.that(currentCenterY).isEqualTo(newCenterY); + } + + @Test + public void panMagnification_continuousDiagonalPanning_rightThenDown() throws RemoteException { + setMagnificationEnabled(MODE_FULLSCREEN); + mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 8.0f, false); + reset(mScreenMagnificationController); + + // At scale 8.0f, each step should be about 27 dip. + float expectedStep = 27.0f; + + DisplayMetrics metrics = new DisplayMetrics(); + mDisplay.getMetrics(metrics); + expectedStep *= metrics.density; + + float expectedDiagonalStep = expectedStep / sqrt(2); + + float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + + // Start panning right. + mMagnificationController.onPanMagnificationStart(TEST_DISPLAY, + MagnificationController.PAN_DIRECTION_RIGHT); + + // A step right should be taken immediately. + float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isLessThan(newCenterX); + expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep); + expect.that(currentCenterY).isEqualTo(newCenterY); + + currentCenterX = newCenterX; + currentCenterY = newCenterY; + + // If we wait for the timeout from the initial pan start, we will move right again. + advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs()); + + for (int i = 0; i < 2; i++) { + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isLessThan(newCenterX); + expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep); + expect.that(currentCenterY).isEqualTo(newCenterY); + + currentCenterX = newCenterX; + currentCenterY = newCenterY; + + // Wait for next animation step. + if (i == 0) { + advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1); + } + } + + // Wait halfway through the next repeat interval. + int halfTimeStep = (int) (MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS / 2.0); + advanceTime(halfTimeStep); + + // Start panning down. + mMagnificationController.onPanMagnificationStart(TEST_DISPLAY, + MagnificationController.PAN_DIRECTION_DOWN); + + // The diagonal step should be taken immediately. + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isLessThan(newCenterX); + expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedDiagonalStep); + expect.that(newCenterY).isGreaterThan(currentCenterY); + expect.that(newCenterY - currentCenterY).isWithin(0.01f).of(expectedDiagonalStep); + + currentCenterX = newCenterX; + currentCenterY = newCenterY; + + // Wait for the rest of the original interval. + advanceTime(halfTimeStep); + + // It has not advanced yet because it hasn't been enough time since we last panned. + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isEqualTo(newCenterX); + expect.that(newCenterY).isEqualTo(currentCenterY); + + currentCenterX = newCenterX; + currentCenterY = newCenterY; + + // Now it moves diagonally automatically. + for (int i = 0; i < 2; i++) { + advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1); + + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isLessThan(newCenterX); + expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedDiagonalStep); + expect.that(newCenterY).isGreaterThan(currentCenterY); + expect.that(newCenterY - currentCenterY).isWithin(0.01f).of(expectedDiagonalStep); + + currentCenterX = newCenterX; + currentCenterY = newCenterY; + } + + + // Release the "right" key. Should continue panning down only at the full step size. + mMagnificationController.onPanMagnificationStop( + MagnificationController.PAN_DIRECTION_RIGHT); + advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1); + + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isEqualTo(newCenterX); + expect.that(newCenterY).isGreaterThan(currentCenterY); + expect.that(newCenterY - currentCenterY).isWithin(0.01f).of(expectedStep); + + currentCenterX = newCenterX; + currentCenterY = newCenterY; + + // Release the "down" key. No more panning. + mMagnificationController.onPanMagnificationStop(MagnificationController.PAN_DIRECTION_DOWN); + + advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1); + + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isEqualTo(newCenterX); + expect.that(currentCenterY).isEqualTo(newCenterY); + } + + @Test + public void panMagnification_continuousDiagonalPanning_upLeft() throws RemoteException { + DisplayMetrics metrics = new DisplayMetrics(); + mDisplay.getMetrics(metrics); + + setMagnificationEnabled(MODE_FULLSCREEN); + mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 8.0f, false); + reset(mScreenMagnificationController); + mScreenMagnificationController.setCenter(TEST_DISPLAY, metrics.widthPixels, + metrics.heightPixels, false, 0); + + // At scale 8.0f, each step should be about 27 dip. + float expectedStep = 27.0f; + expectedStep *= metrics.density; + float expectedDiagonalStep = expectedStep / sqrt(2); + + float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + + // Start panning left. + mMagnificationController.onPanMagnificationStart(TEST_DISPLAY, + MagnificationController.PAN_DIRECTION_LEFT); + + // A step left should be taken immediately. + float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isGreaterThan(newCenterX); + expect.that(currentCenterX - newCenterX).isWithin(0.01f).of(expectedStep); + expect.that(currentCenterY).isEqualTo(newCenterY); + + currentCenterX = newCenterX; + currentCenterY = newCenterY; + + // Start panning up after a moment. + advanceTime(10); + mMagnificationController.onPanMagnificationStart(TEST_DISPLAY, + MagnificationController.PAN_DIRECTION_UP); + + // The diagonal step should be taken immediately. + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isGreaterThan(newCenterX); + expect.that(currentCenterX - newCenterX).isWithin(0.01f).of(expectedDiagonalStep); + expect.that(newCenterY).isLessThan(currentCenterY); + expect.that(currentCenterY - newCenterY).isWithin(0.01f).of(expectedDiagonalStep); + + currentCenterX = newCenterX; + currentCenterY = newCenterY; + + // If we wait for the timeout from the initial pan start, we will move diagonally again. + advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs() - 10); + + for (int i = 0; i < 3; i++) { + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isGreaterThan(newCenterX); + expect.that(currentCenterX - newCenterX).isWithin(0.01f).of(expectedDiagonalStep); + expect.that(newCenterY).isLessThan(currentCenterY); + expect.that(currentCenterY - newCenterY).isWithin(0.01f).of(expectedDiagonalStep); + + currentCenterX = newCenterX; + currentCenterY = newCenterY; + + if (i < 2) { + advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1); + } + } + + // Release the "left" key. Should continue panning up only at the full step size. + mMagnificationController.onPanMagnificationStop(MagnificationController.PAN_DIRECTION_LEFT); + advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1); + + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isEqualTo(newCenterX); + expect.that(newCenterY).isLessThan(currentCenterY); + expect.that(currentCenterY - newCenterY).isWithin(0.01f).of(expectedStep); + + currentCenterX = newCenterX; + currentCenterY = newCenterY; + + // Release the "up" key. No more panning. + mMagnificationController.onPanMagnificationStop(MagnificationController.PAN_DIRECTION_UP); + + advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1); + + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isEqualTo(newCenterX); + expect.that(currentCenterY).isEqualTo(newCenterY); + } + + @Test + public void panMagnification_directionsCancel_leftRight() throws RemoteException { + setMagnificationEnabled(MODE_FULLSCREEN); + mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 2.0f, false); + reset(mScreenMagnificationController); + + float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + + // Start panning right. + mMagnificationController.onPanMagnificationStart(TEST_DISPLAY, + MagnificationController.PAN_DIRECTION_RIGHT); + + // A step right should be taken immediately. + float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isLessThan(newCenterX); + expect.that(currentCenterY).isEqualTo(newCenterY); + + currentCenterX = newCenterX; + currentCenterY = newCenterY; + + // Start panning left. Nothing should happen as the two directions cancel. + mMagnificationController.onPanMagnificationStart(TEST_DISPLAY, + MagnificationController.PAN_DIRECTION_LEFT); + + for (int i = 0; i < 3; i++) { + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isEqualTo(newCenterX); + expect.that(currentCenterY).isEqualTo(newCenterY); + advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs()); + } + + // Now stop going right. + mMagnificationController.onPanMagnificationStop( + MagnificationController.PAN_DIRECTION_RIGHT); + advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1); + + // After the timeout, we've gone left since it is still held down. + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isGreaterThan(newCenterX); + expect.that(currentCenterY).isEqualTo(newCenterY); + + mMagnificationController.onPanMagnificationStop( + MagnificationController.PAN_DIRECTION_RIGHT); + } + + @Test + public void panMagnification_directionsCancel_upDown() throws RemoteException { + setMagnificationEnabled(MODE_FULLSCREEN); + mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 2.0f, false); + reset(mScreenMagnificationController); + + float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + + // Start panning down. + mMagnificationController.onPanMagnificationStart(TEST_DISPLAY, + MagnificationController.PAN_DIRECTION_DOWN); + + // A step down should be taken immediately. + float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isEqualTo(newCenterX); + expect.that(currentCenterY).isLessThan(newCenterY); + + currentCenterX = newCenterX; + currentCenterY = newCenterY; + + // Start panning up. Nothing should happen no matter how long we wait, + // as the two directions cancel. + mMagnificationController.onPanMagnificationStart(TEST_DISPLAY, + MagnificationController.PAN_DIRECTION_UP); + + for (int i = 0; i < 3; i++) { + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isEqualTo(newCenterX); + expect.that(currentCenterY).isEqualTo(newCenterY); + advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs()); + } + + // Now stop going up. + mMagnificationController.onPanMagnificationStop(MagnificationController.PAN_DIRECTION_UP); + advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1); + + // After the timeout, we've gone down again since it is still held down. + newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isEqualTo(newCenterX); + expect.that(currentCenterY).isLessThan(newCenterY); + + mMagnificationController.onPanMagnificationStop(MagnificationController.PAN_DIRECTION_DOWN); + } + + @Test + public void panMagnification_directionsCancel_upDownRight() throws RemoteException { + setMagnificationEnabled(MODE_FULLSCREEN); + mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 2.0f, false); + reset(mScreenMagnificationController); + + // Start panning down and up. + mMagnificationController.onPanMagnificationStart(TEST_DISPLAY, + MagnificationController.PAN_DIRECTION_DOWN); + mMagnificationController.onPanMagnificationStart(TEST_DISPLAY, + MagnificationController.PAN_DIRECTION_UP); + + float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + + // Start panning right. Only the x direction should change, not y, which cancel out. + mMagnificationController.onPanMagnificationStart(TEST_DISPLAY, + MagnificationController.PAN_DIRECTION_RIGHT); + float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); + float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY); + expect.that(currentCenterX).isLessThan(newCenterX); + expect.that(currentCenterY).isEqualTo(newCenterY); + + mMagnificationController.onPanMagnificationStop(MagnificationController.PAN_DIRECTION_UP); + mMagnificationController.onPanMagnificationStop(MagnificationController.PAN_DIRECTION_DOWN); + mMagnificationController.onPanMagnificationStop( + MagnificationController.PAN_DIRECTION_RIGHT); + } + + @Test public void enableWindowMode_notifyMagnificationChanged() throws RemoteException { setMagnificationEnabled(MODE_WINDOW); @@ -1727,7 +2246,7 @@ public class MagnificationControllerTest { expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep); expect.that(currentCenterY).isEqualTo(newCenterY); - mMagnificationController.onPanMagnificationStop(TEST_DISPLAY, + mMagnificationController.onPanMagnificationStop( MagnificationController.PAN_DIRECTION_RIGHT); currentCenterX = newCenterX; currentCenterY = newCenterY; @@ -1741,8 +2260,7 @@ public class MagnificationControllerTest { expect.that(currentCenterX - newCenterX).isWithin(0.01f).of(expectedStep); expect.that(currentCenterY).isEqualTo(newCenterY); - mMagnificationController.onPanMagnificationStop(TEST_DISPLAY, - MagnificationController.PAN_DIRECTION_LEFT); + mMagnificationController.onPanMagnificationStop(MagnificationController.PAN_DIRECTION_LEFT); currentCenterX = newCenterX; currentCenterY = newCenterY; @@ -1755,8 +2273,7 @@ public class MagnificationControllerTest { expect.that(currentCenterY).isLessThan(newCenterY); expect.that(newCenterY - currentCenterY).isWithin(0.1f).of(expectedStep); - mMagnificationController.onPanMagnificationStop(TEST_DISPLAY, - MagnificationController.PAN_DIRECTION_DOWN); + mMagnificationController.onPanMagnificationStop(MagnificationController.PAN_DIRECTION_DOWN); currentCenterX = newCenterX; currentCenterY = newCenterY; @@ -1769,8 +2286,7 @@ public class MagnificationControllerTest { expect.that(currentCenterY).isGreaterThan(newCenterY); expect.that(currentCenterY - newCenterY).isWithin(0.01f).of(expectedStep); - mMagnificationController.onPanMagnificationStop(TEST_DISPLAY, - MagnificationController.PAN_DIRECTION_UP); + mMagnificationController.onPanMagnificationStop(MagnificationController.PAN_DIRECTION_UP); } private void testWindowMagnificationPanWithStepSize(float expectedStepDip) @@ -1784,7 +2300,7 @@ public class MagnificationControllerTest { MagnificationController.PAN_DIRECTION_RIGHT); verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY), floatThat(step -> Math.abs(step - expectedStep) < 0.0001), eq(0.0f)); - mMagnificationController.onPanMagnificationStop(TEST_DISPLAY, + mMagnificationController.onPanMagnificationStop( MagnificationController.PAN_DIRECTION_RIGHT); // Move left. @@ -1792,7 +2308,7 @@ public class MagnificationControllerTest { MagnificationController.PAN_DIRECTION_LEFT); verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY), floatThat(step -> Math.abs(expectedStep - step) < 0.0001), eq(0.0f)); - mMagnificationController.onPanMagnificationStop(TEST_DISPLAY, + mMagnificationController.onPanMagnificationStop( MagnificationController.PAN_DIRECTION_LEFT); // Move down. @@ -1800,7 +2316,7 @@ public class MagnificationControllerTest { MagnificationController.PAN_DIRECTION_DOWN); verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY), eq(0.0f), floatThat(step -> Math.abs(expectedStep - step) < 0.0001)); - mMagnificationController.onPanMagnificationStop(TEST_DISPLAY, + mMagnificationController.onPanMagnificationStop( MagnificationController.PAN_DIRECTION_DOWN); // Move up. @@ -1808,7 +2324,7 @@ public class MagnificationControllerTest { MagnificationController.PAN_DIRECTION_UP); verify(mMockConnection.getConnection()).moveWindowMagnifier(eq(TEST_DISPLAY), eq(0.0f), floatThat(step -> Math.abs(expectedStep - step) < 0.0001)); - mMagnificationController.onPanMagnificationStop(TEST_DISPLAY, + mMagnificationController.onPanMagnificationStop( MagnificationController.PAN_DIRECTION_UP); } @@ -1824,6 +2340,7 @@ public class MagnificationControllerTest { DisplayMetrics metrics = new DisplayMetrics(); mDisplay.getMetrics(metrics); + // At scale 8.0f, each step should be about 27 dip. float expectedStep = 27 * metrics.density; float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY); @@ -1869,7 +2386,7 @@ public class MagnificationControllerTest { } // Stop magnification pan. - mMagnificationController.onPanMagnificationStop(TEST_DISPLAY, + mMagnificationController.onPanMagnificationStop( MagnificationController.PAN_DIRECTION_RIGHT); // It should not move again, even after the appropriate delay. @@ -1882,6 +2399,7 @@ public class MagnificationControllerTest { } private void advanceTime(long timeMs) { + mSystemClock.advanceTime(timeMs); mTestLooper.moveTimeForward(timeMs); mTestLooper.dispatchAll(); } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationKeyHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationKeyHandlerTest.java index 1c85086e2f42..2a623fbef7d3 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationKeyHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationKeyHandlerTest.java @@ -44,6 +44,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; /** @@ -78,9 +79,10 @@ public class MagnificationKeyHandlerTest { // No callbacks were called. verify(mCallback, times(0)).onPanMagnificationStart(anyInt(), anyInt()); - verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt()); + verify(mCallback, times(0)).onPanMagnificationStop(anyInt()); verify(mCallback, times(0)).onScaleMagnificationStart(anyInt(), anyInt()); - verify(mCallback, times(0)).onScaleMagnificationStop(anyInt(), anyInt()); + verify(mCallback, times(0)).onScaleMagnificationStop(anyInt()); + verify(mCallback, times(0)).onKeyboardInteractionStop(); // The event was passed on. verify(mNextHandler, times(1)).onKeyEvent(event, 0); @@ -95,9 +97,10 @@ public class MagnificationKeyHandlerTest { // No callbacks were called. verify(mCallback, times(0)).onPanMagnificationStart(anyInt(), anyInt()); - verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt()); + verify(mCallback, times(0)).onPanMagnificationStop(anyInt()); verify(mCallback, times(0)).onScaleMagnificationStart(anyInt(), anyInt()); - verify(mCallback, times(0)).onScaleMagnificationStop(anyInt(), anyInt()); + verify(mCallback, times(0)).onScaleMagnificationStop(anyInt()); + verify(mCallback, times(0)).onKeyboardInteractionStop(); // The event was passed on. verify(mNextHandler, times(1)).onKeyEvent(event, 0); @@ -112,9 +115,10 @@ public class MagnificationKeyHandlerTest { // No callbacks were called. verify(mCallback, times(0)).onPanMagnificationStart(anyInt(), anyInt()); - verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt()); + verify(mCallback, times(0)).onPanMagnificationStop(anyInt()); verify(mCallback, times(0)).onScaleMagnificationStart(anyInt(), anyInt()); - verify(mCallback, times(0)).onScaleMagnificationStop(anyInt(), anyInt()); + verify(mCallback, times(0)).onScaleMagnificationStop(anyInt()); + verify(mCallback, times(0)).onKeyboardInteractionStop(); // The event was passed on. verify(mNextHandler, times(1)).onKeyEvent(event, 0); @@ -157,48 +161,79 @@ public class MagnificationKeyHandlerTest { mMkh.onKeyEvent(downLeftEvent, 0); verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, PAN_DIRECTION_LEFT); - verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt()); + verify(mCallback, times(0)).onPanMagnificationStop(anyInt()); + + Mockito.clearInvocations(mCallback); // Also press the down arrow key. final KeyEvent downDownEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN, 0, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON); mMkh.onKeyEvent(downDownEvent, 0); - verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, + verify(mCallback, times(0)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, PAN_DIRECTION_LEFT); verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, PAN_DIRECTION_DOWN); - verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt()); + verify(mCallback, times(0)).onPanMagnificationStop(anyInt()); + + Mockito.clearInvocations(mCallback); // Lift the left arrow key. final KeyEvent upLeftEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_LEFT, 0, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON); mMkh.onKeyEvent(upLeftEvent, 0); - verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, + verify(mCallback, times(0)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, PAN_DIRECTION_LEFT); - verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, - PAN_DIRECTION_DOWN); - verify(mCallback, times(1)).onPanMagnificationStop(Display.DEFAULT_DISPLAY, - PAN_DIRECTION_LEFT); - verify(mCallback, times(0)).onPanMagnificationStop(Display.DEFAULT_DISPLAY, + verify(mCallback, times(0)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, PAN_DIRECTION_DOWN); + verify(mCallback, times(1)).onPanMagnificationStop(PAN_DIRECTION_LEFT); + verify(mCallback, times(0)).onPanMagnificationStop(PAN_DIRECTION_DOWN); + + Mockito.clearInvocations(mCallback); // Lift the down arrow key. final KeyEvent upDownEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_DOWN, 0, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON); mMkh.onKeyEvent(upDownEvent, 0); - verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, + verify(mCallback, times(0)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, PAN_DIRECTION_LEFT); - verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, - PAN_DIRECTION_DOWN); - verify(mCallback, times(1)).onPanMagnificationStop(Display.DEFAULT_DISPLAY, - PAN_DIRECTION_LEFT); - verify(mCallback, times(1)).onPanMagnificationStop(Display.DEFAULT_DISPLAY, + verify(mCallback, times(0)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, PAN_DIRECTION_DOWN); + verify(mCallback, times(0)).onPanMagnificationStop(PAN_DIRECTION_LEFT); + verify(mCallback, times(1)).onPanMagnificationStop(PAN_DIRECTION_DOWN); // The event was not passed on. verify(mNextHandler, times(0)).onKeyEvent(any(), anyInt()); } + @Test + public void testPanMagnification_modifiersReleasedBeforeArrows() { + final KeyEvent downEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, + KeyEvent.KEYCODE_DPAD_DOWN, 0, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON); + mMkh.onKeyEvent(downEvent, 0); + + // Pan started. + verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, + PAN_DIRECTION_DOWN); + verify(mCallback, times(0)).onPanMagnificationStop(anyInt()); + verify(mCallback, times(0)).onKeyboardInteractionStop(); + + Mockito.clearInvocations(mCallback); + + // Lift the "meta" key. + final KeyEvent upEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_META_LEFT, + 0, + KeyEvent.META_ALT_ON); + mMkh.onKeyEvent(upEvent, 0); + + // Pan ended. + verify(mCallback, times(0)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, + PAN_DIRECTION_DOWN); + verify(mCallback, times(0)).onPanMagnificationStop(anyInt()); + verify(mCallback, times(1)).onKeyboardInteractionStop(); + + } + private void testPanMagnification(int keyCode, int panDirection) { final KeyEvent downEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, keyCode, 0, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON); @@ -206,19 +241,21 @@ public class MagnificationKeyHandlerTest { // Pan started. verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, panDirection); - verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt()); + verify(mCallback, times(0)).onPanMagnificationStop(anyInt()); + + Mockito.clearInvocations(mCallback); final KeyEvent upEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP, keyCode, 0, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON); mMkh.onKeyEvent(upEvent, 0); // Pan ended. - verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, panDirection); - verify(mCallback, times(1)).onPanMagnificationStop(Display.DEFAULT_DISPLAY, panDirection); + verify(mCallback, times(0)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, panDirection); + verify(mCallback, times(1)).onPanMagnificationStop(panDirection); // Scale callbacks were not called. verify(mCallback, times(0)).onScaleMagnificationStart(anyInt(), anyInt()); - verify(mCallback, times(0)).onScaleMagnificationStop(anyInt(), anyInt()); + verify(mCallback, times(0)).onScaleMagnificationStop(anyInt()); // The events were not passed on. verify(mNextHandler, times(0)).onKeyEvent(any(), anyInt()); @@ -232,25 +269,25 @@ public class MagnificationKeyHandlerTest { // Scale started. verify(mCallback, times(1)).onScaleMagnificationStart(Display.DEFAULT_DISPLAY, zoomDirection); - verify(mCallback, times(0)).onScaleMagnificationStop(anyInt(), anyInt()); + verify(mCallback, times(0)).onScaleMagnificationStop(anyInt()); + + Mockito.clearInvocations(mCallback); final KeyEvent upEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP, keyCode, 0, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON); mMkh.onKeyEvent(upEvent, 0); // Scale ended. - verify(mCallback, times(1)).onScaleMagnificationStart(Display.DEFAULT_DISPLAY, - zoomDirection); - verify(mCallback, times(1)).onScaleMagnificationStop(Display.DEFAULT_DISPLAY, + verify(mCallback, times(0)).onScaleMagnificationStart(Display.DEFAULT_DISPLAY, zoomDirection); + verify(mCallback, times(1)).onScaleMagnificationStop(zoomDirection); // Pan callbacks were not called. verify(mCallback, times(0)).onPanMagnificationStart(anyInt(), anyInt()); - verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt()); + verify(mCallback, times(0)).onPanMagnificationStop(anyInt()); // The events were not passed on. verify(mNextHandler, times(0)).onKeyEvent(any(), anyInt()); - } } |