Android 4.4 WebView file chooser not opening?

30,417

Solution 1

WebView is working as intended

If I understand correctly what the above link says , you (me and probably some hundreds more developers) are looking for a hack

Solution 2

This Code worked for me.

    private class MyWebChromeClient extends WebChromeClient {
    //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 uploadMsg, String acceptType) {
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("*/*");
        MainActivity1.this.startActivityForResult(Intent.createChooser(i, "File Browser"), FILECHOOSER_RESULTCODE);
    }

    //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("*/*");
        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("*/*");
        startActivityForResult(Intent.createChooser(i, "File Chooser"), 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 = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            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;
    }

    @Override
    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {

        Log.d("LogTag", message);
        result.confirm();
        return true;
    }
}

  @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_SELECT_FILE) {
        if (uploadMessage == null) return;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            uploadMessage.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data));
        }
        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 = data == null || resultCode != MainActivity.RESULT_OK ? null : data.getData();
        mUploadMessage.onReceiveValue(result);
        mUploadMessage = null;
    }
}

Solution 3

I was working on this issue too, and the problem and here is my solution

private class MyWebChromeClient extends WebChromeClient {


    /**
     * This is the method used by Android 5.0+ to upload files towards a web form in a Webview
     *
     * @param webView
     * @param filePathCallback
     * @param fileChooserParams
     * @return
     */
    @Override
    public boolean onShowFileChooser(
            WebView webView, ValueCallback<Uri[]> filePathCallback,
            WebChromeClient.FileChooserParams fileChooserParams) {

        if (mFilePathCallback != null) {
            mFilePathCallback.onReceiveValue(null);
        }
        mFilePathCallback = filePathCallback;

        Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
        contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
        contentSelectionIntent.setType("image/*");

        Intent[] intentArray = getCameraIntent();

        Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
        chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
        chooserIntent.putExtra(Intent.EXTRA_TITLE, "Seleccionar Fuente");
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);

        startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);

        return true;
    }

    @Override
    public void onProgressChanged(WebView view, int newProgress) {

        mProgressBar.setVisibility(View.VISIBLE);
        WebActivity.this.setValue(newProgress);
        super.onProgressChanged(view, newProgress);
    }

    @Override
    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {

        Log.d("LogTag", message);
        result.confirm();
        return true;
    }

    /**
     * Despite that there is not a Override annotation, this method overrides the open file
     * chooser function present in Android 3.0+
     *
     * @param uploadMsg
     * @author Tito_Leiva
     */
    public void openFileChooser(ValueCallback<Uri> uploadMsg) {

        mUploadMessage = uploadMsg;
        Intent i = getChooserIntent(getCameraIntent(), getGalleryIntent("image/*"));
        i.addCategory(Intent.CATEGORY_OPENABLE);
        WebActivity.this.startActivityForResult(Intent.createChooser(i, "Selecciona la imagen"), FILECHOOSER_RESULTCODE);

    }

    public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
        mUploadMessage = uploadMsg;
        Intent i = getChooserIntent(getCameraIntent(), getGalleryIntent("*/*"));
        i.addCategory(Intent.CATEGORY_OPENABLE);
        WebActivity.this.startActivityForResult(
                Intent.createChooser(i, "Selecciona la imagen"),
                FILECHOOSER_RESULTCODE);
    }

    /**
     * Despite that there is not a Override annotation, this method overrides the open file
     * chooser function present in Android 4.1+
     *
     * @param uploadMsg
     * @author Tito_Leiva
     */
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        mUploadMessage = uploadMsg;
        Intent i = getChooserIntent(getCameraIntent(), getGalleryIntent("image/*"));
        WebActivity.this.startActivityForResult(Intent.createChooser(i, "Selecciona la imagen"), FILECHOOSER_RESULTCODE);

    }

    private Intent[] getCameraIntent() {

        // Determine Uri of camera image to save.
        Intent takePictureIntent = new Intent(WebActivity.this, CameraActivity.class);
        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(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[] intentArray;
        if (takePictureIntent != null) {
            intentArray = new Intent[]{takePictureIntent};
        } else {
            intentArray = new Intent[0];
        }

        return intentArray;

    }

    private Intent getGalleryIntent(String type) {

        // Filesystem.
        final Intent galleryIntent = new Intent();
        galleryIntent.setType(type);
        galleryIntent.addCategory(Intent.CATEGORY_OPENABLE);
        galleryIntent.setAction(Intent.ACTION_GET_CONTENT);

        return galleryIntent;
    }

    private Intent getChooserIntent(Intent[] cameraIntents, Intent galleryIntent) {

        // Chooser of filesystem options.
        final Intent chooserIntent = Intent.createChooser(galleryIntent, "Seleccionar Fuente");

        // Add the camera options.
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents);

        return chooserIntent;
    }
}

