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
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
index 342f7f5..a1e6e06 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_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 @@
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});
}
}
-bool LegacyResampler::canInterpolate(const InputMessage& futureSample) const {
+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& 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 @@
<< "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 @@
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 @@
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 135f8b4..b372c0b 100644
--- a/libs/input/tests/Resampler_test.cpp
+++ b/libs/input/tests/Resampler_test.cpp
@@ -59,6 +59,9 @@
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 @@
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 @@
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 @@
* 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 @@
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 @@
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 @@
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, 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 @@
* 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, 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 @@
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, 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, 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 @@
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