ant2009
ant2009

Reputation: 22616

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);
            }

        });

Upvotes: 6

Views: 23755

Answers (4)

Luis Carlos
Luis Carlos

Reputation: 1

Through this code could take the path of the mobile file, only that I can not upload the file in php. Below is the php code I'm using:

$arquivo = $_POST['lblpath'];

$url = "file:///storage/external_SD/fotos/IMG10022.jpg"; //url da imagem que venha de um textField

//PEGA INFORMAÇÕES DA IMAGEM
$file_info = getimagesize($url);    


//VERIFICA EXTENSÃO DA IMAGEM
if ($file_info['mime'] == "image/jpeg")
$img = imagecreatefromjpeg($url);
else if ($file_info['mime'] == "image/gif")
$img = imagecreatefromgif($url);
else if ($file_info['mime'] == "image/png")
$img = imagecreatefrompng($url);

$altura = $file_info[1];

//PASSA AS MEDIDAS PARA A IMAGEM
$x   = imagesx($img);
$y   = imagesy($img);
$largura = ($altura * $x)/$y;


//CRIA A IMAGEM
$nova = imagecreatetruecolor($largura, $altura);
imagealphablending( $nova, false );
imagesavealpha( $nova, true );
imagecopyresampled($nova, $img, 0, 0, 0, 0, $largura, $altura, $x, $y);


//NOME DA IMAGEM
$imgName = end(explode("/", $url));

//LOCAL PARA SALVAR
$pasta = "images/"; // defina sua path
$local = $pasta . $imgName;


//SALVA NOVA IMAGEM
if ($file_info['mime'] == "image/jpeg")
imagejpeg($nova, $local, 100);
else if ($file_info['mime'] == "image/png")
imagepng($nova, $local, 9);

//DESTROI ELEMENTOS
imagedestroy($img);
imagedestroy($nova);

Upvotes: 0

Spiri
Spiri

Reputation: 1303

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.

Upvotes: 5

Sushant
Sushant

Reputation: 1874

Well you can achieve it like this

    webView.setWebViewClient(new WebViewClient() {


        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            if (url.equals("alert://alert")) {
                Toast.makeText(this, "alert", Toast.LENGTH_LONG).show();
            } else if (url.equals("choose://image")) {
                Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                intent.setType("image/*");

                startActivityForResult(intent, FILECHOOSER_RESULTCODE);
            }
            return true;
        }
    });

and your html should be like this

<!doctype html>
<html>
    <head>
    <meta charset="utf-8">
    <title>Test Android Popup</title>
    </head>
    <body>
        <label>Test Alert 1:</label>
        <form action="alert://alert">
             <input type="submit" value="Click me!">
        </form>
        <br>
        <label>Test Browse file</label>
        <form action="image://choose">
            <input type="submit" value="Choose File">
        </form>
    </body>
</html>

Upvotes: 2

Amrut Bidri
Amrut Bidri

Reputation: 6360

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.

Upvotes: 10

Related Questions