Skip to content

Commit 1c7ace3

Browse files
HarryCuttsAndroid (Google) Code Review
authored andcommitted
Merge "GestureConverter: Stop flings with fake fingers" into main
2 parents 3057309 + 39648ab commit 1c7ace3

4 files changed

Lines changed: 121 additions & 10 deletions

File tree

libs/input/input_flags.aconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,10 @@ flag {
119119
description: "Controls the API to provide InputDevice view behavior."
120120
bug: "246946631"
121121
}
122+
123+
flag {
124+
name: "enable_touchpad_fling_stop"
125+
namespace: "input"
126+
description: "Enable fling scrolling to be stopped by putting a finger on the touchpad again"
127+
bug: "281106755"
128+
}

services/inputflinger/reader/mapper/gestures/GestureConverter.cpp

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ GestureConverter::GestureConverter(InputReaderContext& readerContext,
6767
: mDeviceId(deviceId),
6868
mReaderContext(readerContext),
6969
mPointerController(readerContext.getPointerController(deviceId)),
70-
mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()) {
70+
mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()),
71+
mEnableFlingStop(input_flags::enable_touchpad_fling_stop()) {
7172
deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo);
7273
deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo);
7374
}
@@ -400,20 +401,55 @@ std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTi
400401
// ensure consistency between touchscreen and touchpad flings), so we're just using
401402
// the "start fling" gestures as a marker for the end of a two-finger scroll
402403
// gesture.
404+
mFlingMayBeInProgress = true;
403405
return endScroll(when, readTime);
404406
}
405407
break;
406408
case GESTURES_FLING_TAP_DOWN:
407409
if (mCurrentClassification == MotionClassification::NONE) {
408-
// Use the tap down state of a fling gesture as an indicator that a contact
409-
// has been initiated with the touchpad. We treat this as a move event with zero
410-
// magnitude, which will also result in the pointer icon being updated.
411-
// TODO(b/282023644): Add a signal in libgestures for when a stable contact has been
412-
// initiated with a touchpad.
413-
return handleMove(when, readTime, gestureStartTime,
414-
Gesture(kGestureMove, gesture.start_time, gesture.end_time,
415-
/*dx=*/0.f,
416-
/*dy=*/0.f));
410+
if (mEnableFlingStop && mFlingMayBeInProgress) {
411+
// The user has just touched the pad again after ending a two-finger scroll
412+
// motion, which might have started a fling. We want to stop the fling, but
413+
// unfortunately there's currently no API for doing so. Instead, send and
414+
// immediately cancel a fake finger to cause the scrolling to stop and hopefully
415+
// avoid side effects (e.g. activation of UI elements).
416+
// TODO(b/326056750): add an API for fling stops.
417+
mFlingMayBeInProgress = false;
418+
const auto [xCursorPosition, yCursorPosition] = mEnablePointerChoreographer
419+
? FloatPoint{0, 0}
420+
: mPointerController->getPosition();
421+
PointerCoords coords;
422+
coords.clear();
423+
coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
424+
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
425+
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
426+
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
427+
428+
std::list<NotifyArgs> out;
429+
mDownTime = when;
430+
out += exitHover(when, readTime, xCursorPosition, yCursorPosition);
431+
// TODO(b/281106755): add a MotionClassification value for fling stops.
432+
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
433+
/*actionButton=*/0, /*buttonState=*/0,
434+
/*pointerCount=*/1, &coords, xCursorPosition,
435+
yCursorPosition));
436+
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_CANCEL,
437+
/*actionButton=*/0, /*buttonState=*/0,
438+
/*pointerCount=*/1, &coords, xCursorPosition,
439+
yCursorPosition));
440+
out += enterHover(when, readTime, xCursorPosition, yCursorPosition);
441+
return out;
442+
} else {
443+
// Use the tap down state of a fling gesture as an indicator that a contact
444+
// has been initiated with the touchpad. We treat this as a move event with zero
445+
// magnitude, which will also result in the pointer icon being updated.
446+
// TODO(b/282023644): Add a signal in libgestures for when a stable contact has
447+
// been initiated with a touchpad.
448+
return handleMove(when, readTime, gestureStartTime,
449+
Gesture(kGestureMove, gesture.start_time, gesture.end_time,
450+
/*dx=*/0.f,
451+
/*dy=*/0.f));
452+
}
417453
}
418454
break;
419455
default:

