diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 33c334ee2475a8032adbfb8822dace82c3debbd5..eed010506b3b93a4a15f7b499995bf1e9af4f9ca 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -435,6 +435,7 @@ action("robolectric_tests") { "test/io/flutter/embedding/engine/loader/ApplicationInfoLoaderTest.java", "test/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistryTest.java", "test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java", + "test/io/flutter/embedding/engine/systemchannels/PlatformChannelTest.java", "test/io/flutter/embedding/engine/systemchannels/RestorationChannelTest.java", "test/io/flutter/external/FlutterLaunchTests.java", "test/io/flutter/plugin/common/StandardMessageCodecTest.java", diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java index ddb39fd0e509377ce97be3ce75c3be15566baed4..06833cbe2c225fa205e5645508da8a43db929d7e 100644 --- a/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java @@ -30,7 +30,7 @@ public class PlatformChannel { @Nullable private PlatformMessageHandler platformMessageHandler; @NonNull @VisibleForTesting - protected final MethodChannel.MethodCallHandler parsingMethodCallHandler = + final MethodChannel.MethodCallHandler parsingMethodCallHandler = new MethodChannel.MethodCallHandler() { @Override public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { @@ -155,6 +155,14 @@ public class PlatformChannel { result.success(null); break; } + case "Clipboard.hasStrings": + { + boolean hasStrings = platformMessageHandler.clipboardHasStrings(); + JSONObject response = new JSONObject(); + response.put("value", hasStrings); + result.success(response); + break; + } default: result.notImplemented(); break; @@ -426,6 +434,12 @@ public class PlatformChannel { * {@code text}. */ void setClipboardData(@NonNull String text); + + /** + * The Flutter application would like to know if the clipboard currently contains a string that + * can be pasted. + */ + boolean clipboardHasStrings(); } /** Types of sounds the Android OS can play on behalf of an application. */ diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index 38f2b275fd275efc8e54e79a647a5a4325d4b92a..cc87aa2eaf254d44afb684be4a41bfc0f87f5faa 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -30,7 +30,8 @@ public class PlatformPlugin { private PlatformChannel.SystemChromeStyle currentTheme; private int mEnabledOverlays; - private final PlatformChannel.PlatformMessageHandler mPlatformMessageHandler = + @VisibleForTesting + final PlatformChannel.PlatformMessageHandler mPlatformMessageHandler = new PlatformChannel.PlatformMessageHandler() { @Override public void playSystemSound(@NonNull PlatformChannel.SoundType soundType) { @@ -85,6 +86,14 @@ public class PlatformPlugin { public void setClipboardData(@NonNull String text) { PlatformPlugin.this.setClipboardData(text); } + + @Override + public boolean clipboardHasStrings() { + CharSequence data = + PlatformPlugin.this.getClipboardData( + PlatformChannel.ClipboardContentFormat.PLAIN_TEXT); + return data != null && data.length() > 0; + } }; public PlatformPlugin(Activity activity, PlatformChannel platformChannel) { diff --git a/shell/platform/android/test/io/flutter/FlutterTestSuite.java b/shell/platform/android/test/io/flutter/FlutterTestSuite.java index 075e33c24b53e6b4e0f5348ac455bb15dc11fe3e..3436a14eb601eab70899c211db800008a6f740e1 100644 --- a/shell/platform/android/test/io/flutter/FlutterTestSuite.java +++ b/shell/platform/android/test/io/flutter/FlutterTestSuite.java @@ -17,6 +17,7 @@ import io.flutter.embedding.engine.LocalizationPluginTest; import io.flutter.embedding.engine.RenderingComponentTest; import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistryTest; import io.flutter.embedding.engine.renderer.FlutterRendererTest; +import io.flutter.embedding.engine.systemchannels.PlatformChannelTest; import io.flutter.embedding.engine.systemchannels.RestorationChannelTest; import io.flutter.external.FlutterLaunchTests; import io.flutter.plugin.common.StandardMessageCodecTest; @@ -68,6 +69,7 @@ import test.io.flutter.embedding.engine.dart.DartExecutorTest; TextInputPluginTest.class, MouseCursorPluginTest.class, AccessibilityBridgeTest.class, + PlatformChannelTest.class, RestorationChannelTest.class, }) /** Runs all of the unit tests listed in the {@code @SuiteClasses} annotation. */ diff --git a/shell/platform/android/test/io/flutter/embedding/engine/systemchannels/PlatformChannelTest.java b/shell/platform/android/test/io/flutter/embedding/engine/systemchannels/PlatformChannelTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3a215e3e5507d7b5b173b300f41cb6aeb55d30d8 --- /dev/null +++ b/shell/platform/android/test/io/flutter/embedding/engine/systemchannels/PlatformChannelTest.java @@ -0,0 +1,45 @@ +package io.flutter.embedding.engine.systemchannels; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.res.AssetManager; +import io.flutter.embedding.engine.FlutterJNI; +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Matchers; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +@Config(manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class PlatformChannelTest { + @Test + public void platformChannel_hasStringsMessage() { + MethodChannel rawChannel = mock(MethodChannel.class); + FlutterJNI mockFlutterJNI = mock(FlutterJNI.class); + DartExecutor dartExecutor = new DartExecutor(mockFlutterJNI, mock(AssetManager.class)); + PlatformChannel fakePlatformChannel = new PlatformChannel(dartExecutor); + PlatformChannel.PlatformMessageHandler mockMessageHandler = + mock(PlatformChannel.PlatformMessageHandler.class); + fakePlatformChannel.setPlatformMessageHandler(mockMessageHandler); + Boolean returnValue = true; + when(mockMessageHandler.clipboardHasStrings()).thenReturn(returnValue); + MethodCall methodCall = new MethodCall("Clipboard.hasStrings", null); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + fakePlatformChannel.parsingMethodCallHandler.onMethodCall(methodCall, mockResult); + + JSONObject expected = new JSONObject(); + try { + expected.put("value", returnValue); + } catch (JSONException e) { + } + verify(mockResult).success(Matchers.refEq(expected)); + } +} diff --git a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java index 848b19161a3f99ae8c47bf5ded9edc84f77c80a6..355ec4e9ea5eee29157c294688001a3432e73905 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java @@ -1,15 +1,20 @@ package io.flutter.plugin.platform; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.app.Activity; +import android.content.ClipboardManager; +import android.content.Context; import android.view.View; import android.view.Window; import io.flutter.embedding.engine.systemchannels.PlatformChannel; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @Config(manifest = Config.NONE) @@ -32,4 +37,25 @@ public class PlatformPluginTest { // SELECTION_CLICK haptic response is only available on "LOLLIPOP" (21) and later. platformPlugin.vibrateHapticFeedback(PlatformChannel.HapticFeedbackType.SELECTION_CLICK); } + + @Test + public void platformPlugin_hasStrings() { + ClipboardManager clipboardManager = + RuntimeEnvironment.application.getSystemService(ClipboardManager.class); + + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + Activity fakeActivity = mock(Activity.class); + when(fakeActivity.getWindow()).thenReturn(fakeWindow); + when(fakeActivity.getSystemService(Context.CLIPBOARD_SERVICE)).thenReturn(clipboardManager); + PlatformChannel fakePlatformChannel = mock(PlatformChannel.class); + PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); + + clipboardManager.setText("iamastring"); + assertTrue(platformPlugin.mPlatformMessageHandler.clipboardHasStrings()); + + clipboardManager.setText(""); + assertFalse(platformPlugin.mPlatformMessageHandler.clipboardHasStrings()); + } }