Android WebView How to download a file using Kotlin

Android WebView How to download a file using Kotlin

In this article, we will learn How to handle the file download for a Web View using Kotlin Programming Language?
If you are looking for a Java Version for this article, then you need to check this article.
Following is the demo about What we will cover in this article.

For this app, we required special permissions for accessing the Internet and to write in the external storage. For this, we need to add the following lines in our Android Manifext.xml file.

Code

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

As shown in the above image on our web page, we have two URLs. When we clicked on the first, then we can download the file. When we use the second URL, we have redirected to the second another page. Now, let’s see How to achieve as shows in the above image.
In our demo app, we are using a new Android Project with an Empty Activity. In our Empty Activity, we have a Constraint Layout as our main layout. In our main layout, we have added a Web View. We also added the top, bottom, left and the right constraint for our Web View, with 0 margins. Following is the code for the activity_main.xml file. 

Code

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/mainLayout"
    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">

    </WebView>

</android.support.constraint.ConstraintLayout>

Next, we need to edit the MainActivity.kt file. In the MainActivity.kt file we first added all variables and constants we need to use for our app. Next, in the onCreate function, we need to initialize all the variables. 
After initializing all the variables, we are calling a function “checkWriteAccess” to check whether we have permission to write in the external storage. It is important, without write access we are unable to download our files. In our checkWriteAccess we have a check for write access. If we don’t have it, we are requesting to the end user please allow the access. We are handling the request permission dialog result in function onRequestPermissionsResult.
Next, we are calling a function “createDownloadListener”. This function is the main code which helps to download the file from the Web View. In this function again, we are checking the external storage write access. If we have it, we are downloading the file. In case we don’t have access, we are again calling the checkWriteAccess.
Next, we have a “onDownloadComplete” function. In the function, we are registering a BroadCast Receiver for Download Complete. It will help us to customize the download file notification message.
Finally, we are calling the last function “configureWebView”. It is for general web view configuration.
Following is the code for MainActivity.kt file.

Code

package com.vlemonn.blog.webviewfiledownload

import android.Manifest
import android.app.Activity
import android.content.Context
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.constraint.ConstraintLayout
import android.Manifest.permission
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.support.v4.app.ActivityCompat
import android.content.DialogInterface
import android.content.pm.PackageManager
import android.os.Build
import android.app.DownloadManager
import android.content.IntentFilter
import android.net.Uri
import android.os.Environment
import android.os.Environment.DIRECTORY_DOWNLOADS
import android.webkit.URLUtil.guessFileName
import android.graphics.Bitmap
import android.support.annotation.RequiresApi
import android.webkit.*
import com.vlemonn.blog.webviewfiledownload.R.id.webView
import android.webkit.URLUtil.guessFileName
import android.webkit.DownloadListener
import android.content.Intent
import android.content.BroadcastReceiver
import android.widget.Toast


class MainActivity : AppCompatActivity() {

    lateinit var webView: WebView
    lateinit var context: Context
    lateinit var activity: Activity
    lateinit var downloadListener: DownloadListener
    var writeAccess = false

    /** Permission Request Code */
    private val PERMISSION_REQUEST_CODE = 1234


    /** Sample Page from which we will download the file */
    private val downloadPage = "https://demo.vl.fyi/PHP/DownloadFile/index.php"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        /** Application Context and Main Activity */
        context = applicationContext
        activity = this

        /** Initialize main layout and web view */
        webView = findViewById(R.id.webView)

