From 0347c533f177812c67fdb3efcf05dca4d45b0229 Mon Sep 17 00:00:00 2001 From: xubaolin Date: Fri, 11 Jun 2021 08:39:03 +0800 Subject: [PATCH] fix a PlatformView gesture bug (#84257) --- .../lib/src/services/platform_views.dart | 20 ++++--- .../test/rendering/platform_view_test.dart | 55 +++++++++++++++++++ 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/packages/flutter/lib/src/services/platform_views.dart b/packages/flutter/lib/src/services/platform_views.dart index 65bdaa515c..bcd33631e8 100644 --- a/packages/flutter/lib/src/services/platform_views.dart +++ b/packages/flutter/lib/src/services/platform_views.dart @@ -540,20 +540,24 @@ class _AndroidMotionEventConverter { ); } - void handlePointerUpEvent(PointerUpEvent event) { - pointerPositions.remove(event.pointer); - usedAndroidPointerIds.remove(pointerProperties[event.pointer]!.id); - pointerProperties.remove(event.pointer); + void _remove(int pointer) { + pointerPositions.remove(pointer); + usedAndroidPointerIds.remove(pointerProperties[pointer]!.id); + pointerProperties.remove(pointer); if (pointerProperties.isEmpty) { downTimeMillis = null; } } + void handlePointerUpEvent(PointerUpEvent event) { + _remove(event.pointer); + } + void handlePointerCancelEvent(PointerCancelEvent event) { - pointerPositions.clear(); - pointerProperties.clear(); - usedAndroidPointerIds.clear(); - downTimeMillis = null; + // The pointer cancel event is handled like pointer up. Normally, + // the difference is that pointer cancel doesn't perform any action, + // but in this case neither up or cancel perform any action. + _remove(event.pointer); } AndroidMotionEvent? toAndroidMotionEvent(PointerEvent event) { diff --git a/packages/flutter/test/rendering/platform_view_test.dart b/packages/flutter/test/rendering/platform_view_test.dart index 93d01325c0..ef2a2db71e 100644 --- a/packages/flutter/test/rendering/platform_view_test.dart +++ b/packages/flutter/test/rendering/platform_view_test.dart @@ -4,9 +4,11 @@ import 'dart:ui' as ui; +import 'package:fake_async/fake_async.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../services/fake_platform_views.dart'; import 'rendering_tester.dart'; @@ -98,6 +100,56 @@ void main() { }); }, skip: isBrowser); // TODO(yjbanov): fails on Web with obscured stack trace: https://github.com/flutter/flutter/issues/42770 + + // Regression test for https://github.com/flutter/flutter/issues/69431 + test('multi-finger touch test', () { + renderer; // Initialize bindings. + final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController(); + viewsController.registerViewType('webview'); + final AndroidViewController viewController = + PlatformViewsService.initAndroidView(id: 0, viewType: 'webview', layoutDirection: TextDirection.rtl); + final PlatformViewRenderBox platformViewRenderBox = PlatformViewRenderBox( + controller: viewController, + hitTestBehavior: PlatformViewHitTestBehavior.opaque, + gestureRecognizers: >{ + Factory( + () => VerticalDragGestureRecognizer(), + ), + }, + ); + layout(platformViewRenderBox); + pumpFrame(phase: EnginePhase.flushSemantics); + + viewController.pointTransformer = (Offset offset) => platformViewRenderBox.globalToLocal(offset); + + FakeAsync().run((FakeAsync async) { + // Put one pointer down. + ui.window.onPointerDataPacket!(ui.PointerDataPacket(data: [ + _pointerData(ui.PointerChange.add, Offset.zero, pointer: 1, kind: PointerDeviceKind.touch), + _pointerData(ui.PointerChange.down, const Offset(10, 10), pointer: 1, kind: PointerDeviceKind.touch), + _pointerData(ui.PointerChange.remove, const Offset(10, 10), pointer: 1, kind: PointerDeviceKind.touch), + ])); + async.flushMicrotasks(); + + // Put another pointer down and then cancel it. + ui.window.onPointerDataPacket!(ui.PointerDataPacket(data: [ + _pointerData(ui.PointerChange.add, Offset.zero, pointer: 2, kind: PointerDeviceKind.touch), + _pointerData(ui.PointerChange.down, const Offset(20, 10), pointer: 2, kind: PointerDeviceKind.touch), + _pointerData(ui.PointerChange.cancel, const Offset(20, 10), pointer: 2, kind: PointerDeviceKind.touch), + ])); + async.flushMicrotasks(); + + // The first pointer can still moving without crashing. + ui.window.onPointerDataPacket!(ui.PointerDataPacket(data: [ + _pointerData(ui.PointerChange.add, Offset.zero, pointer: 1, kind: PointerDeviceKind.touch), + _pointerData(ui.PointerChange.move, const Offset(10, 10), pointer: 1, kind: PointerDeviceKind.touch), + _pointerData(ui.PointerChange.remove, const Offset(10, 10), pointer: 1, kind: PointerDeviceKind.touch), + ])); + async.flushMicrotasks(); + }); + + // Passes if no crashes. + }); } ui.PointerData _pointerData( @@ -105,8 +157,11 @@ ui.PointerData _pointerData( Offset logicalPosition, { int device = 0, PointerDeviceKind kind = PointerDeviceKind.mouse, + int pointer = 0, }) { return ui.PointerData( + pointerIdentifier: pointer, + embedderId: pointer, change: change, physicalX: logicalPosition.dx * ui.window.devicePixelRatio, physicalY: logicalPosition.dy * ui.window.devicePixelRatio,