Handling input type file in android webview – source code included
File input type in android web view doesn’t directly open the file explorer, this needs to be manually handled. The example illustrates using a web view for loading file input type.
Source code includes activity for Loading web view, with codes going backward, uploading a file with permission request and bug fixes. Includes a splash activity and no network dialog for beginners, splash activity can be replaced with a splash screen that changes with activity load.
Instructions for using source code
- if you are using as complete source code, then change the base_url to your URL in strings.xml
- Change logo in drawable to your logo.
- Change app_name and title_activity_start to your website/app name.
Update:
Fixed an issue with the unexpected crashing of app while, exiting file explorer without choosing an image.
Requires permission to Read and write storage,Internet and optionally for camera
<!-- permission -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
Example includes progress bar for status, can be skipped and directly use your desigsn.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />
<ProgressBar
android:id="@+id/webProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminate="true"/>
</RelativeLayout>
strings.xml
Include base url in strings, if required.
<string name="base_url"></string>
Explanation.
- we need to provide permission for accessing storage. Here both in manifest and runtime permission is provided.
- override onShowFileChooser of WebChromeClient class to handle file chooser .
- Here we are providing simple example for directly accessing explorer, this can be replaced with bottomsheet for providing options like camera,gallery etc…
- Save valueCallBack uri to a global variable, and assign its value on activity result, when intent returns the uri of selected item.
package com.agreemindmatrimony.app;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.ClientCertRequest;
import android.webkit.PermissionRequest;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
public WebView webView;
private float m_downX;
private ProgressBar progressBar;
private SwipeRefreshLayout mySwipeRefreshLayout;
private static final int STORAGE_PERMISSION_CODE = 123;
private final static int FILECHOOSER_RESULTCODE=1;
private ValueCallback<Uri[]> mUploadMessage;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = findViewById(R.id.webView);
progressBar = findViewById(R.id.webProgressBar);
getInternetStatus();
webView.loadUrl(getString(R.string.base_url));
// mySwipeRefreshLayout = this.findViewById(R.id.swipeContainer);
// mySwipeRefreshLayout.setOnRefreshListener(
// new SwipeRefreshLayout.OnRefreshListener() {
// @Override
// public void onRefresh() {
// webView.reload();
// }
// }
// );
initWebView();
}
//Requesting permission
private void requestStoragePermission() {
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
openFileExplorer();
return;
}
if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.READ_EXTERNAL_STORAGE)) {
//If the user has denied the permission previously your code will come to this block
//Here you can explain why you need this permission
//Explain here why you need this permission
}
//And finally ask for the permission
ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSION_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
//Checking the request code of our request
if (requestCode == STORAGE_PERMISSION_CODE) {
//If permission is granted
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
openFileExplorer();
//Displaying a toast
// Toast.makeText(this, "Permission granted now you can read the storage", Toast.LENGTH_LONG).show();
} else {
//Displaying another toast if permission is not granted
// Toast.makeText(this, "Oops you just denied the permission", Toast.LENGTH_LONG).show();
}
}
}
private void openFileExplorer(){
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 );
}
@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();
if(result ==null){
mUploadMessage.onReceiveValue(null);
}else{
mUploadMessage.onReceiveValue(new Uri[]{result});
}
mUploadMessage = null;
}
}
public void requestCameraPermission() {
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[] { android.Manifest.permission.CAMERA, android.Manifest.permission.WRITE_EXTERNAL_STORAGE }, 0);
}
}
@Override
public void onBackPressed() {
if (webView.canGoBack()) {
webView.goBack();
return;
}
super.onBackPressed();
}
private void initWebView() {
webView.setWebChromeClient(new MyWebChromeClient(this));
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
progressBar.setVisibility(View.VISIBLE);
invalidateOptionsMenu();
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
webView.loadUrl(url);
return true;
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
progressBar.setVisibility(View.GONE);
// mySwipeRefreshLayout.setRefreshing(false);
invalidateOptionsMenu();
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
progressBar.setVisibility(View.GONE);
// mySwipeRefreshLayout.setRefreshing(false);
invalidateOptionsMenu();
}
});
webView.clearCache(true);
webView.clearHistory();
webView.getSettings().setJavaScriptEnabled(true);
webView.setHorizontalScrollBarEnabled(false);
webView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (event.getPointerCount() > 1) {
//Multi touch detected
return true;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
// save the x
m_downX = event.getX();
}
break;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
// set x so that it doesn't move
event.setLocation(m_downX, event.getY());
}
break;
}
return false;
}
});
}
private void back() {
if (webView.canGoBack()) {
webView.goBack();
}
}
private void forward() {
if (webView.canGoForward()) {
webView.goForward();
}
}
private class MyWebChromeClient extends WebChromeClient {
Context context;
public MyWebChromeClient(Context context) {
super();
this.context = context;
}
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
mUploadMessage = filePathCallback;
requestStoragePermission();
return true;
}
}
}