How to access internal storage while uploading image from webview? - android

I have an app in which I am using WebView, If user wants to upload picture i want to show him internal storage options as we see in google chrome, as shown in image
Here is the code that I am using :
#Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return;
Uri result = intent == null || resultCode != RESULT_OK ? null
: intent.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
}
class MyWebChromeClient extends WebChromeClient {
#Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
openFileChooser();
return true;
}
// The undocumented magic method override
// Eclipse will swear at you if you try to put #Override here
public void openFileChooser() {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image");
MainActivity.this.startActivityForResult(
Intent.createChooser(i, "Image Browser"),
FILECHOOSER_RESULTCODE);
}
}

Try below code to open file chooser.
/**
* Get the Intent for selecting content to be used in an Intent Chooser.
*
* #return The intent for opening a file with Intent.createChooser()
*
*/
public static Intent createGetContentIntent() {
// Implicitly allow the user to select a particular kind of data
final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
// The MIME data type filter
intent.setType("*/*");
// Only return URIs that can be opened with ContentResolver
intent.addCategory(Intent.CATEGORY_OPENABLE);
return intent;
}

Related

File Explorer not opening on webview, opening on regular mobile browser

So I've made a WebView for a friend's website for a project, the website includes a file's uploader, which when you click on it on a regular mobile/pc browser it opens the filesbrowser/explorer to look for the file to upload into the website.
But in the case of the WebView I've made, when I click the file uploader it just clicks without lunching the File Explorer of the phone so I can select files I wish to upload.
Any suggestions ? Thanks.
#Simo You have to set chrome client for your WebView. Please check below code
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setAllowFileAccess(true);
webView.getSettings().setAllowContentAccess(true);
webView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
webView.setWebChromeClient(new WebChromeClient()
{
// For 3.0+ Devices (Start)
// onActivityResult attached before constructor
protected void openFileChooser(ValueCallback uploadMsg, String acceptType)
{
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i, "File Browser"), FILECHOOSER_RESULTCODE);
}
// For Lollipop 5.0+ Devices
public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams)
{
if (uploadMessage != null) {
uploadMessage.onReceiveValue(null);
uploadMessage = null;
}
uploadMessage = filePathCallback;
Intent intent = fileChooserParams.createIntent();
try
{
startActivityForResult(intent, REQUEST_SELECT_FILE);
} catch (ActivityNotFoundException e)
{
uploadMessage = null;
Toast.makeText(getApplicationContext(), "Cannot Open File Chooser", Toast.LENGTH_LONG).show();
return false;
}
return true;
}
//For Android 4.1 only
protected void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)
{
mUploadMessage = uploadMsg;
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(Intent.createChooser(intent, "File Browser"), FILECHOOSER_RESULTCODE);
}
protected void openFileChooser(ValueCallback<Uri> uploadMsg)
{
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
}
});
Also override onActivityResult like below
#Override
public void onActivityResult(int requestCode, int resultCode, Intent intent)
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
if (requestCode == REQUEST_SELECT_FILE)
{
if (uploadMessage == null)
return;
uploadMessage.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, intent));
uploadMessage = null;
}
}
else if (requestCode == FILECHOOSER_RESULTCODE)
{
if (null == mUploadMessage)
return;
// Use MainActivity.RESULT_OK if you're implementing WebView inside Fragment
// Use RESULT_OK only if you're implementing WebView inside an Activity
Uri result = intent == null || resultCode != MainActivity.RESULT_OK ? null : intent.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
else
Toast.makeText(getApplicationContext(), "Failed to Upload Image", Toast.LENGTH_LONG).show();
}
Source: WebView File Upload

FilePicker from android webview working only once

