Skip to content

Commit 27006fe

Browse files
authored
Reland "ChromeOS/Android trackpad gestures" (flutter#34060)
* Android trackpad gestures * Try to fix lint problem
1 parent 3f0bc07 commit 27006fe

File tree

2 files changed

+280
-32
lines changed

2 files changed

+280
-32
lines changed

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

Lines changed: 71 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
import android.view.MotionEvent;
77
import androidx.annotation.IntDef;
88
import androidx.annotation.NonNull;
9+
import androidx.annotation.VisibleForTesting;
910
import io.flutter.embedding.engine.renderer.FlutterRenderer;
1011
import java.nio.ByteBuffer;
1112
import java.nio.ByteOrder;
13+
import java.util.HashMap;
14+
import java.util.Map;
1215

1316
/** Sends touch information from Android to Flutter in a format that Flutter understands. */
1417
public class AndroidTouchProcessor {
@@ -26,7 +29,7 @@ public class AndroidTouchProcessor {
2629
PointerChange.PAN_ZOOM_UPDATE,
2730
PointerChange.PAN_ZOOM_END
2831
})
29-
private @interface PointerChange {
32+
public @interface PointerChange {
3033
int CANCEL = 0;
3134
int ADD = 1;
3235
int REMOVE = 2;
@@ -48,7 +51,7 @@ public class AndroidTouchProcessor {
4851
PointerDeviceKind.TRACKPAD,
4952
PointerDeviceKind.UNKNOWN
5053
})
51-
private @interface PointerDeviceKind {
54+
public @interface PointerDeviceKind {
5255
int TOUCH = 0;
5356
int MOUSE = 1;
5457
int STYLUS = 2;
@@ -59,15 +62,15 @@ public class AndroidTouchProcessor {
5962

6063
// Must match the PointerSignalKind enum in pointer.dart.
6164
@IntDef({PointerSignalKind.NONE, PointerSignalKind.SCROLL, PointerSignalKind.UNKNOWN})
62-
private @interface PointerSignalKind {
65+
public @interface PointerSignalKind {
6366
int NONE = 0;
6467
int SCROLL = 1;
6568
int UNKNOWN = 2;
6669
}
6770

6871
// Must match the unpacking code in hooks.dart.
6972
private static final int POINTER_DATA_FIELD_COUNT = 35;
70-
private static final int BYTES_PER_FIELD = 8;
73+
@VisibleForTesting static final int BYTES_PER_FIELD = 8;
7174

7275
// This value must match the value in framework's platform_view.dart.
7376
// This flag indicates whether the original Android pointer events were batched together.
@@ -76,12 +79,12 @@ public class AndroidTouchProcessor {
7679
@NonNull private final FlutterRenderer renderer;
7780
@NonNull private final MotionEventTracker motionEventTracker;
7881

79-
private static final int _POINTER_BUTTON_PRIMARY = 1;
80-
8182
private static final Matrix IDENTITY_TRANSFORM = new Matrix();
8283

8384
private final boolean trackMotionEvents;
8485

86+
private final Map<Integer, float[]> ongoingPans = new HashMap<>();
87+
8588
/**
8689
* Constructs an {@code AndroidTouchProcessor} that will send touch event data to the Flutter
8790
* execution context represented by the given {@link FlutterRenderer}.
@@ -220,6 +223,28 @@ private void addPointerForIndex(
220223
}
221224

222225
int pointerKind = getPointerDeviceTypeForToolType(event.getToolType(pointerIndex));
226+
// We use this in lieu of using event.getRawX and event.getRawY as we wish to support
227+
// earlier versions than API level 29.
228+
float viewToScreenCoords[] = {event.getX(pointerIndex), event.getY(pointerIndex)};
229+
transformMatrix.mapPoints(viewToScreenCoords);
230+
long buttons;
231+
if (pointerKind == PointerDeviceKind.MOUSE) {
232+
buttons = event.getButtonState() & 0x1F;
233+
if (buttons == 0
234+
&& event.getSource() == InputDevice.SOURCE_MOUSE
235+
&& pointerChange == PointerChange.DOWN) {
236+
// Some implementations translate trackpad scrolling into a mouse down-move-up event
237+
// sequence with buttons: 0, such as ARC on a Chromebook. See #11420, a legacy
238+
// implementation that uses the same condition but converts differently.
239+
ongoingPans.put(event.getPointerId(pointerIndex), viewToScreenCoords);
240+
}
241+
} else if (pointerKind == PointerDeviceKind.STYLUS) {
242+
buttons = (event.getButtonState() >> 4) & 0xF;
243+
} else {
244+
buttons = 0;
245+
}
246+
247+
boolean isTrackpadPan = ongoingPans.containsKey(event.getPointerId(pointerIndex));
223248

224249
int signalKind =
225250
event.getActionMasked() == MotionEvent.ACTION_SCROLL
@@ -230,39 +255,31 @@ private void addPointerForIndex(
230255

231256
packet.putLong(motionEventId); // motionEventId
232257
packet.putLong(timeStamp); // time_stamp
233-
packet.putLong(pointerChange); // change
234-
packet.putLong(pointerKind); // kind
258+
if (isTrackpadPan) {
259+
packet.putLong(getPointerChangeForPanZoom(pointerChange)); // change
260+
packet.putLong(PointerDeviceKind.TRACKPAD); // kind
261+
} else {
262+
packet.putLong(pointerChange); // change
263+
packet.putLong(pointerKind); // kind
264+
}
235265
packet.putLong(signalKind); // signal_kind
236266
packet.putLong(event.getPointerId(pointerIndex)); // device
237267
packet.putLong(0); // pointer_identifier, will be generated in pointer_data_packet_converter.cc.
238268

239-
// We use this in lieu of using event.getRawX and event.getRawY as we wish to support
240-
// earlier versions than API level 29.
241-
float viewToScreenCoords[] = {event.getX(pointerIndex), event.getY(pointerIndex)};
242-
transformMatrix.mapPoints(viewToScreenCoords);
243-
packet.putDouble(viewToScreenCoords[0]); // physical_x
244-
packet.putDouble(viewToScreenCoords[1]); // physical_y
269+
if (isTrackpadPan) {
270+
float[] panStart = ongoingPans.get(event.getPointerId(pointerIndex));
271+
packet.putDouble(panStart[0]);
272+
packet.putDouble(panStart[1]);
273+
} else {
274+
packet.putDouble(viewToScreenCoords[0]); // physical_x
275+
packet.putDouble(viewToScreenCoords[1]); // physical_y
276+
}
245277

246278
packet.putDouble(
247279
0.0); // physical_delta_x, will be generated in pointer_data_packet_converter.cc.
248280
packet.putDouble(
249281
0.0); // physical_delta_y, will be generated in pointer_data_packet_converter.cc.
250282

251-
long buttons;
252-
if (pointerKind == PointerDeviceKind.MOUSE) {
253-
buttons = event.getButtonState() & 0x1F;
254-
// TODO(dkwingsmt): Remove this fix after implementing touchpad gestures
255-
// https://github.com/flutter/flutter/issues/23604#issuecomment-524471152
256-
if (buttons == 0
257-
&& event.getSource() == InputDevice.SOURCE_MOUSE
258-
&& (pointerChange == PointerChange.DOWN || pointerChange == PointerChange.MOVE)) {
259-
buttons = _POINTER_BUTTON_PRIMARY;
260-
}
261-
} else if (pointerKind == PointerDeviceKind.STYLUS) {
262-
buttons = (event.getButtonState() >> 4) & 0xF;
263-
} else {
264-
buttons = 0;
265-
}
266283
packet.putLong(buttons); // buttons
267284

268285
packet.putLong(0); // obscured
@@ -317,12 +334,22 @@ private void addPointerForIndex(
317334
packet.putDouble(0.0); // scroll_delta_x
318335
}
319336

320-
packet.putDouble(0.0); // pan_x
321-
packet.putDouble(0.0); // pan_y
337+
if (isTrackpadPan) {
338+
float[] panStart = ongoingPans.get(event.getPointerId(pointerIndex));
339+
packet.putDouble(viewToScreenCoords[0] - panStart[0]);
340+
packet.putDouble(viewToScreenCoords[1] - panStart[1]);
341+
} else {
342+
packet.putDouble(0.0); // pan_x
343+
packet.putDouble(0.0); // pan_y
344+
}
322345
packet.putDouble(0.0); // pan_delta_x
323346
packet.putDouble(0.0); // pan_delta_y
324347
packet.putDouble(1.0); // scale
325348
packet.putDouble(0.0); // rotation
349+
350+
if (isTrackpadPan && getPointerChangeForPanZoom(pointerChange) == PointerChange.PAN_ZOOM_END) {
351+
ongoingPans.remove(event.getPointerId(pointerIndex));
352+
}
326353
}
327354

328355
@PointerChange
@@ -354,7 +381,19 @@ private int getPointerChangeForAction(int maskedAction) {
354381
if (maskedAction == MotionEvent.ACTION_SCROLL) {
355382
return PointerChange.HOVER;
356383
}
357-
return -1;
384+
throw new AssertionError("Unexpected masked action");
385+
}
386+
387+
@PointerChange
388+
private int getPointerChangeForPanZoom(int pointerChange) {
389+
if (pointerChange == PointerChange.DOWN) {
390+
return PointerChange.PAN_ZOOM_START;
391+
} else if (pointerChange == PointerChange.MOVE) {
392+
return PointerChange.PAN_ZOOM_UPDATE;
393+
} else if (pointerChange == PointerChange.UP || pointerChange == PointerChange.CANCEL) {
394+
return PointerChange.PAN_ZOOM_END;
395+
}
396+
throw new AssertionError("Unexpected pointer change");
358397
}
359398

360399
@PointerDeviceKind

0 commit comments

Comments
 (0)