Loading packages/SystemUI/aconfig/accessibility.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -67,3 +67,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "window_magnification_move_with_mouse_on_edge" namespace: "accessibility" description: "Allows window magnification to be moved by mouse further on the edge of the screen." bug: "420776720" metadata { purpose: PURPOSE_BUGFIX } } packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java +72 −2 Original line number Diff line number Diff line Loading @@ -26,13 +26,17 @@ import static org.mockito.Mockito.verify; import android.os.Handler; import android.os.SystemClock; import android.platform.test.annotations.EnableFlags; import android.view.InputDevice; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import androidx.test.core.view.PointerCoordsBuilder; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import org.junit.After; Loading @@ -50,9 +54,9 @@ public class MagnificationGestureDetectorTest extends SysuiTestCase { private static final float ACTION_DOWN_X = 100; private static final float ACTION_DOWN_Y = 200; private int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); private final int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); private MagnificationGestureDetector mGestureDetector; private MotionEventHelper mMotionEventHelper = new MotionEventHelper(); private final MotionEventHelper mMotionEventHelper = new MotionEventHelper(); private View mSpyView; @Mock private MagnificationGestureDetector.OnGestureListener mListener; Loading Loading @@ -133,6 +137,37 @@ public class MagnificationGestureDetectorTest extends SysuiTestCase { verify(mListener, never()).onSingleTap(mSpyView); } @Test public void performSingleTapWithBatchedSmallTouchEvents_invokeCallback() { mGestureDetector = new MagnificationGestureDetector(mContext, mHandler, mListener); final long downTime = SystemClock.uptimeMillis(); final MotionEvent.PointerCoords coords = PointerCoordsBuilder.newBuilder() .setCoords(ACTION_DOWN_X, ACTION_DOWN_Y).build(); final MotionEvent downEvent = mMotionEventHelper.obtainBatchedMotionEvent(downTime, MotionEvent.ACTION_DOWN, InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.TOOL_TYPE_FINGER, coords); // Move event without changing location. final MotionEvent.PointerCoords dragCoords1 = PointerCoordsBuilder.newBuilder() .setCoords(ACTION_DOWN_X + mTouchSlop / 2.f, ACTION_DOWN_Y).build(); final MotionEvent.PointerCoords dragCoords2 = new MotionEvent.PointerCoords(dragCoords1); final MotionEvent moveEvent = mMotionEventHelper.obtainBatchedMotionEvent(downTime, MotionEvent.ACTION_MOVE, InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.TOOL_TYPE_FINGER, dragCoords1, dragCoords2); final MotionEvent upEvent = mMotionEventHelper.obtainBatchedMotionEvent(downTime, MotionEvent.ACTION_UP, InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.TOOL_TYPE_FINGER, coords); mGestureDetector.onTouch(mSpyView, downEvent); mGestureDetector.onTouch(mSpyView, moveEvent); mGestureDetector.onTouch(mSpyView, upEvent); verify(mListener).onSingleTap(mSpyView); } @Test public void performLongPress_invokeCallbacksInOrder() { final long downTime = SystemClock.uptimeMillis(); Loading Loading @@ -173,4 +208,39 @@ public class MagnificationGestureDetectorTest extends SysuiTestCase { inOrder.verify(mListener).onFinish(ACTION_DOWN_X, ACTION_DOWN_Y); verify(mListener, never()).onSingleTap(mSpyView); } @Test @EnableFlags(Flags.FLAG_WINDOW_MAGNIFICATION_MOVE_WITH_MOUSE_ON_EDGE) public void performDragWithMouse_invokeCallbacksUsingRelative() { mGestureDetector = new MagnificationGestureDetector(mContext, mHandler, mListener); final long downTime = SystemClock.uptimeMillis(); final MotionEvent.PointerCoords coords = PointerCoordsBuilder.newBuilder() .setCoords(ACTION_DOWN_X, ACTION_DOWN_Y).build(); final MotionEvent downEvent = mMotionEventHelper.obtainBatchedMotionEvent(downTime, MotionEvent.ACTION_DOWN, InputDevice.SOURCE_MOUSE, MotionEvent.TOOL_TYPE_MOUSE, coords); // Drag event without changing location but with relative delta on X axis. final MotionEvent.PointerCoords dragCoords1 = new MotionEvent.PointerCoords(coords); dragCoords1.setAxisValue(MotionEvent.AXIS_RELATIVE_X, mTouchSlop + 10); final MotionEvent.PointerCoords dragCoords2 = new MotionEvent.PointerCoords(coords); dragCoords2.setAxisValue(MotionEvent.AXIS_RELATIVE_X, 20); final MotionEvent moveEvent = mMotionEventHelper.obtainBatchedMotionEvent(downTime, MotionEvent.ACTION_MOVE, InputDevice.SOURCE_MOUSE, MotionEvent.TOOL_TYPE_MOUSE, dragCoords1, dragCoords2); final MotionEvent upEvent = mMotionEventHelper.obtainBatchedMotionEvent(downTime, MotionEvent.ACTION_UP, InputDevice.SOURCE_MOUSE, MotionEvent.TOOL_TYPE_MOUSE, coords); mGestureDetector.onTouch(mSpyView, downEvent); mGestureDetector.onTouch(mSpyView, moveEvent); mGestureDetector.onTouch(mSpyView, upEvent); verify(mListener).onStart(ACTION_DOWN_X, ACTION_DOWN_Y); verify(mListener).onDrag(mSpyView, mTouchSlop + 30, 0); verify(mListener).onFinish(ACTION_DOWN_X, ACTION_DOWN_Y); } } packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java +23 −2 Original line number Diff line number Diff line Loading @@ -22,10 +22,13 @@ import android.content.Context; import android.graphics.PointF; import android.os.Handler; import android.view.Display; import android.view.InputDevice; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import com.android.systemui.Flags; /** * Detects single tap and drag gestures using the supplied {@link MotionEvent}s. The {@link * OnGestureListener} callback will notify users when a particular motion event has occurred. This Loading Loading @@ -194,8 +197,22 @@ class MagnificationGestureDetector { break; case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_UP: float dx = event.getRawX() - mLastLocation.x; float dy = event.getRawY() - mLastLocation.y; float dx = 0; float dy = 0; if (Flags.windowMagnificationMoveWithMouseOnEdge() && isMouseEvent(event)) { // With mouse input, we use relative delta values so that user can drag // even at the edge of the screen, where the pointer location doesn't change // but input event still contain the delta value. for (int i = 0; i < event.getHistorySize(); i++) { dx += event.getHistoricalAxisValue(MotionEvent.AXIS_RELATIVE_X, i); dy += event.getHistoricalAxisValue(MotionEvent.AXIS_RELATIVE_Y, i); } dx += event.getAxisValue(MotionEvent.AXIS_RELATIVE_X); dy += event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y); } else { dx = event.getRawX() - mLastLocation.x; dy = event.getRawY() - mLastLocation.y; } mAccumulatedDelta.offset(dx, dy); mLastLocation.set(event.getRawX(), event.getRawY()); break; Loading Loading @@ -227,5 +244,9 @@ class MagnificationGestureDetector { pointF.x = Float.NaN; pointF.y = Float.NaN; } private static boolean isMouseEvent(MotionEvent event) { return (event.getSource() & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE; } } } packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/MotionEventHelper.java +31 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,9 @@ package com.android.systemui.accessibility; import android.view.MotionEvent; import androidx.test.core.view.MotionEventBuilder; import androidx.test.core.view.PointerPropertiesBuilder; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; Loading @@ -44,4 +47,32 @@ public class MotionEventHelper { } return event; } /** * Creates a {@link MotionEvent} with a batch of pointers. Call this with one or more pointer * coordinates, which will be added to the batch of the event. */ public MotionEvent obtainBatchedMotionEvent(long downTime, int action, int source, int toolType, MotionEvent.PointerCoords... coords) { if (coords.length == 0) { throw new IllegalArgumentException("coords cannot be empty"); } MotionEvent event = MotionEventBuilder.newBuilder() .setDownTime(downTime) .setEventTime(downTime) .setAction(action) .setSource(source) .setPointer( PointerPropertiesBuilder.newBuilder().setToolType(toolType).build(), coords[0]) .build(); for (int i = 1; i < coords.length; i++) { event.addBatch(downTime, new MotionEvent.PointerCoords[]{coords[i]}, /* metaState= */ 0); } synchronized (this) { mMotionEvents.add(event); } return event; } } Loading
packages/SystemUI/aconfig/accessibility.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -67,3 +67,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "window_magnification_move_with_mouse_on_edge" namespace: "accessibility" description: "Allows window magnification to be moved by mouse further on the edge of the screen." bug: "420776720" metadata { purpose: PURPOSE_BUGFIX } }
packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java +72 −2 Original line number Diff line number Diff line Loading @@ -26,13 +26,17 @@ import static org.mockito.Mockito.verify; import android.os.Handler; import android.os.SystemClock; import android.platform.test.annotations.EnableFlags; import android.view.InputDevice; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import androidx.test.core.view.PointerCoordsBuilder; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import org.junit.After; Loading @@ -50,9 +54,9 @@ public class MagnificationGestureDetectorTest extends SysuiTestCase { private static final float ACTION_DOWN_X = 100; private static final float ACTION_DOWN_Y = 200; private int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); private final int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); private MagnificationGestureDetector mGestureDetector; private MotionEventHelper mMotionEventHelper = new MotionEventHelper(); private final MotionEventHelper mMotionEventHelper = new MotionEventHelper(); private View mSpyView; @Mock private MagnificationGestureDetector.OnGestureListener mListener; Loading Loading @@ -133,6 +137,37 @@ public class MagnificationGestureDetectorTest extends SysuiTestCase { verify(mListener, never()).onSingleTap(mSpyView); } @Test public void performSingleTapWithBatchedSmallTouchEvents_invokeCallback() { mGestureDetector = new MagnificationGestureDetector(mContext, mHandler, mListener); final long downTime = SystemClock.uptimeMillis(); final MotionEvent.PointerCoords coords = PointerCoordsBuilder.newBuilder() .setCoords(ACTION_DOWN_X, ACTION_DOWN_Y).build(); final MotionEvent downEvent = mMotionEventHelper.obtainBatchedMotionEvent(downTime, MotionEvent.ACTION_DOWN, InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.TOOL_TYPE_FINGER, coords); // Move event without changing location. final MotionEvent.PointerCoords dragCoords1 = PointerCoordsBuilder.newBuilder() .setCoords(ACTION_DOWN_X + mTouchSlop / 2.f, ACTION_DOWN_Y).build(); final MotionEvent.PointerCoords dragCoords2 = new MotionEvent.PointerCoords(dragCoords1); final MotionEvent moveEvent = mMotionEventHelper.obtainBatchedMotionEvent(downTime, MotionEvent.ACTION_MOVE, InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.TOOL_TYPE_FINGER, dragCoords1, dragCoords2); final MotionEvent upEvent = mMotionEventHelper.obtainBatchedMotionEvent(downTime, MotionEvent.ACTION_UP, InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.TOOL_TYPE_FINGER, coords); mGestureDetector.onTouch(mSpyView, downEvent); mGestureDetector.onTouch(mSpyView, moveEvent); mGestureDetector.onTouch(mSpyView, upEvent); verify(mListener).onSingleTap(mSpyView); } @Test public void performLongPress_invokeCallbacksInOrder() { final long downTime = SystemClock.uptimeMillis(); Loading Loading @@ -173,4 +208,39 @@ public class MagnificationGestureDetectorTest extends SysuiTestCase { inOrder.verify(mListener).onFinish(ACTION_DOWN_X, ACTION_DOWN_Y); verify(mListener, never()).onSingleTap(mSpyView); } @Test @EnableFlags(Flags.FLAG_WINDOW_MAGNIFICATION_MOVE_WITH_MOUSE_ON_EDGE) public void performDragWithMouse_invokeCallbacksUsingRelative() { mGestureDetector = new MagnificationGestureDetector(mContext, mHandler, mListener); final long downTime = SystemClock.uptimeMillis(); final MotionEvent.PointerCoords coords = PointerCoordsBuilder.newBuilder() .setCoords(ACTION_DOWN_X, ACTION_DOWN_Y).build(); final MotionEvent downEvent = mMotionEventHelper.obtainBatchedMotionEvent(downTime, MotionEvent.ACTION_DOWN, InputDevice.SOURCE_MOUSE, MotionEvent.TOOL_TYPE_MOUSE, coords); // Drag event without changing location but with relative delta on X axis. final MotionEvent.PointerCoords dragCoords1 = new MotionEvent.PointerCoords(coords); dragCoords1.setAxisValue(MotionEvent.AXIS_RELATIVE_X, mTouchSlop + 10); final MotionEvent.PointerCoords dragCoords2 = new MotionEvent.PointerCoords(coords); dragCoords2.setAxisValue(MotionEvent.AXIS_RELATIVE_X, 20); final MotionEvent moveEvent = mMotionEventHelper.obtainBatchedMotionEvent(downTime, MotionEvent.ACTION_MOVE, InputDevice.SOURCE_MOUSE, MotionEvent.TOOL_TYPE_MOUSE, dragCoords1, dragCoords2); final MotionEvent upEvent = mMotionEventHelper.obtainBatchedMotionEvent(downTime, MotionEvent.ACTION_UP, InputDevice.SOURCE_MOUSE, MotionEvent.TOOL_TYPE_MOUSE, coords); mGestureDetector.onTouch(mSpyView, downEvent); mGestureDetector.onTouch(mSpyView, moveEvent); mGestureDetector.onTouch(mSpyView, upEvent); verify(mListener).onStart(ACTION_DOWN_X, ACTION_DOWN_Y); verify(mListener).onDrag(mSpyView, mTouchSlop + 30, 0); verify(mListener).onFinish(ACTION_DOWN_X, ACTION_DOWN_Y); } }
packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java +23 −2 Original line number Diff line number Diff line Loading @@ -22,10 +22,13 @@ import android.content.Context; import android.graphics.PointF; import android.os.Handler; import android.view.Display; import android.view.InputDevice; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import com.android.systemui.Flags; /** * Detects single tap and drag gestures using the supplied {@link MotionEvent}s. The {@link * OnGestureListener} callback will notify users when a particular motion event has occurred. This Loading Loading @@ -194,8 +197,22 @@ class MagnificationGestureDetector { break; case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_UP: float dx = event.getRawX() - mLastLocation.x; float dy = event.getRawY() - mLastLocation.y; float dx = 0; float dy = 0; if (Flags.windowMagnificationMoveWithMouseOnEdge() && isMouseEvent(event)) { // With mouse input, we use relative delta values so that user can drag // even at the edge of the screen, where the pointer location doesn't change // but input event still contain the delta value. for (int i = 0; i < event.getHistorySize(); i++) { dx += event.getHistoricalAxisValue(MotionEvent.AXIS_RELATIVE_X, i); dy += event.getHistoricalAxisValue(MotionEvent.AXIS_RELATIVE_Y, i); } dx += event.getAxisValue(MotionEvent.AXIS_RELATIVE_X); dy += event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y); } else { dx = event.getRawX() - mLastLocation.x; dy = event.getRawY() - mLastLocation.y; } mAccumulatedDelta.offset(dx, dy); mLastLocation.set(event.getRawX(), event.getRawY()); break; Loading Loading @@ -227,5 +244,9 @@ class MagnificationGestureDetector { pointF.x = Float.NaN; pointF.y = Float.NaN; } private static boolean isMouseEvent(MotionEvent event) { return (event.getSource() & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE; } } }
packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/MotionEventHelper.java +31 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,9 @@ package com.android.systemui.accessibility; import android.view.MotionEvent; import androidx.test.core.view.MotionEventBuilder; import androidx.test.core.view.PointerPropertiesBuilder; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; Loading @@ -44,4 +47,32 @@ public class MotionEventHelper { } return event; } /** * Creates a {@link MotionEvent} with a batch of pointers. Call this with one or more pointer * coordinates, which will be added to the batch of the event. */ public MotionEvent obtainBatchedMotionEvent(long downTime, int action, int source, int toolType, MotionEvent.PointerCoords... coords) { if (coords.length == 0) { throw new IllegalArgumentException("coords cannot be empty"); } MotionEvent event = MotionEventBuilder.newBuilder() .setDownTime(downTime) .setEventTime(downTime) .setAction(action) .setSource(source) .setPointer( PointerPropertiesBuilder.newBuilder().setToolType(toolType).build(), coords[0]) .build(); for (int i = 1; i < coords.length; i++) { event.addBatch(downTime, new MotionEvent.PointerCoords[]{coords[i]}, /* metaState= */ 0); } synchronized (this) { mMotionEvents.add(event); } return event; } }