see changelog (1.3.7) (#102)

This commit is contained in:
Miguel Ruivo 2019-06-24 11:10:19 +01:00
parent b72483dc66
commit c8f5d4fe69
11 changed files with 119 additions and 119 deletions

View File

@ -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.

View File

@ -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
```
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
```
before `<application>` 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 `<application>` 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 `<project root>/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)

View File

@ -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();
}
}

View File

@ -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());

View File

@ -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
}

View File

@ -7,7 +7,7 @@
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.

View File

@ -31,10 +31,12 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
try {
if (_multiPick) {
_path = null;
_paths = await FilePicker.getMultiFilePath(type: _pickingType, fileExtension: _extension);
_paths = await FilePicker.getMultiFilePath(
type: _pickingType, fileExtension: _extension);
} else {
_paths = null;
_path = await FilePicker.getFilePath(type: _pickingType, fileExtension: _extension);
_path = await FilePicker.getFilePath(
type: _pickingType, fileExtension: _extension);
}
} on PlatformException catch (e) {
print("Unsupported operation" + e.toString());
@ -42,7 +44,9 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
if (!mounted) return;
setState(() {
_fileName = _path != null ? _path.split('/').last : _paths != null ? _paths.keys.toString() : '...';
_fileName = _path != null
? _path.split('/').last
: _paths != null ? _paths.keys.toString() : '...';
});
}
}
@ -95,14 +99,15 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
}
})),
),
ConstrainedBox(
new ConstrainedBox(
constraints: BoxConstraints.tightFor(width: 100.0),
child: _pickingType == FileType.CUSTOM
? new TextFormField(
maxLength: 15,
autovalidate: true,
controller: _controller,
decoration: InputDecoration(labelText: 'File extension'),
decoration:
InputDecoration(labelText: 'File extension'),
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.none,
validator: (value) {
@ -112,6 +117,7 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
return 'Invalid format';
}
_hasValidMime = true;
return null;
},
)
: new Container(),
@ -119,8 +125,10 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
new ConstrainedBox(
constraints: BoxConstraints.tightFor(width: 200.0),
child: new SwitchListTile.adaptive(
title: new Text('Pick multiple files', textAlign: TextAlign.right),
onChanged: (bool value) => setState(() => _multiPick = value),
title: new Text('Pick multiple files',
textAlign: TextAlign.right),
onChanged: (bool value) =>
setState(() => _multiPick = value),
value: _multiPick,
),
),
@ -132,30 +140,40 @@ class _FilePickerDemoState extends State<FilePickerDemo> {
),
),
new Builder(
builder: (BuildContext context) => new Container(
padding: const EdgeInsets.only(bottom: 30.0),
height: MediaQuery.of(context).size.height * 0.50,
child: new Scrollbar(
child: _path != null || _paths != null
? new ListView.separated(
itemCount: _paths != null && _paths.isNotEmpty ? _paths.length : 1,
itemBuilder: (BuildContext context, int index) {
final bool isMultiPath = _paths != null && _paths.isNotEmpty;
final String name = 'File $index: ' + (isMultiPath ? _paths.keys.toList()[index] : _fileName ?? '...');
final path = isMultiPath ? _paths.values.toList()[index].toString() : _path;
builder: (BuildContext context) =>
_path != null || _paths != null
? new Container(
padding: const EdgeInsets.only(bottom: 30.0),
height: MediaQuery.of(context).size.height * 0.50,
child: new Scrollbar(
child: new ListView.separated(
itemCount: _paths != null && _paths.isNotEmpty
? _paths.length
: 1,
itemBuilder: (BuildContext context, int index) {
final bool isMultiPath =
_paths != null && _paths.isNotEmpty;
final String name = 'File $index: ' +
(isMultiPath
? _paths.keys.toList()[index]
: _fileName ?? '...');
final path = isMultiPath
? _paths.values.toList()[index].toString()
: _path;
return new ListTile(
title: new Text(
name,
),
subtitle: new Text(path),
);
},
separatorBuilder: (BuildContext context, int index) => new Divider(),
)
: new Container(),
),
),
return new ListTile(
title: new Text(
name,
),
subtitle: new Text(path),
);
},
separatorBuilder:
(BuildContext context, int index) =>
new Divider(),
)),
)
: new Container(),
),
],
),

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.idea" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart Packages" level="project" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
</component>
</module>

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android" name="Android">
<configuration>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="GEN_FOLDER_RELATIVE_PATH_APT" value="/android/gen" />
<option name="GEN_FOLDER_RELATIVE_PATH_AIDL" value="/android/gen" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/android/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/android/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/android/assets" />
<option name="LIBS_FOLDER_RELATIVE_PATH" value="/android/libs" />
<option name="PROGUARD_LOGS_FOLDER_RELATIVE_PATH" value="/android/proguard_logs" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/android">
<sourceFolder url="file://$MODULE_DIR$/android/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/android/gen" isTestSource="false" generated="true" />
</content>
<content url="file://$MODULE_DIR$/example/android">
<sourceFolder url="file://$MODULE_DIR$/example/android/app/src/main/java" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Flutter for Android" level="project" />
</component>
</module>

View File

@ -23,7 +23,8 @@ class FilePicker {
/// A [fileExtension] can be provided to filter the picking results.
/// If provided, it will be use the `FileType.CUSTOM` for that [fileExtension].
/// If not, `FileType.ANY` will be used and any combination of files can be multi picked at once.
static Future<Map<String, String>> getMultiFilePath({FileType type = FileType.ANY, String fileExtension}) async =>
static Future<Map<String, String>> getMultiFilePath(
{FileType type = FileType.ANY, String fileExtension}) async =>
await _getPath(_handleType(type, fileExtension), true);
/// Returns an absolute file path from the calling platform.
@ -31,15 +32,18 @@ class FilePicker {
/// A [type] must be provided to filter the picking results.
/// Can be used a custom file type with `FileType.CUSTOM`. A [fileExtension] must be provided (e.g. PDF, SVG, etc.)
/// Defaults to `FileType.ANY` which will display all file types.
static Future<String> getFilePath({FileType type = FileType.ANY, String fileExtension}) async =>
static Future<String> getFilePath(
{FileType type = FileType.ANY, String fileExtension}) async =>
await _getPath(_handleType(type, fileExtension), false);
/// Returns a `File` object from the selected file path.
///
/// This is an utility method that does the same of `getFilePath()` but saving some boilerplate if
/// you are planing to create a `File` for the returned path.
static Future<File> getFile({FileType type = FileType.ANY, String fileExtension}) async {
final String filePath = await _getPath(_handleType(type, fileExtension), false);
static Future<File> getFile(
{FileType type = FileType.ANY, String fileExtension}) async {
final String filePath =
await _getPath(_handleType(type, fileExtension), false);
return filePath != null ? File(filePath) : null;
}
@ -50,15 +54,18 @@ class FilePicker {
if (result is String) {
result = [result];
}
return Map<String, String>.fromIterable(result, key: (path) => path.split('/').last, value: (path) => path);
return Map<String, String>.fromIterable(result,
key: (path) => path.split('/').last, value: (path) => path);
}
return result;
} on PlatformException catch (e) {
print('[$_tag] Platform exception: ' + e.toString());
print('[$_tag] Platform exception: $e');
rethrow;
} catch (e) {
print('[$_tag] Unsupported operation. Method not found. The exception thrown was: ' + e.toString());
print(
'[$_tag] Unsupported operation. Method not found. The exception thrown was: $e');
rethrow;
}
return null;
}
static String _handleType(FileType type, String fileExtension) {

View File

@ -2,7 +2,8 @@ name: file_picker
description: A package that allows you to use a native file explorer to pick single or multiple absolute file paths, with extensions filtering support.
author: Miguel Ruivo <miguel@miguelruivo.com>
homepage: https://github.com/miguelpruivo/plugins_flutter_file_picker
version: 1.3.5
version: 1.3.7
dependencies:
flutter: