From 1916a9accec48c150821451e6bc4f7dcb625be1c Mon Sep 17 00:00:00 2001 From: Miguel Ruivo Date: Mon, 7 Sep 2020 20:06:18 +0100 Subject: [PATCH 1/6] Refactors plugin to use single package with addition of PlatformFile and FilePickerResult classes --- .../plugin/filepicker/FilePickerDelegate.java | 11 +- file_picker/example/README.md | 16 ++ file_picker/example/android/app/build.gradle | 2 +- .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 2 +- .../filepickerexample/MainActivity.java | 2 +- .../mr/flutter/plugin/example/MainActivity.kt | 6 + .../app/src/profile/AndroidManifest.xml | 7 + .../example/ios/Flutter/.last_build_id | 1 + .../ios/Runner.xcodeproj/project.pbxproj | 2 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../example/ios/Runner/AppDelegate.swift | 13 ++ .../ios/Runner/Runner-Bridging-Header.h | 1 + .../lib/generated_plugin_registrant.dart | 16 ++ .../example/lib/src/file_picker_demo.dart | 169 ++++++++---------- file_picker/example/test/widget_test.dart | 30 ++++ file_picker/example/web/favicon.png | Bin 0 -> 917 bytes file_picker/example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes file_picker/example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes file_picker/example/web/index.html | 33 ++++ file_picker/example/web/manifest.json | 23 +++ file_picker/ios/Classes/FilePickerPlugin.m | 11 +- file_picker/lib/file_picker.dart | 134 +------------- file_picker/lib/src/file_picker.dart | 75 ++++++++ file_picker/lib/src/file_picker_io.dart | 86 +++++++++ file_picker/lib/src/file_picker_result.dart | 26 +++ file_picker/lib/src/file_picker_web.dart | 85 +++++++++ file_picker/lib/src/platform_file.dart | 40 +++++ file_picker/pubspec.yaml | 10 +- file_picker_platform_interface/CHANGELOG.md | 4 + file_picker_platform_interface/README.md | 5 + file_picker_platform_interface/pubspec.yaml | 2 +- file_picker_web/CHANGELOG.md | 4 + file_picker_web/README.md | 4 + file_picker_web/pubspec.yaml | 4 +- 36 files changed, 595 insertions(+), 252 deletions(-) create mode 100644 file_picker/example/README.md create mode 100644 file_picker/example/android/app/src/debug/AndroidManifest.xml create mode 100644 file_picker/example/android/app/src/main/kotlin/com/mr/flutter/plugin/example/MainActivity.kt create mode 100644 file_picker/example/android/app/src/profile/AndroidManifest.xml create mode 100644 file_picker/example/ios/Flutter/.last_build_id create mode 100644 file_picker/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 file_picker/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 file_picker/example/ios/Runner/AppDelegate.swift create mode 100644 file_picker/example/ios/Runner/Runner-Bridging-Header.h create mode 100644 file_picker/example/lib/generated_plugin_registrant.dart create mode 100644 file_picker/example/test/widget_test.dart create mode 100644 file_picker/example/web/favicon.png create mode 100644 file_picker/example/web/icons/Icon-192.png create mode 100644 file_picker/example/web/icons/Icon-512.png create mode 100644 file_picker/example/web/index.html create mode 100644 file_picker/example/web/manifest.json create mode 100644 file_picker/lib/src/file_picker.dart create mode 100644 file_picker/lib/src/file_picker_io.dart create mode 100644 file_picker/lib/src/file_picker_result.dart create mode 100644 file_picker/lib/src/file_picker_web.dart create mode 100644 file_picker/lib/src/platform_file.dart diff --git a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java index b89cdb8..27b70fa 100644 --- a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java +++ b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java @@ -18,6 +18,7 @@ import androidx.core.app.ActivityCompat; import java.io.File; import java.util.ArrayList; +import java.util.Arrays; import io.flutter.plugin.common.EventChannel; import io.flutter.plugin.common.MethodChannel; @@ -100,11 +101,9 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener Log.i(FilePickerDelegate.TAG, "[MultiFilePick] File #" + currentItem + " - URI: " + currentUri.getPath()); currentItem++; } - if (paths.size() > 1) { - finishWithSuccess(paths); - } else { - finishWithSuccess(paths.get(0)); - } + + finishWithSuccess(paths); + } else if (data.getData() != null) { Uri uri = data.getData(); String fullPath; @@ -125,7 +124,7 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener if (fullPath != null) { Log.i(FilePickerDelegate.TAG, "Absolute file path:" + fullPath); - finishWithSuccess(fullPath); + finishWithSuccess(Arrays.asList(fullPath)); } else { finishWithError("unknown_path", "Failed to retrieve path."); } diff --git a/file_picker/example/README.md b/file_picker/example/README.md new file mode 100644 index 0000000..a135626 --- /dev/null +++ b/file_picker/example/README.md @@ -0,0 +1,16 @@ +# example + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/file_picker/example/android/app/build.gradle b/file_picker/example/android/app/build.gradle index ae8e6ae..97913c1 100644 --- a/file_picker/example/android/app/build.gradle +++ b/file_picker/example/android/app/build.gradle @@ -22,7 +22,7 @@ android { } defaultConfig { - applicationId "com.mr.flutter.plugin.filepickerexample" + applicationId "com.mr.flutter.plugin.filepicker.example" minSdkVersion 16 targetSdkVersion 29 versionCode 1 diff --git a/file_picker/example/android/app/src/debug/AndroidManifest.xml b/file_picker/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..66063d7 --- /dev/null +++ b/file_picker/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/file_picker/example/android/app/src/main/AndroidManifest.xml b/file_picker/example/android/app/src/main/AndroidManifest.xml index 91966c1..6365b83 100644 --- a/file_picker/example/android/app/src/main/AndroidManifest.xml +++ b/file_picker/example/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ + + diff --git a/file_picker/example/ios/Flutter/.last_build_id b/file_picker/example/ios/Flutter/.last_build_id new file mode 100644 index 0000000..0b85c88 --- /dev/null +++ b/file_picker/example/ios/Flutter/.last_build_id @@ -0,0 +1 @@ +0915ff87e81a3bfb122df4ced418a2b0 \ No newline at end of file diff --git a/file_picker/example/ios/Runner.xcodeproj/project.pbxproj b/file_picker/example/ios/Runner.xcodeproj/project.pbxproj index 97a44e0..039ac22 100644 --- a/file_picker/example/ios/Runner.xcodeproj/project.pbxproj +++ b/file_picker/example/ios/Runner.xcodeproj/project.pbxproj @@ -310,7 +310,6 @@ /* Begin XCBuildConfiguration section */ 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -366,7 +365,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; diff --git a/file_picker/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/file_picker/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/file_picker/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/file_picker/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/file_picker/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/file_picker/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/file_picker/example/ios/Runner/AppDelegate.swift b/file_picker/example/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..70693e4 --- /dev/null +++ b/file_picker/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/file_picker/example/ios/Runner/Runner-Bridging-Header.h b/file_picker/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/file_picker/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/file_picker/example/lib/generated_plugin_registrant.dart b/file_picker/example/lib/generated_plugin_registrant.dart new file mode 100644 index 0000000..daa72e8 --- /dev/null +++ b/file_picker/example/lib/generated_plugin_registrant.dart @@ -0,0 +1,16 @@ +// +// Generated file. Do not edit. +// + +// ignore: unused_import +import 'dart:ui'; + +import 'package:file_picker/src/file_picker_web.dart'; + +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; + +// ignore: public_member_api_docs +void registerPlugins(PluginRegistry registry) { + FilePickerWeb.registerWith(registry.registrarFor(FilePickerWeb)); + registry.registerMessageHandler(); +} diff --git a/file_picker/example/lib/src/file_picker_demo.dart b/file_picker/example/lib/src/file_picker_demo.dart index 47ac026..99f61ee 100644 --- a/file_picker/example/lib/src/file_picker_demo.dart +++ b/file_picker/example/lib/src/file_picker_demo.dart @@ -5,19 +5,18 @@ import 'package:file_picker/file_picker.dart'; class FilePickerDemo extends StatefulWidget { @override - _FilePickerDemoState createState() => new _FilePickerDemoState(); + _FilePickerDemoState createState() => _FilePickerDemoState(); } class _FilePickerDemoState extends State { final GlobalKey _scaffoldKey = GlobalKey(); String _fileName; - String _path; - Map _paths; + List _paths; String _extension; bool _loadingPath = false; bool _multiPick = false; FileType _pickingType = FileType.any; - TextEditingController _controller = new TextEditingController(); + TextEditingController _controller = TextEditingController(); @override void initState() { @@ -28,97 +27,84 @@ class _FilePickerDemoState extends State { void _openFileExplorer() async { setState(() => _loadingPath = true); try { - if (_multiPick) { - _path = null; - _paths = await FilePicker.getMultiFilePath( - type: _pickingType, - allowedExtensions: (_extension?.isNotEmpty ?? false) - ? _extension?.replaceAll(' ', '')?.split(',') - : null, - ); - } else { - _paths = null; - _path = await FilePicker.getFilePath( - type: _pickingType, - allowedExtensions: (_extension?.isNotEmpty ?? false) - ? _extension?.replaceAll(' ', '')?.split(',') - : null, - ); - } + _paths = (await FilePicker.instance.pickFiles( + type: _pickingType, + allowMultiple: _multiPick, + allowedExtensions: (_extension?.isNotEmpty ?? false) ? _extension?.replaceAll(' ', '')?.split(',') : null, + )) + ?.files; } on PlatformException catch (e) { print("Unsupported operation" + e.toString()); + } catch (ex) { + print(ex); } if (!mounted) return; setState(() { _loadingPath = false; - _fileName = _path != null - ? _path.split('/').last - : _paths != null ? _paths.keys.toString() : '...'; + _fileName = _paths != null ? _paths.map((e) => e.name).toString() : '...'; }); } void _clearCachedFiles() { - FilePicker.clearTemporaryFiles().then((result) { + FilePicker.instance.clearTemporaryFiles().then((result) { _scaffoldKey.currentState.showSnackBar( SnackBar( backgroundColor: result ? Colors.green : Colors.red, - content: Text((result - ? 'Temporary files removed with success.' - : 'Failed to clean temporary files')), + content: Text((result ? 'Temporary files removed with success.' : 'Failed to clean temporary files')), ), ); }); } void _selectFolder() { - FilePicker.getDirectoryPath().then((value) { - setState(() => _path = value); + FilePicker.instance.getDirectoryPath().then((value) { + setState(() => _paths = [value]); }); } @override Widget build(BuildContext context) { - return new MaterialApp( - home: new Scaffold( + return MaterialApp( + home: Scaffold( key: _scaffoldKey, - appBar: new AppBar( + appBar: AppBar( title: const Text('File Picker example app'), ), - body: new Center( - child: new Padding( + body: Center( + child: Padding( padding: const EdgeInsets.only(left: 10.0, right: 10.0), - child: new SingleChildScrollView( - child: new Column( + child: SingleChildScrollView( + child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - new Padding( + Padding( padding: const EdgeInsets.only(top: 20.0), - child: new DropdownButton( - hint: new Text('LOAD PATH FROM'), + child: DropdownButton( + hint: Text('LOAD PATH FROM'), value: _pickingType, items: [ - new DropdownMenuItem( - child: new Text('FROM AUDIO'), + DropdownMenuItem( + child: Text('FROM AUDIO'), value: FileType.audio, ), - new DropdownMenuItem( - child: new Text('FROM IMAGE'), + DropdownMenuItem( + child: Text('FROM IMAGE'), value: FileType.image, ), - new DropdownMenuItem( - child: new Text('FROM VIDEO'), + DropdownMenuItem( + child: Text('FROM VIDEO'), value: FileType.video, ), - new DropdownMenuItem( - child: new Text('FROM MEDIA'), + DropdownMenuItem( + child: Text('FROM MEDIA'), value: FileType.media, ), - new DropdownMenuItem( - child: new Text('FROM ANY'), + DropdownMenuItem( + child: Text('FROM ANY'), value: FileType.any, ), - new DropdownMenuItem( - child: new Text('CUSTOM FORMAT'), + DropdownMenuItem( + child: Text('CUSTOM FORMAT'), value: FileType.custom, ), ], @@ -129,87 +115,76 @@ class _FilePickerDemoState extends State { } })), ), - new ConstrainedBox( - constraints: BoxConstraints.tightFor(width: 100.0), + ConstrainedBox( + constraints: const BoxConstraints.tightFor(width: 100.0), child: _pickingType == FileType.custom - ? new TextFormField( + ? TextFormField( maxLength: 15, autovalidate: true, controller: _controller, - decoration: - InputDecoration(labelText: 'File extension'), + decoration: InputDecoration(labelText: 'File extension'), keyboardType: TextInputType.text, textCapitalization: TextCapitalization.none, ) - : new Container(), + : const SizedBox(), ), - 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), + ConstrainedBox( + constraints: const BoxConstraints.tightFor(width: 200.0), + child: SwitchListTile.adaptive( + title: Text('Pick multiple files', textAlign: TextAlign.right), + onChanged: (bool value) => setState(() => _multiPick = value), value: _multiPick, ), ), - new Padding( + Padding( padding: const EdgeInsets.only(top: 50.0, bottom: 20.0), child: Column( children: [ - new RaisedButton( + RaisedButton( onPressed: () => _openFileExplorer(), - child: new Text("Open file picker"), + child: Text("Open file picker"), ), - new RaisedButton( + RaisedButton( onPressed: () => _selectFolder(), - child: new Text("Pick folder"), + child: Text("Pick folder"), ), - new RaisedButton( + RaisedButton( onPressed: () => _clearCachedFiles(), - child: new Text("Clear temporary files"), + child: Text("Clear temporary files"), ), ], ), ), - new Builder( + Builder( builder: (BuildContext context) => _loadingPath ? Padding( padding: const EdgeInsets.only(bottom: 10.0), - child: const CircularProgressIndicator()) - : _path != null || _paths != null - ? new Container( + child: const CircularProgressIndicator(), + ) + : _paths != null + ? 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, + child: Scrollbar( + child: 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; + final bool isMultiPath = _paths != null && _paths.isNotEmpty; + final String name = + 'File $index: ' + (isMultiPath ? _paths.map((e) => e.name).toList()[index] : _fileName ?? '...'); + final path = _paths.map((e) => e.path).toList()[index].toString(); - return new ListTile( - title: new Text( + return ListTile( + title: Text( name, ), - subtitle: new Text(path), + subtitle: Text(path), ); }, - separatorBuilder: - (BuildContext context, int index) => - new Divider(), + separatorBuilder: (BuildContext context, int index) => const Divider(), )), ) - : new Container(), + : const SizedBox(), ), ], ), diff --git a/file_picker/example/test/widget_test.dart b/file_picker/example/test/widget_test.dart new file mode 100644 index 0000000..747db1d --- /dev/null +++ b/file_picker/example/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility that Flutter provides. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:example/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/file_picker/example/web/favicon.png b/file_picker/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/file_picker/example/web/icons/Icon-192.png b/file_picker/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/file_picker/example/web/icons/Icon-512.png b/file_picker/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/file_picker/example/web/index.html b/file_picker/example/web/index.html new file mode 100644 index 0000000..9b7a438 --- /dev/null +++ b/file_picker/example/web/index.html @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + example + + + + + + + + diff --git a/file_picker/example/web/manifest.json b/file_picker/example/web/manifest.json new file mode 100644 index 0000000..c638001 --- /dev/null +++ b/file_picker/example/web/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "example", + "short_name": "example", + "start_url": ".", + "display": "minimal-ui", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/file_picker/ios/Classes/FilePickerPlugin.m b/file_picker/ios/Classes/FilePickerPlugin.m index ab65a33..72738aa 100644 --- a/file_picker/ios/Classes/FilePickerPlugin.m +++ b/file_picker/ios/Classes/FilePickerPlugin.m @@ -253,7 +253,7 @@ - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url{ [self.documentPickerController dismissViewControllerAnimated:YES completion:nil]; NSString * path = (NSString *)[url path]; - _result(path); + _result(@[path]); _result = nil; } @@ -267,12 +267,7 @@ didPickDocumentsAtURLs:(NSArray *)urls{ [self.documentPickerController dismissViewControllerAnimated:YES completion:nil]; NSArray * result = [FileUtils resolvePath:urls]; - - if([result count] > 1) { - _result(result); - } else { - _result([result objectAtIndex:0]); - } + _result(result); _result = nil; } @@ -323,7 +318,7 @@ didPickDocumentsAtURLs:(NSArray *)urls{ return; } - _result([pickedVideoUrl != nil ? pickedVideoUrl : pickedImageUrl path]); + _result(@[[pickedVideoUrl != nil ? pickedVideoUrl : pickedImageUrl path]]); _result = nil; } diff --git a/file_picker/lib/file_picker.dart b/file_picker/lib/file_picker.dart index 96c63de..7a75eb1 100644 --- a/file_picker/lib/file_picker.dart +++ b/file_picker/lib/file_picker.dart @@ -1,131 +1,5 @@ -import 'dart:async'; -import 'dart:io'; +library file_picker; -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; -import 'package:file_picker_platform_interface/method_channel_file_picker.dart'; - -export 'package:file_picker_platform_interface/file_picker_platform_interface.dart' - show FileType; - -final MethodChannelFilePicker _filePickerPlatform = FilePickerPlatform.instance; - -class FilePicker { - FilePicker._(); - - /// Returns an absolute file path from the calling platform. - /// - /// Extension filters are allowed with [FileType.custom], when used, make sure to provide a [List] - /// of `allowedExtensions` (e.g. [`pdf`, `svg`, `jpg`].). - /// - /// If you want to track picking status, for example, because some files may take some time to be - /// cached (particularly those picked from cloud providers), you may want to set [onFileLoading] handler - /// that will give you the current status of picking. - /// - /// If you plan on picking images/videos and don't want them to be compressed automatically by OS, - /// you should set `allowCompression` to [false]. Calling this on Android won't have any effect, as - /// it already provides you the original file (or integral copy). - /// - /// Defaults to [FileType.any] which will display all file types. - static Future getFilePath({ - FileType type = FileType.any, - List allowedExtensions, - Function(FilePickerStatus) onFileLoading, - bool allowCompression, - }) async => - await _filePickerPlatform.getFiles( - type: type, - allowedExtensions: allowedExtensions, - onFileLoading: onFileLoading, - allowCompression: allowCompression, - ); - - /// Returns an iterable [Map] where the `key` is the name of the file - /// and the `value` the path. - /// - /// A [List] with `allowedExtensions` can be provided to filter the allowed files to picked. - /// If provided, make sure you select [FileType.custom] as type. - /// - /// If you want to track picking status, for example, because some files may take some time to be - /// cached (particularly those picked from cloud providers), you may want to set `onFileLoading` handler - /// that will give you the current status of picking. - /// - /// If you plan on picking images/videos and don't want them to be compressed automatically by OS, - /// you should set `allowCompression` to [false]. Calling this on Android won't have any effect, as - /// it already provides you the original file (or integral copy). - /// - /// Defaults to `FileType.any`, which allows any combination of files to be multi selected at once. - static Future> getMultiFilePath({ - FileType type = FileType.any, - List allowedExtensions, - Function(FilePickerStatus) onFileLoading, - bool allowCompression, - }) async => - await _filePickerPlatform.getFiles( - type: type, - allowMultiple: true, - allowedExtensions: allowedExtensions, - onFileLoading: onFileLoading, - allowCompression: allowCompression, - ); - - /// 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 getFile({ - FileType type = FileType.any, - List allowedExtensions, - Function(FilePickerStatus) onFileLoading, - bool allowCompression, - }) async { - final String filePath = await _filePickerPlatform.getFiles( - type: type, - allowedExtensions: allowedExtensions, - onFileLoading: onFileLoading, - allowCompression: allowCompression, - ); - return filePath != null ? File(filePath) : null; - } - - /// Returns a [List] object from the selected files paths. - /// - /// This is an utility method that does the same of [getMultiFilePath] but saving some boilerplate if - /// you are planing to create a list of [File]`s for the returned paths. - static Future> getMultiFile({ - FileType type = FileType.any, - List allowedExtensions, - Function(FilePickerStatus) onFileLoading, - bool allowCompression, - }) async { - final Map paths = await _filePickerPlatform.getFiles( - type: type, - allowMultiple: true, - allowedExtensions: allowedExtensions, - onFileLoading: onFileLoading, - allowCompression: allowCompression, - ); - - return paths != null && paths.isNotEmpty - ? paths.values.map((path) => File(path)).toList() - : null; - } - - /// Selects a directory and returns its absolute path. - /// - /// On Android, this requires to be running on SDK 21 or above, else won't work. - /// Returns [null] if folder path couldn't be resolved. - static Future getDirectoryPath() async { - return _filePickerPlatform.getDirectoryPath(); - } - - /// Asks the underlying platform to remove any temporary files created by this plugin. - /// - /// This typically relates to cached files that are stored in the cache directory of - /// each platform and it isn't required to invoke this as the system should take care - /// of it whenever needed. However, this will force the cleanup if you want to manage those on your own. - /// - /// Returns [true] if the files were removed with success, [false] otherwise. - static Future clearTemporaryFiles() async { - return _filePickerPlatform.clearTemporaryFiles(); - } -} +export './src/file_picker.dart'; +export './src/platform_file.dart'; +export './src/file_picker_result.dart'; diff --git a/file_picker/lib/src/file_picker.dart b/file_picker/lib/src/file_picker.dart new file mode 100644 index 0000000..4876dec --- /dev/null +++ b/file_picker/lib/src/file_picker.dart @@ -0,0 +1,75 @@ +import 'dart:async'; + +import 'package:file_picker/file_picker.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'file_picker_io.dart'; +import 'file_picker_result.dart'; + +enum FileType { + any, + media, + image, + video, + audio, + custom, +} + +enum FilePickerStatus { + picking, + done, +} + +/// The interface that implementations of file_picker must implement. +/// +/// Platform implementations should extend this class rather than implement it as `file_picker` +/// does not consider newly added methods to be breaking changes. Extending this class +/// (using `extends`) ensures that the subclass will get the default implementation, while +/// platform implementations that `implements` this interface will be broken by newly added +/// [FilePicker] methods. +abstract class FilePicker extends PlatformInterface { + FilePicker() : super(token: _token); + + static final Object _token = Object(); + + static FilePicker _instance = FilePickerIO(); + + static FilePicker get instance => _instance; + + static set instance(FilePicker instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + /// Retrieves the file(s) from the underlying platform + /// + /// Default [type] set to `FileType.any` with [allowMultiple] set to `false` + /// Optionally, [allowedExtensions] might be provided (e.g. [`pdf`, `svg`, `jpg`].). + /// + /// If you want to track picking status, for example, because some files may take some time to be + /// cached (particularly those picked from cloud providers), you may want to set [onFileLoading] handler + /// that will give you the current status of picking. + Future pickFiles({ + FileType type = FileType.any, + List allowedExtensions, + Function(FilePickerStatus) onFileLoading, + bool allowCompression, + bool allowMultiple = false, + }) async => + throw UnimplementedError('pickFiles() has not been implemented.'); + + /// Asks the underlying platform to remove any temporary files created by this plugin. + /// + /// This typically relates to cached files that are stored in the cache directory of + /// each platform and it isn't required to invoke this as the system should take care + /// of it whenever needed. However, this will force the cleanup if you want to manage those on your own. + /// + /// Returns `true` if the files were removed with success, `false` otherwise. + Future clearTemporaryFiles() async => throw UnimplementedError('clearTemporaryFiles() has not been implemented.'); + + /// Selects a directory and returns its absolute path. + /// + /// On Android, this requires to be running on SDK 21 or above, else won't work. + /// Returns `null` if folder path couldn't be resolved. + Future getDirectoryPath() async => throw UnimplementedError('getDirectoryPath() has not been implemented.'); +} diff --git a/file_picker/lib/src/file_picker_io.dart b/file_picker/lib/src/file_picker_io.dart new file mode 100644 index 0000000..64493ab --- /dev/null +++ b/file_picker/lib/src/file_picker_io.dart @@ -0,0 +1,86 @@ +import 'dart:async'; + +import 'package:file_picker/file_picker.dart'; +import 'package:file_picker/src/platform_file.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'file_picker_result.dart'; + +const MethodChannel _channel = MethodChannel('miguelruivo.flutter.plugins.filepicker'); +const EventChannel _eventChannel = EventChannel('miguelruivo.flutter.plugins.filepickerevent'); + +/// An implementation of [FilePicker] that uses method channels. +class FilePickerIO extends FilePicker { + static const String _tag = 'MethodChannelFilePicker'; + static StreamSubscription _eventSubscription; + + @override + Future pickFiles({ + FileType type = FileType.any, + List allowedExtensions, + Function(FilePickerStatus) onFileLoading, + bool allowCompression, + bool allowMultiple = false, + }) => + _getPath(type, allowMultiple, allowCompression, allowedExtensions, onFileLoading); + + @override + Future clearTemporaryFiles() async => _channel.invokeMethod('clear'); + + @override + Future getDirectoryPath() async { + try { + String result = await _channel.invokeMethod('dir', {}); + if (result != null) { + return PlatformFile(path: result, isDirectory: true); + } + } on PlatformException catch (ex) { + if (ex.code == "unknown_path") { + print( + '[$_tag] Could not resolve directory path. Maybe it\'s a protected one or unsupported (such as Downloads folder). If you are on Android, make sure that you are on SDK 21 or above.'); + } + } + return null; + } + + Future _getPath( + FileType fileType, + bool allowMultipleSelection, + bool allowCompression, + List allowedExtensions, + Function(FilePickerStatus) onFileLoading, + ) async { + final String type = describeEnum(fileType); + if (type != 'custom' && (allowedExtensions?.isNotEmpty ?? false)) { + throw Exception('If you are using a custom extension filter, please use the FileType.custom instead.'); + } + try { + _eventSubscription?.cancel(); + if (onFileLoading != null) { + _eventSubscription = _eventChannel.receiveBroadcastStream().listen( + (data) => onFileLoading((data as bool) ? FilePickerStatus.picking : FilePickerStatus.done), + onError: (error) => throw Exception(error), + ); + } + + final List result = await _channel.invokeListMethod(type, { + 'allowMultipleSelection': allowMultipleSelection, + 'allowedExtensions': allowedExtensions, + 'allowCompression': allowCompression, + }); + + if (result == null) { + return null; + } + + return FilePickerResult(result.map((file) => PlatformFile(name: file.split('/').last, path: file)).toList()); + } on PlatformException catch (e) { + print('[$_tag] Platform exception: $e'); + rethrow; + } catch (e) { + print('[$_tag] Unsupported operation. Method not found. The exception thrown was: $e'); + rethrow; + } + } +} diff --git a/file_picker/lib/src/file_picker_result.dart b/file_picker/lib/src/file_picker_result.dart new file mode 100644 index 0000000..b3b5cfc --- /dev/null +++ b/file_picker/lib/src/file_picker_result.dart @@ -0,0 +1,26 @@ +import 'package:file_picker/src/platform_file.dart'; +import 'package:flutter/foundation.dart'; + +class FilePickerResult { + const FilePickerResult(this.files); + + /// Picked files. + final List files; + + /// If this pick contains only a single resource. + bool get isSinglePick => files.length == 1; + + /// The length of picked files. + int get count => files.length; + + /// A `List` containing all paths from picked files. + /// + /// This may or not be available and will typically reference cached copies of + /// original files (which can be accessed through its URI property). + /// + /// Only available on IO. Throws `UnsupportedError` on Web. + List get paths => files.map((file) => kIsWeb ? throw UnsupportedError('Unsupported on Web') : file.path).toList(); + + /// A `List` containing all names from picked files with its extensions. + List get names => files.map((file) => file.name).toList(); +} diff --git a/file_picker/lib/src/file_picker_web.dart b/file_picker/lib/src/file_picker_web.dart new file mode 100644 index 0000000..c4a336e --- /dev/null +++ b/file_picker/lib/src/file_picker_web.dart @@ -0,0 +1,85 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:html' as html; + +import 'package:file_picker/file_picker.dart'; +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; + +import 'file_picker_result.dart'; +import 'platform_file.dart'; + +class FilePickerWeb extends FilePicker { + FilePickerWeb._(); + static final FilePickerWeb platform = FilePickerWeb._(); + + static void registerWith(Registrar registrar) { + FilePicker.instance = platform; + } + + @override + Future pickFiles({ + FileType type = FileType.any, + List allowedExtensions, + bool allowMultiple = false, + Function(FilePickerStatus) onFileLoading, + bool allowCompression, + }) async { + final Completer> filesCompleter = Completer>(); + + String accept = _fileType(type, allowedExtensions); + html.InputElement uploadInput = html.FileUploadInputElement(); + uploadInput.draggable = true; + uploadInput.multiple = allowMultiple; + uploadInput.accept = accept; + uploadInput.click(); + + uploadInput.onChange.listen((e) { + final files = uploadInput.files; + final reader = html.FileReader(); + + List pickedFiles = []; + + reader.onLoadEnd.listen((e) { + pickedFiles.add( + PlatformFile( + name: uploadInput.value.replaceAll('\\', '/'), + bytes: Base64Decoder().convert(reader.result.toString().split(",").last), + ), + ); + + if (pickedFiles.length >= files.length) { + filesCompleter.complete(pickedFiles); + } + }); + + files.forEach((element) { + reader.readAsDataUrl(element); + }); + }); + return FilePickerResult(await filesCompleter.future); + } + + static String _fileType(FileType type, List allowedExtensions) { + switch (type) { + case FileType.any: + return ''; + + case FileType.audio: + return 'audio/*'; + + case FileType.image: + return 'image/*'; + + case FileType.video: + return 'video/*'; + + case FileType.media: + return 'video/*|image/*'; + + case FileType.custom: + return allowedExtensions.fold('', (prev, next) => '${prev.isEmpty ? '' : '$prev,'} .$next'); + break; + } + return ''; + } +} diff --git a/file_picker/lib/src/platform_file.dart b/file_picker/lib/src/platform_file.dart new file mode 100644 index 0000000..cbefcb6 --- /dev/null +++ b/file_picker/lib/src/platform_file.dart @@ -0,0 +1,40 @@ +import 'dart:typed_data'; + +class PlatformFile { + PlatformFile({ + this.path, + this.uri, + this.name, + this.bytes, + this.isDirectory = false, + }); + + /// The absolute path for this file instance. + /// + /// Typically whis will reflect a copy cached file and not the original source, + /// also, it's not guaranteed that this path is always available as some files + /// can be protected by OS. + /// + /// Available on IO only. On Web is always `null`. + final String path; + + /// The URI (Universal Resource Identifier) for this file. + /// + /// This is the original file resource identifier and can be used to + /// manipulate the original file (read, write, delete). + /// + /// Available on IO only. On Web is always `null`. + final String uri; + + /// File name including its extension. + final String name; + + /// Byte data for this file. + final Uint8List bytes; + + /// Whether this file references a directory or not. + final bool isDirectory; + + /// File extension for this file. + String get extension => name.split('/').last; +} diff --git a/file_picker/pubspec.yaml b/file_picker/pubspec.yaml index 2940e87..22e4485 100644 --- a/file_picker/pubspec.yaml +++ b/file_picker/pubspec.yaml @@ -1,13 +1,16 @@ name: file_picker description: A package that allows you to use a native file explorer to pick single or multiple absolute file paths, with extension filtering support. homepage: https://github.com/miguelpruivo/plugins_flutter_file_picker -version: 1.13.3 +version: 2.0.0 dependencies: flutter: sdk: flutter + flutter_web_plugins: + sdk: flutter + flutter_plugin_android_lifecycle: ^1.0.6 - file_picker_platform_interface: ^1.3.1 + plugin_platform_interface: ^1.0.1 environment: sdk: ">=2.0.0 <3.0.0" @@ -22,4 +25,5 @@ flutter: ios: pluginClass: FilePickerPlugin web: - default_package: file_picker_web + pluginClass: FilePickerWeb + fileName: src/file_picker_web.dart diff --git a/file_picker_platform_interface/CHANGELOG.md b/file_picker_platform_interface/CHANGELOG.md index 75f0c6d..978f2d6 100644 --- a/file_picker_platform_interface/CHANGELOG.md +++ b/file_picker_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## [2.0.0] - Deprecates interface + +Deprecates interface in favor of standalone [file_picker](https://pub.dev/packages/file_picker) for all platforms. + ## [1.3.1] - Rollback `allowCompression` Removes `allowCompression` from interface as it should only be used from `file_picker` (Android & iOS). diff --git a/file_picker_platform_interface/README.md b/file_picker_platform_interface/README.md index 9b8f456..68d2f37 100644 --- a/file_picker_platform_interface/README.md +++ b/file_picker_platform_interface/README.md @@ -1,3 +1,7 @@ +# MUST READ! + +The interface is deprectated in favor of standalone [file_picker](https://pub.dev/packages/file_picker) for all platforms where an interface is integrated. This should be the one used as this package is not longer mantained. + # file_picker_platform_interface A common platform interface for the [`file_picker`][1] plugin. @@ -6,6 +10,7 @@ This interface allows platform-specific implementations of the `file_picker` plugin, as well as the plugin itself, to ensure they are supporting the same interface. + # Usage To implement a new platform-specific implementation of `file_picker`, extend diff --git a/file_picker_platform_interface/pubspec.yaml b/file_picker_platform_interface/pubspec.yaml index 74bf79f..ccf6174 100644 --- a/file_picker_platform_interface/pubspec.yaml +++ b/file_picker_platform_interface/pubspec.yaml @@ -1,7 +1,7 @@ name: file_picker_platform_interface description: A common platform interface for the file_picker plugin that must be used to share commom features homepage: https://github.com/miguelpruivo/plugins_flutter_file_picker/file_picker_platform_interface -version: 1.3.1 +version: 2.0.0 environment: sdk: ">=2.1.0 <3.0.0" diff --git a/file_picker_web/CHANGELOG.md b/file_picker_web/CHANGELOG.md index 0bd9387..ea47c14 100644 --- a/file_picker_web/CHANGELOG.md +++ b/file_picker_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.0 + +Deprecates plugin in favor of standalone [file_picker](https://pub.dev/packages/file_picker) for all platforms. + ## 1.0.2+1 Fix custom filter String creation. diff --git a/file_picker_web/README.md b/file_picker_web/README.md index 9223183..30cd96e 100644 --- a/file_picker_web/README.md +++ b/file_picker_web/README.md @@ -1,3 +1,7 @@ +# MUST READ! + +The web standalone plugin is deprectated in favor of standalone [file_picker](https://pub.dev/packages/file_picker) for all platforms. This should be the one used as this package is not longer mantained. + # file_picker_web The web implementation of [`file_picker`][1]. diff --git a/file_picker_web/pubspec.yaml b/file_picker_web/pubspec.yaml index 03def2f..9001480 100644 --- a/file_picker_web/pubspec.yaml +++ b/file_picker_web/pubspec.yaml @@ -1,14 +1,14 @@ name: file_picker_web description: Web platform implementation of file_picker. Provides a way to pick files with filter support for Web. homepage: https://github.com/miguelpruivo/flutter_file_picker/tree/master/file_picker_web -version: 1.0.2+1 +version: 2.0.0 environment: sdk: ">=2.7.0 <3.0.0" flutter: ">=1.10.0" dependencies: - file_picker_platform_interface: ^1.3.1 + file_picker_platform_interface: ^2.0.0 flutter: sdk: flutter flutter_web_plugins: From c4d80c5d7ceefd6efa14c4d8bcecfdd2a4ee9d01 Mon Sep 17 00:00:00 2001 From: Miguel Ruivo Date: Thu, 10 Sep 2020 00:52:45 +0100 Subject: [PATCH 2/6] Adds Android implementation --- .../flutter/plugin/filepicker/FileInfo.java | 83 +++++++++++++++++++ .../plugin/filepicker/FilePickerDelegate.java | 52 ++++++------ .../flutter/plugin/filepicker/FileUtils.java | 60 ++++++++++---- file_picker/example/test/widget_test.dart | 30 ------- file_picker/lib/src/file_picker_io.dart | 4 +- file_picker/lib/src/platform_file.dart | 30 ++++--- 6 files changed, 175 insertions(+), 84 deletions(-) create mode 100644 file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileInfo.java delete mode 100644 file_picker/example/test/widget_test.dart diff --git a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileInfo.java b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileInfo.java new file mode 100644 index 0000000..830e35d --- /dev/null +++ b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileInfo.java @@ -0,0 +1,83 @@ +package com.mr.flutter.plugin.filepicker; + +import android.net.Uri; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class FileInfo { + + final Uri uri; + final String path; + final String name; + final int size; + final byte[] bytes; + final boolean isDirectory; + + public FileInfo(Uri uri, String path, String name, int size, byte[] bytes, boolean isDirectory) { + this.uri = uri; + this.path = path; + this.name = name; + this.size = size; + this.bytes = bytes; + this.isDirectory = isDirectory; + } + + public static class Builder { + + private Uri uri; + private String path; + private String name; + private int size; + private byte[] bytes; + private boolean isDirectory; + + public Builder withUri(Uri uri){ + this.uri = uri; + return this; + } + + public Builder withPath(String path){ + this.path = path; + return this; + } + + public Builder withName(String name){ + this.name = name; + return this; + } + + public Builder withSize(int size){ + this.size = size; + return this; + } + + public Builder withData(byte[] bytes){ + this.bytes = bytes; + return this; + } + + public Builder withDirectory(String directory){ + this.path = directory; + this.isDirectory = directory != null; + return this; + } + + public FileInfo build() { + return new FileInfo(this.uri, this.path, this.name, this.size, this.bytes, this.isDirectory); + } + } + + + public HashMap toMap() { + final HashMap data = new HashMap<>(); + data.put("uri", uri.toString()); + data.put("path", path); + data.put("name", name); + data.put("size", size); + data.put("bytes", bytes); + data.put("isDirectory", isDirectory); + return data; + } +} diff --git a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java index 27b70fa..a097467 100644 --- a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java +++ b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java @@ -17,8 +17,11 @@ import androidx.annotation.VisibleForTesting; import androidx.core.app.ActivityCompat; import java.io.File; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import io.flutter.plugin.common.EventChannel; import io.flutter.plugin.common.MethodChannel; @@ -82,49 +85,42 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener @Override public void run() { if (data != null) { + final ArrayList files = new ArrayList<>(); + if (data.getClipData() != null) { final int count = data.getClipData().getItemCount(); int currentItem = 0; - final ArrayList paths = new ArrayList<>(); while (currentItem < count) { final Uri currentUri = data.getClipData().getItemAt(currentItem).getUri(); - String path; - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - path = FileUtils.getUriFromRemote(FilePickerDelegate.this.activity, currentUri); - } else { - path = FileUtils.getPath(currentUri, FilePickerDelegate.this.activity); - if (path == null) { - path = FileUtils.getUriFromRemote(FilePickerDelegate.this.activity, currentUri); - } + final FileInfo file = FileUtils.openFileStream(FilePickerDelegate.this.activity, currentUri); + + if(file != null) { + files.add(file); + Log.d(FilePickerDelegate.TAG, "[MultiFilePick] File #" + currentItem + " - URI: " + currentUri.getPath()); } - paths.add(path); - Log.i(FilePickerDelegate.TAG, "[MultiFilePick] File #" + currentItem + " - URI: " + currentUri.getPath()); currentItem++; } - finishWithSuccess(paths); - + finishWithSuccess(files); } else if (data.getData() != null) { Uri uri = data.getData(); - String fullPath; + if (type.equals("dir") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { uri = DocumentsContract.buildDocumentUriUsingTree(uri, DocumentsContract.getTreeDocumentId(uri)); } - Log.i(FilePickerDelegate.TAG, "[SingleFilePick] File URI:" + uri.toString()); + Log.d(FilePickerDelegate.TAG, "[SingleFilePick] File URI:" + uri.toString()); if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - fullPath = type.equals("dir") ? FileUtils.getFullPathFromTreeUri(uri, activity) : FileUtils.getUriFromRemote(FilePickerDelegate.this.activity, uri); - } else { - fullPath = FileUtils.getPath(uri, FilePickerDelegate.this.activity); - if (fullPath == null) { - fullPath = type.equals("dir") ? FileUtils.getFullPathFromTreeUri(uri, activity) : FileUtils.getUriFromRemote(FilePickerDelegate.this.activity, uri); + final FileInfo file = type.equals("dir") ? FileUtils.getFullPathFromTreeUri(uri, activity) : FileUtils.openFileStream(FilePickerDelegate.this.activity, uri); + if(file != null) { + files.add(file); } } - if (fullPath != null) { - Log.i(FilePickerDelegate.TAG, "Absolute file path:" + fullPath); - finishWithSuccess(Arrays.asList(fullPath)); + if (!files.isEmpty()) { + Log.d(FilePickerDelegate.TAG, "File path:" + files.toString()); + finishWithSuccess(files); } else { finishWithError("unknown_path", "Failed to retrieve path."); } @@ -137,7 +133,6 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener } } }).start(); - return true; } else if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_CANCELED) { @@ -238,13 +233,20 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener this.startFileExplorer(); } - private void finishWithSuccess(final Object data) { + private void finishWithSuccess(final ArrayList files) { if (eventSink != null) { this.dispatchEventStatus(false); } // Temporary fix, remove this null-check after Flutter Engine 1.14 has landed on stable if (this.pendingResult != null) { + + final ArrayList> data = new ArrayList<>(); + + for(FileInfo file : files) { + data.add(file.toMap()); + } + this.pendingResult.success(data); this.clearPendingResult(); } diff --git a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java index 7ca9d8c..aa84526 100644 --- a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java +++ b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java @@ -17,7 +17,7 @@ import android.webkit.MimeTypeMap; import androidx.annotation.Nullable; -import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -236,19 +236,21 @@ public class FileUtils { return true; } - public static String getUriFromRemote(final Context context, final Uri uri) { + public static FileInfo openFileStream(final Context context, final Uri uri) { - Log.i(TAG, "Caching file from remote/external URI"); + Log.i(TAG, "Caching from URI: " + uri.toString()); FileOutputStream fos = null; + final FileInfo.Builder fileInfo = new FileInfo.Builder(); final String fileName = FileUtils.getFileName(uri, context); - final String externalFile = context.getCacheDir().getAbsolutePath() + "/file_picker/" + (fileName != null ? fileName : new Random().nextInt(100000)); + final String path = context.getCacheDir().getAbsolutePath() + "/file_picker/" + (fileName != null ? fileName : new Random().nextInt(100000)); - new File(externalFile).getParentFile().mkdirs(); + final File file = new File(path); + file.getParentFile().mkdirs(); try { - fos = new FileOutputStream(externalFile); + fos = new FileOutputStream(path); try { - final BufferedOutputStream out = new BufferedOutputStream(fos); + final ByteArrayOutputStream out = new ByteArrayOutputStream(); final InputStream in = context.getContentResolver().openInputStream(uri); final byte[] buffer = new byte[8192]; @@ -258,6 +260,8 @@ public class FileUtils { out.write(buffer, 0, len); } + fileInfo.withData(out.toByteArray()); + out.writeTo(fos); out.flush(); } finally { fos.getFD().sync(); @@ -273,28 +277,50 @@ public class FileUtils { return null; } - Log.i(TAG, "File loaded and cached at:" + externalFile); - return externalFile; + Log.d(TAG, "File loaded and cached at:" + path); + + fileInfo + .withPath(path) + .withName(fileName) + .withSize(Integer.parseInt(String.valueOf(file.length()/1024))) + .withUri(uri); + + return fileInfo.build(); } @Nullable - public static String getFullPathFromTreeUri(@Nullable final Uri treeUri, Context con) { - if (treeUri == null) return null; + public static FileInfo getFullPathFromTreeUri(@Nullable final Uri treeUri, Context con) { + if (treeUri == null) { + return null; + } + String volumePath = getVolumePath(getVolumeIdFromTreeUri(treeUri), con); - if (volumePath == null) return File.separator; + FileInfo.Builder fileInfo = new FileInfo.Builder(); + + fileInfo.withUri(treeUri); + + if (volumePath == null) { + return fileInfo.withDirectory(File.separator).build(); + } + if (volumePath.endsWith(File.separator)) volumePath = volumePath.substring(0, volumePath.length() - 1); String documentPath = getDocumentPathFromTreeUri(treeUri); + if (documentPath.endsWith(File.separator)) documentPath = documentPath.substring(0, documentPath.length() - 1); if (documentPath.length() > 0) { - if (documentPath.startsWith(File.separator)) - return volumePath + documentPath; - else - return volumePath + File.separator + documentPath; - } else return volumePath; + if (documentPath.startsWith(File.separator)) { + return fileInfo.withDirectory(volumePath + documentPath).build(); + } + else { + return fileInfo.withDirectory(volumePath + File.separator + documentPath).build(); + } + } else { + return fileInfo.withDirectory(volumePath).build(); + } } diff --git a/file_picker/example/test/widget_test.dart b/file_picker/example/test/widget_test.dart deleted file mode 100644 index 747db1d..0000000 --- a/file_picker/example/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:example/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} diff --git a/file_picker/lib/src/file_picker_io.dart b/file_picker/lib/src/file_picker_io.dart index 64493ab..0084e23 100644 --- a/file_picker/lib/src/file_picker_io.dart +++ b/file_picker/lib/src/file_picker_io.dart @@ -64,7 +64,7 @@ class FilePickerIO extends FilePicker { ); } - final List result = await _channel.invokeListMethod(type, { + final List result = await _channel.invokeListMethod(type, { 'allowMultipleSelection': allowMultipleSelection, 'allowedExtensions': allowedExtensions, 'allowCompression': allowCompression, @@ -74,7 +74,7 @@ class FilePickerIO extends FilePicker { return null; } - return FilePickerResult(result.map((file) => PlatformFile(name: file.split('/').last, path: file)).toList()); + return FilePickerResult(result.map((file) => PlatformFile.fromMap(file)).toList()); } on PlatformException catch (e) { print('[$_tag] Platform exception: $e'); rethrow; diff --git a/file_picker/lib/src/platform_file.dart b/file_picker/lib/src/platform_file.dart index cbefcb6..4b25e19 100644 --- a/file_picker/lib/src/platform_file.dart +++ b/file_picker/lib/src/platform_file.dart @@ -6,32 +6,42 @@ class PlatformFile { this.uri, this.name, this.bytes, + this.size, this.isDirectory = false, }); - /// The absolute path for this file instance. - /// - /// Typically whis will reflect a copy cached file and not the original source, - /// also, it's not guaranteed that this path is always available as some files - /// can be protected by OS. - /// - /// Available on IO only. On Web is always `null`. + PlatformFile.fromMap(Map data) + : this.path = data['path'], + this.uri = data['uri'], + this.name = data['name'], + this.bytes = data['bytes'], + this.size = data['size'], + this.isDirectory = data['isDirectory']; + + /// The absolute path for a cached copy of this file. + /// If you want to access the original file identifier use [uri] property instead. final String path; /// The URI (Universal Resource Identifier) for this file. /// - /// This is the original file resource identifier and can be used to + /// This is the identifier of original resource and can be used to /// manipulate the original file (read, write, delete). /// - /// Available on IO only. On Web is always `null`. + /// Android: it can be either content:// or file:// url. + /// iOS: a file:// URL below a document provider (like iCloud). + /// Web: Not supported, will be always `null`. final String uri; /// File name including its extension. final String name; - /// Byte data for this file. + /// Byte data for this file. Particurlarly useful if you want to manipulate its data + /// or easily upload to somewhere else. final Uint8List bytes; + /// The file size in KB. + final int size; + /// Whether this file references a directory or not. final bool isDirectory; From ea601246fd80e7205dbcbe061cbe52527c02046e Mon Sep 17 00:00:00 2001 From: Miguel Ruivo Date: Fri, 11 Sep 2020 14:53:18 +0100 Subject: [PATCH 3/6] Adds lastModified property and prevents caching if file already exists (Android) --- .../flutter/plugin/filepicker/FileInfo.java | 21 +- .../plugin/filepicker/FilePickerDelegate.java | 28 ++- .../flutter/plugin/filepicker/FileUtils.java | 224 ++++-------------- .../example/lib/src/file_picker_demo.dart | 61 ++--- file_picker/lib/src/file_picker.dart | 6 +- file_picker/lib/src/file_picker_io.dart | 7 +- file_picker/lib/src/file_picker_web.dart | 9 +- file_picker/lib/src/platform_file.dart | 6 +- 8 files changed, 135 insertions(+), 227 deletions(-) diff --git a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileInfo.java b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileInfo.java index 830e35d..13ca187 100644 --- a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileInfo.java +++ b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileInfo.java @@ -2,9 +2,7 @@ package com.mr.flutter.plugin.filepicker; import android.net.Uri; -import java.util.Collections; import java.util.HashMap; -import java.util.Map; public class FileInfo { @@ -13,14 +11,16 @@ public class FileInfo { final String name; final int size; final byte[] bytes; + final long lastModified; final boolean isDirectory; - public FileInfo(Uri uri, String path, String name, int size, byte[] bytes, boolean isDirectory) { + public FileInfo(Uri uri, String path, String name, int size, byte[] bytes, boolean isDirectory, long lastModified) { this.uri = uri; this.path = path; this.name = name; this.size = size; this.bytes = bytes; + this.lastModified = lastModified; this.isDirectory = isDirectory; } @@ -30,6 +30,7 @@ public class FileInfo { private String path; private String name; private int size; + private long lastModified; private byte[] bytes; private boolean isDirectory; @@ -58,14 +59,19 @@ public class FileInfo { return this; } - public Builder withDirectory(String directory){ - this.path = directory; - this.isDirectory = directory != null; + public Builder withDirectory(String path){ + this.path = path; + this.isDirectory = path != null; + return this; + } + + public Builder lastModifiedAt(long timeStamp){ + this.lastModified = timeStamp; return this; } public FileInfo build() { - return new FileInfo(this.uri, this.path, this.name, this.size, this.bytes, this.isDirectory); + return new FileInfo(this.uri, this.path, this.name, this.size, this.bytes, this.isDirectory, this.lastModified); } } @@ -78,6 +84,7 @@ public class FileInfo { data.put("size", size); data.put("bytes", bytes); data.put("isDirectory", isDirectory); + data.put("lastModified", lastModified); return data; } } diff --git a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java index a097467..fd8705f 100644 --- a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java +++ b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java @@ -111,11 +111,20 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener Log.d(FilePickerDelegate.TAG, "[SingleFilePick] File URI:" + uri.toString()); - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - final FileInfo file = type.equals("dir") ? FileUtils.getFullPathFromTreeUri(uri, activity) : FileUtils.openFileStream(FilePickerDelegate.this.activity, uri); - if(file != null) { - files.add(file); + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && type.equals("dir")) { + final String dirPath = FileUtils.getFullPathFromTreeUri(uri, activity); + if(dirPath != null) { + finishWithSuccess(dirPath); + } else { + finishWithError("unknown_path", "Failed to retrieve directory path."); } + return; + } + + final FileInfo file = FileUtils.openFileStream(FilePickerDelegate.this.activity, uri); + + if(file != null) { + files.add(file); } if (!files.isEmpty()) { @@ -233,7 +242,7 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener this.startFileExplorer(); } - private void finishWithSuccess(final ArrayList files) { + private void finishWithSuccess(Object data) { if (eventSink != null) { this.dispatchEventStatus(false); } @@ -241,10 +250,13 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener // Temporary fix, remove this null-check after Flutter Engine 1.14 has landed on stable if (this.pendingResult != null) { - final ArrayList> data = new ArrayList<>(); + if(data != null && !(data instanceof String)) { + final ArrayList> files = new ArrayList<>(); - for(FileInfo file : files) { - data.add(file.toMap()); + for (FileInfo file : (ArrayList)data) { + files.add(file.toMap()); + } + data = files; } this.pendingResult.success(data); diff --git a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java index aa84526..17643ea 100644 --- a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java +++ b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java @@ -17,8 +17,11 @@ import android.webkit.MimeTypeMap; import androidx.annotation.Nullable; +import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -32,106 +35,6 @@ public class FileUtils { private static final String TAG = "FilePickerUtils"; private static final String PRIMARY_VOLUME_NAME = "primary"; - public static String getPath(final Uri uri, final Context context) { - final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; - if (isKitKat) { - return getForApi19(context, uri); - } else if ("content".equalsIgnoreCase(uri.getScheme())) { - if (isGooglePhotosUri(uri)) { - return uri.getLastPathSegment(); - } - return getDataColumn(context, uri, null, null); - } else if ("file".equalsIgnoreCase(uri.getScheme())) { - return uri.getPath(); - } - return null; - } - - @TargetApi(19) - @SuppressWarnings("deprecation") - private static String getForApi19(final Context context, final Uri uri) { - Log.e(TAG, "Getting for API 19 or above" + uri); - if (DocumentsContract.isDocumentUri(context, uri)) { - Log.e(TAG, "Document URI"); - if (isExternalStorageDocument(uri)) { - Log.e(TAG, "External Document URI"); - final String docId = DocumentsContract.getDocumentId(uri); - final String[] split = docId.split(":"); - final String type = split[0]; - if ("primary".equalsIgnoreCase(type)) { - Log.e(TAG, "Primary External Document URI"); - return Environment.getExternalStorageDirectory() + (split.length > 1 ? ("/" + split[1]) : ""); - } - } else if (isDownloadsDocument(uri)) { - Log.e(TAG, "Downloads External Document URI"); - String id = DocumentsContract.getDocumentId(uri); - - if (!TextUtils.isEmpty(id)) { - if (id.startsWith("raw:")) { - return id.replaceFirst("raw:", ""); - } - final String[] contentUriPrefixesToTry = new String[]{ - "content://downloads/public_downloads", - "content://downloads/my_downloads", - "content://downloads/all_downloads" - }; - if (id.contains(":")) { - id = id.split(":")[1]; - } - for (final String contentUriPrefix : contentUriPrefixesToTry) { - try { - final Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.valueOf(id)); - final String path = getDataColumn(context, contentUri, null, null); - if (path != null) { - return path; - } - } catch (final Exception e) { - Log.e(TAG, "Something went wrong while retrieving document path: " + e.toString()); - return null; - } - } - - } - } else if (isMediaDocument(uri)) { - Log.e(TAG, "Media Document URI"); - final String docId = DocumentsContract.getDocumentId(uri); - final String[] split = docId.split(":"); - final String type = split[0]; - - Uri contentUri = null; - if ("image".equals(type)) { - Log.i(TAG, "Image Media Document URI"); - contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; - } else if ("video".equals(type)) { - Log.i(TAG, "Video Media Document URI"); - contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; - } else if ("audio".equals(type)) { - Log.i(TAG, "Audio Media Document URI"); - contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; - } - - final String selection = MediaStore.Images.Media._ID + "=?"; - final String[] selectionArgs = new String[]{ - split[1] - }; - - return getDataColumn(context, contentUri, selection, selectionArgs); - } - } else if ("content".equalsIgnoreCase(uri.getScheme())) { - Log.e(TAG, "NO DOCUMENT URI - CONTENT: " + uri.getPath()); - 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: " + uri.getPath()); - return uri.getPath(); - } - return null; - } - public static String[] getMimeTypes(final ArrayList allowedExtensions) { if (allowedExtensions == null || allowedExtensions.isEmpty()) { @@ -153,29 +56,6 @@ public class FileUtils { return mimes.toArray(new String[0]); } - private static String getDataColumn(final Context context, final Uri uri, final String selection, - final String[] selectionArgs) { - Cursor cursor = null; - final String column = MediaStore.Images.Media.DATA; - final String[] projection = { - column - }; - try { - cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, - null); - if (cursor != null && cursor.moveToFirst()) { - final int index = cursor.getColumnIndexOrThrow(column); - return cursor.getString(index); - } - } catch (final Exception ex) { - } finally { - if (cursor != null) { - cursor.close(); - } - } - return null; - } - public static String getFileName(Uri uri, final Context context) { String result = null; @@ -245,41 +125,59 @@ public class FileUtils { final String path = context.getCacheDir().getAbsolutePath() + "/file_picker/" + (fileName != null ? fileName : new Random().nextInt(100000)); final File file = new File(path); - file.getParentFile().mkdirs(); - try { - fos = new FileOutputStream(path); + if(file.exists()) { + int size = (int) file.length(); + byte[] bytes = new byte[size]; + try { - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - final InputStream in = context.getContentResolver().openInputStream(uri); - - final byte[] buffer = new byte[8192]; - int len = 0; - - while ((len = in.read(buffer)) >= 0) { - out.write(buffer, 0, len); - } - - fileInfo.withData(out.toByteArray()); - out.writeTo(fos); - out.flush(); - } finally { - fos.getFD().sync(); - } - } catch (final Exception e) { - try { - fos.close(); - } catch (final IOException | NullPointerException ex) { + BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file)); + buf.read(bytes, 0, bytes.length); + buf.close(); + } catch (FileNotFoundException e) { + Log.e(TAG, "File not found: " + e.getMessage(), null); + } catch (IOException e) { Log.e(TAG, "Failed to close file streams: " + e.getMessage(), null); + } + fileInfo.withData(bytes); + } else { + + file.getParentFile().mkdirs(); + try { + fos = new FileOutputStream(path); + try { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final InputStream in = context.getContentResolver().openInputStream(uri); + + final byte[] buffer = new byte[8192]; + int len = 0; + + while ((len = in.read(buffer)) >= 0) { + out.write(buffer, 0, len); + } + + fileInfo.withData(out.toByteArray()); + out.writeTo(fos); + out.flush(); + } finally { + fos.getFD().sync(); + } + } catch (final Exception e) { + try { + fos.close(); + } catch (final IOException | NullPointerException ex) { + Log.e(TAG, "Failed to close file streams: " + e.getMessage(), null); + return null; + } + Log.e(TAG, "Failed to retrieve path: " + e.getMessage(), null); return null; } - Log.e(TAG, "Failed to retrieve path: " + e.getMessage(), null); - return null; } Log.d(TAG, "File loaded and cached at:" + path); fileInfo + .lastModifiedAt(file.lastModified()) .withPath(path) .withName(fileName) .withSize(Integer.parseInt(String.valueOf(file.length()/1024))) @@ -289,7 +187,7 @@ public class FileUtils { } @Nullable - public static FileInfo getFullPathFromTreeUri(@Nullable final Uri treeUri, Context con) { + public static String getFullPathFromTreeUri(@Nullable final Uri treeUri, Context con) { if (treeUri == null) { return null; } @@ -300,7 +198,7 @@ public class FileUtils { fileInfo.withUri(treeUri); if (volumePath == null) { - return fileInfo.withDirectory(File.separator).build(); + return File.separator; } if (volumePath.endsWith(File.separator)) @@ -313,13 +211,13 @@ public class FileUtils { if (documentPath.length() > 0) { if (documentPath.startsWith(File.separator)) { - return fileInfo.withDirectory(volumePath + documentPath).build(); + return volumePath + documentPath; } else { - return fileInfo.withDirectory(volumePath + File.separator + documentPath).build(); + return volumePath + File.separator + documentPath; } } else { - return fileInfo.withDirectory(volumePath).build(); + return volumePath; } } @@ -375,24 +273,4 @@ public class FileUtils { else return File.separator; } - private static boolean isDropBoxUri(final Uri uri) { - return "com.dropbox.android.FileCache".equals(uri.getAuthority()); - } - - private static boolean isExternalStorageDocument(final Uri uri) { - return "com.android.externalstorage.documents".equals(uri.getAuthority()); - } - - private static boolean isDownloadsDocument(final Uri uri) { - return "com.android.providers.downloads.documents".equals(uri.getAuthority()); - } - - private static boolean isMediaDocument(final Uri uri) { - return "com.android.providers.media.documents".equals(uri.getAuthority()); - } - - private static boolean isGooglePhotosUri(final Uri uri) { - return "com.google.android.apps.photos.content".equals(uri.getAuthority()); - } - } \ No newline at end of file diff --git a/file_picker/example/lib/src/file_picker_demo.dart b/file_picker/example/lib/src/file_picker_demo.dart index 99f61ee..2b114e4 100644 --- a/file_picker/example/lib/src/file_picker_demo.dart +++ b/file_picker/example/lib/src/file_picker_demo.dart @@ -12,6 +12,7 @@ class _FilePickerDemoState extends State { final GlobalKey _scaffoldKey = GlobalKey(); String _fileName; List _paths; + String _directoryPath; String _extension; bool _loadingPath = false; bool _multiPick = false; @@ -27,7 +28,8 @@ class _FilePickerDemoState extends State { void _openFileExplorer() async { setState(() => _loadingPath = true); try { - _paths = (await FilePicker.instance.pickFiles( + _directoryPath = null; + _paths = (await FilePicker.platform.pickFiles( type: _pickingType, allowMultiple: _multiPick, allowedExtensions: (_extension?.isNotEmpty ?? false) ? _extension?.replaceAll(' ', '')?.split(',') : null, @@ -46,7 +48,7 @@ class _FilePickerDemoState extends State { } void _clearCachedFiles() { - FilePicker.instance.clearTemporaryFiles().then((result) { + FilePicker.platform.clearTemporaryFiles().then((result) { _scaffoldKey.currentState.showSnackBar( SnackBar( backgroundColor: result ? Colors.green : Colors.red, @@ -57,8 +59,8 @@ class _FilePickerDemoState extends State { } void _selectFolder() { - FilePicker.instance.getDirectoryPath().then((value) { - setState(() => _paths = [value]); + FilePicker.platform.getDirectoryPath().then((value) { + setState(() => _directoryPath = value); }); } @@ -161,30 +163,35 @@ class _FilePickerDemoState extends State { padding: const EdgeInsets.only(bottom: 10.0), child: const CircularProgressIndicator(), ) - : _paths != null - ? Container( - padding: const EdgeInsets.only(bottom: 30.0), - height: MediaQuery.of(context).size.height * 0.50, - child: Scrollbar( - child: 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.map((e) => e.name).toList()[index] : _fileName ?? '...'); - final path = _paths.map((e) => e.path).toList()[index].toString(); - - return ListTile( - title: Text( - name, - ), - subtitle: Text(path), - ); - }, - separatorBuilder: (BuildContext context, int index) => const Divider(), - )), + : _directoryPath != null + ? ListTile( + title: Text('Directory path'), + subtitle: Text(_directoryPath), ) - : const SizedBox(), + : _paths != null + ? Container( + padding: const EdgeInsets.only(bottom: 30.0), + height: MediaQuery.of(context).size.height * 0.50, + child: Scrollbar( + child: 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.map((e) => e.name).toList()[index] : _fileName ?? '...'); + final path = _paths.map((e) => e.path).toList()[index].toString(); + + return ListTile( + title: Text( + name, + ), + subtitle: Text(path), + ); + }, + separatorBuilder: (BuildContext context, int index) => const Divider(), + )), + ) + : const SizedBox(), ), ], ), diff --git a/file_picker/lib/src/file_picker.dart b/file_picker/lib/src/file_picker.dart index 4876dec..1621ed9 100644 --- a/file_picker/lib/src/file_picker.dart +++ b/file_picker/lib/src/file_picker.dart @@ -34,9 +34,9 @@ abstract class FilePicker extends PlatformInterface { static FilePicker _instance = FilePickerIO(); - static FilePicker get instance => _instance; + static FilePicker get platform => _instance; - static set instance(FilePicker instance) { + static set platform(FilePicker instance) { PlatformInterface.verifyToken(instance, _token); _instance = instance; } @@ -71,5 +71,5 @@ abstract class FilePicker extends PlatformInterface { /// /// On Android, this requires to be running on SDK 21 or above, else won't work. /// Returns `null` if folder path couldn't be resolved. - Future getDirectoryPath() async => throw UnimplementedError('getDirectoryPath() has not been implemented.'); + Future getDirectoryPath() async => throw UnimplementedError('getDirectoryPath() has not been implemented.'); } diff --git a/file_picker/lib/src/file_picker_io.dart b/file_picker/lib/src/file_picker_io.dart index 0084e23..c33e1eb 100644 --- a/file_picker/lib/src/file_picker_io.dart +++ b/file_picker/lib/src/file_picker_io.dart @@ -29,12 +29,9 @@ class FilePickerIO extends FilePicker { Future clearTemporaryFiles() async => _channel.invokeMethod('clear'); @override - Future getDirectoryPath() async { + Future getDirectoryPath() async { try { - String result = await _channel.invokeMethod('dir', {}); - if (result != null) { - return PlatformFile(path: result, isDirectory: true); - } + return await _channel.invokeMethod('dir', {}); } on PlatformException catch (ex) { if (ex.code == "unknown_path") { print( diff --git a/file_picker/lib/src/file_picker_web.dart b/file_picker/lib/src/file_picker_web.dart index c4a336e..013f0df 100644 --- a/file_picker/lib/src/file_picker_web.dart +++ b/file_picker/lib/src/file_picker_web.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:html' as html; +import 'dart:typed_data'; import 'package:file_picker/file_picker.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; @@ -13,7 +14,7 @@ class FilePickerWeb extends FilePicker { static final FilePickerWeb platform = FilePickerWeb._(); static void registerWith(Registrar registrar) { - FilePicker.instance = platform; + FilePicker.platform = platform; } @override @@ -40,10 +41,14 @@ class FilePickerWeb extends FilePicker { List pickedFiles = []; reader.onLoadEnd.listen((e) { + final Uint8List bytes = Base64Decoder().convert(reader.result.toString().split(",").last); + pickedFiles.add( PlatformFile( name: uploadInput.value.replaceAll('\\', '/'), - bytes: Base64Decoder().convert(reader.result.toString().split(",").last), + path: uploadInput.value, + size: bytes.length ~/ 1024, + bytes: bytes, ), ); diff --git a/file_picker/lib/src/platform_file.dart b/file_picker/lib/src/platform_file.dart index 4b25e19..fa82f81 100644 --- a/file_picker/lib/src/platform_file.dart +++ b/file_picker/lib/src/platform_file.dart @@ -1,7 +1,7 @@ import 'dart:typed_data'; class PlatformFile { - PlatformFile({ + const PlatformFile({ this.path, this.uri, this.name, @@ -28,7 +28,9 @@ class PlatformFile { /// manipulate the original file (read, write, delete). /// /// Android: it can be either content:// or file:// url. + /// /// iOS: a file:// URL below a document provider (like iCloud). + /// /// Web: Not supported, will be always `null`. final String uri; @@ -46,5 +48,5 @@ class PlatformFile { final bool isDirectory; /// File extension for this file. - String get extension => name.split('/').last; + String get extension => name?.split('/')?.last; } From 1c0e471458ba1b118f9227e53d3879ccba3c8c25 Mon Sep 17 00:00:00 2001 From: Miguel Ruivo Date: Fri, 11 Sep 2020 18:01:34 +0100 Subject: [PATCH 4/6] Adds iOS implementation, removes URI and isDirectory and adds withData optional property --- .../flutter/plugin/filepicker/FileInfo.java | 32 +----------- .../plugin/filepicker/FilePickerDelegate.java | 12 ++--- .../plugin/filepicker/FilePickerPlugin.java | 4 +- .../flutter/plugin/filepicker/FileUtils.java | 14 +++-- file_picker/example/android/build.gradle | 2 +- .../ios/Runner.xcodeproj/project.pbxproj | 7 +-- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- file_picker/ios/Classes/FileInfo.h | 21 ++++++++ file_picker/ios/Classes/FileInfo.m | 34 ++++++++++++ file_picker/ios/Classes/FilePickerPlugin.m | 52 ++++++++++++------- file_picker/ios/Classes/FileUtils.h | 2 +- file_picker/ios/Classes/FileUtils.m | 26 +++++++--- file_picker/lib/src/file_picker.dart | 1 + file_picker/lib/src/file_picker_io.dart | 14 ++++- file_picker/lib/src/file_picker_web.dart | 3 +- file_picker/lib/src/platform_file.dart | 23 +++----- 16 files changed, 150 insertions(+), 99 deletions(-) create mode 100644 file_picker/ios/Classes/FileInfo.h create mode 100644 file_picker/ios/Classes/FileInfo.m diff --git a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileInfo.java b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileInfo.java index 13ca187..6a639d3 100644 --- a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileInfo.java +++ b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileInfo.java @@ -6,38 +6,24 @@ import java.util.HashMap; public class FileInfo { - final Uri uri; final String path; final String name; final int size; final byte[] bytes; - final long lastModified; - final boolean isDirectory; - public FileInfo(Uri uri, String path, String name, int size, byte[] bytes, boolean isDirectory, long lastModified) { - this.uri = uri; + public FileInfo(String path, String name, int size, byte[] bytes) { this.path = path; this.name = name; this.size = size; this.bytes = bytes; - this.lastModified = lastModified; - this.isDirectory = isDirectory; } public static class Builder { - private Uri uri; private String path; private String name; private int size; - private long lastModified; private byte[] bytes; - private boolean isDirectory; - - public Builder withUri(Uri uri){ - this.uri = uri; - return this; - } public Builder withPath(String path){ this.path = path; @@ -59,32 +45,18 @@ public class FileInfo { return this; } - public Builder withDirectory(String path){ - this.path = path; - this.isDirectory = path != null; - return this; - } - - public Builder lastModifiedAt(long timeStamp){ - this.lastModified = timeStamp; - return this; - } - public FileInfo build() { - return new FileInfo(this.uri, this.path, this.name, this.size, this.bytes, this.isDirectory, this.lastModified); + return new FileInfo(this.path, this.name, this.size, this.bytes); } } public HashMap toMap() { final HashMap data = new HashMap<>(); - data.put("uri", uri.toString()); data.put("path", path); data.put("name", name); data.put("size", size); data.put("bytes", bytes); - data.put("isDirectory", isDirectory); - data.put("lastModified", lastModified); return data; } } diff --git a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java index fd8705f..703e183 100644 --- a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java +++ b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java @@ -17,10 +17,7 @@ import androidx.annotation.VisibleForTesting; import androidx.core.app.ActivityCompat; import java.io.File; -import java.lang.reflect.Array; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import io.flutter.plugin.common.EventChannel; @@ -36,6 +33,7 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener private final PermissionManager permissionManager; private MethodChannel.Result pendingResult; private boolean isMultipleSelection = false; + private boolean loadDataToMemory = false; private String type; private String[] allowedExtensions; private EventChannel.EventSink eventSink; @@ -92,7 +90,7 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener int currentItem = 0; while (currentItem < count) { final Uri currentUri = data.getClipData().getItemAt(currentItem).getUri(); - final FileInfo file = FileUtils.openFileStream(FilePickerDelegate.this.activity, currentUri); + final FileInfo file = FileUtils.openFileStream(FilePickerDelegate.this.activity, currentUri, loadDataToMemory); if(file != null) { files.add(file); @@ -121,7 +119,7 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener return; } - final FileInfo file = FileUtils.openFileStream(FilePickerDelegate.this.activity, uri); + final FileInfo file = FileUtils.openFileStream(FilePickerDelegate.this.activity, uri, loadDataToMemory); if(file != null) { files.add(file); @@ -223,7 +221,7 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener } @SuppressWarnings("deprecation") - public void startFileExplorer(final String type, final boolean isMultipleSelection, final String[] allowedExtensions, final MethodChannel.Result result) { + public void startFileExplorer(final String type, final boolean isMultipleSelection, final boolean withData, final String[] allowedExtensions, final MethodChannel.Result result) { if (!this.setPendingMethodCallAndResult(result)) { finishWithAlreadyActiveError(result); @@ -232,6 +230,7 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener this.type = type; this.isMultipleSelection = isMultipleSelection; + this.loadDataToMemory = withData; this.allowedExtensions = allowedExtensions; if (!this.permissionManager.isPermissionGranted(Manifest.permission.READ_EXTERNAL_STORAGE)) { @@ -242,6 +241,7 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener this.startFileExplorer(); } + @SuppressWarnings("unchecked") private void finishWithSuccess(Object data) { if (eventSink != null) { this.dispatchEventStatus(false); diff --git a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerPlugin.java b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerPlugin.java index 4d9df23..e969996 100644 --- a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerPlugin.java +++ b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerPlugin.java @@ -112,6 +112,7 @@ public class FilePickerPlugin implements MethodChannel.MethodCallHandler, Flutte private MethodChannel channel; private static String fileType; private static boolean isMultipleSelection = false; + private static boolean withData = false; /** * Plugin registration. @@ -160,13 +161,14 @@ public class FilePickerPlugin implements MethodChannel.MethodCallHandler, Flutte result.notImplemented(); } else if (fileType != "dir") { isMultipleSelection = (boolean) arguments.get("allowMultipleSelection"); + withData = (boolean) arguments.get("withData"); allowedExtensions = FileUtils.getMimeTypes((ArrayList) arguments.get("allowedExtensions")); } if (fileType == "custom" && (allowedExtensions == null || allowedExtensions.length == 0)) { result.error(TAG, "Unsupported filter. Make sure that you are only using the extension without the dot, (ie., jpg instead of .jpg). This could also have happened because you are using an unsupported file extension. If the problem persists, you may want to consider using FileType.all instead.", null); } else { - this.delegate.startFileExplorer(fileType, isMultipleSelection, allowedExtensions, result); + this.delegate.startFileExplorer(fileType, isMultipleSelection, withData, allowedExtensions, result); } } diff --git a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java index 17643ea..41f302b 100644 --- a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java +++ b/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java @@ -116,7 +116,7 @@ public class FileUtils { return true; } - public static FileInfo openFileStream(final Context context, final Uri uri) { + public static FileInfo openFileStream(final Context context, final Uri uri, boolean withData) { Log.i(TAG, "Caching from URI: " + uri.toString()); FileOutputStream fos = null; @@ -126,7 +126,7 @@ public class FileUtils { final File file = new File(path); - if(file.exists()) { + if(file.exists() && withData) { int size = (int) file.length(); byte[] bytes = new byte[size]; @@ -156,7 +156,9 @@ public class FileUtils { out.write(buffer, 0, len); } - fileInfo.withData(out.toByteArray()); + if(withData) { + fileInfo.withData(out.toByteArray()); + } out.writeTo(fos); out.flush(); } finally { @@ -177,11 +179,9 @@ public class FileUtils { Log.d(TAG, "File loaded and cached at:" + path); fileInfo - .lastModifiedAt(file.lastModified()) .withPath(path) .withName(fileName) - .withSize(Integer.parseInt(String.valueOf(file.length()/1024))) - .withUri(uri); + .withSize(Integer.parseInt(String.valueOf(file.length()/1024))); return fileInfo.build(); } @@ -195,8 +195,6 @@ public class FileUtils { String volumePath = getVolumePath(getVolumeIdFromTreeUri(treeUri), con); FileInfo.Builder fileInfo = new FileInfo.Builder(); - fileInfo.withUri(treeUri); - if (volumePath == null) { return File.separator; } diff --git a/file_picker/example/android/build.gradle b/file_picker/example/android/build.gradle index e71893f..a61ccb1 100644 --- a/file_picker/example/android/build.gradle +++ b/file_picker/example/android/build.gradle @@ -17,7 +17,7 @@ allprojects { gradle.projectsEvaluated { tasks.withType(JavaCompile) { options.encoding = 'UTF-8' - options.compilerArgs << "-Xlint:deprecation" + options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" } } } diff --git a/file_picker/example/ios/Runner.xcodeproj/project.pbxproj b/file_picker/example/ios/Runner.xcodeproj/project.pbxproj index 039ac22..e34ad1c 100644 --- a/file_picker/example/ios/Runner.xcodeproj/project.pbxproj +++ b/file_picker/example/ios/Runner.xcodeproj/project.pbxproj @@ -164,7 +164,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1010; + LastUpgradeCheck = 1170; ORGANIZATIONNAME = "The Chromium Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -176,10 +176,9 @@ }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( - English, en, Base, ); @@ -416,6 +415,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; @@ -443,6 +443,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; diff --git a/file_picker/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/file_picker/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 486f9c3..e74a32f 100644 --- a/file_picker/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/file_picker/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ * allowedExtensions; +@property (nonatomic) BOOL loadDataToMemory; @end @implementation FilePickerPlugin @@ -76,6 +77,8 @@ NSDictionary * arguments = call.arguments; BOOL isMultiplePick = ((NSNumber*)[arguments valueForKey:@"allowMultipleSelection"]).boolValue; + self.loadDataToMemory = ((NSNumber*)[arguments valueForKey:@"withData"]).boolValue; + if([call.method isEqualToString:@"any"] || [call.method containsString:@"custom"]) { self.allowedExtensions = [FileUtils resolveType:call.method withAllowedExtensions: [arguments valueForKey:@"allowedExtensions"]]; if(self.allowedExtensions == nil) { @@ -89,7 +92,7 @@ } else if([call.method isEqualToString:@"video"] || [call.method isEqualToString:@"image"] || [call.method isEqualToString:@"media"]) { [self resolvePickMedia:[FileUtils resolveMediaType:call.method] withMultiPick:isMultiplePick withCompressionAllowed:[arguments valueForKey:@"allowCompression"]]; } else if([call.method isEqualToString:@"audio"]) { - [self resolvePickAudio]; + [self resolvePickAudioWithMultiPick: isMultiplePick]; } else { result(FlutterMethodNotImplemented); _result = nil; @@ -223,38 +226,40 @@ // Did select [dkImagePickerController setDidSelectAssets:^(NSArray * __nonnull DKAssets) { - NSMutableArray* paths = [[NSMutableArray alloc] init]; + NSMutableArray* paths = [[NSMutableArray alloc] init]; for(DKAsset * asset in DKAssets){ - [paths addObject:asset.localTemporaryPath.path]; + [paths addObject:asset.localTemporaryPath.absoluteURL]; } - self->_result([paths count] > 0 ? paths : nil); - self->_result = nil; + [self handleResult: paths]; }]; [_viewController presentViewController:dkImagePickerController animated:YES completion:nil]; } -- (void) resolvePickAudio { +- (void) resolvePickAudioWithMultiPick:(BOOL)isMultiPick { self.audioPickerController = [[MPMediaPickerController alloc] initWithMediaTypes:MPMediaTypeAnyAudio]; self.audioPickerController.delegate = self; - self.audioPickerController.showsCloudItems = NO; - self.audioPickerController.allowsPickingMultipleItems = NO; + self.audioPickerController.showsCloudItems = YES; + self.audioPickerController.allowsPickingMultipleItems = isMultiPick; self.audioPickerController.modalPresentationStyle = UIModalPresentationCurrentContext; [self.viewController presentViewController:self.audioPickerController animated:YES completion:nil]; } +- (void) handleResult:(id) files { + _result([FileUtils resolveFileInfo: [files isKindOfClass: [NSArray class]] ? files : @[files] withData:self.loadDataToMemory]); + _result = nil; +} + #pragma mark - Delegates // DocumentPicker delegate - iOS 10 only - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url{ [self.documentPickerController dismissViewControllerAnimated:YES completion:nil]; - NSString * path = (NSString *)[url path]; - _result(@[path]); - _result = nil; + [self handleResult:url]; } // DocumentPicker delegate @@ -266,10 +271,14 @@ didPickDocumentsAtURLs:(NSArray *)urls{ } [self.documentPickerController dismissViewControllerAnimated:YES completion:nil]; - NSArray * result = [FileUtils resolvePath:urls]; - _result(result); - _result = nil; + if(controller.documentPickerMode == UIDocumentPickerModeOpen) { + _result([urls objectAtIndex:0].path); + _result = nil; + return; + } + + [self handleResult: urls]; } @@ -318,8 +327,7 @@ didPickDocumentsAtURLs:(NSArray *)urls{ return; } - _result(@[[pickedVideoUrl != nil ? pickedVideoUrl : pickedImageUrl path]]); - _result = nil; + [self handleResult: pickedVideoUrl != nil ? pickedVideoUrl : pickedImageUrl]; } @@ -327,12 +335,16 @@ didPickDocumentsAtURLs:(NSArray *)urls{ - (void)mediaPicker: (MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection { [mediaPicker dismissViewControllerAnimated:YES completion:NULL]; - NSURL *url = [[[mediaItemCollection items] objectAtIndex:0] valueForKey:MPMediaItemPropertyAssetURL]; - if(url == nil) { + NSMutableArray * urls = [[NSMutableArray alloc] initWithCapacity:[mediaItemCollection items].count]; + + for(MPMediaItemCollection * item in [mediaItemCollection items]) { + [urls addObject: [item valueForKey:MPMediaItemPropertyAssetURL]]; + } + + if(urls.count == 0) { Log(@"Couldn't retrieve the audio file path, either is not locally downloaded or the file is DRM protected."); } - _result([url absoluteString]); - _result = nil; + [self handleResult:urls]; } #pragma mark - Actions canceled diff --git a/file_picker/ios/Classes/FileUtils.h b/file_picker/ios/Classes/FileUtils.h index 5c9839d..a4f7b77 100644 --- a/file_picker/ios/Classes/FileUtils.h +++ b/file_picker/ios/Classes/FileUtils.h @@ -23,7 +23,7 @@ typedef NS_ENUM(NSInteger, MediaType) { + (BOOL) clearTemporaryFiles; + (NSArray*) resolveType:(NSString*)type withAllowedExtensions:(NSArray*)allowedExtensions; + (MediaType) resolveMediaType:(NSString*)type; -+ (NSArray*) resolvePath:(NSArray *)urls; ++ (NSArray*) resolveFileInfo:(NSArray *)urls withData:(BOOL)loadData; @end diff --git a/file_picker/ios/Classes/FileUtils.m b/file_picker/ios/Classes/FileUtils.m index fb59c9a..a25cf1b 100644 --- a/file_picker/ios/Classes/FileUtils.m +++ b/file_picker/ios/Classes/FileUtils.m @@ -6,11 +6,12 @@ // #import "FileUtils.h" +#import "FileInfo.h" @implementation FileUtils + (BOOL) clearTemporaryFiles { - NSString *tmpDirectory = NSTemporaryDirectory(); + NSString *tmpDirectory = NSTemporaryDirectory(); NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error; NSArray *cacheFiles = [fileManager contentsOfDirectoryAtPath:tmpDirectory error:&error]; @@ -75,16 +76,25 @@ } } -+ (NSMutableArray*) resolvePath:(NSArray *)urls{ - NSString * uri; - NSMutableArray * paths = [[NSMutableArray alloc] init]; ++ (NSArray *)resolveFileInfo:(NSArray *)urls withData: (BOOL)loadData { - for (NSURL *url in urls) { - uri = (NSString *)[url path]; - [paths addObject:uri]; + if(urls == nil) { + return nil; } - return paths; + NSMutableArray * files = [[NSMutableArray alloc] initWithCapacity:urls.count]; + + for(NSURL * url in urls) { + NSString * path = (NSString *)[url path]; + NSDictionary * fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil]; + + [files addObject: [[[FileInfo alloc] initWithPath: path + andName: [[path lastPathComponent] stringByDeletingPathExtension] + andSize: [NSNumber numberWithLongLong: [@(fileAttributes.fileSize) longLongValue] / 1024] + andData: loadData ? [NSData dataWithContentsOfFile:path options: 0 error:nil] : nil] toData]]; + } + + return files; } @end diff --git a/file_picker/lib/src/file_picker.dart b/file_picker/lib/src/file_picker.dart index 1621ed9..d1646f2 100644 --- a/file_picker/lib/src/file_picker.dart +++ b/file_picker/lib/src/file_picker.dart @@ -55,6 +55,7 @@ abstract class FilePicker extends PlatformInterface { Function(FilePickerStatus) onFileLoading, bool allowCompression, bool allowMultiple = false, + bool withData, }) async => throw UnimplementedError('pickFiles() has not been implemented.'); diff --git a/file_picker/lib/src/file_picker_io.dart b/file_picker/lib/src/file_picker_io.dart index c33e1eb..d41860a 100644 --- a/file_picker/lib/src/file_picker_io.dart +++ b/file_picker/lib/src/file_picker_io.dart @@ -20,10 +20,18 @@ class FilePickerIO extends FilePicker { FileType type = FileType.any, List allowedExtensions, Function(FilePickerStatus) onFileLoading, - bool allowCompression, + bool allowCompression = true, bool allowMultiple = false, + bool withData = false, }) => - _getPath(type, allowMultiple, allowCompression, allowedExtensions, onFileLoading); + _getPath( + type, + allowMultiple, + allowCompression, + allowedExtensions, + onFileLoading, + withData, + ); @override Future clearTemporaryFiles() async => _channel.invokeMethod('clear'); @@ -47,6 +55,7 @@ class FilePickerIO extends FilePicker { bool allowCompression, List allowedExtensions, Function(FilePickerStatus) onFileLoading, + bool withData, ) async { final String type = describeEnum(fileType); if (type != 'custom' && (allowedExtensions?.isNotEmpty ?? false)) { @@ -65,6 +74,7 @@ class FilePickerIO extends FilePicker { 'allowMultipleSelection': allowMultipleSelection, 'allowedExtensions': allowedExtensions, 'allowCompression': allowCompression, + 'withData': withData, }); if (result == null) { diff --git a/file_picker/lib/src/file_picker_web.dart b/file_picker/lib/src/file_picker_web.dart index 013f0df..6d951a0 100644 --- a/file_picker/lib/src/file_picker_web.dart +++ b/file_picker/lib/src/file_picker_web.dart @@ -24,6 +24,7 @@ class FilePickerWeb extends FilePicker { bool allowMultiple = false, Function(FilePickerStatus) onFileLoading, bool allowCompression, + bool withData = true, }) async { final Completer> filesCompleter = Completer>(); @@ -48,7 +49,7 @@ class FilePickerWeb extends FilePicker { name: uploadInput.value.replaceAll('\\', '/'), path: uploadInput.value, size: bytes.length ~/ 1024, - bytes: bytes, + bytes: withData ? bytes : null, ), ); diff --git a/file_picker/lib/src/platform_file.dart b/file_picker/lib/src/platform_file.dart index fa82f81..435e14f 100644 --- a/file_picker/lib/src/platform_file.dart +++ b/file_picker/lib/src/platform_file.dart @@ -3,7 +3,6 @@ import 'dart:typed_data'; class PlatformFile { const PlatformFile({ this.path, - this.uri, this.name, this.bytes, this.size, @@ -12,28 +11,18 @@ class PlatformFile { PlatformFile.fromMap(Map data) : this.path = data['path'], - this.uri = data['uri'], this.name = data['name'], this.bytes = data['bytes'], this.size = data['size'], this.isDirectory = data['isDirectory']; - /// The absolute path for a cached copy of this file. - /// If you want to access the original file identifier use [uri] property instead. + /// The absolute path for a cached copy of this file. It can be used to create a + /// a file instance with a descriptor for the given path. + /// ``` + /// final File myFile = File(platformFile.path); + /// ``` final String path; - /// The URI (Universal Resource Identifier) for this file. - /// - /// This is the identifier of original resource and can be used to - /// manipulate the original file (read, write, delete). - /// - /// Android: it can be either content:// or file:// url. - /// - /// iOS: a file:// URL below a document provider (like iCloud). - /// - /// Web: Not supported, will be always `null`. - final String uri; - /// File name including its extension. final String name; @@ -48,5 +37,5 @@ class PlatformFile { final bool isDirectory; /// File extension for this file. - String get extension => name?.split('/')?.last; + String get extension => path?.split('.')?.last; } From 1239c3911395dfbc4a65ccebad52587cf542ac61 Mon Sep 17 00:00:00 2001 From: Miguel Ruivo Date: Fri, 11 Sep 2020 18:05:22 +0100 Subject: [PATCH 5/6] Unifies all platforms in a single plugin with addition of new features --- file_picker/CHANGELOG.md => CHANGELOG.md | 0 file_picker/LICENSE => LICENSE | 0 file_picker/README.md => README.md | 0 {file_picker/android => android}/.classpath | 0 {file_picker/android => android}/.gitignore | 0 {file_picker/android => android}/.project | 0 .../org.eclipse.buildship.core.prefs | 0 {file_picker/android => android}/build.gradle | 0 .../android => android}/gradle.properties | 0 .../gradle/wrapper/gradle-wrapper.properties | 0 .../android => android}/settings.gradle | 0 .../src/main/AndroidManifest.xml | 0 .../flutter/plugin/filepicker/FileInfo.java | 0 .../plugin/filepicker/FilePickerDelegate.java | 0 .../plugin/filepicker/FilePickerPlugin.java | 0 .../flutter/plugin/filepicker/FileUtils.java | 0 {file_picker/example => example}/.gitignore | 0 {file_picker/example => example}/.metadata | 0 {file_picker/example => example}/README.md | 0 .../example => example}/android/.gitignore | 0 .../example => example}/android/.project | 0 .../org.eclipse.buildship.core.prefs | 0 .../android/app/.classpath | 0 .../example => example}/android/app/.project | 0 .../org.eclipse.buildship.core.prefs | 0 .../android/app/build.gradle | 0 .../android/app/src/debug/AndroidManifest.xml | 0 .../android/app/src/main/AndroidManifest.xml | 0 .../filepickerexample/MainActivity.java | 0 .../mr/flutter/plugin/example/MainActivity.kt | 0 .../main/res/drawable/launch_background.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../app/src/main/res/values/styles.xml | 0 .../app/src/profile/AndroidManifest.xml | 0 .../example => example}/android/build.gradle | 0 .../android/gradle.properties | 0 .../gradle/wrapper/gradle-wrapper.properties | 0 .../android/settings.gradle | 0 {file_picker/example => example}/example.gif | Bin .../example => example}/example_ios.gif | Bin .../example => example}/ios/.gitignore | 0 .../ios/Flutter/.last_build_id | 0 .../ios/Flutter/AppFrameworkInfo.plist | 0 .../ios/Flutter/Debug.xcconfig | 0 .../ios/Flutter/Flutter.podspec | 0 .../ios/Flutter/Release.xcconfig | 0 {file_picker/example => example}/ios/Podfile | 0 .../ios/Runner.xcodeproj/project.pbxproj | 0 .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 0 .../xcshareddata/xcschemes/Runner.xcscheme | 0 .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 0 .../ios/Runner/AppDelegate.h | 0 .../ios/Runner/AppDelegate.m | 0 .../ios/Runner/AppDelegate.swift | 0 .../AppIcon.appiconset/Contents.json | 0 .../Icon-App-1024x1024@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin .../Icon-App-83.5x83.5@2x.png | Bin .../LaunchImage.imageset/Contents.json | 0 .../LaunchImage.imageset/LaunchImage.png | Bin .../LaunchImage.imageset/LaunchImage@2x.png | Bin .../LaunchImage.imageset/LaunchImage@3x.png | Bin .../LaunchImage.imageset/README.md | 0 .../Runner/Base.lproj/LaunchScreen.storyboard | 0 .../ios/Runner/Base.lproj/Main.storyboard | 0 .../example => example}/ios/Runner/Info.plist | 0 .../ios/Runner/Runner-Bridging-Header.h | 0 .../example => example}/ios/Runner/main.m | 0 .../lib/generated_plugin_registrant.dart | 0 .../example => example}/lib/main.dart | 0 .../example => example}/lib/main_desktop.dart | 0 .../lib/src/file_picker_demo.dart | 0 {file_picker/example => example}/pubspec.yaml | 0 .../example => example}/web/favicon.png | Bin .../web/icons/Icon-192.png | Bin .../web/icons/Icon-512.png | Bin .../example => example}/web/index.html | 0 .../example => example}/web/manifest.json | 0 file_picker_platform_interface/CHANGELOG.md | 39 -------- file_picker_platform_interface/LICENSE | 21 ---- file_picker_platform_interface/README.md | 31 ------ .../lib/file_picker_platform_interface.dart | 77 --------------- .../lib/method_channel_file_picker.dart | 91 ------------------ file_picker_platform_interface/pubspec.yaml | 16 --- file_picker_web/CHANGELOG.md | 35 ------- file_picker_web/LICENSE | 21 ---- file_picker_web/README.md | 33 ------- file_picker_web/example/.gitignore | 43 --------- file_picker_web/example/.metadata | 10 -- file_picker_web/example/README.md | 16 --- file_picker_web/example/lib/main.dart | 65 ------------- file_picker_web/example/pubspec.yaml | 27 ------ file_picker_web/example/web/favicon.png | Bin 917 -> 0 bytes .../example/web/icons/Icon-192.png | Bin 5292 -> 0 bytes .../example/web/icons/Icon-512.png | Bin 8252 -> 0 bytes file_picker_web/example/web/index.html | 33 ------- file_picker_web/example/web/manifest.json | 23 ----- file_picker_web/ios/file_picker_web.podspec | 20 ---- file_picker_web/lib/file_picker_web.dart | 79 --------------- file_picker_web/pubspec.yaml | 27 ------ {file_picker/go => go}/README.md | 0 {file_picker/go => go}/file_darwin.go | 0 {file_picker/go => go}/file_linux.go | 0 {file_picker/go => go}/file_unsupported.go | 0 {file_picker/go => go}/file_windows.go | 0 {file_picker/go => go}/go.mod | 0 {file_picker/go => go}/go.sum | 0 {file_picker/go => go}/import.go.tmpl | 0 {file_picker/go => go}/plugin.go | 0 {file_picker/ios => ios}/.gitignore | 0 {file_picker/ios => ios}/Assets/.gitkeep | 0 {file_picker/ios => ios}/Classes/FileInfo.h | 0 {file_picker/ios => ios}/Classes/FileInfo.m | 0 .../ios => ios}/Classes/FilePickerPlugin.h | 0 .../ios => ios}/Classes/FilePickerPlugin.m | 0 {file_picker/ios => ios}/Classes/FileUtils.h | 0 {file_picker/ios => ios}/Classes/FileUtils.m | 0 {file_picker/ios => ios}/Classes/ImageUtils.h | 0 {file_picker/ios => ios}/Classes/ImageUtils.m | 0 {file_picker/ios => ios}/file_picker.podspec | 0 {file_picker/lib => lib}/file_picker.dart | 0 {file_picker/lib => lib}/src/file_picker.dart | 0 .../lib => lib}/src/file_picker_io.dart | 0 .../lib => lib}/src/file_picker_result.dart | 0 .../lib => lib}/src/file_picker_web.dart | 0 .../lib => lib}/src/platform_file.dart | 0 file_picker/pubspec.yaml => pubspec.yaml | 0 147 files changed, 707 deletions(-) rename file_picker/CHANGELOG.md => CHANGELOG.md (100%) rename file_picker/LICENSE => LICENSE (100%) rename file_picker/README.md => README.md (100%) rename {file_picker/android => android}/.classpath (100%) rename {file_picker/android => android}/.gitignore (100%) rename {file_picker/android => android}/.project (100%) rename {file_picker/android => android}/.settings/org.eclipse.buildship.core.prefs (100%) rename {file_picker/android => android}/build.gradle (100%) rename {file_picker/android => android}/gradle.properties (100%) rename {file_picker/android => android}/gradle/wrapper/gradle-wrapper.properties (100%) rename {file_picker/android => android}/settings.gradle (100%) rename {file_picker/android => android}/src/main/AndroidManifest.xml (100%) rename {file_picker/android => android}/src/main/java/com/mr/flutter/plugin/filepicker/FileInfo.java (100%) rename {file_picker/android => android}/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java (100%) rename {file_picker/android => android}/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerPlugin.java (100%) rename {file_picker/android => android}/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java (100%) rename {file_picker/example => example}/.gitignore (100%) rename {file_picker/example => example}/.metadata (100%) rename {file_picker/example => example}/README.md (100%) rename {file_picker/example => example}/android/.gitignore (100%) rename {file_picker/example => example}/android/.project (100%) rename {file_picker/example => example}/android/.settings/org.eclipse.buildship.core.prefs (100%) rename {file_picker/example => example}/android/app/.classpath (100%) rename {file_picker/example => example}/android/app/.project (100%) rename {file_picker/example => example}/android/app/.settings/org.eclipse.buildship.core.prefs (100%) rename {file_picker/example => example}/android/app/build.gradle (100%) rename {file_picker/example => example}/android/app/src/debug/AndroidManifest.xml (100%) rename {file_picker/example => example}/android/app/src/main/AndroidManifest.xml (100%) rename {file_picker/example => example}/android/app/src/main/java/com/mr/flutter/plugin/filepickerexample/MainActivity.java (100%) rename {file_picker/example => example}/android/app/src/main/kotlin/com/mr/flutter/plugin/example/MainActivity.kt (100%) rename {file_picker/example => example}/android/app/src/main/res/drawable/launch_background.xml (100%) rename {file_picker/example => example}/android/app/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename {file_picker/example => example}/android/app/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename {file_picker/example => example}/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename {file_picker/example => example}/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {file_picker/example => example}/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {file_picker/example => example}/android/app/src/main/res/values/styles.xml (100%) rename {file_picker/example => example}/android/app/src/profile/AndroidManifest.xml (100%) rename {file_picker/example => example}/android/build.gradle (100%) rename {file_picker/example => example}/android/gradle.properties (100%) rename {file_picker/example => example}/android/gradle/wrapper/gradle-wrapper.properties (100%) rename {file_picker/example => example}/android/settings.gradle (100%) rename {file_picker/example => example}/example.gif (100%) rename {file_picker/example => example}/example_ios.gif (100%) rename {file_picker/example => example}/ios/.gitignore (100%) rename {file_picker/example => example}/ios/Flutter/.last_build_id (100%) rename {file_picker/example => example}/ios/Flutter/AppFrameworkInfo.plist (100%) rename {file_picker/example => example}/ios/Flutter/Debug.xcconfig (100%) rename {file_picker/example => example}/ios/Flutter/Flutter.podspec (100%) rename {file_picker/example => example}/ios/Flutter/Release.xcconfig (100%) rename {file_picker/example => example}/ios/Podfile (100%) rename {file_picker/example => example}/ios/Runner.xcodeproj/project.pbxproj (100%) rename {file_picker/example => example}/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata (100%) rename {file_picker/example => example}/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {file_picker/example => example}/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (100%) rename {file_picker/example => example}/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (100%) rename {file_picker/example => example}/ios/Runner.xcworkspace/contents.xcworkspacedata (100%) rename {file_picker/example => example}/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {file_picker/example => example}/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (100%) rename {file_picker/example => example}/ios/Runner/AppDelegate.h (100%) rename {file_picker/example => example}/ios/Runner/AppDelegate.m (100%) rename {file_picker/example => example}/ios/Runner/AppDelegate.swift (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png (100%) rename {file_picker/example => example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md (100%) rename {file_picker/example => example}/ios/Runner/Base.lproj/LaunchScreen.storyboard (100%) rename {file_picker/example => example}/ios/Runner/Base.lproj/Main.storyboard (100%) rename {file_picker/example => example}/ios/Runner/Info.plist (100%) rename {file_picker/example => example}/ios/Runner/Runner-Bridging-Header.h (100%) rename {file_picker/example => example}/ios/Runner/main.m (100%) rename {file_picker/example => example}/lib/generated_plugin_registrant.dart (100%) rename {file_picker/example => example}/lib/main.dart (100%) rename {file_picker/example => example}/lib/main_desktop.dart (100%) rename {file_picker/example => example}/lib/src/file_picker_demo.dart (100%) rename {file_picker/example => example}/pubspec.yaml (100%) rename {file_picker/example => example}/web/favicon.png (100%) rename {file_picker/example => example}/web/icons/Icon-192.png (100%) rename {file_picker/example => example}/web/icons/Icon-512.png (100%) rename {file_picker/example => example}/web/index.html (100%) rename {file_picker/example => example}/web/manifest.json (100%) delete mode 100644 file_picker_platform_interface/CHANGELOG.md delete mode 100644 file_picker_platform_interface/LICENSE delete mode 100644 file_picker_platform_interface/README.md delete mode 100644 file_picker_platform_interface/lib/file_picker_platform_interface.dart delete mode 100644 file_picker_platform_interface/lib/method_channel_file_picker.dart delete mode 100644 file_picker_platform_interface/pubspec.yaml delete mode 100644 file_picker_web/CHANGELOG.md delete mode 100644 file_picker_web/LICENSE delete mode 100644 file_picker_web/README.md delete mode 100644 file_picker_web/example/.gitignore delete mode 100644 file_picker_web/example/.metadata delete mode 100644 file_picker_web/example/README.md delete mode 100644 file_picker_web/example/lib/main.dart delete mode 100644 file_picker_web/example/pubspec.yaml delete mode 100644 file_picker_web/example/web/favicon.png delete mode 100644 file_picker_web/example/web/icons/Icon-192.png delete mode 100644 file_picker_web/example/web/icons/Icon-512.png delete mode 100644 file_picker_web/example/web/index.html delete mode 100644 file_picker_web/example/web/manifest.json delete mode 100644 file_picker_web/ios/file_picker_web.podspec delete mode 100644 file_picker_web/lib/file_picker_web.dart delete mode 100644 file_picker_web/pubspec.yaml rename {file_picker/go => go}/README.md (100%) rename {file_picker/go => go}/file_darwin.go (100%) rename {file_picker/go => go}/file_linux.go (100%) rename {file_picker/go => go}/file_unsupported.go (100%) rename {file_picker/go => go}/file_windows.go (100%) rename {file_picker/go => go}/go.mod (100%) rename {file_picker/go => go}/go.sum (100%) rename {file_picker/go => go}/import.go.tmpl (100%) rename {file_picker/go => go}/plugin.go (100%) rename {file_picker/ios => ios}/.gitignore (100%) rename {file_picker/ios => ios}/Assets/.gitkeep (100%) rename {file_picker/ios => ios}/Classes/FileInfo.h (100%) rename {file_picker/ios => ios}/Classes/FileInfo.m (100%) rename {file_picker/ios => ios}/Classes/FilePickerPlugin.h (100%) rename {file_picker/ios => ios}/Classes/FilePickerPlugin.m (100%) rename {file_picker/ios => ios}/Classes/FileUtils.h (100%) rename {file_picker/ios => ios}/Classes/FileUtils.m (100%) rename {file_picker/ios => ios}/Classes/ImageUtils.h (100%) rename {file_picker/ios => ios}/Classes/ImageUtils.m (100%) rename {file_picker/ios => ios}/file_picker.podspec (100%) rename {file_picker/lib => lib}/file_picker.dart (100%) rename {file_picker/lib => lib}/src/file_picker.dart (100%) rename {file_picker/lib => lib}/src/file_picker_io.dart (100%) rename {file_picker/lib => lib}/src/file_picker_result.dart (100%) rename {file_picker/lib => lib}/src/file_picker_web.dart (100%) rename {file_picker/lib => lib}/src/platform_file.dart (100%) rename file_picker/pubspec.yaml => pubspec.yaml (100%) diff --git a/file_picker/CHANGELOG.md b/CHANGELOG.md similarity index 100% rename from file_picker/CHANGELOG.md rename to CHANGELOG.md diff --git a/file_picker/LICENSE b/LICENSE similarity index 100% rename from file_picker/LICENSE rename to LICENSE diff --git a/file_picker/README.md b/README.md similarity index 100% rename from file_picker/README.md rename to README.md diff --git a/file_picker/android/.classpath b/android/.classpath similarity index 100% rename from file_picker/android/.classpath rename to android/.classpath diff --git a/file_picker/android/.gitignore b/android/.gitignore similarity index 100% rename from file_picker/android/.gitignore rename to android/.gitignore diff --git a/file_picker/android/.project b/android/.project similarity index 100% rename from file_picker/android/.project rename to android/.project diff --git a/file_picker/android/.settings/org.eclipse.buildship.core.prefs b/android/.settings/org.eclipse.buildship.core.prefs similarity index 100% rename from file_picker/android/.settings/org.eclipse.buildship.core.prefs rename to android/.settings/org.eclipse.buildship.core.prefs diff --git a/file_picker/android/build.gradle b/android/build.gradle similarity index 100% rename from file_picker/android/build.gradle rename to android/build.gradle diff --git a/file_picker/android/gradle.properties b/android/gradle.properties similarity index 100% rename from file_picker/android/gradle.properties rename to android/gradle.properties diff --git a/file_picker/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from file_picker/android/gradle/wrapper/gradle-wrapper.properties rename to android/gradle/wrapper/gradle-wrapper.properties diff --git a/file_picker/android/settings.gradle b/android/settings.gradle similarity index 100% rename from file_picker/android/settings.gradle rename to android/settings.gradle diff --git a/file_picker/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml similarity index 100% rename from file_picker/android/src/main/AndroidManifest.xml rename to android/src/main/AndroidManifest.xml diff --git a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileInfo.java b/android/src/main/java/com/mr/flutter/plugin/filepicker/FileInfo.java similarity index 100% rename from file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileInfo.java rename to android/src/main/java/com/mr/flutter/plugin/filepicker/FileInfo.java diff --git a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java b/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java similarity index 100% rename from file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java rename to android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerDelegate.java diff --git a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerPlugin.java b/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerPlugin.java similarity index 100% rename from file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerPlugin.java rename to android/src/main/java/com/mr/flutter/plugin/filepicker/FilePickerPlugin.java diff --git a/file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java b/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java similarity index 100% rename from file_picker/android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java rename to android/src/main/java/com/mr/flutter/plugin/filepicker/FileUtils.java diff --git a/file_picker/example/.gitignore b/example/.gitignore similarity index 100% rename from file_picker/example/.gitignore rename to example/.gitignore diff --git a/file_picker/example/.metadata b/example/.metadata similarity index 100% rename from file_picker/example/.metadata rename to example/.metadata diff --git a/file_picker/example/README.md b/example/README.md similarity index 100% rename from file_picker/example/README.md rename to example/README.md diff --git a/file_picker/example/android/.gitignore b/example/android/.gitignore similarity index 100% rename from file_picker/example/android/.gitignore rename to example/android/.gitignore diff --git a/file_picker/example/android/.project b/example/android/.project similarity index 100% rename from file_picker/example/android/.project rename to example/android/.project diff --git a/file_picker/example/android/.settings/org.eclipse.buildship.core.prefs b/example/android/.settings/org.eclipse.buildship.core.prefs similarity index 100% rename from file_picker/example/android/.settings/org.eclipse.buildship.core.prefs rename to example/android/.settings/org.eclipse.buildship.core.prefs diff --git a/file_picker/example/android/app/.classpath b/example/android/app/.classpath similarity index 100% rename from file_picker/example/android/app/.classpath rename to example/android/app/.classpath diff --git a/file_picker/example/android/app/.project b/example/android/app/.project similarity index 100% rename from file_picker/example/android/app/.project rename to example/android/app/.project diff --git a/file_picker/example/android/app/.settings/org.eclipse.buildship.core.prefs b/example/android/app/.settings/org.eclipse.buildship.core.prefs similarity index 100% rename from file_picker/example/android/app/.settings/org.eclipse.buildship.core.prefs rename to example/android/app/.settings/org.eclipse.buildship.core.prefs diff --git a/file_picker/example/android/app/build.gradle b/example/android/app/build.gradle similarity index 100% rename from file_picker/example/android/app/build.gradle rename to example/android/app/build.gradle diff --git a/file_picker/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from file_picker/example/android/app/src/debug/AndroidManifest.xml rename to example/android/app/src/debug/AndroidManifest.xml diff --git a/file_picker/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml similarity index 100% rename from file_picker/example/android/app/src/main/AndroidManifest.xml rename to example/android/app/src/main/AndroidManifest.xml diff --git a/file_picker/example/android/app/src/main/java/com/mr/flutter/plugin/filepickerexample/MainActivity.java b/example/android/app/src/main/java/com/mr/flutter/plugin/filepickerexample/MainActivity.java similarity index 100% rename from file_picker/example/android/app/src/main/java/com/mr/flutter/plugin/filepickerexample/MainActivity.java rename to example/android/app/src/main/java/com/mr/flutter/plugin/filepickerexample/MainActivity.java diff --git a/file_picker/example/android/app/src/main/kotlin/com/mr/flutter/plugin/example/MainActivity.kt b/example/android/app/src/main/kotlin/com/mr/flutter/plugin/example/MainActivity.kt similarity index 100% rename from file_picker/example/android/app/src/main/kotlin/com/mr/flutter/plugin/example/MainActivity.kt rename to example/android/app/src/main/kotlin/com/mr/flutter/plugin/example/MainActivity.kt diff --git a/file_picker/example/android/app/src/main/res/drawable/launch_background.xml b/example/android/app/src/main/res/drawable/launch_background.xml similarity index 100% rename from file_picker/example/android/app/src/main/res/drawable/launch_background.xml rename to example/android/app/src/main/res/drawable/launch_background.xml diff --git a/file_picker/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from file_picker/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/file_picker/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from file_picker/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/file_picker/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from file_picker/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/file_picker/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from file_picker/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/file_picker/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from file_picker/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/file_picker/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml similarity index 100% rename from file_picker/example/android/app/src/main/res/values/styles.xml rename to example/android/app/src/main/res/values/styles.xml diff --git a/file_picker/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml similarity index 100% rename from file_picker/example/android/app/src/profile/AndroidManifest.xml rename to example/android/app/src/profile/AndroidManifest.xml diff --git a/file_picker/example/android/build.gradle b/example/android/build.gradle similarity index 100% rename from file_picker/example/android/build.gradle rename to example/android/build.gradle diff --git a/file_picker/example/android/gradle.properties b/example/android/gradle.properties similarity index 100% rename from file_picker/example/android/gradle.properties rename to example/android/gradle.properties diff --git a/file_picker/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from file_picker/example/android/gradle/wrapper/gradle-wrapper.properties rename to example/android/gradle/wrapper/gradle-wrapper.properties diff --git a/file_picker/example/android/settings.gradle b/example/android/settings.gradle similarity index 100% rename from file_picker/example/android/settings.gradle rename to example/android/settings.gradle diff --git a/file_picker/example/example.gif b/example/example.gif similarity index 100% rename from file_picker/example/example.gif rename to example/example.gif diff --git a/file_picker/example/example_ios.gif b/example/example_ios.gif similarity index 100% rename from file_picker/example/example_ios.gif rename to example/example_ios.gif diff --git a/file_picker/example/ios/.gitignore b/example/ios/.gitignore similarity index 100% rename from file_picker/example/ios/.gitignore rename to example/ios/.gitignore diff --git a/file_picker/example/ios/Flutter/.last_build_id b/example/ios/Flutter/.last_build_id similarity index 100% rename from file_picker/example/ios/Flutter/.last_build_id rename to example/ios/Flutter/.last_build_id diff --git a/file_picker/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist similarity index 100% rename from file_picker/example/ios/Flutter/AppFrameworkInfo.plist rename to example/ios/Flutter/AppFrameworkInfo.plist diff --git a/file_picker/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig similarity index 100% rename from file_picker/example/ios/Flutter/Debug.xcconfig rename to example/ios/Flutter/Debug.xcconfig diff --git a/file_picker/example/ios/Flutter/Flutter.podspec b/example/ios/Flutter/Flutter.podspec similarity index 100% rename from file_picker/example/ios/Flutter/Flutter.podspec rename to example/ios/Flutter/Flutter.podspec diff --git a/file_picker/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig similarity index 100% rename from file_picker/example/ios/Flutter/Release.xcconfig rename to example/ios/Flutter/Release.xcconfig diff --git a/file_picker/example/ios/Podfile b/example/ios/Podfile similarity index 100% rename from file_picker/example/ios/Podfile rename to example/ios/Podfile diff --git a/file_picker/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj similarity index 100% rename from file_picker/example/ios/Runner.xcodeproj/project.pbxproj rename to example/ios/Runner.xcodeproj/project.pbxproj diff --git a/file_picker/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from file_picker/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/file_picker/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from file_picker/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/file_picker/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from file_picker/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/file_picker/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from file_picker/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/file_picker/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from file_picker/example/ios/Runner.xcworkspace/contents.xcworkspacedata rename to example/ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/file_picker/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from file_picker/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/file_picker/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from file_picker/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/file_picker/example/ios/Runner/AppDelegate.h b/example/ios/Runner/AppDelegate.h similarity index 100% rename from file_picker/example/ios/Runner/AppDelegate.h rename to example/ios/Runner/AppDelegate.h diff --git a/file_picker/example/ios/Runner/AppDelegate.m b/example/ios/Runner/AppDelegate.m similarity index 100% rename from file_picker/example/ios/Runner/AppDelegate.m rename to example/ios/Runner/AppDelegate.m diff --git a/file_picker/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift similarity index 100% rename from file_picker/example/ios/Runner/AppDelegate.swift rename to example/ios/Runner/AppDelegate.swift diff --git a/file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/file_picker/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/file_picker/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from file_picker/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/file_picker/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from file_picker/example/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to example/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/file_picker/example/ios/Runner/Base.lproj/Main.storyboard b/example/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from file_picker/example/ios/Runner/Base.lproj/Main.storyboard rename to example/ios/Runner/Base.lproj/Main.storyboard diff --git a/file_picker/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist similarity index 100% rename from file_picker/example/ios/Runner/Info.plist rename to example/ios/Runner/Info.plist diff --git a/file_picker/example/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h similarity index 100% rename from file_picker/example/ios/Runner/Runner-Bridging-Header.h rename to example/ios/Runner/Runner-Bridging-Header.h diff --git a/file_picker/example/ios/Runner/main.m b/example/ios/Runner/main.m similarity index 100% rename from file_picker/example/ios/Runner/main.m rename to example/ios/Runner/main.m diff --git a/file_picker/example/lib/generated_plugin_registrant.dart b/example/lib/generated_plugin_registrant.dart similarity index 100% rename from file_picker/example/lib/generated_plugin_registrant.dart rename to example/lib/generated_plugin_registrant.dart diff --git a/file_picker/example/lib/main.dart b/example/lib/main.dart similarity index 100% rename from file_picker/example/lib/main.dart rename to example/lib/main.dart diff --git a/file_picker/example/lib/main_desktop.dart b/example/lib/main_desktop.dart similarity index 100% rename from file_picker/example/lib/main_desktop.dart rename to example/lib/main_desktop.dart diff --git a/file_picker/example/lib/src/file_picker_demo.dart b/example/lib/src/file_picker_demo.dart similarity index 100% rename from file_picker/example/lib/src/file_picker_demo.dart rename to example/lib/src/file_picker_demo.dart diff --git a/file_picker/example/pubspec.yaml b/example/pubspec.yaml similarity index 100% rename from file_picker/example/pubspec.yaml rename to example/pubspec.yaml diff --git a/file_picker/example/web/favicon.png b/example/web/favicon.png similarity index 100% rename from file_picker/example/web/favicon.png rename to example/web/favicon.png diff --git a/file_picker/example/web/icons/Icon-192.png b/example/web/icons/Icon-192.png similarity index 100% rename from file_picker/example/web/icons/Icon-192.png rename to example/web/icons/Icon-192.png diff --git a/file_picker/example/web/icons/Icon-512.png b/example/web/icons/Icon-512.png similarity index 100% rename from file_picker/example/web/icons/Icon-512.png rename to example/web/icons/Icon-512.png diff --git a/file_picker/example/web/index.html b/example/web/index.html similarity index 100% rename from file_picker/example/web/index.html rename to example/web/index.html diff --git a/file_picker/example/web/manifest.json b/example/web/manifest.json similarity index 100% rename from file_picker/example/web/manifest.json rename to example/web/manifest.json diff --git a/file_picker_platform_interface/CHANGELOG.md b/file_picker_platform_interface/CHANGELOG.md deleted file mode 100644 index 978f2d6..0000000 --- a/file_picker_platform_interface/CHANGELOG.md +++ /dev/null @@ -1,39 +0,0 @@ -## [2.0.0] - Deprecates interface - -Deprecates interface in favor of standalone [file_picker](https://pub.dev/packages/file_picker) for all platforms. - -## [1.3.1] - Rollback `allowCompression` - -Removes `allowCompression` from interface as it should only be used from `file_picker` (Android & iOS). - -## [1.3.0] - Adds `allowCompression` parameter - -Adds `allowCompression` that will allow developers to set whether the picked media files (image/video) can be automatically compressed by OS or not. Defaults to `true`. - -## [1.2.0] - Adds FilePickerStatus - -Adds `onFiledLoading` that will provide an event handler with `FilePickerStatus` when picking files. - -## [1.1.0] - Adds directory pick - -Implements `getDirectoryPath()` - -## [1.0.0] - Updates method channel - -Implements `getFiles()` - -## [0.0.3] - Updates interface - -Removes `getFilePath()` - -## [0.0.2] - Updates interface - -Updates methods from File Picker interface - -## [0.0.1+1] - Update README - -Updates README - -## [0.0.1] - Create Platform Interface - -Added Filer Picker platform interface. diff --git a/file_picker_platform_interface/LICENSE b/file_picker_platform_interface/LICENSE deleted file mode 100644 index 6ceb585..0000000 --- a/file_picker_platform_interface/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 Miguel Ruivo - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/file_picker_platform_interface/README.md b/file_picker_platform_interface/README.md deleted file mode 100644 index 68d2f37..0000000 --- a/file_picker_platform_interface/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# MUST READ! - -The interface is deprectated in favor of standalone [file_picker](https://pub.dev/packages/file_picker) for all platforms where an interface is integrated. This should be the one used as this package is not longer mantained. - -# file_picker_platform_interface - -A common platform interface for the [`file_picker`][1] plugin. - -This interface allows platform-specific implementations of the `file_picker` -plugin, as well as the plugin itself, to ensure they are supporting the -same interface. - - -# Usage - -To implement a new platform-specific implementation of `file_picker`, extend -[`FilePickerPlatform`][2] with an implementation that performs the -platform-specific behavior, and when you register your plugin, set the default -`FilePickerPlatform` by calling -`FilePickerPlatform.instance = MyPlatformFilePicker()`. - -# Note on breaking changes - -Strongly prefer non-breaking changes (such as adding a method to the interface) -over breaking changes for this package. - -See https://flutter.dev/go/platform-interface-breaking-changes for a discussion -on why a less-clean interface is preferable to a breaking change. - -[1]: ../file_picker -[2]: lib/file_picker_platform_interface.dart diff --git a/file_picker_platform_interface/lib/file_picker_platform_interface.dart b/file_picker_platform_interface/lib/file_picker_platform_interface.dart deleted file mode 100644 index 0ae6e27..0000000 --- a/file_picker_platform_interface/lib/file_picker_platform_interface.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -import 'method_channel_file_picker.dart'; - -enum FileType { - any, - media, - image, - video, - audio, - custom, -} - -enum FilePickerStatus { - picking, - done, -} - -/// The interface that implementations of file_picker must implement. -/// -/// Platform implementations should extend this class rather than implement it as `file_picker` -/// does not consider newly added methods to be breaking changes. Extending this class -/// (using `extends`) ensures that the subclass will get the default implementation, while -/// platform implementations that `implements` this interface will be broken by newly added -/// [FilePickerPlatform] methods. -abstract class FilePickerPlatform extends PlatformInterface { - /// Constructs a UrlLauncherPlatform. - FilePickerPlatform() : super(token: _token); - - static final Object _token = Object(); - - static FilePickerPlatform _instance = MethodChannelFilePicker(); - - static FilePickerPlatform get instance => _instance; - - static set instance(FilePickerPlatform instance) { - PlatformInterface.verifyToken(instance, _token); - _instance = instance; - } - - /// Retrieves the file(s) from the underlying platform - /// - /// Default [type] set to `FileType.any` with [allowMultiple] set to `false` - /// Optionally, [allowedExtensions] might be provided (e.g. [`pdf`, `svg`, `jpg`].). - /// - /// If you want to track picking status, for example, because some files may take some time to be - /// cached (particularly those picked from cloud providers), you may want to set [onFileLoading] handler - /// that will give you the current status of picking. - /// - /// Allows `dynamic` return as files may be resolved to different types, based - /// on each platform implementation. For example, when using _dart:html_ with Flutter Web - /// or _dart:io_ with Flutter, different `File` instances could be used. - Future getFiles({ - FileType type = FileType.any, - List allowedExtensions, - bool allowMultiple = false, - Function(FilePickerStatus) onFileLoading, - }) async => - throw UnimplementedError('getFiles() has not been implemented.'); - - /// Asks the underlying platform to remove any temporary files created by this plugin. - /// - /// This typically relates to cached files that are stored in the cache directory of - /// each platform and it isn't required to invoke this as the system should take care - /// of it whenever needed. However, this will force the cleanup if you want to manage those on your own. - /// - /// Returns `true` if the files were removed with success, `false` otherwise. - Future clearTemporaryFiles() async => throw UnimplementedError( - 'clearTemporaryFiles() has not been implemented.'); - - /// Selects a directory and returns its absolute path. - /// - /// On Android, this requires to be running on SDK 21 or above, else won't work. - /// Returns `null` if folder path couldn't be resolved. - Future getDirectoryPath() async => - throw UnimplementedError('getDirectoryPath() has not been implemented.'); -} diff --git a/file_picker_platform_interface/lib/method_channel_file_picker.dart b/file_picker_platform_interface/lib/method_channel_file_picker.dart deleted file mode 100644 index be0e271..0000000 --- a/file_picker_platform_interface/lib/method_channel_file_picker.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; - -import 'file_picker_platform_interface.dart'; - -const MethodChannel _channel = - MethodChannel('miguelruivo.flutter.plugins.filepicker'); -const EventChannel _eventChannel = - EventChannel('miguelruivo.flutter.plugins.filepickerevent'); - -/// An implementation of [FilePickerPlatform] that uses method channels. -class MethodChannelFilePicker extends FilePickerPlatform { - static const String _tag = 'MethodChannelFilePicker'; - static StreamSubscription _eventSubscription; - - @override - Future getFiles({ - FileType type = FileType.any, - List allowedExtensions, - bool allowMultiple = false, - bool allowCompression = true, - Function(FilePickerStatus) onFileLoading, - }) => - _getPath(type, allowMultiple, allowCompression, allowedExtensions, - onFileLoading); - - @override - Future clearTemporaryFiles() async => - _channel.invokeMethod('clear'); - - @override - Future getDirectoryPath() async { - try { - return await _channel.invokeMethod('dir', {}); - } on PlatformException catch (ex) { - if (ex.code == "unknown_path") { - print( - '[$_tag] Could not resolve directory path. Maybe it\'s a protected one or unsupported (such as Downloads folder). If you are on Android, make sure that you are on SDK 21 or above.'); - } - return null; - } - } - - Future _getPath( - FileType fileType, - bool allowMultipleSelection, - bool allowCompression, - List allowedExtensions, - Function(FilePickerStatus) onFileLoading, - ) async { - final String type = describeEnum(fileType); - if (type != 'custom' && (allowedExtensions?.isNotEmpty ?? false)) { - throw Exception( - 'If you are using a custom extension filter, please use the FileType.custom instead.'); - } - try { - _eventSubscription?.cancel(); - if (onFileLoading != null) { - _eventSubscription = _eventChannel.receiveBroadcastStream().listen( - (data) => onFileLoading((data as bool) - ? FilePickerStatus.picking - : FilePickerStatus.done), - onError: (error) => throw Exception(error), - ); - } - - dynamic result = await _channel.invokeMethod(type, { - 'allowMultipleSelection': allowMultipleSelection, - 'allowedExtensions': allowedExtensions, - 'allowCompression': allowCompression, - }); - if (result != null && allowMultipleSelection) { - if (result is String) { - result = [result]; - } - return Map.fromIterable(result, - key: (path) => path.split('/').last, value: (path) => path); - } - return result; - } on PlatformException catch (e) { - print('[$_tag] Platform exception: $e'); - rethrow; - } catch (e) { - print( - '[$_tag] Unsupported operation. Method not found. The exception thrown was: $e'); - rethrow; - } - } -} diff --git a/file_picker_platform_interface/pubspec.yaml b/file_picker_platform_interface/pubspec.yaml deleted file mode 100644 index ccf6174..0000000 --- a/file_picker_platform_interface/pubspec.yaml +++ /dev/null @@ -1,16 +0,0 @@ -name: file_picker_platform_interface -description: A common platform interface for the file_picker plugin that must be used to share commom features -homepage: https://github.com/miguelpruivo/plugins_flutter_file_picker/file_picker_platform_interface -version: 2.0.0 - -environment: - sdk: ">=2.1.0 <3.0.0" - -dependencies: - flutter: - sdk: flutter - plugin_platform_interface: ^1.0.1 - -dev_dependencies: - flutter_test: - sdk: flutter diff --git a/file_picker_web/CHANGELOG.md b/file_picker_web/CHANGELOG.md deleted file mode 100644 index ea47c14..0000000 --- a/file_picker_web/CHANGELOG.md +++ /dev/null @@ -1,35 +0,0 @@ -## 2.0.0 - -Deprecates plugin in favor of standalone [file_picker](https://pub.dev/packages/file_picker) for all platforms. - -## 1.0.2+1 - -Fix custom filter String creation. - -## 1.0.2 - -Addresses an issue that would cause dot being required on filters for web (#343) - -## 1.0.1+2 - -Bumps file_picker_interface dependency version. - -## 1.0.1+1 - -Updates homepage & description. - -## 1.0.1 - -Updates API to support `onFileLoading` from FilePickerInterface. - -## 1.0.0 - -Adds public API documentation and updates file_picker_platform_interface dependency. - -## 0.0.2 - -Added no-op iOS podspec to prevent build issues on iOS. - -## 0.0.1 - -Creation of File Picker Web project draft (not yet usable) diff --git a/file_picker_web/LICENSE b/file_picker_web/LICENSE deleted file mode 100644 index 6ab60fa..0000000 --- a/file_picker_web/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 Miguel Ruivo - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/file_picker_web/README.md b/file_picker_web/README.md deleted file mode 100644 index 30cd96e..0000000 --- a/file_picker_web/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# MUST READ! - -The web standalone plugin is deprectated in favor of standalone [file_picker](https://pub.dev/packages/file_picker) for all platforms. This should be the one used as this package is not longer mantained. - -# file_picker_web - -The web implementation of [`file_picker`][1]. - -## Usage - -### Import the package -To use this plugin in your Flutter Web app, simply add it as a dependency in -your pubspec alongside the base `file_picker` plugin. - -_(This is only temporary: in the future we hope to make this package an -"endorsed" implementation of `file_picker`, so that it is automatically -included in your Flutter Web app when you depend on `package:file_picker`.)_ - -This is what the above means to your `pubspec.yaml`: - -```yaml -... -dependencies: - ... - file_picker_web: ^1.0.0 - ... -``` - -### Use the plugin -Once you have the `file_picker_web` dependency in your pubspec, you should -be able to use `package:file_picker` as normal. - -[1]: ../file_picker diff --git a/file_picker_web/example/.gitignore b/file_picker_web/example/.gitignore deleted file mode 100644 index 1ba9c33..0000000 --- a/file_picker_web/example/.gitignore +++ /dev/null @@ -1,43 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.packages -.pub-cache/ -.pub/ -/build/ - -# Web related -lib/generated_plugin_registrant.dart - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json - -# Exceptions to above rules. -!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/file_picker_web/example/.metadata b/file_picker_web/example/.metadata deleted file mode 100644 index bb25afd..0000000 --- a/file_picker_web/example/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: e6b34c2b5c96bb95325269a29a84e83ed8909b5f - channel: beta - -project_type: app diff --git a/file_picker_web/example/README.md b/file_picker_web/example/README.md deleted file mode 100644 index bce5708..0000000 --- a/file_picker_web/example/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# file_picker_web_example - -Demonstrates how to use the file_picker_web plugin. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/file_picker_web/example/lib/main.dart b/file_picker_web/example/lib/main.dart deleted file mode 100644 index 279b607..0000000 --- a/file_picker_web/example/lib/main.dart +++ /dev/null @@ -1,65 +0,0 @@ -// ignore: avoid_web_libraries_in_flutter -import 'dart:html'; -import 'package:file_picker_web/file_picker_web.dart'; -import 'package:flutter/material.dart'; - -void main() { - runApp(MyApp()); -} - -class MyApp extends StatefulWidget { - @override - _MyAppState createState() => _MyAppState(); -} - -class _MyAppState extends State { - List _files = []; - - void _pickFiles() async { - _files = await FilePicker.getMultiFile() ?? []; - setState(() {}); - } - - @override - Widget build(BuildContext context) { - return MaterialApp( - home: Scaffold( - appBar: AppBar( - title: const Text('Plugin example app'), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Expanded( - child: _files.isNotEmpty - ? ListView.separated( - itemBuilder: (BuildContext context, int index) => - Text(_files[index].name), - itemCount: _files.length, - separatorBuilder: (_, __) => const Divider( - thickness: 5.0, - ), - ) - : Center( - child: Text( - 'Pick some files', - textAlign: TextAlign.center, - ), - ), - ), - Padding( - padding: const EdgeInsets.all(15.0), - child: RaisedButton( - onPressed: _pickFiles, - child: Text('Pick Files'), - ), - ) - ], - ), - ), - ), - ); - } -} diff --git a/file_picker_web/example/pubspec.yaml b/file_picker_web/example/pubspec.yaml deleted file mode 100644 index dc463fb..0000000 --- a/file_picker_web/example/pubspec.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: file_picker_web_example -description: Demonstrates how to use the file_picker_web plugin. - -# The following line prevents the package from being accidentally published to -# pub.dev using `pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -environment: - sdk: ">=2.7.0 <3.0.0" - -dependencies: - flutter: - sdk: flutter - - file_picker_web: - path: ../ - - cupertino_icons: ^0.1.3 - -dev_dependencies: - flutter_test: - sdk: flutter - - -flutter: - - uses-material-design: true diff --git a/file_picker_web/example/web/favicon.png b/file_picker_web/example/web/favicon.png deleted file mode 100644 index 8aaa46ac1ae21512746f852a42ba87e4165dfdd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM diff --git a/file_picker_web/example/web/icons/Icon-192.png b/file_picker_web/example/web/icons/Icon-192.png deleted file mode 100644 index b749bfef07473333cf1dd31e9eed89862a5d52aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5292 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 diff --git a/file_picker_web/example/web/icons/Icon-512.png b/file_picker_web/example/web/icons/Icon-512.png deleted file mode 100644 index 88cfd48dff1169879ba46840804b412fe02fefd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8252 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 diff --git a/file_picker_web/example/web/index.html b/file_picker_web/example/web/index.html deleted file mode 100644 index c1b8f5a..0000000 --- a/file_picker_web/example/web/index.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - file_picker_web_example - - - - - - - - diff --git a/file_picker_web/example/web/manifest.json b/file_picker_web/example/web/manifest.json deleted file mode 100644 index 3f21237..0000000 --- a/file_picker_web/example/web/manifest.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "file_picker_web_example", - "short_name": "file_picker_web_example", - "start_url": ".", - "display": "minimal-ui", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "Demonstrates how to use the file_picker_web plugin.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - } - ] -} diff --git a/file_picker_web/ios/file_picker_web.podspec b/file_picker_web/ios/file_picker_web.podspec deleted file mode 100644 index 5e4bb8c..0000000 --- a/file_picker_web/ios/file_picker_web.podspec +++ /dev/null @@ -1,20 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html -# -Pod::Spec.new do |s| - s.name = 'file_picker_web' - s.version = '0.0.1' - s.summary = 'No-op implementation of file_picker_web web plugin to avoid build issues on iOS' - s.description = <<-DESC - temp fake video_player_web plugin - DESC - s.homepage = 'https://github.com/miguelpruivo/plugins_flutter_file_picker/file_picker_web' - s.license = { :file => '../LICENSE' } - s.author = { 'Miguel Ruivo' => 'miguel@miguelruivo.com' } - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.public_header_files = 'Classes/**/*.h' - s.dependency 'Flutter' - - s.ios.deployment_target = '8.0' - end \ No newline at end of file diff --git a/file_picker_web/lib/file_picker_web.dart b/file_picker_web/lib/file_picker_web.dart deleted file mode 100644 index 30bebe3..0000000 --- a/file_picker_web/lib/file_picker_web.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'dart:async'; - -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; -import 'package:flutter_web_plugins/flutter_web_plugins.dart'; -import 'dart:html' as html; - -class FilePicker extends FilePickerPlatform { - FilePicker._(); - static final FilePicker _instance = FilePicker._(); - - static void registerWith(Registrar registrar) { - FilePickerPlatform.instance = _instance; - } - - /// Opens browser file picker window to select multiple files. - /// [type] defaults to `FileType.any` which allows all file types to be picked. Optionally, - /// [allowedExtensions] can be used (eg. `[.jpg, .pdf]`) to restrict picking types - /// - /// Returns a `List` - static Future> getMultiFile( - {FileType type = FileType.any, List allowedExtensions}) async { - return await _instance.getFiles( - type: type, allowMultiple: true, allowedExtensions: allowedExtensions); - } - - /// Opens browser file picker window to select a single file. - /// [type] defaults to `FileType.any` which allows all file types to be picked. Optionally, - /// [allowedExtensions] can be used (eg. `[.jpg, .pdf]`) to restrict picking types - /// - /// Returns a `html.File` - static Future getFile( - {FileType type = FileType.any, List allowedExtensions}) async { - return (await _instance.getFiles( - type: type, allowedExtensions: allowedExtensions)) - .first; - } - - @override - Future getFiles({ - FileType type = FileType.any, - List allowedExtensions, - bool allowMultiple = false, - Function(FilePickerStatus) onFileLoading, - }) async { - final Completer> pickedFiles = Completer>(); - html.InputElement uploadInput = html.FileUploadInputElement(); - uploadInput.multiple = allowMultiple; - uploadInput.accept = _fileType(type, allowedExtensions); - uploadInput.onChange - .listen((event) => pickedFiles.complete(uploadInput.files)); - uploadInput.click(); - return await pickedFiles.future; - } - - static String _fileType(FileType type, List allowedExtensions) { - switch (type) { - case FileType.any: - return ''; - - case FileType.audio: - return 'audio/*'; - - case FileType.image: - return 'image/*'; - - case FileType.video: - return 'video/*'; - - case FileType.media: - return 'video/*|image/*'; - - case FileType.custom: - return allowedExtensions.fold( - '', (prev, next) => '${prev.isEmpty ? '' : '$prev,'} .$next'); - break; - } - return ''; - } -} diff --git a/file_picker_web/pubspec.yaml b/file_picker_web/pubspec.yaml deleted file mode 100644 index 9001480..0000000 --- a/file_picker_web/pubspec.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: file_picker_web -description: Web platform implementation of file_picker. Provides a way to pick files with filter support for Web. -homepage: https://github.com/miguelpruivo/flutter_file_picker/tree/master/file_picker_web -version: 2.0.0 - -environment: - sdk: ">=2.7.0 <3.0.0" - flutter: ">=1.10.0" - -dependencies: - file_picker_platform_interface: ^2.0.0 - flutter: - sdk: flutter - flutter_web_plugins: - sdk: flutter - -dev_dependencies: - flutter_test: - sdk: flutter - - -flutter: - plugin: - platforms: - web: - pluginClass: FilePicker - fileName: file_picker_web.dart diff --git a/file_picker/go/README.md b/go/README.md similarity index 100% rename from file_picker/go/README.md rename to go/README.md diff --git a/file_picker/go/file_darwin.go b/go/file_darwin.go similarity index 100% rename from file_picker/go/file_darwin.go rename to go/file_darwin.go diff --git a/file_picker/go/file_linux.go b/go/file_linux.go similarity index 100% rename from file_picker/go/file_linux.go rename to go/file_linux.go diff --git a/file_picker/go/file_unsupported.go b/go/file_unsupported.go similarity index 100% rename from file_picker/go/file_unsupported.go rename to go/file_unsupported.go diff --git a/file_picker/go/file_windows.go b/go/file_windows.go similarity index 100% rename from file_picker/go/file_windows.go rename to go/file_windows.go diff --git a/file_picker/go/go.mod b/go/go.mod similarity index 100% rename from file_picker/go/go.mod rename to go/go.mod diff --git a/file_picker/go/go.sum b/go/go.sum similarity index 100% rename from file_picker/go/go.sum rename to go/go.sum diff --git a/file_picker/go/import.go.tmpl b/go/import.go.tmpl similarity index 100% rename from file_picker/go/import.go.tmpl rename to go/import.go.tmpl diff --git a/file_picker/go/plugin.go b/go/plugin.go similarity index 100% rename from file_picker/go/plugin.go rename to go/plugin.go diff --git a/file_picker/ios/.gitignore b/ios/.gitignore similarity index 100% rename from file_picker/ios/.gitignore rename to ios/.gitignore diff --git a/file_picker/ios/Assets/.gitkeep b/ios/Assets/.gitkeep similarity index 100% rename from file_picker/ios/Assets/.gitkeep rename to ios/Assets/.gitkeep diff --git a/file_picker/ios/Classes/FileInfo.h b/ios/Classes/FileInfo.h similarity index 100% rename from file_picker/ios/Classes/FileInfo.h rename to ios/Classes/FileInfo.h diff --git a/file_picker/ios/Classes/FileInfo.m b/ios/Classes/FileInfo.m similarity index 100% rename from file_picker/ios/Classes/FileInfo.m rename to ios/Classes/FileInfo.m diff --git a/file_picker/ios/Classes/FilePickerPlugin.h b/ios/Classes/FilePickerPlugin.h similarity index 100% rename from file_picker/ios/Classes/FilePickerPlugin.h rename to ios/Classes/FilePickerPlugin.h diff --git a/file_picker/ios/Classes/FilePickerPlugin.m b/ios/Classes/FilePickerPlugin.m similarity index 100% rename from file_picker/ios/Classes/FilePickerPlugin.m rename to ios/Classes/FilePickerPlugin.m diff --git a/file_picker/ios/Classes/FileUtils.h b/ios/Classes/FileUtils.h similarity index 100% rename from file_picker/ios/Classes/FileUtils.h rename to ios/Classes/FileUtils.h diff --git a/file_picker/ios/Classes/FileUtils.m b/ios/Classes/FileUtils.m similarity index 100% rename from file_picker/ios/Classes/FileUtils.m rename to ios/Classes/FileUtils.m diff --git a/file_picker/ios/Classes/ImageUtils.h b/ios/Classes/ImageUtils.h similarity index 100% rename from file_picker/ios/Classes/ImageUtils.h rename to ios/Classes/ImageUtils.h diff --git a/file_picker/ios/Classes/ImageUtils.m b/ios/Classes/ImageUtils.m similarity index 100% rename from file_picker/ios/Classes/ImageUtils.m rename to ios/Classes/ImageUtils.m diff --git a/file_picker/ios/file_picker.podspec b/ios/file_picker.podspec similarity index 100% rename from file_picker/ios/file_picker.podspec rename to ios/file_picker.podspec diff --git a/file_picker/lib/file_picker.dart b/lib/file_picker.dart similarity index 100% rename from file_picker/lib/file_picker.dart rename to lib/file_picker.dart diff --git a/file_picker/lib/src/file_picker.dart b/lib/src/file_picker.dart similarity index 100% rename from file_picker/lib/src/file_picker.dart rename to lib/src/file_picker.dart diff --git a/file_picker/lib/src/file_picker_io.dart b/lib/src/file_picker_io.dart similarity index 100% rename from file_picker/lib/src/file_picker_io.dart rename to lib/src/file_picker_io.dart diff --git a/file_picker/lib/src/file_picker_result.dart b/lib/src/file_picker_result.dart similarity index 100% rename from file_picker/lib/src/file_picker_result.dart rename to lib/src/file_picker_result.dart diff --git a/file_picker/lib/src/file_picker_web.dart b/lib/src/file_picker_web.dart similarity index 100% rename from file_picker/lib/src/file_picker_web.dart rename to lib/src/file_picker_web.dart diff --git a/file_picker/lib/src/platform_file.dart b/lib/src/platform_file.dart similarity index 100% rename from file_picker/lib/src/platform_file.dart rename to lib/src/platform_file.dart diff --git a/file_picker/pubspec.yaml b/pubspec.yaml similarity index 100% rename from file_picker/pubspec.yaml rename to pubspec.yaml From 4da8c0f180b98780e71d050a070f76760b7a24da Mon Sep 17 00:00:00 2001 From: Miguel Ruivo Date: Fri, 11 Sep 2020 18:37:45 +0100 Subject: [PATCH 6/6] Updates docs --- CHANGELOG.md | 17 +++++++++ README.md | 50 ++++++++++++++++++++------- example/lib/src/file_picker_demo.dart | 48 ++++++++++++++++++------- lib/src/file_picker.dart | 22 ++++++++---- lib/src/file_picker_io.dart | 22 ++++++++---- lib/src/file_picker_result.dart | 5 ++- lib/src/file_picker_web.dart | 9 +++-- lib/src/platform_file.dart | 7 +--- 8 files changed, 131 insertions(+), 49 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 725daae..aab3f57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +## 2.0.0 +**Breaking Changes** +- Unifies all platforms (IO, Web and Desktop) in a single plugin (file_picker) that can be used seamlessly across all. Both [file_picker_interface](https://pub.dev/packages/file_picker_platform_interface) and [file_picker_web](https://pub.dev/packages/file_picker_web) are no longer mantained from now on. +- You'll now have to use `FilePicker.platform.pickFiles()` and extract the files from `FilePickerResult`; +- Because platforms are unified and mixing `File` instances from dart:io and dart:html required stubbing and bloated code, it's no longer available as helper method so you'll have to instanciate a `File` with the picked paths; + +**New features** +- Simplified usage, now you only need to use `FilePicker.platform.pickFiles()` with desired parameters which will return a `FilePickerResult` with a `List` containing the picked data; +- Added classes `FilePickerResult` and `PlatformFile` classes with helper getters; +- On Android all picked files are scoped cached which should result in most of files being available. Caching process is only made once, so once done, the picked instance should be the same; +- On iOS picking audio now supports multiple and cloud picks; +- Added parameter `withData` that allows file data to be immediately available on memory as `Uint8List` (part of `PlatformFile` instance). This is particularly helpful on web or if you are going to upload to somehwere else; +- Major refactor with some clean-up and improvements; + +**Removed** +- Single methods such as `getFilePath()`, `getMultiFilePath()`, `getFile()` and `getMultiFile()` are no longer availble in favor o `pickFiles()`; + ## 1.13.3 Go: Updates MacOS directory picker to applescript (thank you @trister1997). diff --git a/README.md b/README.md index 74ee6c3..6b61dac 100644 --- a/README.md +++ b/README.md @@ -18,16 +18,17 @@ A package that allows you to use a native file explorer to pick single or multiple absolute file paths, with extensions filtering support. ## Currently supported features -* Load paths from **cloud files** (GDrive, Dropbox, iCloud) -* Load path from a **custom format** by providing a list of file extensions (pdf, svg, zip, etc.) -* Load path from **multiple files** optionally, supplying file extensions -* Load path from **media** (video & image only) -* Load path from **audio** only -* Load path from **image** only -* Load path from **video** only -* Load path from **directory** -* Load path from **any** -* Create a `File` or `List` objects from **any** selected file(s) +* Load files from **cloud files** (GDrive, Dropbox, iCloud) +* Load files from a **custom format** by providing a list of file extensions (pdf, svg, zip, etc.) +* Load files from **multiple files** optionally, supplying file extensions +* Load files from **media** (video & image only) +* Load files from **audio** only +* Load files from **image** only +* Load files from **video** only +* Load files from **directory** +* Load files from **any** +* Load files data immediately to memory (`Uint8List`); +* Supports web; * Supports desktop through **go-flutter** (MacOS, Windows, Linux) If you have any feature that you want to see in this package, please feel free to issue a suggestion. 🎉 @@ -54,19 +55,42 @@ Quick simple usage example: #### Single file ``` -File file = await FilePicker.getFile(); +FilePickerResult result = await FilePicker.platform.pickFiles(); + +if(result != null) { + File file = File(result.files.single.path); +} ``` #### Multiple files ``` -List files = await FilePicker.getMultiFile(); +FilePickerResult result = await FilePicker.platform.pickFiles(allowMultiple: true); + +if(result != null) { + List files = result.paths.map((path) => File(path)); +} ``` #### Multiple files with extension filter ``` - List files = await FilePicker.getMultiFile( +FilePickerResult result = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: ['jpg', 'pdf', 'doc'], ); ``` +### Load result and file details +``` +FilePickerResult result = await FilePicker.platform.pickFiles(); + +if(result != null) { + PlatformFile file = result.files.first; + + print(file.name); + print(file.bytes); + print(file.size); + print(file.extension); + print(file.path); +} +``` + For full usage details refer to the **[Wiki](https://github.com/miguelpruivo/flutter_file_picker/wiki)** above. ## Example App diff --git a/example/lib/src/file_picker_demo.dart b/example/lib/src/file_picker_demo.dart index 2b114e4..c67d222 100644 --- a/example/lib/src/file_picker_demo.dart +++ b/example/lib/src/file_picker_demo.dart @@ -32,7 +32,9 @@ class _FilePickerDemoState extends State { _paths = (await FilePicker.platform.pickFiles( type: _pickingType, allowMultiple: _multiPick, - allowedExtensions: (_extension?.isNotEmpty ?? false) ? _extension?.replaceAll(' ', '')?.split(',') : null, + allowedExtensions: (_extension?.isNotEmpty ?? false) + ? _extension?.replaceAll(' ', '')?.split(',') + : null, )) ?.files; } on PlatformException catch (e) { @@ -52,7 +54,9 @@ class _FilePickerDemoState extends State { _scaffoldKey.currentState.showSnackBar( SnackBar( backgroundColor: result ? Colors.green : Colors.red, - content: Text((result ? 'Temporary files removed with success.' : 'Failed to clean temporary files')), + content: Text((result + ? 'Temporary files removed with success.' + : 'Failed to clean temporary files')), ), ); }); @@ -124,7 +128,8 @@ class _FilePickerDemoState extends State { maxLength: 15, autovalidate: true, controller: _controller, - decoration: InputDecoration(labelText: 'File extension'), + decoration: + InputDecoration(labelText: 'File extension'), keyboardType: TextInputType.text, textCapitalization: TextCapitalization.none, ) @@ -133,8 +138,10 @@ class _FilePickerDemoState extends State { ConstrainedBox( constraints: const BoxConstraints.tightFor(width: 200.0), child: SwitchListTile.adaptive( - title: Text('Pick multiple files', textAlign: TextAlign.right), - onChanged: (bool value) => setState(() => _multiPick = value), + title: + Text('Pick multiple files', textAlign: TextAlign.right), + onChanged: (bool value) => + setState(() => _multiPick = value), value: _multiPick, ), ), @@ -171,15 +178,28 @@ class _FilePickerDemoState extends State { : _paths != null ? Container( padding: const EdgeInsets.only(bottom: 30.0), - height: MediaQuery.of(context).size.height * 0.50, + height: + MediaQuery.of(context).size.height * 0.50, child: Scrollbar( child: 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.map((e) => e.name).toList()[index] : _fileName ?? '...'); - final path = _paths.map((e) => e.path).toList()[index].toString(); + 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 + .map((e) => e.name) + .toList()[index] + : _fileName ?? '...'); + final path = _paths + .map((e) => e.path) + .toList()[index] + .toString(); return ListTile( title: Text( @@ -188,7 +208,9 @@ class _FilePickerDemoState extends State { subtitle: Text(path), ); }, - separatorBuilder: (BuildContext context, int index) => const Divider(), + separatorBuilder: + (BuildContext context, int index) => + const Divider(), )), ) : const SizedBox(), diff --git a/lib/src/file_picker.dart b/lib/src/file_picker.dart index d1646f2..cd16d75 100644 --- a/lib/src/file_picker.dart +++ b/lib/src/file_picker.dart @@ -43,12 +43,20 @@ abstract class FilePicker extends PlatformInterface { /// Retrieves the file(s) from the underlying platform /// - /// Default [type] set to `FileType.any` with [allowMultiple] set to `false` - /// Optionally, [allowedExtensions] might be provided (e.g. [`pdf`, `svg`, `jpg`].). + /// Default [type] set to [FileType.any] with [allowMultiple] set to [false] + /// Optionally, [allowedExtensions] might be provided (e.g. `[pdf, svg, jpg]`.). + /// + /// If [withData] is set, picked files will have its byte data immediately available on memory as [Uint8List] + /// which can be useful if you are picking it for server upload or similar. /// /// If you want to track picking status, for example, because some files may take some time to be /// cached (particularly those picked from cloud providers), you may want to set [onFileLoading] handler /// that will give you the current status of picking. + /// + /// The result is wrapped in a [FilePickerResult] which contains helper getters + /// with useful information regarding the picked [List]. + /// + /// Returns [null] if aborted. Future pickFiles({ FileType type = FileType.any, List allowedExtensions, @@ -65,12 +73,14 @@ abstract class FilePicker extends PlatformInterface { /// each platform and it isn't required to invoke this as the system should take care /// of it whenever needed. However, this will force the cleanup if you want to manage those on your own. /// - /// Returns `true` if the files were removed with success, `false` otherwise. - Future clearTemporaryFiles() async => throw UnimplementedError('clearTemporaryFiles() has not been implemented.'); + /// Returns [true] if the files were removed with success, [false] otherwise. + Future clearTemporaryFiles() async => throw UnimplementedError( + 'clearTemporaryFiles() has not been implemented.'); /// Selects a directory and returns its absolute path. /// /// On Android, this requires to be running on SDK 21 or above, else won't work. - /// Returns `null` if folder path couldn't be resolved. - Future getDirectoryPath() async => throw UnimplementedError('getDirectoryPath() has not been implemented.'); + /// Returns [null] if folder path couldn't be resolved. + Future getDirectoryPath() async => + throw UnimplementedError('getDirectoryPath() has not been implemented.'); } diff --git a/lib/src/file_picker_io.dart b/lib/src/file_picker_io.dart index d41860a..0d3dfbd 100644 --- a/lib/src/file_picker_io.dart +++ b/lib/src/file_picker_io.dart @@ -7,8 +7,10 @@ import 'package:flutter/services.dart'; import 'file_picker_result.dart'; -const MethodChannel _channel = MethodChannel('miguelruivo.flutter.plugins.filepicker'); -const EventChannel _eventChannel = EventChannel('miguelruivo.flutter.plugins.filepickerevent'); +const MethodChannel _channel = + MethodChannel('miguelruivo.flutter.plugins.filepicker'); +const EventChannel _eventChannel = + EventChannel('miguelruivo.flutter.plugins.filepickerevent'); /// An implementation of [FilePicker] that uses method channels. class FilePickerIO extends FilePicker { @@ -34,7 +36,8 @@ class FilePickerIO extends FilePicker { ); @override - Future clearTemporaryFiles() async => _channel.invokeMethod('clear'); + Future clearTemporaryFiles() async => + _channel.invokeMethod('clear'); @override Future getDirectoryPath() async { @@ -59,13 +62,16 @@ class FilePickerIO extends FilePicker { ) async { final String type = describeEnum(fileType); if (type != 'custom' && (allowedExtensions?.isNotEmpty ?? false)) { - throw Exception('If you are using a custom extension filter, please use the FileType.custom instead.'); + throw Exception( + 'If you are using a custom extension filter, please use the FileType.custom instead.'); } try { _eventSubscription?.cancel(); if (onFileLoading != null) { _eventSubscription = _eventChannel.receiveBroadcastStream().listen( - (data) => onFileLoading((data as bool) ? FilePickerStatus.picking : FilePickerStatus.done), + (data) => onFileLoading((data as bool) + ? FilePickerStatus.picking + : FilePickerStatus.done), onError: (error) => throw Exception(error), ); } @@ -81,12 +87,14 @@ class FilePickerIO extends FilePicker { return null; } - return FilePickerResult(result.map((file) => PlatformFile.fromMap(file)).toList()); + return FilePickerResult( + result.map((file) => PlatformFile.fromMap(file)).toList()); } on PlatformException catch (e) { print('[$_tag] Platform exception: $e'); rethrow; } catch (e) { - print('[$_tag] Unsupported operation. Method not found. The exception thrown was: $e'); + print( + '[$_tag] Unsupported operation. Method not found. The exception thrown was: $e'); rethrow; } } diff --git a/lib/src/file_picker_result.dart b/lib/src/file_picker_result.dart index b3b5cfc..30432ee 100644 --- a/lib/src/file_picker_result.dart +++ b/lib/src/file_picker_result.dart @@ -19,7 +19,10 @@ class FilePickerResult { /// original files (which can be accessed through its URI property). /// /// Only available on IO. Throws `UnsupportedError` on Web. - List get paths => files.map((file) => kIsWeb ? throw UnsupportedError('Unsupported on Web') : file.path).toList(); + List get paths => files + .map((file) => + kIsWeb ? throw UnsupportedError('Unsupported on Web') : file.path) + .toList(); /// A `List` containing all names from picked files with its extensions. List get names => files.map((file) => file.name).toList(); diff --git a/lib/src/file_picker_web.dart b/lib/src/file_picker_web.dart index 6d951a0..025b92f 100644 --- a/lib/src/file_picker_web.dart +++ b/lib/src/file_picker_web.dart @@ -26,7 +26,8 @@ class FilePickerWeb extends FilePicker { bool allowCompression, bool withData = true, }) async { - final Completer> filesCompleter = Completer>(); + final Completer> filesCompleter = + Completer>(); String accept = _fileType(type, allowedExtensions); html.InputElement uploadInput = html.FileUploadInputElement(); @@ -42,7 +43,8 @@ class FilePickerWeb extends FilePicker { List pickedFiles = []; reader.onLoadEnd.listen((e) { - final Uint8List bytes = Base64Decoder().convert(reader.result.toString().split(",").last); + final Uint8List bytes = + Base64Decoder().convert(reader.result.toString().split(",").last); pickedFiles.add( PlatformFile( @@ -83,7 +85,8 @@ class FilePickerWeb extends FilePicker { return 'video/*|image/*'; case FileType.custom: - return allowedExtensions.fold('', (prev, next) => '${prev.isEmpty ? '' : '$prev,'} .$next'); + return allowedExtensions.fold( + '', (prev, next) => '${prev.isEmpty ? '' : '$prev,'} .$next'); break; } return ''; diff --git a/lib/src/platform_file.dart b/lib/src/platform_file.dart index 435e14f..2ddebd2 100644 --- a/lib/src/platform_file.dart +++ b/lib/src/platform_file.dart @@ -6,15 +6,13 @@ class PlatformFile { this.name, this.bytes, this.size, - this.isDirectory = false, }); PlatformFile.fromMap(Map data) : this.path = data['path'], this.name = data['name'], this.bytes = data['bytes'], - this.size = data['size'], - this.isDirectory = data['isDirectory']; + this.size = data['size']; /// The absolute path for a cached copy of this file. It can be used to create a /// a file instance with a descriptor for the given path. @@ -33,9 +31,6 @@ class PlatformFile { /// The file size in KB. final int size; - /// Whether this file references a directory or not. - final bool isDirectory; - /// File extension for this file. String get extension => path?.split('.')?.last; }