Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 6.1.0
### Android
- saveFile() method implemented for the android platform
## 6.0.0
Update minimum Flutter version to 3.7.0.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ public FileInfo(String path, String name, Uri uri, long size, byte[] bytes) {
this.uri = uri;
}

public String toString() {
return "path: " + this.path + ", name: " + this.name;
}

public static class Builder {

private String path;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener
private boolean isMultipleSelection = false;
private boolean loadDataToMemory = false;
private String type;
private String input;
private String[] allowedExtensions;
private EventChannel.EventSink eventSink;

Expand Down Expand Up @@ -119,16 +120,26 @@ public void run() {
finishWithError("unknown_path", "Failed to retrieve directory path.");
}
return;
} else if (type.equals("save") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Log.d(FilePickerDelegate.TAG, "[SaveFile] File URI:" + uri.toString());
final String filePath = FileUtils.getAbsolutePathFromUri(uri, input, activity);
if(filePath != null) {
Log.d(FilePickerDelegate.TAG, "[SaveFile] File Path:" + filePath);
finishWithSuccess(filePath);
} else {
finishWithError("unknown_path", "Failed to retrieve file path.");
}
return;
}

final FileInfo file = FileUtils.openFileStream(FilePickerDelegate.this.activity, uri, loadDataToMemory);

if(file != null) {
Log.d(FilePickerDelegate.TAG, file.toString());
files.add(file);
}

