diff --git a/super_clipboard/example/android/build.gradle b/super_clipboard/example/android/build.gradle index 9d273445..586557f0 100644 --- a/super_clipboard/example/android/build.gradle +++ b/super_clipboard/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() diff --git a/super_native_extensions/android/src/main/java/com/superlist/super_native_extensions/ClipDataHelper.java b/super_native_extensions/android/src/main/java/com/superlist/super_native_extensions/ClipDataHelper.java index 88e9cdec..9866f7c0 100644 --- a/super_native_extensions/android/src/main/java/com/superlist/super_native_extensions/ClipDataHelper.java +++ b/super_native_extensions/android/src/main/java/com/superlist/super_native_extensions/ClipDataHelper.java @@ -24,6 +24,7 @@ import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Objects; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -36,6 +37,8 @@ public final class ClipDataHelper { static final String typeTextHtml = "text/html"; static final String typeUriList = "text/uri-list"; + private static final String TAG = "SuperClipboard" + ClipDataHelper.class.getSimpleName(); + public String[] getFormats(ClipData data, int index, Context context) { if (index < data.getItemCount()) { return getFormats(data.getItemAt(index), context); @@ -88,8 +91,22 @@ String[] getFormats(ClipData.Item item, Context context) { if (item.getText() != null) { res.add(typeTextPlain); } + + ContentResolver contentResolver = context.getContentResolver(); if (item.getUri() != null) { - String[] types = context.getContentResolver().getStreamTypes(item.getUri(), "*/*"); + try { + readUriOrThrow( + context, + item.getUri() + ); + } catch (SecurityException e) { + Log.e(TAG, "Could not read the URI " + contentResolver.getType(item.getUri()) + " due to app lifecycle changes: " + e); + return res.toArray(new String[0]); + } catch (IOException e) { + Log.e(TAG, "Could not read the URI " + contentResolver.getType(item.getUri()) + " due to IO error: " + e); + return res.toArray(new String[0]); + } + String[] types = contentResolver.getStreamTypes(item.getUri(), "*/*"); if (types != null) { for (String type : types) { if (!res.contains(type)) { @@ -97,7 +114,7 @@ String[] getFormats(ClipData.Item item, Context context) { } } } else { - String type = context.getContentResolver().getType(item.getUri()); + String type = contentResolver.getType(item.getUri()); if (type != null) { res.add(type); } else { @@ -108,6 +125,20 @@ String[] getFormats(ClipData.Item item, Context context) { return res.toArray(new String[0]); } + /** + * A method to see if any exceptions can occur + * before start using the Uri. + *

+ * The app can lose access to the [Uri] due to lifecycle changes. + * + * @throws SecurityException When the app loses access to the [Uri] due to app lifecycle changes + * or app restart. + * @throws FileNotFoundException Could be thrown when the [Uri] is no longer on the clipboard. + * */ + private void readUriOrThrow(Context context, Uri uri) throws SecurityException, IOException { + Objects.requireNonNull(context.getContentResolver().openInputStream(uri)).close();; + } + CharSequence getText(ClipData.Item item, Context context) { return coerceToPlainText(item, context); }