Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3c730b71 authored by Katie Dektar's avatar Katie Dektar
Browse files

Magnification pans diagonally with same radius as straight.

Also fixes bug where modifier keys lifted before arrow
or +/- keys didn't stop scaling or zooming.

Bug:388847283
Test: atest MagnificationControllerTest
Flag: com.android.server.accessibility.enable_magnification_keyboard_control

Change-Id: Ia8e28d12f152885263e05b349edccff1fb5fd282
parent c533272a
Loading
Loading
Loading
Loading
+117 −36
Original line number Original line Diff line number Diff line
@@ -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_FULLSCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
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;
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.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.Handler;
import android.os.Looper;
import android.os.Looper;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.DisplayMetrics;
@@ -116,8 +116,14 @@ public class MagnificationController implements MagnificationConnectionManager.C
    private final Executor mBackgroundExecutor;
    private final Executor mBackgroundExecutor;


    private final Handler mHandler;
    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;
    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 boolean mRepeatKeysEnabled = true;


    private @ZoomDirection int mActiveZoomDirection = ZOOM_DIRECTION_IN;
    private @ZoomDirection int mActiveZoomDirection = ZOOM_DIRECTION_IN;
@@ -186,6 +192,25 @@ public class MagnificationController implements MagnificationConnectionManager.C
        void onResult(int displayId, boolean success);
        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
     * 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;
        mScaleProvider = scaleProvider;
        mBackgroundExecutor = backgroundExecutor;
        mBackgroundExecutor = backgroundExecutor;
        mHandler = new Handler(looper);
        mHandler = new Handler(looper);
        mSystemClock = new SystemClockImpl();
        LocalServices.getService(WindowManagerInternal.class)
        LocalServices.getService(WindowManagerInternal.class)
                .getAccessibilityController().setUiChangesForAccessibilityCallbacks(this);
                .getAccessibilityController().setUiChangesForAccessibilityCallbacks(this);
        mSupportWindowMagnification = context.getPackageManager().hasSystemFeature(
        mSupportWindowMagnification = context.getPackageManager().hasSystemFeature(
@@ -327,10 +353,12 @@ public class MagnificationController implements MagnificationConnectionManager.C
    public MagnificationController(AccessibilityManagerService ams, Object lock,
    public MagnificationController(AccessibilityManagerService ams, Object lock,
            Context context, FullScreenMagnificationController fullScreenMagnificationController,
            Context context, FullScreenMagnificationController fullScreenMagnificationController,
            MagnificationConnectionManager magnificationConnectionManager,
            MagnificationConnectionManager magnificationConnectionManager,
            MagnificationScaleProvider scaleProvider, Executor backgroundExecutor, Looper looper) {
            MagnificationScaleProvider scaleProvider, Executor backgroundExecutor, Looper looper,
            SystemClock systemClock) {
        this(ams, lock, context, scaleProvider, backgroundExecutor, looper);
        this(ams, lock, context, scaleProvider, backgroundExecutor, looper);
        mFullScreenMagnificationController = fullScreenMagnificationController;
        mFullScreenMagnificationController = fullScreenMagnificationController;
        mMagnificationConnectionManager = magnificationConnectionManager;
        mMagnificationConnectionManager = magnificationConnectionManager;
        mSystemClock = systemClock;
    }
    }


    @Override
    @Override
@@ -368,13 +396,13 @@ public class MagnificationController implements MagnificationConnectionManager.C
    @Override
    @Override
    public void onPanMagnificationStart(int displayId,
    public void onPanMagnificationStart(int displayId,
            @MagnificationController.PanDirection int direction) {
            @MagnificationController.PanDirection int direction) {
        // TODO(b/355499907): Handle multiple pan gestures at the same time (e.g. user may try to
        // Update the current panning state for any callbacks.
        // pan diagonally) by decreasing diagonal movement by sqrt(2) to make it appear the same
        boolean isAlreadyPanning = mActivePanDisplay != Display.INVALID_DISPLAY;
        // speed as non-diagonal movement.
        panMagnificationByStep(displayId, direction);
        mActivePanDirection = direction;
        mActivePanDisplay = displayId;
        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(
            mHandler.sendMessageDelayed(
                    PooledLambda.obtainMessage(MagnificationController::maybeContinuePan, this),
                    PooledLambda.obtainMessage(MagnificationController::maybeContinuePan, this),
                    mInitialKeyboardRepeatIntervalMs);
                    mInitialKeyboardRepeatIntervalMs);
@@ -382,9 +410,14 @@ public class MagnificationController implements MagnificationConnectionManager.C
    }
    }


    @Override
    @Override
    public void onPanMagnificationStop(int displayId,
    public void onPanMagnificationStop(@MagnificationController.PanDirection int direction) {
            @MagnificationController.PanDirection int direction) {
        // Stop panning in this direction.
        if (direction == mActivePanDirection) {
        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;
            mActivePanDisplay = Display.INVALID_DISPLAY;
        }
        }
    }
    }
@@ -392,9 +425,14 @@ public class MagnificationController implements MagnificationConnectionManager.C
    @Override
    @Override
    public void onScaleMagnificationStart(int displayId,
    public void onScaleMagnificationStart(int displayId,
            @MagnificationController.ZoomDirection int direction) {
            @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;
        mActiveZoomDirection = direction;
        mActiveZoomDisplay = displayId;
        mActiveZoomDisplay = displayId;
        scaleMagnificationByStep(displayId, direction);
        if (mRepeatKeysEnabled) {
        if (mRepeatKeysEnabled) {
            mHandler.sendMessageDelayed(
            mHandler.sendMessageDelayed(
                    PooledLambda.obtainMessage(MagnificationController::maybeContinueZoom, this),
                    PooledLambda.obtainMessage(MagnificationController::maybeContinueZoom, this),
@@ -403,16 +441,27 @@ public class MagnificationController implements MagnificationConnectionManager.C
    }
    }


    @Override
    @Override
    public void onScaleMagnificationStop(int displayId,
    public void onScaleMagnificationStop(@MagnificationController.ZoomDirection int direction) {
            @MagnificationController.ZoomDirection int direction) {
        if (direction == mActiveZoomDirection) {
        if (direction == mActiveZoomDirection) {
            mActiveZoomDisplay = Display.INVALID_DISPLAY;
            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() {
    private void maybeContinuePan() {
        if (mActivePanDisplay != Display.INVALID_DISPLAY) {
        if (mActivePanDisplay == Display.INVALID_DISPLAY) {
            panMagnificationByStep(mActivePanDisplay, mActivePanDirection);
            return;
        }
        if (mSystemClock.uptimeMillis() - mLastPannedTime >= KEYBOARD_REPEAT_INTERVAL_MS) {
            panMagnificationByStep(mActivePanDisplay, mActivePanDirections);
        }
        if (mRepeatKeysEnabled) {
            mHandler.sendMessageDelayed(
            mHandler.sendMessageDelayed(
                    PooledLambda.obtainMessage(MagnificationController::maybeContinuePan, this),
                    PooledLambda.obtainMessage(MagnificationController::maybeContinuePan, this),
                    KEYBOARD_REPEAT_INTERVAL_MS);
                    KEYBOARD_REPEAT_INTERVAL_MS);
@@ -422,11 +471,14 @@ public class MagnificationController implements MagnificationConnectionManager.C
    private void maybeContinueZoom() {
    private void maybeContinueZoom() {
        if (mActiveZoomDisplay != Display.INVALID_DISPLAY) {
        if (mActiveZoomDisplay != Display.INVALID_DISPLAY) {
            scaleMagnificationByStep(mActiveZoomDisplay, mActiveZoomDirection);
            scaleMagnificationByStep(mActiveZoomDisplay, mActiveZoomDirection);
            if (mRepeatKeysEnabled) {
                mHandler.sendMessageDelayed(
                mHandler.sendMessageDelayed(
                    PooledLambda.obtainMessage(MagnificationController::maybeContinueZoom, this),
                        PooledLambda.obtainMessage(MagnificationController::maybeContinueZoom,
                                this),
                        KEYBOARD_REPEAT_INTERVAL_MS);
                        KEYBOARD_REPEAT_INTERVAL_MS);
            }
            }
        }
        }
    }


    public void setRepeatKeysEnabled(boolean isRepeatKeysEnabled) {
    public void setRepeatKeysEnabled(boolean isRepeatKeysEnabled) {
        mRepeatKeysEnabled = isRepeatKeysEnabled;
        mRepeatKeysEnabled = isRepeatKeysEnabled;
@@ -719,7 +771,7 @@ public class MagnificationController implements MagnificationConnectionManager.C
    public void onWindowMagnificationActivationState(int displayId, boolean activated) {
    public void onWindowMagnificationActivationState(int displayId, boolean activated) {
        if (activated) {
        if (activated) {
            synchronized (mLock) {
            synchronized (mLock) {
                mWindowModeEnabledTimeArray.put(displayId, SystemClock.uptimeMillis());
                mWindowModeEnabledTimeArray.put(displayId, mSystemClock.uptimeMillis());
                setCurrentMagnificationModeAndSwitchDelegate(displayId,
                setCurrentMagnificationModeAndSwitchDelegate(displayId,
                        ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
                        ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
                mLastMagnificationActivatedModeArray.put(displayId,
                mLastMagnificationActivatedModeArray.put(displayId,
@@ -733,7 +785,7 @@ public class MagnificationController implements MagnificationConnectionManager.C
            synchronized (mLock) {
            synchronized (mLock) {
                setCurrentMagnificationModeAndSwitchDelegate(displayId,
                setCurrentMagnificationModeAndSwitchDelegate(displayId,
                        ACCESSIBILITY_MAGNIFICATION_MODE_NONE);
                        ACCESSIBILITY_MAGNIFICATION_MODE_NONE);
                duration = SystemClock.uptimeMillis() - mWindowModeEnabledTimeArray.get(displayId);
                duration = mSystemClock.uptimeMillis() - mWindowModeEnabledTimeArray.get(displayId);
                scale = mMagnificationConnectionManager.getLastActivatedScale(displayId);
                scale = mMagnificationConnectionManager.getLastActivatedScale(displayId);
            }
            }
            logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, duration, scale);
            logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, duration, scale);
@@ -830,7 +882,7 @@ public class MagnificationController implements MagnificationConnectionManager.C


        if (activated) {
        if (activated) {
            synchronized (mLock) {
            synchronized (mLock) {
                mFullScreenModeEnabledTimeArray.put(displayId, SystemClock.uptimeMillis());
                mFullScreenModeEnabledTimeArray.put(displayId, mSystemClock.uptimeMillis());
                setCurrentMagnificationModeAndSwitchDelegate(displayId,
                setCurrentMagnificationModeAndSwitchDelegate(displayId,
                        ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
                        ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
                mLastMagnificationActivatedModeArray.put(displayId,
                mLastMagnificationActivatedModeArray.put(displayId,
@@ -844,7 +896,7 @@ public class MagnificationController implements MagnificationConnectionManager.C
            synchronized (mLock) {
            synchronized (mLock) {
                setCurrentMagnificationModeAndSwitchDelegate(displayId,
                setCurrentMagnificationModeAndSwitchDelegate(displayId,
                        ACCESSIBILITY_MAGNIFICATION_MODE_NONE);
                        ACCESSIBILITY_MAGNIFICATION_MODE_NONE);
                duration = SystemClock.uptimeMillis()
                duration = mSystemClock.uptimeMillis()
                        - mFullScreenModeEnabledTimeArray.get(displayId);
                        - mFullScreenModeEnabledTimeArray.get(displayId);
                scale = mFullScreenMagnificationController.getLastActivatedScale(displayId);
                scale = mFullScreenMagnificationController.getLastActivatedScale(displayId);
            }
            }
@@ -1132,7 +1184,7 @@ public class MagnificationController implements MagnificationConnectionManager.C
     * @param displayId The logical display id.
     * @param displayId The logical display id.
     * @param direction Whether the scale should be zoomed in or out.
     * @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)) {
        if (getFullScreenMagnificationController().isActivated(displayId)) {
            final float magnificationScale = getFullScreenMagnificationController().getScale(
            final float magnificationScale = getFullScreenMagnificationController().getScale(
                    displayId);
                    displayId);
@@ -1157,9 +1209,14 @@ public class MagnificationController implements MagnificationConnectionManager.C
     * param.
     * param.
     *
     *
     * @param displayId The logical display id.
     * @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 =
        final boolean fullscreenActivated =
                getFullScreenMagnificationController().isActivated(displayId);
                getFullScreenMagnificationController().isActivated(displayId);
        final boolean windowActivated =
        final boolean windowActivated =
@@ -1168,21 +1225,43 @@ public class MagnificationController implements MagnificationConnectionManager.C
            return;
            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
        final float scale = fullscreenActivated
                ? getFullScreenMagnificationController().getScale(displayId)
                ? getFullScreenMagnificationController().getScale(displayId)
                        : getMagnificationConnectionManager().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 offsetX = 0;
        float offsetY = 0;
        float offsetY = 0;
        if (direction == PAN_DIRECTION_LEFT) {
        if (directions[PAN_DIRECTION_LEFT]) {
            offsetX = -step;
            offsetX -= step;
        } else if (direction == PAN_DIRECTION_RIGHT) {
        }
            offsetX = step;
        if (directions[PAN_DIRECTION_RIGHT]) {
        } else if (direction == PAN_DIRECTION_UP) {
            offsetX += step;
            offsetY = -step;
        }
        } else if (direction == PAN_DIRECTION_DOWN) {
        if (directions[PAN_DIRECTION_UP]) {
            offsetY = step;
            offsetY -= step;
        }
        if (directions[PAN_DIRECTION_DOWN]) {
            offsetY += step;
        }
        }


        if (fullscreenActivated) {
        if (fullscreenActivated) {
@@ -1194,6 +1273,8 @@ public class MagnificationController implements MagnificationConnectionManager.C
            getMagnificationConnectionManager().moveWindowMagnification(displayId, offsetX,
            getMagnificationConnectionManager().moveWindowMagnification(displayId, offsetX,
                    offsetY);
                    offsetY);
        }
        }

        mLastPannedTime = mSystemClock.uptimeMillis();
    }
    }


    private final class DisableMagnificationCallback implements
    private final class DisableMagnificationCallback implements
+18 −8
Original line number Original line Diff line number Diff line
@@ -46,10 +46,8 @@ public class MagnificationKeyHandler extends BaseEventStreamTransformation {
         * arrows had been pressed at the same time (e.g. diagonal panning).
         * arrows had been pressed at the same time (e.g. diagonal panning).
         *
         *
         * @param displayId The logical display ID
         * @param displayId The logical display ID
         * @param direction The direction in which panning stopped
         */
         */
        void onPanMagnificationStop(int displayId,
        void onPanMagnificationStop(int displayId);
                @MagnificationController.PanDirection int direction);


        /**
        /**
         * Called when a keyboard shortcut to scale magnification in direction `direction` is
         * 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
         * Called when a keyboard shortcut to scale magnification in direction `direction` is
         * unpressed by a user.
         * unpressed by a user.
         *
         *
         * @param displayId The logical display ID
         * @param direction The direction in which scaling stopped
         * @param direction The direction in which scaling stopped
         */
         */
        void onScaleMagnificationStop(int displayId,
        void onScaleMagnificationStop(@MagnificationController.ZoomDirection int direction);
                @MagnificationController.ZoomDirection int direction);

        /**
         * Called when all keyboard interaction with magnification should be stopped.
         */
        void onKeyboardInteractionStop();
    }
    }


    protected final MagnificationKeyHandler.Callback mCallback;
    protected final MagnificationKeyHandler.Callback mCallback;
    private boolean mIsKeyboardInteracting = false;


    public MagnificationKeyHandler(Callback callback) {
    public MagnificationKeyHandler(Callback callback) {
        mCallback = callback;
        mCallback = callback;
@@ -88,6 +90,12 @@ public class MagnificationKeyHandler extends BaseEventStreamTransformation {
        boolean modifiersPressed = event.isAltPressed() && event.isMetaPressed();
        boolean modifiersPressed = event.isAltPressed() && event.isMetaPressed();
        if (!modifiersPressed) {
        if (!modifiersPressed) {
            super.onKeyEvent(event, policyFlags);
            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;
            return;
        }
        }
        boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN;
        boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN;
@@ -102,8 +110,9 @@ public class MagnificationKeyHandler extends BaseEventStreamTransformation {
            };
            };
            if (isDown) {
            if (isDown) {
                mCallback.onPanMagnificationStart(getDisplayId(event), panDirection);
                mCallback.onPanMagnificationStart(getDisplayId(event), panDirection);
                mIsKeyboardInteracting = true;
            } else {
            } else {
                mCallback.onPanMagnificationStop(getDisplayId(event), panDirection);
                mCallback.onPanMagnificationStop(panDirection);
            }
            }
            return;
            return;
        } else if (keyCode == KeyEvent.KEYCODE_EQUALS || keyCode == KeyEvent.KEYCODE_MINUS) {
        } else if (keyCode == KeyEvent.KEYCODE_EQUALS || keyCode == KeyEvent.KEYCODE_MINUS) {
@@ -113,8 +122,9 @@ public class MagnificationKeyHandler extends BaseEventStreamTransformation {
            }
            }
            if (isDown) {
            if (isDown) {
                mCallback.onScaleMagnificationStart(getDisplayId(event), zoomDirection);
                mCallback.onScaleMagnificationStart(getDisplayId(event), zoomDirection);
                mIsKeyboardInteracting = true;
            } else {
            } else {
                mCallback.onScaleMagnificationStop(getDisplayId(event), zoomDirection);
                mCallback.onScaleMagnificationStop(zoomDirection);
            }
            }
            return;
            return;
        }
        }
+566 −48

File changed.

Preview size limit exceeded, changes collapsed.

+68 −31
Original line number Original line Diff line number Diff line
@@ -44,6 +44,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoAnnotations;


/**
/**
@@ -78,9 +79,10 @@ public class MagnificationKeyHandlerTest {


        // No callbacks were called.
        // No callbacks were called.
        verify(mCallback, times(0)).onPanMagnificationStart(anyInt(), anyInt());
        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)).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.
        // The event was passed on.
        verify(mNextHandler, times(1)).onKeyEvent(event, 0);
        verify(mNextHandler, times(1)).onKeyEvent(event, 0);
@@ -95,9 +97,10 @@ public class MagnificationKeyHandlerTest {


        // No callbacks were called.
        // No callbacks were called.
        verify(mCallback, times(0)).onPanMagnificationStart(anyInt(), anyInt());
        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)).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.
        // The event was passed on.
        verify(mNextHandler, times(1)).onKeyEvent(event, 0);
        verify(mNextHandler, times(1)).onKeyEvent(event, 0);
@@ -112,9 +115,10 @@ public class MagnificationKeyHandlerTest {


        // No callbacks were called.
        // No callbacks were called.
        verify(mCallback, times(0)).onPanMagnificationStart(anyInt(), anyInt());
        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)).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.
        // The event was passed on.
        verify(mNextHandler, times(1)).onKeyEvent(event, 0);
        verify(mNextHandler, times(1)).onKeyEvent(event, 0);
@@ -157,48 +161,79 @@ public class MagnificationKeyHandlerTest {
        mMkh.onKeyEvent(downLeftEvent, 0);
        mMkh.onKeyEvent(downLeftEvent, 0);
        verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
        verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
                PAN_DIRECTION_LEFT);
                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.
        // Also press the down arrow key.
        final KeyEvent downDownEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
        final KeyEvent downDownEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
                KeyEvent.KEYCODE_DPAD_DOWN, 0, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
                KeyEvent.KEYCODE_DPAD_DOWN, 0, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
        mMkh.onKeyEvent(downDownEvent, 0);
        mMkh.onKeyEvent(downDownEvent, 0);
        verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
        verify(mCallback, times(0)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
                PAN_DIRECTION_LEFT);
                PAN_DIRECTION_LEFT);
        verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
        verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
                PAN_DIRECTION_DOWN);
                PAN_DIRECTION_DOWN);
        verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt());
        verify(mCallback, times(0)).onPanMagnificationStop(anyInt());

        Mockito.clearInvocations(mCallback);


        // Lift the left arrow key.
        // Lift the left arrow key.
        final KeyEvent upLeftEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP,
        final KeyEvent upLeftEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP,
                KeyEvent.KEYCODE_DPAD_LEFT, 0, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
                KeyEvent.KEYCODE_DPAD_LEFT, 0, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
        mMkh.onKeyEvent(upLeftEvent, 0);
        mMkh.onKeyEvent(upLeftEvent, 0);
        verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
        verify(mCallback, times(0)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
                PAN_DIRECTION_LEFT);
                PAN_DIRECTION_LEFT);
        verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
        verify(mCallback, times(0)).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,
                PAN_DIRECTION_DOWN);
                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.
        // Lift the down arrow key.
        final KeyEvent upDownEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP,
        final KeyEvent upDownEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP,
                KeyEvent.KEYCODE_DPAD_DOWN, 0, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
                KeyEvent.KEYCODE_DPAD_DOWN, 0, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
        mMkh.onKeyEvent(upDownEvent, 0);
        mMkh.onKeyEvent(upDownEvent, 0);
        verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
        verify(mCallback, times(0)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
                PAN_DIRECTION_LEFT);
                PAN_DIRECTION_LEFT);
        verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY,
        verify(mCallback, times(0)).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,
                PAN_DIRECTION_DOWN);
                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.
        // The event was not passed on.
        verify(mNextHandler, times(0)).onKeyEvent(any(), anyInt());
        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) {
    private void testPanMagnification(int keyCode, int panDirection) {
        final KeyEvent downEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, keyCode, 0,
        final KeyEvent downEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, keyCode, 0,
                KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
                KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
@@ -206,19 +241,21 @@ public class MagnificationKeyHandlerTest {


        // Pan started.
        // Pan started.
        verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, panDirection);
        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,
        final KeyEvent upEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP, keyCode, 0,
                KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
                KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
        mMkh.onKeyEvent(upEvent, 0);
        mMkh.onKeyEvent(upEvent, 0);


        // Pan ended.
        // Pan ended.
        verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, panDirection);
        verify(mCallback, times(0)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, panDirection);
        verify(mCallback, times(1)).onPanMagnificationStop(Display.DEFAULT_DISPLAY, panDirection);
        verify(mCallback, times(1)).onPanMagnificationStop(panDirection);


        // Scale callbacks were not called.
        // Scale callbacks were not called.
        verify(mCallback, times(0)).onScaleMagnificationStart(anyInt(), anyInt());
        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.
        // The events were not passed on.
        verify(mNextHandler, times(0)).onKeyEvent(any(), anyInt());
        verify(mNextHandler, times(0)).onKeyEvent(any(), anyInt());
@@ -232,25 +269,25 @@ public class MagnificationKeyHandlerTest {
        // Scale started.
        // Scale started.
        verify(mCallback, times(1)).onScaleMagnificationStart(Display.DEFAULT_DISPLAY,
        verify(mCallback, times(1)).onScaleMagnificationStart(Display.DEFAULT_DISPLAY,
                zoomDirection);
                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,
        final KeyEvent upEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP, keyCode, 0,
                KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
                KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
        mMkh.onKeyEvent(upEvent, 0);
        mMkh.onKeyEvent(upEvent, 0);


        // Scale ended.
        // Scale ended.
        verify(mCallback, times(1)).onScaleMagnificationStart(Display.DEFAULT_DISPLAY,
        verify(mCallback, times(0)).onScaleMagnificationStart(Display.DEFAULT_DISPLAY,
                zoomDirection);
        verify(mCallback, times(1)).onScaleMagnificationStop(Display.DEFAULT_DISPLAY,
                zoomDirection);
                zoomDirection);
        verify(mCallback, times(1)).onScaleMagnificationStop(zoomDirection);


        // Pan callbacks were not called.
        // Pan callbacks were not called.
        verify(mCallback, times(0)).onPanMagnificationStart(anyInt(), anyInt());
        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.
        // The events were not passed on.
        verify(mNextHandler, times(0)).onKeyEvent(any(), anyInt());
        verify(mNextHandler, times(0)).onKeyEvent(any(), anyInt());

    }
    }


}
}