Skip to content

Commit e5e9ec2

Browse files
author
Kaushik Iska
committed
Track motion events for reuse post gesture disambiguation
This change makes it so that we track all the motion events encountered by `FlutterView` and all of its subviews in the `MotionEventTracker` class, indexed by a unique `MotionEventId`. This identifier is then passed to the Flutter framework as seen in flutter/flutter#60930. Once the gestures take part in gesture disambiguation and are sent back to the engine, we look-up the original motion event using the `MotionEventId` and dispatch it to the platform. Bug: flutter/flutter#58837
1 parent d1f3037 commit e5e9ec2

File tree

10 files changed

+122
-8
lines changed

10 files changed

+122
-8
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/Flutt
697697
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java
698698
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java
699699
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java
700+
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/MotionEventTracker.java
700701
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/RenderMode.java
701702
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/SplashScreen.java
702703
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/SplashScreenProvider.java

lib/ui/hooks.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,9 +287,9 @@ void _invoke3<A1, A2, A3>(void callback(A1 a1, A2 a2, A3 a3)?, Zone zone, A1 arg
287287
// If this value changes, update the encoding code in the following files:
288288
//
289289
// * pointer_data.cc
290-
// * pointers.dart
290+
// * pointer.dart
291291
// * AndroidTouchProcessor.java
292-
const int _kPointerDataFieldCount = 28;
292+
const int _kPointerDataFieldCount = 29;
293293

294294
PointerDataPacket _unpackPointerDataPacket(ByteData packet) {
295295
const int kStride = Int64List.bytesPerElement;
@@ -300,6 +300,7 @@ PointerDataPacket _unpackPointerDataPacket(ByteData packet) {
300300
for (int i = 0; i < length; ++i) {
301301
int offset = i * _kPointerDataFieldCount;
302302
data.add(PointerData(
303+
motionEventId: packet.getInt64(kStride * offset++, _kFakeHostEndian),
303304
timeStamp: Duration(microseconds: packet.getInt64(kStride * offset++, _kFakeHostEndian)),
304305
change: PointerChange.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)],
305306
kind: PointerDeviceKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)],

lib/ui/pointer.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ enum PointerSignalKind {
7272
class PointerData {
7373
/// Creates an object that represents the state of a pointer.
7474
const PointerData({
75+
this.motionEventId = 0,
7576
this.timeStamp = Duration.zero,
7677
this.change = PointerChange.cancel,
7778
this.kind = PointerDeviceKind.touch,
@@ -102,6 +103,9 @@ class PointerData {
102103
this.scrollDeltaY = 0.0,
103104
});
104105

106+
/// Unique identifier for the motion event corresponding to the pointer event.
107+
final int motionEventId;
108+
105109
/// Time of event dispatch, relative to an arbitrary timeline.
106110
final Duration timeStamp;
107111

@@ -263,6 +267,7 @@ class PointerData {
263267
/// Returns a complete textual description of the information in this object.
264268
String toStringFull() {
265269
return '$runtimeType('
270+
'motionEventId: $motionEventId, '
266271
'timeStamp: $timeStamp, '
267272
'change: $change, '
268273
'kind: $kind, '

lib/ui/window/pointer_data.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
namespace flutter {
1111

1212
// If this value changes, update the pointer data unpacking code in hooks.dart.
13-
static constexpr int kPointerDataFieldCount = 28;
13+
static constexpr int kPointerDataFieldCount = 29;
1414
static constexpr int kBytesPerField = sizeof(int64_t);
1515
// Must match the button constants in events.dart.
1616
enum PointerButtonMouse : int64_t {
@@ -58,6 +58,7 @@ struct alignas(8) PointerData {
5858
kScroll,
5959
};
6060

61+
int64_t motion_event_id;
6162
int64_t time_stamp;
6263
Change change;
6364
DeviceKind kind;

shell/platform/android/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ android_java_sources = [
140140
"io/flutter/embedding/android/FlutterSurfaceView.java",
141141
"io/flutter/embedding/android/FlutterTextureView.java",
142142
"io/flutter/embedding/android/FlutterView.java",
143+
"io/flutter/embedding/android/MotionEventTracker.java",
143144
"io/flutter/embedding/android/RenderMode.java",
144145
"io/flutter/embedding/android/SplashScreen.java",
145146
"io/flutter/embedding/android/SplashScreenProvider.java",

shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,15 @@ public class AndroidTouchProcessor {
5757
}
5858

5959
// Must match the unpacking code in hooks.dart.
60-
private static final int POINTER_DATA_FIELD_COUNT = 28;
60+
private static final int POINTER_DATA_FIELD_COUNT = 29;
6161
private static final int BYTES_PER_FIELD = 8;
6262

6363
// This value must match the value in framework's platform_view.dart.
6464
// This flag indicates whether the original Android pointer events were batched together.
6565
private static final int POINTER_DATA_FLAG_BATCHED = 1;
6666

6767
@NonNull private final FlutterRenderer renderer;
68+
@NonNull private final MotionEventTracker motionEventTracker;
6869

6970
private static final int _POINTER_BUTTON_PRIMARY = 1;
7071

@@ -76,6 +77,7 @@ public class AndroidTouchProcessor {
7677
// FlutterRenderer
7778
public AndroidTouchProcessor(@NonNull FlutterRenderer renderer) {
7879
this.renderer = renderer;
80+
this.motionEventTracker = MotionEventTracker.getInstance();
7981
}
8082

8183
/** Sends the given {@link MotionEvent} data to Flutter in a format that Flutter understands. */
@@ -174,6 +176,8 @@ private void addPointerForIndex(
174176
return;
175177
}
176178

179+
MotionEventTracker.MotionEventId motionEventId = motionEventTracker.track(event);
180+
177181
int pointerKind = getPointerDeviceTypeForToolType(event.getToolType(pointerIndex));
178182

179183
int signalKind =
@@ -183,6 +187,7 @@ private void addPointerForIndex(
183187

184188
long timeStamp = event.getEventTime() * 1000; // Convert from milliseconds to microseconds.
185189

190+
packet.putLong(motionEventId.getId());
186191
packet.putLong(timeStamp); // time_stamp
187192
packet.putLong(pointerChange); // change
188193
packet.putLong(pointerKind); // kind
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package io.flutter.embedding.android;
2+
3+
import android.util.LongSparseArray;
4+
import android.view.MotionEvent;
5+
import androidx.annotation.Nullable;
6+
import java.util.PriorityQueue;
7+
import java.util.concurrent.atomic.AtomicLong;
8+
9+
/** Tracks the motion events received by the FlutterView. */
10+
public final class MotionEventTracker {
11+
12+
/** Represents a unique identifier corresponding to a motion event. */
13+
public static class MotionEventId {
14+
private static final AtomicLong ID_COUNTER = new AtomicLong(0);
15+
private final long id;
16+
17+
private MotionEventId(long id) {
18+
this.id = id;
19+
}
20+
21+
public static MotionEventId from(long id) {
22+
return new MotionEventId(id);
23+
}
24+
25+
public static MotionEventId createUnique() {
26+
return MotionEventId.from(ID_COUNTER.incrementAndGet());
27+
}
28+
29+
public long getId() {
30+
return id;
31+
}
32+
}
33+
34+
private final LongSparseArray<MotionEvent> eventById;
35+
private final PriorityQueue<Long> unusedEvents;
36+
private static MotionEventTracker INSTANCE;
37+
38+
public static MotionEventTracker getInstance() {
39+
if (INSTANCE == null) {
40+
INSTANCE = new MotionEventTracker();
41+
}
42+
return INSTANCE;
43+
}
44+
45+
private MotionEventTracker() {
46+
eventById = new LongSparseArray<>();
47+
unusedEvents = new PriorityQueue<>();
48+
}
49+
50+
/** Tracks the event and returns a unique MotionEventId identifying the event. */
51+
public MotionEventId track(MotionEvent event) {
52+
MotionEventId eventId = MotionEventId.createUnique();
53+
eventById.put(eventId.id, event);
54+
unusedEvents.add(eventId.id);
55+
return eventId;
56+
}
57+
58+
/**
59+
* Returns the MotionEvent corresponding to the eventId while discarding all the motion events
60+
* that occured prior to the event represented by the eventId. Returns null if this event was
61+
* popped or discarded.
62+
*/
63+
@Nullable
64+
public MotionEvent pop(MotionEventId eventId) {
65+
// remove all the older events.
66+
while (!unusedEvents.isEmpty() && unusedEvents.peek() < eventId.id) {
67+
eventById.remove(unusedEvents.poll());
68+
}
69+
70+
// remove the current event from the heap if it exists.
71+
if (!unusedEvents.isEmpty() && unusedEvents.peek() == eventId.id) {
72+
unusedEvents.poll();
73+
}
74+
75+
MotionEvent event = eventById.get(eventId.id);
76+
eventById.remove(eventId.id);
77+
return event;
78+
}
79+
}

shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformViewsChannel.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,8 @@ private void touch(@NonNull MethodCall call, @NonNull MethodChannel.Result resul
166166
(int) args.get(11),
167167
(int) args.get(12),
168168
(int) args.get(13),
169-
(int) args.get(14));
169+
(int) args.get(14),
170+
((Number) args.get(15)).longValue());
170171

171172
try {
172173
handler.onTouch(touch);
@@ -380,6 +381,8 @@ public static class PlatformViewTouch {
380381
public final int source;
381382
/** TODO(mattcarroll): javadoc */
382383
public final int flags;
384+
/** TODO(iskakaushik): javadoc */
385+
public final long motionEventId;
383386

384387
PlatformViewTouch(
385388
int viewId,
@@ -396,7 +399,8 @@ public static class PlatformViewTouch {
396399
int deviceId,
397400
int edgeFlags,
398401
int source,
399-
int flags) {
402+
int flags,
403+
long motionEventId) {
400404
this.viewId = viewId;
401405
this.downTime = downTime;
402406
this.eventTime = eventTime;
@@ -412,6 +416,7 @@ public static class PlatformViewTouch {
412416
this.edgeFlags = edgeFlags;
413417
this.source = source;
414418
this.flags = flags;
419+
this.motionEventId = motionEventId;
415420
}
416421
}
417422
}

shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import androidx.annotation.VisibleForTesting;
2222
import io.flutter.embedding.android.FlutterImageView;
2323
import io.flutter.embedding.android.FlutterView;
24+
import io.flutter.embedding.android.MotionEventTracker;
2425
import io.flutter.embedding.engine.FlutterOverlaySurface;
2526
import io.flutter.embedding.engine.dart.DartExecutor;
2627
import io.flutter.embedding.engine.mutatorsstack.*;
@@ -93,6 +94,9 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
9394
// Platform view IDs that were displayed since the start of the current frame.
9495
private HashSet<Integer> currentFrameUsedPlatformViewIds;
9596

97+
// Used to acquire the original motion events using the motionEventIds.
98+
private final MotionEventTracker motionEventTracker;
99+
96100
private final PlatformViewsChannel.PlatformViewsHandler channelHandler =
97101
new PlatformViewsChannel.PlatformViewsHandler() {
98102

@@ -301,8 +305,16 @@ private void ensureValidAndroidVersion(int minSdkVersion) {
301305
}
302306
};
303307

304-
private static MotionEvent toMotionEvent(
305-
float density, PlatformViewsChannel.PlatformViewTouch touch) {
308+
private MotionEvent toMotionEvent(float density, PlatformViewsChannel.PlatformViewTouch touch) {
309+
MotionEventTracker.MotionEventId motionEventId =
310+
MotionEventTracker.MotionEventId.from(touch.motionEventId);
311+
MotionEvent trackedEvent = motionEventTracker.pop(motionEventId);
312+
if (trackedEvent != null) {
313+
return trackedEvent;
314+
}
315+
316+
// TODO (kaushikiska) : warn that we are potentially using an untracked
317+
// event in the platform views.
306318
PointerProperties[] pointerProperties =
307319
parsePointerPropertiesList(touch.rawPointerPropertiesList)
308320
.toArray(new PointerProperties[touch.pointerCount]);
@@ -339,6 +351,8 @@ public PlatformViewsController() {
339351
platformViewRequests = new SparseArray<>();
340352
platformViews = new SparseArray<>();
341353
mutatorViews = new SparseArray<>();
354+
355+
motionEventTracker = MotionEventTracker.getInstance();
342356
}
343357

344358
/**

shell/platform/embedder/embedder.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,8 @@ FlutterEngineResult FlutterEngineSendPointerEvent(
12251225
for (size_t i = 0; i < events_count; ++i) {
12261226
flutter::PointerData pointer_data;
12271227
pointer_data.Clear();
1228+
// this is only for android embedding.
1229+
pointer_data.motion_event_id = 0;
12281230
pointer_data.time_stamp = SAFE_ACCESS(current, timestamp, 0);
12291231
pointer_data.change = ToPointerDataChange(
12301232
SAFE_ACCESS(current, phase, FlutterPointerPhase::kCancel));

0 commit comments

Comments
 (0)