diff options
author | 2024-08-01 17:11:58 +0000 | |
---|---|---|
committer | 2024-08-19 16:29:06 +0000 | |
commit | cf1b06e7d9a0922628c6d13acd0e5d7c0eb79617 (patch) | |
tree | 4db2e1fd5eed635f2bb726aa5c4eadc4824fe12d | |
parent | 00c3b29107d5fddc4634328a888de32073cdcba3 (diff) |
Add multiple pointer support to LegacyResampler with tests
Added multiple pointer support to LegacyResampler and included the
corresponding unit tests to ensure correctness.
Bug: 297226446
Flag: EXEMPT refactor
Test: TEST=libinput_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST --gtest_filter="ResamplerTest*"
Change-Id: Ib639942c31311dcdcf6d72ed33a4d4476b76fe7f
-rw-r--r-- | include/input/Resampler.h | 26 | ||||
-rw-r--r-- | libs/input/Resampler.cpp | 102 | ||||
-rw-r--r-- | libs/input/tests/Resampler_test.cpp | 613 |
3 files changed, 619 insertions, 122 deletions
diff --git a/include/input/Resampler.h b/include/input/Resampler.h index 5980d5d4c4..2892137ae7 100644 --- a/include/input/Resampler.h +++ b/include/input/Resampler.h @@ -18,6 +18,7 @@ #include <chrono> #include <optional> +#include <vector> #include <input/Input.h> #include <input/InputTransport.h> @@ -71,7 +72,15 @@ private: struct Sample { std::chrono::nanoseconds eventTime; - Pointer pointer; + std::vector<Pointer> pointers; + + std::vector<PointerCoords> asPointerCoords() const { + std::vector<PointerCoords> pointersCoords; + for (const Pointer& pointer : pointers) { + pointersCoords.push_back(pointer.coords); + } + return pointersCoords; + } }; /** @@ -88,12 +97,21 @@ private: RingBuffer<Sample> mLatestSamples{/*capacity=*/2}; /** - * Adds up to mLatestSamples.capacity() of motionEvent's latest samples to mLatestSamples. (If + * Adds up to mLatestSamples.capacity() of motionEvent's latest samples to mLatestSamples. If * motionEvent has fewer samples than mLatestSamples.capacity(), then the available samples are - * added to mLatestSamples.) + * added to mLatestSamples. */ void updateLatestSamples(const MotionEvent& motionEvent); + static Sample messageToSample(const InputMessage& message); + + /** + * Checks if auxiliary sample has the same pointer properties of target sample. That is, + * auxiliary pointer IDs must appear in the same order as target pointer IDs, their toolType + * must match and be resampleable. + */ + static bool pointerPropertiesResampleable(const Sample& target, const Sample& auxiliary); + /** * Checks if there are necessary conditions to interpolate. For example, interpolation cannot * take place if samples are too far apart in time. mLatestSamples must have at least one sample @@ -125,4 +143,4 @@ private: inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent); }; -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp index 342f7f557f..a1e6e06784 100644 --- a/libs/input/Resampler.cpp +++ b/libs/input/Resampler.cpp @@ -21,6 +21,7 @@ #include <android-base/logging.h> #include <android-base/properties.h> +#include <ftl/enum.h> #include <input/Resampler.h> #include <utils/Timers.h> @@ -56,6 +57,11 @@ constexpr std::chrono::milliseconds RESAMPLE_MAX_DELTA{20}; constexpr std::chrono::milliseconds RESAMPLE_MAX_PREDICTION{8}; +bool canResampleTool(ToolType toolType) { + return toolType == ToolType::FINGER || toolType == ToolType::MOUSE || + toolType == ToolType::STYLUS || toolType == ToolType::UNKNOWN; +} + inline float lerp(float a, float b, float alpha) { return a + alpha * (b - a); } @@ -73,21 +79,71 @@ PointerCoords calculateResampledCoords(const PointerCoords& a, const PointerCoor void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) { const size_t numSamples = motionEvent.getHistorySize() + 1; - for (size_t i = 0; i < numSamples; ++i) { + const size_t latestIndex = numSamples - 1; + const size_t secondToLatestIndex = (latestIndex > 0) ? (latestIndex - 1) : 0; + for (size_t sampleIndex = secondToLatestIndex; sampleIndex < numSamples; ++sampleIndex) { + std::vector<Pointer> pointers; + const size_t numPointers = motionEvent.getPointerCount(); + for (size_t pointerIndex = 0; pointerIndex < numPointers; ++pointerIndex) { + // getSamplePointerCoords is the vector representation of a getHistorySize by + // getPointerCount matrix. + const PointerCoords& pointerCoords = + motionEvent.getSamplePointerCoords()[sampleIndex * numPointers + pointerIndex]; + pointers.push_back( + Pointer{*motionEvent.getPointerProperties(pointerIndex), pointerCoords}); + } mLatestSamples.pushBack( - Sample{static_cast<nanoseconds>(motionEvent.getHistoricalEventTime(i)), - Pointer{*motionEvent.getPointerProperties(0), - motionEvent.getSamplePointerCoords()[i]}}); + Sample{static_cast<nanoseconds>(motionEvent.getHistoricalEventTime(sampleIndex)), + pointers}); + } +} + +LegacyResampler::Sample LegacyResampler::messageToSample(const InputMessage& message) { + std::vector<Pointer> pointers; + for (uint32_t i = 0; i < message.body.motion.pointerCount; ++i) { + pointers.push_back(Pointer{message.body.motion.pointers[i].properties, + message.body.motion.pointers[i].coords}); + } + return Sample{static_cast<nanoseconds>(message.body.motion.eventTime), pointers}; +} + +bool LegacyResampler::pointerPropertiesResampleable(const Sample& target, const Sample& auxiliary) { + if (target.pointers.size() > auxiliary.pointers.size()) { + LOG_IF(INFO, debugResampling()) + << "Not resampled. Auxiliary sample has fewer pointers than target sample."; + return false; } + for (size_t i = 0; i < target.pointers.size(); ++i) { + if (target.pointers[i].properties.id != auxiliary.pointers[i].properties.id) { + LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ID mismatch."; + return false; + } + if (target.pointers[i].properties.toolType != auxiliary.pointers[i].properties.toolType) { + LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ToolType mismatch."; + return false; + } + if (!canResampleTool(target.pointers[i].properties.toolType)) { + LOG_IF(INFO, debugResampling()) + << "Not resampled. Cannot resample " + << ftl::enum_string(target.pointers[i].properties.toolType) << " ToolType."; + return false; + } + } + return true; } -bool LegacyResampler::canInterpolate(const InputMessage& futureSample) const { +bool LegacyResampler::canInterpolate(const InputMessage& message) const { LOG_IF(FATAL, mLatestSamples.empty()) << "Not resampled. mLatestSamples must not be empty to interpolate."; const Sample& pastSample = *(mLatestSamples.end() - 1); - const nanoseconds delta = - static_cast<nanoseconds>(futureSample.body.motion.eventTime) - pastSample.eventTime; + const Sample& futureSample = messageToSample(message); + + if (!pointerPropertiesResampleable(pastSample, futureSample)) { + return false; + } + + const nanoseconds delta = futureSample.eventTime - pastSample.eventTime; if (delta < RESAMPLE_MIN_DELTA) { LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns."; return false; @@ -104,15 +160,20 @@ std::optional<LegacyResampler::Sample> LegacyResampler::attemptInterpolation( << "Not resampled. mLatestSamples must not be empty to interpolate."; const Sample& pastSample = *(mLatestSamples.end() - 1); + const nanoseconds delta = static_cast<nanoseconds>(futureSample.body.motion.eventTime) - pastSample.eventTime; const float alpha = std::chrono::duration<float, std::milli>(resampleTime - pastSample.eventTime) / delta; - const PointerCoords resampledCoords = - calculateResampledCoords(pastSample.pointer.coords, - futureSample.body.motion.pointers[0].coords, alpha); - return Sample{resampleTime, Pointer{pastSample.pointer.properties, resampledCoords}}; + std::vector<Pointer> resampledPointers; + for (size_t i = 0; i < pastSample.pointers.size(); ++i) { + const PointerCoords& resampledCoords = + calculateResampledCoords(pastSample.pointers[i].coords, + futureSample.body.motion.pointers[i].coords, alpha); + resampledPointers.push_back(Pointer{pastSample.pointers[i].properties, resampledCoords}); + } + return Sample{resampleTime, resampledPointers}; } bool LegacyResampler::canExtrapolate() const { @@ -124,6 +185,10 @@ bool LegacyResampler::canExtrapolate() const { const Sample& pastSample = *(mLatestSamples.end() - 2); const Sample& presentSample = *(mLatestSamples.end() - 1); + if (!pointerPropertiesResampleable(presentSample, pastSample)) { + return false; + } + const nanoseconds delta = presentSample.eventTime - pastSample.eventTime; if (delta < RESAMPLE_MIN_DELTA) { LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns."; @@ -160,16 +225,21 @@ std::optional<LegacyResampler::Sample> LegacyResampler::attemptExtrapolation( const float alpha = std::chrono::duration<float, std::milli>(newResampleTime - pastSample.eventTime) / delta; - const PointerCoords resampledCoords = - calculateResampledCoords(pastSample.pointer.coords, presentSample.pointer.coords, - alpha); - return Sample{newResampleTime, Pointer{presentSample.pointer.properties, resampledCoords}}; + std::vector<Pointer> resampledPointers; + for (size_t i = 0; i < presentSample.pointers.size(); ++i) { + const PointerCoords& resampledCoords = + calculateResampledCoords(pastSample.pointers[i].coords, + presentSample.pointers[i].coords, alpha); + resampledPointers.push_back(Pointer{presentSample.pointers[i].properties, resampledCoords}); + } + return Sample{newResampleTime, resampledPointers}; } inline void LegacyResampler::addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent) { - motionEvent.addSample(sample.eventTime.count(), &sample.pointer.coords, motionEvent.getId()); + motionEvent.addSample(sample.eventTime.count(), sample.asPointerCoords().data(), + motionEvent.getId()); } void LegacyResampler::resampleMotionEvent(nanoseconds resampleTime, MotionEvent& motionEvent, diff --git a/libs/input/tests/Resampler_test.cpp b/libs/input/tests/Resampler_test.cpp index 135f8b41a2..b372c0b183 100644 --- a/libs/input/tests/Resampler_test.cpp +++ b/libs/input/tests/Resampler_test.cpp @@ -59,6 +59,9 @@ Pointer::operator PointerCoords() const { struct InputSample { std::chrono::milliseconds eventTime{0}; std::vector<Pointer> pointers{}; + + explicit InputSample(std::chrono::milliseconds eventTime, const std::vector<Pointer>& pointers) + : eventTime{eventTime}, pointers{pointers} {} /** * Converts from InputSample to InputMessage. Enables calling LegacyResampler methods only with * the relevant data for tests. @@ -73,6 +76,7 @@ InputSample::operator InputMessage() const { message.body.motion.eventTime = static_cast<std::chrono::nanoseconds>(eventTime).count(); message.body.motion.source = AINPUT_SOURCE_CLASS_POINTER; message.body.motion.downTime = 0; + const uint32_t pointerCount = message.body.motion.pointerCount; for (uint32_t i = 0; i < pointerCount; ++i) { message.body.motion.pointers[i].properties.id = pointers[i].id; @@ -132,14 +136,6 @@ protected: std::unique_ptr<Resampler> mResampler; - MotionEvent buildMotionEvent(const int32_t action, const nsecs_t eventTime, - const std::vector<PointerBuilder>& pointers); - - InputMessage createMessage(const uint32_t pointerCount, const nsecs_t eventTime, - const int32_t action, - const std::vector<PointerProperties>& properties, - const std::vector<PointerCoords>& coords); - /** * Checks that beforeCall and afterCall are equal except for the mutated attributes by addSample * member function. @@ -153,42 +149,14 @@ protected: * Asserts the MotionEvent is resampled by checking an increment in history size and that the * resampled coordinates are near the expected ones. */ - void assertMotionEventIsResampledAndCoordsNear(const MotionEvent& original, - const MotionEvent& resampled, - const PointerCoords& expectedCoords); + void assertMotionEventIsResampledAndCoordsNear( + const MotionEvent& original, const MotionEvent& resampled, + const std::vector<PointerCoords>& expectedCoords); void assertMotionEventIsNotResampled(const MotionEvent& original, const MotionEvent& notResampled); }; -MotionEvent ResamplerTest::buildMotionEvent(const int32_t action, const nsecs_t eventTime, - const std::vector<PointerBuilder>& pointerBuilders) { - MotionEventBuilder motionEventBuilder = MotionEventBuilder(action, AINPUT_SOURCE_CLASS_POINTER) - .downTime(0) - .eventTime(eventTime); - for (const PointerBuilder& pointerBuilder : pointerBuilders) { - motionEventBuilder.pointer(pointerBuilder); - } - return motionEventBuilder.build(); -} - -InputMessage ResamplerTest::createMessage(const uint32_t pointerCount, const nsecs_t eventTime, - const int32_t action, - const std::vector<PointerProperties>& properties, - const std::vector<PointerCoords>& coords) { - InputMessage message; - message.header.type = InputMessage::Type::MOTION; - message.body.motion.pointerCount = pointerCount; - message.body.motion.eventTime = eventTime; - message.body.motion.source = AINPUT_SOURCE_CLASS_POINTER; - message.body.motion.downTime = 0; - for (uint32_t i = 0; i < pointerCount; ++i) { - message.body.motion.pointers[i].properties = properties[i]; - message.body.motion.pointers[i].coords = coords[i]; - } - return message; -} - void ResamplerTest::assertMotionEventMetaDataDidNotMutate(const MotionEvent& beforeCall, const MotionEvent& afterCall) { EXPECT_EQ(beforeCall.getDeviceId(), afterCall.getDeviceId()); @@ -207,18 +175,29 @@ void ResamplerTest::assertMotionEventMetaDataDidNotMutate(const MotionEvent& bef EXPECT_EQ(beforeCall.getDisplayId(), afterCall.getDisplayId()); } -void ResamplerTest::assertMotionEventIsResampledAndCoordsNear(const MotionEvent& original, - const MotionEvent& resampled, - const PointerCoords& expectedCoords) { +void ResamplerTest::assertMotionEventIsResampledAndCoordsNear( + const MotionEvent& original, const MotionEvent& resampled, + const std::vector<PointerCoords>& expectedCoords) { assertMotionEventMetaDataDidNotMutate(original, resampled); + const size_t originalSampleSize = original.getHistorySize() + 1; const size_t resampledSampleSize = resampled.getHistorySize() + 1; EXPECT_EQ(originalSampleSize + 1, resampledSampleSize); - const PointerCoords& resampledCoords = - resampled.getSamplePointerCoords()[resampled.getHistorySize()]; - EXPECT_TRUE(resampledCoords.isResampled); - EXPECT_NEAR(expectedCoords.getX(), resampledCoords.getX(), EPSILON); - EXPECT_NEAR(expectedCoords.getY(), resampledCoords.getY(), EPSILON); + + const size_t numPointers = resampled.getPointerCount(); + const size_t beginLatestSample = resampledSampleSize - 1; + for (size_t i = 0; i < numPointers; ++i) { + SCOPED_TRACE(i); + EXPECT_EQ(original.getPointerId(i), resampled.getPointerId(i)); + EXPECT_EQ(original.getToolType(i), resampled.getToolType(i)); + + const PointerCoords& resampledCoords = + resampled.getSamplePointerCoords()[beginLatestSample * numPointers + i]; + + EXPECT_TRUE(resampledCoords.isResampled); + EXPECT_NEAR(expectedCoords[i].getX(), resampledCoords.getX(), EPSILON); + EXPECT_NEAR(expectedCoords[i].getY(), resampledCoords.getY(), EPSILON); + } } void ResamplerTest::assertMotionEventIsNotResampled(const MotionEvent& original, @@ -233,7 +212,7 @@ TEST_F(ResamplerTest, NonResampledAxesArePreserved) { constexpr float TOUCH_MAJOR_VALUE = 1.0f; MotionEvent motionEvent = - InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}}, + InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}}, AMOTION_EVENT_ACTION_MOVE}; constexpr std::chrono::nanoseconds eventTime{10ms}; @@ -255,34 +234,40 @@ TEST_F(ResamplerTest, NonResampledAxesArePreserved) { EXPECT_EQ(motionEvent.getTouchMajor(0), TOUCH_MAJOR_VALUE); assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, - Pointer{.id = 0, - .x = 2.2f, - .y = 2.4f, - .isResampled = true}); + {Pointer{.id = 0, + .x = 2.2f, + .y = 2.4f, + .isResampled = true}}); } TEST_F(ResamplerTest, SinglePointerNotEnoughDataToResample) { MotionEvent motionEvent = - InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}}, - AMOTION_EVENT_ACTION_MOVE, - .deviceId = 0}; + InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, nullptr); + + mResampler->resampleMotionEvent(11ms, motionEvent, /*futureSample=*/nullptr); + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); } TEST_F(ResamplerTest, SinglePointerDifferentDeviceIdBetweenMotionEvents) { MotionEvent motionFromFirstDevice = - InputStream{{{4ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}, - {8ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + InputStream{{InputSample{4ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}, + InputSample{8ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, AMOTION_EVENT_ACTION_MOVE, .deviceId = 0}; + mResampler->resampleMotionEvent(10ms, motionFromFirstDevice, nullptr); + MotionEvent motionFromSecondDevice = - InputStream{{{11ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}}, + InputStream{{InputSample{11ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}}}, AMOTION_EVENT_ACTION_MOVE, .deviceId = 1}; const MotionEvent originalMotionEvent = motionFromSecondDevice; + mResampler->resampleMotionEvent(12ms, motionFromSecondDevice, nullptr); // The MotionEvent should not be resampled because the second event came from a different device // than the previous event. @@ -308,28 +293,30 @@ TEST_F(ResamplerTest, SinglePointerDifferentDeviceIdBetweenMotionEvents) { */ TEST_F(ResamplerTest, SinglePointerSingleSampleInterpolation) { MotionEvent motionEvent = - InputStream{{{10ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}}, + InputStream{{InputSample{10ms, + {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}}, AMOTION_EVENT_ACTION_MOVE}; const InputMessage futureSample = - InputSample{15ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}; + InputSample{15ms, {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}; const MotionEvent originalMotionEvent = motionEvent; mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, - Pointer{.id = 0, - .x = 1.2f, - .y = 1.2f, - .isResampled = true}); + {Pointer{.id = 0, + .x = 1.2f, + .y = 2.4f, + .isResampled = true}}); } TEST_F(ResamplerTest, SinglePointerDeltaTooSmallInterpolation) { MotionEvent motionEvent = - InputStream{{{10ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}}, + InputStream{{InputSample{10ms, + {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}}, AMOTION_EVENT_ACTION_MOVE}; const InputMessage futureSample = - InputSample{11ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}; + InputSample{11ms, {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}; const MotionEvent originalMotionEvent = motionEvent; @@ -342,25 +329,26 @@ TEST_F(ResamplerTest, SinglePointerDeltaTooSmallInterpolation) { * Tests extrapolation given two MotionEvents with a single sample. */ TEST_F(ResamplerTest, SinglePointerSingleSampleExtrapolation) { - MotionEvent previousMotionEvent = - InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}}, + MotionEvent firstMotionEvent = + InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}}, AMOTION_EVENT_ACTION_MOVE}; - mResampler->resampleMotionEvent(10ms, previousMotionEvent, nullptr); + mResampler->resampleMotionEvent(9ms, firstMotionEvent, nullptr); - MotionEvent motionEvent = - InputStream{{{10ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}}, + MotionEvent secondMotionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}}, AMOTION_EVENT_ACTION_MOVE}; - const MotionEvent originalMotionEvent = motionEvent; + const MotionEvent originalMotionEvent = secondMotionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, nullptr); + mResampler->resampleMotionEvent(11ms, secondMotionEvent, nullptr); - assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, - Pointer{.id = 0, - .x = 1.0f, - .y = 1.0f, - .isResampled = true}); + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, secondMotionEvent, + {Pointer{.id = 0, + .x = 2.2f, + .y = 4.4f, + .isResampled = true}}); // Integrity of the whole motionEvent // History size should increment by 1 // Check if the resampled value is the last one @@ -370,27 +358,30 @@ TEST_F(ResamplerTest, SinglePointerSingleSampleExtrapolation) { TEST_F(ResamplerTest, SinglePointerMultipleSampleInterpolation) { MotionEvent motionEvent = - InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}, - {10ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}, + InputSample{10ms, + {{.id = 0, .x = 2.0f, .y = 3.0f, .isResampled = false}}}}, AMOTION_EVENT_ACTION_MOVE}; + const InputMessage futureSample = - InputSample{15ms, {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}}; + InputSample{15ms, {{.id = 0, .x = 3.0f, .y = 5.0f, .isResampled = false}}}; const MotionEvent originalMotionEvent = motionEvent; mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, - Pointer{.id = 0, - .x = 2.2f, - .y = 2.2f, - .isResampled = true}); + {Pointer{.id = 0, + .x = 2.2f, + .y = 3.4f, + .isResampled = true}}); } TEST_F(ResamplerTest, SinglePointerMultipleSampleExtrapolation) { MotionEvent motionEvent = - InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}, - {10ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}, + InputSample{10ms, + {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}}, AMOTION_EVENT_ACTION_MOVE}; const MotionEvent originalMotionEvent = motionEvent; @@ -398,16 +389,17 @@ TEST_F(ResamplerTest, SinglePointerMultipleSampleExtrapolation) { mResampler->resampleMotionEvent(11ms, motionEvent, nullptr); assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, - Pointer{.id = 0, - .x = 2.2f, - .y = 2.2f, - .isResampled = true}); + {Pointer{.id = 0, + .x = 2.2f, + .y = 4.4f, + .isResampled = true}}); } TEST_F(ResamplerTest, SinglePointerDeltaTooSmallExtrapolation) { MotionEvent motionEvent = - InputStream{{{9ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}, - {10ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + InputStream{{InputSample{9ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}, + InputSample{10ms, + {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}}, AMOTION_EVENT_ACTION_MOVE}; const MotionEvent originalMotionEvent = motionEvent; @@ -419,8 +411,9 @@ TEST_F(ResamplerTest, SinglePointerDeltaTooSmallExtrapolation) { TEST_F(ResamplerTest, SinglePointerDeltaTooLargeExtrapolation) { MotionEvent motionEvent = - InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}, - {26ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}, + InputSample{26ms, + {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}}, AMOTION_EVENT_ACTION_MOVE}; const MotionEvent originalMotionEvent = motionEvent; @@ -432,8 +425,9 @@ TEST_F(ResamplerTest, SinglePointerDeltaTooLargeExtrapolation) { TEST_F(ResamplerTest, SinglePointerResampleTimeTooFarExtrapolation) { MotionEvent motionEvent = - InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}, - {25ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}, + InputSample{25ms, + {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}}, AMOTION_EVENT_ACTION_MOVE}; const MotionEvent originalMotionEvent = motionEvent; @@ -441,9 +435,424 @@ TEST_F(ResamplerTest, SinglePointerResampleTimeTooFarExtrapolation) { mResampler->resampleMotionEvent(43ms, motionEvent, nullptr); assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, - Pointer{.id = 0, - .x = 2.4f, - .y = 2.4f, - .isResampled = true}); + {Pointer{.id = 0, + .x = 2.4f, + .y = 4.8f, + .isResampled = true}}); +} + +TEST_F(ResamplerTest, MultiplePointerSingleSampleInterpolation) { + MotionEvent motionEvent = + InputStream{{InputSample{5ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const InputMessage futureSample = + InputSample{15ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + {Pointer{.x = 2.2f, .y = 2.2f, .isResampled = true}, + Pointer{.x = 3.2f, .y = 3.2f, .isResampled = true}}); +} + +TEST_F(ResamplerTest, MultiplePointerSingleSampleExtrapolation) { + MotionEvent firstMotionEvent = + InputStream{{InputSample{5ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr); + + MotionEvent secondMotionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent originalMotionEvent = secondMotionEvent; + + mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, secondMotionEvent, + {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true}, + Pointer{.x = 4.4f, .y = 4.4f, .isResampled = true}}); +} + +TEST_F(ResamplerTest, MultiplePointerMultipleSampleInterpolation) { + MotionEvent motionEvent = + InputStream{{InputSample{5ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}, + InputSample{10ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + const InputMessage futureSample = + InputSample{15ms, + {{.id = 0, .x = 5.0f, .y = 5.0f, .isResampled = false}, + {.id = 1, .x = 6.0f, .y = 6.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true}, + Pointer{.x = 4.4f, .y = 4.4f, .isResampled = true}}); +} + +TEST_F(ResamplerTest, MultiplePointerMultipleSampleExtrapolation) { + MotionEvent motionEvent = + InputStream{{InputSample{5ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}, + InputSample{10ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(11ms, motionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true}, + Pointer{.x = 4.4f, .y = 4.4f, .isResampled = true}}); +} + +TEST_F(ResamplerTest, MultiplePointerIncreaseNumPointersInterpolation) { + MotionEvent motionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const InputMessage futureSample = + InputSample{15ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 2, .x = 5.0f, .y = 5.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + {Pointer{.x = 1.4f, .y = 1.4f, .isResampled = true}, + Pointer{.x = 2.4f, .y = 2.4f, .isResampled = true}}); + + MotionEvent secondMotionEvent = + InputStream{{InputSample{25ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 2, .x = 5.0f, .y = 5.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const InputMessage secondFutureSample = + InputSample{30ms, + {{.id = 0, .x = 5.0f, .y = 5.0f, .isResampled = false}, + {.id = 1, .x = 6.0f, .y = 6.0f, .isResampled = false}, + {.id = 2, .x = 7.0f, .y = 7.0f, .isResampled = false}}}; + + const MotionEvent originalSecondMotionEvent = secondMotionEvent; + + mResampler->resampleMotionEvent(27ms, secondMotionEvent, &secondFutureSample); + + assertMotionEventIsResampledAndCoordsNear(originalSecondMotionEvent, secondMotionEvent, + {Pointer{.x = 3.8f, .y = 3.8f, .isResampled = true}, + Pointer{.x = 4.8f, .y = 4.8f, .isResampled = true}, + Pointer{.x = 5.8f, .y = 5.8f, .isResampled = true}}); +} + +TEST_F(ResamplerTest, MultiplePointerIncreaseNumPointersExtrapolation) { + MotionEvent firstMotionEvent = + InputStream{{InputSample{5ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr); + + MotionEvent secondMotionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 2, .x = 5.0f, .y = 5.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent secondOriginalMotionEvent = secondMotionEvent; + + mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerDecreaseNumPointersInterpolation) { + MotionEvent motionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 2, .x = 5.0f, .y = 5.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const InputMessage futureSample = + InputSample{15ms, + {{.id = 0, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 1, .x = 5.0f, .y = 5.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerDecreaseNumPointersExtrapolation) { + MotionEvent firstMotionEvent = + InputStream{{InputSample{5ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}, + {.id = 2, .x = 3.0f, .y = 3.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr); + + MotionEvent secondMotionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}, + {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent secondOriginalMotionEvent = secondMotionEvent; + + mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsResampledAndCoordsNear(secondOriginalMotionEvent, secondMotionEvent, + {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true}, + Pointer{.x = 4.4f, .y = 4.4f, .isResampled = true}}); +} + +TEST_F(ResamplerTest, MultiplePointerDifferentIdOrderInterpolation) { + MotionEvent motionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const InputMessage futureSample = + InputSample{15ms, + {{.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerDifferentIdOrderExtrapolation) { + MotionEvent firstMotionEvent = + InputStream{{InputSample{5ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr); + + MotionEvent secondMotionEvent = + InputStream{{InputSample{10ms, + {{.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent secondOriginalMotionEvent = secondMotionEvent; + + mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerDifferentIdsInterpolation) { + MotionEvent motionEvent = + InputStream{{InputSample{10ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const InputMessage futureSample = + InputSample{15ms, + {{.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 2, .x = 3.0f, .y = 3.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerDifferentIdsExtrapolation) { + MotionEvent firstMotionEvent = + InputStream{{InputSample{5ms, + {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}, + {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr); + + MotionEvent secondMotionEvent = + InputStream{{InputSample{10ms, + {{.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}, + {.id = 2, .x = 3.0f, .y = 3.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent secondOriginalMotionEvent = secondMotionEvent; + + mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerDifferentToolTypeInterpolation) { + MotionEvent motionEvent = InputStream{{InputSample{10ms, + {{.id = 0, + .toolType = ToolType::FINGER, + .x = 1.0f, + .y = 1.0f, + .isResampled = false}, + {.id = 1, + .toolType = ToolType::FINGER, + .x = 2.0f, + .y = 2.0f, + .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const InputMessage futureSample = InputSample{15ms, + {{.id = 0, + .toolType = ToolType::FINGER, + .x = 3.0, + .y = 3.0, + .isResampled = false}, + {.id = 1, + .toolType = ToolType::STYLUS, + .x = 4.0, + .y = 4.0, + .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerDifferentToolTypeExtrapolation) { + MotionEvent firstMotionEvent = InputStream{{InputSample{5ms, + {{.id = 0, + .toolType = ToolType::FINGER, + .x = 1.0f, + .y = 1.0f, + .isResampled = false}, + {.id = 1, + .toolType = ToolType::FINGER, + .x = 2.0f, + .y = 2.0f, + .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr); + + MotionEvent secondMotionEvent = InputStream{{InputSample{10ms, + {{.id = 0, + .toolType = ToolType::FINGER, + .x = 1.0f, + .y = 1.0f, + .isResampled = false}, + {.id = 1, + .toolType = ToolType::STYLUS, + .x = 2.0f, + .y = 2.0f, + .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent secondOriginalMotionEvent = secondMotionEvent; + + mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerShouldNotResampleToolTypeInterpolation) { + MotionEvent motionEvent = InputStream{{InputSample{10ms, + {{.id = 0, + .toolType = ToolType::PALM, + .x = 1.0f, + .y = 1.0f, + .isResampled = false}, + {.id = 1, + .toolType = ToolType::PALM, + .x = 2.0f, + .y = 2.0f, + .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const InputMessage futureSample = InputSample{15ms, + {{.id = 0, + .toolType = ToolType::PALM, + .x = 3.0, + .y = 3.0, + .isResampled = false}, + {.id = 1, + .toolType = ToolType::PALM, + .x = 4.0, + .y = 4.0, + .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(11ms, motionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +TEST_F(ResamplerTest, MultiplePointerShouldNotResampleToolTypeExtrapolation) { + MotionEvent motionEvent = InputStream{{InputSample{5ms, + {{.id = 0, + .toolType = ToolType::PALM, + .x = 1.0f, + .y = 1.0f, + .isResampled = false}, + {.id = 1, + .toolType = ToolType::PALM, + .x = 2.0f, + .y = 2.0f, + .isResampled = false}}}, + InputSample{10ms, + {{.id = 0, + .toolType = ToolType::PALM, + .x = 3.0f, + .y = 3.0f, + .isResampled = false}, + {.id = 1, + .toolType = ToolType::PALM, + .x = 4.0f, + .y = 4.0f, + .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(11ms, motionEvent, /*futureSample=*/nullptr); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); } } // namespace android |