Hello developers! 👋 In modern Android development, sometimes you need to embed web content inside your app using a WebView. This could be a login form or a dynamic web page where you need to capture data. Specifically, capturing form data and URL query parameters from a WebView can be crucial for interacting with web content in your app. In this article, we’ll break down how to achieve this using Android Jetpack Compose with clear code examples in Kotlin. Let’s dive in!

Key Steps

Here’s a quick overview of the key steps we’ll follow:

  1. Set up WebView inside Android Compose.
  2. Use JavaScriptInterface to capture form data from WebView.
  3. Utilize WebViewClient to monitor URL changes and retrieve query parameters.
  4. Process the captured data (form data or query parameters).

Step 1: Setting Up WebView in Android Compose

In Android Jetpack Compose, we can embed a WebView using the AndroidView component. We also need to enable JavaScript and create a JavaScriptInterface to capture the form data.

Here’s how we set up the WebView:

import android.os.Bundle
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            AndroidView(
                modifier = Modifier.fillMaxSize(),
                factory = { context ->
                    WebView(context).apply {
                        val webView = this
                        webViewClient = MyWebViewClient(this)  // Attach WebViewClient to handle URLs
                        settings.javaScriptEnabled = true  // Enable JavaScript
                        addJavascriptInterface(WebAppInterface(context, this), "Android")  // Add JavaScript interface
                        loadUrl("https://www.example.com/form-page")  // Load the URL containing the form
                    }
                }
            )
        }
    }
}

Explanation: In the code above, we initialize a WebView in Compose using AndroidView and enable JavaScript. The WebAppInterface is added, which will allow us to communicate with the web page’s JavaScript and retrieve data. We also load a sample URL that contains a form.

Step 2: Using JavaScriptInterface to Capture Form Data

The JavaScriptInterface allows Android to receive data directly from JavaScript running in the WebView. Below is how you can create a WebAppInterface class to handle the incoming data.

import android.content.Context
import android.webkit.JavascriptInterface
import android.webkit.WebView
import android.widget.Toast
import org.json.JSONObject

class WebAppInterface(private val context: Context, private val webView: WebView) {

    @JavascriptInterface
    fun receiveData(data: String?) {
        if (data.isNullOrEmpty()) {
            // If form data is empty, process the URL query parameters instead
            processUrlParameters(webView.url)
        } else {
            try {
                // Parse the received form data (JSON format)
                val jsonObject = JSONObject(data)
                val formData = FormData(
                    name = jsonObject.getString("name"),
                    email = jsonObject.getString("email"),
                    phoneNumber = jsonObject.optString("phoneNumber"),
                    address = jsonObject.getString("address"),
                    postalCode = jsonObject.getString("postalCode")
                )
                processFormData(formData)
            } catch (e: Exception) {
                e.printStackTrace()
                Toast.makeText(context, "Error parsing data", Toast.LENGTH_SHORT).show()
            }
        }
    }

    // Process the URL query parameters in case form data is unavailable
    private fun processUrlParameters(url: String?) {
        if (url == null) return
        val uri = android.net.Uri.parse(url)
        val formData = FormData(
            name = uri.getQueryParameter("name"),
            email = uri.getQueryParameter("email"),
            phoneNumber = uri.getQueryParameter("phoneNumber"),
            address = uri.getQueryParameter("address"),
            postalCode = uri.getQueryParameter("postalCode")
        )
        processFormData(formData)
    }

    // Handle form data after retrieval (form or query parameters)
    private fun processFormData(formData: FormData) {
        Toast.makeText(context, "Received: $formData", Toast.LENGTH_LONG).show()
    }
}

Explanation: The WebAppInterface class handles the communication between JavaScript and Android. If form data is received, it parses it from JSON format. If the data is empty, we fall back to retrieving query parameters from the URL.

Step 3: Retrieving URL Query Parameters Using WebViewClient

In some cases, you may need to capture data passed as URL parameters. To do this, we can use WebViewClient to monitor URL changes and inject JavaScript when a specific URL is loaded.

import android.webkit.WebView
import android.webkit.WebViewClient

class MyWebViewClient(private val webView: WebView) : WebViewClient() {

    override fun onPageFinished(view: WebView?, url: String?) {
        // Check if the loaded URL matches the one we want to retrieve data from
        if (url?.contains("form-page") == true) {
            webView.loadUrl(
                "javascript:(function() { " +
                        "var form = document.querySelector('form');" +
                        "if (form) {" +
                        "   window.Android.receiveData(JSON.stringify({" +
                        "       name: form.querySelector('[name=name]').value," +
                        "       email: form.querySelector('[name=email]').value," +
                        "       phoneNumber: form.querySelector('[name=phoneNumber]').value," +
                        "       address: form.querySelector('[name=address]').value," +
                        "       postalCode: form.querySelector('[name=postalCode]').value" +
                        "   }));" +
                        "} else {" +
                        "   window.Android.receiveData(null);" +
                        "}" +
                        "})()"
            )
        }
        super.onPageFinished(view, url)
    }
}

Explanation: In this code, we monitor when a specific URL is loaded in the WebView. Once the page is fully loaded, we inject JavaScript to capture form data. If no form is found, we call receiveData(null), which then falls back to processing the URL query parameters.

Step 4: Processing Form Data or Query Parameters

Now that we have either the form data or query parameters, we can create a FormData class to structure this data. This class will make it easy to pass around and process the retrieved data.

data class FormData(
    val name: String?,
    val email: String?,
    val phoneNumber: String?,
    val address: String?,
    val postalCode: String?
)

Explanation: The FormData class structures the data retrieved from either the form or the URL. You can extend this class to include other fields as needed.

Step 5: Testing WebView Data Capture

To ensure everything is working correctly, you can create a basic UI test to verify that the WebView correctly captures and processes the data. Here’s a simplified test setup:

import android.content.Context
import android.webkit.WebView
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
import org.mockito.Mockito.verify

@RunWith(AndroidJUnit4::class)
class WebViewTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun testWebViewDataCapture() {
        // Mock context and WebView for testing
        val context = Mockito.mock(Context::class.java)
        val webView = Mockito.mock(WebView::class.java)

        // Set up WebAppInterface and WebViewClient
        val webAppInterface = WebAppInterface(context, webView)
        val webViewClient = MyWebViewClient(webView)

        composeTestRule.setContent {
            AndroidView(
                modifier = Modifier.fillMaxSize(),
                factory = { context ->
                    WebView(context).apply {
                        webViewClient = webViewClient
                        settings.javaScriptEnabled = true
                        addJavascriptInterface(webAppInterface, "Android")
                        loadUrl("https://www.example.com/form-page")
                    }
                },
                update = {
                    it.loadUrl("https://www.example.com/form-page")
                }
            )
        }

        // Verify data capture
        verify(webAppInterface).receiveData(Mockito.anyString())
    }
}

This test ensures that the WebView captures the data correctly, whether from form fields or query parameters.

Conclusion: Capturing Data from WebView in Android Compose

In this guide, we explored how to capture form data and query parameters from a WebView inside Android Compose. Using JavaScriptInterface and WebViewClient, we can effectively capture and process data for use in our Android apps. Whether you’re handling login forms or parsing URL parameters, these techniques will help you integrate web content seamlessly into your app.

Thanks for reading, and happy coding! 😊

Source: WebView Documentation

Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *