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

hasStrings on Android #20393

Merged
merged 8 commits into from
Aug 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@ action("robolectric_tests") {
"test/io/flutter/embedding/engine/dart/DartExecutorTest.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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -155,6 +155,14 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result
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;
Expand Down Expand Up @@ -426,6 +434,12 @@ public interface PlatformMessageHandler {
* {@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. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -85,6 +86,14 @@ public CharSequence getClipboardData(
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) {
Expand Down
2 changes: 2 additions & 0 deletions shell/platform/android/test/io/flutter/FlutterTestSuite.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
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;
Expand Down Expand Up @@ -68,6 +69,7 @@
TextInputPluginTest.class,
MouseCursorPluginTest.class,
AccessibilityBridgeTest.class,
PlatformChannelTest.class,
RestorationChannelTest.class,
})
/** Runs all of the unit tests listed in the {@code @SuiteClasses} annotation. */
Expand Down
Original file line number Diff line number Diff line change
@@ -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));
}
}
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -32,4 +37,25 @@ public void itIgnoresNewHapticEventsOnOldAndroidPlatforms() {
// 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());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gaaclarke Thanks for all the help with the tests. The first test above works, but this one is failing because the engine is unable to use the clipboard internally.

I thought that my use of ShadowClipboardManager would solve this problem, but maybe I'm doing this wrong? I got this from InputConnectionAdaptorTest.

The engine is unable to get a ClipboardManager here (it's null):

ClipboardManager clipboard =
(ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = clipboard.getPrimaryClip();

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's weird, you can try moving the ClipboardManager to the top of the test? I'm not sure if there is a temporal problem here. I don't see a difference between what you are doing and the other test. Worse case scenario you can make your own interface on top of the ClipboardManager that the PlatformPlugin uses. It would be nice if we could get this working though. Sorry, gotta run to a meeting.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I think I'm on to something with that. I'll post back later with results.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it's working, thank you! Let me know if it looks alright. I did have to make one protected method public at some point.


clipboardManager.setText("");
assertFalse(platformPlugin.mPlatformMessageHandler.clipboardHasStrings());
}
}