diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fa83b9..95adeb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## 1.3.7 + +**Rollback - Breaking change:** Re-adds runtime verification for external storage read permission. Don't forget to add the permission to the `AndroidManifest.xml` file as well. More info in the README file. +**Bug fix:** Fixes a crash that could cause some Android API to crash when multiple files were selected from external storage. + +## 1.3.6 + +**Improvements** + * Removes the Android write permissions requirement. + * Minor improvements in the example app. + * Now the exceptions are rethrown in case the user wants to handle them, despite that already being done in the plugin call. + ## 1.3.5 **Bug fix:** Fixes an issue that could prevent users to pick files from the iCloud Drive app, on versions below iOS 11. diff --git a/README.md b/README.md index 70604c2..ff9835b 100644 --- a/README.md +++ b/README.md @@ -11,15 +11,16 @@ A package that allows you to use a native file explorer to pick single or multip First, add *file_picker* as a dependency in [your pubspec.yaml file](https://flutter.io/platform-plugins/). ``` -file_picker: ^1.3.5 +file_picker: ^1.3.7 ``` ### Android -Add + +Add ``` - - + ``` -before `` to your app's `AndroidManifest.xml` file. This is required due to file caching when a path is required from a remote file (eg. Google Drive). +before `` to your app's `AndroidManifest.xml` file. This is required to access files from external storage. + ### iOS Based on the location of the files that you are willing to pick paths, you may need to add some keys to your iOS app's _Info.plist_ file, located in `/ios/Runner/Info.plist`: @@ -108,6 +109,8 @@ String someFilePath = filePaths['fileName']; // Access a file path directly by i * [X] Load path from **any** * [X] Create a `File` object from **any** selected file +If you have any feature that you want to see in this package, please add it [here](https://github.com/miguelpruivo/plugins_flutter_file_picker/issues/99). 🎉 + ## Demo App ![Demo](https://github.com/miguelpruivo/plugins_flutter_file_picker/blob/master/example/example.gif) diff --git a/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerPlugin.java b/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerPlugin.java index cced080..ced49cd 100644 --- a/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerPlugin.java +++ b/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerPlugin.java @@ -30,7 +30,7 @@ public class FilePickerPlugin implements MethodCallHandler { private static final int REQUEST_CODE = (FilePickerPlugin.class.hashCode() + 43) & 0x0000ffff; private static final int PERM_CODE = (FilePickerPlugin.class.hashCode() + 50) & 0x0000ffff; private static final String TAG = "FilePicker"; - private static final String permission = Manifest.permission.WRITE_EXTERNAL_STORAGE; + private static final String permission = Manifest.permission.READ_EXTERNAL_STORAGE; private static Result result; private static Registrar instance; @@ -63,6 +63,9 @@ public class FilePickerPlugin implements MethodCallHandler { while(currentItem < count) { final Uri currentUri = data.getClipData().getItemAt(currentItem).getUri(); String path = FileUtils.getPath(currentUri, instance.context()); + if(path == null) { + path = FileUtils.getUriFromRemote(instance.activeContext(), currentUri, result); + } paths.add(path); Log.i(TAG, "[MultiFilePick] File #" + currentItem + " - URI: " +currentUri.getPath()); currentItem++; @@ -174,21 +177,26 @@ public class FilePickerPlugin implements MethodCallHandler { Intent intent; if (checkPermission()) { - if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - intent = new Intent(Intent.ACTION_PICK); - } else { - intent = new Intent(Intent.ACTION_GET_CONTENT); - } + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + intent = new Intent(Intent.ACTION_PICK); + } else { + intent = new Intent(Intent.ACTION_GET_CONTENT); + } - Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath() + File.separator); - intent.setDataAndType(uri, type); - intent.setType(type); - intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, isMultipleSelection); - intent.addCategory(Intent.CATEGORY_OPENABLE); + Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath() + File.separator); + intent.setDataAndType(uri, type); + intent.setType(type); + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, isMultipleSelection); + intent.addCategory(Intent.CATEGORY_OPENABLE); - instance.activity().startActivityForResult(intent, REQUEST_CODE); + if (intent.resolveActivity(instance.activity().getPackageManager()) != null) { + instance.activity().startActivityForResult(intent, REQUEST_CODE); + } else { + Log.e(TAG, "Can't find a valid activity to handle the request. Make sure you've a file explorer installed."); + result.error(TAG, "Can't handle the provided file type.", null); + } } else { - requestPermission(); + requestPermission(); } } diff --git a/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java b/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java index 077653e..e66185f 100644 --- a/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java +++ b/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java @@ -19,10 +19,6 @@ import java.io.InputStream; import io.flutter.plugin.common.MethodChannel; -/** - * Credits to NiRRaNjAN from utils extracted of in.gauriinfotech.commons;. - **/ - public class FileUtils { private static final String TAG = "FilePickerUtils"; @@ -109,9 +105,11 @@ public class FileUtils { } } else if ("content".equalsIgnoreCase(uri.getScheme())) { Log.e(TAG, "NO DOCUMENT URI - CONTENT"); - if (isGooglePhotosUri(uri)) + if (isGooglePhotosUri(uri)) { return uri.getLastPathSegment(); - + } else if (isDropBoxUri(uri)) { + return null; + } return getDataColumn(context, uri, null, null); } else if ("file".equalsIgnoreCase(uri.getScheme())) { Log.e(TAG, "No DOCUMENT URI - FILE"); @@ -177,11 +175,12 @@ public class FileUtils { public static String getUriFromRemote(Context context, Uri uri, MethodChannel.Result result) { + Log.i(TAG, "Caching file from remote/external URI"); FileOutputStream fos = null; - String cloudFile = context.getCacheDir().getAbsolutePath() + "/" + FileUtils.getFileName(uri, context); + String externalFile = context.getCacheDir().getAbsolutePath() + "/" + FileUtils.getFileName(uri, context); try { - fos = new FileOutputStream(cloudFile); + fos = new FileOutputStream(externalFile); try { BufferedOutputStream out = new BufferedOutputStream(fos); InputStream in = context.getContentResolver().openInputStream(uri); @@ -208,10 +207,13 @@ public class FileUtils { return null; } - Log.i(TAG, "Remote file loaded and cached at:" + cloudFile); - return cloudFile; + Log.i(TAG, "File loaded and cached at:" + externalFile); + return externalFile; } + private static boolean isDropBoxUri(Uri uri) { + return "com.dropbox.android.FileCache".equals(uri.getAuthority()); + } private static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index ce5983a..3f815b3 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -22,7 +22,6 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.mr.flutter.plugin.filepickerexample" minSdkVersion 16 targetSdkVersion 28 @@ -33,7 +32,6 @@ android { buildTypes { release { - // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug } diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 8755b71..94e4412 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -7,7 +7,7 @@ to allow setting breakpoints, to provide hot reload, etc. --> - +