        /** Check permission to write in external storage */
        checkWriteAccess()
        /** Create a Download Listener */
        createDownloadListener()
        /** Display Toast Message When Download Complete */
        onDownloadComplete()
        /** Configure Web View */
        configureWebView()

    }

    private fun onDownloadComplete()
    {
        /**  Code that receives and handles broadcast intents sent by Context.sendBroadcast(Intent) */
        val onComplete = object : BroadcastReceiver() {
            override fun onReceive(ctxt: Context, intent: Intent) {
                Toast.makeText(context,"File Downloaded",Toast.LENGTH_LONG).show()
            }
        }

        /** Register to receives above broadcast */
        registerReceiver(onComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
    }

    @SuppressLint("SetJavaScriptEnabled")
    private fun configureWebView()
    {

        /** Web View General Setup */

        /**
            When user clicks on an URL the default behaviour is android open the default application
            which handles URL. It means android will open a default browser. We need to handle this.
            Why? Because we need to open the URL in the same web view. In our MyWebViewClient we
            will override shouldOverrideUrlLoading function to again load the new url in our
            web view.
        */
        webView.webViewClient = MyWebViewClient()
        /** getSettings() : Gets the WebSettings object used to control the settings for this WebView. */
        /** We will use it to enable the Java Script Support. */
        webView.settings.javaScriptEnabled = true
        /** loadUrl : Loads the given URL. */
        webView.loadUrl(downloadPage)

        /** File Download Listener */
        webView.setDownloadListener(downloadListener)
    }


    /**
     * Custom WebViewClient to override URL Loading.
     */

    private inner class MyWebViewClient : WebViewClient() {

        /**
         * Override to open URL in WebView
         * */
        override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
            view.loadUrl(url)
            return true
        }

        /**
         * Override to open URL in WebView
         * */
        @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
        override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
            view.loadUrl(request.url.toString())
            return true
        }
    }


    private fun createDownloadListener()
    {


        /** A New Download Listener for our WebView */
        downloadListener = DownloadListener { url, userAgent, contentDescription, mimetype, contentLength ->

            /**
            * This class contains all the information necessary to request a new download.
            * The URI is the only required parameter. Note that the default download destination
            * is a shared volume where the system might delete your file if it needs to reclaim
            * space for system use.
            * */
            val request = DownloadManager.Request(Uri.parse(url))

            /**
             * If the file to be downloaded is to be scanned by MediaScanner, this method should
             * be called before DownloadManager.enqueue(Request) is called.
             */
            request.allowScanningByMediaScanner()

            /**
            * Control whether a system notification is posted by the download manager while this
             * download is running or when it is completed.
            * */
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)

            /**
             * Guesses canonical filename that a download would have, using the
             * URL and contentDisposition.
            * */
            val fileName = URLUtil.guessFileName(url, contentDescription, mimetype)

            /**
             * Set the local destination for the downloaded file to a path within the public
             * external storage directory (as returned by Environment.getExternalStoragePublicDirectory(String)).
             * */
            request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)

            /**
             * Get Download Manager Service
             * */
            val dManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager

            /**
             * Enqueue a new request to Download our File.
             * */

            if(writeAccess)
                dManager.enqueue(request)
            else
            {
                Toast.makeText(context,"Unable to download file. Required Privileges are not available.",Toast.LENGTH_LONG).show()
                checkWriteAccess()
            }

        }
    }

    private fun checkWriteAccess()
    {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
        {
            /**
             * Check for permission status.
             * */
            if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
            {
                if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE))
                {
                    val builder = AlertDialog.Builder(activity)
                    builder.setMessage("Required permission to write external storage to save downloaded file.")
                    builder.setTitle("Please Grant Write Permission")
                    builder.setPositiveButton("OK") { _, _->
                        ActivityCompat.requestPermissions(
                                activity,
                                arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                                PERMISSION_REQUEST_CODE
                        )
                    }
                    builder.setNeutralButton("Cancel", null)
                    val dialog = builder.create()
                    dialog.show()
                } else {
                    ActivityCompat.requestPermissions(
                            activity,
                            arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                            PERMISSION_REQUEST_CODE
                    )
                }
            }
            else {
                /**
                * Already have required permission.
                * */
                writeAccess = false
            }
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        when (requestCode) {
            PERMISSION_REQUEST_CODE -> {
                if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    writeAccess=true
                } else {
                    // Permission denied
                    writeAccess=false
                    Toast.makeText(context,"Permission Denied. This app will not work with right permission.",Toast.LENGTH_LONG).show()
                }
            }
        }
    }
}

We are done with the coding. Let’s test our App.

Permission Reject Demo

File Download Demo

You may like the following article.

How to get WebView Loaded Page Title and URL in Android
Android WebView Display Loading Percentage

0 Comments
Leave A Comment

Please login to post your valuable comments.

Join the newsletter

Get the latest vLemonn news first

share