Upload camera photo and filechooser from webview INPUT field

25,189

Solution 1

Solved. Inside my question, there's the functional code in case anyone needs it.

Here's the solution of the issues:

  1. Couldn't open camera/filechooser if I previously opened and cancelled:

    //inside onActivityResult
    if (resultCode != RESULT_OK) {
         mUploadMessage.onReceiveValue(null);
         return;
    }
    
  2. 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));
    

Solution 2

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 :)

Solution 3

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

Share:
25,189
Jordi
Author by

Jordi

Just arrived to the programmer's world. Novel (baby) Android's programmer.

Updated on November 05, 2020

Comments

  • Jordi
    Jordi over 3 years

    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