Integration for Android

Documentation

This guide explains how to embed our Smart Assistant chatbot in your Android app using a WebView. The integration consists of two parts:

  1. An HTML page that loads the Smart Assistant web component.
  2. An Android screen that hosts the WebView and bridges native ↔ JavaScript communication (e.g. to close the assistant).

1. Create the HTML page

Add an HTML file that loads our launcher.js script and declares the <smart-assistant> web component. You can place it either as a local asset inside your app (app/src/main/assets/smart_assistant.html) or host it on a remote URL.

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
  <title>Smart Assistant</title>

  <script src="https://sa-media.usizy.es/smart-assistant/launcher.js"></script>
</head>
<body>
  <smart-assistant language="en" api-key="YOUR_API_KEY" opened hide-minimize></smart-assistant>

  <script>
    document.addEventListener('DOMContentLoaded', function () {
      const sa = document.querySelector('smart-assistant')

      // Listen for the event that the component emits when it closes.
      sa.addEventListener('close', function () {
        if (window.Android) {
          window.Android.close()   // triggers onBack() on Android
        }
      })
    })
  </script>
</body>
</html>
AttributeDescription
languageInterface language (e.g. en, es).
api-keyYour API key. Replace YOUR_API_KEY with the key provided by usizy.
openedOpens the assistant automatically when the page loads.
hide-minimizeHides the minimize button (recommended for full-screen WebView usage).

The close event listener is essential: it calls window.Android.close(), which is the JavaScript bridge that lets the web component close the native Android screen.

3. Create the Android screen (Jetpack Compose)

The following example uses Jetpack Compose. It wraps a native WebView inside an AndroidView, configures the required settings, and exposes a JavaScript bridge (window.Android) so the web component can trigger native navigation.

package com.yourcompany.yourapp.ui.screens

import android.os.Handler
import android.os.Looper
import android.webkit.ConsoleMessage
import android.webkit.JavascriptInterface
import android.webkit.WebChromeClient
import android.webkit.WebResourceRequest
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView

// Bridge exposed to JavaScript as window.Android
private class AndroidBridge(private val onClose: () -> Unit) {
    @JavascriptInterface
    fun close() {
        // JavascriptInterface is called on a background thread → dispatch to main
        Handler(Looper.getMainLooper()).post { onClose() }
    }
}

// Choose the source for the Smart Assistant page.
// Option A – local asset (default): "file:///android_asset/smart_assistant.html"
// Option B – remote URL:            "https://your-host.example.com/smart-assistant"
private const val SMART_ASSISTANT_SOURCE = "file:///android_asset/smart_assistant.html"

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SmartAssistantScreen(onBack: () -> Unit) {
    var pageTitle by remember { mutableStateOf("Smart Assistant") }

    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text(pageTitle) },
                navigationIcon = {
                    IconButton(onClick = onBack) {
                        Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
                    }
                },
                colors = TopAppBarDefaults.topAppBarColors(
                    containerColor = MaterialTheme.colorScheme.primary,
                    titleContentColor = MaterialTheme.colorScheme.onPrimary,
                    navigationIconContentColor = MaterialTheme.colorScheme.onPrimary,
                ),
            )
        },
    ) { paddingValues ->
        AndroidView(
            factory = { context ->
                // Enable remote debugging via chrome://inspect
                WebView.setWebContentsDebuggingEnabled(true)
                WebView(context).apply {
                    settings.apply {
                        javaScriptEnabled = true
                        domStorageEnabled = true
                        mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
                        cacheMode = WebSettings.LOAD_DEFAULT
                        allowFileAccess = true
                        allowContentAccess = true
                        mediaPlaybackRequiresUserGesture = false
                    }
                    webChromeClient = object : WebChromeClient() {
                        override fun onReceivedTitle(view: WebView?, title: String?) {
                            if (!title.isNullOrBlank() && title != "about:blank") {
                                pageTitle = title
                            }
                        }
                        // Forward JS console logs to Logcat (tag: SmartAssistant)
                        override fun onConsoleMessage(msg: ConsoleMessage): Boolean {
                            val level = when (msg.messageLevel()) {
                                ConsoleMessage.MessageLevel.ERROR -> "ERROR"
                                ConsoleMessage.MessageLevel.WARNING -> "WARN"
                                else -> "LOG"
                            }
                            android.util.Log.d(
                                "SmartAssistant",
                                "[$level] ${msg.message()} — ${msg.sourceId()}:${msg.lineNumber()}"
                            )
                            return true
                        }
                    }
                    // Expose window.Android to the page's JavaScript
                    addJavascriptInterface(AndroidBridge(onBack), "Android")

                    webViewClient = object : WebViewClient() {
                        override fun shouldOverrideUrlLoading(
                            view: WebView?,
                            request: WebResourceRequest?,
                        ) = false

                        // Log resource load failures (JS, etc.) to Logcat
                        override fun onReceivedError(
                            view: WebView?,
                            request: WebResourceRequest?,
                            error: android.webkit.WebResourceError?,
                        ) {
                            android.util.Log.e(
                                "SmartAssistant",
                                "Resource error [${error?.errorCode}] ${error?.description} — ${request?.url}"
                            )
                        }
                    }
                    loadUrl(SMART_ASSISTANT_SOURCE)
                }
            },
            modifier = Modifier
                .fillMaxSize()
                .padding(paddingValues),
        )
    }
}

4. Key configuration explained

WebView settings

These WebSettings are required for the Smart Assistant to work correctly:

SettingWhy it is needed
javaScriptEnabled = trueThe web component is built with JavaScript and won’t run without it.
domStorageEnabled = trueEnables localStorage/sessionStorage used by the assistant.
mixedContentMode = …ALWAYS_ALLOWAllows loading remote HTTPS resources from a local file:// page.
allowFileAccess = trueRequired when loading the HTML from a local asset.
mediaPlaybackRequiresUserGestureSet to false to allow audio/video playback inside the assistant.

JavaScript bridge (window.Android)

The AndroidBridge class exposes a native close() method to JavaScript via addJavascriptInterface(…, “Android”). When the user closes the assistant, the web component emits a close event, the HTML calls window.Android.close(), and the bridge invokes your onBack() callback on the main thread to close the screen. Note: @JavascriptInterface methods run on a background thread, so any UI work (like navigation) must be dispatched to the main thread, as shown with Handler(Looper.getMainLooper()).

5. Use the screen in your navigation

Add the screen to your navigation graph and provide the onBack callback:

composable("smart_assistant") {
    SmartAssistantScreen(onBack = { navController.popBackStack() })
}

6. Required permissions

Make sure your app has internet access in AndroidManifest.xml:

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

Sample App

Check our sample app for Android