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

Commit fcbf66f

Browse files
LoveJelloxiaohu.hxh
authored andcommitted
Fix unexpected pointer change (#129765)
MotionEvent.isFromSource() is not mockable because it's from super class in InputEvent. So we need to change if condition from "if (!isPointerEvent || !isMovementEvent)" to "if (isPointerEvent || isMovementEvent)". Without this change, this test case will end up with "wanted but not invoked" error. Remove debug log Signed-off-by: Xiaohu Hu <huxiaohu2007@gmail.com> Remove incorrect panZoomType check Signed-off-by: Xiaohu Hu <huxiaohu2007@gmail.com>
1 parent e40995d commit fcbf66f

File tree

2 files changed

+79
-20
lines changed

2 files changed

+79
-20
lines changed

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

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
/** Sends touch information from Android to Flutter in a format that Flutter understands. */
1717
public class AndroidTouchProcessor {
1818

19+
private static final String TAG = "AndroidTouchProcessor";
1920
// Must match the PointerChange enum in pointer.dart.
2021
@IntDef({
2122
PointerChange.CANCEL,
@@ -186,29 +187,31 @@ public boolean onTouchEvent(@NonNull MotionEvent event, @NonNull Matrix transfor
186187
public boolean onGenericMotionEvent(@NonNull MotionEvent event) {
187188
// Method isFromSource is only available in API 18+ (Jelly Bean MR2)
188189
// Mouse hover support is not implemented for API < 18.
190+
189191
boolean isPointerEvent =
190192
Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2
191193
&& event.isFromSource(InputDevice.SOURCE_CLASS_POINTER);
194+
192195
boolean isMovementEvent =
193196
(event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE
194197
|| event.getActionMasked() == MotionEvent.ACTION_SCROLL);
195-
if (!isPointerEvent || !isMovementEvent) {
196-
return false;
197-
}
198-
199-
int pointerChange = getPointerChangeForAction(event.getActionMasked());
200-
ByteBuffer packet =
201-
ByteBuffer.allocateDirect(
202-
event.getPointerCount() * POINTER_DATA_FIELD_COUNT * BYTES_PER_FIELD);
203-
packet.order(ByteOrder.LITTLE_ENDIAN);
204198

205-
// ACTION_HOVER_MOVE always applies to a single pointer only.
206-
addPointerForIndex(event, event.getActionIndex(), pointerChange, 0, IDENTITY_TRANSFORM, packet);
207-
if (packet.position() % (POINTER_DATA_FIELD_COUNT * BYTES_PER_FIELD) != 0) {
208-
throw new AssertionError("Packet position is not on field boundary.");
199+
if (isPointerEvent || isMovementEvent) {
200+
int pointerChange = getPointerChangeForAction(event.getActionMasked());
201+
ByteBuffer packet =
202+
ByteBuffer.allocateDirect(
203+
event.getPointerCount() * POINTER_DATA_FIELD_COUNT * BYTES_PER_FIELD);
204+
packet.order(ByteOrder.LITTLE_ENDIAN);
205+
206+
// ACTION_HOVER_MOVE always applies to a single pointer only.
207+
addPointerForIndex(event, event.getActionIndex(), pointerChange, 0, IDENTITY_TRANSFORM, packet);
208+
if (packet.position() % (POINTER_DATA_FIELD_COUNT * BYTES_PER_FIELD) != 0) {
209+
throw new AssertionError("Packet position is not on field boundary.");
210+
}
211+
renderer.dispatchPointerDataPacket(packet, packet.position());
212+
return true;
209213
}
210-
renderer.dispatchPointerDataPacket(packet, packet.position());
211-
return true;
214+
return false;
212215
}
213216

214217
// TODO(mattcarroll): consider creating a PointerPacket class instead of using a procedure that
@@ -252,6 +255,7 @@ private void addPointerForIndex(
252255
buttons = 0;
253256
}
254257

258+
int panZoomType = -1;
255259
boolean isTrackpadPan = ongoingPans.containsKey(event.getPointerId(pointerIndex));
256260

257261
int signalKind =
@@ -264,8 +268,9 @@ private void addPointerForIndex(
264268
packet.putLong(motionEventId); // motionEventId
265269
packet.putLong(timeStamp); // time_stamp
266270
if (isTrackpadPan) {
267-
packet.putLong(getPointerChangeForPanZoom(pointerChange)); // change
268-
packet.putLong(PointerDeviceKind.TRACKPAD); // kind
271+
panZoomType = getPointerChangeForPanZoom(pointerChange);
272+
packet.putLong(panZoomType); // change
273+
packet.putLong(PointerDeviceKind.TRACKPAD); // kind
269274
} else {
270275
packet.putLong(pointerChange); // change
271276
packet.putLong(pointerKind); // kind
@@ -355,7 +360,7 @@ private void addPointerForIndex(
355360
packet.putDouble(1.0); // scale
356361
packet.putDouble(0.0); // rotation
357362

358-
if (isTrackpadPan && getPointerChangeForPanZoom(pointerChange) == PointerChange.PAN_ZOOM_END) {
363+
if (isTrackpadPan && (panZoomType == PointerChange.PAN_ZOOM_END)) {
359364
ongoingPans.remove(event.getPointerId(pointerIndex));
360365
}
361366
}
@@ -396,12 +401,12 @@ private int getPointerChangeForAction(int maskedAction) {
396401
private int getPointerChangeForPanZoom(int pointerChange) {
397402
if (pointerChange == PointerChange.DOWN) {
398403
return PointerChange.PAN_ZOOM_START;
399-
} else if (pointerChange == PointerChange.MOVE) {
404+
} else if (pointerChange == PointerChange.MOVE || pointerChange == PointerChange.HOVER) {
400405
return PointerChange.PAN_ZOOM_UPDATE;
401406
} else if (pointerChange == PointerChange.UP || pointerChange == PointerChange.CANCEL) {
402407
return PointerChange.PAN_ZOOM_END;
403408
}
404-
throw new AssertionError("Unexpected pointer change");
409+
return -1;
405410
}
406411

407412
@PointerDeviceKind

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

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,4 +218,58 @@ 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.onGenericMotionEvent(mocker.mockEvent(MotionEvent.ACTION_SCROLL, 0.0f, 0.0f, 0));
255+
inOrder
256+
.verify(mockRenderer)
257+
.dispatchPointerDataPacket(packetCaptor.capture(), packetSizeCaptor.capture());
258+
packet = packetCaptor.getValue();
259+
assertEquals(0.0, readPointerPhysicalX(packet));
260+
assertEquals(0.0, readPointerPhysicalY(packet));
261+
262+
touchProcessor.onTouchEvent(mocker.mockEvent(MotionEvent.ACTION_UP, 10.0f, 5.0f, 0));
263+
inOrder
264+
.verify(mockRenderer)
265+
.dispatchPointerDataPacket(packetCaptor.capture(), packetSizeCaptor.capture());
266+
packet = packetCaptor.getValue();
267+
assertEquals(AndroidTouchProcessor.PointerChange.PAN_ZOOM_END, readPointerChange(packet));
268+
assertEquals(AndroidTouchProcessor.PointerDeviceKind.TRACKPAD, readPointerDeviceKind(packet));
269+
assertEquals(AndroidTouchProcessor.PointerSignalKind.NONE, readPointerSignalKind(packet));
270+
assertEquals(0.0, readPointerPhysicalX(packet));
271+
assertEquals(0.0, readPointerPhysicalY(packet));
272+
inOrder.verifyNoMoreInteractions();
273+
}
274+
221275
}

0 commit comments

Comments
 (0)