summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/hardware/input/KeyGestureEvent.java34
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java112
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java32
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationKeyHandler.java133
-rw-r--r--services/core/java/com/android/server/input/InputGestureManager.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java90
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationKeyHandlerTest.java256
-rw-r--r--tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt72
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,