From edb08a5b72987550915ab3c2bc2201179295c1e2 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Thu, 16 Nov 2023 16:43:54 -0800 Subject: [PATCH 01/12] Add fix --- .../io/flutter/plugin/platform/PlatformPlugin.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index 41ffa23853c7c..b25e926cad68f 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -517,8 +517,18 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for 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); + // First, try getting clipboard as text. + CharSequence itemText = item.getText(); + if (itemText == null) { + // Clipboard does not contain text, so check whether or not we will be + // able to retrieve text from URI. FileNotFoundException will be thrown + // if not, and then null will be returned. + if (item.getUri() != null) { + activity.getContentResolver().openTypedAssetFileDescriptor(item.getUri(), "*/*", null); + } + } + // Safely return clipbaord item into text by returning itemText or text retrieved + // from its URI. return item.coerceToText(activity); } } catch (SecurityException e) { From 99bdb12677707e1a1544f10cda0d3c7667903aad Mon Sep 17 00:00:00 2001 From: camsim99 Date: Fri, 17 Nov 2023 16:52:34 -0800 Subject: [PATCH 02/12] Re-add text check --- .../android/io/flutter/plugin/platform/PlatformPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index b25e926cad68f..4c2fd7cbbe7f7 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -524,7 +524,7 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for // able to retrieve text from URI. FileNotFoundException will be thrown // if not, and then null will be returned. if (item.getUri() != null) { - activity.getContentResolver().openTypedAssetFileDescriptor(item.getUri(), "*/*", null); + activity.getContentResolver().openTypedAssetFileDescriptor(item.getUri(), "text/*", null); } } // Safely return clipbaord item into text by returning itemText or text retrieved From 29bfca2194f9b90c02f901e96b2e2706ab2372ba Mon Sep 17 00:00:00 2001 From: camsim99 Date: Fri, 17 Nov 2023 16:53:34 -0800 Subject: [PATCH 03/12] Formatting --- .../android/io/flutter/plugin/platform/PlatformPlugin.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index 4c2fd7cbbe7f7..c4e278e60d7a5 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -524,7 +524,9 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for // able to retrieve text from URI. FileNotFoundException will be thrown // if not, and then null will be returned. if (item.getUri() != null) { - activity.getContentResolver().openTypedAssetFileDescriptor(item.getUri(), "text/*", null); + activity + .getContentResolver() + .openTypedAssetFileDescriptor(item.getUri(), "text/*", null); } } // Safely return clipbaord item into text by returning itemText or text retrieved From b2a514a9967d0a1ff9d6b709e9b25fd18bbf2083 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 20 Nov 2023 10:53:26 -0800 Subject: [PATCH 04/12] Add branching to clipboard logic --- .../plugin/platform/PlatformPlugin.java | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index c4e278e60d7a5..06fdbd8936cf1 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -11,6 +11,7 @@ import android.content.ClipDescription; import android.content.ClipboardManager; import android.content.Context; +import android.net.Uri; import android.os.Build; import android.view.HapticFeedbackConstants; import android.view.SoundEffectConstants; @@ -521,12 +522,22 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for CharSequence itemText = item.getText(); if (itemText == null) { // Clipboard does not contain text, so check whether or not we will be - // able to retrieve text from URI. FileNotFoundException will be thrown - // if not, and then null will be returned. - if (item.getUri() != null) { - activity - .getContentResolver() - .openTypedAssetFileDescriptor(item.getUri(), "text/*", null); + // able to retrieve text from URI. + Uri itemUri = item.getUri(); + if (itemUri != null) { + if (itemUri.getScheme() == "content://") { + // Ensure text can be received from content URI. FileNotFoundException + // will be thrown if not, in which case we return null. + activity + .getContentResolver() + .openTypedAssetFileDescriptor(item.getUri(), "text/*", null); + } else { + Log.w(TAG, "Clipboard item contains a Uri with a scheme that is unhandled."); + return null; + } + } else { + Log.w(TAG, "Clipboard item contained no textual content nor a URI to retrieve it from."); + return null; } } // Safely return clipbaord item into text by returning itemText or text retrieved @@ -542,6 +553,7 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for e); return null; } catch (FileNotFoundException e) { + Log.w(TAG, "Clipboard text was unable to be received from content URI."); return null; } From f26c5b3c1642e75c282e8350c20d3e46891e9e7e Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 20 Nov 2023 11:18:25 -0800 Subject: [PATCH 05/12] Working on tests --- .../plugin/platform/PlatformPlugin.java | 9 +- .../plugin/platform/PlatformPluginTest.java | 86 ++++++++++++++++++- 2 files changed, 89 insertions(+), 6 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index 06fdbd8936cf1..f7e7a0d909d7a 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -525,14 +525,15 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for // able to retrieve text from URI. Uri itemUri = item.getUri(); if (itemUri != null) { - if (itemUri.getScheme() == "content://") { + String uriScheme = itemUri.getScheme(); + if (uriScheme == "content://") { // Ensure text can be received from content URI. FileNotFoundException // will be thrown if not, in which case we return null. activity .getContentResolver() .openTypedAssetFileDescriptor(item.getUri(), "text/*", null); } else { - Log.w(TAG, "Clipboard item contains a Uri with a scheme that is unhandled."); + Log.w(TAG, "Clipboard item contains a Uri with scheme '"+ uriScheme + "'that is unhandled."); return null; } } else { @@ -540,8 +541,8 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for return null; } } - // Safely return clipbaord item into text by returning itemText or text retrieved - // from its URI. + // Safely return clipboard data coerced into text; will return either + // itemText or text retrieved from its URI. return item.coerceToText(activity); } } catch (SecurityException e) { 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 23b7fbf5b6a98..7a30d5b68585e 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java @@ -70,9 +70,9 @@ public void itIgnoresNewHapticEventsOnOldAndroidPlatforms() { platformPlugin.vibrateHapticFeedback(PlatformChannel.HapticFeedbackType.SELECTION_CLICK); } - @Config(sdk = Build.VERSION_CODES.Q) + // @Config(sdk = Build.VERSION_CODES.Q) @Test - public void platformPlugin_getClipboardData() throws IOException { + public void platformPlugin_getClipboardDataIsNonNullWhenPlainTextCopied() throws IOException { ClipboardManager clipboardManager = ctx.getSystemService(ClipboardManager.class); View fakeDecorView = mock(View.class); @@ -89,6 +89,42 @@ public void platformPlugin_getClipboardData() throws IOException { ClipData clip = ClipData.newPlainText("label", "Text"); clipboardManager.setPrimaryClip(clip); assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + } + + // @Config(sdk = Build.VERSION_CODES.Q) + @Test + public void platformPlugin_getClipboardDataIsNonNullWhenContentUriWithTextProvided() throws IOException { + // ClipboardManager clipboardManager = ctx.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)); + } + + // @Config(sdk = Build.VERSION_CODES.Q) + @Test + public void platformPlugin_getClipboardDataIsNullWhenContentUriProvidedContainsNoText() throws IOException { + ClipboardManager clipboardManager = ctx.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); ContentResolver contentResolver = ctx.getContentResolver(); when(fakeActivity.getContentResolver()).thenReturn(contentResolver); @@ -98,6 +134,52 @@ public void platformPlugin_getClipboardData() throws IOException { assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } + // @Config(sdk = Build.VERSION_CODES.Q) + @Test + public void platformPlugin_getClipboardDataIsNullWhenNonContentUriProvided() throws IOException { + // ClipboardManager clipboardManager = ctx.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); + + // ContentResolver contentResolver = ctx.getContentResolver(); + // when(fakeActivity.getContentResolver()).thenReturn(contentResolver); + // Uri uri = Uri.parse("content://media/external_primary/images/media/"); + // clip = ClipData.newUri(contentResolver, "URI", uri); + // clipboardManager.setPrimaryClip(clip); + // assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + } + + // @Config(sdk = Build.VERSION_CODES.Q) + @Test + public void platformPlugin_getClipboardDataIsNullWhenItemHasNoTextNorUri() throws IOException { + ClipboardManager clipboardManager = ctx.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); + + ContentResolver contentResolver = ctx.getContentResolver(); + when(fakeActivity.getContentResolver()).thenReturn(contentResolver); + ClipData.Item mockItem = mock(ClipData.Item.class); + when(mockItem.getText()).thenReturn(null); + when(mockItem.getUri()).thenReturn(null); + clip = new ClipData("label", new String[0], mockItem); + clipboardManager.setPrimaryClip(clip); + assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + } + @SuppressWarnings("deprecation") // ClipboardManager.getText @Config(sdk = Build.VERSION_CODES.P) From 3f84b32c066f82da3067280890099f8fd3446140 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 20 Nov 2023 17:44:36 -0800 Subject: [PATCH 06/12] modify code, add tests --- .../plugin/platform/PlatformPlugin.java | 2 +- .../plugin/platform/PlatformPluginTest.java | 92 ++++++++++--------- 2 files changed, 51 insertions(+), 43 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index f7e7a0d909d7a..dc0df2f370da4 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -526,7 +526,7 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for Uri itemUri = item.getUri(); if (itemUri != null) { String uriScheme = itemUri.getScheme(); - if (uriScheme == "content://") { + if (uriScheme.equals("content")) { // Ensure text can be received from content URI. FileNotFoundException // will be thrown if not, in which case we return null. activity 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 7a30d5b68585e..5d4ad28d60596 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java @@ -10,6 +10,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -23,6 +24,7 @@ import android.content.ClipboardManager; import android.content.ContentResolver; import android.content.Context; +import android.content.res.AssetFileDescriptor; import android.net.Uri; import android.os.Build; import android.view.View; @@ -70,7 +72,6 @@ public void itIgnoresNewHapticEventsOnOldAndroidPlatforms() { platformPlugin.vibrateHapticFeedback(PlatformChannel.HapticFeedbackType.SELECTION_CLICK); } - // @Config(sdk = Build.VERSION_CODES.Q) @Test public void platformPlugin_getClipboardDataIsNonNullWhenPlainTextCopied() throws IOException { ClipboardManager clipboardManager = ctx.getSystemService(ClipboardManager.class); @@ -91,28 +92,36 @@ public void platformPlugin_getClipboardDataIsNonNullWhenPlainTextCopied() throws assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } - // @Config(sdk = Build.VERSION_CODES.Q) @Test public void platformPlugin_getClipboardDataIsNonNullWhenContentUriWithTextProvided() throws IOException { - // ClipboardManager clipboardManager = ctx.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)); + ClipboardManager clipboardManager = ctx.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); + + ContentResolver contentResolver = mock(ContentResolver.class); + when(fakeActivity.getContentResolver()).thenReturn(contentResolver); + ClipboardContentFormat clipboardFormat = ClipboardContentFormat.PLAIN_TEXT; + ClipData.Item mockItem = mock(ClipData.Item.class); + Uri mockUri = mock(Uri.class); + + when(mockUri.getScheme()).thenReturn("content"); + when(mockItem.getText()).thenReturn(null); + when(mockItem.getUri()).thenReturn(mockUri); + when(contentResolver.openTypedAssetFileDescriptor(any(Uri.class), any(), any())).thenReturn(mock(AssetFileDescriptor.class)); + when(mockItem.coerceToText(fakeActivity)).thenReturn("something non-null"); + + ClipData clip = new ClipData("label", new String[0], mockItem); + clipboardManager.setPrimaryClip(clip); + assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } - // @Config(sdk = Build.VERSION_CODES.Q) @Test public void platformPlugin_getClipboardDataIsNullWhenContentUriProvidedContainsNoText() throws IOException { ClipboardManager clipboardManager = ctx.getSystemService(ClipboardManager.class); @@ -127,36 +136,36 @@ public void platformPlugin_getClipboardDataIsNullWhenContentUriProvidedContainsN PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); ContentResolver contentResolver = ctx.getContentResolver(); + ClipboardContentFormat clipboardFormat = ClipboardContentFormat.PLAIN_TEXT; when(fakeActivity.getContentResolver()).thenReturn(contentResolver); Uri uri = Uri.parse("content://media/external_primary/images/media/"); - clip = ClipData.newUri(contentResolver, "URI", uri); + ClipData clip = ClipData.newUri(contentResolver, "URI", uri); clipboardManager.setPrimaryClip(clip); assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } - // @Config(sdk = Build.VERSION_CODES.Q) @Test public void platformPlugin_getClipboardDataIsNullWhenNonContentUriProvided() throws IOException { - // ClipboardManager clipboardManager = ctx.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); - - // ContentResolver contentResolver = ctx.getContentResolver(); - // when(fakeActivity.getContentResolver()).thenReturn(contentResolver); - // Uri uri = Uri.parse("content://media/external_primary/images/media/"); - // clip = ClipData.newUri(contentResolver, "URI", uri); - // clipboardManager.setPrimaryClip(clip); - // assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + ClipboardManager clipboardManager = ctx.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); + + ContentResolver contentResolver = ctx.getContentResolver(); + ClipboardContentFormat clipboardFormat = ClipboardContentFormat.PLAIN_TEXT; + when(fakeActivity.getContentResolver()).thenReturn(contentResolver); + Uri uri = Uri.parse("file:///"); + ClipData clip = ClipData.newUri(contentResolver, "URI", uri); + clipboardManager.setPrimaryClip(clip); + assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } - // @Config(sdk = Build.VERSION_CODES.Q) @Test public void platformPlugin_getClipboardDataIsNullWhenItemHasNoTextNorUri() throws IOException { ClipboardManager clipboardManager = ctx.getSystemService(ClipboardManager.class); @@ -170,12 +179,11 @@ public void platformPlugin_getClipboardDataIsNullWhenItemHasNoTextNorUri() throw PlatformChannel fakePlatformChannel = mock(PlatformChannel.class); PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); - ContentResolver contentResolver = ctx.getContentResolver(); - when(fakeActivity.getContentResolver()).thenReturn(contentResolver); + ClipboardContentFormat clipboardFormat = ClipboardContentFormat.PLAIN_TEXT; ClipData.Item mockItem = mock(ClipData.Item.class); when(mockItem.getText()).thenReturn(null); when(mockItem.getUri()).thenReturn(null); - clip = new ClipData("label", new String[0], mockItem); + ClipData clip = new ClipData("label", new String[0], mockItem); clipboardManager.setPrimaryClip(clip); assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } From 2df7f8c3c3dda3208cb54a20a8fb981336bdfbd8 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 20 Nov 2023 17:45:24 -0800 Subject: [PATCH 07/12] Formatting --- .../io/flutter/plugin/platform/PlatformPlugin.java | 9 +++++++-- .../io/flutter/plugin/platform/PlatformPluginTest.java | 9 ++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index dc0df2f370da4..1f2d8b0bec0bb 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -533,11 +533,16 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for .getContentResolver() .openTypedAssetFileDescriptor(item.getUri(), "text/*", null); } else { - Log.w(TAG, "Clipboard item contains a Uri with scheme '"+ uriScheme + "'that is unhandled."); + Log.w( + TAG, + "Clipboard item contains a Uri with scheme '" + + uriScheme + + "'that is unhandled."); return null; } } else { - Log.w(TAG, "Clipboard item contained no textual content nor a URI to retrieve it from."); + Log.w( + TAG, "Clipboard item contained no textual content nor a URI to retrieve it from."); 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 5d4ad28d60596..af428019718f4 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java @@ -93,7 +93,8 @@ public void platformPlugin_getClipboardDataIsNonNullWhenPlainTextCopied() throws } @Test - public void platformPlugin_getClipboardDataIsNonNullWhenContentUriWithTextProvided() throws IOException { + public void platformPlugin_getClipboardDataIsNonNullWhenContentUriWithTextProvided() + throws IOException { ClipboardManager clipboardManager = ctx.getSystemService(ClipboardManager.class); View fakeDecorView = mock(View.class); @@ -114,7 +115,8 @@ public void platformPlugin_getClipboardDataIsNonNullWhenContentUriWithTextProvid when(mockUri.getScheme()).thenReturn("content"); when(mockItem.getText()).thenReturn(null); when(mockItem.getUri()).thenReturn(mockUri); - when(contentResolver.openTypedAssetFileDescriptor(any(Uri.class), any(), any())).thenReturn(mock(AssetFileDescriptor.class)); + when(contentResolver.openTypedAssetFileDescriptor(any(Uri.class), any(), any())) + .thenReturn(mock(AssetFileDescriptor.class)); when(mockItem.coerceToText(fakeActivity)).thenReturn("something non-null"); ClipData clip = new ClipData("label", new String[0], mockItem); @@ -123,7 +125,8 @@ public void platformPlugin_getClipboardDataIsNonNullWhenContentUriWithTextProvid } @Test - public void platformPlugin_getClipboardDataIsNullWhenContentUriProvidedContainsNoText() throws IOException { + public void platformPlugin_getClipboardDataIsNullWhenContentUriProvidedContainsNoText() + throws IOException { ClipboardManager clipboardManager = ctx.getSystemService(ClipboardManager.class); View fakeDecorView = mock(View.class); From a339df3c46d34e6efd0f687adc4e3cd5e2474bab Mon Sep 17 00:00:00 2001 From: camsim99 Date: Mon, 20 Nov 2023 18:12:56 -0800 Subject: [PATCH 08/12] Reduce branching, reorganize tests --- .../plugin/platform/PlatformPlugin.java | 43 ++++++++++--------- .../plugin/platform/PlatformPluginTest.java | 16 ++++++- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index 1f2d8b0bec0bb..69f12a655c08a 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -518,34 +518,37 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for if (clip == null) return null; if (format == null || format == PlatformChannel.ClipboardContentFormat.PLAIN_TEXT) { ClipData.Item item = clip.getItemAt(0); - // First, try getting clipboard as text. + // First, try getting clipboard data as text; no further processing + // required if so. CharSequence itemText = item.getText(); + if (itemText == null) { - // Clipboard does not contain text, so check whether or not we will be - // able to retrieve text from URI. + // Clipboard data does not contain text, so check whether or not it + // contains a URI to extract text from. Uri itemUri = item.getUri(); - if (itemUri != null) { - String uriScheme = itemUri.getScheme(); - if (uriScheme.equals("content")) { - // Ensure text can be received from content URI. FileNotFoundException - // will be thrown if not, in which case we return null. - activity - .getContentResolver() - .openTypedAssetFileDescriptor(item.getUri(), "text/*", null); - } else { - Log.w( - TAG, - "Clipboard item contains a Uri with scheme '" - + uriScheme - + "'that is unhandled."); - return null; - } - } else { + + if (itemUri == null) { Log.w( TAG, "Clipboard item contained no textual content nor a URI to retrieve it from."); return null; } + + // Clipboard data contains a URI to potentially check extract text + // from, but first ensure its scheme is content in order to do so. + String uriScheme = itemUri.getScheme(); + + if (!uriScheme.equals("content")) { + Log.w( + TAG, + "Clipboard item contains a Uri with scheme '" + uriScheme + "'that is unhandled."); + return null; + } + + // Try extracting text from content URI; FileNotFoundException will be + // thrown if text cannot be extracted from the URI. + activity.getContentResolver().openTypedAssetFileDescriptor(itemUri, "text/*", null); } + // Safely return clipboard data coerced into text; will return either // itemText or text retrieved from its URI. return item.coerceToText(activity); 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 af428019718f4..9e8d78922fe27 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java @@ -86,8 +86,9 @@ public void platformPlugin_getClipboardDataIsNonNullWhenPlainTextCopied() throws PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); ClipboardContentFormat clipboardFormat = ClipboardContentFormat.PLAIN_TEXT; - assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); ClipData clip = ClipData.newPlainText("label", "Text"); + + assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); clipboardManager.setPrimaryClip(clip); assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } @@ -107,11 +108,11 @@ public void platformPlugin_getClipboardDataIsNonNullWhenContentUriWithTextProvid PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); ContentResolver contentResolver = mock(ContentResolver.class); - when(fakeActivity.getContentResolver()).thenReturn(contentResolver); ClipboardContentFormat clipboardFormat = ClipboardContentFormat.PLAIN_TEXT; ClipData.Item mockItem = mock(ClipData.Item.class); Uri mockUri = mock(Uri.class); + when(fakeActivity.getContentResolver()).thenReturn(contentResolver); when(mockUri.getScheme()).thenReturn("content"); when(mockItem.getText()).thenReturn(null); when(mockItem.getUri()).thenReturn(mockUri); @@ -120,6 +121,8 @@ public void platformPlugin_getClipboardDataIsNonNullWhenContentUriWithTextProvid when(mockItem.coerceToText(fakeActivity)).thenReturn("something non-null"); ClipData clip = new ClipData("label", new String[0], mockItem); + + assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); clipboardManager.setPrimaryClip(clip); assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } @@ -140,9 +143,12 @@ public void platformPlugin_getClipboardDataIsNullWhenContentUriProvidedContainsN ContentResolver contentResolver = ctx.getContentResolver(); ClipboardContentFormat clipboardFormat = ClipboardContentFormat.PLAIN_TEXT; + when(fakeActivity.getContentResolver()).thenReturn(contentResolver); + Uri uri = Uri.parse("content://media/external_primary/images/media/"); ClipData clip = ClipData.newUri(contentResolver, "URI", uri); + clipboardManager.setPrimaryClip(clip); assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } @@ -162,9 +168,12 @@ public void platformPlugin_getClipboardDataIsNullWhenNonContentUriProvided() thr ContentResolver contentResolver = ctx.getContentResolver(); ClipboardContentFormat clipboardFormat = ClipboardContentFormat.PLAIN_TEXT; + when(fakeActivity.getContentResolver()).thenReturn(contentResolver); + Uri uri = Uri.parse("file:///"); ClipData clip = ClipData.newUri(contentResolver, "URI", uri); + clipboardManager.setPrimaryClip(clip); assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } @@ -184,9 +193,12 @@ public void platformPlugin_getClipboardDataIsNullWhenItemHasNoTextNorUri() throw ClipboardContentFormat clipboardFormat = ClipboardContentFormat.PLAIN_TEXT; ClipData.Item mockItem = mock(ClipData.Item.class); + when(mockItem.getText()).thenReturn(null); when(mockItem.getUri()).thenReturn(null); + ClipData clip = new ClipData("label", new String[0], mockItem); + clipboardManager.setPrimaryClip(clip); assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } From d1343b1f7d53d7ba9b2f6e66c7e68b2911544333 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Sun, 26 Nov 2023 18:31:52 -0800 Subject: [PATCH 09/12] Extract URI logic --- .../plugin/platform/PlatformPlugin.java | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index 69f12a655c08a..f01a90b344ba2 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -531,22 +531,10 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for Log.w( TAG, "Clipboard item contained no textual content nor a URI to retrieve it from."); return null; - } - - // Clipboard data contains a URI to potentially check extract text - // from, but first ensure its scheme is content in order to do so. - String uriScheme = itemUri.getScheme(); - - if (!uriScheme.equals("content")) { - Log.w( - TAG, - "Clipboard item contains a Uri with scheme '" + uriScheme + "'that is unhandled."); + } else if (!canExtractTextFromUri(itemUri)) { + Log.w(TAG, "Clipboard text was unable to be received from content URI."); return null; } - - // Try extracting text from content URI; FileNotFoundException will be - // thrown if text cannot be extracted from the URI. - activity.getContentResolver().openTypedAssetFileDescriptor(itemUri, "text/*", null); } // Safely return clipboard data coerced into text; will return either @@ -561,14 +549,30 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for + "https://developer.android.com/guide/topics/permissions/overview", e); return null; - } catch (FileNotFoundException e) { - Log.w(TAG, "Clipboard text was unable to be received from content URI."); - return null; } return null; } + private boolean canExtractTextFromUri(Uri uri) { + // Will only try to extract text from URI if it has the content scheme. + String uriScheme = uri.getScheme(); + + if (!uriScheme.equals("content")) { + Log.w(TAG, "Clipboard item contains a Uri with scheme '" + uriScheme + "'that is unhandled."); + return false; + } + + try { + activity.getContentResolver().openTypedAssetFileDescriptor(uri, "text/*", null); + } catch (FileNotFoundException e) { + return false; + } + + // Text can successfully be extracted from content URI. + return true; + } + private void setClipboardData(String text) { ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE); From b7aa9d3f1606d9daaf9f2f876869691ca5c6dabe Mon Sep 17 00:00:00 2001 From: camsim99 Date: Sun, 26 Nov 2023 19:34:59 -0800 Subject: [PATCH 10/12] Reduce redundant setup in test --- .../plugin/platform/PlatformPluginTest.java | 272 +++++------------- 1 file changed, 74 insertions(+), 198 deletions(-) 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 9e8d78922fe27..48bfab1466bcd 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java @@ -41,6 +41,7 @@ import io.flutter.plugin.platform.PlatformPlugin.PlatformPluginDelegate; import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; @@ -54,61 +55,55 @@ public class PlatformPluginTest { private final Context ctx = ApplicationProvider.getApplicationContext(); - @Config(sdk = Build.VERSION_CODES.KITKAT) - @Test - public void itIgnoresNewHapticEventsOnOldAndroidPlatforms() { - View fakeDecorView = mock(View.class); - Window fakeWindow = mock(Window.class); + private View fakeDecorView = mock(View.class); + private Window fakeWindow = mock(Window.class); + private Activity fakeActivity = mock(Activity.class); + private PlatformChannel mockPlatformChannel = mock(PlatformChannel.class); + private PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class); + private PlatformPlugin testPlatformPlugin = new PlatformPlugin(fakeActivity, mockPlatformChannel); + private PlatformPlugin testPlatformPluginWithDelegate = + new PlatformPlugin(fakeActivity, mockPlatformChannel, mockPlatformPluginDelegate); + + private ClipboardManager clipboardManager; + private ClipboardContentFormat clipboardFormat; + + @Before + public void setUp() { when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); - Activity fakeActivity = mock(Activity.class); when(fakeActivity.getWindow()).thenReturn(fakeWindow); - PlatformChannel fakePlatformChannel = mock(PlatformChannel.class); - PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); + } + + public void setUpForTextClipboardTests() { + clipboardManager = spy(ctx.getSystemService(ClipboardManager.class)); + when(fakeActivity.getSystemService(Context.CLIPBOARD_SERVICE)).thenReturn(clipboardManager); + clipboardFormat = ClipboardContentFormat.PLAIN_TEXT; + } + @Config(sdk = Build.VERSION_CODES.KITKAT) + @Test + public void itIgnoresNewHapticEventsOnOldAndroidPlatforms() { // HEAVY_IMPACT haptic response is only available on "M" (23) and later. - platformPlugin.vibrateHapticFeedback(PlatformChannel.HapticFeedbackType.HEAVY_IMPACT); + testPlatformPlugin.vibrateHapticFeedback(PlatformChannel.HapticFeedbackType.HEAVY_IMPACT); // SELECTION_CLICK haptic response is only available on "LOLLIPOP" (21) and later. - platformPlugin.vibrateHapticFeedback(PlatformChannel.HapticFeedbackType.SELECTION_CLICK); + testPlatformPlugin.vibrateHapticFeedback(PlatformChannel.HapticFeedbackType.SELECTION_CLICK); } @Test public void platformPlugin_getClipboardDataIsNonNullWhenPlainTextCopied() throws IOException { - ClipboardManager clipboardManager = ctx.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; + setUpForTextClipboardTests(); ClipData clip = ClipData.newPlainText("label", "Text"); - assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + assertNull(testPlatformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); clipboardManager.setPrimaryClip(clip); - assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + assertNotNull(testPlatformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } @Test public void platformPlugin_getClipboardDataIsNonNullWhenContentUriWithTextProvided() throws IOException { - ClipboardManager clipboardManager = ctx.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); - + setUpForTextClipboardTests(); ContentResolver contentResolver = mock(ContentResolver.class); - ClipboardContentFormat clipboardFormat = ClipboardContentFormat.PLAIN_TEXT; ClipData.Item mockItem = mock(ClipData.Item.class); Uri mockUri = mock(Uri.class); @@ -122,27 +117,16 @@ public void platformPlugin_getClipboardDataIsNonNullWhenContentUriWithTextProvid ClipData clip = new ClipData("label", new String[0], mockItem); - assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + assertNull(testPlatformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); clipboardManager.setPrimaryClip(clip); - assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + assertNotNull(testPlatformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } @Test public void platformPlugin_getClipboardDataIsNullWhenContentUriProvidedContainsNoText() throws IOException { - ClipboardManager clipboardManager = ctx.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); - + setUpForTextClipboardTests(); ContentResolver contentResolver = ctx.getContentResolver(); - ClipboardContentFormat clipboardFormat = ClipboardContentFormat.PLAIN_TEXT; when(fakeActivity.getContentResolver()).thenReturn(contentResolver); @@ -150,24 +134,13 @@ public void platformPlugin_getClipboardDataIsNullWhenContentUriProvidedContainsN ClipData clip = ClipData.newUri(contentResolver, "URI", uri); clipboardManager.setPrimaryClip(clip); - assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + assertNull(testPlatformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } @Test public void platformPlugin_getClipboardDataIsNullWhenNonContentUriProvided() throws IOException { - ClipboardManager clipboardManager = ctx.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); - + setUpForTextClipboardTests(); ContentResolver contentResolver = ctx.getContentResolver(); - ClipboardContentFormat clipboardFormat = ClipboardContentFormat.PLAIN_TEXT; when(fakeActivity.getContentResolver()).thenReturn(contentResolver); @@ -175,23 +148,12 @@ public void platformPlugin_getClipboardDataIsNullWhenNonContentUriProvided() thr ClipData clip = ClipData.newUri(contentResolver, "URI", uri); clipboardManager.setPrimaryClip(clip); - assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + assertNull(testPlatformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } @Test public void platformPlugin_getClipboardDataIsNullWhenItemHasNoTextNorUri() throws IOException { - ClipboardManager clipboardManager = ctx.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; + setUpForTextClipboardTests(); ClipData.Item mockItem = mock(ClipData.Item.class); when(mockItem.getText()).thenReturn(null); @@ -200,7 +162,7 @@ public void platformPlugin_getClipboardDataIsNullWhenItemHasNoTextNorUri() throw ClipData clip = new ClipData("label", new String[0], mockItem); clipboardManager.setPrimaryClip(clip); - assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + assertNull(testPlatformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } @SuppressWarnings("deprecation") @@ -208,50 +170,41 @@ public void platformPlugin_getClipboardDataIsNullWhenItemHasNoTextNorUri() throw @Config(sdk = Build.VERSION_CODES.P) @Test public void platformPlugin_hasStrings() { - ClipboardManager clipboardManager = spy(ctx.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); + setUpForTextClipboardTests(); // Plain text ClipData clip = ClipData.newPlainText("label", "Text"); clipboardManager.setPrimaryClip(clip); - assertTrue(platformPlugin.mPlatformMessageHandler.clipboardHasStrings()); + assertTrue(testPlatformPlugin.mPlatformMessageHandler.clipboardHasStrings()); // Empty plain text clip = ClipData.newPlainText("", ""); clipboardManager.setPrimaryClip(clip); // Without actually accessing clipboard data (preferred behavior), it is not possible to // distinguish between empty and non-empty string contents. - assertTrue(platformPlugin.mPlatformMessageHandler.clipboardHasStrings()); + assertTrue(testPlatformPlugin.mPlatformMessageHandler.clipboardHasStrings()); // HTML text clip = ClipData.newHtmlText("motto", "Don't be evil", "Don't be evil"); clipboardManager.setPrimaryClip(clip); - assertTrue(platformPlugin.mPlatformMessageHandler.clipboardHasStrings()); + assertTrue(testPlatformPlugin.mPlatformMessageHandler.clipboardHasStrings()); // Text MIME type clip = new ClipData("label", new String[] {"text/something"}, new ClipData.Item("content")); clipboardManager.setPrimaryClip(clip); - assertTrue(platformPlugin.mPlatformMessageHandler.clipboardHasStrings()); + assertTrue(testPlatformPlugin.mPlatformMessageHandler.clipboardHasStrings()); // Other MIME type clip = new ClipData( "label", new String[] {"application/octet-stream"}, new ClipData.Item("content")); clipboardManager.setPrimaryClip(clip); - assertFalse(platformPlugin.mPlatformMessageHandler.clipboardHasStrings()); + assertFalse(testPlatformPlugin.mPlatformMessageHandler.clipboardHasStrings()); if (Build.VERSION.SDK_INT >= 28) { // Empty clipboard clipboardManager.clearPrimaryClip(); - assertFalse(platformPlugin.mPlatformMessageHandler.clipboardHasStrings()); + assertFalse(testPlatformPlugin.mPlatformMessageHandler.clipboardHasStrings()); } // Verify that the clipboard contents are never accessed. @@ -262,14 +215,6 @@ public void platformPlugin_hasStrings() { @Config(sdk = Build.VERSION_CODES.Q) @Test public void setNavigationBarDividerColor() { - 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); - PlatformChannel fakePlatformChannel = mock(PlatformChannel.class); - PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); - if (Build.VERSION.SDK_INT >= 28) { // Default style test SystemChromeStyle style = @@ -282,7 +227,7 @@ public void setNavigationBarDividerColor() { 0XFF006DB3, // systemNavigationBarDividerColor true); // systemNavigationBarContrastEnforced - platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); + testPlatformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); verify(fakeWindow).setStatusBarColor(0xFF000000); verify(fakeWindow).setNavigationBarColor(0XFFC70039); @@ -302,7 +247,7 @@ public void setNavigationBarDividerColor() { 0XFF006DB3, // systemNavigationBarDividerColor false); // systemNavigationBarContrastEnforced - platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); + testPlatformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); verify(fakeWindow).setStatusBarColor(0XFF006DB3); verify(fakeWindow).setNavigationBarColor(0XFF000000); @@ -321,7 +266,7 @@ public void setNavigationBarDividerColor() { 0XFF006DB3, // systemNavigationBarDividerColor null); // systemNavigationBarContrastEnforced - platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); + testPlatformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); verify(fakeWindow, times(2)).setStatusBarColor(0XFF006DB3); verify(fakeWindow, times(2)).setNavigationBarColor(0XFF000000); @@ -338,15 +283,8 @@ public void setNavigationBarDividerColor() { @Test public void setNavigationBarIconBrightness() { if (Build.VERSION.SDK_INT >= 30) { - View fakeDecorView = mock(View.class); WindowInsetsController fakeWindowInsetsController = mock(WindowInsetsController.class); - Window fakeWindow = mock(Window.class); - when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); when(fakeWindow.getInsetsController()).thenReturn(fakeWindowInsetsController); - Activity fakeActivity = mock(Activity.class); - when(fakeActivity.getWindow()).thenReturn(fakeWindow); - PlatformChannel fakePlatformChannel = mock(PlatformChannel.class); - PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); SystemChromeStyle style = new SystemChromeStyle( @@ -358,7 +296,7 @@ public void setNavigationBarIconBrightness() { null, // systemNavigationBarDividerColor null); // systemNavigationBarContrastEnforced - platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); + testPlatformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); verify(fakeWindowInsetsController) .setSystemBarsAppearance(0, APPEARANCE_LIGHT_NAVIGATION_BARS); @@ -373,7 +311,7 @@ public void setNavigationBarIconBrightness() { null, // systemNavigationBarDividerColor null); // systemNavigationBarContrastEnforced - platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); + testPlatformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); verify(fakeWindowInsetsController) .setSystemBarsAppearance( @@ -385,15 +323,8 @@ public void setNavigationBarIconBrightness() { @Test public void setStatusBarIconBrightness() { if (Build.VERSION.SDK_INT >= 30) { - View fakeDecorView = mock(View.class); WindowInsetsController fakeWindowInsetsController = mock(WindowInsetsController.class); - Window fakeWindow = mock(Window.class); - when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); when(fakeWindow.getInsetsController()).thenReturn(fakeWindowInsetsController); - Activity fakeActivity = mock(Activity.class); - when(fakeActivity.getWindow()).thenReturn(fakeWindow); - PlatformChannel fakePlatformChannel = mock(PlatformChannel.class); - PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); SystemChromeStyle style = new SystemChromeStyle( @@ -405,7 +336,7 @@ public void setStatusBarIconBrightness() { null, // systemNavigationBarDividerColor null); // systemNavigationBarContrastEnforced - platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); + testPlatformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); verify(fakeWindowInsetsController).setSystemBarsAppearance(0, APPEARANCE_LIGHT_STATUS_BARS); @@ -419,7 +350,7 @@ public void setStatusBarIconBrightness() { null, // systemNavigationBarDividerColor null); // systemNavigationBarContrastEnforced - platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); + testPlatformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); verify(fakeWindowInsetsController) .setSystemBarsAppearance(APPEARANCE_LIGHT_STATUS_BARS, APPEARANCE_LIGHT_STATUS_BARS); @@ -431,16 +362,8 @@ public void setStatusBarIconBrightness() { @Config(sdk = Build.VERSION_CODES.Q) @Test public void setSystemUiMode() { - 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); - PlatformChannel fakePlatformChannel = mock(PlatformChannel.class); - PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); - if (Build.VERSION.SDK_INT >= 28) { - platformPlugin.mPlatformMessageHandler.showSystemUiMode( + testPlatformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.LEAN_BACK); verify(fakeDecorView) .setSystemUiVisibility( @@ -450,7 +373,7 @@ public void setSystemUiMode() { | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN); - platformPlugin.mPlatformMessageHandler.showSystemUiMode( + testPlatformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.IMMERSIVE); verify(fakeDecorView) .setSystemUiVisibility( @@ -461,7 +384,7 @@ public void setSystemUiMode() { | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN); - platformPlugin.mPlatformMessageHandler.showSystemUiMode( + testPlatformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.IMMERSIVE_STICKY); verify(fakeDecorView) .setSystemUiVisibility( @@ -474,7 +397,7 @@ public void setSystemUiMode() { } if (Build.VERSION.SDK_INT >= 29) { - platformPlugin.mPlatformMessageHandler.showSystemUiMode( + testPlatformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.EDGE_TO_EDGE); verify(fakeDecorView) .setSystemUiVisibility( @@ -487,13 +410,12 @@ public void setSystemUiMode() { @SuppressWarnings("deprecation") // SYSTEM_UI_FLAG_FULLSCREEN @Test - public void setSystemUiModeListener_overlaysAreHidden() { + public void setSystemUiModeListener_overlaysAreHidden() { // HANDLE DIFF ActivityController controller = Robolectric.buildActivity(Activity.class); controller.setup(); Activity fakeActivity = controller.get(); - PlatformChannel fakePlatformChannel = mock(PlatformChannel.class); - PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); + PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, mockPlatformChannel); // Subscribe to system UI visibility events. platformPlugin.mPlatformMessageHandler.setSystemUiChangeListener(); @@ -506,13 +428,13 @@ public void setSystemUiModeListener_overlaysAreHidden() { // No events should have been sent to the platform channel yet. They are scheduled for // the next frame. - verify(fakePlatformChannel, never()).systemChromeChanged(anyBoolean()); + verify(mockPlatformChannel, never()).systemChromeChanged(anyBoolean()); // Simulate the next frame. ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); // Now the platform channel should receive the event. - verify(fakePlatformChannel).systemChromeChanged(false); + verify(mockPlatformChannel).systemChromeChanged(false); } @SuppressWarnings("deprecation") @@ -522,9 +444,7 @@ public void setSystemUiModeListener_overlaysAreVisible() { ActivityController controller = Robolectric.buildActivity(Activity.class); controller.setup(); Activity fakeActivity = controller.get(); - - PlatformChannel fakePlatformChannel = mock(PlatformChannel.class); - PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); + PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, mockPlatformChannel); // Subscribe to system Ui visibility events. platformPlugin.mPlatformMessageHandler.setSystemUiChangeListener(); @@ -534,13 +454,13 @@ public void setSystemUiModeListener_overlaysAreVisible() { // No events should have been sent to the platform channel yet. They are scheduled for // the next frame. - verify(fakePlatformChannel, never()).systemChromeChanged(anyBoolean()); + verify(mockPlatformChannel, never()).systemChromeChanged(anyBoolean()); // Simulate the next frame. ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); // Now the platform channel should receive the event. - verify(fakePlatformChannel).systemChromeChanged(true); + verify(mockPlatformChannel).systemChromeChanged(true); } @SuppressWarnings("deprecation") @@ -548,15 +468,7 @@ public void setSystemUiModeListener_overlaysAreVisible() { @Config(sdk = Build.VERSION_CODES.P) @Test public void doNotEnableEdgeToEdgeOnOlderSdk() { - 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); - PlatformChannel fakePlatformChannel = mock(PlatformChannel.class); - PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); - - platformPlugin.mPlatformMessageHandler.showSystemUiMode( + testPlatformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.EDGE_TO_EDGE); verify(fakeDecorView, never()) .setSystemUiVisibility( @@ -570,19 +482,11 @@ public void doNotEnableEdgeToEdgeOnOlderSdk() { @Config(sdk = Build.VERSION_CODES.Q) @Test public void verifyWindowFlagsSetToStyleOverlays() { - 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); - PlatformChannel fakePlatformChannel = mock(PlatformChannel.class); - PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel); - SystemChromeStyle style = new SystemChromeStyle( 0XFF000000, Brightness.LIGHT, true, 0XFFC70039, Brightness.LIGHT, 0XFF006DB3, true); - platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); + testPlatformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); verify(fakeWindow).addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); verify(fakeWindow) .clearFlags( @@ -592,46 +496,30 @@ public void verifyWindowFlagsSetToStyleOverlays() { @Test public void setFrameworkHandlesBackFlutterActivity() { - Activity mockActivity = mock(Activity.class); - PlatformChannel mockPlatformChannel = mock(PlatformChannel.class); - PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class); - PlatformPlugin platformPlugin = - new PlatformPlugin(mockActivity, mockPlatformChannel, mockPlatformPluginDelegate); - - platformPlugin.mPlatformMessageHandler.setFrameworkHandlesBack(true); + testPlatformPluginWithDelegate.mPlatformMessageHandler.setFrameworkHandlesBack(true); verify(mockPlatformPluginDelegate, times(1)).setFrameworkHandlesBack(true); } @Test public void popSystemNavigatorFlutterActivity() { - Activity mockActivity = mock(Activity.class); - PlatformChannel mockPlatformChannel = mock(PlatformChannel.class); - PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class); when(mockPlatformPluginDelegate.popSystemNavigator()).thenReturn(false); - PlatformPlugin platformPlugin = - new PlatformPlugin(mockActivity, mockPlatformChannel, mockPlatformPluginDelegate); - platformPlugin.mPlatformMessageHandler.popSystemNavigator(); + testPlatformPluginWithDelegate.mPlatformMessageHandler.popSystemNavigator(); verify(mockPlatformPluginDelegate, times(1)).popSystemNavigator(); - verify(mockActivity, times(1)).finish(); + verify(fakeActivity, times(1)).finish(); } @Test public void doesNotDoAnythingByDefaultIfPopSystemNavigatorOverridden() { - Activity mockActivity = mock(Activity.class); - PlatformChannel mockPlatformChannel = mock(PlatformChannel.class); - PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class); when(mockPlatformPluginDelegate.popSystemNavigator()).thenReturn(true); - PlatformPlugin platformPlugin = - new PlatformPlugin(mockActivity, mockPlatformChannel, mockPlatformPluginDelegate); - platformPlugin.mPlatformMessageHandler.popSystemNavigator(); + testPlatformPluginWithDelegate.mPlatformMessageHandler.popSystemNavigator(); verify(mockPlatformPluginDelegate, times(1)).popSystemNavigator(); // No longer perform the default action when overridden. - verify(mockActivity, never()).finish(); + verify(fakeActivity, never()).finish(); } @SuppressWarnings("deprecation") @@ -651,8 +539,6 @@ public void handleOnBackPressed() { }; activity.getOnBackPressedDispatcher().addCallback(backCallback); - PlatformChannel mockPlatformChannel = mock(PlatformChannel.class); - PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class); when(mockPlatformPluginDelegate.popSystemNavigator()).thenReturn(false); PlatformPlugin platformPlugin = new PlatformPlugin(activity, mockPlatformChannel, mockPlatformPluginDelegate); @@ -670,13 +556,9 @@ public void handleOnBackPressed() { @Test public void doesNotDoAnythingByDefaultIfFragmentPopSystemNavigatorOverridden() { FragmentActivity activity = spy(Robolectric.setupActivity(FragmentActivity.class)); - PlatformChannel mockPlatformChannel = mock(PlatformChannel.class); - PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class); when(mockPlatformPluginDelegate.popSystemNavigator()).thenReturn(true); - PlatformPlugin platformPlugin = - new PlatformPlugin(activity, mockPlatformChannel, mockPlatformPluginDelegate); - platformPlugin.mPlatformMessageHandler.popSystemNavigator(); + testPlatformPluginWithDelegate.mPlatformMessageHandler.popSystemNavigator(); verify(mockPlatformPluginDelegate, times(1)).popSystemNavigator(); // No longer perform the default action when overridden. @@ -686,8 +568,6 @@ public void doesNotDoAnythingByDefaultIfFragmentPopSystemNavigatorOverridden() { @Test public void setRequestedOrientationFlutterFragment() { FragmentActivity mockFragmentActivity = mock(FragmentActivity.class); - PlatformChannel mockPlatformChannel = mock(PlatformChannel.class); - PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class); when(mockPlatformPluginDelegate.popSystemNavigator()).thenReturn(false); PlatformPlugin platformPlugin = new PlatformPlugin(mockFragmentActivity, mockPlatformChannel, mockPlatformPluginDelegate); @@ -699,12 +579,8 @@ public void setRequestedOrientationFlutterFragment() { @Test public void performsDefaultBehaviorWhenNoDelegateProvided() { - Activity mockActivity = mock(Activity.class); - PlatformChannel mockPlatformChannel = mock(PlatformChannel.class); - PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel); - - platformPlugin.mPlatformMessageHandler.popSystemNavigator(); + testPlatformPlugin.mPlatformMessageHandler.popSystemNavigator(); - verify(mockActivity, times(1)).finish(); + verify(fakeActivity, times(1)).finish(); } } From 483ea4db872d8cc80bd3460c0ecd8c3be86f2367 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Wed, 29 Nov 2023 15:36:57 -0800 Subject: [PATCH 11/12] Undo overkill --- .../plugin/platform/PlatformPluginTest.java | 224 +++++++++++++----- 1 file changed, 159 insertions(+), 65 deletions(-) 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 48bfab1466bcd..4a3531b196663 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java @@ -41,7 +41,6 @@ import io.flutter.plugin.platform.PlatformPlugin.PlatformPluginDelegate; import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; @@ -54,106 +53,129 @@ @RunWith(RobolectricTestRunner.class) public class PlatformPluginTest { private final Context ctx = ApplicationProvider.getApplicationContext(); - - private View fakeDecorView = mock(View.class); - private Window fakeWindow = mock(Window.class); - private Activity fakeActivity = mock(Activity.class); - private PlatformChannel mockPlatformChannel = mock(PlatformChannel.class); - private PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class); - private PlatformPlugin testPlatformPlugin = new PlatformPlugin(fakeActivity, mockPlatformChannel); - private PlatformPlugin testPlatformPluginWithDelegate = - new PlatformPlugin(fakeActivity, mockPlatformChannel, mockPlatformPluginDelegate); + private final PlatformChannel mockPlatformChannel = mock(PlatformChannel.class); private ClipboardManager clipboardManager; private ClipboardContentFormat clipboardFormat; - @Before - public void setUp() { - when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); - when(fakeActivity.getWindow()).thenReturn(fakeWindow); - } - - public void setUpForTextClipboardTests() { + public void setUpForTextClipboardTests(Activity mockActivity) { clipboardManager = spy(ctx.getSystemService(ClipboardManager.class)); - when(fakeActivity.getSystemService(Context.CLIPBOARD_SERVICE)).thenReturn(clipboardManager); + when(mockActivity.getSystemService(Context.CLIPBOARD_SERVICE)).thenReturn(clipboardManager); clipboardFormat = ClipboardContentFormat.PLAIN_TEXT; } @Config(sdk = Build.VERSION_CODES.KITKAT) @Test public void itIgnoresNewHapticEventsOnOldAndroidPlatforms() { + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + Activity mockActivity = mock(Activity.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + when(mockActivity.getWindow()).thenReturn(fakeWindow); + PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel); + // HEAVY_IMPACT haptic response is only available on "M" (23) and later. - testPlatformPlugin.vibrateHapticFeedback(PlatformChannel.HapticFeedbackType.HEAVY_IMPACT); + platformPlugin.vibrateHapticFeedback(PlatformChannel.HapticFeedbackType.HEAVY_IMPACT); // SELECTION_CLICK haptic response is only available on "LOLLIPOP" (21) and later. - testPlatformPlugin.vibrateHapticFeedback(PlatformChannel.HapticFeedbackType.SELECTION_CLICK); + platformPlugin.vibrateHapticFeedback(PlatformChannel.HapticFeedbackType.SELECTION_CLICK); } @Test public void platformPlugin_getClipboardDataIsNonNullWhenPlainTextCopied() throws IOException { - setUpForTextClipboardTests(); + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + Activity mockActivity = mock(Activity.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + when(mockActivity.getWindow()).thenReturn(fakeWindow); + PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel); + setUpForTextClipboardTests(mockActivity); ClipData clip = ClipData.newPlainText("label", "Text"); - assertNull(testPlatformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); clipboardManager.setPrimaryClip(clip); - assertNotNull(testPlatformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } @Test public void platformPlugin_getClipboardDataIsNonNullWhenContentUriWithTextProvided() throws IOException { - setUpForTextClipboardTests(); + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + Activity mockActivity = mock(Activity.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + when(mockActivity.getWindow()).thenReturn(fakeWindow); + PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel); + setUpForTextClipboardTests(mockActivity); ContentResolver contentResolver = mock(ContentResolver.class); ClipData.Item mockItem = mock(ClipData.Item.class); Uri mockUri = mock(Uri.class); - when(fakeActivity.getContentResolver()).thenReturn(contentResolver); + when(mockActivity.getContentResolver()).thenReturn(contentResolver); when(mockUri.getScheme()).thenReturn("content"); when(mockItem.getText()).thenReturn(null); when(mockItem.getUri()).thenReturn(mockUri); when(contentResolver.openTypedAssetFileDescriptor(any(Uri.class), any(), any())) .thenReturn(mock(AssetFileDescriptor.class)); - when(mockItem.coerceToText(fakeActivity)).thenReturn("something non-null"); + when(mockItem.coerceToText(mockActivity)).thenReturn("something non-null"); ClipData clip = new ClipData("label", new String[0], mockItem); - assertNull(testPlatformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); clipboardManager.setPrimaryClip(clip); - assertNotNull(testPlatformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } @Test public void platformPlugin_getClipboardDataIsNullWhenContentUriProvidedContainsNoText() throws IOException { - setUpForTextClipboardTests(); + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + Activity mockActivity = mock(Activity.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + when(mockActivity.getWindow()).thenReturn(fakeWindow); + PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel); + setUpForTextClipboardTests(mockActivity); ContentResolver contentResolver = ctx.getContentResolver(); - when(fakeActivity.getContentResolver()).thenReturn(contentResolver); + when(mockActivity.getContentResolver()).thenReturn(contentResolver); Uri uri = Uri.parse("content://media/external_primary/images/media/"); ClipData clip = ClipData.newUri(contentResolver, "URI", uri); clipboardManager.setPrimaryClip(clip); - assertNull(testPlatformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } @Test public void platformPlugin_getClipboardDataIsNullWhenNonContentUriProvided() throws IOException { - setUpForTextClipboardTests(); + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + Activity mockActivity = mock(Activity.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + when(mockActivity.getWindow()).thenReturn(fakeWindow); + PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel); + setUpForTextClipboardTests(mockActivity); ContentResolver contentResolver = ctx.getContentResolver(); - when(fakeActivity.getContentResolver()).thenReturn(contentResolver); + when(mockActivity.getContentResolver()).thenReturn(contentResolver); Uri uri = Uri.parse("file:///"); ClipData clip = ClipData.newUri(contentResolver, "URI", uri); clipboardManager.setPrimaryClip(clip); - assertNull(testPlatformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } @Test public void platformPlugin_getClipboardDataIsNullWhenItemHasNoTextNorUri() throws IOException { - setUpForTextClipboardTests(); + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + Activity mockActivity = mock(Activity.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + when(mockActivity.getWindow()).thenReturn(fakeWindow); + PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel); + setUpForTextClipboardTests(mockActivity); ClipData.Item mockItem = mock(ClipData.Item.class); when(mockItem.getText()).thenReturn(null); @@ -162,7 +184,7 @@ public void platformPlugin_getClipboardDataIsNullWhenItemHasNoTextNorUri() throw ClipData clip = new ClipData("label", new String[0], mockItem); clipboardManager.setPrimaryClip(clip); - assertNull(testPlatformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); } @SuppressWarnings("deprecation") @@ -170,41 +192,47 @@ public void platformPlugin_getClipboardDataIsNullWhenItemHasNoTextNorUri() throw @Config(sdk = Build.VERSION_CODES.P) @Test public void platformPlugin_hasStrings() { - setUpForTextClipboardTests(); + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + Activity mockActivity = mock(Activity.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + when(mockActivity.getWindow()).thenReturn(fakeWindow); + PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel); + setUpForTextClipboardTests(mockActivity); // Plain text ClipData clip = ClipData.newPlainText("label", "Text"); clipboardManager.setPrimaryClip(clip); - assertTrue(testPlatformPlugin.mPlatformMessageHandler.clipboardHasStrings()); + assertTrue(platformPlugin.mPlatformMessageHandler.clipboardHasStrings()); // Empty plain text clip = ClipData.newPlainText("", ""); clipboardManager.setPrimaryClip(clip); // Without actually accessing clipboard data (preferred behavior), it is not possible to // distinguish between empty and non-empty string contents. - assertTrue(testPlatformPlugin.mPlatformMessageHandler.clipboardHasStrings()); + assertTrue(platformPlugin.mPlatformMessageHandler.clipboardHasStrings()); // HTML text clip = ClipData.newHtmlText("motto", "Don't be evil", "Don't be evil"); clipboardManager.setPrimaryClip(clip); - assertTrue(testPlatformPlugin.mPlatformMessageHandler.clipboardHasStrings()); + assertTrue(platformPlugin.mPlatformMessageHandler.clipboardHasStrings()); // Text MIME type clip = new ClipData("label", new String[] {"text/something"}, new ClipData.Item("content")); clipboardManager.setPrimaryClip(clip); - assertTrue(testPlatformPlugin.mPlatformMessageHandler.clipboardHasStrings()); + assertTrue(platformPlugin.mPlatformMessageHandler.clipboardHasStrings()); // Other MIME type clip = new ClipData( "label", new String[] {"application/octet-stream"}, new ClipData.Item("content")); clipboardManager.setPrimaryClip(clip); - assertFalse(testPlatformPlugin.mPlatformMessageHandler.clipboardHasStrings()); + assertFalse(platformPlugin.mPlatformMessageHandler.clipboardHasStrings()); if (Build.VERSION.SDK_INT >= 28) { // Empty clipboard clipboardManager.clearPrimaryClip(); - assertFalse(testPlatformPlugin.mPlatformMessageHandler.clipboardHasStrings()); + assertFalse(platformPlugin.mPlatformMessageHandler.clipboardHasStrings()); } // Verify that the clipboard contents are never accessed. @@ -215,6 +243,13 @@ public void platformPlugin_hasStrings() { @Config(sdk = Build.VERSION_CODES.Q) @Test public void setNavigationBarDividerColor() { + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + Activity mockActivity = mock(Activity.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + when(mockActivity.getWindow()).thenReturn(fakeWindow); + PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel); + if (Build.VERSION.SDK_INT >= 28) { // Default style test SystemChromeStyle style = @@ -227,7 +262,7 @@ public void setNavigationBarDividerColor() { 0XFF006DB3, // systemNavigationBarDividerColor true); // systemNavigationBarContrastEnforced - testPlatformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); + platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); verify(fakeWindow).setStatusBarColor(0xFF000000); verify(fakeWindow).setNavigationBarColor(0XFFC70039); @@ -247,7 +282,7 @@ public void setNavigationBarDividerColor() { 0XFF006DB3, // systemNavigationBarDividerColor false); // systemNavigationBarContrastEnforced - testPlatformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); + platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); verify(fakeWindow).setStatusBarColor(0XFF006DB3); verify(fakeWindow).setNavigationBarColor(0XFF000000); @@ -266,7 +301,7 @@ public void setNavigationBarDividerColor() { 0XFF006DB3, // systemNavigationBarDividerColor null); // systemNavigationBarContrastEnforced - testPlatformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); + platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); verify(fakeWindow, times(2)).setStatusBarColor(0XFF006DB3); verify(fakeWindow, times(2)).setNavigationBarColor(0XFF000000); @@ -282,6 +317,13 @@ public void setNavigationBarDividerColor() { @Config(sdk = Build.VERSION_CODES.R) @Test public void setNavigationBarIconBrightness() { + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + Activity mockActivity = mock(Activity.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + when(mockActivity.getWindow()).thenReturn(fakeWindow); + PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel); + if (Build.VERSION.SDK_INT >= 30) { WindowInsetsController fakeWindowInsetsController = mock(WindowInsetsController.class); when(fakeWindow.getInsetsController()).thenReturn(fakeWindowInsetsController); @@ -296,7 +338,7 @@ public void setNavigationBarIconBrightness() { null, // systemNavigationBarDividerColor null); // systemNavigationBarContrastEnforced - testPlatformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); + platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); verify(fakeWindowInsetsController) .setSystemBarsAppearance(0, APPEARANCE_LIGHT_NAVIGATION_BARS); @@ -311,7 +353,7 @@ public void setNavigationBarIconBrightness() { null, // systemNavigationBarDividerColor null); // systemNavigationBarContrastEnforced - testPlatformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); + platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); verify(fakeWindowInsetsController) .setSystemBarsAppearance( @@ -322,6 +364,13 @@ public void setNavigationBarIconBrightness() { @Config(sdk = Build.VERSION_CODES.R) @Test public void setStatusBarIconBrightness() { + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + Activity mockActivity = mock(Activity.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + when(mockActivity.getWindow()).thenReturn(fakeWindow); + PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel); + if (Build.VERSION.SDK_INT >= 30) { WindowInsetsController fakeWindowInsetsController = mock(WindowInsetsController.class); when(fakeWindow.getInsetsController()).thenReturn(fakeWindowInsetsController); @@ -336,7 +385,7 @@ public void setStatusBarIconBrightness() { null, // systemNavigationBarDividerColor null); // systemNavigationBarContrastEnforced - testPlatformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); + platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); verify(fakeWindowInsetsController).setSystemBarsAppearance(0, APPEARANCE_LIGHT_STATUS_BARS); @@ -350,7 +399,7 @@ public void setStatusBarIconBrightness() { null, // systemNavigationBarDividerColor null); // systemNavigationBarContrastEnforced - testPlatformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); + platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); verify(fakeWindowInsetsController) .setSystemBarsAppearance(APPEARANCE_LIGHT_STATUS_BARS, APPEARANCE_LIGHT_STATUS_BARS); @@ -362,8 +411,15 @@ public void setStatusBarIconBrightness() { @Config(sdk = Build.VERSION_CODES.Q) @Test public void setSystemUiMode() { + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + Activity mockActivity = mock(Activity.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + when(mockActivity.getWindow()).thenReturn(fakeWindow); + PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel); + if (Build.VERSION.SDK_INT >= 28) { - testPlatformPlugin.mPlatformMessageHandler.showSystemUiMode( + platformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.LEAN_BACK); verify(fakeDecorView) .setSystemUiVisibility( @@ -373,7 +429,7 @@ public void setSystemUiMode() { | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN); - testPlatformPlugin.mPlatformMessageHandler.showSystemUiMode( + platformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.IMMERSIVE); verify(fakeDecorView) .setSystemUiVisibility( @@ -384,7 +440,7 @@ public void setSystemUiMode() { | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN); - testPlatformPlugin.mPlatformMessageHandler.showSystemUiMode( + platformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.IMMERSIVE_STICKY); verify(fakeDecorView) .setSystemUiVisibility( @@ -397,7 +453,7 @@ public void setSystemUiMode() { } if (Build.VERSION.SDK_INT >= 29) { - testPlatformPlugin.mPlatformMessageHandler.showSystemUiMode( + platformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.EDGE_TO_EDGE); verify(fakeDecorView) .setSystemUiVisibility( @@ -410,7 +466,7 @@ public void setSystemUiMode() { @SuppressWarnings("deprecation") // SYSTEM_UI_FLAG_FULLSCREEN @Test - public void setSystemUiModeListener_overlaysAreHidden() { // HANDLE DIFF + public void setSystemUiModeListener_overlaysAreHidden() { ActivityController controller = Robolectric.buildActivity(Activity.class); controller.setup(); Activity fakeActivity = controller.get(); @@ -468,7 +524,14 @@ public void setSystemUiModeListener_overlaysAreVisible() { @Config(sdk = Build.VERSION_CODES.P) @Test public void doNotEnableEdgeToEdgeOnOlderSdk() { - testPlatformPlugin.mPlatformMessageHandler.showSystemUiMode( + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + Activity mockActivity = mock(Activity.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + when(mockActivity.getWindow()).thenReturn(fakeWindow); + PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel); + + platformPlugin.mPlatformMessageHandler.showSystemUiMode( PlatformChannel.SystemUiMode.EDGE_TO_EDGE); verify(fakeDecorView, never()) .setSystemUiVisibility( @@ -482,11 +545,18 @@ public void doNotEnableEdgeToEdgeOnOlderSdk() { @Config(sdk = Build.VERSION_CODES.Q) @Test public void verifyWindowFlagsSetToStyleOverlays() { + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + Activity mockActivity = mock(Activity.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + when(mockActivity.getWindow()).thenReturn(fakeWindow); + PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel); + SystemChromeStyle style = new SystemChromeStyle( 0XFF000000, Brightness.LIGHT, true, 0XFFC70039, Brightness.LIGHT, 0XFF006DB3, true); - testPlatformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); + platformPlugin.mPlatformMessageHandler.setSystemUiOverlayStyle(style); verify(fakeWindow).addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); verify(fakeWindow) .clearFlags( @@ -496,30 +566,45 @@ public void verifyWindowFlagsSetToStyleOverlays() { @Test public void setFrameworkHandlesBackFlutterActivity() { - testPlatformPluginWithDelegate.mPlatformMessageHandler.setFrameworkHandlesBack(true); + Activity mockActivity = mock(Activity.class); + PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class); + PlatformPlugin platformPlugin = + new PlatformPlugin(mockActivity, mockPlatformChannel, mockPlatformPluginDelegate); + + platformPlugin.mPlatformMessageHandler.setFrameworkHandlesBack(true); verify(mockPlatformPluginDelegate, times(1)).setFrameworkHandlesBack(true); } @Test public void popSystemNavigatorFlutterActivity() { + Activity mockActivity = mock(Activity.class); + PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class); + PlatformPlugin platformPlugin = + new PlatformPlugin(mockActivity, mockPlatformChannel, mockPlatformPluginDelegate); + when(mockPlatformPluginDelegate.popSystemNavigator()).thenReturn(false); - testPlatformPluginWithDelegate.mPlatformMessageHandler.popSystemNavigator(); + platformPlugin.mPlatformMessageHandler.popSystemNavigator(); verify(mockPlatformPluginDelegate, times(1)).popSystemNavigator(); - verify(fakeActivity, times(1)).finish(); + verify(mockActivity, times(1)).finish(); } @Test public void doesNotDoAnythingByDefaultIfPopSystemNavigatorOverridden() { + Activity mockActivity = mock(Activity.class); + PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class); + PlatformPlugin platformPlugin = + new PlatformPlugin(mockActivity, mockPlatformChannel, mockPlatformPluginDelegate); + when(mockPlatformPluginDelegate.popSystemNavigator()).thenReturn(true); - testPlatformPluginWithDelegate.mPlatformMessageHandler.popSystemNavigator(); + platformPlugin.mPlatformMessageHandler.popSystemNavigator(); verify(mockPlatformPluginDelegate, times(1)).popSystemNavigator(); // No longer perform the default action when overridden. - verify(fakeActivity, never()).finish(); + verify(mockActivity, never()).finish(); } @SuppressWarnings("deprecation") @@ -539,6 +624,7 @@ public void handleOnBackPressed() { }; activity.getOnBackPressedDispatcher().addCallback(backCallback); + PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class); when(mockPlatformPluginDelegate.popSystemNavigator()).thenReturn(false); PlatformPlugin platformPlugin = new PlatformPlugin(activity, mockPlatformChannel, mockPlatformPluginDelegate); @@ -556,9 +642,12 @@ public void handleOnBackPressed() { @Test public void doesNotDoAnythingByDefaultIfFragmentPopSystemNavigatorOverridden() { FragmentActivity activity = spy(Robolectric.setupActivity(FragmentActivity.class)); + PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class); when(mockPlatformPluginDelegate.popSystemNavigator()).thenReturn(true); + PlatformPlugin platformPlugin = + new PlatformPlugin(activity, mockPlatformChannel, mockPlatformPluginDelegate); - testPlatformPluginWithDelegate.mPlatformMessageHandler.popSystemNavigator(); + platformPlugin.mPlatformMessageHandler.popSystemNavigator(); verify(mockPlatformPluginDelegate, times(1)).popSystemNavigator(); // No longer perform the default action when overridden. @@ -568,6 +657,7 @@ public void doesNotDoAnythingByDefaultIfFragmentPopSystemNavigatorOverridden() { @Test public void setRequestedOrientationFlutterFragment() { FragmentActivity mockFragmentActivity = mock(FragmentActivity.class); + PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class); when(mockPlatformPluginDelegate.popSystemNavigator()).thenReturn(false); PlatformPlugin platformPlugin = new PlatformPlugin(mockFragmentActivity, mockPlatformChannel, mockPlatformPluginDelegate); @@ -579,8 +669,12 @@ public void setRequestedOrientationFlutterFragment() { @Test public void performsDefaultBehaviorWhenNoDelegateProvided() { - testPlatformPlugin.mPlatformMessageHandler.popSystemNavigator(); + Activity mockActivity = mock(Activity.class); + PlatformChannel mockPlatformChannel = mock(PlatformChannel.class); + PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel); + + platformPlugin.mPlatformMessageHandler.popSystemNavigator(); - verify(fakeActivity, times(1)).finish(); + verify(mockActivity, times(1)).finish(); } } From 190c9ad598dab3ccb093fc703fc269db7d3547b4 Mon Sep 17 00:00:00 2001 From: camsim99 Date: Thu, 30 Nov 2023 14:56:49 -0800 Subject: [PATCH 12/12] fix merge --- .../plugin/platform/PlatformPlugin.java | 56 ++++++++----------- .../plugin/platform/PlatformPluginTest.java | 54 ++++++++++-------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index d0bbd28d66132..c6cec517f445f 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -11,11 +11,8 @@ import android.content.ClipDescription; import android.content.ClipboardManager; import android.content.Context; -<<<<<<< HEAD -import android.net.Uri; -======= import android.content.res.AssetFileDescriptor; ->>>>>>> upstream/main +import android.net.Uri; import android.os.Build; import android.view.HapticFeedbackConstants; import android.view.SoundEffectConstants; @@ -527,7 +524,6 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for // First, try getting clipboard data as text; no further processing // required if so. itemText = item.getText(); - if (itemText == null) { // Clipboard data does not contain text, so check whether or not it // contains a URI to extract text from. @@ -537,15 +533,28 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for Log.w( TAG, "Clipboard item contained no textual content nor a URI to retrieve it from."); return null; - } else if (!canExtractTextFromUri(itemUri)) { - Log.w(TAG, "Clipboard text was unable to be received from content URI."); + } + + // Will only try to extract text from URI if it has the content scheme. + String uriScheme = itemUri.getScheme(); + + if (!uriScheme.equals("content")) { + Log.w( + TAG, + "Clipboard item contains a Uri with scheme '" + uriScheme + "'that is unhandled."); return null; } + + AssetFileDescriptor assetFileDescriptor = + activity.getContentResolver().openTypedAssetFileDescriptor(itemUri, "text/*", null); + + // Safely return clipboard data coerced into text; will return either + // itemText or text retrieved from its URI. + itemText = item.coerceToText(activity); + if (assetFileDescriptor != null) assetFileDescriptor.close(); } - // Safely return clipboard data coerced into text; will return either - // itemText or text retrieved from its URI. - return item.coerceToText(activity); + return itemText; } } catch (SecurityException e) { Log.w( @@ -555,34 +564,17 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for + "https://developer.android.com/guide/topics/permissions/overview", e); return null; + } catch (FileNotFoundException e) { + Log.w(TAG, "Clipboard text was unable to be received from content URI."); + return null; } catch (IOException e) { - Log.w(TAG, "Failed to close AssetFileDescriptor while accessing clipboard data.", e); - return charSequence; + Log.w(TAG, "Failed to close AssetFileDescriptor while trying to read text from URI.", e); + return itemText; } return null; } - private boolean canExtractTextFromUri(Uri uri) { - // Will only try to extract text from URI if it has the content scheme. - String uriScheme = uri.getScheme(); - - if (!uriScheme.equals("content")) { - Log.w(TAG, "Clipboard item contains a Uri with scheme '" + uriScheme + "'that is unhandled."); - return false; - } - - try { - AssetFileDescriptor assetFileDescriptor = activity.getContentResolver().openTypedAssetFileDescriptor(uri, "text/*", null); - if (assetFileDescriptor != null) assetFileDescriptor.close(); - } catch (FileNotFoundException e) { - return false; - } - - // Text can successfully be extracted from content URI. - return true; - } - private void setClipboardData(String text) { ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE); 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 d8bc4d1993d2c..4f1e141174a60 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java @@ -12,10 +12,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -25,7 +22,6 @@ import android.app.Activity; import android.content.ClipData; -import android.content.ClipDescription; import android.content.ClipboardManager; import android.content.ContentResolver; import android.content.Context; @@ -95,7 +91,38 @@ public void platformPlugin_getClipboardDataIsNonNullWhenPlainTextCopied() throws when(mockActivity.getWindow()).thenReturn(fakeWindow); PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel); setUpForTextClipboardTests(mockActivity); + + // Primary clip contains non-text media. ClipData clip = ClipData.newPlainText("label", "Text"); + assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + clipboardManager.setPrimaryClip(clip); + assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); + } + + @Test + public void platformPlugin_getClipboardDataIsNonNullWhenIOExceptionThrown() throws IOException { + View fakeDecorView = mock(View.class); + Window fakeWindow = mock(Window.class); + Activity mockActivity = mock(Activity.class); + when(fakeWindow.getDecorView()).thenReturn(fakeDecorView); + when(mockActivity.getWindow()).thenReturn(fakeWindow); + PlatformPlugin platformPlugin = new PlatformPlugin(mockActivity, mockPlatformChannel); + setUpForTextClipboardTests(mockActivity); + ContentResolver contentResolver = mock(ContentResolver.class); + ClipData.Item mockItem = mock(ClipData.Item.class); + Uri mockUri = mock(Uri.class); + AssetFileDescriptor mockAssetFileDescriptor = mock(AssetFileDescriptor.class); + + when(mockActivity.getContentResolver()).thenReturn(contentResolver); + when(mockUri.getScheme()).thenReturn("content"); + when(mockItem.getText()).thenReturn(null); + when(mockItem.getUri()).thenReturn(mockUri); + when(contentResolver.openTypedAssetFileDescriptor(any(Uri.class), any(), any())) + .thenReturn(mockAssetFileDescriptor); + when(mockItem.coerceToText(mockActivity)).thenReturn("something non-null"); + doThrow(new IOException()).when(mockAssetFileDescriptor).close(); + + ClipData clip = new ClipData("label", new String[0], mockItem); assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); clipboardManager.setPrimaryClip(clip); @@ -190,25 +217,6 @@ public void platformPlugin_getClipboardDataIsNullWhenItemHasNoTextNorUri() throw clipboardManager.setPrimaryClip(clip); assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); - - // Still return text when the AssetFileDescriptor throws an IOException. - when(fakeActivity.getContentResolver()).thenReturn(contentResolver); - ClipDescription clipDescription = - new ClipDescription( - "label", - new String[] { - ClipDescription.MIMETYPE_TEXT_PLAIN, ClipDescription.MIMETYPE_TEXT_URILIST - }); - ClipData.Item clipDataItem = new ClipData.Item("Text", null, uri); - ClipData clipData = new ClipData(clipDescription, clipDataItem); - clipboardManager.setPrimaryClip(clipData); - AssetFileDescriptor fakeAssetFileDescriptor = mock(AssetFileDescriptor.class); - doReturn(fakeAssetFileDescriptor) - .when(contentResolver) - .openTypedAssetFileDescriptor(eq(uri), anyString(), eq(null)); - doThrow(new IOException()).when(fakeAssetFileDescriptor).close(); - assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat)); - verify(fakeAssetFileDescriptor).close(); } @SuppressWarnings("deprecation")