In Cordova Android when clicking on an html type="file" input a system file picker appears instead of camera options.

If I perform the same action in the system browser I get the expected dialog:

"cordova-android": "^8.0.0",
"cordova-plugin-camera": "^4.1.0"
cordova-cli 9.0.0
Full test case repo here
This is my first Android Cordova application, am I missing a certain plugin? I don't remember being asked for permission to use the camera at any stage so I wonder if I'm missing something in the config.xml linked above?
You need to set the capture and accept attributes like below:
<input type="file" capture="camera" accept="image/*" />
EDIT
Also, make sure your platforms/android/AndroidManifest.xml includes the following elements:
<application android:allowBackup="false" android:hardwareAccelerated="true" android:icon="@mipmap/icon" android:label="@string/app_name">
<provider android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true" android:name="android.support.v4.content.FileProvider">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" />
</provider>
<provider android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true" android:name="org.apache.cordova.camera.FileProvider">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/camera_provider_paths" />
</provider>
</application>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
If they don't already exist, create file platforms/android/res/xml/file_paths.xml containing:
<!-- file_paths.xml -->
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="photos" path="photos/" />
</paths>
and file platforms/android/res/xml/camera_provider_paths.xml:
<!-- camera_provider_paths.xml -->
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
If that still does not seem to do the trick, you may additionally need to add/modify the SystemWebChromeClient.java file in the platforms/android/CordovaLib/src/org/apache/cordova/engine directory to include the following:
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onShowFileChooser(WebView webView, final ValueCallback<Uri[]> filePathsCallback, final WebChromeClient.FileChooserParams fileChooserParams) {
// Image from file intent
boolean multiple = fileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE;
String type = "*/*";
if (fileChooserParams.getAcceptTypes() != null && fileChooserParams.getAcceptTypes().length > 0) {
type = fileChooserParams.getAcceptTypes()[0];
}
Intent fileIntent = new Intent(Intent.ACTION_GET_CONTENT);
fileIntent.addCategory(Intent.CATEGORY_OPENABLE);
fileIntent.setTypeAndNormalize(type);
fileIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple);
// Image from camera intent
Uri tempUri = null;
Intent captureIntent = null;
if (fileChooserParams.isCaptureEnabled()) {
captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Context context = parentEngine.getView().getContext();
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA) && captureIntent.resolveActivity(context.getPackageManager()) != null) {
try {
File tempFile = createTempFile(context);
Log.d(LOG_TAG, "Temporary photo capture file: " + tempFile);
tempUri = createUriForFile(context, tempFile);
Log.d(LOG_TAG, "Temporary photo capture URI: " + tempUri);
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, tempUri);
} catch (IOException e) {
Log.e(LOG_TAG, "Unable to create temporary file for photo capture", e);
captureIntent = null;
}
} else {
Log.w(LOG_TAG, "Device does not support photo capture");
captureIntent = null;
}
}
final Uri captureUri = tempUri;
// Chooser intent
Intent chooserIntent = Intent.createChooser(fileIntent, null);
if (captureIntent != null) {
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { captureIntent });
}
try {
Log.i(LOG_TAG, "Starting intent for file chooser");
parentEngine.cordova.startActivityForResult(new CordovaPlugin() {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
// Handle result
Uri[] result = null;
if (resultCode == Activity.RESULT_OK) {
List<Uri> uris = new ArrayList<Uri>();
if (intent == null && captureUri != null) { // camera
Log.v(LOG_TAG, "Adding camera capture: " + captureUri);
uris.add(captureUri);
} else if (intent.getClipData() != null) { // multiple files
ClipData clipData = intent.getClipData();
int count = clipData.getItemCount();
for (int i = 0; i < count; i++) {
Uri uri = clipData.getItemAt(i).getUri();
Log.v(LOG_TAG, "Adding file (multiple): " + uri);
if (uri != null) {
uris.add(uri);
}
}
} else if (intent.getData() != null) { // single file
Log.v(LOG_TAG, "Adding file (single): " + intent.getData());
uris.add(intent.getData());
}
if (!uris.isEmpty()) {
Log.d(LOG_TAG, "Receive file chooser URL: " + uris.toString());
result = uris.toArray(new Uri[uris.size()]);
}
}
filePathsCallback.onReceiveValue(result);
}
}, chooserIntent, FILECHOOSER_RESULTCODE);
} 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