Loading services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +6 −0 Original line number Diff line number Diff line Loading @@ -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; Loading services/accessibility/java/com/android/server/accessibility/magnification/MagnificationKeyHandler.java +49 −21 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) { Loading @@ -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; Loading @@ -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) { Loading services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationKeyHandlerTest.java +49 −26 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading Loading @@ -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); } } Loading
services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +6 −0 Original line number Diff line number Diff line Loading @@ -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; Loading
services/accessibility/java/com/android/server/accessibility/magnification/MagnificationKeyHandler.java +49 −21 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) { Loading @@ -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; Loading @@ -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) { Loading
services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationKeyHandlerTest.java +49 −26 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading Loading @@ -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); } }