Android Webview
It is a basic code for creating a web app using Android Webview. You can customize various kinds of settings. Some of the main functions of web application are upper progress bar and pull-down-to-refresh. If you simply apply only SwipeRefreshLayout, you may have unexpected refresh due to the influence of scrolls in Webview.
You can detect if WebView’s content is scrolling by defining a custom WebView class that extends WebView and overriding the method onOverScrolled. Start with SwipeRefreshLayout disabled and enable it when content in the WebView is not scrolling. Then, you can detect the scrolling gesture start by catching a touch DOWN event occurring on the WebView.
initWebView
private ImageView mImageView; // Splash
private ProgressBar mProgressBar; // Progressbar
private SwipeRefreshLayout mSwipeRefreshLayout; // Pull down to refresh
private ViewTreeObserver.OnScrollChangedListener mOnScrollChangedListener;
private MyWebView mWebView; // Customed WebView
private String mInitUrl = "http://yoursite.net/"; // initial URL
private String mCurrUrl = mInitUrl; // Refresh URL
private String mLastUrl = ""; // Last URL
private ValueCallback<Uri> mUploadMessage;
public ValueCallback<Uri[]> uploadMessage;
private String file_uri;
private final static int FILECHOOSER_RESULTCODE = 1;
private final static int PERM_FILEWRITE_REQCODE = 1;
/**
* Initialize WebView
*/
protected void initWebView() {
if (mWebView == null) {
mWebView = findViewById(R.id.web_view);
WebSettings webSettings = mWebView.getSettings();
webSettings.setSupportZoom(true); // Multi-touch zoom(need to be setBuiltInZoomControls(true))
webSettings.setBuiltInZoomControls(true); // Enable Zoom Control
webSettings.setDisplayZoomControls(false); // Hide Zoom Control
webSettings.setLoadWithOverviewMode(true); // The Webweb adjusts according to the screen size the html contents is greater than the webview
webSettings.setUseWideViewPort(true); // Support meta tag of html viewport
webSettings.setDomStorageEnabled(true); // Enable to use local storage
webSettings.setJavaScriptEnabled(true);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
webSettings.setAllowFileAccess(true); // Allow file access
webSettings.setAllowFileAccessFromFileURLs(true);
webSettings.setAllowUniversalAccessFromFileURLs(true);
webSettings.setAllowContentAccess(true);
webSettings.setSaveFormData(false); // save form cache and auto-fill information
mWebView.setHapticFeedbackEnabled(false);
mWebView.clearCache(true);
mWebView.clearHistory();
mWebView.setWebChromeClient(new WebChromeClient() {
public void onProgressChanged(WebView view, int progress)
{
mProgressBar.setProgress(progress);
}
@Override
public boolean onJsAlert(WebView view, String url,
String message, final android.webkit.JsResult result) {
return true;
}
// handling input[type="file"]
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams){
boolean perm = ContextCompat.checkSelfPermission(mActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
if (!perm) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, PERM_FILEWRITE_REQCODE);
return false;
}
uploadMessage = filePathCallback;
Intent takePictureIntent = null;
Intent takeVideoIntent = null;
boolean includeVideo = false;
boolean includePhoto = false;
// Check the accept parameter to determine which intent(s) to include.
paramCheck:
for (String acceptTypes : fileChooserParams.getAcceptTypes()) {
// Although it's an array, it still seems to be the whole value.
// Split it out into chunks so that we can detect multiple values.
String[] splitTypes = acceptTypes.split(", ?+");
for (String acceptType : splitTypes) {
switch (acceptType) {
case "*/*":
includePhoto = true;
includeVideo = true;
break paramCheck;
case "image/*":
includePhoto = true;
break;
case "video/*":
includeVideo = true;
break;
}
}
}
// If no `accept` parameter was specified, allow both photo and video.
if (fileChooserParams.getAcceptTypes().length == 0) {
includePhoto = true;
includeVideo = true;
}
if (includePhoto) {
takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(MainActivity.this.getPackageManager()) != null) {
File photoFile = null;
try {
photoFile = create_image();
takePictureIntent.putExtra("PhotoPath", file_uri);
} catch (IOException ex) {
Log.e(TAG, "Image file creation failed", ex);
}
if (photoFile != null) {
file_uri = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
} else {
takePictureIntent = null;
}
}
}
if (includeVideo) {
takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
if (takeVideoIntent.resolveActivity(MainActivity.this.getPackageManager()) != null) {
File videoFile = null;
try {
videoFile = create_video();
} catch (IOException ex) {
Log.e(TAG, "Video file creation failed", ex);
}
if (videoFile != null) {
file_uri = "file:" + videoFile.getAbsolutePath();
takeVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(videoFile));
} else {
takeVideoIntent = null;
}
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE); // in case you want only camera files to upload
contentSelectionIntent.setType("*/*"); // in case you want only camera files to upload
// contentSelectionIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); // upload multiple files in webview
Intent[] intentArray;
if (takePictureIntent != null && takeVideoIntent != null) {
intentArray = new Intent[]{takePictureIntent, takeVideoIntent};
} else if (takePictureIntent != null) {
intentArray = new Intent[]{takePictureIntent};
} else if (takeVideoIntent != null) {
intentArray = new Intent[]{takeVideoIntent};
} else {
intentArray = new Intent[0];
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "파일을 선택해주세요.");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
return true;
}
});
mWebView.setWebViewClient(new WebViewClient() {
@TargetApi(android.os.Build.VERSION_CODES.M)
@Override
public void onReceivedError(WebView view, WebResourceRequest req, WebResourceError rerr) {
// Redirect to deprecated method, so you can use it in all SDK versions
onReceivedError(view, rerr.getErrorCode(), rerr.getDescription().toString(), req.getUrl().toString());
}
@SuppressWarnings("deprecation")
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
Log.e(TAG, String.format("onReceiveError:(%s)(%s)", failingUrl, description));
// Hide progress bar
mProgressBar.setProgress(100);
mProgressBar.setVisibility(View.GONE);
// Hide splash
mImageView.setVisibility(View.GONE);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
Log.i(TAG, "onPageStarted: Browser Page Started:" + url);
mCurrUrl = url;
// Show progress bar
mProgressBar.setProgress(0);
mProgressBar.setVisibility(View.VISIBLE);
}
@Override
public void onPageFinished(WebView view, final String url) {
// Hide progress bar
mProgressBar.setProgress(100);
mProgressBar.setVisibility(View.GONE);
// Hide splash
mImageView.setVisibility(View.GONE);
// Remove history (Back is impossible)
// mWebView.clearHistory();
// mWebView.clearCache(true);
// mWebView.clearView();
super.onPageFinished(view, url);
Log.i(TAG, "onPageFinished: Browser Page Finished:" + url);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Log.i(TAG, "shouldOverrideUrlLoading: " + url);
// --------------------------------------------------------------
//Browser Redirection
// --------------------------------------------------------------
view.loadUrl(url);
return false; // false : Back button is available
}
});
}
}
MyWebView.java
package net.yoursite.test;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewParent;
import android.webkit.WebView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
public class MyWebView extends WebView {
public MyWebView(Context context) {
super(context);
}
public MyWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
if( clampedX || clampedY ){
//Content is not scrolling
//Enable SwipeRefreshLayout
ViewParent parent = this.getParent();
if ( parent instanceof SwipeRefreshLayout) {
((SwipeRefreshLayout)parent).setEnabled(true);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if(event.getActionMasked()==MotionEvent.ACTION_DOWN){
//Disable SwipeRefreshLayout
ViewParent parent = this.getParent();
if ( parent instanceof SwipeRefreshLayout) {
((SwipeRefreshLayout)parent).setEnabled(false);
}
}
return true;
}
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="net.yoursite.test.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ProgressBar
android:id="@+id/am_pb_progress"
android:layout_width="fill_parent"
android:layout_height="2dp"
android:layout_gravity="center"
android:indeterminate="false"
style="@android:style/Widget.ProgressBar.Horizontal"
android:visibility="gone"/>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeToRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<net.yoursite.test.MyWebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true"
android:focusableInTouchMode="true" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</LinearLayout>
<FrameLayout android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="@+id/am_iv_splash"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="false"
android:contentDescription="@string/app_name"
android:scaleType="fitXY"
android:src="@drawable/splash"
android:visibility="visible"/>
</FrameLayout>
</FrameLayout>
Leave a comment