diff options
8 files changed, 550 insertions, 212 deletions
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java index 4025242fd208..1a712d2b3f31 100644 --- a/core/java/android/hardware/input/KeyGestureEvent.java +++ b/core/java/android/hardware/input/KeyGestureEvent.java @@ -122,17 +122,11 @@ public final class KeyGestureEvent { public static final int KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW = 69; public static final int KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW = 70; public static final int KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW = 71; - public static final int KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_IN = 72; - public static final int KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_OUT = 73; - public static final int KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION = 74; - public static final int KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK = 75; - public static final int KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW = 76; - public static final int KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB = 77; - public static final int KEY_GESTURE_TYPE_MAGNIFICATION_PAN_LEFT = 78; - public static final int KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT = 79; - public static final int KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP = 80; - public static final int KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN = 81; - public static final int KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS = 82; + public static final int KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION = 72; + public static final int KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK = 73; + public static final int KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW = 74; + public static final int KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB = 75; + public static final int KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS = 76; public static final int FLAG_CANCELLED = 1; @@ -220,16 +214,10 @@ public final class KeyGestureEvent { KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW, KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW, KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW, - KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_IN, - KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_OUT, KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION, KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK, KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW, KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB, - KEY_GESTURE_TYPE_MAGNIFICATION_PAN_LEFT, - KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT, - KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP, - KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN, KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS, }) @Retention(RetentionPolicy.SOURCE) @@ -815,10 +803,6 @@ public final class KeyGestureEvent { return "KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW"; case KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW: return "KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW"; - case KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_IN: - return "KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_IN"; - case KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_OUT: - return "KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_OUT"; case KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION: return "KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION"; case KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK: @@ -827,14 +811,6 @@ public final class KeyGestureEvent { return "KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW"; case KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB: return "KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB"; - case KEY_GESTURE_TYPE_MAGNIFICATION_PAN_LEFT: - return "KEY_GESTURE_TYPE_MAGNIFICATION_PAN_LEFT"; - case KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT: - return "KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT"; - case KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP: - return "KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP"; - case KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN: - return "KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN"; case KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS: return "KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS"; default: diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index 79888b051c54..70c4c1311fc9 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -21,14 +21,14 @@ import static android.view.MotionEvent.ACTION_SCROLL; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; +import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures; + import android.accessibilityservice.AccessibilityTrace; import android.annotation.MainThread; import android.annotation.NonNull; import android.content.Context; import android.graphics.Region; import android.hardware.input.InputManager; -import android.hardware.input.KeyGestureEvent; -import android.os.IBinder; import android.os.Looper; import android.os.PowerManager; import android.os.SystemClock; @@ -46,15 +46,13 @@ import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; import android.view.accessibility.AccessibilityEvent; -import androidx.annotation.Nullable; - import com.android.server.LocalServices; import com.android.server.accessibility.gestures.TouchExplorer; import com.android.server.accessibility.magnification.FullScreenMagnificationController; import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler; import com.android.server.accessibility.magnification.FullScreenMagnificationVibrationHelper; -import com.android.server.accessibility.magnification.MagnificationController; import com.android.server.accessibility.magnification.MagnificationGestureHandler; +import com.android.server.accessibility.magnification.MagnificationKeyHandler; import com.android.server.accessibility.magnification.MouseEventHandler; import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler; import com.android.server.accessibility.magnification.WindowMagnificationPromptController; @@ -209,6 +207,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo private MouseKeysInterceptor mMouseKeysInterceptor; + private MagnificationKeyHandler mMagnificationKeyHandler; + private boolean mInstalled; private int mUserId; @@ -235,74 +235,6 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo */ private MotionEvent mLastActiveDeviceMotionEvent = null; - private boolean mKeyGestureEventHandlerInstalled = false; - private InputManager.KeyGestureEventHandler mKeyGestureEventHandler = - new InputManager.KeyGestureEventHandler() { - @Override - public boolean handleKeyGestureEvent( - @NonNull KeyGestureEvent event, - @Nullable IBinder focusedToken) { - final boolean complete = - event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE - && !event.isCancelled(); - - // TODO(b/355499907): Receive and handle held key gestures, which can be used - // for continuous scaling and panning. In addition, handle multiple pan gestures - // at the same time (e.g. user may try to pan diagonally) reasonably, including - // decreasing diagonal movement by sqrt(2) to make it appear the same speed - // as non-diagonal movement. - - if (!complete) { - return false; - } - - final int gestureType = event.getKeyGestureType(); - final int displayId = isDisplayIdValid(event.getDisplayId()) - ? event.getDisplayId() : Display.DEFAULT_DISPLAY; - - switch (gestureType) { - case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_IN: - mAms.getMagnificationController().scaleMagnificationByStep( - displayId, MagnificationController.ZOOM_DIRECTION_IN); - return true; - case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_OUT: - mAms.getMagnificationController().scaleMagnificationByStep( - displayId, MagnificationController.ZOOM_DIRECTION_OUT); - return true; - case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_LEFT: - mAms.getMagnificationController().panMagnificationByStep( - displayId, MagnificationController.PAN_DIRECTION_LEFT); - return true; - case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT: - mAms.getMagnificationController().panMagnificationByStep( - displayId, MagnificationController.PAN_DIRECTION_RIGHT); - return true; - case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP: - mAms.getMagnificationController().panMagnificationByStep( - displayId, MagnificationController.PAN_DIRECTION_UP); - return true; - case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN: - mAms.getMagnificationController().panMagnificationByStep( - displayId, MagnificationController.PAN_DIRECTION_DOWN); - return true; - } - return false; - } - - @Override - public boolean isKeyGestureSupported(int gestureType) { - return switch (gestureType) { - case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_IN, - KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_OUT, - KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_LEFT, - KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT, - KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP, - KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN -> true; - default -> false; - }; - } - }; - private static MotionEvent cancelMotion(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT @@ -787,20 +719,11 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo }); } - if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0 - || ((mEnabledFeatures & FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP) != 0) - || ((mEnabledFeatures & FLAG_FEATURE_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP) != 0) - || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) { + if (isAnyMagnificationEnabled()) { final MagnificationGestureHandler magnificationGestureHandler = createMagnificationGestureHandler(displayId, displayContext); addFirstEventHandler(displayId, magnificationGestureHandler); mMagnificationGestureHandler.put(displayId, magnificationGestureHandler); - - if (com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures() - && !mKeyGestureEventHandlerInstalled) { - mInputManager.registerKeyGestureEventHandler(mKeyGestureEventHandler); - mKeyGestureEventHandlerInstalled = true; - } } if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) { @@ -817,6 +740,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } if ((mEnabledFeatures & FLAG_FEATURE_FILTER_KEY_EVENTS) != 0) { + // mKeyboardInterceptor does not forward KeyEvents to other EventStreamTransformations, + // so it must be the last EventStreamTransformation for key events in the list. mKeyboardInterceptor = new KeyboardInterceptor(mAms, LocalServices.getService(WindowManagerPolicy.class)); // Since the display id of KeyEvent always would be -1 and it would be dispatched to @@ -832,6 +757,19 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo Display.DEFAULT_DISPLAY); addFirstEventHandler(Display.DEFAULT_DISPLAY, mMouseKeysInterceptor); } + + if (enableTalkbackAndMagnifierKeyGestures() && isAnyMagnificationEnabled()) { + mMagnificationKeyHandler = new MagnificationKeyHandler( + mAms.getMagnificationController()); + addFirstEventHandler(Display.DEFAULT_DISPLAY, mMagnificationKeyHandler); + } + } + + private boolean isAnyMagnificationEnabled() { + return (mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0 + || ((mEnabledFeatures & FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP) != 0) + || ((mEnabledFeatures & FLAG_FEATURE_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP) != 0) + || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0); } /** @@ -921,9 +859,9 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo mMouseKeysInterceptor = null; } - if (mKeyGestureEventHandlerInstalled) { - mInputManager.unregisterKeyGestureEventHandler(mKeyGestureEventHandler); - mKeyGestureEventHandlerInstalled = false; + if (mMagnificationKeyHandler != null) { + mMagnificationKeyHandler.onDestroy(); + mMagnificationKeyHandler = null; } } @@ -1365,6 +1303,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo joiner.add("AutoclickController"); } else if (next instanceof MotionEventInjector) { joiner.add("MotionEventInjector"); + } else if (next instanceof MagnificationKeyHandler) { + joiner.add("MagnificationKeyHandler"); } next = next.getNext(); } 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 2e131b696afc..75ec8ea88ace 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -84,7 +84,7 @@ import java.util.concurrent.Executor; * is done and before invoking {@link TransitionCallBack#onResult}. */ public class MagnificationController implements MagnificationConnectionManager.Callback, - MagnificationGestureHandler.Callback, + MagnificationGestureHandler.Callback, MagnificationKeyHandler.Callback, FullScreenMagnificationController.MagnificationInfoChangedCallback, WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks { @@ -347,6 +347,36 @@ public class MagnificationController implements MagnificationConnectionManager.C handleUserInteractionChanged(displayId, mode); } + @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); + } + + @Override + public void onPanMagnificationStop(int displayId, + @MagnificationController.PanDirection int direction) { + // TODO(b/388847283): Handle held key gestures, which can be used + // for continuous scaling and panning, until they are released. + + } + + @Override + public void onScaleMagnificationStart(int displayId, + @MagnificationController.ZoomDirection int direction) { + scaleMagnificationByStep(displayId, direction); + } + + @Override + public void onScaleMagnificationStop(int displayId, + @MagnificationController.ZoomDirection int direction) { + // TODO(b/388847283): Handle held key gestures, which can be used + // for continuous scaling and panning, until they are released. + } + private void handleUserInteractionChanged(int displayId, int mode) { if (mMagnificationCapabilities != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL) { return; diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationKeyHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationKeyHandler.java new file mode 100644 index 000000000000..a65580c82124 --- /dev/null +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationKeyHandler.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility.magnification; + +import android.view.Display; +import android.view.KeyEvent; + +import com.android.server.accessibility.BaseEventStreamTransformation; + +/* + * A class that listens to key presses used to control magnification. + */ +public class MagnificationKeyHandler extends BaseEventStreamTransformation { + + /** Callback interface to report that a user is intending to interact with Magnification. */ + public interface Callback { + /** + * Called when a keyboard shortcut to pan magnification in direction {@code direction} is + * pressed by a user. Note that this can be called for multiple directions if multiple + * arrows are pressed at the same time (e.g. diagonal panning). + * + * @param displayId The logical display ID + * @param direction The direction to start panning + */ + void onPanMagnificationStart(int displayId, + @MagnificationController.PanDirection int direction); + + /** + * Called when a keyboard shortcut to pan magnification in direction {@code direction} is + * unpressed by a user. Note that this can be called for multiple directions if multiple + * 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); + + /** + * Called when a keyboard shortcut to scale magnification in direction `direction` is + * pressed by a user. + * + * @param displayId The logical display ID + * @param direction The direction in which scaling started + */ + void onScaleMagnificationStart(int displayId, + @MagnificationController.ZoomDirection int direction); + + /** + * 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); + } + + protected final MagnificationKeyHandler.Callback mCallback; + + public MagnificationKeyHandler(Callback callback) { + mCallback = callback; + } + + @Override + public void onKeyEvent(KeyEvent event, int policyFlags) { + if (!com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures()) { + // Send to the rest of the handlers. + super.onKeyEvent(event, policyFlags); + return; + } + boolean modifiersPressed = event.isAltPressed() && event.isMetaPressed(); + if (!modifiersPressed) { + super.onKeyEvent(event, policyFlags); + 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) { + int panDirection = switch(keyCode) { + case KeyEvent.KEYCODE_DPAD_LEFT -> MagnificationController.PAN_DIRECTION_LEFT; + case KeyEvent.KEYCODE_DPAD_RIGHT -> MagnificationController.PAN_DIRECTION_RIGHT; + case KeyEvent.KEYCODE_DPAD_UP -> MagnificationController.PAN_DIRECTION_UP; + default -> MagnificationController.PAN_DIRECTION_DOWN; + }; + if (isDown) { + mCallback.onPanMagnificationStart(getDisplayId(event), panDirection); + } else { + mCallback.onPanMagnificationStop(getDisplayId(event), panDirection); + } + return; + } else if (keyCode == KeyEvent.KEYCODE_EQUALS || keyCode == KeyEvent.KEYCODE_MINUS) { + int zoomDirection = MagnificationController.ZOOM_DIRECTION_OUT; + if (keyCode == KeyEvent.KEYCODE_EQUALS) { + zoomDirection = MagnificationController.ZOOM_DIRECTION_IN; + } + if (isDown) { + mCallback.onScaleMagnificationStart(getDisplayId(event), zoomDirection); + } else { + mCallback.onScaleMagnificationStop(getDisplayId(event), zoomDirection); + } + return; + } + + // Continue down the eventing chain if this was unused. + super.onKeyEvent(event, policyFlags); + } + + private int getDisplayId(KeyEvent event) { + // Display ID may be invalid, e.g. for external keyboard attached to phone. + // In that case, use the default display. + if (event.getDisplayId() != Display.INVALID_DISPLAY) { + return event.getDisplayId(); + } + return Display.DEFAULT_DISPLAY; + } +} diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java index 93fdbc787ed0..fd755e3cefe2 100644 --- a/services/core/java/com/android/server/input/InputGestureManager.java +++ b/services/core/java/com/android/server/input/InputGestureManager.java @@ -87,7 +87,20 @@ final class InputGestureManager { createKeyTrigger(KeyEvent.KEYCODE_V, KeyEvent.META_CTRL_ON), createKeyTrigger(KeyEvent.KEYCODE_X, KeyEvent.META_CTRL_ON), createKeyTrigger(KeyEvent.KEYCODE_Z, KeyEvent.META_CTRL_ON), - createKeyTrigger(KeyEvent.KEYCODE_Y, KeyEvent.META_CTRL_ON) + createKeyTrigger(KeyEvent.KEYCODE_Y, KeyEvent.META_CTRL_ON), + // Used for magnification viewport control. + createKeyTrigger(KeyEvent.KEYCODE_MINUS, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON), + createKeyTrigger(KeyEvent.KEYCODE_EQUALS, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON), + createKeyTrigger(KeyEvent.KEYCODE_DPAD_LEFT, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON), + createKeyTrigger(KeyEvent.KEYCODE_DPAD_RIGHT, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON), + createKeyTrigger(KeyEvent.KEYCODE_DPAD_UP, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON), + createKeyTrigger(KeyEvent.KEYCODE_DPAD_DOWN, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON) )); public InputGestureManager(Context context) { @@ -216,24 +229,6 @@ final class InputGestureManager { systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_T, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK)); - systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_MINUS, - KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_OUT)); - systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_EQUALS, - KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_IN)); - systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_LEFT, - KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_LEFT)); - systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_RIGHT, - KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT)); - systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_UP, - KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP)); - systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_DPAD_DOWN, - KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN)); systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_M, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION)); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java index 464fee2bfc11..fb31cfe762f2 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java @@ -20,6 +20,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; import static android.view.WindowManagerPolicyConstants.FLAG_PASS_TO_USER; +import static com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES; import static com.android.server.accessibility.AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK; import static com.android.server.accessibility.AccessibilityInputFilter.FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER; import static com.android.server.accessibility.AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS; @@ -39,6 +40,10 @@ import android.content.Context; import android.hardware.display.DisplayManagerGlobal; import android.os.Looper; import android.os.SystemClock; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import android.util.SparseArray; import android.view.Display; @@ -55,12 +60,14 @@ import com.android.server.LocalServices; import com.android.server.accessibility.gestures.TouchExplorer; import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler; import com.android.server.accessibility.magnification.MagnificationGestureHandler; +import com.android.server.accessibility.magnification.MagnificationKeyHandler; import com.android.server.accessibility.magnification.MagnificationProcessor; import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler; import com.android.server.wm.WindowManagerInternal; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -88,8 +95,16 @@ public class AccessibilityInputFilterTest { | FLAG_FEATURE_INJECT_MOTION_EVENTS | FLAG_FEATURE_FILTER_KEY_EVENTS; + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + // The expected order of EventStreamTransformations. private final Class[] mExpectedEventHandlerTypes = + {MagnificationKeyHandler.class, KeyboardInterceptor.class, MotionEventInjector.class, + FullScreenMagnificationGestureHandler.class, TouchExplorer.class, + AutoclickController.class, AccessibilityInputFilter.class}; + + private final Class[] mExpectedEventHandlerTypesWithoutMagKeyboard = {KeyboardInterceptor.class, MotionEventInjector.class, FullScreenMagnificationGestureHandler.class, TouchExplorer.class, AutoclickController.class, AccessibilityInputFilter.class}; @@ -176,6 +191,7 @@ public class AccessibilityInputFilterTest { } @Test + @RequiresFlagsEnabled(FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) public void testEventHandler_shouldIncreaseAndHaveCorrectOrderAfterOnDisplayAdded() { prepareLooper(); @@ -191,9 +207,9 @@ public class AccessibilityInputFilterTest { EventStreamTransformation next = mEventHandler.get(SECOND_DISPLAY); assertNotNull(next); - // Start from index 1 because KeyboardInterceptor only exists in EventHandler for - // DEFAULT_DISPLAY. - for (int i = 1; next != null; i++) { + // Start from index 2 because KeyboardInterceptor and MagnificationKeyHandler only exist in + // EventHandler for DEFAULT_DISPLAY. + for (int i = 2; next != null; i++) { assertEquals(next.getClass(), mExpectedEventHandlerTypes[i]); next = next.getNext(); } @@ -232,6 +248,7 @@ public class AccessibilityInputFilterTest { } @Test + @RequiresFlagsEnabled(FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) public void testEventHandler_shouldHaveCorrectOrderForEventStreamTransformation() { prepareLooper(); @@ -248,10 +265,36 @@ public class AccessibilityInputFilterTest { } next = mEventHandler.get(SECOND_DISPLAY); + // Start from index 2 because KeyboardInterceptor and MagnificationKeyHandler only exist + // in EventHandler for DEFAULT_DISPLAY. + for (int i = 2; next != null; i++) { + assertEquals(next.getClass(), mExpectedEventHandlerTypes[i]); + next = next.getNext(); + } + } + + @Test + @RequiresFlagsDisabled(FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) + public void testEventHandler_shouldHaveCorrectOrderForEventStreamTransformation_noMagKeys() { + prepareLooper(); + + setDisplayCount(2); + mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures); + assertEquals(2, mEventHandler.size()); + + // Check if mEventHandler for each display has correct order of the + // EventStreamTransformations. + EventStreamTransformation next = mEventHandler.get(DEFAULT_DISPLAY); + for (int i = 0; next != null; i++) { + assertEquals(next.getClass(), mExpectedEventHandlerTypesWithoutMagKeyboard[i]); + next = next.getNext(); + } + + next = mEventHandler.get(SECOND_DISPLAY); // Start from index 1 because KeyboardInterceptor only exists in EventHandler for // DEFAULT_DISPLAY. for (int i = 1; next != null; i++) { - assertEquals(next.getClass(), mExpectedEventHandlerTypes[i]); + assertEquals(next.getClass(), mExpectedEventHandlerTypesWithoutMagKeyboard[i]); next = next.getNext(); } } @@ -387,7 +430,6 @@ public class AccessibilityInputFilterTest { assertNotNull(handler); assertEquals(WindowMagnificationGestureHandler.class, handler.getClass()); assertEquals(nextEventStream.getClass(), handler.getNext().getClass()); - } @Test public void @@ -412,6 +454,32 @@ public class AccessibilityInputFilterTest { assertEquals(nextEventStream.getClass(), handler.getNext().getClass()); } + @Test + @RequiresFlagsEnabled(FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) + public void testEnabledFeatures_windowMagnificationMode_expectedMagnificationKeyHandler() { + prepareLooper(); + doReturn(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW).when( + mAms).getMagnificationMode(DEFAULT_DISPLAY); + + mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures); + + MagnificationKeyHandler handler = getMagnificationKeyHandlerFromEventHandler(); + assertNotNull(handler); + } + + @Test + @RequiresFlagsEnabled(FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) + public void testEnabledFeatures_fullscreenMagnificationMode_expectedMagnificationKeyHandler() { + prepareLooper(); + doReturn(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN).when( + mAms).getMagnificationMode(DEFAULT_DISPLAY); + + mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures); + + MagnificationKeyHandler handler = getMagnificationKeyHandlerFromEventHandler(); + assertNotNull(handler); + } + private static void prepareLooper() { if (Looper.myLooper() == null) { Looper.prepare(); @@ -458,4 +526,16 @@ public class AccessibilityInputFilterTest { } return null; } + + @Nullable + private MagnificationKeyHandler getMagnificationKeyHandlerFromEventHandler() { + EventStreamTransformation next = mEventHandler.get(DEFAULT_DISPLAY); + while (next != null) { + if (next instanceof MagnificationKeyHandler) { + return (MagnificationKeyHandler) next; + } + next = next.getNext(); + } + return null; + } } 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 new file mode 100644 index 000000000000..d1ef33d8fb70 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationKeyHandlerTest.java @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility.magnification; + +import static com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES; +import static com.android.server.accessibility.magnification.MagnificationController.PAN_DIRECTION_DOWN; +import static com.android.server.accessibility.magnification.MagnificationController.PAN_DIRECTION_LEFT; +import static com.android.server.accessibility.magnification.MagnificationController.PAN_DIRECTION_RIGHT; +import static com.android.server.accessibility.magnification.MagnificationController.PAN_DIRECTION_UP; +import static com.android.server.accessibility.magnification.MagnificationController.ZOOM_DIRECTION_IN; +import static com.android.server.accessibility.magnification.MagnificationController.ZOOM_DIRECTION_OUT; + +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 android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.view.Display; +import android.view.KeyEvent; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.accessibility.EventStreamTransformation; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link MagnificationKeyHandler}. + */ +@RunWith(AndroidJUnit4.class) +@RequiresFlagsEnabled(FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) +public class MagnificationKeyHandlerTest { + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + private MagnificationKeyHandler mMkh; + + @Mock + MagnificationKeyHandler.Callback mCallback; + + @Mock + EventStreamTransformation mNextHandler; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mMkh = new MagnificationKeyHandler(mCallback); + mMkh.setNext(mNextHandler); + } + + @Test + public void onKeyEvent_unusedKeyPress_sendToNext() { + 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(), anyInt()); + verify(mCallback, times(0)).onScaleMagnificationStart(anyInt(), anyInt()); + verify(mCallback, times(0)).onScaleMagnificationStop(anyInt(), anyInt()); + + // The event was passed on. + verify(mNextHandler, times(1)).onKeyEvent(event, 0); + } + + @Test + public void onKeyEvent_arrowKeyPressWithIncorrectModifiers_sendToNext() { + final KeyEvent event = + new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT, + 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(), anyInt()); + verify(mCallback, times(0)).onScaleMagnificationStart(anyInt(), anyInt()); + verify(mCallback, times(0)).onScaleMagnificationStop(anyInt(), anyInt()); + + // The event was passed on. + verify(mNextHandler, times(1)).onKeyEvent(event, 0); + } + + @Test + public void onKeyEvent_unusedKeyPressWithCorrectModifiers_sendToNext() { + final KeyEvent event = + new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_J, 0, + 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(), anyInt()); + verify(mCallback, times(0)).onScaleMagnificationStart(anyInt(), anyInt()); + verify(mCallback, times(0)).onScaleMagnificationStop(anyInt(), anyInt()); + + // The event was passed on. + verify(mNextHandler, times(1)).onKeyEvent(event, 0); + } + + @Test + public void onKeyEvent_panStartAndEnd_left() { + testPanMagnification(KeyEvent.KEYCODE_DPAD_LEFT, PAN_DIRECTION_LEFT); + } + + @Test + public void onKeyEvent_panStartAndEnd_right() { + testPanMagnification(KeyEvent.KEYCODE_DPAD_RIGHT, PAN_DIRECTION_RIGHT); + } + + @Test + public void onKeyEvent_panStartAndEnd_up() { + testPanMagnification(KeyEvent.KEYCODE_DPAD_UP, PAN_DIRECTION_UP); + } + + @Test + public void onKeyEvent_panStartAndEnd_down() { + testPanMagnification(KeyEvent.KEYCODE_DPAD_DOWN, PAN_DIRECTION_DOWN); + } + + @Test + public void onKeyEvent_scaleStartAndEnd_zoomIn() { + testScaleMagnification(KeyEvent.KEYCODE_EQUALS, ZOOM_DIRECTION_IN); + } + + @Test + public void onKeyEvent_scaleStartAndEnd_zoomOut() { + testScaleMagnification(KeyEvent.KEYCODE_MINUS, ZOOM_DIRECTION_OUT); + } + + @Test + public void onKeyEvent_panStartAndStop_diagonal() { + final KeyEvent downLeftEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, + KeyEvent.KEYCODE_DPAD_LEFT, 0, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON); + mMkh.onKeyEvent(downLeftEvent, 0); + verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, + PAN_DIRECTION_LEFT); + verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt()); + + // 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, + PAN_DIRECTION_LEFT); + verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, + PAN_DIRECTION_DOWN); + verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt()); + + // 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, + 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, + PAN_DIRECTION_DOWN); + + // 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, + 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, + PAN_DIRECTION_DOWN); + + // The event was not passed on. + verify(mNextHandler, times(0)).onKeyEvent(any(), anyInt()); + } + + 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); + mMkh.onKeyEvent(downEvent, 0); + + // Pan started. + verify(mCallback, times(1)).onPanMagnificationStart(Display.DEFAULT_DISPLAY, panDirection); + verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt()); + + 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); + + // Scale callbacks were not called. + verify(mCallback, times(0)).onScaleMagnificationStart(anyInt(), anyInt()); + verify(mCallback, times(0)).onScaleMagnificationStop(anyInt(), anyInt()); + + // The events were not passed on. + verify(mNextHandler, times(0)).onKeyEvent(any(), anyInt()); + } + + private void testScaleMagnification(int keyCode, int zoomDirection) { + final KeyEvent downEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, keyCode, 0, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON); + mMkh.onKeyEvent(downEvent, 0); + + // Scale started. + verify(mCallback, times(1)).onScaleMagnificationStart(Display.DEFAULT_DISPLAY, + zoomDirection); + verify(mCallback, times(0)).onScaleMagnificationStop(anyInt(), anyInt()); + + 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, + zoomDirection); + + // Pan callbacks were not called. + verify(mCallback, times(0)).onPanMagnificationStart(anyInt(), anyInt()); + verify(mCallback, times(0)).onPanMagnificationStop(anyInt(), anyInt()); + + // The events were not passed on. + verify(mNextHandler, times(0)).onKeyEvent(any(), anyInt()); + + } + +} diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt index 8c04f647fb2f..e0532633d40b 100644 --- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt @@ -736,30 +736,6 @@ class KeyGestureControllerTests { intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) ), TestData( - "META + ALT + '-' -> Magnification Zoom Out", - intArrayOf( - KeyEvent.KEYCODE_META_LEFT, - KeyEvent.KEYCODE_ALT_LEFT, - KeyEvent.KEYCODE_MINUS - ), - KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_OUT, - intArrayOf(KeyEvent.KEYCODE_MINUS), - KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( - "META + ALT + '=' -> Magnification Zoom In", - intArrayOf( - KeyEvent.KEYCODE_META_LEFT, - KeyEvent.KEYCODE_ALT_LEFT, - KeyEvent.KEYCODE_EQUALS - ), - KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_ZOOM_IN, - intArrayOf(KeyEvent.KEYCODE_EQUALS), - KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( "META + ALT + M -> Toggle Magnification", intArrayOf( KeyEvent.KEYCODE_META_LEFT, @@ -784,54 +760,6 @@ class KeyGestureControllerTests { intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) ), TestData( - "META + ALT + 'Down' -> Magnification Pan Down", - intArrayOf( - KeyEvent.KEYCODE_META_LEFT, - KeyEvent.KEYCODE_ALT_LEFT, - KeyEvent.KEYCODE_DPAD_DOWN - ), - KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN, - intArrayOf(KeyEvent.KEYCODE_DPAD_DOWN), - KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( - "META + ALT + 'Up' -> Magnification Pan Up", - intArrayOf( - KeyEvent.KEYCODE_META_LEFT, - KeyEvent.KEYCODE_ALT_LEFT, - KeyEvent.KEYCODE_DPAD_UP - ), - KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP, - intArrayOf(KeyEvent.KEYCODE_DPAD_UP), - KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( - "META + ALT + 'Left' -> Magnification Pan Left", - intArrayOf( - KeyEvent.KEYCODE_META_LEFT, - KeyEvent.KEYCODE_ALT_LEFT, - KeyEvent.KEYCODE_DPAD_LEFT - ), - KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_LEFT, - intArrayOf(KeyEvent.KEYCODE_DPAD_LEFT), - KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( - "META + ALT + 'Right' -> Magnification Pan Right", - intArrayOf( - KeyEvent.KEYCODE_META_LEFT, - KeyEvent.KEYCODE_ALT_LEFT, - KeyEvent.KEYCODE_DPAD_RIGHT - ), - KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT, - intArrayOf(KeyEvent.KEYCODE_DPAD_RIGHT), - KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( "META + ALT + 'V' -> Toggle Voice Access", intArrayOf( KeyEvent.KEYCODE_META_LEFT, |