I am using the following code to upload camera image in android webview
private ValueCallback<Uri[]> file_path; // received file(s) temp. location
private final static int file_req_code = 1;
/*-- creating new image file here --*/
private File create_image() throws IOException {
@SuppressLint("SimpleDateFormat") String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "checkimg_"+timeStamp+"_";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
return File.createTempFile(imageFileName,".jpg",storageDir);
}
// Handling onshow file chooser
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
file_path = filePathCallback;
Intent takePictureIntent = null;
takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(MainActivity.this.getPackageManager()) != null) {
File photoFile = null;
try {
photoFile = create_image();
takePictureIntent.putExtra("PhotoPath", cam_file_data);
} catch (IOException ex) {
Log.e(TAG, "Image file creation failed", ex);
}
//Code modiied to make camera uploads work with webview with latest android version - https://stackoverflow.com/questions/62424102/android-webview-cannot-save-or-upload-image-taken-with-camera-to-html5-canvas
if (photoFile != null) {
cam_file_data = "file:" + photoFile.getAbsolutePath();
Uri imgUrl;
if (getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.M) {
String authority = getPackageName();
imgUrl = FileProvider.getUriForFile(MainActivity.this, authority, photoFile);
} else {
imgUrl = Uri.fromFile(photoFile);
}
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imgUrl);
} else {
cam_file_data = null;
takePictureIntent = null;
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType(file_type);
Intent[] intentArray;
intentArray = new Intent[]{takePictureIntent};
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Select Photo for Upload");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
chooserIntentForCameraPermission = chooserIntent;
//Camera & Storage Permissions Granted
if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED && Build.VERSION.SDK_INT >= 21) {
startActivityForResult(chooserIntent, file_req_code);
return true;
} else {
file_path = filePathCallback;
Intent intent = fileChooserParams.createIntent();
intentForFileChooserPermissions = intent;
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.CAMERA}, 2);
return true;
}
}
// On activity result of file chooser
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent){
super.onActivityResult(requestCode, resultCode, intent);
if(Build.VERSION.SDK_INT >= 21){
Uri[] results = null;
/*-- if file request cancelled; exited camera. we need to send null value to make future attempts workable --*/
if (resultCode == Activity.RESULT_CANCELED) {
if (requestCode == file_req_code) {
file_path.onReceiveValue(null);
return;
}
}
/*-- continue if response is positive --*/
if(resultCode== Activity.RESULT_OK){
if(requestCode == file_req_code){
if(null == file_path){
return;
}
ClipData clipData;
String stringData;
try {
clipData = intent.getClipData();
stringData = intent.getDataString();
}catch (Exception e){
clipData = null;
stringData = null;
}
if (clipData == null && stringData == null && cam_file_data != null) {
results = new Uri[]{Uri.parse(cam_file_data)};
}else{
if (clipData != null) { // checking if multiple files selected or not
final int numSelectedFiles = clipData.getItemCount();
results = new Uri[numSelectedFiles];
for (int i = 0; i < clipData.getItemCount(); i++) {
results[i] = clipData.getItemAt(i).getUri();
}
} else {
results = new Uri[]{Uri.parse(stringData)};//This part is causing sdk 30 to crash after camera image upload as stringData is null
}
}
}
}
file_path.onReceiveValue(results);
file_path = null;
}else{
if(requestCode == file_req_code){
if(null == file_data) return;
Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
file_data.onReceiveValue(result);
file_data = null;
}
}
}
The manifest has the following provider registered:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
provider_paths.xml
is also present with the following code in res/xml
:
<?xml version="1.0" encoding="utf-8"?>
<!-- File added to enable camera uploads in webview - https://stackoverflow.com/questions/62424102/android-webview-cannot-save-or-upload-image-taken-with-camera-to-html5-canvas -->
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
This is working fine with Android SDK 29. However, with SDK 30, intent.getDataString()
is null in onActivityResult
part of the code, which is throwing up an error when we use Uri.parse()
on this. The error can be avoided by using if condition to check the value, however, since intent.getDataString()
is null, camera image uploads are not working with SDK 30. How to make this work?