Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 8c2ba5d

Browse files
LoveJelloxiaohu.hxh
authored andcommitted
Fix unexpected pointer change (#129765) and add test case
Signed-off-by: Xiaohu Hu <huxiaohu2007@gmail.com>
1 parent 3f70201 commit 8c2ba5d

File tree

2 files changed

+90
-18
lines changed

2 files changed

+90
-18
lines changed

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

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ public class AndroidTouchProcessor {
9393

9494
private final Map<Integer, float[]> ongoingPans = new HashMap<>();
9595

96+
private boolean isSkipVersionCheck = false;
97+
98+
@VisibleForTesting
99+
public void setSkipVersionCheck(boolean enabled) {
100+
isSkipVersionCheck = enabled;
101+
}
102+
96103
/**
97104
* Constructs an {@code AndroidTouchProcessor} that will send touch event data to the Flutter
98105
* execution context represented by the given {@link FlutterRenderer}.
@@ -186,16 +193,18 @@ public boolean onTouchEvent(@NonNull MotionEvent event, @NonNull Matrix transfor
186193
public boolean onGenericMotionEvent(@NonNull MotionEvent event) {
187194
// Method isFromSource is only available in API 18+ (Jelly Bean MR2)
188195
// Mouse hover support is not implemented for API < 18.
189-
boolean isPointerEvent =
190-
Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2
191-
&& event.isFromSource(InputDevice.SOURCE_CLASS_POINTER);
192-
boolean isMovementEvent =
193-
(event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE
194-
|| event.getActionMasked() == MotionEvent.ACTION_SCROLL);
195-
if (!isPointerEvent || !isMovementEvent) {
196-
return false;
196+
if (!isSkipVersionCheck) {
197+
boolean isPointerEvent =
198+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2
199+
&& event.isFromSource(InputDevice.SOURCE_CLASS_POINTER);
200+
boolean isMovementEvent =
201+
(event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE
202+
|| event.getActionMasked() == MotionEvent.ACTION_SCROLL);
203+
204+
if (!isPointerEvent || !isMovementEvent) {
205+
return false;
206+
}
197207
}
198-
199208
int pointerChange = getPointerChangeForAction(event.getActionMasked());
200209
ByteBuffer packet =
201210
ByteBuffer.allocateDirect(
@@ -224,12 +233,6 @@ private void addPointerForIndex(
224233
return;
225234
}
226235

227-
long motionEventId = 0;
228-
if (trackMotionEvents) {
229-
MotionEventTracker.MotionEventId trackedEvent = motionEventTracker.track(event);
230-
motionEventId = trackedEvent.getId();
231-
}
232-
233236
int pointerKind = getPointerDeviceTypeForToolType(event.getToolType(pointerIndex));
234237
// We use this in lieu of using event.getRawX and event.getRawY as we wish to support
235238
// earlier versions than API level 29.
@@ -252,7 +255,20 @@ private void addPointerForIndex(
252255
buttons = 0;
253256
}
254257

258+
int panZoomType = -1;
255259
boolean isTrackpadPan = ongoingPans.containsKey(event.getPointerId(pointerIndex));
260+
if (isTrackpadPan) {
261+
panZoomType = getPointerChangeForPanZoom(pointerChange);
262+
if (panZoomType == -1) {
263+
return;
264+
}
265+
}
266+
267+
long motionEventId = 0;
268+
if (trackMotionEvents) {
269+
MotionEventTracker.MotionEventId trackedEvent = motionEventTracker.track(event);
270+
motionEventId = trackedEvent.getId();
271+
}
256272

257273
int signalKind =
258274
event.getActionMasked() == MotionEvent.ACTION_SCROLL
@@ -264,7 +280,7 @@ private void addPointerForIndex(
264280
packet.putLong(motionEventId); // motionEventId
265281
packet.putLong(timeStamp); // time_stamp
266282
if (isTrackpadPan) {
267-
packet.putLong(getPointerChangeForPanZoom(pointerChange)); // change
283+
packet.putLong(panZoomType); // change
268284
packet.putLong(PointerDeviceKind.TRACKPAD); // kind
269285
} else {
270286
packet.putLong(pointerChange); // change
@@ -355,7 +371,7 @@ private void addPointerForIndex(
355371
packet.putDouble(1.0); // scale
356372
packet.putDouble(0.0); // rotation
357373

358-
if (isTrackpadPan && getPointerChangeForPanZoom(pointerChange) == PointerChange.PAN_ZOOM_END) {
374+
if (isTrackpadPan && (panZoomType == PointerChange.PAN_ZOOM_END)) {
359375
ongoingPans.remove(event.getPointerId(pointerIndex));
360376
}
361377
}
@@ -401,7 +417,7 @@ private int getPointerChangeForPanZoom(int pointerChange) {
401417
} else if (pointerChange == PointerChange.UP || pointerChange == PointerChange.CANCEL) {
402418
return PointerChange.PAN_ZOOM_END;
403419
}
404-
throw new AssertionError("Unexpected pointer change");
420+
return -1;
405421
}
406422

407423
@PointerDeviceKind

shell/platform/android/test/io/flutter/embedding/android/AndroidTouchProcessorTest.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,4 +218,60 @@ public void unexpectedMaskedAction() {
218218
touchProcessor.onTouchEvent(mocker.mockEvent(MotionEvent.ACTION_BUTTON_PRESS, 0.0f, 0.0f, 0));
219219
verify(mockRenderer, never()).dispatchPointerDataPacket(ByteBuffer.allocate(0), 0);
220220
}
221+
222+
@Test
223+
public void unexpectedPointerChange() {
224+
// Regression test for https://github.com/flutter/flutter/issues/129765
225+
226+
MotionEventMocker mocker =
227+
new MotionEventMocker(0, InputDevice.SOURCE_MOUSE, MotionEvent.TOOL_TYPE_MOUSE);
228+
229+
touchProcessor.onTouchEvent(mocker.mockEvent(MotionEvent.ACTION_DOWN, 0.0f, 0.0f, 0));
230+
InOrder inOrder = inOrder(mockRenderer);
231+
inOrder
232+
.verify(mockRenderer)
233+
.dispatchPointerDataPacket(packetCaptor.capture(), packetSizeCaptor.capture());
234+
ByteBuffer packet = packetCaptor.getValue();
235+
assertEquals(AndroidTouchProcessor.PointerChange.PAN_ZOOM_START, readPointerChange(packet));
236+
assertEquals(AndroidTouchProcessor.PointerDeviceKind.TRACKPAD, readPointerDeviceKind(packet));
237+
assertEquals(AndroidTouchProcessor.PointerSignalKind.NONE, readPointerSignalKind(packet));
238+
assertEquals(0.0, readPointerPhysicalX(packet));
239+
assertEquals(0.0, readPointerPhysicalY(packet));
240+
241+
touchProcessor.onTouchEvent(mocker.mockEvent(MotionEvent.ACTION_MOVE, 10.0f, 5.0f, 0));
242+
inOrder
243+
.verify(mockRenderer)
244+
.dispatchPointerDataPacket(packetCaptor.capture(), packetSizeCaptor.capture());
245+
packet = packetCaptor.getValue();
246+
assertEquals(AndroidTouchProcessor.PointerChange.PAN_ZOOM_UPDATE, readPointerChange(packet));
247+
assertEquals(AndroidTouchProcessor.PointerDeviceKind.TRACKPAD, readPointerDeviceKind(packet));
248+
assertEquals(AndroidTouchProcessor.PointerSignalKind.NONE, readPointerSignalKind(packet));
249+
assertEquals(0.0, readPointerPhysicalX(packet));
250+
assertEquals(0.0, readPointerPhysicalY(packet));
251+
assertEquals(10.0, readPointerPanX(packet));
252+
assertEquals(5.0, readPointerPanY(packet));
253+
254+
touchProcessor.setSkipVersionCheck(true);
255+
touchProcessor.onGenericMotionEvent(mocker.mockEvent(MotionEvent.ACTION_SCROLL, 0.0f, 0.0f, 0));
256+
inOrder
257+
.verify(mockRenderer)
258+
.dispatchPointerDataPacket(packetCaptor.capture(), packetSizeCaptor.capture());
259+
packet = packetCaptor.getValue();
260+
packet.rewind();
261+
while (packet.hasRemaining()) {
262+
assertEquals(0, packet.get());
263+
}
264+
265+
touchProcessor.onTouchEvent(mocker.mockEvent(MotionEvent.ACTION_UP, 10.0f, 5.0f, 0));
266+
inOrder
267+
.verify(mockRenderer)
268+
.dispatchPointerDataPacket(packetCaptor.capture(), packetSizeCaptor.capture());
269+
packet = packetCaptor.getValue();
270+
assertEquals(AndroidTouchProcessor.PointerChange.PAN_ZOOM_END, readPointerChange(packet));
271+
assertEquals(AndroidTouchProcessor.PointerDeviceKind.TRACKPAD, readPointerDeviceKind(packet));
272+
assertEquals(AndroidTouchProcessor.PointerSignalKind.NONE, readPointerSignalKind(packet));
273+
assertEquals(0.0, readPointerPhysicalX(packet));
274+
assertEquals(0.0, readPointerPhysicalY(packet));
275+
inOrder.verifyNoMoreInteractions();
276+
}
221277
}

0 commit comments

Comments
 (0)