Load Custom Error Page in Android WebView

Network connectivity is very fluctuating in mobile devices. User may be using your app in Hi-Speed internet or low-Speed internet. whenever there is low internet connection or no connection User device Android webview display actual URL with some error messages. So you have to check your app in all environments. Otherwise you may leak your website URL accidentally which may consist of URL parameters. You never know your user is normal user or hacker so this way you can leak some data to any one. So to make your app secure follow this tutorial and create your own error page for your application.

Load Custom Error Page

Android Webview widget have very handful methods which you can use to identify error types and display message accordingly. In this tutorial I am going to display my default error page in all cases and network message if internet connection is not available. But you can implement it as per your requirement with different error types like Unauthorized web page, SSL error etc.


WebViewCustomizationDemo.java
package com.androprogrammer.tutorials.samples;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.os.Build;
import android.os.Bundle;
import android.transition.Explode;
import android.transition.Transition;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.webkit.JavascriptInterface;
import android.webkit.SslErrorHandler;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;

import com.androprogrammer.tutorials.R;
import com.androprogrammer.tutorials.activities.Baseactivity;
import com.androprogrammer.tutorials.util.Common;

import butterknife.Bind;
import butterknife.ButterKnife;

public class WebViewCustomizationDemo extends Baseactivity {

    @Bind(R.id.progress_loading)
    ProgressBar progressLoading;
    @Bind(R.id.wv_customization)
    WebView wvCustomization;

    private View viewLayout;

    private String pageUrl = "https://www.androprogrammer.com/errorPage.html";

    private String DEFAULT_ERROR_PAGE_PATH = "file:///android_asset/html/default_error_page.html";

    private static final String TAG = "WebViewCustomization";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

            //set the transition
            Transition ts = new Explode();
            ts.setDuration(5000);
            getWindow().setEnterTransition(ts);
            getWindow().setExitTransition(ts);
        }

        super.onCreate(savedInstanceState);

        setReference();

        setSimpleToolbar(true);
        setToolbarElevation(getResources().getDimension(R.dimen.elevation_normal));

        setToolbarSubTittle(this.getClass().getSimpleName());

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

    }

    @Override
    public void setReference() {

        viewLayout = LayoutInflater.from(this).inflate(R.layout.activity_web_customization, container);

        ButterKnife.bind(this, viewLayout);

        wvCustomization.clearHistory();
        wvCustomization.getSettings().setAppCacheEnabled(false);
        wvCustomization.getSettings().setJavaScriptEnabled(true);

        wvCustomization.addJavascriptInterface(new SimpleWebJavascriptInterface(this), "Android");
        wvCustomization.setWebViewClient(new MyWebViewClient());

        wvCustomization.loadUrl(pageUrl);

    }


    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        
        switch (item.getItemId()) {
            // Respond to the action bar's Up/Home button
            case android.R.id.home:
                finish();
                break;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onBackPressed() {
        if (wvCustomization.canGoBack()) {
            wvCustomization.goBack();
        } else {
            finish();
        }
    }

    private class MyWebViewClient extends WebViewClient {

        @Override public boolean shouldOverrideUrlLoading(WebView view, String url) {

            Log.d(TAG, "--" + url);

            return true;
        }

        @Override
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
            String message = null;
            switch (error.getPrimaryError()) {
                case SslError.SSL_UNTRUSTED:
                    message = "The certificate authority is not trusted.";
                    break;
                case SslError.SSL_EXPIRED:
                    message = "The certificate has expired.";
                    break;
                case SslError.SSL_IDMISMATCH:
                    message = "The certificate Hostname mismatch.";
                    break;
                case SslError.SSL_INVALID:
                    message = "SSL connection is invalid.";
                    break;
            }

            // display error dialog with title and message
            Common.showDialog(WebViewCustomizationDemo.this, "SSL Certificate Error", message,
                    getResources().getString(android.R.string.ok),
                    null, null, 1);
        }

        @SuppressLint("NewApi")
        @Override
        public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
            if (request.isForMainFrame() && error != null) {
                view.loadUrl(DEFAULT_ERROR_PAGE_PATH);
            }
        }

        @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            if (errorCode != WebViewClient.ERROR_UNSUPPORTED_SCHEME && errorCode != WebViewClient.ERROR_HOST_LOOKUP) {
                view.loadUrl(DEFAULT_ERROR_PAGE_PATH);
            }
        }

        @Override public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
            if (progressLoading != null) {
                progressLoading.setVisibility(View.VISIBLE);
                wvCustomization.setVisibility(View.GONE);
            }
        }

        @Override public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            if (progressLoading != null) {
                progressLoading.setVisibility(View.GONE);
                wvCustomization.setVisibility(View.VISIBLE);
            }
        }
    }
}
layout/activity_web_customization_demo.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/layout_webView_loader"
    android:background="@android:color/transparent"
    tools:background="@android:color/black"
    android:fitsSystemWindows="false">

    <ProgressBar
        android:id="@+id/progress_loading"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_gravity="center"
        android:visibility="gone"
        style="?android:attr/progressBarStyle" />

    <WebView
        android:id="@+id/wv_customization"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

Android webview even allow custom javascript interface also which allow to instantiate our class method also from webpage. But this is only available after Android Kitkat (Android Api 19). Below code will allow you to listen java script actions in webview and on the basis of that you can  call class methods. As you can see it display my custom html page insted of url and native UI for webview this way it won't leak any info of my website incase of any error.

public class SimpleWebJavascriptInterface {

 Activity activity;

 public SimpleWebJavascriptInterface(Activity activity) {
  this.activity = activity;
 }

 @JavascriptInterface
 public void reloadWebPage() {

  activity.runOnUiThread(new Runnable() {
   @Override public void run() {
    wvCommonViewer.loadUrl(pageUrl);
   }
  });
}


That's it from my side. for html code checkout my github repo. Now run the app and see the result. If you have any query please let me know in below comment box.

keep coding... :)

Source code

0 comments :

Post a Comment