Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix file & image picking with android apps that has launchMode=singleInstance #1380

Closed
wants to merge 11 commits into from
Closed
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.mr.flutter.plugin.filepicker;

import static androidx.core.app.ActivityCompat.startActivityForResult;

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
Expand Down Expand Up @@ -39,6 +41,7 @@ public class FilePickerDelegate implements PluginRegistry.ActivityResultListener
private String type;
private String[] allowedExtensions;
private EventChannel.EventSink eventSink;
private String imagePickerTitle;

public FilePickerDelegate(final Activity activity) {
this(
Expand All @@ -65,7 +68,11 @@ public void setEventHandler(final EventChannel.EventSink eventSink) {
}

@VisibleForTesting
FilePickerDelegate(final Activity activity, final MethodChannel.Result result, final PermissionManager permissionManager) {
FilePickerDelegate(
final Activity activity,
final MethodChannel.Result result,
final PermissionManager permissionManager
) {
this.activity = activity;
this.pendingResult = result;
this.permissionManager = permissionManager;
Expand All @@ -75,7 +82,7 @@ public void setEventHandler(final EventChannel.EventSink eventSink) {
@Override
public boolean onActivityResult(final int requestCode, final int resultCode, final Intent data) {

if(type == null) {
if (type == null) {
return false;
}

Expand All @@ -96,7 +103,7 @@ public void run() {
final Uri currentUri = data.getClipData().getItemAt(currentItem).getUri();
final FileInfo file = FileUtils.openFileStream(FilePickerDelegate.this.activity, currentUri, loadDataToMemory);

if(file != null) {
if (file != null) {
files.add(file);
Log.d(FilePickerDelegate.TAG, "[MultiFilePick] File #" + currentItem + " - URI: " + currentUri.getPath());
}
Expand All @@ -113,7 +120,7 @@ public void run() {
Log.d(FilePickerDelegate.TAG, "[SingleFilePick] File URI:" + uri.toString());
final String dirPath = FileUtils.getFullPathFromTreeUri(uri, activity);

if(dirPath != null) {
if (dirPath != null) {
finishWithSuccess(dirPath);
} else {
finishWithError("unknown_path", "Failed to retrieve directory path.");
Expand All @@ -123,7 +130,7 @@ public void run() {

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

if(file != null) {
if (file != null) {
files.add(file);
}

Expand All @@ -134,7 +141,7 @@ public void run() {
finishWithError("unknown_path", "Failed to retrieve path.");
}

} else if (data.getExtras() != null){
} else if (data.getExtras() != null) {
Bundle bundle = data.getExtras();
if (bundle.keySet().contains("selectedItems")) {
ArrayList<Parcelable> fileUris = getSelectedItems(bundle);
Expand Down Expand Up @@ -210,8 +217,8 @@ private static void finishWithAlreadyActiveError(final MethodChannel.Result resu
}

@SuppressWarnings("deprecation")
private ArrayList<Parcelable> getSelectedItems(Bundle bundle){
if(Build.VERSION.SDK_INT >= 33){
private ArrayList<Parcelable> getSelectedItems(Bundle bundle) {
if (Build.VERSION.SDK_INT >= 33) {
return bundle.getParcelableArrayList("selectedItems", Parcelable.class);
}

Expand All @@ -221,6 +228,7 @@ private ArrayList<Parcelable> getSelectedItems(Bundle bundle){
@SuppressWarnings("deprecation")
private void startFileExplorer() {
final Intent intent;
Intent intentChooser = null;

// Temporary fix, remove this null-check after Flutter Engine 1.14 has landed on stable
if (type == null) {
Expand All @@ -232,8 +240,9 @@ private void startFileExplorer() {
} else {
if (type.equals("image/*")) {
intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intentChooser = Intent.createChooser(intent, this.imagePickerTitle != null ? this.imagePickerTitle : "Select a photo");
} else {
intent = new Intent(Intent.ACTION_GET_CONTENT);
intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
Comment on lines -236 to +245

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems this change breaks some older Android versions. For me with this change the file picker doesn't work on Android 9, but reverting this change or setting the intent type depending on Android version (like here) fixes this. I am not sure though which versions should use which intent type and why.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems wrapping all intents with createChooser works with launchMode="singleInstance" regardless of the version of Android even with the old ACTION_GET_CONTENT intent type: example. It will probably work with other launch modes too, but according to the description here it may force the system to show an extra activity chooser dialog if there are several possible activities for choosing a file. So perhaps this wrapping should better be somehow limited only to singleInstance applications.

intent.addCategory(Intent.CATEGORY_OPENABLE);
}
final Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath() + File.separator);
Expand All @@ -253,22 +262,34 @@ private void startFileExplorer() {
}

if (intent.resolveActivity(this.activity.getPackageManager()) != null) {
this.activity.startActivityForResult(intent, REQUEST_CODE);
if (intentChooser != null) {
this.activity.startActivityForResult(intentChooser, REQUEST_CODE);
} else {
this.activity.startActivityForResult(intent, REQUEST_CODE);
}
} else {
Log.e(TAG, "Can't find a valid activity to handle the request. Make sure you've a file explorer installed.");
finishWithError("invalid_format_type", "Can't handle the provided file type.");
}
}

@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 boolean isMultipleSelection,
final boolean withData,
final String[] allowedExtensions,
final MethodChannel.Result result,
final String imagePickerTitle
) {

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

this.type = type;
this.imagePickerTitle = imagePickerTitle;
this.isMultipleSelection = isMultipleSelection;
this.loadDataToMemory = withData;
this.allowedExtensions = allowedExtensions;
Expand All @@ -292,10 +313,10 @@ private void finishWithSuccess(Object data) {
// Temporary fix, remove this null-check after Flutter Engine 1.14 has landed on stable
if (this.pendingResult != null) {

if(data != null && !(data instanceof String)) {
if (data != null && !(data instanceof String)) {
final ArrayList<HashMap<String, Object>> files = new ArrayList<>();

for (FileInfo file : (ArrayList<FileInfo>)data) {
for (FileInfo file : (ArrayList<FileInfo>) data) {
files.add(file.toMap());
}
data = files;
Expand All @@ -318,7 +339,7 @@ private void finishWithError(final String errorCode, final String errorMessage)

private void dispatchEventStatus(final boolean status) {

if(eventSink == null || type.equals("dir")) {
if (eventSink == null || type.equals("dir")) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.lifecycle.DefaultLifecycleObserver;
Expand Down Expand Up @@ -168,7 +169,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, isMultipleSelection, withData, allowedExtensions, result, (String) arguments.get("dialogTitle"));
}

}
Expand Down
3 changes: 3 additions & 0 deletions lib/src/file_picker_io.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class FilePickerIO extends FilePicker {
}) =>
_getPath(
type,
dialogTitle,
allowMultiple,
allowCompression,
allowedExtensions,
Expand Down Expand Up @@ -65,6 +66,7 @@ class FilePickerIO extends FilePicker {

Future<FilePickerResult?> _getPath(
FileType fileType,
String? dialogTitle,
bool allowMultipleSelection,
bool? allowCompression,
List<String>? allowedExtensions,
Expand Down Expand Up @@ -93,6 +95,7 @@ class FilePickerIO extends FilePicker {
'allowedExtensions': allowedExtensions,
'allowCompression': allowCompression,
'withData': withData,
'dialogTitle': dialogTitle,
});

if (result == null) {
Expand Down
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.0.1

dependencies:
flutter:
Expand Down