One problem that I solved is the uri delivered for the onActivityResult() method does not have extension. To solve this I use this method

public static Uri savePicture(Context context, Bitmap bitmap, int maxSize) {

    int cropWidth = bitmap.getWidth();
    int cropHeight = bitmap.getHeight();

    if (cropWidth > maxSize) {
        cropHeight = cropHeight * maxSize / cropWidth;
        cropWidth = maxSize;

    }

    if (cropHeight > maxSize) {
        cropWidth = cropWidth * maxSize / cropHeight;
        cropHeight = maxSize;

    }

    bitmap = ThumbnailUtils.extractThumbnail(bitmap, cropWidth, cropHeight, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);

    File mediaStorageDir = new File(
            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
            context.getString(R.string.app_name)
    );

    if (!mediaStorageDir.exists()) {
        if (!mediaStorageDir.mkdirs()) {
            return null;
        }
    }

    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File mediaFile = new File(
            mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg"
    );

    // Saving the bitmap
    try {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 80, out);

        FileOutputStream stream = new FileOutputStream(mediaFile);
        stream.write(out.toByteArray());
        stream.close();

    } catch (IOException exception) {
        exception.printStackTrace();
    }

    // Mediascanner need to scan for the image saved
    Intent mediaScannerIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    Uri fileContentUri = Uri.fromFile(mediaFile);
    mediaScannerIntent.setData(fileContentUri);
    context.sendBroadcast(mediaScannerIntent);

    return fileContentUri;
} 

Finally, onActivityResult() method is

@Override
protected void onActivityResult(int requestCode, int resultCode,
                                Intent intent) {

    if (resultCode == RESULT_OK) {

        // This is for Android 4.4.4- (JellyBean & KitKat)
        if (requestCode == FILECHOOSER_RESULTCODE) {

            if (null == mUploadMessage) {
                super.onActivityResult(requestCode, resultCode, intent);
                return;
            }

            final boolean isCamera;

            if (intent == null) {
                isCamera = true;
            } else {
                final String action = intent.getAction();
                if (action == null) {
                    isCamera = false;
                } else {
                    isCamera = action.equals(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
                }
            }

            Uri selectedImageUri;

            if (isCamera) {

                selectedImageUri = mOutputFileUri;
                mUploadMessage.onReceiveValue(selectedImageUri);
                mUploadMessage = null;

                return;

            } else {

                try {

                    Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), intent.getData());
                    selectedImageUri = intent == null ? null : ImageUtility.savePicture(this, bitmap, 1400);

                    mUploadMessage.onReceiveValue(selectedImageUri);
                    mUploadMessage = null;

                    return;

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            // And this is for Android 5.0+ (Lollipop)
        } else if (requestCode == INPUT_FILE_REQUEST_CODE) {

            Uri[] results = null;

            // Check that the response is a good one
            if (resultCode == Activity.RESULT_OK) {
                if (intent == null) {
                    // If there is not data, then we may have taken a photo
                    if (mCameraPhotoPath != null) {
                        results = new Uri[]{Uri.parse(mCameraPhotoPath)};
                    }
                } else {

                    Bitmap bitmap = null;

                    try {
                        bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), intent.getData());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    Uri dataUri = ImageUtility.savePicture(this, bitmap, 1400);

                    if (dataUri != null) {
                        results = new Uri[]{dataUri};
                    }
                }
            }

            mFilePathCallback.onReceiveValue(results);
            mFilePathCallback = null;

            return;
        }
    } else {

        super.onActivityResult(requestCode, resultCode, intent);
        return;
    }
} 

