diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index 7237576790394..3f18d9472ede5 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -17,7 +17,9 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import io.flutter.Log; import io.flutter.embedding.engine.systemchannels.PlatformChannel; +import java.io.FileNotFoundException; import java.util.List; /** Android implementation of the platform plugin. */ @@ -29,6 +31,7 @@ public class PlatformPlugin { private final PlatformChannel platformChannel; private PlatformChannel.SystemChromeStyle currentTheme; private int mEnabledOverlays; + private static final String TAG = "PlatformPlugin"; @VisibleForTesting final PlatformChannel.PlatformMessageHandler mPlatformMessageHandler = @@ -283,11 +286,25 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for if (!clipboard.hasPrimaryClip()) return null; - ClipData clip = clipboard.getPrimaryClip(); - if (clip == null) return null; - - if (format == null || format == PlatformChannel.ClipboardContentFormat.PLAIN_TEXT) { - return clip.getItemAt(0).coerceToText(activity); + try { + ClipData clip = clipboard.getPrimaryClip(); + if (clip == null) return null; + if (format == null || format == PlatformChannel.ClipboardContentFormat.PLAIN_TEXT) { + ClipData.Item item = clip.getItemAt(0); + if (item.getUri() != null) + activity.getContentResolver().openTypedAssetFileDescriptor(item.getUri(), "text/*", null); + return item.coerceToText(activity); + } + } catch (SecurityException e) { + Log.w( + TAG, + "Attempted to get clipboard data that requires additional permission(s).\n" + + "See the exception details for which permission(s) are required, and consider adding them to your Android Manifest as described in:\n" + + "https://developer.android.com/guide/topics/permissions/overview", + e); + return null; + } catch (FileNotFoundException e) { + return null; } return null; 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 495b332678fe9..0d9e0ea88e16c 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java @@ -1,5 +1,6 @@ package io.flutter.plugin.platform; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -10,11 +11,17 @@ import android.app.Activity; import android.content.ClipData; import android.content.ClipboardManager; +import android.content.ContentResolver; import android.content.Context; +import android.media.RingtoneManager; +import android.net.Uri; import android.view.View; import android.view.Window; import io.flutter.embedding.engine.systemchannels.PlatformChannel; import io.flutter.embedding.engine.systemchannels.PlatformChannel.ClipboardContentFormat; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -42,8 +49,9 @@ public void itIgnoresNewHapticEventsOnOldAndroidPlatforms() { platformPlugin.vibrateHapticFeedback(PlatformChannel.HapticFeedbackType.SELECTION_CLICK); } + @Config(sdk = 29) @Test - public void platformPlugin_getClipboardData() { + public void platformPlugin_getClipboardData() throws IOException { ClipboardManager clipboardManager = RuntimeEnvironment.application.getSystemService(ClipboardManager.class); @@ -61,6 +69,23 @@ public void platformPlugin_getClipboardData() { ClipData clip = ClipData.newPlainText("label", "Text"); clipboardManager.setPrimaryClip(clip); assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + + ContentResolver contentResolver = RuntimeEnvironment.application.getContentResolver(); + Uri uri = Uri.parse("content://media/external_primary/images/media/"); + clip = ClipData.newUri(contentResolver, "URI", uri); + clipboardManager.setPrimaryClip(clip); + assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + + uri = + RingtoneManager.getActualDefaultRingtoneUri( + RuntimeEnvironment.application.getApplicationContext(), RingtoneManager.TYPE_RINGTONE); + clip = ClipData.newUri(contentResolver, "URI", uri); + clipboardManager.setPrimaryClip(clip); + String uriData = + platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat).toString(); + InputStream uriInputStream = contentResolver.openInputStream(uri); + InputStream dataInputStream = new ByteArrayInputStream(uriData.getBytes()); + assertEquals(dataInputStream.read(), uriInputStream.read()); } @Test