diff --git a/DEPS b/DEPS index ddc9f7be2fa0b..e10de2a338be4 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '1d923a386d7a9f384435a2b4c1287b2937504566', + 'dart_revision': 'ecf9ce8ef42de11033801b2870e8b310c3722902', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 7324f83075dc8..8f3f3360d00a4 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: fcda8704ef65a785787b4045cbe4f432 +Signature: 35f962fd83423ee7de3761729e0c25c4 UNUSED LICENSES: diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index cc87aa2eaf254..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 = @@ -280,11 +283,28 @@ private void popSystemNavigator() { private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat format) { ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = clipboard.getPrimaryClip(); - if (clip == null) return null; - if (format == null || format == PlatformChannel.ClipboardContentFormat.PLAIN_TEXT) { - return clip.getItemAt(0).coerceToText(activity); + if (!clipboard.hasPrimaryClip()) return null; + + 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 355ec4e9ea5ee..8c869838c34b4 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java @@ -1,16 +1,27 @@ 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; 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.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; @@ -38,6 +49,45 @@ public void itIgnoresNewHapticEventsOnOldAndroidPlatforms() { platformPlugin.vibrateHapticFeedback(PlatformChannel.HapticFeedbackType.SELECTION_CLICK); } + @Config(sdk = 29) + @Test + public void platformPlugin_getClipboardData() throws IOException { + 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); + + ClipboardContentFormat clipboardFormat = ClipboardContentFormat.PLAIN_TEXT; + assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + 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 public void platformPlugin_hasStrings() { ClipboardManager clipboardManager =