I need to pick images from the device and upload it to the server. For the first time, when I pick the images, onShowFileChooser() gets called and everything works. But, when I try to click upload again, onShowFileChooser() never gets called. But it's working for non-lollypop devices. openFileChoser() gets called, whenever I click upload. Is there anything that I'm missing. Here is my code :
//Needed for file upload feature
vWebView.setWebChromeClient(new WebChromeClient() {
// file upload callback (Android 2.2 (API level 8) -- Android 2.3 (API level 10)) (hidden method)
public void openFileChooser(ValueCallback<Uri> filePathCallback) {
showAttachmentDialog(filePathCallback);
}
// file upload callback (Android 3.0 (API level 11) -- Android 4.0 (API level 15)) (hidden method)
public void openFileChooser(ValueCallback filePathCallback, String acceptType) {
showAttachmentDialog(filePathCallback);
}
// file upload callback (Android 4.1 (API level 16) -- Android 4.3 (API level 18)) (hidden method)
public void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType, String capture) {
showAttachmentDialog(filePathCallback);
}
// file upload callback (Android 5.0 (API level 21) -- current) (public method)
// for Lollipop, all in one
@Override
public boolean onShowFileChooser(
WebView webView, ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams) {
// Double check that we don't have any existing callbacks
if (mFilePathCallbackArray != null) {
mFilePathCallbackArray.onReceiveValue(null);
}
mFilePathCallbackArray = filePathCallback;
// Set up the take picture intent
if (mTypeCap == IMAGE) {
Intent takePictureIntent = pictureIntentSetup();
return showChooserDialog(takePictureIntent);
}
//set up video capture intent
else {
Intent takeVideoIntent = videoIntentSetUp();
return showChooserDialog(takeVideoIntent);
}
}
});
//For lollypop
private Intent pictureIntentSetup() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getActivity().getPackageManager()) != null) {
// create the file where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
} catch (IOException ex) {
// Error occurred while creating the File
Log.e("Failed", "Unable to create Image File", ex);
}
// continue only if the file was successfully created
if (photoFile != null) {
mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photoFile));
} else {
takePictureIntent = null;
}
}
return takePictureIntent;
}
//For lollypop
private Intent videoIntentSetUp() {
Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
if (takeVideoIntent.resolveActivity(getActivity().getPackageManager()) != null) {
// create the file where the video should go
File videoFile = null;
try {
videoFile = createVideoFile();
takeVideoIntent.putExtra("PhotoPath", mCameraPhotoPath);
} catch (IOException ex) {
// Error occurred while creating the File
Log.e("Failed", "Unable to create Video File", ex);
}
// continue only if the file was successfully created
if (videoFile != null) {
mCameraPhotoPath = "file:" + videoFile.getAbsolutePath();
takeVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(videoFile));
} else {
takeVideoIntent = null;
}
}
return takeVideoIntent;
}
//For lollypop
private boolean showChooserDialog(Intent intent) {
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
if (mTypeCap.equalsIgnoreCase(IMAGE))
contentSelectionIntent.setType(IMAGE);
else
contentSelectionIntent.setType(VIDEO);
Intent[] intentArray;
if (intent != null) {
intentArray = new Intent[]{intent};
} else {
intentArray = new Intent[0];
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
if (mTypeCap.equalsIgnoreCase(IMAGE))
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
else
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Video Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
getActivity().startActivityForResult(chooserIntent, FILE_CHOOSER_RESULT_CODE);
return true;
}
OnActivityResult of the activity:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//File upload related
if (requestCode == NewsDetailFragment.FILE_CHOOSER_RESULT_CODE && (resultCode == RESULT_OK || resultCode == RESULT_CANCELED)) {
Fragment fragment = getSupportFragmentManager()
.findFragmentById(R.id.container);
if (fragment != null && fragment instanceof DetailFragment) {
Fragment currentFragment = ((DetailFragment) fragment).getCurrentFragment();
if (currentFragment instanceof WebDetailFragment)
currentFragment.onActivityResult(requestCode, resultCode, data);
}
}
}
}
onActivityResult of fragment:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intentData) {
super.onActivityResult(requestCode, resultCode, intentData);
// code for all versions except of Lollipop
if (!Utility.isLollypopAndAbove()) {
Uri result = null;
// check that the response is a good one
if (resultCode == Activity.RESULT_OK) {
if (requestCode == FILE_CHOOSER_RESULT_CODE) {
if (null == this.mFilePathCallback) {
return;
}
if (null == mFilePathCallback) return;
if (intentData == null) {
// if there is not data, then we may have taken a photo
if (mCameraPhotoPath != null) {
result = Uri.parse(mCameraPhotoPath);
}
} else {
String dataString = intentData.getDataString();
if (dataString != null) {
result = Uri.parse(dataString);
}
}
// Uri result = intentData == null || resultCode != Activity.RESULT_OK ? null
// : intentData.getData();
}
// for Lollipop only
}
mFilePathCallback.onReceiveValue(result);
mFilePathCallback = null;
}
else {
Uri[] results = null;
// check that the response is a good one
if(resultCode==Activity.RESULT_OK) {
if (requestCode == FILE_CHOOSER_RESULT_CODE) {
if (null == mFilePathCallbackArray) {
return;
}
if (intentData == null) {
// if there is not data, then we may have taken a photo
if (mCameraPhotoPath != null) {
results = new Uri[]{Uri.parse(mCameraPhotoPath)};
}
} else {
String dataString = intentData.getDataString();
if (dataString != null) {
results = new Uri[]{Uri.parse(dataString)};
}
}
}
}
mFilePathCallbackArray.onReceiveValue(results);
mFilePathCallbackArray = null;
return;
}
}
firstly, sorry to my english. you should return empty Uri[]{} to file receive
mUploadMessageForAndroid5.onReceiveValue(new Uri[]{});
my code can choose take photo or local image:
private static final int REQUEST_GET_THE_THUMBNAIL = 4000;
private static final long ANIMATION_DURATION = 200;
public final static int FILECHOOSER_RESULTCODE = 1;
public final static int FILECHOOSER_RESULTCODE_FOR_ANDROID_5 = 2;
//JS
webView.getSettings().setJavaScriptEnabled(true);
//set ChromeClient
webView.setWebChromeClient(getChromeClient());
//ChromeClinet配置
private WebChromeClient getChromeClient() {
return new WebChromeClient() {
//3.0++
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
openFileChooserImpl(uploadMsg);
}
//3.0--
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
openFileChooserImpl(uploadMsg);
}
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
openFileChooserImpl(uploadMsg);
}
// For Android > 5.0
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> uploadMsg, WebChromeClient.FileChooserParams fileChooserParams) {
openFileChooserImplForAndroid5(uploadMsg);
return true;
}
};
}
private void openFileChooserImpl(ValueCallback<Uri> uploadMsg) {
mUploadMessage = uploadMsg;
new AlertDialog.Builder(mActivity)
.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (items[which].equals(items[0])) {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
} else if (items[which].equals(items[1])) {
dispatchTakePictureIntent();
}
dialog.dismiss();
}
})
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
Log.v(TAG, TAG + " # onCancel");
mUploadMessage = null;
dialog.dismiss();
}})
.show();
}
private void openFileChooserImplForAndroid5(ValueCallback<Uri[]> uploadMsg) {
mUploadMessageForAndroid5 = uploadMsg;
new AlertDialog.Builder(mActivity)
.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (items[which].equals(items[0])) {
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("image/*");
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE_FOR_ANDROID_5);
} else if (items[which].equals(items[1])) {
dispatchTakePictureIntent();
}
dialog.dismiss();
}
})
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
Log.v(TAG, TAG + " # onCancel");
//important to return new Uri[]{}, when nothing to do. This can slove input file wrok for once.
//InputEventReceiver: Attempted to finish an input event but the input event receiver has already been disposed.
mUploadMessageForAndroid5.onReceiveValue(new Uri[]{});
mUploadMessageForAndroid5 = null;
dialog.dismiss();
}}).show();
}
private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(mActivity.getPackageManager()) != null) {
// File file = new File(createImageFile());
Uri imageUri = null;
try {
imageUri = Uri.fromFile(createImageFile());
} catch (IOException e) {
e.printStackTrace();
}
//temp sd card file
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(takePictureIntent, REQUEST_GET_THE_THUMBNAIL);
}
}
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/don_test/");
if (!storageDir.exists()) {
storageDir.mkdirs();
}
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath();
return image;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
Log.v(TAG, TAG + " # onActivityResult # requestCode=" + requestCode + " # resultCode=" + resultCode);
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return;
Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
} else if (requestCode == FILECHOOSER_RESULTCODE_FOR_ANDROID_5) {
if (null == mUploadMessageForAndroid5)
return;
Uri result;
if (intent == null || resultCode != Activity.RESULT_OK) {
result = null;
} else {
result = intent.getData();
}
if (result != null) {
Log.v(TAG, TAG + " # result.getPath()=" + result.getPath());
mUploadMessageForAndroid5.onReceiveValue(new Uri[]{result});
} else {
mUploadMessageForAndroid5.onReceiveValue(new Uri[]{});
}
mUploadMessageForAndroid5 = null;
} else if (requestCode == REQUEST_GET_THE_THUMBNAIL) {
if (resultCode == Activity.RESULT_OK) {
File file = new File(mCurrentPhotoPath);
Uri localUri = Uri.fromFile(file);
Intent localIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, localUri);
mActivity.sendBroadcast(localIntent);
Uri result = Uri.fromFile(file);
mUploadMessageForAndroid5.onReceiveValue(new Uri[]{result});
mUploadMessageForAndroid5 = null;
} else {
File file = new File(mCurrentPhotoPath);
Log.v(TAG, TAG + " # file=" + file.exists());
if (file.exists()) {
file.delete();
}
}
}
}
Faced similar problem i was returning boolean value in method onShowFileChooser, issue was fixed when i called super class method
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
//Logic to implement
return super.onShowFileChooser(webView, filePathCallback, fileChooserParams);
}
I had exactly same issue, But my use case is little different but my webview was loaded on a fragment as is in your code. first time the file chooser displayed fine on both Lollipop & pre Lollipop devices, but next time onwards it failed to display file chooser it self. The Reason being the I was not handled the onActivityResult in proper way at first time. My working code snippet is as follows for Lollipop:
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private boolean onActivityResultLolliPop(
int requestCode, int resultCode, Intent data) {
if (requestCode != SurveyWebChromeClient.FILE_CHOOSER_REQUEST_CODE
|| mFilePathCallbackL == null) {
super.onActivityResult(requestCode, resultCode, data);
return true;
}
Uri[] results = null;
if (resultCode == Activity.RESULT_OK) {
if (data == null) {
if (mCameraPhotoVideoPath != null) {//if there is not data here, then we may have taken a photo/video
results = new Uri[]{Uri.parse(mCameraPhotoVideoPath)};
}
} else {
String dataString = data.getDataString();
ClipData clipData = data.getClipData();
if (clipData != null) {
results = new Uri[clipData.getItemCount()];
for (int i = 0; i < clipData.getItemCount(); i++) {
ClipData.Item item = clipData.getItemAt(i);
results[i] = item.getUri();
}
}
if (dataString != null)
results = new Uri[]{Uri.parse(dataString)};
}
}
mFilePathCallbackL.onReceiveValue(results);
mFilePathCallbackL = null;
return false;
}
In the onShowFileChooser() method you should return true
, only if you are using the filePathCallback
, which is the best way:
private ValueCallback<Uri[]> filePathCallback;
@Override
public boolean onShowFileChooser(
WebView webView, ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams) {
// do whatever you need to do, to show a file chooser/camera intent
this.filePathCallback = filePathCallback;
return true;
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
// Check for your correct requestCode from your intent here
if (resultCode == RESULT_CANCELED) {
// this is important, call the callback with null parameter
this.filePathCallback.onReceiveValue(null);
} else if (resultCode == RESULT_OK) {
// extract the uri(s) you need here
this.filePathCallback.onReceiveValue(new Uri[]{result});
}
}
Just let onShowFIleChooser()
return false
, will work next time
public boolean onShowFileChooser(){
...
return **false**;
}
I had the same issue, the problem was I was expecting an OnActivityResult, but when you press the back button, this was not triggered.
The solution was implementing on onResume the following code, to tell the callback the answer was empty and being able to reuse it:
@Override
protected void onResume() {
super.onResume();
if (mFilePathCallbackArray == null)
return;
mFilePathCallbackArray.onReceiveValue(new Uri[]{});
mFilePathCallbackArray = null;
}
来源:https://stackoverflow.com/questions/29666844/onshowfilechooser-from-android-webview-works-only-once