From dbca16555e9680eb65a863ded60c861397a4c951 Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Fri, 25 Sep 2020 17:46:34 -0700 Subject: [PATCH] Support dragging native platform views (flutter/engine#21396) --- .../flutter/shell/platform/android/BUILD.gn | 1 + .../mutatorsstack/FlutterMutatorView.java | 25 +++++- .../test/io/flutter/FlutterTestSuite.java | 2 + .../mutatorsstack/FlutterMutatorViewTest.java | 79 +++++++++++++++++++ 4 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorViewTest.java diff --git a/engine/src/flutter/shell/platform/android/BUILD.gn b/engine/src/flutter/shell/platform/android/BUILD.gn index 52373e3c74f..f5714f1fd0f 100644 --- a/engine/src/flutter/shell/platform/android/BUILD.gn +++ b/engine/src/flutter/shell/platform/android/BUILD.gn @@ -439,6 +439,7 @@ action("robolectric_tests") { "test/io/flutter/embedding/engine/dart/DartExecutorTest.java", "test/io/flutter/embedding/engine/loader/ApplicationInfoLoaderTest.java", "test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java", + "test/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorViewTest.java", "test/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistryTest.java", "test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java", "test/io/flutter/embedding/engine/systemchannels/KeyEventChannelTest.java", diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorView.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorView.java index d910ab82f2e..25089bbc4d7 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorView.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorView.java @@ -19,6 +19,8 @@ public class FlutterMutatorView extends FrameLayout { private float screenDensity; private int left; private int top; + private int prevLeft; + private int prevTop; private final AndroidTouchProcessor androidTouchProcessor; @@ -122,11 +124,26 @@ public class FlutterMutatorView extends FrameLayout { return super.onTouchEvent(event); } - // Mutator view itself doesn't rotate, scale, skew, etc. - // we only need to account for translation. - Matrix screenMatrix = new Matrix(); - screenMatrix.postTranslate(left, top); + final Matrix screenMatrix = new Matrix(); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + prevLeft = left; + prevTop = top; + screenMatrix.postTranslate(left, top); + break; + case MotionEvent.ACTION_MOVE: + // While the view is dragged, use the left and top positions as + // they were at the moment the touch event fired. + screenMatrix.postTranslate(prevLeft, prevTop); + prevLeft = left; + prevTop = top; + break; + case MotionEvent.ACTION_UP: + default: + screenMatrix.postTranslate(left, top); + break; + } return androidTouchProcessor.onTouchEvent(event, screenMatrix); } } diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/FlutterTestSuite.java b/engine/src/flutter/shell/platform/android/test/io/flutter/FlutterTestSuite.java index bdc130ef87e..c96f8d5dd57 100644 --- a/engine/src/flutter/shell/platform/android/test/io/flutter/FlutterTestSuite.java +++ b/engine/src/flutter/shell/platform/android/test/io/flutter/FlutterTestSuite.java @@ -18,6 +18,7 @@ import io.flutter.embedding.engine.LocalizationPluginTest; import io.flutter.embedding.engine.RenderingComponentTest; import io.flutter.embedding.engine.loader.ApplicationInfoLoaderTest; import io.flutter.embedding.engine.loader.FlutterLoaderTest; +import io.flutter.embedding.engine.mutatorsstack.FlutterMutatorViewTest; import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistryTest; import io.flutter.embedding.engine.renderer.FlutterRendererTest; import io.flutter.embedding.engine.systemchannels.KeyEventChannelTest; @@ -60,6 +61,7 @@ import test.io.flutter.embedding.engine.dart.DartExecutorTest; FlutterJNITest.class, FlutterLaunchTests.class, FlutterLoaderTest.class, + FlutterMutatorViewTest.class, FlutterShellArgsTest.class, FlutterRendererTest.class, FlutterShellArgsTest.class, diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorViewTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorViewTest.java new file mode 100644 index 00000000000..35f412058be --- /dev/null +++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorViewTest.java @@ -0,0 +1,79 @@ +package io.flutter.embedding.engine.mutatorsstack; + +import static junit.framework.TestCase.*; +import static org.mockito.Mockito.*; + +import android.graphics.Matrix; +import android.view.MotionEvent; +import io.flutter.embedding.android.AndroidTouchProcessor; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@Config(manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class FlutterMutatorViewTest { + + @Test + public void canDragViews() { + final AndroidTouchProcessor touchProcessor = mock(AndroidTouchProcessor.class); + final FlutterMutatorView view = + new FlutterMutatorView(RuntimeEnvironment.systemContext, 1.0f, touchProcessor); + final FlutterMutatorsStack mutatorStack = mock(FlutterMutatorsStack.class); + + assertTrue(view.onInterceptTouchEvent(mock(MotionEvent.class))); + + { + view.readyToDisplay(mutatorStack, /*left=*/ 1, /*top=*/ 2, /*width=*/ 0, /*height=*/ 0); + view.onTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0.0f, 0.0f, 0)); + final ArgumentCaptor matrixCaptor = ArgumentCaptor.forClass(Matrix.class); + verify(touchProcessor).onTouchEvent(any(), matrixCaptor.capture()); + + final Matrix screenMatrix = new Matrix(); + screenMatrix.postTranslate(1, 2); + assertTrue(matrixCaptor.getValue().equals(screenMatrix)); + } + + reset(touchProcessor); + + { + view.readyToDisplay(mutatorStack, /*left=*/ 3, /*top=*/ 4, /*width=*/ 0, /*height=*/ 0); + view.onTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0.0f, 0.0f, 0)); + final ArgumentCaptor matrixCaptor = ArgumentCaptor.forClass(Matrix.class); + verify(touchProcessor).onTouchEvent(any(), matrixCaptor.capture()); + + final Matrix screenMatrix = new Matrix(); + screenMatrix.postTranslate(1, 2); + assertTrue(matrixCaptor.getValue().equals(screenMatrix)); + } + + reset(touchProcessor); + + { + view.readyToDisplay(mutatorStack, /*left=*/ 5, /*top=*/ 6, /*width=*/ 0, /*height=*/ 0); + view.onTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0.0f, 0.0f, 0)); + final ArgumentCaptor matrixCaptor = ArgumentCaptor.forClass(Matrix.class); + verify(touchProcessor).onTouchEvent(any(), matrixCaptor.capture()); + + final Matrix screenMatrix = new Matrix(); + screenMatrix.postTranslate(3, 4); + assertTrue(matrixCaptor.getValue().equals(screenMatrix)); + } + + reset(touchProcessor); + + { + view.readyToDisplay(mutatorStack, /*left=*/ 7, /*top=*/ 8, /*width=*/ 0, /*height=*/ 0); + view.onTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0.0f, 0.0f, 0)); + final ArgumentCaptor matrixCaptor = ArgumentCaptor.forClass(Matrix.class); + verify(touchProcessor).onTouchEvent(any(), matrixCaptor.capture()); + + final Matrix screenMatrix = new Matrix(); + screenMatrix.postTranslate(7, 8); + assertTrue(matrixCaptor.getValue().equals(screenMatrix)); + } + } +}