I need to pick an image from webview and upload it onto server. The code works, but if I press back without picking anything, next time the control does not goes in onShowFileChooser.
I have checked the same on android browser and it works there, so there has to be something that I am missing.
Below is the code:
web.setWebChromeClient(new BizzerClient());
web.setWebViewClient(new WebViewClient(){
//=========================================
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url){
if(url.contains(Domain) && !url.startsWith(SMS)){
//== if url is of same site and not related to sms, do nothing
}else{
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
return true;
}
return false;
}
});
//=========================================
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if(requestCode==FileChooser){
Uri result = intent == null || resultCode != RESULT_OK ?null:Uri.parse(intent.getDataString());
if(result==null) return;
if(fPathCallback!=null){
fPathCallback.onReceiveValue(new Uri[]{result});
fPathCallback = null;
}else{
upload.onReceiveValue(result);
upload = null;
}
}
}
//=====================================================
class BizzerClient extends WebChromeClient{
//=========================================
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
upload = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i,"File Chooser"), FileChooser);
}
//=========================================
//== For Android 3.0+
public void openFileChooser( ValueCallback uploadMsg, String acceptType ) {
upload = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
startActivityForResult(Intent.createChooser(i, "File Browser"),FileChooser);
}
//=========================================
//== For Android 4.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
upload = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult( Intent.createChooser( i, "File Chooser" ), FileChooser );
}
//=========================================
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,WebChromeClient.FileChooserParams fileChooserParams) {
fPathCallback = filePathCallback;
Toast.makeText(getApplicationContext(), "A", Toast.LENGTH_SHORT).show();
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i,"File Chooser"), FileChooser);
return true;
}
}
Does anyone know how to fix this?
My workmate has exactly same issue, he has fixed it by
upload.onReceiveValue(null)
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 (fPathCallback == null)
return;
fPathCallback.onReceiveValue(new Uri[]{});
fPathCallback = null;
}

Android openFileChooser onCreate() called after onActivityResult()

I have a webview and on one of the pages there is an Upload Photo button. I found some code to implement the file chooser (Android, why so hard????) and if I pick the gallery everything works fine. If I choose camera 1 of 10 times it works. But most of the time when I take the picture and click save (this is all in the camera activity) the webview loads the first page loaded when the app was started. It seems that the onActivityResult() is not called but instead of it onCreate() is and this messes up my app. Can you give me an example of how to restore the webView state after I take the picture? (maybe I should mention that I am logged in in the WebView).
This is the WebChromeClient class:
public class WebViewChromeClient extends WebChromeClient {
private Activity activity;
public Uri imageUri;
private static final int FILECHOOSER_RESULTCODE = 1;
private Uri mCapturedImageURI = null;
private Context context;
private MainActivity mainActivity;
public WebViewChromeClient(Context context, Activity activity,
MainActivity mainActivity) {
this.activity = activity;
this.context = context;
this.mainActivity = mainActivity;
}
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
// Update message
((Audi) activity.getApplication()).setmUploadMessage(uploadMsg);
if (uploadMsg == null) {
Log.d("UPLOAD MESSAGE", "NULL");
}
try {
File imageStorageDir = new File(
Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
"AndroidExampleFolder");
if (!imageStorageDir.exists()) {
// Create AndroidExampleFolder at sdcard
imageStorageDir.mkdirs();
}
// Create camera captured image file path and name
File file = new File(imageStorageDir + File.separator + "IMG_"
+ String.valueOf(System.currentTimeMillis()) + ".jpg");
mCapturedImageURI = Uri.fromFile(file);
mainActivity.setmCapturedImageURI(mCapturedImageURI);
Log.d("Line", "57");
// Camera capture image intent
final Intent captureIntent = new Intent(
android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCapturedImageURI);
mainActivity.setmCapturedImageURI(mCapturedImageURI);
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
// Create file chooser intent
Intent chooserIntent = Intent.createChooser(i, "Image Chooser");
// Set camera intent to file chooser
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,
new Parcelable[] { captureIntent });
// On select image call onActivityResult method of activity
activity.startActivityForResult(chooserIntent,
FILECHOOSER_RESULTCODE);
} catch (Exception e) {
Toast.makeText(context, "Exception:" + e, Toast.LENGTH_LONG).show();
}
}
// openFileChooser for Android < 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
openFileChooser(uploadMsg, "");
}
// openFileChooser for other Android versions
public void openFileChooser(ValueCallback<Uri> uploadMsg,
String acceptType, String capture) {
openFileChooser(uploadMsg, acceptType);
}
// The webPage has 2 filechoosers and will send a
// console message informing what action to perform,android wml_siso init
// taking a photo or updating the file
public boolean onConsoleMessage(ConsoleMessage cm) {
onConsoleMessage(cm.message(), cm.lineNumber(), cm.sourceId());
return true;
}
public void onConsoleMessage(String message, int lineNumber, String sourceID) {
Log.d("androidruntime", "Show console messages, Used for debugging: "
+ message);
}
}
And this is the onActivityResult method:
#Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
webView.requestFocus();
if (requestCode == FILECHOOSER_RESULTCODE) {
Log.d("MainActivity", "onActivityResult");
if (null == ((Audi) getApplication()).getmUploadMessage()) {
Log.d("FileChooser Result", "58");
return;
}
Log.d("MainActivity", "onActivityResult");
Uri result = null;
try {
if (resultCode != RESULT_OK) {
result = null;
} else {
// retrieve from the private variable if the intent is null
result = intent == null ? mCapturedImageURI : intent
.getData();
}
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "activity :" + e,
Toast.LENGTH_LONG).show();
}
((Audi) getApplication()).getmUploadMessage().onReceiveValue(result);
((Audi) getApplication()).setmUploadMessage(null);
}
Log.d("MainActivity", "onActivityResult");
}
Your activity is probably being paused/stopped by the system while you run the file chooser.
You need to handle onResume() and restore your activity's previous state. The onResume method is invoked when your activity is restarted, so this is a good place to reload whatever WebView you were displaying before launching the file chooser.
Fixed by adding this line in the Manifest android:configChanges="keyboardHidden|orientation|screenSize"
Looking at your posted code, here is a dangerous line:
webView.requestFocus();
is first in onActivityResult(). If MainActivity is being recreated, this could cause NullPointerException. The system will seamlessly re-launch your app, possibly producing the reported behavior.
Add another Log.d() to the beginning of onActivityResult(), put try/catch around this dangerous line, and use onSaveInstanceState() and onRestoreInstanceState().
Note that if your MainActivity is Portrait, you face extra trouble, with Camera app forcing Landscape.

