Loading include/input/VelocityTracker.h +1 −1 Original line number Diff line number Diff line Loading @@ -98,7 +98,7 @@ public: void addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis, float position); // Adds movement information for all pointers in a MotionEvent, including historical samples. void addMovement(const MotionEvent* event); void addMovement(const MotionEvent& event); // Returns the velocity of the specified pointer id and axis in position units per second. // Returns empty optional if there is insufficient movement information for the pointer, or if Loading libs/input/VelocityTracker.cpp +21 −10 Original line number Diff line number Diff line Loading @@ -275,10 +275,10 @@ void VelocityTracker::addMovement(nsecs_t eventTime, int32_t pointerId, int32_t } } void VelocityTracker::addMovement(const MotionEvent* event) { void VelocityTracker::addMovement(const MotionEvent& event) { // Stores data about which axes to process based on the incoming motion event. std::set<int32_t> axesToProcess; int32_t actionMasked = event->getActionMasked(); int32_t actionMasked = event.getActionMasked(); switch (actionMasked) { case AMOTION_EVENT_ACTION_DOWN: Loading @@ -291,7 +291,7 @@ void VelocityTracker::addMovement(const MotionEvent* event) { // Start a new movement trace for a pointer that just went down. // We do this on down instead of on up because the client may want to query the // final velocity for a pointer that just went up. clearPointer(event->getPointerId(event->getActionIndex())); clearPointer(event.getPointerId(event.getActionIndex())); axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); break; } Loading @@ -300,8 +300,14 @@ void VelocityTracker::addMovement(const MotionEvent* event) { axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); break; case AMOTION_EVENT_ACTION_POINTER_UP: if (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) { clearPointer(event.getPointerId(event.getActionIndex())); return; } // Continue to ACTION_UP to ensure that the POINTER_STOPPED logic is triggered. [[fallthrough]]; case AMOTION_EVENT_ACTION_UP: { std::chrono::nanoseconds delaySinceLastEvent(event->getEventTime() - mLastEventTime); std::chrono::nanoseconds delaySinceLastEvent(event.getEventTime() - mLastEventTime); if (delaySinceLastEvent > ASSUME_POINTER_STOPPED_TIME) { ALOGD_IF(DEBUG_VELOCITY, "VelocityTracker: stopped for %s, clearing state upon pointer liftoff.", Loading @@ -325,21 +331,26 @@ void VelocityTracker::addMovement(const MotionEvent* event) { case AMOTION_EVENT_ACTION_SCROLL: axesToProcess.insert(AMOTION_EVENT_AXIS_SCROLL); break; case AMOTION_EVENT_ACTION_CANCEL: { clear(); return; } default: // Ignore all other actions. return; } const size_t historySize = event->getHistorySize(); const size_t historySize = event.getHistorySize(); for (size_t h = 0; h <= historySize; h++) { const nsecs_t eventTime = event->getHistoricalEventTime(h); for (size_t i = 0; i < event->getPointerCount(); i++) { if (event->isResampled(i, h)) { const nsecs_t eventTime = event.getHistoricalEventTime(h); for (size_t i = 0; i < event.getPointerCount(); i++) { if (event.isResampled(i, h)) { continue; // skip resampled samples } const int32_t pointerId = event->getPointerId(i); const int32_t pointerId = event.getPointerId(i); for (int32_t axis : axesToProcess) { const float position = event->getHistoricalAxisValue(axis, i, h); const float position = event.getHistoricalAxisValue(axis, i, h); addMovement(eventTime, pointerId, axis, position); } } Loading libs/input/tests/VelocityTracker_test.cpp +111 −43 Original line number Diff line number Diff line Loading @@ -229,41 +229,23 @@ static std::vector<MotionEvent> createTouchMotionEventStream( return events; } static std::optional<float> computePlanarVelocity( const VelocityTracker::Strategy strategy, const std::vector<PlanarMotionEventEntry>& motions, int32_t axis, static std::optional<float> computeVelocity(const VelocityTracker::Strategy strategy, const std::vector<MotionEvent>& events, int32_t axis, uint32_t pointerId = DEFAULT_POINTER_ID) { VelocityTracker vt(strategy); std::vector<MotionEvent> events = createTouchMotionEventStream(motions); for (MotionEvent event : events) { vt.addMovement(&event); for (const MotionEvent& event : events) { vt.addMovement(event); } return vt.getVelocity(axis, pointerId); } static std::vector<MotionEvent> createMotionEventStream( int32_t axis, const std::vector<std::pair<std::chrono::nanoseconds, float>>& motion) { switch (axis) { case AMOTION_EVENT_AXIS_SCROLL: return createAxisScrollMotionEventStream(motion); default: ADD_FAILURE() << "Axis " << axis << " is not supported"; return {}; } } static std::optional<float> computeVelocity( static std::optional<float> computePlanarVelocity( const VelocityTracker::Strategy strategy, const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions, int32_t axis) { VelocityTracker vt(strategy); for (const MotionEvent& event : createMotionEventStream(axis, motions)) { vt.addMovement(&event); } return vt.getVelocity(axis, DEFAULT_POINTER_ID); const std::vector<PlanarMotionEventEntry>& motions, int32_t axis, uint32_t pointerId) { std::vector<MotionEvent> events = createTouchMotionEventStream(motions); return computeVelocity(strategy, events, axis, pointerId); } static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy, Loading @@ -277,23 +259,23 @@ static void computeAndCheckAxisScrollVelocity( const VelocityTracker::Strategy strategy, const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions, std::optional<float> targetVelocity) { checkVelocity(computeVelocity(strategy, motions, AMOTION_EVENT_AXIS_SCROLL), targetVelocity); std::vector<MotionEvent> events = createAxisScrollMotionEventStream(motions); checkVelocity(computeVelocity(strategy, events, AMOTION_EVENT_AXIS_SCROLL), targetVelocity); // The strategy LSQ2 is not compatible with AXIS_SCROLL. In those situations, we should fall // back to a strategy that supports differential axes. checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, motions, checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_SCROLL), targetVelocity); } static void computeAndCheckQuadraticVelocity(const std::vector<PlanarMotionEventEntry>& motions, float velocity) { VelocityTracker vt(VelocityTracker::Strategy::LSQ2); std::vector<MotionEvent> events = createTouchMotionEventStream(motions); for (MotionEvent event : events) { vt.addMovement(&event); } std::optional<float> velocityX = vt.getVelocity(AMOTION_EVENT_AXIS_X, 0); std::optional<float> velocityY = vt.getVelocity(AMOTION_EVENT_AXIS_Y, 0); std::optional<float> velocityX = computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID); std::optional<float> velocityY = computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, DEFAULT_POINTER_ID); ASSERT_TRUE(velocityX); ASSERT_TRUE(velocityY); Loading Loading @@ -330,12 +312,14 @@ TEST_F(VelocityTrackerTest, TestDefaultStrategiesForPlanarAxes) { {30ms, {{6, 20}}}, {40ms, {{10, 30}}}}; EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X), EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID), computePlanarVelocity(VelocityTracker::Strategy::DEFAULT, motions, AMOTION_EVENT_AXIS_X)); EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y), AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)); EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, DEFAULT_POINTER_ID), computePlanarVelocity(VelocityTracker::Strategy::DEFAULT, motions, AMOTION_EVENT_AXIS_Y)); AMOTION_EVENT_AXIS_Y, DEFAULT_POINTER_ID)); } TEST_F(VelocityTrackerTest, TestComputedVelocity) { Loading Loading @@ -431,7 +415,7 @@ TEST_F(VelocityTrackerTest, TestGetComputedVelocity) { VelocityTracker vt(VelocityTracker::Strategy::IMPULSE); std::vector<MotionEvent> events = createTouchMotionEventStream(motions); for (const MotionEvent& event : events) { vt.addMovement(&event); vt.addMovement(event); } float maxFloat = std::numeric_limits<float>::max(); Loading Loading @@ -509,6 +493,89 @@ TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) { computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 500); } /** * When the stream is terminated with ACTION_CANCEL, the resulting velocity should be 0. */ TEST_F(VelocityTrackerTest, ActionCancelResultsInZeroVelocity) { std::vector<PlanarMotionEventEntry> motions = { {0ms, {{0, 0}}}, // DOWN {10ms, {{5, 10}}}, // MOVE {20ms, {{10, 20}}}, // MOVE {20ms, {{10, 20}}}, // ACTION_UP }; std::vector<MotionEvent> events = createTouchMotionEventStream(motions); // By default, `createTouchMotionEventStream` produces an event stream that terminates with // ACTION_UP. We need to manually change it to ACTION_CANCEL. MotionEvent& lastEvent = events.back(); lastEvent.setAction(AMOTION_EVENT_ACTION_CANCEL); lastEvent.setFlags(lastEvent.getFlags() | AMOTION_EVENT_FLAG_CANCELED); const int32_t pointerId = lastEvent.getPointerId(0); checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_X, pointerId), /*targetVelocity*/ std::nullopt); checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_Y, pointerId), /*targetVelocity*/ std::nullopt); checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_X, pointerId), /*targetVelocity*/ std::nullopt); checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_Y, pointerId), /*targetVelocity*/ std::nullopt); } /** * When the stream is terminated with ACTION_CANCEL, the resulting velocity should be 0. */ TEST_F(VelocityTrackerTest, ActionPointerCancelResultsInZeroVelocityForThatPointer) { std::vector<PlanarMotionEventEntry> motions = { {0ms, {{0, 5}, {NAN, NAN}}}, // DOWN {0ms, {{0, 5}, {10, 15}}}, // POINTER_DOWN {10ms, {{5, 10}, {15, 20}}}, // MOVE {20ms, {{10, 15}, {20, 25}}}, // MOVE {30ms, {{10, 15}, {20, 25}}}, // POINTER_UP {30ms, {{10, 15}, {NAN, NAN}}}, // UP }; std::vector<MotionEvent> events = createTouchMotionEventStream(motions); // Cancel the lifting pointer of the ACTION_POINTER_UP event MotionEvent& pointerUpEvent = events.rbegin()[1]; pointerUpEvent.setFlags(pointerUpEvent.getFlags() | AMOTION_EVENT_FLAG_CANCELED); const int32_t pointerId = pointerUpEvent.getPointerId(pointerUpEvent.getActionIndex()); // Double check the stream ASSERT_EQ(1, pointerId); ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP, pointerUpEvent.getActionMasked()); ASSERT_EQ(AMOTION_EVENT_ACTION_UP, events.back().getActionMasked()); // Ensure the velocity of the lifting pointer is zero checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_X, pointerId), /*targetVelocity*/ std::nullopt); checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_Y, pointerId), /*targetVelocity*/ std::nullopt); checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_X, pointerId), /*targetVelocity*/ std::nullopt); checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_Y, pointerId), /*targetVelocity*/ std::nullopt); // The remaining pointer should have the correct velocity. const int32_t remainingPointerId = events.back().getPointerId(0); ASSERT_EQ(0, remainingPointerId); checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_X, remainingPointerId), /*targetVelocity*/ 500); checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_Y, remainingPointerId), /*targetVelocity*/ 500); checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_X, remainingPointerId), /*targetVelocity*/ 500); checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_Y, remainingPointerId), /*targetVelocity*/ 500); } /** * ================== VelocityTracker tests generated by recording real events ===================== Loading Loading @@ -1336,9 +1403,10 @@ TEST_F(VelocityTrackerTest, TestDefaultStrategyForAxisScroll) { {40ms, 100}, }; EXPECT_EQ(computeVelocity(VelocityTracker::Strategy::IMPULSE, motions, std::vector<MotionEvent> events = createAxisScrollMotionEventStream(motions); EXPECT_EQ(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_SCROLL), computeVelocity(VelocityTracker::Strategy::DEFAULT, motions, computeVelocity(VelocityTracker::Strategy::DEFAULT, events, AMOTION_EVENT_AXIS_SCROLL)); } Loading Loading
include/input/VelocityTracker.h +1 −1 Original line number Diff line number Diff line Loading @@ -98,7 +98,7 @@ public: void addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis, float position); // Adds movement information for all pointers in a MotionEvent, including historical samples. void addMovement(const MotionEvent* event); void addMovement(const MotionEvent& event); // Returns the velocity of the specified pointer id and axis in position units per second. // Returns empty optional if there is insufficient movement information for the pointer, or if Loading
libs/input/VelocityTracker.cpp +21 −10 Original line number Diff line number Diff line Loading @@ -275,10 +275,10 @@ void VelocityTracker::addMovement(nsecs_t eventTime, int32_t pointerId, int32_t } } void VelocityTracker::addMovement(const MotionEvent* event) { void VelocityTracker::addMovement(const MotionEvent& event) { // Stores data about which axes to process based on the incoming motion event. std::set<int32_t> axesToProcess; int32_t actionMasked = event->getActionMasked(); int32_t actionMasked = event.getActionMasked(); switch (actionMasked) { case AMOTION_EVENT_ACTION_DOWN: Loading @@ -291,7 +291,7 @@ void VelocityTracker::addMovement(const MotionEvent* event) { // Start a new movement trace for a pointer that just went down. // We do this on down instead of on up because the client may want to query the // final velocity for a pointer that just went up. clearPointer(event->getPointerId(event->getActionIndex())); clearPointer(event.getPointerId(event.getActionIndex())); axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); break; } Loading @@ -300,8 +300,14 @@ void VelocityTracker::addMovement(const MotionEvent* event) { axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); break; case AMOTION_EVENT_ACTION_POINTER_UP: if (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) { clearPointer(event.getPointerId(event.getActionIndex())); return; } // Continue to ACTION_UP to ensure that the POINTER_STOPPED logic is triggered. [[fallthrough]]; case AMOTION_EVENT_ACTION_UP: { std::chrono::nanoseconds delaySinceLastEvent(event->getEventTime() - mLastEventTime); std::chrono::nanoseconds delaySinceLastEvent(event.getEventTime() - mLastEventTime); if (delaySinceLastEvent > ASSUME_POINTER_STOPPED_TIME) { ALOGD_IF(DEBUG_VELOCITY, "VelocityTracker: stopped for %s, clearing state upon pointer liftoff.", Loading @@ -325,21 +331,26 @@ void VelocityTracker::addMovement(const MotionEvent* event) { case AMOTION_EVENT_ACTION_SCROLL: axesToProcess.insert(AMOTION_EVENT_AXIS_SCROLL); break; case AMOTION_EVENT_ACTION_CANCEL: { clear(); return; } default: // Ignore all other actions. return; } const size_t historySize = event->getHistorySize(); const size_t historySize = event.getHistorySize(); for (size_t h = 0; h <= historySize; h++) { const nsecs_t eventTime = event->getHistoricalEventTime(h); for (size_t i = 0; i < event->getPointerCount(); i++) { if (event->isResampled(i, h)) { const nsecs_t eventTime = event.getHistoricalEventTime(h); for (size_t i = 0; i < event.getPointerCount(); i++) { if (event.isResampled(i, h)) { continue; // skip resampled samples } const int32_t pointerId = event->getPointerId(i); const int32_t pointerId = event.getPointerId(i); for (int32_t axis : axesToProcess) { const float position = event->getHistoricalAxisValue(axis, i, h); const float position = event.getHistoricalAxisValue(axis, i, h); addMovement(eventTime, pointerId, axis, position); } } Loading
libs/input/tests/VelocityTracker_test.cpp +111 −43 Original line number Diff line number Diff line Loading @@ -229,41 +229,23 @@ static std::vector<MotionEvent> createTouchMotionEventStream( return events; } static std::optional<float> computePlanarVelocity( const VelocityTracker::Strategy strategy, const std::vector<PlanarMotionEventEntry>& motions, int32_t axis, static std::optional<float> computeVelocity(const VelocityTracker::Strategy strategy, const std::vector<MotionEvent>& events, int32_t axis, uint32_t pointerId = DEFAULT_POINTER_ID) { VelocityTracker vt(strategy); std::vector<MotionEvent> events = createTouchMotionEventStream(motions); for (MotionEvent event : events) { vt.addMovement(&event); for (const MotionEvent& event : events) { vt.addMovement(event); } return vt.getVelocity(axis, pointerId); } static std::vector<MotionEvent> createMotionEventStream( int32_t axis, const std::vector<std::pair<std::chrono::nanoseconds, float>>& motion) { switch (axis) { case AMOTION_EVENT_AXIS_SCROLL: return createAxisScrollMotionEventStream(motion); default: ADD_FAILURE() << "Axis " << axis << " is not supported"; return {}; } } static std::optional<float> computeVelocity( static std::optional<float> computePlanarVelocity( const VelocityTracker::Strategy strategy, const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions, int32_t axis) { VelocityTracker vt(strategy); for (const MotionEvent& event : createMotionEventStream(axis, motions)) { vt.addMovement(&event); } return vt.getVelocity(axis, DEFAULT_POINTER_ID); const std::vector<PlanarMotionEventEntry>& motions, int32_t axis, uint32_t pointerId) { std::vector<MotionEvent> events = createTouchMotionEventStream(motions); return computeVelocity(strategy, events, axis, pointerId); } static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy, Loading @@ -277,23 +259,23 @@ static void computeAndCheckAxisScrollVelocity( const VelocityTracker::Strategy strategy, const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions, std::optional<float> targetVelocity) { checkVelocity(computeVelocity(strategy, motions, AMOTION_EVENT_AXIS_SCROLL), targetVelocity); std::vector<MotionEvent> events = createAxisScrollMotionEventStream(motions); checkVelocity(computeVelocity(strategy, events, AMOTION_EVENT_AXIS_SCROLL), targetVelocity); // The strategy LSQ2 is not compatible with AXIS_SCROLL. In those situations, we should fall // back to a strategy that supports differential axes. checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, motions, checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_SCROLL), targetVelocity); } static void computeAndCheckQuadraticVelocity(const std::vector<PlanarMotionEventEntry>& motions, float velocity) { VelocityTracker vt(VelocityTracker::Strategy::LSQ2); std::vector<MotionEvent> events = createTouchMotionEventStream(motions); for (MotionEvent event : events) { vt.addMovement(&event); } std::optional<float> velocityX = vt.getVelocity(AMOTION_EVENT_AXIS_X, 0); std::optional<float> velocityY = vt.getVelocity(AMOTION_EVENT_AXIS_Y, 0); std::optional<float> velocityX = computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID); std::optional<float> velocityY = computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, DEFAULT_POINTER_ID); ASSERT_TRUE(velocityX); ASSERT_TRUE(velocityY); Loading Loading @@ -330,12 +312,14 @@ TEST_F(VelocityTrackerTest, TestDefaultStrategiesForPlanarAxes) { {30ms, {{6, 20}}}, {40ms, {{10, 30}}}}; EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X), EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID), computePlanarVelocity(VelocityTracker::Strategy::DEFAULT, motions, AMOTION_EVENT_AXIS_X)); EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y), AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)); EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, DEFAULT_POINTER_ID), computePlanarVelocity(VelocityTracker::Strategy::DEFAULT, motions, AMOTION_EVENT_AXIS_Y)); AMOTION_EVENT_AXIS_Y, DEFAULT_POINTER_ID)); } TEST_F(VelocityTrackerTest, TestComputedVelocity) { Loading Loading @@ -431,7 +415,7 @@ TEST_F(VelocityTrackerTest, TestGetComputedVelocity) { VelocityTracker vt(VelocityTracker::Strategy::IMPULSE); std::vector<MotionEvent> events = createTouchMotionEventStream(motions); for (const MotionEvent& event : events) { vt.addMovement(&event); vt.addMovement(event); } float maxFloat = std::numeric_limits<float>::max(); Loading Loading @@ -509,6 +493,89 @@ TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) { computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 500); } /** * When the stream is terminated with ACTION_CANCEL, the resulting velocity should be 0. */ TEST_F(VelocityTrackerTest, ActionCancelResultsInZeroVelocity) { std::vector<PlanarMotionEventEntry> motions = { {0ms, {{0, 0}}}, // DOWN {10ms, {{5, 10}}}, // MOVE {20ms, {{10, 20}}}, // MOVE {20ms, {{10, 20}}}, // ACTION_UP }; std::vector<MotionEvent> events = createTouchMotionEventStream(motions); // By default, `createTouchMotionEventStream` produces an event stream that terminates with // ACTION_UP. We need to manually change it to ACTION_CANCEL. MotionEvent& lastEvent = events.back(); lastEvent.setAction(AMOTION_EVENT_ACTION_CANCEL); lastEvent.setFlags(lastEvent.getFlags() | AMOTION_EVENT_FLAG_CANCELED); const int32_t pointerId = lastEvent.getPointerId(0); checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_X, pointerId), /*targetVelocity*/ std::nullopt); checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_Y, pointerId), /*targetVelocity*/ std::nullopt); checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_X, pointerId), /*targetVelocity*/ std::nullopt); checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_Y, pointerId), /*targetVelocity*/ std::nullopt); } /** * When the stream is terminated with ACTION_CANCEL, the resulting velocity should be 0. */ TEST_F(VelocityTrackerTest, ActionPointerCancelResultsInZeroVelocityForThatPointer) { std::vector<PlanarMotionEventEntry> motions = { {0ms, {{0, 5}, {NAN, NAN}}}, // DOWN {0ms, {{0, 5}, {10, 15}}}, // POINTER_DOWN {10ms, {{5, 10}, {15, 20}}}, // MOVE {20ms, {{10, 15}, {20, 25}}}, // MOVE {30ms, {{10, 15}, {20, 25}}}, // POINTER_UP {30ms, {{10, 15}, {NAN, NAN}}}, // UP }; std::vector<MotionEvent> events = createTouchMotionEventStream(motions); // Cancel the lifting pointer of the ACTION_POINTER_UP event MotionEvent& pointerUpEvent = events.rbegin()[1]; pointerUpEvent.setFlags(pointerUpEvent.getFlags() | AMOTION_EVENT_FLAG_CANCELED); const int32_t pointerId = pointerUpEvent.getPointerId(pointerUpEvent.getActionIndex()); // Double check the stream ASSERT_EQ(1, pointerId); ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP, pointerUpEvent.getActionMasked()); ASSERT_EQ(AMOTION_EVENT_ACTION_UP, events.back().getActionMasked()); // Ensure the velocity of the lifting pointer is zero checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_X, pointerId), /*targetVelocity*/ std::nullopt); checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_Y, pointerId), /*targetVelocity*/ std::nullopt); checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_X, pointerId), /*targetVelocity*/ std::nullopt); checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_Y, pointerId), /*targetVelocity*/ std::nullopt); // The remaining pointer should have the correct velocity. const int32_t remainingPointerId = events.back().getPointerId(0); ASSERT_EQ(0, remainingPointerId); checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_X, remainingPointerId), /*targetVelocity*/ 500); checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_Y, remainingPointerId), /*targetVelocity*/ 500); checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_X, remainingPointerId), /*targetVelocity*/ 500); checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_Y, remainingPointerId), /*targetVelocity*/ 500); } /** * ================== VelocityTracker tests generated by recording real events ===================== Loading Loading @@ -1336,9 +1403,10 @@ TEST_F(VelocityTrackerTest, TestDefaultStrategyForAxisScroll) { {40ms, 100}, }; EXPECT_EQ(computeVelocity(VelocityTracker::Strategy::IMPULSE, motions, std::vector<MotionEvent> events = createAxisScrollMotionEventStream(motions); EXPECT_EQ(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_SCROLL), computeVelocity(VelocityTracker::Strategy::DEFAULT, motions, computeVelocity(VelocityTracker::Strategy::DEFAULT, events, AMOTION_EVENT_AXIS_SCROLL)); } Loading