How to create a website as android app using web view in Kotlin language.


Hi All, Now a days Koltin becomes a more popular language in the world as it makes developers happier and open source forever. Here I am going to explain how to create an android app using a web view. By this, you can see the website in your app and behaves like a native app. The only thing to remember is your website should be built-in mobile responsive.


Here is the complete working code to load a website in android `webView`. Just create a new project from the android studio and use the following code to load the web view. Just change the URL you want to load it in the web view.

 - It will load a website in the webView
 - It will check internet connection availability
 - If there is no internet ask the user to enable WiFi from the snack bar message
 - Loads the progress until website loads
 - Handles the error if something went wrong while loading website
 - Handles the back button press cases
 - Handles exit from the app confirmation with toast

activity_main.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/rootView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <WebView
            android:id="@+id/webView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <TextView
            android:id="@+id/infoTV"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt:

    import android.annotation.SuppressLint
    import android.content.Context
    import android.content.Intent
    import android.graphics.Bitmap
    import android.net.ConnectivityManager
    import android.net.NetworkCapabilities
    import android.os.Bundle
    import android.os.Handler
    import android.os.Looper
    import android.provider.Settings
    import android.util.Log
    import android.view.KeyEvent
    import android.webkit.WebResourceError
    import android.webkit.WebResourceRequest
    import android.webkit.WebView
    import android.webkit.WebViewClient
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    import com.google.android.material.snackbar.Snackbar
    import kotlinx.android.synthetic.main.activity_main.*
    
    
    class MainActivity : AppCompatActivity() {
    
        private var progress: Progress? = null
        private var isLoaded: Boolean = false
        private var doubleBackToExitPressedOnce = false
        private var webURL = "https://www.geeklabs.co.in/" // Change it with your URL
    
        @SuppressLint("SetJavaScriptEnabled")
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            webView.settings.javaScriptEnabled = true
            if (!isOnline()) {
                showToast(getString(R.string.no_internet))
                infoTV.text = getString(R.string.no_internet)
                showNoNetSnackBar()
                return
            }
        }
    
        override fun onResume() {
            if (isOnline() && !isLoaded) loadWebView()
            super.onResume()
        }
    
        private fun loadWebView() {
            infoTV.text = ""
            webView.loadUrl(webURL)
            webView.webViewClient = object : WebViewClient() {
                override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
                    val url = request?.url.toString()
                    view?.loadUrl(url)
                    return super.shouldOverrideUrlLoading(view, request)
                }
    
                override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
                    setProgressDialogVisibility(true)
                    super.onPageStarted(view, url, favicon)
                }
    
                override fun onPageFinished(view: WebView?, url: String?) {
                    isLoaded = true
                    setProgressDialogVisibility(false)
                    super.onPageFinished(view, url)
                }
    
                override fun onReceivedError(view: WebView, request: WebResourceRequest, error: WebResourceError) {
                    isLoaded = false
                    val errorMessage = "Got Error! $error"
                    showToast(errorMessage)
                    infoTV.text = errorMessage
                    setProgressDialogVisibility(false)
                    super.onReceivedError(view, request, error)
                }
            }
        }
    
        override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
            if (event.action == KeyEvent.ACTION_DOWN) {
                if (keyCode == KeyEvent.KEYCODE_BACK) {
                    if (webView.canGoBack()) {
                        webView.goBack()
                    } else {
                        showToastToExit()
                    }
                    return true
                }
            }
            return super.onKeyDown(keyCode, event)
        }
    
        private fun showToastToExit() {
            when {
                doubleBackToExitPressedOnce -> {
                    onBackPressed()
                }
                else -> {
                    doubleBackToExitPressedOnce = true
                    showToast(getString(R.string.back_again_to_exit))
                    Handler(Looper.myLooper()!!).postDelayed({ doubleBackToExitPressedOnce = false }, 2000)
                }
            }
        }
    
        private fun setProgressDialogVisibility(visible: Boolean) {
            if (visible) progress = Progress(this, R.string.please_wait, cancelable = true)
            progress?.apply { if (visible) show() else dismiss() }
        }
    
        private fun isOnline(): Boolean {
            val connectivityManager =
                    getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val capabilities =
                    connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
            if (capabilities != null) {
                when {
                    capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> {
                        Log.i("Internet", "NetworkCapabilities.TRANSPORT_CELLULAR")
                        return true
                    }
                    capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> {
                        Log.i("Internet", "NetworkCapabilities.TRANSPORT_WIFI")
                        return true
                    }
                    capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> {
                        Log.i("Internet", "NetworkCapabilities.TRANSPORT_ETHERNET")
                        return true
                    }
                }
            }
            return false
        }
    
        private fun showToast(message: String) {
            Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
        }
    
        private fun showNoNetSnackBar() {
            val snack = Snackbar.make(rootView, getString(R.string.no_internet), Snackbar.LENGTH_INDEFINITE)
            snack.setAction(getString(R.string.settings)) {
                startActivity(Intent(Settings.ACTION_WIFI_SETTINGS))
            }
            snack.show()
        }
    }