Upload an Image from camera or gallery in WebView

WebView in this app opens a page with upload button.
Below is the code block that allows to open a dialog box to upload image from gallery or camera.
Within my Activity I have:
private WebView wv;
//make HTML upload button work in Webview
private ValueCallback<Uri> mUploadMessage;
private final static int FILECHOOSER_RESULTCODE=1;
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if(requestCode==FILECHOOSER_RESULTCODE)
{
if (null == mUploadMessage) return;
Uri result = intent == null || resultCode != RESULT_OK ? null
: intent.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
}
Within onCreate I have the following:
wv.setWebChromeClient(new WebChromeClient() {
private Uri imageUri;
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType ) {
File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyApp");
// Create the storage directory if it does not exist
if (! imageStorageDir.exists()){
imageStorageDir.mkdirs();
}
File file = new File(imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".jpg");
imageUri = Uri.fromFile(file);
final List<Intent> cameraIntents = new ArrayList<Intent>();
final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
final PackageManager packageManager = getPackageManager();
final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
for(ResolveInfo res : listCam) {
final String packageName = res.activityInfo.packageName;
final Intent i = new Intent(captureIntent);
i.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
i.setPackage(packageName);
i.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
cameraIntents.add(i);
}
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
Intent chooserIntent = Intent.createChooser(i,"Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{}));
MainActivity.this.startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
}
I am able to see option for camera, image gallery and file explorer on clicking Upload button.
File explorer and Gallery is working as expected. The problem is that, when I take a picture using camera, it is not uploaded in the "choose file" option which shows status "No file chosen".
ON SELECTING CAMERA:
ON TAKING SNAPSHOT USING CAMERA: back and check options appear.
ON CHOOSING CHECK MARK:
FILE IS NOT UPLOADED :( IN "CHOOSE FILE" OPTION
WHAT IS EXPECTED:
I checked that I have the proper writing permission and hence a directory named "MyApp" is generated and the picture is stored within it (if taken by invoking camera after clicking upload button on webpage).
How to programatically tell the application to choose picture taken from camera (that was stored in MyApp directory) after hitting check mark?
After struggling a lot I found a code that works for taking files from galley and camera from 5.0+ devices
private ValueCallback<Uri> mUploadMessage;
private Uri mCapturedImageURI = null;
private ValueCallback<Uri[]> mFilePathCallback;
private String mCameraPhotoPath;
private static final int INPUT_FILE_REQUEST_CODE = 1;
private static final int FILECHOOSER_RESULTCODE = 1;
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 = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
File imageFile = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
return imageFile;
}
this is initialization and setting webview
mWebView= (WebView) findViewById(R.id.webview);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setPluginState(WebSettings.PluginState.OFF);
mWebView.getSettings().setLoadWithOverviewMode(true);
mWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
mWebView.getSettings().setUseWideViewPort(true);
mWebView.getSettings().setUserAgentString("Android Mozilla/5.0 AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30");
mWebView.getSettings().setAllowFileAccess(true);
mWebView.getSettings().setAllowFileAccess(true);
mWebView.getSettings().setAllowContentAccess(true);
mWebView.getSettings().supportZoom();
mWebView.loadUrl(Common.adPostUrl);
mWebView.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// do your handling codes here, which url is the requested url
// probably you need to open that url rather than redirect:
if ( url.contains(".pdf")){
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(url), "application/pdf");
try{
view.getContext().startActivity(intent);
} catch (ActivityNotFoundException e) {
//user does not have a pdf viewer installed
}
} else {
mWebView.loadUrl(url);
}
return false; // then it is not handled by default action
}
#Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
Log.e("error",description);
}
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon) { //show progressbar here
super.onPageStarted(view, url, favicon);
}
#Override
public void onPageFinished(WebView view, String url) {
//hide progressbar here
}
});
mWebView.setWebChromeClient(new ChromeClient());
and here is my ChomeClient() method
public class ChromeClient extends WebChromeClient {
// For Android 5.0
public boolean onShowFileChooser(WebView view, ValueCallback<Uri[]> filePath, WebChromeClient.FileChooserParams fileChooserParams) {
// Double check that we don't have any existing callbacks
if (mFilePathCallback != null) {
mFilePathCallback.onReceiveValue(null);
}
mFilePathCallback = filePath;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(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(Common.TAG, "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;
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("image/*");
Intent[] intentArray;
if (takePictureIntent != null) {
intentArray = new Intent[]{takePictureIntent};
} else {
intentArray = new Intent[0];
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);
return true;
}
// openFileChooser for Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
mUploadMessage = uploadMsg;
// Create AndroidExampleFolder at sdcard
// Create AndroidExampleFolder at sdcard
File imageStorageDir = new File(
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES)
, "AndroidExampleFolder");
if (!imageStorageDir.exists()) {
// Create AndroidExampleFolder at sdcard
imageStorageDir.mkdirs();
}
// Create camera captured image file path and name
File file = new File(
imageStorageDir + File.separator + "IMG_"
+ String.valueOf(System.currentTimeMillis())
+ ".jpg");
mCapturedImageURI = Uri.fromFile(file);
// Camera capture image intent
final Intent captureIntent = new Intent(
android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCapturedImageURI);
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
// Create file chooser intent
Intent chooserIntent = Intent.createChooser(i, "Image Chooser");
// Set camera intent to file chooser
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS
, new Parcelable[] { captureIntent });
// On select image call onActivityResult method of activity
startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
}
// openFileChooser for Android < 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
openFileChooser(uploadMsg, "");
}
//openFileChooser for other Android versions
public void openFileChooser(ValueCallback<Uri> uploadMsg,
String acceptType,
String capture) {
openFileChooser(uploadMsg, acceptType);
}
}
//here is my onActivityResult method to handle data from gallery or camera intent
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (requestCode != INPUT_FILE_REQUEST_CODE || mFilePathCallback == null) {
super.onActivityResult(requestCode, resultCode, data);
return;
}
Uri[] results = null;
// Check that the response is a good one
if (resultCode == Activity.RESULT_OK) {
if (data == 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 = data.getDataString();
if (dataString != null) {
results = new Uri[]{Uri.parse(dataString)};
}
}
}
mFilePathCallback.onReceiveValue(results);
mFilePathCallback = null;
} else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
if (requestCode != FILECHOOSER_RESULTCODE || mUploadMessage == null) {
super.onActivityResult(requestCode, resultCode, data);
return;
}
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == this.mUploadMessage) {
return;
}
Uri result = null;
try {
if (resultCode != RESULT_OK) {
result = null;
} else {
// retrieve from the private variable if the intent is null
result = data == null ? mCapturedImageURI : data.getData();
}
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "activity :" + e,
Toast.LENGTH_LONG).show();
}
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
}
return;
}
and here is the permissions required to open camera
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CAMERA2" /> // for new versions api 21+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
Note: My code also contains the code for devices running 3.0+ also but I never tested them , the above code worked on Lolipop, Marshmallow and Nougat emulators.
One more thing , if you see and icon of Android System in place of Camera it means you have many apps available in your device to handle camera.
I suppose that the onActivityResult method is actually called, but the 3rd parameter Intent intent is null. It seems that it is a bug of Nexus phones.
But you can save the output image uri to the private variable and use it instead of the intent:
private Uri imageUri;
private void showAttachmentDialog(ValueCallback<Uri> uploadMsg) {
this.mUploadMessage = uploadMsg;
File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "TestApp");
if (!imageStorageDir.exists()) {
imageStorageDir.mkdirs();
}
File file = new File(imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".jpg");
this.imageUri = Uri.fromFile(file); // save to the private variable
final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
Intent chooserIntent = Intent.createChooser(i, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[] { captureIntent });
this.startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == this.mUploadMessage) {
return;
}
Uri result;
if (resultCode != RESULT_OK) {
result = null;
} else {
result = intent == null ? this.imageUri : intent.getData(); // retrieve from the private variable if the intent is null
}
this.mUploadMessage.onReceiveValue(result);
this.mUploadMessage = null;
}
}
In this code I added the imageUri variable to the activity and used it in both methods.
Update 6/18: This doesn't seem to work on Samsung Galaxy S2 with Android 4.2.1. This worked fine on HTC One X+ with 4.1.2. Be cautioned.
I faced the same problem. Here's the problem and how I solved it.
Problem:
When openFileChooser is called, the call back object ValueCallback<Uri> is being passed. This is the actual call back to web view when we have a file ready for it. We save this object to mUploadMessage and use mUploadMessage.onReceiveValue() function in onActivityResult to return the file to Webview. While you choose camera, click a picture, save it and return to the webview activity, our activity might get recycled, which means we actually lose the call back object mUploadMessage. And hence, the file cannot be passed back to webview for upload.
Fix:
Fix involves executing some javascript, so enable javascript on webview. Basically, we will get another call back object if we lost the previous one.
We need to create a boolean field 'mUploadFileOnLoad' and three fields.
private int mReturnCode;
private int mResultCode;
private Intent mResultIntent;
private boolean mUploadFileOnLoad = false;
When we return to our activity from camera, onActivityResult will be called. If activity is reconstructed, mUploadMessage is null. So, we will save the parameters to fields and set mUploadFileOnLoad to true and return. The else part is very important.
#Override
protected void onActivityResult(int requestCode,
int resultCode,
Intent intent)
{
//if the callback object has been recycled
if(null==mUploadMessage)
{
//Save the result
mReturnCode = requestCode;
mResultCode = resultCode;
mResultIntent = intent;
//remember to invoke file upload using Javascript
mUploadFileOnLoad = true;
return;
}else
mUploadFileOnLoad = false;
//rest of the code
}
The important parts of this solution are in WebViewClient and WebChromeClient
new WebChromeClient() {
//Other overloaded functions
//See http://stackoverflow.com/a/15423907/375093 for full explanation
//The undocumented magic method override
//Eclipse will swear at you if you try to put #Override here
// For Android < 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
//If we lost the callback object
if(mUploadFileOnLoad)
{
mUploadMessage = uploadMsg;
//use the saved result objects to invoke onActivityResult
onActivityResult(mReturnCode, mResultCode, mResultIntent);
return;
}
//Rest of the code....
}
and
new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
if(mUploadFileOnLoad)
{
webview.loadUrl("javascript:document.getElementById('my_file').click()");
}
}
In the above, my_file is the id of the <input> element in the web page.
<input type="file" id="my_file">
So, in summary, what we did is - When we don't have a callback object, we save the data received from other activity and set mUploadFileOnLoad to true and wait for page to load. When page loads, we use Javascript to invoke the file chooser so we get a callback object. Since we already have results, we invoke onActivityResult and return. onActivityResult now has a callback from the webview.
I am sorry for my English.
This is a solution.
The first, you define file members like this.
public File mTempFile;
your's open file chooser is ok.
onActivityResult method is so important.
the camera app does not return URL, but ValueCallback must have URL.
Get URI from mTempFile.
this is work.
I use to like this.
if ( mTempFile.exists() ) {
mUploadMessage.onReceiveValue(Uri.fromFile(mTempFile));
mUploadMessage = null;
} else {
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
If mTempFile is exist that was called camera, other case from gallery.
Make sure you do NOT have android:launchMode="singleInstance" in manifest file

Upload camera photo and filechooser from webview INPUT field

My app is webbased and I need to upload pictures from an INPUT field camp. I've two situations and as i don't know another way to do it depending the page I'm choosing one or another with "boolean boolFileChoser" depending its URL petition:
a. file picker
b. camera photo shoot.
I've dealt with file picker and it upload the file perfectly, the problem is with the camera. Once i try to upload the Camera Pic, it crashes.
As far as i know its because the URI.
a) File picker: content://media/external/images/1234
b) Camera shoot: file:///mnt/sdcard/Pic.jpg
I've found no way to change it.
See update
It now crashes because a nullpointerexception while trying to upload the "content://media/external/images/1234". (only with camera, not file chooser. ).
Also if the chooser/camera is closed (back button), i'm unable to call it again.
Case a) and b) 100% working, here's the working code, including how I know if fileChooser or camera are called:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (resultCode != RESULT_OK) {
/** fixed code **/
//To be able to use the filechooser again in case of error
mUploadMessage.onReceiveValue(null);
/** fixed code **/
return;
}
if (mUploadMessage==null) {
Log.d("androidruntime","no mUploadMessage");
return;
}
if (requestCode == FILECHOOSER_RESULTCODE) {
Uri selectedImage= intent == null || resultCode != RESULT_OK ? null : intent.getData();
Log.d("androidruntime","url: "+selectedImage.toString());
}else if (requestCode == CAMERAREQUEST_RESULTCODE) {
if(mCapturedImageURI==null){
Log.d("androidruntime","no mCapturedImageURI");
return;
}
/** fixed code **/
getContentResolver().notifyChange(mCapturedImageURI, null);
ContentResolver cr = getContentResolver();
Uri uriContent= Uri.parse(MediaStore.Images.Media.insertImage(getContentResolver(), photo.getAbsolutePath(), null, null));
photo = null;
/** fixed code **/
}
mUploadMessage.onReceiveValue(selectedImage);
mUploadMessage = null;
}
private static final int FILECHOOSER_RESULTCODE = 2888;
private static final int CAMERAREQUEST_RESULTCODE = 1888;
private ValueCallback<Uri> mUploadMessage;
private Uri mCapturedImageURI = null;
protected class AwesomeWebChromeClient extends WebChromeClient{
// Per Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType){
/**updated, out of the IF **/
mUploadMessage = uploadMsg;
/**updated, out of the IF **/
if(boolFileChooser){ //Take picture from filechooser
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
MainActivity.this.startActivityForResult( Intent.createChooser( i, "Escoger Archivo" ), MainActivity.FILECHOOSER_RESULTCODE );
} else { //Take photo and upload picture
Intent cameraIntent = new Intent("android.media.action.IMAGE_CAPTURE");
File photo = new File(Environment.getExternalStorageDirectory(), "Pic.jpg");
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photo));
mCapturedImageURI = Uri.fromFile(photo);
startActivityForResult(cameraIntent, MainActivity.CAMERA_REQUEST);
}
}
// Per Android < 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsg){
openFileChooser(uploadMsg, "");
}
//Altre
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
openFileChooser(uploadMsg, "");
}
/** Added code to clarify chooser. **/
//The webPage has 2 filechoosers and will send a console message informing what action to perform, taking a photo or updating the file
public boolean onConsoleMessage(ConsoleMessage cm) {
onConsoleMessage(cm.message(), cm.lineNumber(), cm.sourceId());
return true;
}
public void onConsoleMessage(String message, int lineNumber, String sourceID) {
Log.d("androidruntime", "Per cònsola: " + cm.message());
if(message.endsWith("foto")){ boolFileChooser= true; }
else if(message.endsWith("pujada")){ boolFileChooser= false; }
}
/** Added code to clarify chooser. **/
}
UPDATE 1
I could get the "content://media/external/images/xxx" uri format, but the app still crashes while trying to upload the uri via "mUploadMessage.onReceiveValue(selectedImage);". Now I'm getting a nullpointerexception.
UPDATE 2
Fixed and working.
I've had the 'ValueCallback uploadMsg' in local variable only in file-chooser case, so it allways throwed me an exception when i tried to upload a photo file because it was null.
Once i took out from if-else statement, all worked.
The previous update was the easiest method to deal with the file upload.
I've already added a 'mUploadMessage.onReceiveValue(null);' if the Camera/filechooser intent is cancelled (you must deal with it in your webpage), if not, you won't be able to launch the INPUT field (Intent) again.
UPDATE 3
Added the part of the code inside AwesomeChromeClient to discriminate the option, take a photo or choose a file.. its MY way of doing it and added by petition, i'm sure there're a lot of other valid ways to do it,
The code is 100% functionally now. If you indicate if you want picture or file-chooser
This is how I implemented camera upload and filechooser from WebView input field:
Below is the code for this important topic. The non relevant code is removed.
public class MainActivity extends Activity {
private WebView webView;
private String urlStart = "http://www.example.com/mobile/";
//File choser parameters
private static final int FILECHOOSER_RESULTCODE = 2888;
private ValueCallback<Uri> mUploadMessage;
//Camera parameters
private Uri mCapturedImageURI = null;
#SuppressLint("SetJavaScriptEnabled")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = (WebView) findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setLoadWithOverviewMode(true);
webView.getSettings().setAllowFileAccess(true);
webView.loadUrl(urlStart);
webView.setWebChromeClient(new WebChromeClient() {
// openFileChooser for Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
mUploadMessage = uploadMsg;
try{
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File externalDataDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM);
File cameraDataDir = new File(externalDataDir.getAbsolutePath() +
File.separator + "browser-photos");
cameraDataDir.mkdirs();
String mCameraFilePath = cameraDataDir.getAbsolutePath() + File.separator +
System.currentTimeMillis() + ".jpg";
mCapturedImageURI = Uri.fromFile(new File(mCameraFilePath));
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCapturedImageURI);
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
Intent chooserIntent = Intent.createChooser(i, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[] { cameraIntent });
startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
}
catch(Exception e){
Toast.makeText(getBaseContext(), "Camera Exception:"+e, Toast.LENGTH_LONG).show();
}
}
// For Android < 3.0
#SuppressWarnings("unused")
public void openFileChooser(ValueCallback<Uri> uploadMsg ) {
openFileChooser(uploadMsg, "");
}
// For Android > 4.1.1
#SuppressWarnings("unused")
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
openFileChooser(uploadMsg, acceptType);
}
public boolean onConsoleMessage(ConsoleMessage cm) {
onConsoleMessage(cm.message(), cm.lineNumber(), cm.sourceId());
return true;
}
public void onConsoleMessage(String message, int lineNumber, String sourceID) {
Log.d("androidruntime", "www.example.com: " + message);
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
// TODO Auto-generated method stub
if(requestCode==FILECHOOSER_RESULTCODE)
{
if (null == this.mUploadMessage) {
return;
}
Uri result=null;
try{
if (resultCode != RESULT_OK) {
result = null;
} else {
// retrieve from the private variable if the intent is null
result = intent == null ? mCapturedImageURI : intent.getData();
}
}
catch(Exception e)
{
Toast.makeText(getApplicationContext(), "activity :"+e, Toast.LENGTH_LONG).show();
}
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
}
I hope it will be helpful for someone :)
Solved. Inside my question, there's the functional code in case anyone needs it.
Here's the solution of the issues:
Couldn't open camera/filechooser if I previously opened and cancelled:
//inside onActivityResult
if (resultCode != RESULT_OK) {
mUploadMessage.onReceiveValue(null);
return;
}
Get the "content://media/external/images/xxx" uri format, to upload the uri via "mUploadMessage.onReceiveValue(selectedImage);", avoiding a nullpointerexception
//inside OnActivityResult
getContentResolver().notifyChange(mCapturedImageURI, null);
ContentResolver cr = getContentResolver();
Uri uriContent = Uri.parse(android.provider.MediaStore.Images.Media.insertImage(getContentResolver(), photo.getAbsolutePath(), null, null));
Resolved and its working fine.
Just look at the code of WebChromeClient class.
You will find parameters are changed from earlier.
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
throw new RuntimeException("Stub!");
}
Solution:
You just follow the latest ChromeBrowser code in this github link
Android Chrome browser code on Github
Utilize same github code for your project.
Note: There is still issue with android 4.4 KitKat version. Except android 4.4 its working fine for other versions of Android.
Because this is still open defect from Google. Please check in the below link.
openFileChooser not called when is clicked on android 4.4 webview

Categories

Resources