Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1c7ace3d authored by Harry Cutts's avatar Harry Cutts Committed by Android (Google) Code Review
Browse files

Merge "GestureConverter: Stop flings with fake fingers" into main

parents 30573096 39648ab3
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -119,3 +119,10 @@ flag {
  description: "Controls the API to provide InputDevice view behavior."
  bug: "246946631"
}

flag {
  name: "enable_touchpad_fling_stop"
  namespace: "input"
  description: "Enable fling scrolling to be stopped by putting a finger on the touchpad again"
  bug: "281106755"
}
+46 −10
Original line number Diff line number Diff line
@@ -67,7 +67,8 @@ GestureConverter::GestureConverter(InputReaderContext& readerContext,
      : mDeviceId(deviceId),
        mReaderContext(readerContext),
        mPointerController(readerContext.getPointerController(deviceId)),
        mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()) {
        mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()),
        mEnableFlingStop(input_flags::enable_touchpad_fling_stop()) {
    deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo);
    deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo);
}
@@ -400,21 +401,56 @@ std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTi
                // 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.
                mFlingMayBeInProgress = true;
                return endScroll(when, readTime);
            }
            break;
        case GESTURES_FLING_TAP_DOWN:
            if (mCurrentClassification == MotionClassification::NONE) {
                if (mEnableFlingStop && mFlingMayBeInProgress) {
                    // The user has just touched the pad again after ending a two-finger scroll
                    // motion, which might have started a fling. We want to stop the fling, but
                    // unfortunately there's currently no API for doing so. Instead, send and
                    // immediately cancel a fake finger to cause the scrolling to stop and hopefully
                    // avoid side effects (e.g. activation of UI elements).
                    // TODO(b/326056750): add an API for fling stops.
                    mFlingMayBeInProgress = false;
                    const auto [xCursorPosition, yCursorPosition] = mEnablePointerChoreographer
                            ? FloatPoint{0, 0}
                            : mPointerController->getPosition();
                    PointerCoords coords;
                    coords.clear();
                    coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
                    coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
                    coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
                    coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);

                    std::list<NotifyArgs> out;
                    mDownTime = when;
                    out += exitHover(when, readTime, xCursorPosition, yCursorPosition);
                    // TODO(b/281106755): add a MotionClassification value for fling stops.
                    out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
                                                 /*actionButton=*/0, /*buttonState=*/0,
                                                 /*pointerCount=*/1, &coords, xCursorPosition,
                                                 yCursorPosition));
                    out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_CANCEL,
                                                 /*actionButton=*/0, /*buttonState=*/0,
                                                 /*pointerCount=*/1, &coords, xCursorPosition,
                                                 yCursorPosition));
                    out += enterHover(when, readTime, xCursorPosition, yCursorPosition);
                    return out;
                } else {
                    // Use the tap down state of a fling gesture as an indicator that a contact
                    // has been initiated with the touchpad. We treat this as a move event with zero
                    // magnitude, which will also result in the pointer icon being updated.
                // TODO(b/282023644): Add a signal in libgestures for when a stable contact has been
                //  initiated with a touchpad.
                    // TODO(b/282023644): Add a signal in libgestures for when a stable contact has
                    //  been initiated with a touchpad.
                    return handleMove(when, readTime, gestureStartTime,
                                      Gesture(kGestureMove, gesture.start_time, gesture.end_time,
                                              /*dx=*/0.f,
                                              /*dy=*/0.f));
                }
            }
            break;
        default:
            break;
+4 −0
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ private:
    InputReaderContext& mReaderContext;
    std::shared_ptr<PointerControllerInterface> mPointerController;
    const bool mEnablePointerChoreographer;
    const bool mEnableFlingStop;

    std::optional<int32_t> mDisplayId;
    FloatRect mBoundsInLogicalDisplay{};
@@ -120,6 +121,9 @@ private:
    // Whether we are currently in a hover state (i.e. a HOVER_ENTER event has been sent without a
    // matching HOVER_EXIT).
    bool mIsHovering = false;
    // Whether we've received a "fling start" gesture (i.e. the end of a scroll) but no "fling tap
    // down" gesture to match it yet.
    bool mFlingMayBeInProgress = false;

    MotionClassification mCurrentClassification = MotionClassification::NONE;
    // Only used when mCurrentClassification is MULTI_FINGER_SWIPE.
+64 −0
Original line number Diff line number Diff line
@@ -1167,6 +1167,38 @@ TEST_F(GestureConverterTest, FlingTapDown) {
    ASSERT_TRUE(mFakePointerController->isPointerShown());
}

TEST_F(GestureConverterTest, FlingTapDownAfterScrollStopsFling) {
    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
    input_flags::enable_touchpad_fling_stop(true);
    GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
    converter.setDisplayId(ADISPLAY_ID_DEFAULT);

    Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
    std::list<NotifyArgs> args =
            converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, scrollGesture);
    Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
                         GESTURES_FLING_START);
    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);

    Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
                           /*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture);
    ASSERT_THAT(args,
                ElementsAre(VariantWith<NotifyMotionArgs>(
                                    WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)),
                            VariantWith<NotifyMotionArgs>(
                                    WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
                            VariantWith<NotifyMotionArgs>(
                                    WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)),
                            VariantWith<NotifyMotionArgs>(
                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
                                          WithMotionClassification(MotionClassification::NONE)))));
    ASSERT_THAT(args,
                Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y),
                                                         WithToolType(ToolType::FINGER),
                                                         WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}

TEST_F(GestureConverterTest, Tap) {
    // Tap should produce button press/release events
    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
@@ -2556,6 +2588,38 @@ TEST_F(GestureConverterTestWithChoreographer, FlingTapDown) {
                      WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}

TEST_F(GestureConverterTestWithChoreographer, FlingTapDownAfterScrollStopsFling) {
    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
    input_flags::enable_touchpad_fling_stop(true);
    GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
    converter.setDisplayId(ADISPLAY_ID_DEFAULT);

    Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
    std::list<NotifyArgs> args =
            converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, scrollGesture);
    Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
                         GESTURES_FLING_START);
    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);

    Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
                           /*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture);
    ASSERT_THAT(args,
                ElementsAre(VariantWith<NotifyMotionArgs>(
                                    WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)),
                            VariantWith<NotifyMotionArgs>(
                                    WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
                            VariantWith<NotifyMotionArgs>(
                                    WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)),
                            VariantWith<NotifyMotionArgs>(
                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
                                          WithMotionClassification(MotionClassification::NONE)))));
    ASSERT_THAT(args,
                Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
                                                         WithToolType(ToolType::FINGER),
                                                         WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}

TEST_F(GestureConverterTestWithChoreographer, Tap) {
    // Tap should produce button press/release events
    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);