Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a webview to browse the photo gallery

Min SDK API 15

Hello,

I using a webview that has a button on it that will browse the apps photo gallery.

However, when the button is clicked in the webview, nothing happens.

The url is in the format:

https://www.xxxxxxxxxxx

I have added the following permissions:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera" android:required="false" />

I have a fragment that will load the webview in the onCreateView method (snippet only) with javascript enabled.

if(!message.isEmpty()) {            
    WebView webView = (WebView)view.findViewById(R.id.webview);
    webView.getSettings().setJavaScriptEnabled(true);
    webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
    webView.setWebViewClient(new WebViewClient());
    webView.loadUrl(message);
}

I have created a simple html page to test:

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Test Android Popup</title>
</head>
<body>
<label>Test Alert 1:</label>
<button type="button" onClick="alert('Test Alert Box1');">Click Me!</button>
<br>
<label>Test Browse file</label>
<input type="file" name="img">
</body>
</html>

So the url will be loaded into the webview. The webview displays a button that the user will click to browse the photos in their gallery.

The webview looks like this: enter image description here

None of the buttons work when I click them.

Many thanks for any suggestions,

This code snippet works for < 4.3. However, 4.4 and 5.0 it fails.

 webView.setWebChromeClient(new WebChromeClient() {
            /* Open File */
            public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
                mImageFilePath = uploadMsg;

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

                startActivityForResult(intent, FILECHOOSER_RESULTCODE);
            }


            public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
                mImageFilePath = uploadMsg;

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

                startActivityForResult(intent, FILECHOOSER_RESULTCODE);
            }

        });
like image 478
ant2009 Avatar asked Feb 24 '15 05:02

ant2009


People also ask

How do I access my gallery on Android?

On your Android phone, open Gallery . New folder. Enter the name of your new folder. Choose where you want your folder.


2 Answers

create this file and place it in your assets folder: webdemo.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>title</title>
</head>
<body>
<h1>Web View Demo</h1>
<br>
    <input type="button" value="Say hello"
        onClick="showAndroidToast('Hello Android!')" />
    <br />
    File Uri: <label id="lbluri">no file uri</label>
    <br />
    File Path: <label id="lblpath">no file path</label>
    <br />
    <input type="button" value="Choose Photo" onClick="choosePhoto()" />
    <script type="text/javascript">
        function showAndroidToast(toast) {
            Android.showToast(toast);
        }
        function setFilePath(file) {
            document.getElementById('lblpath').innerHTML = file;
            Android.showToast(file);
        }
        function setFileUri(uri) {
            document.getElementById('lbluri').innerHTML = uri;
            Android.showToast(uri);
        }
        function choosePhoto() {
            var file = Android.choosePhoto();
            window.alert("file = " + file);
        }
    </script>
</body>
</html>

concentrate on the javascript written here.

write your activity as below: WebViewDemo.java

public class WebViewDemo extends Activity
{

    private WebView webView;

    final int SELECT_PHOTO = 1;

    @SuppressLint("SetJavaScriptEnabled")
    public void onCreate(Bundle savedInstanceState)
    {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.show_web_view);

        webView = (WebView) findViewById(R.id.webView1);

        webView.getSettings().setJavaScriptEnabled(true);

        webView.getSettings().setLoadWithOverviewMode(true);

        // Other webview settings
        webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
        webView.setScrollbarFadingEnabled(false);
        webView.getSettings().setBuiltInZoomControls(true);
        webView.getSettings().setPluginState(PluginState.ON);
        webView.getSettings().setAllowFileAccess(true);
        webView.getSettings().setSupportZoom(true);
        webView.addJavascriptInterface(new MyJavascriptInterface(this), "Android");

        webView.loadUrl("file:///android_asset/webdemo.html");

    }

    class MyJavascriptInterface
    {

        Context mContext;

        /** Instantiate the interface and set the context */
        MyJavascriptInterface(Context c)
        {
            mContext = c;
        }

        /** Show a toast from the web page */
        @JavascriptInterface
        public void showToast(String toast)
        {
            Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
        }

        @JavascriptInterface
        public String choosePhoto()
        {
            // TODO Auto-generated method stub
            String file = "test";
            Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
            photoPickerIntent.setType("image/*");
            startActivityForResult(photoPickerIntent, SELECT_PHOTO);
            return file;
        }

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent)
    {
        switch (requestCode)
        {
        case SELECT_PHOTO:
            if (resultCode == RESULT_OK)
            {
                Uri selectedImage = intent.getData();
                webView.loadUrl("javascript:setFileUri('" + selectedImage.toString() + "')");
                String path = getRealPathFromURI(this, selectedImage);
                webView.loadUrl("javascript:setFilePath('" + path + "')");
            }
        }

    }

    public String getRealPathFromURI(Context context, Uri contentUri)
    {
        Cursor cursor = null;
        try
        {
            String[] proj = { MediaStore.Images.Media.DATA };
            cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
            int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            cursor.moveToFirst();
            return cursor.getString(column_index);
        }
        finally
        {
            if (cursor != null)
            {
                cursor.close();
            }
        }
    }

}

use this code snippet and see if it works.

this screenshot below is taken from a Android 4.4 Kitkat device.

see this screen shot