Solution 4

Have a look this. Because api 19 4.4, webview has been migrated to use Chromium, not WebChromeClient anymore.

Solution 5

Found a solution that worked for me. I've added one more rule in proguard-android.txt:

-keepclassmembers class * extends android.webkit.WebChromeClient {
     public void openFileChooser(...);
}
Share:
30,417

Related videos on Youtube

MikeCon94
Author by

MikeCon94

SOreadytohelp

Updated on July 09, 2022

Comments

  • MikeCon94
    MikeCon94 almost 2 years

    We are creating an app which uses the webview and will access a page where the user needs to upload a file. We are experiencing problems with Android 4.4 where the file chooser does not open and clicking the upload button causes nothing to happen. This functionality works with earlier versions using the openFileChooser method like so:

     webview.setWebChromeClient(new WebChromeClient() {
            //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) {
                mUploadMessage = uploadMsg;
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("image/*");
                MainActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
            }
    
            // For Android 3.0+
            public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
                mUploadMessage = uploadMsg;
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("*/*");
                MainActivity.this.startActivityForResult(
                        Intent.createChooser(i, "File Browser"),
                        FILECHOOSER_RESULTCODE);
            }
    
            //For Android 4.1
            public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
                mUploadMessage = uploadMsg;
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("image/*");
                MainActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), MainActivity.FILECHOOSER_RESULTCODE);
            }
    
        });
    

    I have spent a good amount of time searching for a way to do it on 4.4 but have had no luck. Has anyone managed to do this?

    • greenapps
      greenapps about 10 years
      A connection with a WebView is not visible in the code you posted so I wonder what it has to do with a WebView. Where is that upload button situated? Please show the code where above functions are called.
    • MikeCon94
      MikeCon94 about 10 years
      I have added in the part that shows where the code is. Hope this helps clear it up.
    • greenapps
      greenapps about 10 years
      And can you post a minimal <form> from a html page where you try to invoke the chooser? (I asked this also before). And tell if the page comes from internet or the assets.
    • Reshma
      Reshma almost 10 years
      Hi @MikeCon94 i am facing same issue with my application right now. so did you came up with any solution with 4.4?
    • MikeCon94
      MikeCon94 over 9 years
      Hey @Reshma, I have not yet found a solution but I am contemplating implementing the phonegap workaround that I have seen people talking about. I can't find the link right now but someone posted on a SO a full guide to implementing it. Looks like it could take some time to get it up and running though. When I find the link I will post it here.
    • Muhammad Babar
      Muhammad Babar over 9 years
      setTargetSdkVersion less than 18
    • Tushar Narang
      Tushar Narang about 9 years
      Have u found the solution ? I am stuck here
  • magmike
    magmike about 10 years
    @MikeCon94 take a look at what steveN says here in the comments
  • Muhammad Babar
    Muhammad Babar over 9 years
    Well the simple solution is to setTargetSdkVersion to less than 18 :)
  • Rubin
    Rubin about 8 years
    greetings, I tried your solution and somehow it is not working for me on android 4.4.x . I am not getting any method call when on this OS version. can you please tell me if it works in your case in these kinds of devices?
  • Emanuel Moecklin
    Emanuel Moecklin almost 6 years
    Both methods that worked in Android < 4.4 were made final. Doing some reflection on the WebChromeClient I get: public final void WebChromeClient.openFileChooser(android.webkit.ValueCallback‌​,java.lang.String,ja‌​va.lang.String) and public final void WebChromeClient.openFileChooser(android.webkit.ValueCallback‌​,java.lang.String). In other words: there's no solution for Android 4.4
  • Vasudev Vyas
    Vasudev Vyas about 5 years
    how can we use the camera captured file? it will show me corrupted.
  • Salman Nazir
    Salman Nazir about 5 years
    @Vasudev Did you try this? stackoverflow.com/a/30322290/4201378
  • Vasudev Vyas
    Vasudev Vyas about 5 years
    already solved. but this one good one example. Thanks, @SalmanNazir for the help.
  • Vegeta ZA
    Vegeta ZA almost 4 years
    By any chance do you have a version of this that uses chooserIntent so you can upload via an image via file browser or the camera? Please share if you do