Progress.kt:

    import android.app.AlertDialog
    import android.app.Dialog
    import android.content.Context
    import android.view.LayoutInflater
    import android.view.View
    import android.widget.TextView
    import androidx.annotation.StringRes
    
    class Progress constructor(
        context: Context?,
        @StringRes private val titleRes: Int,
        cancelable: Boolean = false
    ) {
    
        private var view: View? = null
        private var builder: AlertDialog.Builder
        private var dialog: Dialog
    
        init {
            view = LayoutInflater.from(context).inflate(R.layout.progress, null)
            view?.findViewById<TextView>(R.id.text)?.setText(titleRes)
            builder = AlertDialog.Builder(context)
            builder.setView(view)
            dialog = builder.create()
            dialog.setCancelable(cancelable)
        }
    
        fun setProgressMessage(@StringRes titleRes: Int) {
            view?.findViewById<TextView>(R.id.text)?.setText(titleRes)
        }
    
        fun show() {
            dialog.show()
        }
    
        fun dismiss() {
            if (dialog.isShowing) {
                dialog.dismiss()
            }
        }
    
        fun setProgressDialogVisibility(isVisible: Boolean) {
    
        }
    }

pregress.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/linearLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical">
    
        <ProgressBar
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="16dp" />
    
        <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="@style/TextAppearance.AppCompat.SearchResult.Subtitle"
            tools:text="Loading" />
    
    </LinearLayout>

Manifest.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.geeklabs.webviewkotlin">
    
        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    
        <application
            android:allowBackup="true"
            android:fullBackupContent="false"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>

strings.xml:

    <resources>
        <string name="app_name">WebViewKotlin</string>
        <string name="please_wait">Please wait&#8230;</string>
        <string name="no_internet">No internet connection!</string>
        <string name="settings">Settings</string>
        <string name="back_again_to_exit">Please click BACK again to exit</string>
    </resources>

build.gradle:

    apply plugin: 'com.android.application'
    apply plugin: 'kotlin-android'
    apply plugin: 'kotlin-android-extensions'
    
    android {
        compileSdkVersion 30
        buildToolsVersion "30.0.0"
    
        defaultConfig {
            applicationId "com.geeklabs.webviewkotlin"
            minSdkVersion 23
            targetSdkVersion 30
            versionCode 1
            versionName "1.0"
    
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        }
    
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }
    }
    
    dependencies {
        implementation fileTree(dir: "libs", include: ["*.jar"])
        implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
        implementation 'androidx.core:core-ktx:1.3.1'
        implementation 'androidx.appcompat:appcompat:1.1.0'
        implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
        implementation 'com.google.android.material:material:1.1.0'
        testImplementation 'junit:junit:4.13'
        androidTestImplementation 'androidx.test.ext:junit:1.1.1'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    
    }

Here is the complete working code in Git hub. Click here to navigate to Git Hub


Comments

Popular posts from this blog

Android - Run app without USB cable every time

Android Singleton class to make it use of globally in application