I dont know if this solution works you or not. It may be just a workaround or a breakthrough for you. I hope some part of it would help.

like image 79
Amrut Bidri Avatar answered Oct 15 '22 23:10

Amrut Bidri


Unfortunately, as user Slartibartfast said in one of the comments to your question, input type "file" will not work on 4.4 devices. There is an issue on google code that states that this is an intended behaviour (Status: WorkingAsIntended).

Even so, the code you provided works on 4.4.4 (tested on a Nexus 7 tablet) and on < 4.4 os versions.

On 5.0, they added a documented method to do this. I'm using the following code:

mWebView.setWebChromeClient(new WebChromeClient() {
    // For Android < 3.0 - undocumented method
    @SuppressWarnings("unused")
    public void openFileChooser( ValueCallback<Uri> uploadMsg ) {
        openFileChooser( uploadMsg, "" );
    }

    // For Android 3.0+ - undocumented method
    public void openFileChooser( ValueCallback<Uri> uploadMsg, String acceptType ) {                
        mUploadCallback = uploadMsg;
        openFileChooserActivity();
    }

    // For Android > 4.1 - undocumented method
    @SuppressWarnings("unused")
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
        openFileChooser( uploadMsg, "" );
    }

    // For Android > 5.0
    public boolean onShowFileChooser (WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
        mUploadCallbackLollipop = filePathCallback;
        openFileChooserActivity();
        return true;
    }
});

Take note on the changed callback parameter for > 5.0: from ValueCallback<Uri> to ValueCallback<Uri[]>.

So, for >= 4.4 and < 4.4.4, you should implement your own file uploading mechanism. Some suggestions:

  • check with javascript the android os version and provide a <input type="file"> where it works. On 4.4 versions create a html element with your own url schema (as Sushant said, for example <form action="image://choose">). You could then catch it in shouldOverrideUrlLoading and call a javascript function from java with the file path, but you won't be able to access the contents of your file. For small files, you could pass the file content in base64, as a parameter to your javascript function, but for larger files, you'll most certainly get a OutOfMemory exception.
  • you could do the steps above but handle the file uploading in java and provide only the path to the javascript function. A button with submit could make use of what Amrut Bidri said to trigger you upload in java.

So, as a conclusion, I see two options for os versions between 4.4 and 4.4.4: either use javascript to upload the file (memory problems) or upload it in java and implement you own communication mechanism between your app and the webview. For both of them you need to have access to the web page you're loading in the webview.

I used the javascript upload method, as we're using small files and it was a bit faster to implement:

public boolean shouldOverrideUrlLoading(WebView view, String url) {         
    ... else if (url.startsWith(UPLOAD_FILE_URL_PREFIX)) {
        openFileChooserActivity();
        return true;
    }

    return false;
}

On activity result, I do something like this for 4.4 versions (the javascript function insertFileForUpload handles the file uploading):

private void invokeJavascriptWithFileContent(Uri fileUri) {
    String fileContentInBase64 = readAndConvertFileToBase64(fileUri);

    if (!fileContentInBase64.isEmpty()) {
        String fileMimeType = getMimeType(activity, fileUri);
        String fileName = getFileName(activity, fileUri);

        // invoke javascript
        String js = String.format("javascript:insertFileForUpload(\"%s\",\"%s\",\"%s\")", 
                fileName,
                fileMimeType,
                fileContentInBase64
                );
        mWebView.loadUrl(js);
    }
}

public static String getMimeType(Context context, Uri fileUri)    
{
    ContentResolver cR = context.getContentResolver();
    MimeTypeMap mime = MimeTypeMap.getSingleton();
    String type = mime.getExtensionFromMimeType(cR.getType(fileUri));

    return type;
}

Also, regarding https://github.com/delight-im/Android-AdvancedWebView, it seems that file uploading works, but they say: 'File uploads are handled automatically (check availability with AdvancedWebView.isFileUploadAvailable())' and 'isFileUploadAvailable' contains the following code:

/**
 * Returns whether file uploads can be used on the current device (generally all platform versions except for 4.4)
 *
 * @return whether file uploads can be used
 */
public static boolean isFileUploadAvailable() {
    return isFileUploadAvailable(false);
}

/**
 * Returns whether file uploads can be used on the current device (generally all platform versions except for 4.4)
 *
 * On Android 4.4.3/4.4.4, file uploads may be possible but will come with a wrong MIME type
 *
 * @param needsCorrectMimeType whether a correct MIME type is required for file uploads or `application/octet-stream` is acceptable
 * @return whether file uploads can be used
 */
public static boolean isFileUploadAvailable(final boolean needsCorrectMimeType) {
    if (Build.VERSION.SDK_INT == 19) {
        final String platformVersion = (Build.VERSION.RELEASE == null) ? "" : Build.VERSION.RELEASE;

        return !needsCorrectMimeType && (platformVersion.startsWith("4.4.3") || platformVersion.startsWith("4.4.4"));
    }
    else {
        return true;
    }
}

So you might just have the same problems with the normal webview, but I haven't tested the library so I can't really say.

I might have created a mess of an answer, but I hope you can find something useful.

like image 5
Spiri Avatar answered Oct 15 '22 22:10

Spiri