diff options
14 files changed, 1921 insertions, 85 deletions
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index e529bddea5..f1faf69758 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -31,6 +31,11 @@ bool isFromMouse(const NotifyMotionArgs& args) { args.pointerProperties[0].toolType == ToolType::MOUSE; } +bool isFromTouchpad(const NotifyMotionArgs& args) { + return isFromSource(args.source, AINPUT_SOURCE_MOUSE) && + args.pointerProperties[0].toolType == ToolType::FINGER; +} + bool isHoverAction(int32_t action) { return action == AMOTION_EVENT_ACTION_HOVER_ENTER || action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT; @@ -83,6 +88,8 @@ NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& arg if (isFromMouse(args)) { return processMouseEventLocked(args); + } else if (isFromTouchpad(args)) { + return processTouchpadEventLocked(args); } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) { processStylusHoverEventLocked(args); } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) { @@ -97,17 +104,7 @@ NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotio << args.dump(); } - const int32_t displayId = getTargetMouseDisplayLocked(args.displayId); - - // Get the mouse pointer controller for the display, or create one if it doesn't exist. - auto [it, emplaced] = - mMousePointersByDisplay.try_emplace(displayId, - getMouseControllerConstructor(displayId)); - if (emplaced) { - notifyPointerDisplayIdChangedLocked(); - } - - PointerControllerInterface& pc = *it->second; + auto [displayId, pc] = getDisplayIdAndMouseControllerLocked(args.displayId); const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); @@ -124,6 +121,40 @@ NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotio return newArgs; } +NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMotionArgs& args) { + auto [displayId, pc] = getDisplayIdAndMouseControllerLocked(args.displayId); + + NotifyMotionArgs newArgs(args); + newArgs.displayId = displayId; + if (args.getPointerCount() == 1 && args.classification == MotionClassification::NONE) { + // This is a movement of the mouse pointer. + const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + pc.move(deltaX, deltaY); + pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + + const auto [x, y] = pc.getPosition(); + newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + newArgs.xCursorPosition = x; + newArgs.yCursorPosition = y; + } else { + // This is a trackpad gesture with fake finger(s) that should not move the mouse pointer. + pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + + const auto [x, y] = pc.getPosition(); + for (uint32_t i = 0; i < newArgs.getPointerCount(); i++) { + newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, + args.pointerCoords[i].getX() + x); + newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, + args.pointerCoords[i].getY() + y); + } + newArgs.xCursorPosition = x; + newArgs.yCursorPosition = y; + } + return newArgs; +} + /** * When screen is touched, fade the mouse pointer on that display. We only call fade for * ACTION_DOWN events.This would allow both mouse and touch to be used at the same time if the @@ -270,6 +301,21 @@ int32_t PointerChoreographer::getTargetMouseDisplayLocked(int32_t associatedDisp return associatedDisplayId == ADISPLAY_ID_NONE ? mDefaultMouseDisplayId : associatedDisplayId; } +std::pair<int32_t, PointerControllerInterface&> +PointerChoreographer::getDisplayIdAndMouseControllerLocked(int32_t associatedDisplayId) { + const int32_t displayId = getTargetMouseDisplayLocked(associatedDisplayId); + + // Get the mouse pointer controller for the display, or create one if it doesn't exist. + auto [it, emplaced] = + mMousePointersByDisplay.try_emplace(displayId, + getMouseControllerConstructor(displayId)); + if (emplaced) { + notifyPointerDisplayIdChangedLocked(); + } + + return {displayId, *it->second}; +} + InputDeviceInfo* PointerChoreographer::findInputDeviceLocked(DeviceId deviceId) { auto it = std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(), [deviceId](const auto& info) { return info.getId() == deviceId; }); diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h index 26d2fef5c3..90a0d3fc72 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -95,10 +95,13 @@ private: void notifyPointerDisplayIdChangedLocked() REQUIRES(mLock); const DisplayViewport* findViewportByIdLocked(int32_t displayId) const REQUIRES(mLock); int32_t getTargetMouseDisplayLocked(int32_t associatedDisplayId) const REQUIRES(mLock); + std::pair<int32_t, PointerControllerInterface&> getDisplayIdAndMouseControllerLocked( + int32_t associatedDisplayId) REQUIRES(mLock); InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock); NotifyMotionArgs processMotion(const NotifyMotionArgs& args); NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); + NotifyMotionArgs processTouchpadEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); void processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); void processStylusHoverEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); void processDeviceReset(const NotifyDeviceResetArgs& args); diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 7aeb215174..58e35a6aba 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -527,10 +527,8 @@ void CursorInputMapper::configureOnChangeDisplayInfo(const InputReaderConfigurat if (mEnablePointerChoreographer) { // Always use DISPLAY_ID_NONE for mouse events. // PointerChoreographer will make it target the correct the displayId later. - const auto pointerViewport = - getContext()->getPolicy()->getPointerViewportForAssociatedDisplay(); - mDisplayId = pointerViewport ? std::make_optional(ADISPLAY_ID_NONE) : std::nullopt; - resolvedViewport = pointerViewport; + resolvedViewport = getContext()->getPolicy()->getPointerViewportForAssociatedDisplay(); + mDisplayId = resolvedViewport ? std::make_optional(ADISPLAY_ID_NONE) : std::nullopt; } else { mDisplayId = mPointerController->getDisplayId(); if (auto v = config.getDisplayViewportById(*mDisplayId); v) { diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index c76fec7280..34ca0b3767 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -246,7 +246,8 @@ TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext, mStateConverter(deviceContext, mMotionAccumulator), mGestureConverter(*getContext(), deviceContext, getDeviceId()), mCapturedEventConverter(*getContext(), deviceContext, mMotionAccumulator, getDeviceId()), - mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())) { + mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())), + mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()) { RawAbsoluteAxisInfo slotAxisInfo; deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo); if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) { @@ -331,31 +332,56 @@ std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when, if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) { mDisplayId = ADISPLAY_ID_NONE; - if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) { + std::optional<DisplayViewport> resolvedViewport; + std::optional<FloatRect> boundsInLogicalDisplay; + if (auto assocViewport = mDeviceContext.getAssociatedViewport(); assocViewport) { // This InputDevice is associated with a viewport. // Only generate events for the associated display. - const bool mismatchedPointerDisplay = - (viewport->displayId != mPointerController->getDisplayId()); - if (mismatchedPointerDisplay) { - ALOGW("Touchpad \"%s\" associated viewport display does not match pointer " - "controller", - mDeviceContext.getName().c_str()); + mDisplayId = assocViewport->displayId; + resolvedViewport = *assocViewport; + if (!mEnablePointerChoreographer) { + const bool mismatchedPointerDisplay = + (assocViewport->displayId != mPointerController->getDisplayId()); + if (mismatchedPointerDisplay) { + ALOGW("Touchpad \"%s\" associated viewport display does not match pointer " + "controller", + mDeviceContext.getName().c_str()); + mDisplayId.reset(); + } } - mDisplayId = mismatchedPointerDisplay ? std::nullopt - : std::make_optional(viewport->displayId); } else { // The InputDevice is not associated with a viewport, but it controls the mouse pointer. - mDisplayId = mPointerController->getDisplayId(); - } - - ui::Rotation orientation = ui::ROTATION_0; - if (mDisplayId.has_value()) { - if (auto viewport = config.getDisplayViewportById(*mDisplayId); viewport) { - orientation = getInverseRotation(viewport->orientation); + if (mEnablePointerChoreographer) { + // Always use DISPLAY_ID_NONE for touchpad events. + // PointerChoreographer will make it target the correct the displayId later. + resolvedViewport = + getContext()->getPolicy()->getPointerViewportForAssociatedDisplay(); + mDisplayId = resolvedViewport ? std::make_optional(ADISPLAY_ID_NONE) : std::nullopt; + } else { + mDisplayId = mPointerController->getDisplayId(); + if (auto v = config.getDisplayViewportById(*mDisplayId); v) { + resolvedViewport = *v; + } + if (auto bounds = mPointerController->getBounds(); bounds) { + boundsInLogicalDisplay = *bounds; + } } } + mGestureConverter.setDisplayId(mDisplayId); - mGestureConverter.setOrientation(orientation); + mGestureConverter.setOrientation(resolvedViewport + ? getInverseRotation(resolvedViewport->orientation) + : ui::ROTATION_0); + + if (!boundsInLogicalDisplay) { + boundsInLogicalDisplay = resolvedViewport + ? FloatRect{static_cast<float>(resolvedViewport->logicalLeft), + static_cast<float>(resolvedViewport->logicalTop), + static_cast<float>(resolvedViewport->logicalRight - 1), + static_cast<float>(resolvedViewport->logicalBottom - 1)} + : FloatRect{0, 0, 0, 0}; + } + mGestureConverter.setBoundsInLogicalDisplay(*boundsInLogicalDisplay); } if (!changes.any() || changes.test(InputReaderConfiguration::Change::TOUCHPAD_SETTINGS)) { mPropertyProvider.getProperty("Use Custom Touchpad Pointer Accel Curve") diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h index a68ae43912..ece0eca0e7 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h @@ -107,6 +107,8 @@ private: // Tracking IDs for touches that have at some point been reported as palms by the touchpad. std::set<int32_t> mPalmTrackingIds; + const bool mEnablePointerChoreographer; + // The display that events generated by this mapper should target. This can be set to // ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e. // std::nullopt), all events will be ignored. diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp index 4d2b66d56d..955210479f 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp @@ -60,7 +60,8 @@ GestureConverter::GestureConverter(InputReaderContext& readerContext, const InputDeviceContext& deviceContext, int32_t deviceId) : mDeviceId(deviceId), mReaderContext(readerContext), - mPointerController(readerContext.getPointerController(deviceId)) { + mPointerController(readerContext.getPointerController(deviceId)), + mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()) { deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo); deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo); } @@ -110,9 +111,11 @@ void GestureConverter::populateMotionRanges(InputDeviceInfo& info) const { // TODO(b/259547750): set this using the raw axis ranges from the touchpad when pointer capture // is enabled. - if (std::optional<FloatRect> rect = mPointerController->getBounds(); rect.has_value()) { - info.addMotionRange(AMOTION_EVENT_AXIS_X, SOURCE, rect->left, rect->right, 0, 0, 0); - info.addMotionRange(AMOTION_EVENT_AXIS_Y, SOURCE, rect->top, rect->bottom, 0, 0, 0); + if (!mBoundsInLogicalDisplay.isEmpty()) { + info.addMotionRange(AMOTION_EVENT_AXIS_X, SOURCE, mBoundsInLogicalDisplay.left, + mBoundsInLogicalDisplay.right, 0, 0, 0); + info.addMotionRange(AMOTION_EVENT_AXIS_Y, SOURCE, mBoundsInLogicalDisplay.top, + mBoundsInLogicalDisplay.bottom, 0, 0, 0); } info.addMotionRange(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, SOURCE, -1.0f, 1.0f, 0, 0, 0); @@ -172,7 +175,8 @@ NotifyMotionArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime, mPointerController->move(deltaX, deltaY); mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); - const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition(); + const auto [xCursorPosition, yCursorPosition] = + mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); PointerCoords coords; coords.clear(); @@ -196,7 +200,8 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); - const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition(); + const auto [xCursorPosition, yCursorPosition] = + mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); PointerCoords coords; coords.clear(); @@ -273,7 +278,8 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ std::list<NotifyArgs> GestureConverter::releaseAllButtons(nsecs_t when, nsecs_t readTime) { std::list<NotifyArgs> out; - const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition(); + const auto [xCursorPosition, yCursorPosition] = + mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); PointerCoords coords; coords.clear(); @@ -308,7 +314,8 @@ std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readT const Gesture& gesture) { std::list<NotifyArgs> out; PointerCoords& coords = mFakeFingerCoords[0]; - const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition(); + const auto [xCursorPosition, yCursorPosition] = + mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); if (mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) { mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE; coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); @@ -376,7 +383,8 @@ std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTi } NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { - const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition(); + const auto [xCursorPosition, yCursorPosition] = + mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0); NotifyMotionArgs args = @@ -394,7 +402,8 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { float dx, float dy) { std::list<NotifyArgs> out = {}; - const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition(); + const auto [xCursorPosition, yCursorPosition] = + mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) { // If the user changes the number of fingers mid-way through a swipe (e.g. they start with // three and then put a fourth finger down), the gesture library will treat it as two @@ -457,7 +466,8 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) { return out; } - const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition(); + const auto [xCursorPosition, yCursorPosition] = + mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, 0); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, 0); @@ -481,7 +491,8 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { [[nodiscard]] std::list<NotifyArgs> GestureConverter::handlePinch(nsecs_t when, nsecs_t readTime, const Gesture& gesture) { - const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition(); + const auto [xCursorPosition, yCursorPosition] = + mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); // Pinch gesture phases are reported a little differently from others, in that the same details // struct is used for all phases of the gesture, just with different zoom_state values. When @@ -538,7 +549,8 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { std::list<NotifyArgs> GestureConverter::endPinch(nsecs_t when, nsecs_t readTime) { std::list<NotifyArgs> out; - const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition(); + const auto [xCursorPosition, yCursorPosition] = + mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0); out.push_back(makeMotionArgs(when, readTime, diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h index e6cf617500..732a4b2ffb 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h @@ -48,6 +48,8 @@ public: void setDisplayId(std::optional<int32_t> displayId) { mDisplayId = displayId; } + void setBoundsInLogicalDisplay(FloatRect bounds) { mBoundsInLogicalDisplay = bounds; } + void populateMotionRanges(InputDeviceInfo& info) const; [[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime, @@ -85,8 +87,10 @@ private: const int32_t mDeviceId; InputReaderContext& mReaderContext; std::shared_ptr<PointerControllerInterface> mPointerController; + const bool mEnablePointerChoreographer; std::optional<int32_t> mDisplayId; + FloatRect mBoundsInLogicalDisplay{}; ui::Rotation mOrientation = ui::ROTATION_0; RawAbsoluteAxisInfo mXAxisInfo; RawAbsoluteAxisInfo mYAxisInfo; diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp index 5475594113..8043812999 100644 --- a/services/inputflinger/tests/FakePointerController.cpp +++ b/services/inputflinger/tests/FakePointerController.cpp @@ -42,7 +42,10 @@ FloatPoint FakePointerController::getPosition() const { } int32_t FakePointerController::getDisplayId() const { - return mDisplayId; + if (!mDisplayId) { + return ADISPLAY_ID_NONE; + } + return *mDisplayId; } void FakePointerController::setDisplayViewport(const DisplayViewport& viewport) { @@ -51,6 +54,15 @@ void FakePointerController::setDisplayViewport(const DisplayViewport& viewport) viewport.logicalBottom - 1); } +void FakePointerController::assertViewportSet(int32_t displayId) { + ASSERT_TRUE(mDisplayId); + ASSERT_EQ(displayId, mDisplayId); +} + +void FakePointerController::assertViewportNotSet() { + ASSERT_EQ(std::nullopt, mDisplayId); +} + void FakePointerController::assertPosition(float x, float y) { const auto [actualX, actualY] = getPosition(); ASSERT_NEAR(x, actualX, 1); diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h index d7e40b3cc1..9be6a6c7b4 100644 --- a/services/inputflinger/tests/FakePointerController.h +++ b/services/inputflinger/tests/FakePointerController.h @@ -36,6 +36,8 @@ public: int32_t getDisplayId() const override; void setDisplayViewport(const DisplayViewport& viewport) override; + void assertViewportSet(int32_t displayId); + void assertViewportNotSet(); void assertPosition(float x, float y); void assertSpotCount(int32_t displayId, int32_t count); bool isPointerShown(); @@ -54,7 +56,7 @@ private: bool mHaveBounds{false}; float mMinX{0}, mMinY{0}, mMaxX{0}, mMaxY{0}; float mX{0}, mY{0}; - int32_t mDisplayId{ADISPLAY_ID_NONE}; + std::optional<int32_t> mDisplayId; bool mIsPointerShown{false}; std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay; diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp index 41c7392cbc..22c1eefa15 100644 --- a/services/inputflinger/tests/GestureConverter_test.cpp +++ b/services/inputflinger/tests/GestureConverter_test.cpp @@ -35,16 +35,18 @@ namespace android { +namespace input_flags = com::android::input::flags; + namespace { const auto TOUCHPAD_PALM_REJECTION = - ACONFIG_FLAG(com::android::input::flags, enable_touchpad_typing_palm_rejection); + ACONFIG_FLAG(input_flags, enable_touchpad_typing_palm_rejection); } // namespace using testing::AllOf; -class GestureConverterTest : public testing::Test { +class GestureConverterTestBase : public testing::Test { protected: static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000; static constexpr int32_t EVENTHUB_ID = 1; @@ -91,6 +93,14 @@ protected: std::shared_ptr<FakePointerController> mFakePointerController; }; +class GestureConverterTest : public GestureConverterTestBase { +protected: + void SetUp() override { + input_flags::enable_pointer_choreographer(false); + GestureConverterTestBase::SetUp(); + } +}; + TEST_F(GestureConverterTest, Move) { InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); @@ -1295,4 +1305,1180 @@ TEST_F_WITH_FLAGS(GestureConverterTest, MoveEnablesTapToClick, ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); } +class GestureConverterTestWithChoreographer : public GestureConverterTestBase { +protected: + void SetUp() override { + input_flags::enable_pointer_choreographer(true); + GestureConverterTestBase::SetUp(); + } +}; + +TEST_F(GestureConverterTestWithChoreographer, Move) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); + ASSERT_EQ(1u, args.size()); + + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, Move_Rotated) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setOrientation(ui::ROTATION_90); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); + ASSERT_EQ(1u, args.size()); + + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(10, 5), WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, ButtonsChange) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + // Press left and right buttons at once + Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* down= */ GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT, + /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture); + ASSERT_EQ(3u, args.size()); + + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | + AMOTION_EVENT_BUTTON_SECONDARY), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY | + AMOTION_EVENT_BUTTON_SECONDARY), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + + // Then release the left button + Gesture leftUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT, + /* is_tap= */ false); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, leftUpGesture); + ASSERT_EQ(1u, args.size()); + + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), WithCoords(0, 0), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + + // Finally release the right button + Gesture rightUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_RIGHT, + /* is_tap= */ false); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, rightUpGesture); + ASSERT_EQ(3u, args.size()); + + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, DragWithButton) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + // Press the button + Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_NONE, + /* is_tap= */ false); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture); + ASSERT_EQ(2u, args.size()); + + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + + // Move + 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(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, 0), + WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + + // Release the button + Gesture upGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT, + /* is_tap= */ false); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, upGesture); + ASSERT_EQ(3u, args.size()); + + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithButtonState(0), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, Scroll) { + const nsecs_t downTime = 12345; + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + 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(0, 0), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), WithDownTime(downTime), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, -10), + WithGestureScrollDistance(0, 10, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + + 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(0, -15), + WithGestureScrollDistance(0, 5, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + + 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(0, 0 - 15), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, Scroll_Rotated) { + const nsecs_t downTime = 12345; + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setOrientation(ui::ROTATION_90); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + 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(0, 0), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), WithDownTime(downTime), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(-10, 0), + WithGestureScrollDistance(0, 10, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + + 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(-15, 0), + WithGestureScrollDistance(0, 5, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + + 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(-15, 0), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsClassificationAfterGesture) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + 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()); + EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionClassification(MotionClassification::NONE), + WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsScrollDistanceAfterGesture) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + 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); + + // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we + // need to use another gesture type, like pinch. + Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, + GESTURES_ZOOM_START); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, pinchGesture); + ASSERT_FALSE(args.empty()); + EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGestureScrollDistance(0, 0, EPSILON)); +} + +TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_ClearsClassificationAfterGesture) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0, + /*dy=*/0); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + + Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); + + Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/-5, + /*dy=*/10); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); + ASSERT_EQ(1u, args.size()); + EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), + WithMotionClassification(MotionClassification::NONE)); +} + +TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_ClearsGestureAxesAfterGesture) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/5, + /*dy=*/5); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + + Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); + + // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we + // need to use another gesture type, like pinch. + Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, + GESTURES_ZOOM_START); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, pinchGesture); + ASSERT_FALSE(args.empty()); + EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(0))); +} + +TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Vertical) { + // The gestures library will "lock" a swipe into the dimension it starts in. For example, if you + // start swiping up and then start moving left or right, it'll return gesture events with only Y + // deltas until you lift your fingers and start swiping again. That's why each of these tests + // only checks movement in one dimension. + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, + /* dy= */ 10); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + ASSERT_EQ(4u, args.size()); + + // Three fake fingers should be created. We don't actually care where they are, so long as they + // move appropriately. + NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(3), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + PointerCoords finger0Start = arg.pointerCoords[0]; + args.pop_front(); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + PointerCoords finger1Start = arg.pointerCoords[1]; + args.pop_front(); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + PointerCoords finger2Start = arg.pointerCoords[2]; + args.pop_front(); + + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithGestureOffset(0, -0.01, EPSILON), WithGestureSwipeFingerCount(3), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX()); + EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX()); + EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX()); + EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY() - 10); + EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY() - 10); + EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY() - 10); + + Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* dx= */ 0, /* dy= */ 5); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + ASSERT_EQ(1u, args.size()); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithGestureOffset(0, -0.005, EPSILON), WithGestureSwipeFingerCount(3), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX()); + EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX()); + EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX()); + EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY() - 15); + EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY() - 15); + EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY() - 15); + + Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); + ASSERT_EQ(3u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(3), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(3), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Rotated) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setOrientation(ui::ROTATION_90); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0, + /* dy= */ 10); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + ASSERT_EQ(4u, args.size()); + + // Three fake fingers should be created. We don't actually care where they are, so long as they + // move appropriately. + NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON), + WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT))); + PointerCoords finger0Start = arg.pointerCoords[0]; + args.pop_front(); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + PointerCoords finger1Start = arg.pointerCoords[1]; + args.pop_front(); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + PointerCoords finger2Start = arg.pointerCoords[2]; + args.pop_front(); + + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 10); + EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 10); + EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 10); + EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY()); + EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY()); + EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY()); + + Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* dx= */ 0, /* dy= */ 5); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + ASSERT_EQ(1u, args.size()); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithGestureOffset(0, -0.005, EPSILON), WithPointerCount(3u), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 15); + EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 15); + EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 15); + EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY()); + EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY()); + EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY()); + + Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); + ASSERT_EQ(3u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), + WithPointerCount(1u), WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, FourFingerSwipe_Horizontal) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture startGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* dx= */ 10, /* dy= */ 0); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + ASSERT_EQ(5u, args.size()); + + // Four fake fingers should be created. We don't actually care where they are, so long as they + // move appropriately. + NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + PointerCoords finger0Start = arg.pointerCoords[0]; + args.pop_front(); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + PointerCoords finger1Start = arg.pointerCoords[1]; + args.pop_front(); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + PointerCoords finger2Start = arg.pointerCoords[2]; + args.pop_front(); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(4u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + PointerCoords finger3Start = arg.pointerCoords[3]; + args.pop_front(); + + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithGestureOffset(0.01, 0, EPSILON), WithGestureSwipeFingerCount(4), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(4u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 10); + EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 10); + EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 10); + EXPECT_EQ(arg.pointerCoords[3].getX(), finger3Start.getX() + 10); + EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY()); + EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY()); + EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY()); + EXPECT_EQ(arg.pointerCoords[3].getY(), finger3Start.getY()); + + Gesture continueGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* dx= */ 5, /* dy= */ 0); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture); + ASSERT_EQ(1u, args.size()); + arg = std::get<NotifyMotionArgs>(args.front()); + ASSERT_THAT(arg, + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithGestureOffset(0.005, 0, EPSILON), WithGestureSwipeFingerCount(4), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(4u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 15); + EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 15); + EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 15); + EXPECT_EQ(arg.pointerCoords[3].getX(), finger3Start.getX() + 15); + EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY()); + EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY()); + EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY()); + EXPECT_EQ(arg.pointerCoords[3].getY(), finger3Start.getY()); + + Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, liftGesture); + ASSERT_EQ(4u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(4u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(4), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), + WithGestureSwipeFingerCount(4), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, Pinch_Inwards) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, + GESTURES_ZOOM_START); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + ASSERT_EQ(2u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), WithCoords(-100, 0), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCoords(1, 100, 0), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + + Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* dz= */ 0.8, GESTURES_ZOOM_UPDATE); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(0.8f, EPSILON), WithPointerCoords(0, -80, 0), + WithPointerCoords(1, 80, 0), WithPointerCount(2u), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + + Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, + GESTURES_ZOOM_END); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); + ASSERT_EQ(2u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, Pinch_Outwards) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, + GESTURES_ZOOM_START); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + ASSERT_EQ(2u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), WithCoords(-100, 0), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCoords(1, 100, 0), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + + Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* dz= */ 1.1, GESTURES_ZOOM_UPDATE); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.1f, EPSILON), WithPointerCoords(0, -110, 0), + WithPointerCoords(1, 110, 0), WithPointerCount(2u), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + + Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, + GESTURES_ZOOM_END); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); + ASSERT_EQ(2u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, Pinch_ClearsClassificationAfterGesture) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, + GESTURES_ZOOM_START); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + + Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /*dz=*/1.2, GESTURES_ZOOM_UPDATE); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); + + Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, + GESTURES_ZOOM_END); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); + + Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); + ASSERT_EQ(1u, args.size()); + EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), + WithMotionClassification(MotionClassification::NONE)); +} + +TEST_F(GestureConverterTestWithChoreographer, Pinch_ClearsScaleFactorAfterGesture) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, + GESTURES_ZOOM_START); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + + Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /*dz=*/1.2, GESTURES_ZOOM_UPDATE); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, updateGesture); + + Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, + GESTURES_ZOOM_END); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, endGesture); + + // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we + // need to use another gesture type, like scroll. + Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/1, + /*dy=*/0); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, scrollGesture); + ASSERT_FALSE(args.empty()); + EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGesturePinchScaleFactor(0, EPSILON)); +} + +TEST_F(GestureConverterTestWithChoreographer, ResetWithButtonPressed) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /*down=*/GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT, + /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture); + + std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); + ASSERT_EQ(3u, args.size()); + + EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), WithCoords(0, 0), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, ResetDuringScroll) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + + std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); + ASSERT_EQ(1u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, -10), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, ResetDuringThreeFingerSwipe) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0, + /*dy=*/10); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + + std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); + ASSERT_EQ(3u, args.size()); + EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(3u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithGestureOffset(0, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(2u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, ResetDuringPinch) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1, + GESTURES_ZOOM_START); + (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture); + + std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); + ASSERT_EQ(2u, args.size()); + EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP | + 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMotionClassification(MotionClassification::PINCH), + WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, FlingTapDown) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapDownGesture); + + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, Tap) { + // Tap should produce button press/release events + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, + /* vy= */ 0, GESTURES_FLING_TAP_DOWN); + std::list<NotifyArgs> 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_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + + Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture); + + ASSERT_EQ(5u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F(GestureConverterTestWithChoreographer, Click) { + // Click should produce button press/release events + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, + /* vy= */ 0, GESTURES_FLING_TAP_DOWN); + std::list<NotifyArgs> 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_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + + Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture); + + ASSERT_EQ(2u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + + Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* down= */ GESTURES_BUTTON_NONE, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture); + + ASSERT_EQ(3u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); +} + +TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabled, + REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) { + // Tap should be ignored when disabled + mReader->getContext()->setPreventingTouchpadTaps(true); + + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, + /* vy= */ 0, GESTURES_FLING_TAP_DOWN); + std::list<NotifyArgs> 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_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + + Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapGesture); + + // no events should be generated + ASSERT_EQ(0u, args.size()); + + // Future taps should be re-enabled + ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); +} + +TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, ClickWithTapToClickDisabled, + REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) { + // Click should still produce button press/release events + mReader->getContext()->setPreventingTouchpadTaps(true); + + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0, + /* vy= */ 0, GESTURES_FLING_TAP_DOWN); + std::list<NotifyArgs> 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_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + + Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* down= */ GESTURES_BUTTON_LEFT, + /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonDownGesture); + ASSERT_EQ(2u, args.size()); + + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + + Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /* down= */ GESTURES_BUTTON_NONE, + /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, buttonUpGesture); + + ASSERT_EQ(3u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(1.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0), + WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + args.pop_front(); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + + // Future taps should be re-enabled + ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); +} + +TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, MoveEnablesTapToClick, + REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) { + // initially disable tap-to-click + mReader->getContext()->setPreventingTouchpadTaps(true); + + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); + std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture); + ASSERT_EQ(1u, args.size()); + + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), + WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + + // Future taps should be re-enabled + ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); +} + } // namespace android diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp index 787444c9e6..36be684f96 100644 --- a/services/inputflinger/tests/InputMapperTest.cpp +++ b/services/inputflinger/tests/InputMapperTest.cpp @@ -28,10 +28,13 @@ void InputMapperUnitTest::SetUp() { mFakePointerController = std::make_shared<FakePointerController>(); mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); mFakePointerController->setPosition(INITIAL_CURSOR_X, INITIAL_CURSOR_Y); + mFakePolicy = sp<FakeInputReaderPolicy>::make(); EXPECT_CALL(mMockInputReaderContext, getPointerController(DEVICE_ID)) .WillRepeatedly(Return(mFakePointerController)); + EXPECT_CALL(mMockInputReaderContext, getPolicy()).WillRepeatedly(Return(mFakePolicy.get())); + EXPECT_CALL(mMockInputReaderContext, getEventHub()).WillRepeatedly(Return(&mMockEventHub)); InputDeviceIdentifier identifier; identifier.name = "device"; diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h index 3f9061fc6a..05b0e97ff6 100644 --- a/services/inputflinger/tests/InputMapperTest.h +++ b/services/inputflinger/tests/InputMapperTest.h @@ -55,6 +55,7 @@ protected: std::list<NotifyArgs> process(nsecs_t when, int32_t type, int32_t code, int32_t value); MockEventHubInterface mMockEventHub; + sp<FakeInputReaderPolicy> mFakePolicy; std::shared_ptr<FakePointerController> mFakePointerController; MockInputReaderContext mMockInputReaderContext; std::unique_ptr<InputDevice> mDevice; diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index 68f58571f5..1efb7979f4 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -50,6 +50,9 @@ const auto MOUSE_POINTER = PointerBuilder(/*id=*/0, ToolType::MOUSE) const auto FIRST_TOUCH_POINTER = PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200); const auto SECOND_TOUCH_POINTER = PointerBuilder(/*id=*/1, ToolType::FINGER).x(200).y(300); const auto STYLUS_POINTER = PointerBuilder(/*id=*/0, ToolType::STYLUS).x(100).y(200); +const auto TOUCHPAD_POINTER = PointerBuilder(/*id=*/0, ToolType::FINGER) + .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 10) + .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 20); static InputDeviceInfo generateTestDeviceInfo(int32_t deviceId, uint32_t source, int32_t associatedDisplayId) { @@ -237,7 +240,7 @@ TEST_F(PointerChoreographerTest, SetsViewportForAssociatedMouse) { .displayId(DISPLAY_ID) .build()); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); - ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); + pc->assertViewportSet(DISPLAY_ID); } TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedMouse) { @@ -252,11 +255,11 @@ TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedMo .displayId(DISPLAY_ID) .build()); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); - ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId()); + pc->assertViewportNotSet(); // After Choreographer gets viewport, PointerController should also have viewport. mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); + pc->assertViewportSet(DISPLAY_ID); } TEST_F(PointerChoreographerTest, SetsDefaultMouseViewportForPointerController) { @@ -274,7 +277,7 @@ TEST_F(PointerChoreographerTest, SetsDefaultMouseViewportForPointerController) { .displayId(ADISPLAY_ID_NONE) .build()); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); - ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); + pc->assertViewportSet(DISPLAY_ID); } TEST_F(PointerChoreographerTest, @@ -291,7 +294,7 @@ TEST_F(PointerChoreographerTest, .displayId(ADISPLAY_ID_NONE) .build()); auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE); - ASSERT_EQ(DISPLAY_ID, firstDisplayPc->getDisplayId()); + firstDisplayPc->assertViewportSet(DISPLAY_ID); // Change default mouse display. Existing PointerController should be removed. mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID); @@ -306,7 +309,7 @@ TEST_F(PointerChoreographerTest, .displayId(ADISPLAY_ID_NONE) .build()); auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE); - ASSERT_EQ(ANOTHER_DISPLAY_ID, secondDisplayPc->getDisplayId()); + secondDisplayPc->assertViewportSet(ANOTHER_DISPLAY_ID); } TEST_F(PointerChoreographerTest, CallsNotifyPointerDisplayIdChanged) { @@ -410,7 +413,7 @@ TEST_F(PointerChoreographerTest, MouseMovesPointerAndReturnsNewArgs) { auto pc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); - // Set bounds and initial position of the PointerController. + // Set initial position of the PointerController. pc->setPosition(100, 200); // Make NotifyMotionArgs and notify Choreographer. @@ -461,7 +464,7 @@ TEST_F(PointerChoreographerTest, auto associatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId()); - // Set bounds and initial position for PointerControllers. + // Set initial position for PointerControllers. unassociatedMousePc->setPosition(100, 200); associatedMousePc->setPosition(300, 400); @@ -501,7 +504,7 @@ TEST_F(PointerChoreographerTest, DoesNotMovePointerForMouseRelativeSource) { auto pc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); - // Set bounds and initial position of the PointerController. + // Set initial position of the PointerController. pc->setPosition(100, 200); // Assume that pointer capture is enabled. @@ -549,20 +552,6 @@ TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledHidesPointer) { mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); - - // Set bounds and initial position of the PointerController. - pc->setPosition(100, 200); - - // Make NotifyMotionArgs and notify Choreographer. - mChoreographer.notifyMotion( - MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) - .pointer(MOUSE_POINTER) - .deviceId(DEVICE_ID) - .displayId(ADISPLAY_ID_NONE) - .build()); - - // Check that the PointerController updated the position and the pointer is shown. - pc->assertPosition(110, 220); ASSERT_TRUE(pc->isPointerShown()); // Enable pointer capture and check if the PointerController hid the pointer. @@ -641,7 +630,6 @@ TEST_F(PointerChoreographerTest, WhenShowTouchesDisabledRemovesPointerController mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}}); mChoreographer.setShowTouchesEnabled(true); - assertPointerControllerNotCreated(); mChoreographer.notifyMotion( MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) .pointer(FIRST_TOUCH_POINTER) @@ -885,8 +873,8 @@ TEST_F(PointerChoreographerTest, SetsViewportForStylusPointerController) { .build()); auto pc = assertPointerControllerCreated(ControllerType::STYLUS); - // Check that displayId is set. - ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); + // Check that viewport is set for the PointerController. + pc->assertViewportSet(DISPLAY_ID); } TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterSetsViewportForStylusPointerController) { @@ -902,14 +890,14 @@ TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterSetsViewportForStylusPoin .build()); auto pc = assertPointerControllerCreated(ControllerType::STYLUS); - // Check that displayId is unset. - ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId()); + // Check that viewport is unset. + pc->assertViewportNotSet(); // Set viewport. mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); - // Check that displayId is set. - ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); + // Check that the viewport is set for the PointerController. + pc->assertViewportSet(DISPLAY_ID); } TEST_F(PointerChoreographerTest, @@ -926,14 +914,14 @@ TEST_F(PointerChoreographerTest, .build()); auto pc = assertPointerControllerCreated(ControllerType::STYLUS); - // Check that displayId is unset. - ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId()); + // Check that viewport is unset. + pc->assertViewportNotSet(); // Set viewport which does not match the associated display of the stylus. mChoreographer.setDisplayViewports(createViewports({ANOTHER_DISPLAY_ID})); - // Check that displayId is still unset. - ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId()); + // Check that viewport is still unset. + pc->assertViewportNotSet(); } TEST_F(PointerChoreographerTest, StylusHoverManipulatesPointer) { @@ -1050,4 +1038,477 @@ TEST_F(PointerChoreographerTest, WhenStylusDeviceIsResetRemovesPointer) { assertPointerControllerRemoved(pc); } +TEST_F(PointerChoreographerTest, WhenTouchpadIsJustAddedDoesNotCreatePointerController) { + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ADISPLAY_ID_NONE)}}); + assertPointerControllerNotCreated(); +} + +TEST_F(PointerChoreographerTest, WhenTouchpadEventOccursCreatesPointerController) { + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + assertPointerControllerCreated(ControllerType::MOUSE); +} + +TEST_F(PointerChoreographerTest, WhenTouchpadIsRemovedRemovesPointerController) { + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + + // Remove the touchpad. + mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}}); + assertPointerControllerRemoved(pc); +} + +TEST_F(PointerChoreographerTest, SetsViewportForAssociatedTouchpad) { + // Just adding a viewport or device should not create a PointerController. + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + DISPLAY_ID)}}); + assertPointerControllerNotCreated(); + + // After the touchpad emits event, PointerController will be created and viewport will be set. + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + pc->assertViewportSet(DISPLAY_ID); +} + +TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedTouchpad) { + // Without viewport information, PointerController will be created by a touchpad event + // but viewport won't be set. + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + DISPLAY_ID)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + pc->assertViewportNotSet(); + + // After Choreographer gets viewport, PointerController should also have viewport. + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + pc->assertViewportSet(DISPLAY_ID); +} + +TEST_F(PointerChoreographerTest, SetsDefaultTouchpadViewportForPointerController) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + + // For a touchpad event without a target display, default viewport should be set for + // the PointerController. + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + pc->assertViewportSet(DISPLAY_ID); +} + +TEST_F(PointerChoreographerTest, + WhenDefaultTouchpadDisplayChangesSetsDefaultTouchpadViewportForPointerController) { + // Set one display as a default touchpad display and emit touchpad event to create + // PointerController. + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE); + firstDisplayPc->assertViewportSet(DISPLAY_ID); + + // Change default mouse display. Existing PointerController should be removed. + mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID); + assertPointerControllerRemoved(firstDisplayPc); + + // New PointerController for the new default display will be created by the motion event. + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE); + secondDisplayPc->assertViewportSet(ANOTHER_DISPLAY_ID); +} + +TEST_F(PointerChoreographerTest, TouchpadCallsNotifyPointerDisplayIdChanged) { + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + assertPointerControllerCreated(ControllerType::MOUSE); + + assertPointerDisplayIdNotified(DISPLAY_ID); +} + +TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterTouchpadCallsNotifyPointerDisplayIdChanged) { + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + assertPointerControllerCreated(ControllerType::MOUSE); + assertPointerDisplayIdNotNotified(); + + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + assertPointerDisplayIdNotified(DISPLAY_ID); +} + +TEST_F(PointerChoreographerTest, WhenTouchpadIsRemovedCallsNotifyPointerDisplayIdChanged) { + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + assertPointerDisplayIdNotified(DISPLAY_ID); + + mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}}); + assertPointerDisplayIdNotified(ADISPLAY_ID_NONE); + assertPointerControllerRemoved(pc); +} + +TEST_F(PointerChoreographerTest, + WhenDefaultMouseDisplayChangesTouchpadCallsNotifyPointerDisplayIdChanged) { + // Add two viewports. + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); + + // Set one viewport as a default mouse display ID. + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE); + assertPointerDisplayIdNotified(DISPLAY_ID); + + // Set another viewport as a default mouse display ID. ADISPLAY_ID_NONE will be notified + // before a touchpad event. + mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID); + assertPointerDisplayIdNotified(ADISPLAY_ID_NONE); + assertPointerControllerRemoved(firstDisplayPc); + + // After a touchpad event, pointer display ID will be notified with new default mouse display. + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + assertPointerControllerCreated(ControllerType::MOUSE); + assertPointerDisplayIdNotified(ANOTHER_DISPLAY_ID); +} + +TEST_F(PointerChoreographerTest, TouchpadMovesPointerAndReturnsNewArgs) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); + + // Set initial position of the PointerController. + pc->setPosition(100, 200); + + // Make NotifyMotionArgs and notify Choreographer. + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + + // Check that the PointerController updated the position and the pointer is shown. + pc->assertPosition(110, 220); + ASSERT_TRUE(pc->isPointerShown()); + + // Check that x-y cooridnates, displayId and cursor position are correctly updated. + mTestListener.assertNotifyMotionWasCalled( + AllOf(WithCoords(110, 220), WithDisplayId(DISPLAY_ID), WithCursorPosition(110, 220))); +} + +TEST_F(PointerChoreographerTest, TouchpadAddsPointerPositionToTheCoords) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); + + // Set initial position of the PointerController. + pc->setPosition(100, 200); + + // Notify motion with fake fingers, as if it is multi-finger swipe. + // Check if the position of the PointerController is added to the fake finger coords. + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(-100).y(0)) + .classification(MotionClassification::MULTI_FINGER_SWIPE) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + mTestListener.assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithCoords(0, 200), WithCursorPosition(100, 200))); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_MOUSE) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(-100).y(0)) + .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(0).y(0)) + .classification(MotionClassification::MULTI_FINGER_SWIPE) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + mTestListener.assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT)), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCoords(0, 0, 200), WithPointerCoords(1, 100, 200), + WithCursorPosition(100, 200))); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | + (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_MOUSE) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(-100).y(0)) + .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(0).y(0)) + .pointer(PointerBuilder(/*id=*/2, ToolType::FINGER).x(100).y(0)) + .classification(MotionClassification::MULTI_FINGER_SWIPE) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + mTestListener.assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN | + (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT)), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCoords(0, 0, 200), WithPointerCoords(1, 100, 200), + WithPointerCoords(2, 200, 200), WithCursorPosition(100, 200))); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(-90).y(10)) + .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(10).y(10)) + .pointer(PointerBuilder(/*id=*/2, ToolType::FINGER).x(110).y(10)) + .classification(MotionClassification::MULTI_FINGER_SWIPE) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + mTestListener.assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE), + WithPointerCoords(0, 10, 210), WithPointerCoords(1, 110, 210), + WithPointerCoords(2, 210, 210), WithCursorPosition(100, 200))); +} + +TEST_F(PointerChoreographerTest, + AssociatedTouchpadMovesPointerOnAssociatedDisplayAndDoesNotMovePointerOnDefaultDisplay) { + // Add two displays and set one to default. + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + + // Add two devices, one unassociated and the other associated with non-default mouse display. + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ADISPLAY_ID_NONE), + generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ANOTHER_DISPLAY_ID)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); + auto unassociatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId()); + + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(SECOND_DEVICE_ID) + .displayId(ANOTHER_DISPLAY_ID) + .build()); + mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); + auto associatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId()); + + // Set initial positions for PointerControllers. + unassociatedMousePc->setPosition(100, 200); + associatedMousePc->setPosition(300, 400); + + // Make NotifyMotionArgs from the associated mouse and notify Choreographer. + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(SECOND_DEVICE_ID) + .displayId(ANOTHER_DISPLAY_ID) + .build()); + + // Check the status of the PointerControllers. + unassociatedMousePc->assertPosition(100, 200); + ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId()); + associatedMousePc->assertPosition(310, 420); + ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId()); + ASSERT_TRUE(associatedMousePc->isPointerShown()); + + // Check that x-y cooridnates, displayId and cursor position are correctly updated. + mTestListener.assertNotifyMotionWasCalled( + AllOf(WithCoords(310, 420), WithDeviceId(SECOND_DEVICE_ID), + WithDisplayId(ANOTHER_DISPLAY_ID), WithCursorPosition(310, 420))); +} + +TEST_F(PointerChoreographerTest, DoesNotMovePointerForTouchpadSource) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); + + // Set initial position of the PointerController. + pc->setPosition(200, 300); + + // Assume that pointer capture is enabled. + mChoreographer.notifyPointerCaptureChanged( + NotifyPointerCaptureChangedArgs(/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC), + PointerCaptureRequest(/*enable=*/true, /*seq=*/0))); + + // Notify motion as if pointer capture is enabled. + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHPAD) + .pointer(FIRST_TOUCH_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + + // Check that there's no update on the PointerController. + pc->assertPosition(200, 300); + ASSERT_FALSE(pc->isPointerShown()); + + // Check x-y cooridnates, displayId and cursor position are not changed. + mTestListener.assertNotifyMotionWasCalled( + AllOf(WithCoords(100, 200), WithDisplayId(ADISPLAY_ID_NONE), + WithCursorPosition(AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION))); +} + +TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledTouchpadHidesPointer) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(TOUCHPAD_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); + ASSERT_TRUE(pc->isPointerShown()); + + // Enable pointer capture and check if the PointerController hid the pointer. + mChoreographer.notifyPointerCaptureChanged( + NotifyPointerCaptureChangedArgs(/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC), + PointerCaptureRequest(/*enable=*/true, /*seq=*/0))); + ASSERT_FALSE(pc->isPointerShown()); +} + } // namespace android diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp index 6203a1d0b9..7a0005b4f5 100644 --- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp +++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp @@ -19,6 +19,7 @@ #include <android-base/logging.h> #include <gtest/gtest.h> +#include <com_android_input_flags.h> #include <thread> #include "FakePointerController.h" #include "InputMapperTest.h" @@ -36,11 +37,17 @@ constexpr auto ACTION_UP = AMOTION_EVENT_ACTION_UP; constexpr auto BUTTON_PRESS = AMOTION_EVENT_ACTION_BUTTON_PRESS; constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE; constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE; +constexpr int32_t DISPLAY_ID = 0; +constexpr int32_t DISPLAY_WIDTH = 480; +constexpr int32_t DISPLAY_HEIGHT = 800; +constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified + +namespace input_flags = com::android::input::flags; /** * Unit tests for TouchpadInputMapper. */ -class TouchpadInputMapperTest : public InputMapperUnitTest { +class TouchpadInputMapperTestBase : public InputMapperUnitTest { protected: void SetUp() override { InputMapperUnitTest::SetUp(); @@ -104,6 +111,14 @@ protected: } }; +class TouchpadInputMapperTest : public TouchpadInputMapperTestBase { +protected: + void SetUp() override { + input_flags::enable_pointer_choreographer(false); + TouchpadInputMapperTestBase::SetUp(); + } +}; + /** * Start moving the finger and then click the left touchpad button. Check whether HOVER_EXIT is * generated when hovering stops. Currently, it is not. @@ -153,4 +168,69 @@ TEST_F(TouchpadInputMapperTest, HoverAndLeftButtonPress) { ASSERT_THAT(args, testing::IsEmpty()); } +class TouchpadInputMapperTestWithChoreographer : public TouchpadInputMapperTestBase { +protected: + void SetUp() override { + input_flags::enable_pointer_choreographer(true); + TouchpadInputMapperTestBase::SetUp(); + } +}; + +/** + * Start moving the finger and then click the left touchpad button. Check whether HOVER_EXIT is + * generated when hovering stops. Currently, it is not. + * In the current implementation, HOVER_MOVE and ACTION_DOWN events are not sent out right away, + * but only after the button is released. + */ +TEST_F(TouchpadInputMapperTestWithChoreographer, HoverAndLeftButtonPress) { + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL); + + std::list<NotifyArgs> args; + + args += mMapper->reconfigure(systemTime(SYSTEM_TIME_MONOTONIC), mReaderConfiguration, + InputReaderConfiguration::Change::DISPLAY_INFO); + ASSERT_THAT(args, testing::IsEmpty()); + + args += process(EV_ABS, ABS_MT_TRACKING_ID, 1); + args += process(EV_KEY, BTN_TOUCH, 1); + setScanCodeState(KeyState::DOWN, {BTN_TOOL_FINGER}); + args += process(EV_KEY, BTN_TOOL_FINGER, 1); + args += process(EV_ABS, ABS_MT_POSITION_X, 50); + args += process(EV_ABS, ABS_MT_POSITION_Y, 50); + args += process(EV_ABS, ABS_MT_PRESSURE, 1); + args += process(EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, testing::IsEmpty()); + + // Without this sleep, the test fails. + // TODO(b/284133337): Figure out whether this can be removed + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + + args += process(EV_KEY, BTN_LEFT, 1); + setScanCodeState(KeyState::DOWN, {BTN_LEFT}); + args += process(EV_SYN, SYN_REPORT, 0); + + args += process(EV_KEY, BTN_LEFT, 0); + setScanCodeState(KeyState::UP, {BTN_LEFT}); + args += process(EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)), + VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)), + VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)), + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)))); + + // Liftoff + args.clear(); + args += process(EV_ABS, ABS_MT_PRESSURE, 0); + args += process(EV_ABS, ABS_MT_TRACKING_ID, -1); + args += process(EV_KEY, BTN_TOUCH, 0); + setScanCodeState(KeyState::UP, {BTN_TOOL_FINGER}); + args += process(EV_KEY, BTN_TOOL_FINGER, 0); + args += process(EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, testing::IsEmpty()); +} + } // namespace android |