Android

This document provides instructions to create a WebView into an Android project.

Android Manifest

Add these lines to the AndroidManifest.xml file in your app module:

<uses-feature
    android:name="android.hardware.camera"
    android:required="true" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Android WebView (Kotlin)

This is an example of an activity using a WebView

class WebViewActivity : AppCompatActivity() {
    private var filePathCallback: ValueCallback<Array<Uri>>? = null
    private var isFilePickerShown = false
    private lateinit var webView: WebView
    private val url = "https://cadastro.io/:token"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        this.webView = getWebView()
        setContentView(webView)
    }

    /**
     * The permission request is made when the activity is resumed.
     * The webView is loaded only if the permission is granted.
     */
    override fun onResume() {
        super.onResume()
        if (!isFilePickerShown) {
            if (ContextCompat.checkSelfPermission(
                    this,
                    Manifest.permission.CAMERA
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                ActivityCompat.requestPermissions(
                    this,
                    arrayOf(
                        Manifest.permission.CAMERA,
                        Manifest.permission.READ_EXTERNAL_STORAGE
                    ),
                    CAMERA_REQUEST_CODE
                )
            } else {
                webView.loadUrl(url)
            }
        }
        isFilePickerShown = false
    }

    private fun getWebView(): WebView {
        val webView = WebView(this)
        val webSettings = webView.settings
        webSettings.javaScriptEnabled = true
        webSettings.domStorageEnabled = true
        webSettings.mediaPlaybackRequiresUserGesture = false
        webSettings.javaScriptCanOpenWindowsAutomatically = true
        webSettings.loadWithOverviewMode = true
        webSettings.useWideViewPort = true
        webSettings.allowFileAccess = true
        webSettings.allowContentAccess = true
        webSettings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
        webView.setLayerType(View.LAYER_TYPE_HARDWARE, null)
        webView.webChromeClient = MyWebChromeClient()
        webView.webViewClient = WebViewClient()
        return webView
    }

    /**
     * The permission request result is handled here.
     * If the permission is granted, the webView is loaded.
     */
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String?>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == CAMERA_REQUEST_CODE && grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            webView.loadUrl(url)
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == FILE_CHOOSER_REQUEST_CODE) {
            if (filePathCallback == null) {
                return
            }
            val results = WebChromeClient.FileChooserParams.parseResult(resultCode, data)
            filePathCallback!!.onReceiveValue(results)
            filePathCallback = null
        }
    }

    private inner class MyWebChromeClient : WebChromeClient() {
        var originalOrientation: Int = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
        var originalVisibility: Int = View.INVISIBLE
        private var customView: View? = null
        private var customViewCallback: CustomViewCallback? = null

        override fun onPermissionRequest(request: PermissionRequest) {
            request.grant(request.resources)
        }

        /**
         * Callback will tell the host application that the current page would
         * like to show a custom View in a particular orientation
         */
        override fun onShowCustomView(view: View, callback: CustomViewCallback) {
            if (customView != null) {
                onHideCustomView()
                return
            }
            customView = view
            originalVisibility = window.decorView.systemUiVisibility
            originalOrientation = requestedOrientation
            (window.decorView as FrameLayout).addView(
                customView,
                FrameLayout.LayoutParams(-1, -1)
            )
            // This will ensure the custom view is in fullscreen mode
            window.decorView.systemUiVisibility = 3846 or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
        }

        /**
         * Callback will tell the host application that the current page exited full screen mode,
         * and the app has to hide custom view.
         */
        override fun onHideCustomView() {
            (window.decorView as FrameLayout).removeView(customView)
            customView = null
            window.decorView.systemUiVisibility = originalVisibility
            requestedOrientation = originalOrientation
            if (customViewCallback != null) {
                customViewCallback!!.onCustomViewHidden()
            }
            customViewCallback = null
        }

        override fun onShowFileChooser(
            webView: WebView,
            filePathCallback: ValueCallback<Array<Uri>>,
            fileChooserParams: FileChooserParams
        ): Boolean {
            if (this@WebViewActivity.filePathCallback != null) {
                this@WebViewActivity.filePathCallback?.onReceiveValue(null)
            }
            this@WebViewActivity.filePathCallback = filePathCallback
            isFilePickerShown = true

            val intent = fileChooserParams.createIntent()
            try {
                startActivityForResult(intent, FILE_CHOOSER_REQUEST_CODE)
            } catch (e: ActivityNotFoundException) {
                this@WebViewActivity.filePathCallback = null
                return false
            }
            return true
        }
    }

    companion object {
        private const val CAMERA_REQUEST_CODE = 1001
        private const val FILE_CHOOSER_REQUEST_CODE = 1002
    }
}

Last updated

Logo

2023 © Caf. - All rights reserved