diff options
-rw-r--r-- | include/android/input.h | 15 | ||||
-rw-r--r-- | libs/input/InputEventLabels.cpp | 4 | ||||
-rw-r--r-- | services/inputflinger/InputCommonConverter.cpp | 4 | ||||
-rw-r--r-- | services/inputflinger/reader/mapper/gestures/GestureConverter.cpp | 57 | ||||
-rw-r--r-- | services/inputflinger/reader/mapper/gestures/GestureConverter.h | 5 | ||||
-rw-r--r-- | services/inputflinger/tests/GestureConverter_test.cpp | 111 | ||||
-rw-r--r-- | services/inputflinger/tests/TestInputListenerMatchers.h | 18 |
7 files changed, 209 insertions, 5 deletions
diff --git a/include/android/input.h b/include/android/input.h index a0b46de260..e1aac65457 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -786,6 +786,19 @@ enum { * The same as {@link AMOTION_EVENT_AXIS_GESTURE_X_OFFSET}, but for the Y axis. */ AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET = 49, + /** + * Axis constant: X scroll distance axis of a motion event. + * + * - For a touch pad, reports the distance that should be scrolled in the X axis as a result of + * the user's two-finger scroll gesture, in display pixels. + */ + AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE = 50, + /** + * Axis constant: Y scroll distance axis of a motion event. + * + * The same as {@link AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE}, but for the Y axis. + */ + AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE = 51, /** * Note: This is not an "Axis constant". It does not represent any axis, nor should it be used @@ -793,7 +806,7 @@ enum { * to make some computations (like iterating through all possible axes) cleaner. * Please update the value accordingly if you add a new axis. */ - AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE = AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, + AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE = AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, // NOTE: If you add a new axis here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp index dd7cbb5ba9..8ffd220ad8 100644 --- a/libs/input/InputEventLabels.cpp +++ b/libs/input/InputEventLabels.cpp @@ -394,7 +394,9 @@ namespace android { DEFINE_AXIS(GENERIC_15), \ DEFINE_AXIS(GENERIC_16), \ DEFINE_AXIS(GESTURE_X_OFFSET), \ - DEFINE_AXIS(GESTURE_Y_OFFSET) + DEFINE_AXIS(GESTURE_Y_OFFSET), \ + DEFINE_AXIS(GESTURE_SCROLL_X_DISTANCE), \ + DEFINE_AXIS(GESTURE_SCROLL_Y_DISTANCE) // NOTE: If you add new LEDs here, you must also add them to Input.h #define LEDS_SEQUENCE \ diff --git a/services/inputflinger/InputCommonConverter.cpp b/services/inputflinger/InputCommonConverter.cpp index 628ce6fc9a..ea0a429000 100644 --- a/services/inputflinger/InputCommonConverter.cpp +++ b/services/inputflinger/InputCommonConverter.cpp @@ -263,11 +263,11 @@ static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_13) == common static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_14) == common::Axis::GENERIC_14); static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_15) == common::Axis::GENERIC_15); static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_16) == common::Axis::GENERIC_16); -// TODO(hcutts): add GESTURE_X_OFFSET and GESTURE_Y_OFFSET. +// TODO(b/251196347): add GESTURE_{X,Y}_OFFSET and GESTURE_SCROLL_{X,Y}_DISTANCE. // If you added a new axis, consider whether this should also be exposed as a HAL axis. Update the // static_assert below and add the new axis here, or leave a comment summarizing your decision. static_assert(static_cast<common::Axis>(AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE) == - static_cast<common::Axis>(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET)); + static_cast<common::Axis>(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE)); static common::VideoFrame getHalVideoFrame(const TouchVideoFrame& frame) { common::VideoFrame out; diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp index ffc0523e97..575acb06b6 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp @@ -65,6 +65,10 @@ std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t read return {handleMove(when, readTime, gesture)}; case kGestureTypeButtonsChange: return handleButtonsChange(when, readTime, gesture); + case kGestureTypeScroll: + return handleScroll(when, readTime, gesture); + case kGestureTypeFling: + return {handleFling(when, readTime, gesture)}; case kGestureTypeSwipe: return handleMultiFingerSwipe(when, readTime, 3, gesture.details.swipe.dx, gesture.details.swipe.dy); @@ -175,6 +179,59 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ return out; } +std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readTime, + const Gesture& gesture) { + std::list<NotifyArgs> out; + PointerCoords& coords = mFakeFingerCoords[0]; + float xCursorPosition, yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + if (mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) { + mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE; + coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + mDownTime = when; + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, + /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, + mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, + yCursorPosition)); + } + float deltaX = gesture.details.scroll.dx; + float deltaY = gesture.details.scroll.dy; + rotateDelta(mOrientation, &deltaX, &deltaY); + + coords.setAxisValue(AMOTION_EVENT_AXIS_X, coords.getAxisValue(AMOTION_EVENT_AXIS_X) - deltaX); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y) - deltaY); + // TODO(b/262876643): set AXIS_GESTURE_{X,Y}_OFFSET. + coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, gesture.details.scroll.dx); + coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, gesture.details.scroll.dy); + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0, + mButtonState, /* pointerCount= */ 1, mFingerProps.data(), + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); + return out; +} + +NotifyArgs GestureConverter::handleFling(nsecs_t when, nsecs_t readTime, const Gesture& gesture) { + // We don't actually want to use the gestures library's fling velocity values (to ensure + // consistency between touchscreen and touchpad flings), so we're just using the "start fling" + // gestures as a marker for the end of a two-finger scroll gesture. + if (gesture.details.fling.fling_state != GESTURES_FLING_START || + mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) { + return {}; + } + + float xCursorPosition, yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0); + mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0); + NotifyArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, + /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, + mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, + yCursorPosition); + mCurrentClassification = MotionClassification::NONE; + return args; +} + [[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipe(nsecs_t when, nsecs_t readTime, uint32_t fingerCount, diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h index ae5581d3ec..6bea2d922c 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h @@ -47,9 +47,12 @@ public: const Gesture& gesture); private: - NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture); + [[nodiscard]] NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture); [[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime, const Gesture& gesture); + [[nodiscard]] std::list<NotifyArgs> handleScroll(nsecs_t when, nsecs_t readTime, + const Gesture& gesture); + [[nodiscard]] NotifyArgs handleFling(nsecs_t when, nsecs_t readTime, const Gesture& gesture); [[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipe(nsecs_t when, nsecs_t readTime, uint32_t fingerCount, float dx, float dy); diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp index 1c7ec7665a..683e78e348 100644 --- a/services/inputflinger/tests/GestureConverter_test.cpp +++ b/services/inputflinger/tests/GestureConverter_test.cpp @@ -239,6 +239,117 @@ TEST_F(GestureConverterTest, DragWithButton) { WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); } +TEST_F(GestureConverterTest, Scroll) { + const nsecs_t downTime = 12345; + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + + Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 10); + std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture); + ASSERT_EQ(2u, args.size()); + + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithDownTime(downTime))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X, POINTER_Y - 10), + WithGestureScrollDistance(0, 10, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + + Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 5); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X, POINTER_Y - 15), + WithGestureScrollDistance(0, 5, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + + Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, + GESTURES_FLING_START); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X, POINTER_Y - 15), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); +} + +TEST_F(GestureConverterTest, Scroll_Rotated) { + const nsecs_t downTime = 12345; + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setOrientation(ui::ROTATION_90); + + Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 10); + std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture); + ASSERT_EQ(2u, args.size()); + + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithDownTime(downTime))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X - 10, POINTER_Y), + WithGestureScrollDistance(0, 10, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + + Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 5); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(POINTER_X - 15, POINTER_Y), + WithGestureScrollDistance(0, 5, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); + + Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, + GESTURES_FLING_START); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X - 15, POINTER_Y), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))); +} + +TEST_F(GestureConverterTest, Scroll_ClearsClassificationAndOffsetsAfterGesture) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + + Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 10); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + + Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 5); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + + Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, + GESTURES_FLING_START); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, flingGesture); + + Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionClassification(MotionClassification::NONE), + WithGestureScrollDistance(0, 0, EPSILON))); +} + TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsClassificationAndOffsetsAfterGesture) { InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h index 64c2c7511b..53e4066b20 100644 --- a/services/inputflinger/tests/TestInputListenerMatchers.h +++ b/services/inputflinger/tests/TestInputListenerMatchers.h @@ -100,6 +100,19 @@ MATCHER_P3(WithGestureOffset, dx, dy, epsilon, return xDiff <= epsilon && yDiff <= epsilon; } +MATCHER_P3(WithGestureScrollDistance, x, y, epsilon, + "InputEvent with specified touchpad gesture scroll distance") { + const auto argXDistance = + arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE); + const auto argYDistance = + arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE); + const double xDiff = fabs(argXDistance - x); + const double yDiff = fabs(argYDistance - y); + *result_listener << "expected gesture offset (" << x << ", " << y << ") within " << epsilon + << ", but got (" << argXDistance << ", " << argYDistance << ")"; + return xDiff <= epsilon && yDiff <= epsilon; +} + MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") { const auto argPressure = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); *result_listener << "expected pressure " << pressure << ", but got " << argPressure; @@ -141,4 +154,9 @@ MATCHER_P(WithEventTime, eventTime, "InputEvent with specified eventTime") { return arg.eventTime == eventTime; } +MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") { + *result_listener << "expected down time " << downTime << ", but got " << arg.downTime; + return arg.downTime == downTime; +} + } // namespace android |