Android Webview

4 minute read

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>

Tags:

Categories:

Updated:

Leave a comment