I have a hybrid app built using cordova and angularjs, for Android I run the app using crosswalk.
I've been scouring the internet to find the solution for the html5 file input to allow selection of multiple files.
I'm using the following element for file selecting:
<input type="file" multiple="multiple" name="files[]" />
I am running Android Lollipop version 5.1.1 and Crosswalk version 20, I have tested with Crosswalk version 18 and 19 also. Chrome is installed on my device running the latest version although I don't think that makes a difference.
When I click the input element above I get the expected dialog asking me to select from my Documents or Camera. If I choose to select from my Documents then I am only able to select single files, in this case images. This is true for every App that I can select images from, so the default android 'Images', 'Videos', 'Audio', etc and external Apps such as Google Photos - All only allow me to select one single file at a time.
In the image below you can see the files listed, a long press on each tile does not add the file to a multiple selection.
This works on the IOS version of the App.
After digging through all the material I can find online it seems that the multiple attribute is supported on Android 5+ running Chrome 49+.
I'm unsure if this is a crosswalk browser implementation or Android Operating System issue, or something else? Could anyone advise.
Edit
Just to confirm this does not work with or without using Crosswalk.
After weeks of trying to sort this out, I finally got it to work (Cordova without Crosswalk). This was done using Cordova Tools in Windows so please pardon the filespecs below.
Step 1: Change the minSdkVersion in platforms\Android\CordovaLib\AndroidManifest.xml to 21
Explanation: onShowFileChooser
API was introduced in LOLLIPOP (API 21). It allows returning url[]
instead of url
returned by showFileChooser
in earlier API versions. This gets called only when you change the API to 21 or greater.
Step 2: Update/Replace the onActivityResult
method to retrieve multiple files.
Append the following after creating intent using fileChooserParams
to allow choosing multiple files:
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
Location: platforms\android\CordovaLib\src\org\apache\cordova\engine\SystemWebChromeClient.java
Step 3: Update the corresponding onActivityResult
method to return multiple urls using intent.getClipData()
.
Caveats:
Final Code:
Uri photoUri;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onShowFileChooser(WebView webView, final ValueCallback<Uri[]> filePathsCallback, final WebChromeClient.FileChooserParams fileChooserParams) {
// Check and use MIME Type.
String mimeType = "*/*";
int ACTION_CODE = FILECHOOSER_RESULTCODE;
try {
if (fileChooserParams.getAcceptTypes().length > 0) {
mimeType = fileChooserParams.getAcceptTypes()[0];
} else {
mimeType = "*/*";
}
} catch (Exception e) {
mimeType = "*/*";
};
// Check if Mutiple is specified
Boolean selectMultiple = false;
if (fileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE) {
selectMultiple = true;
};
Intent intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
if (selectMultiple) { intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); };
intent.setType(mimeType);
ACTION_CODE = FILECHOOSER_RESULTCODE;
final Intent chooserIntent = Intent.createChooser(intent, "Select Source");
// Add camera intent to the chooser if image and send URI to return full image
if (mimeType.equals("image/*")) {
photoUri = null;
try {
File photoFile = createImageFile();
photoUri = Uri.fromFile(photoFile);
}
catch (Exception ex) {
photoUri = null;
}
if (photoUri != null) {
Intent camIntent = new Intent();
camIntent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
camIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
camIntent.putExtra("return-data", true);
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent [] {camIntent} );
}
}
try {
parentEngine.cordova.startActivityForResult(new CordovaPlugin() {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (resultCode == Activity.RESULT_OK && intent != null) {
if (intent.getData() != null)
{
Uri[] result = WebChromeClient.FileChooserParams.parseResult(resultCode, intent);
filePathsCallback.onReceiveValue(result);
}
else
{
if (intent.getClipData() != null) {
final int numSelectedFiles = intent.getClipData().getItemCount();
Uri[] result = new Uri[numSelectedFiles];
for (int i = 0; i < numSelectedFiles; i++) {
result[i] = intent.getClipData().getItemAt(i).getUri();
}
filePathsCallback.onReceiveValue(result);
}
else {
filePathsCallback.onReceiveValue(null);
}
}
}
else if(resultCode == Activity.RESULT_OK && (intent == null || intent.getData() == null )) {
Uri[] result = new Uri[1];
result[0] = photoUri;
filePathsCallback.onReceiveValue(result);
} else {
filePathsCallback.onReceiveValue(null);
}
}
}, chooserIntent, ACTION_CODE);
} catch (ActivityNotFoundException e) {
Log.w("No activity found to handle file chooser intent.", e);
filePathsCallback.onReceiveValue(null);
}
return true;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With