services/inputflinger/reader/mapper/gestures/GestureConverter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ class GestureConverter {
106106
InputReaderContext& mReaderContext;
107107
std::shared_ptr<PointerControllerInterface> mPointerController;
108108
const bool mEnablePointerChoreographer;
109+
const bool mEnableFlingStop;
109110

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

124128
MotionClassification mCurrentClassification = MotionClassification::NONE;
125129
// Only used when mCurrentClassification is MULTI_FINGER_SWIPE.

services/inputflinger/tests/GestureConverter_test.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,38 @@ TEST_F(GestureConverterTest, FlingTapDown) {
11671167
ASSERT_TRUE(mFakePointerController->isPointerShown());
11681168
}
11691169

1170+
TEST_F(GestureConverterTest, FlingTapDownAfterScrollStopsFling) {
1171+
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
1172+
input_flags::enable_touchpad_fling_stop(true);
1173+
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
1174+
converter.setDisplayId(ADISPLAY_ID_DEFAULT);
1175+
1176+
Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
1177+
std::list<NotifyArgs> args =
1178+
converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, scrollGesture);
1179+
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
1180+
GESTURES_FLING_START);
1181+
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
1182+
1183+
Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
1184+
/*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
1185+
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture);
1186+
ASSERT_THAT(args,
1187+
ElementsAre(VariantWith<NotifyMotionArgs>(
1188+
WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)),
1189+
VariantWith<NotifyMotionArgs>(
1190+
WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
1191+
VariantWith<NotifyMotionArgs>(
1192+
WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)),
1193+
VariantWith<NotifyMotionArgs>(
1194+
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
1195+
WithMotionClassification(MotionClassification::NONE)))));
1196+
ASSERT_THAT(args,
1197+
Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y),
1198+
WithToolType(ToolType::FINGER),
1199+
WithDisplayId(ADISPLAY_ID_DEFAULT)))));
1200+
}
1201+
11701202
TEST_F(GestureConverterTest, Tap) {
11711203
// Tap should produce button press/release events
11721204
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
@@ -2556,6 +2588,38 @@ TEST_F(GestureConverterTestWithChoreographer, FlingTapDown) {
25562588
WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
25572589
}
25582590

2591+
TEST_F(GestureConverterTestWithChoreographer, FlingTapDownAfterScrollStopsFling) {
2592+
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
2593+
input_flags::enable_touchpad_fling_stop(true);
2594+
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
2595+
converter.setDisplayId(ADISPLAY_ID_DEFAULT);
2596+
2597+
Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
2598+
std::list<NotifyArgs> args =
2599+
converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, scrollGesture);
2600+
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
2601+
GESTURES_FLING_START);
2602+
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
2603+
2604+
Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
2605+
/*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
2606+
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture);
2607+
ASSERT_THAT(args,
2608+
ElementsAre(VariantWith<NotifyMotionArgs>(
2609+
WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)),
2610+
VariantWith<NotifyMotionArgs>(
2611+
WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
2612+
VariantWith<NotifyMotionArgs>(
2613+
WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)),
2614+
VariantWith<NotifyMotionArgs>(
2615+
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
2616+
WithMotionClassification(MotionClassification::NONE)))));
2617+
ASSERT_THAT(args,
2618+
Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
2619+
WithToolType(ToolType::FINGER),
2620+
WithDisplayId(ADISPLAY_ID_DEFAULT)))));
2621+
}
2622+
25592623
TEST_F(GestureConverterTestWithChoreographer, Tap) {
25602624
// Tap should produce button press/release events
25612625
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);

0 commit comments

Comments
 (0)