Skip to content

Integrating LIQA into Android app

Below is the guide for integrating LIQA into an Android app using Kotlin language.

WebView with LIQA integrated

Here is a ready-to-use implementation of WebView with integrated LIQA functionality.

Production recommendation

For the simplicity of the guide, WebView loads the HTML page from a string containing the page‘s HTML content.

In a real-world application, you may consider loading the page from a CDN managed by your organization. Check the Hosting the HTML Page section for more details.

kotlin
package ai.haut.liqa

import android.net.Uri
import android.webkit.*
import androidx.activity.ComponentActivity
import androidx.activity.result.contract.ActivityResultContracts
import org.json.JSONObject

const val HTML = """ PUT THE HTML PAGE CONTENT HERE """

fun LiqaWebView(context: ComponentActivity, onLiqaEvent: (name: String, payload: JSONObject?) -> Unit): WebView {
  return WebView(context).apply {
    webViewClient = WebViewClient()
    webChromeClient = object : WebChromeClient() {
      // Grant camera permissions to WebView
      override fun onPermissionRequest(request: PermissionRequest) {
        request.grant(request.resources)
      }

      // Handle Upload device photo
      override fun onShowFileChooser(
        webView: WebView?,
        filePathCallback: ValueCallback<Array<Uri>>?,
        fileChooserParams: FileChooserParams?
      ): Boolean {
        onFilePicked = filePathCallback
        filePicker.launch(fileChooserParams?.createIntent())
        return true
      }
      private var onFilePicked: ValueCallback<Array<Uri>>? = null
      private val filePicker = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        val data = result.data?.data
        if (data != null)
          onFilePicked?.onReceiveValue(arrayOf(data))
      }
    }
    settings.apply {
      javaScriptEnabled = true
      mediaPlaybackRequiresUserGesture = false
    }

    // Communication interface between WebView‘s JavaScript and the Native App
    addJavascriptInterface(object {
      @JavascriptInterface
      fun postMessage(message: String) {
        val json = JSONObject(message)
        val name = json.getString("name")
        val payload = if (json.has("payload")) json.getJSONObject("payload") else null

        onLiqaEvent(name, payload)
      }
    }, "NativeApp")

    // `https://` is required to get WebView camera working
    loadDataWithBaseURL("https://YOUR.DOMAIN.COM", HTML, "text/html", "UTF-8", null)
  }
}
package ai.haut.liqa

import android.net.Uri
import android.webkit.*
import androidx.activity.ComponentActivity
import androidx.activity.result.contract.ActivityResultContracts
import org.json.JSONObject

const val HTML = """ PUT THE HTML PAGE CONTENT HERE """

fun LiqaWebView(context: ComponentActivity, onLiqaEvent: (name: String, payload: JSONObject?) -> Unit): WebView {
  return WebView(context).apply {
    webViewClient = WebViewClient()
    webChromeClient = object : WebChromeClient() {
      // Grant camera permissions to WebView
      override fun onPermissionRequest(request: PermissionRequest) {
        request.grant(request.resources)
      }

      // Handle Upload device photo
      override fun onShowFileChooser(
        webView: WebView?,
        filePathCallback: ValueCallback<Array<Uri>>?,
        fileChooserParams: FileChooserParams?
      ): Boolean {
        onFilePicked = filePathCallback
        filePicker.launch(fileChooserParams?.createIntent())
        return true
      }
      private var onFilePicked: ValueCallback<Array<Uri>>? = null
      private val filePicker = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        val data = result.data?.data
        if (data != null)
          onFilePicked?.onReceiveValue(arrayOf(data))
      }
    }
    settings.apply {
      javaScriptEnabled = true
      mediaPlaybackRequiresUserGesture = false
    }

    // Communication interface between WebView‘s JavaScript and the Native App
    addJavascriptInterface(object {
      @JavascriptInterface
      fun postMessage(message: String) {
        val json = JSONObject(message)
        val name = json.getString("name")
        val payload = if (json.has("payload")) json.getJSONObject("payload") else null

        onLiqaEvent(name, payload)
      }
    }, "NativeApp")

    // `https://` is required to get WebView camera working
    loadDataWithBaseURL("https://YOUR.DOMAIN.COM", HTML, "text/html", "UTF-8", null)
  }
}

Before integrating the snippet into your app, please, ensure that:

Application integration example

Here is an app‘s entry point, showcasing a sample integration of LiqaWebView:

kotlin
package ai.haut.liqa

import android.Manifest
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.result.contract.ActivityResultContracts
import org.json.JSONObject
import android.widget.Toast

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

    val webView = LiqaWebView(this) { name, payload ->
      handleLiqaEvent(name, payload)
    }

    // Request camera permission before loading WebView
    registerForActivityResult(ActivityResultContracts.RequestPermission()) {
      setContentView(webView)
    }.launch(Manifest.permission.CAMERA)
  }

  // The App‘s business logic for handling LIQA events
  private fun handleLiqaEvent(name: String, payload: JSONObject?) {
    var text = "LIQA: $name"

    payload?.let {
      var extars = "$payload"
      // Do not overwhelm the log
      if (extars.length >= 1024) extars = extars.substring(0, 1024) + "..."
      text += "\n$extars"
    }

    log(text)
  }

  private fun log(text: String) {
    Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
  }
}
package ai.haut.liqa

import android.Manifest
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.result.contract.ActivityResultContracts
import org.json.JSONObject
import android.widget.Toast

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

    val webView = LiqaWebView(this) { name, payload ->
      handleLiqaEvent(name, payload)
    }

    // Request camera permission before loading WebView
    registerForActivityResult(ActivityResultContracts.RequestPermission()) {
      setContentView(webView)
    }.launch(Manifest.permission.CAMERA)
  }

  // The App‘s business logic for handling LIQA events
  private fun handleLiqaEvent(name: String, payload: JSONObject?) {
    var text = "LIQA: $name"

    payload?.let {
      var extars = "$payload"
      // Do not overwhelm the log
      if (extars.length >= 1024) extars = extars.substring(0, 1024) + "..."
      text += "\n$extars"
    }

    log(text)
  }

  private fun log(text: String) {
    Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
  }
}

Feel free to adapt the example for your‘s application needs.

Application permissions configuration

Additionally, there is an example of the minimal Android manifest file necessary for LiqaWebView to function correctly:

xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

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

  <uses-feature
      android:name="android.hardware.camera"
      android:required="false" />

  <application android:label="LIQA WebView integration">
    <activity
        android:name=".MainActivity"
        android:exported="true">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
  </application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

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

  <uses-feature
      android:name="android.hardware.camera"
      android:required="false" />

  <application android:label="LIQA WebView integration">
    <activity
        android:name=".MainActivity"
        android:exported="true">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
  </application>
</manifest>

Before running your app with LiqaWebView integrated, please, ensure that:

Need Assistance?

If you have any questions regarding the integration or need further assistance, please contact Haut.AI Support Team with this request via the Support Desk.