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

Commit 64eb92eb authored by Katie Dektar's avatar Katie Dektar
Browse files

[A11y] Magnification key handler checks if mag is active

Magnification key handler is installed when magnification is enabled,
so it steals key events whether or not magnification is active as
long as the correct keys were pressed. Adds a method to the callback
to check for magnification activated, so that key events are only
consumed when magnification is both enabled and activated.

Also adds a check that ctrl isn't held down with action and alt,
so that ctrl+action+alt+arrows doesn't get consumed.

Bug: b/410887351
Flag: EXEMPT bugfix
Test: atest MagnificationKeyHandlerTest and manual with TalkBack
Change-Id: Ief65b8b70138625148ee5254c318113174c6f0a5
parent ad562932
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -455,6 +455,12 @@ public class MagnificationController implements MagnificationConnectionManager.C
        mActivePanDirections = new boolean[]{false, false, false, false};
    }

    @Override
    public boolean isMagnificationActivated(int displayId) {
        return mFullScreenMagnificationController.isActivated(displayId)
                || getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId);
    }

    private void maybeContinuePan() {
        if (mActivePanDisplay == Display.INVALID_DISPLAY) {
            return;
+49 −21
Original line number Diff line number Diff line
@@ -71,6 +71,18 @@ public class MagnificationKeyHandler extends BaseEventStreamTransformation {
         * Called when all keyboard interaction with magnification should be stopped.
         */
        void onKeyboardInteractionStop();

        /**
         * Check for whether magnification is active on the given display. If it is not,
         * there is no need to send scale and zoom events.
         * Note that magnification may be enabled (so this handler is installed) but not
         * activated, for example when the shortcut button is shown or if an A11y service
         * using magnification is active.
         *
         * @param displayId The logical display ID
         * @return true if magnification is activated.
         */
        boolean isMagnificationActivated(int displayId);
    }

    protected final MagnificationKeyHandler.Callback mCallback;
@@ -87,7 +99,9 @@ public class MagnificationKeyHandler extends BaseEventStreamTransformation {
            super.onKeyEvent(event, policyFlags);
            return;
        }
        boolean modifiersPressed = event.isAltPressed() && event.isMetaPressed();
        // Look for exactly Alt and Meta.
        boolean modifiersPressed = event.isAltPressed() && event.isMetaPressed()
                && !event.isCtrlPressed() && !event.isShiftPressed();
        if (!modifiersPressed) {
            super.onKeyEvent(event, policyFlags);
            if (mIsKeyboardInteracting) {
@@ -98,10 +112,28 @@ public class MagnificationKeyHandler extends BaseEventStreamTransformation {
            }
            return;
        }
        boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN;
        int keyCode = event.getKeyCode();
        if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
                || keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
        boolean isArrowKeyCode = keyCode == KeyEvent.KEYCODE_DPAD_LEFT
                || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
                || keyCode == KeyEvent.KEYCODE_DPAD_UP
                || keyCode == KeyEvent.KEYCODE_DPAD_DOWN;
        boolean isZoomKeyCode = keyCode == KeyEvent.KEYCODE_EQUALS
                || keyCode == KeyEvent.KEYCODE_MINUS;
        if (!isArrowKeyCode && !isZoomKeyCode) {
            // Some other key was pressed.
            super.onKeyEvent(event, policyFlags);
            return;
        }
        int displayId = getDisplayId(event);
        // Check magnification is active only when we know we have the correct keys pressed.
        // This requires `synchronized` which is expensive to do on every key event.
        if (!mCallback.isMagnificationActivated(displayId)) {
            // Magnification isn't active.
            super.onKeyEvent(event, policyFlags);
            return;
        }
        boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN;
        if (isArrowKeyCode) {
            int panDirection = switch (keyCode) {
                case KeyEvent.KEYCODE_DPAD_LEFT -> MagnificationController.PAN_DIRECTION_LEFT;
                case KeyEvent.KEYCODE_DPAD_RIGHT -> MagnificationController.PAN_DIRECTION_RIGHT;
@@ -109,28 +141,24 @@ public class MagnificationKeyHandler extends BaseEventStreamTransformation {
                default -> MagnificationController.PAN_DIRECTION_DOWN;
            };
            if (isDown) {
                mCallback.onPanMagnificationStart(getDisplayId(event), panDirection);
                mCallback.onPanMagnificationStart(displayId, panDirection);
                mIsKeyboardInteracting = true;
            } else {
                mCallback.onPanMagnificationStop(panDirection);
            }
            return;
        } else if (keyCode == KeyEvent.KEYCODE_EQUALS || keyCode == KeyEvent.KEYCODE_MINUS) {
        }
        // Zoom key code.
        int zoomDirection = MagnificationController.ZOOM_DIRECTION_OUT;
        if (keyCode == KeyEvent.KEYCODE_EQUALS) {
            zoomDirection = MagnificationController.ZOOM_DIRECTION_IN;
        }
        if (isDown) {
                mCallback.onScaleMagnificationStart(getDisplayId(event), zoomDirection);
            mCallback.onScaleMagnificationStart(displayId, zoomDirection);
            mIsKeyboardInteracting = true;
        } else {
            mCallback.onScaleMagnificationStop(zoomDirection);
        }
            return;
        }

        // Continue down the eventing chain if this was unused.
        super.onKeyEvent(event, policyFlags);
    }

    private int getDisplayId(KeyEvent event) {
+49 −26
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
@@ -68,6 +69,7 @@ public class MagnificationKeyHandlerTest {
    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        when(mCallback.isMagnificationActivated(any(Integer.class))).thenReturn(true);
        mMkh = new MagnificationKeyHandler(mCallback);
        mMkh.setNext(mNextHandler);
    }
@@ -77,15 +79,7 @@ public class MagnificationKeyHandlerTest {
        final KeyEvent event = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_L, 0, 0);
        mMkh.onKeyEvent(event, 0);

        // No callbacks were called.
        verify(mCallback, times(0)).onPanMagnificationStart(anyInt(), anyInt());
        verify(mCallback, times(0)).onPanMagnificationStop(anyInt());
        verify(mCallback, times(0)).onScaleMagnificationStart(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);
        verifySentEventToNext(event);
    }

    @Test
@@ -95,15 +89,7 @@ public class MagnificationKeyHandlerTest {
                        0, KeyEvent.META_ALT_ON);
        mMkh.onKeyEvent(event, 0);

        // No callbacks were called.
        verify(mCallback, times(0)).onPanMagnificationStart(anyInt(), anyInt());
        verify(mCallback, times(0)).onPanMagnificationStop(anyInt());
        verify(mCallback, times(0)).onScaleMagnificationStart(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);
        verifySentEventToNext(event);
    }

    @Test
@@ -113,15 +99,41 @@ public class MagnificationKeyHandlerTest {
                        KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
        mMkh.onKeyEvent(event, 0);

        // No callbacks were called.
        verify(mCallback, times(0)).onPanMagnificationStart(anyInt(), anyInt());
        verify(mCallback, times(0)).onPanMagnificationStop(anyInt());
        verify(mCallback, times(0)).onScaleMagnificationStart(anyInt(), anyInt());
        verify(mCallback, times(0)).onScaleMagnificationStop(anyInt());
        verify(mCallback, times(0)).onKeyboardInteractionStop();
        verifySentEventToNext(event);
    }

        // The event was passed on.
        verify(mNextHandler, times(1)).onKeyEvent(event, 0);
    @Test
    public void onKeyEvent_arrowKeyPressWithTooManyModifiers_sendToNext() {
        // Add ctrl.
        KeyEvent event =
                new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT, 0,
                        KeyEvent.META_META_ON | KeyEvent.META_ALT_ON | KeyEvent.META_CTRL_ON);
        mMkh.onKeyEvent(event, 0);
        verifySentEventToNext(event);

        // Add shift.
        event = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT, 0,
                        KeyEvent.META_META_ON | KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON);
        mMkh.onKeyEvent(event, 0);
        verifySentEventToNext(event);

        // Add ctrl and shift.
        event = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT, 0,
                KeyEvent.META_META_ON | KeyEvent.META_ALT_ON | KeyEvent.META_CTRL_ON
                        | KeyEvent.META_SHIFT_ON);
        mMkh.onKeyEvent(event, 0);
        verifySentEventToNext(event);
    }

    @Test
    public void onKeyEvent_arrowKeyPressWithoutMagnificationActive_sendToNext() {
        when(mCallback.isMagnificationActivated(any(Integer.class))).thenReturn(false);
        final KeyEvent event =
                new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT, 0,
                        KeyEvent.META_META_ON | KeyEvent.META_ALT_ON);
        mMkh.onKeyEvent(event, 0);

        verifySentEventToNext(event);
    }

    @Test
@@ -290,4 +302,15 @@ public class MagnificationKeyHandlerTest {
        verify(mNextHandler, times(0)).onKeyEvent(any(), anyInt());
    }

    private void verifySentEventToNext(KeyEvent event) {
        // No callbacks were called.
        verify(mCallback, times(0)).onPanMagnificationStart(anyInt(), anyInt());
        verify(mCallback, times(0)).onPanMagnificationStop(anyInt());
        verify(mCallback, times(0)).onScaleMagnificationStart(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);
    }
}