Loading core/java/android/hardware/input/KeyGestureEvent.java +5 −29 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) Loading Loading @@ -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: Loading @@ -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: Loading services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +26 −86 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -209,6 +207,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo private MouseKeysInterceptor mMouseKeysInterceptor; private MagnificationKeyHandler mMagnificationKeyHandler; private boolean mInstalled; private int mUserId; Loading @@ -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 Loading Loading @@ -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) { Loading @@ -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 Loading @@ -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); } /** Loading Loading @@ -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; } } Loading Loading @@ -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(); } Loading services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +31 −1 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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; Loading services/accessibility/java/com/android/server/accessibility/magnification/MagnificationKeyHandler.java 0 → 100644 +133 −0 Original line number Diff line number Diff line /* * 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; } } services/core/java/com/android/server/input/InputGestureManager.java +14 −19 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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)); Loading Loading
core/java/android/hardware/input/KeyGestureEvent.java +5 −29 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) Loading Loading @@ -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: Loading @@ -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: Loading
services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +26 −86 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -209,6 +207,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo private MouseKeysInterceptor mMouseKeysInterceptor; private MagnificationKeyHandler mMagnificationKeyHandler; private boolean mInstalled; private int mUserId; Loading @@ -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 Loading Loading @@ -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) { Loading @@ -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 Loading @@ -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); } /** Loading Loading @@ -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; } } Loading Loading @@ -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(); } Loading
services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +31 −1 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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; Loading
services/accessibility/java/com/android/server/accessibility/magnification/MagnificationKeyHandler.java 0 → 100644 +133 −0 Original line number Diff line number Diff line /* * 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; } }
services/core/java/com/android/server/input/InputGestureManager.java +14 −19 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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)); Loading