if (!files.isEmpty()) {
Log.d(FilePickerDelegate.TAG, "File path:" + files.toString());
finishWithSuccess(files);
} else {
finishWithError("unknown_path", "Failed to retrieve path.");
Expand Down Expand Up @@ -229,6 +240,14 @@ private void startFileExplorer() {

if (type.equals("dir")) {
intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
} else if (type.equals("save")) {
intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
Log.d(TAG, "Selected type " + type);
intent.setType("*/*");
if (allowedExtensions != null) {
intent.putExtra(Intent.EXTRA_MIME_TYPES, allowedExtensions);
}
} else {
if (type.equals("image/*")) {
intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
Expand Down Expand Up @@ -261,14 +280,15 @@ private void startFileExplorer() {
}

@SuppressWarnings("deprecation")
public void startFileExplorer(final String type, final boolean isMultipleSelection, final boolean withData, final String[] allowedExtensions, final MethodChannel.Result result) {
public void startFileExplorer(final String type, final String input, final boolean isMultipleSelection, final boolean withData, final String[] allowedExtensions, final MethodChannel.Result result) {

if (!this.setPendingMethodCallAndResult(result)) {
finishWithAlreadyActiveError(result);
return;
}

this.type = type;
this.input = input;
this.isMultipleSelection = isMultipleSelection;
this.loadDataToMemory = withData;
this.allowedExtensions = allowedExtensions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ public void onActivityStopped(final Activity activity) {
private Activity activity;
private MethodChannel channel;
private static String fileType;
private static String inputFile;
private static boolean isMultipleSelection = false;
private static boolean withData = false;

Expand Down Expand Up @@ -159,6 +160,9 @@ public void onMethodCall(final MethodCall call, final MethodChannel.Result rawRe

if (fileType == null) {
result.notImplemented();
} else if (fileType == "save") {
inputFile = (String) arguments.get("inputFile");
allowedExtensions = FileUtils.getMimeTypes((ArrayList<String>) arguments.get("allowedExtensions"));
} else if (fileType != "dir") {
isMultipleSelection = (boolean) arguments.get("allowMultipleSelection");
withData = (boolean) arguments.get("withData");
Expand All @@ -168,7 +172,7 @@ public void onMethodCall(final MethodCall call, final MethodChannel.Result rawRe
if (call.method != null && call.method.equals("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, withData, allowedExtensions, result);
this.delegate.startFileExplorer(fileType, inputFile, isMultipleSelection, withData, allowedExtensions, result);
}

}
Expand All @@ -189,6 +193,9 @@ private static String resolveType(final String type) {
return "*/*";
case "dir":
return "dir";
case "save":
return "save";

default:
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.storage.StorageVolume;
import android.os.storage.StorageManager;
import android.provider.DocumentsContract;
import android.provider.OpenableColumns;
import android.util.Log;
import android.webkit.MimeTypeMap;
import java.util.List;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
Expand All @@ -24,6 +26,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.ArrayList;
Expand Down Expand Up @@ -178,6 +181,60 @@ public static FileInfo openFileStream(final Context context, final Uri uri, bool
return fileInfo.build();
}

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Nullable
@SuppressWarnings("deprecation")
public static String getAbsolutePathFromUri(Uri uri, String input, Context context) {
String absolutePath = null;
if (DocumentsContract.isDocumentUri(context, uri)) {

// DocumentProvider
String documentId = DocumentsContract.getDocumentId(uri);
String[] split = documentId.split(":");
String authority = split[0];
String path = split[1];

try {
StorageManager storageManager =
(StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
StorageVolume storageVolume = null;
List<StorageVolume> storageVolumes = storageManager.getStorageVolumes();
for (StorageVolume volume : storageVolumes) {
if (
PRIMARY_VOLUME_NAME.equals(authority) ||
(volume.getUuid() != null && volume.getUuid().equals(authority))
) {
storageVolume = volume;
break;
}
}

if (storageVolume != null) {
String rootPath = storageVolume.getDirectory().getAbsolutePath();
absolutePath = rootPath + "/" + path;
try {
File iFile = new File(input);
if (iFile.exists() && iFile.isFile()) {
OutputStream outputStream = context.getContentResolver().openOutputStream(uri);
InputStream inputStream = new FileInputStream(input);
byte[] buffer = new byte[1024];
int bytesRead = 0;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}

return absolutePath;
}

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Nullable
@SuppressWarnings("deprecation")
Expand Down Expand Up @@ -307,4 +364,4 @@ private static String getDocumentPathFromTreeUri(final Uri treeUri) {
else return File.separator;
}

}
}
43 changes: 43 additions & 0 deletions lib/src/file_picker_io.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@ class FilePickerIO extends FilePicker {
withReadStream,
);

@override
Future<String?> saveFile({
String? dialogTitle,
String? fileName,
String? initialDirectory,
FileType type = FileType.any,
List<String>? allowedExtensions,
bool lockParentWindow = false,
}) =>
_getSavePath(
fileName!,
type,
allowedExtensions,
);

@override
Future<bool?> clearTemporaryFiles() async =>
_channel.invokeMethod<bool>('clear');
Expand Down Expand Up @@ -122,4 +137,32 @@ class FilePickerIO extends FilePicker {
rethrow;
}
}

Future<String?> _getSavePath(
String inputFile,
FileType fileType,
List<String>? allowedExtensions,
) async {
final String type = fileType.name;
if (type != 'custom' && (allowedExtensions?.isNotEmpty ?? false)) {
throw Exception(
'You are setting a type [$fileType]. Custom extension filters are only allowed with FileType.custom, please change it or remove filters.');
}
try {
return await _channel.invokeMethod('save', {
'inputFile': inputFile,
'allowedExtensions': allowedExtensions,
});
} on PlatformException catch (e) {
if (e.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.');
}
print('[$_tag] Platform exception: $e');
} catch (e) {
print(
'[$_tag] Unsupported operation. Method not found. The exception thrown was: $e');
}
return null;
}
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: A package that allows you to use a native file explorer to pick sin
homepage: https://github.com/miguelpruivo/plugins_flutter_file_picker
repository: https://github.com/miguelpruivo/flutter_file_picker
issue_tracker: https://github.com/miguelpruivo/flutter_file_picker/issues
version: 6.0.0
version: 6.1.0

dependencies:
flutter:
Expand Down