[flutter] Resample down/up pointer changes
This makes resampled events work better with code that expect a
fixed interval between touch events.
Test: fx run-host-tests dart_widget_tests
Change-Id: I9f6d85301fe3441f9ef2da0c8b8684ce59e35259
Reviewed-on: https://fuchsia-review.googlesource.com/c/experiences/+/399957
Reviewed-by: Sanjay Chouksey <sanjayc@google.com>
Testability-Review: Sanjay Chouksey <sanjayc@google.com>
Commit-Queue: David Reveman <reveman@google.com>
diff --git a/settings/lib/widgets/lib/src/utils/pointer_events_listener.dart b/settings/lib/widgets/lib/src/utils/pointer_events_listener.dart
index ffab120..8de20e5 100644
--- a/settings/lib/widgets/lib/src/utils/pointer_events_listener.dart
+++ b/settings/lib/widgets/lib/src/utils/pointer_events_listener.dart
@@ -206,7 +206,7 @@
Timeline.timeSync('PointerEventsListener.dispatchQueuedEvents', () {
_consumePendingEvents();
_updateDownPointers();
- _dispatchMoveChanges();
+ _dispatchResampledChanges();
// Schedule frame callback if down pointers exists or events are
// still queued. We need the frame callback to determine sample
@@ -245,10 +245,7 @@
if (!_downPointers.containsKey(event.p.pointerId)) {
_downPointers[event.p.pointerId] = _DownPointer(event, event);
}
-
- // Move changes will be re-sampled instead of dispatched directly.
_downPointers[event.p.pointerId].next = event;
- dispatchEvent = false;
break;
case PointerEventPhase.up:
case PointerEventPhase.cancel:
@@ -259,6 +256,9 @@
case PointerEventPhase.hover:
break;
}
+
+ // Touch changes will be re-sampled instead of dispatched directly.
+ dispatchEvent = false;
}
if (dispatchEvent) {
@@ -307,14 +307,14 @@
}
}
- void _dispatchMoveChanges() {
+ void _dispatchResampledChanges() {
final packets = <ui.PointerData>[];
- // Add move changes for [_downPointers].
+ // Add resampled changes for [_downPointers].
for (var v in _downPointers.values) {
var x = v.next.p.x;
var y = v.next.p.y;
- var eventTime = v.next.p.eventTime;
+
// Re-sample if next time stamp is past sample time.
if (v.next.p.eventTime > _sampleTimeNs &&
v.next.p.eventTime > v.last.p.eventTime) {
@@ -328,20 +328,33 @@
(_sampleTimeNs - v.last.p.eventTime).toDouble() / interval;
x = v.last.p.x + (v.next.p.x - v.last.p.x) * scalar;
y = v.last.p.y + (v.next.p.y - v.last.p.y) * scalar;
- eventTime = _sampleTimeNs;
}, arguments: resampleArguments);
}
- // Add move change if time stamp is greater than last event.
- if (eventTime > v.last.p.eventTime) {
- final event = _clone(v.next.p, PointerEventPhase.move, x, y, eventTime);
+ // Add resampled change if time stamp is greater than last event.
+ if (_sampleTimeNs > v.last.p.eventTime) {
+ final event = _clone(v.next.p, v.next.p.phase, x, y, _sampleTimeNs);
final eventArguments = <String, int>{
- 'eventTimeUs': eventTime ~/ 1000,
+ 'eventTimeUs': _sampleTimeNs ~/ 1000,
};
- Timeline.timeSync('PointerEventsListener.addMoveEvent', () {
- final packet = _getPacket(event);
- if (packet != null) {
- packets.add(packet);
+ Timeline.timeSync('PointerEventsListener.addResampledEvents', () {
+ var addMove = true;
+ // Add `down` event if needed.
+ if (_downEvent[v.next.p.pointerId] != true) {
+ final event =
+ _clone(v.next.p, PointerEventPhase.down, x, y, _sampleTimeNs);
+ final packet = _getPacket(event);
+ if (packet != null) {
+ packets.add(packet);
+ }
+ // Skip `move` phase if we already added a `down` event.
+ addMove = false;
+ }
+ if (addMove || v.next.p.phase != PointerEventPhase.move) {
+ final packet = _getPacket(event);
+ if (packet != null) {
+ packets.add(packet);
+ }
}
}, arguments: eventArguments, flow: Flow.end(v.last.flow2.id));
diff --git a/settings/lib/widgets/test/pointer_events_listener_test.dart b/settings/lib/widgets/test/pointer_events_listener_test.dart
index 4a70d96..51a322f 100644
--- a/settings/lib/widgets/test/pointer_events_listener_test.dart
+++ b/settings/lib/widgets/test/pointer_events_listener_test.dart
@@ -78,22 +78,15 @@
when(scheduler.currentSystemFrameTimeStamp).thenReturn(frameTime);
callback(Duration());
- // Two pointer events should have been dispatched.
- expect(result.length, 2);
- expect(result[0].timeStamp, Duration(milliseconds: 1));
+ // One pointer event should have been dispatched.
+ expect(result.length, 1);
+ expect(result[0].timeStamp, frameTime + _samplingOffset);
expect(result[0].change, ui.PointerChange.down);
- expect(result[0].physicalX, 0.0);
+ expect(result[0].physicalX, 5.0 * ui.window.devicePixelRatio);
expect(result[0].physicalY, 0.0);
expect(result[0].physicalDeltaX, 0.0);
expect(result[0].physicalDeltaY, 0.0);
- expect(result[1].timeStamp, frameTime + _samplingOffset);
- expect(result[1].change, ui.PointerChange.move);
- expect(result[1].physicalX, 5.0 * ui.window.devicePixelRatio);
- expect(result[1].physicalY, 0.0);
- expect(result[1].physicalDeltaX, 5.0 * ui.window.devicePixelRatio);
- expect(result[1].physicalDeltaY, 0.0);
-
// Another frame callback should have been requested.
callback = verify(scheduler.scheduleFrameCallback(captureThat(isNotNull)))
.captured
@@ -106,46 +99,35 @@
callback(Duration());
// Another pointer event should have been dispatched.
- expect(result.length, 3);
- expect(result[2].timeStamp, frameTime + _samplingOffset);
- expect(result[2].change, ui.PointerChange.move);
- expect(result[2].physicalX, 25.0 * ui.window.devicePixelRatio);
- expect(result[2].physicalY, 0.0);
- expect(result[2].physicalDeltaX, 20.0 * ui.window.devicePixelRatio);
- expect(result[2].physicalDeltaY, 0.0);
-
- // Another frame callback should have been requested.
- callback = verify(scheduler.scheduleFrameCallback(captureThat(isNotNull)))
- .captured
- .single;
- verify(scheduler.scheduleFrame());
- clearInteractions(scheduler);
-
- frameTime = Duration(milliseconds: 11);
- when(scheduler.currentSystemFrameTimeStamp).thenReturn(frameTime);
- callback(Duration());
-
- // Last pointer event should have been dispatched.
- expect(result.length, 4);
- expect(result[3].timeStamp, Duration(milliseconds: 4));
- expect(result[3].change, ui.PointerChange.up);
- expect(result[3].physicalX, 30.0 * ui.window.devicePixelRatio);
- expect(result[3].physicalY, 0.0);
- expect(result[3].physicalDeltaX, 5.0 * ui.window.devicePixelRatio);
- expect(result[3].physicalDeltaY, 0.0);
+ expect(result.length, 2);
+ expect(result[1].timeStamp, frameTime + _samplingOffset);
+ expect(result[1].change, ui.PointerChange.up);
+ expect(result[1].physicalX, 25.0 * ui.window.devicePixelRatio);
+ expect(result[1].physicalY, 0.0);
+ expect(result[1].physicalDeltaX, 20.0 * ui.window.devicePixelRatio);
+ expect(result[1].physicalDeltaY, 0.0);
});
test('bad event time', () {
const _badEventTimeUs = 9999999;
final event0 = _createSimulatedPointerEvent(
PointerEventPhase.down, _badEventTimeUs, 0.0, 0.0);
- final event1 = _createSimulatedPointerEvent(
- PointerEventPhase.up, _badEventTimeUs, 0.0, 0.0);
var frameTime = Duration(milliseconds: 6);
when(scheduler.currentSystemFrameTimeStamp).thenReturn(frameTime);
- pointerEventsListener..onPointerEvent(event0)..onPointerEvent(event1);
+ pointerEventsListener.onPointerEvent(event0);
+
+ // No pointer events should have been dispatched yet.
+ expect(result.isEmpty, true);
+
+ final event1 =
+ _createSimulatedPointerEvent(PointerEventPhase.up, 7000, 0.0, 0.0);
+
+ frameTime = Duration(milliseconds: 7);
+ when(scheduler.currentSystemFrameTimeStamp).thenReturn(frameTime);
+
+ pointerEventsListener.onPointerEvent(event1);
// No pointer events should have been dispatched yet.
expect(result.isEmpty, true);
@@ -158,18 +140,18 @@
verify(scheduler.scheduleFrame());
clearInteractions(scheduler);
- frameTime = Duration(milliseconds: 20);
+ frameTime = Duration(milliseconds: 12);
when(scheduler.currentSystemFrameTimeStamp).thenReturn(frameTime);
callback(Duration());
- // Event time stamps should have been ignored and two pointer events
+ // First event time stamp should have been ignored and two pointer events
// should have been dispatched.
expect(result.length, 2);
expect(result[0].change, ui.PointerChange.down);
expect(result[1].change, ui.PointerChange.up);
});
- test('Handle a move without a previous pointer down.', () {
+ test('move without a previous pointer down.', () {
final event0 =
_createSimulatedPointerEvent(PointerEventPhase.move, 1000, 0.0, 0.0);
final event1 =
@@ -203,10 +185,10 @@
when(scheduler.currentSystemFrameTimeStamp).thenReturn(frameTime);
callback(Duration());
- // Two pointer events should have been dispatched.
+ // One pointer event should have been dispatched.
expect(result.length, 1);
expect(result[0].timeStamp, frameTime + _samplingOffset);
- expect(result[0].change, ui.PointerChange.add);
+ expect(result[0].change, ui.PointerChange.down);
expect(result[0].physicalX, 5.0 * ui.window.devicePixelRatio);
expect(result[0].physicalY, 0.0);
expect(result[0].physicalDeltaX, 0.0);
@@ -226,30 +208,10 @@
// Another pointer event should have been dispatched.
expect(result.length, 2);
expect(result[1].timeStamp, frameTime + _samplingOffset);
- expect(result[1].change, ui.PointerChange.down);
+ expect(result[1].change, ui.PointerChange.up);
expect(result[1].physicalX, 25.0 * ui.window.devicePixelRatio);
expect(result[1].physicalY, 0.0);
expect(result[1].physicalDeltaX, 20.0 * ui.window.devicePixelRatio);
expect(result[1].physicalDeltaY, 0.0);
-
- // Another frame callback should have been requested.
- callback = verify(scheduler.scheduleFrameCallback(captureThat(isNotNull)))
- .captured
- .single;
- verify(scheduler.scheduleFrame());
- clearInteractions(scheduler);
-
- frameTime = Duration(milliseconds: 11);
- when(scheduler.currentSystemFrameTimeStamp).thenReturn(frameTime);
- callback(Duration());
-
- // Last pointer event should have been dispatched.
- expect(result.length, 3);
- expect(result[2].timeStamp, Duration(milliseconds: 4));
- expect(result[2].change, ui.PointerChange.up);
- expect(result[2].physicalX, 30.0 * ui.window.devicePixelRatio);
- expect(result[2].physicalY, 0.0);
- expect(result[2].physicalDeltaX, 5.0 * ui.window.devicePixelRatio);
- expect(result[2].physicalDeltaY, 0.0);
});
}