> ## Documentation Index
> Fetch the complete documentation index at: https://hellotars.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Kotlin integration

> Embed a Tars agent in an Android app with WebView and a JavascriptInterface named AndroidBridge.

This reference covers the Android wiring for the [WebView bridge](/docs/developer/mobile/webview-bridge). The dashboard generates a starting snippet with your agent's real URL: open **Distribute** → **Mobile App**, turn on **Enable Bridge Events**, and select the **Kotlin/Android** tab. The snippet on this page also relays widget messages to the app, which the bridge requires.

## Requirements

* `android.webkit.WebView` with JavaScript and DOM storage enabled
* A `@JavascriptInterface` object registered under the name `AndroidBridge`
* **Enable Bridge Events** turned on for the agent

## Snippet

```kotlin theme={null}
class AgentChatActivity : AppCompatActivity() {
    private lateinit var webView: WebView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        webView = WebView(this).apply {
            settings.javaScriptEnabled = true
            settings.domStorageEnabled = true
        }
        setContentView(webView)

        webView.webViewClient = object : WebViewClient() {
            override fun onPageFinished(view: WebView, url: String) {
                // Forward every tars: message from the page to AndroidBridge
                val relay = """
                    window.addEventListener('message', function (event) {
                      var msg = event.data;
                      if (msg && typeof msg.type === 'string' && msg.type.indexOf('tars:') === 0) {
                        AndroidBridge.postMessage(JSON.stringify(msg));
                      }
                    });
                """
                webView.evaluateJavascript(relay, null)

                val init = """{"type":"tars:init","platform":"android","version":"1.0","chrome":{"showHeader":false}}"""
                webView.evaluateJavascript("window.postMessage($init, '*');", null)
            }
        }

        webView.addJavascriptInterface(object {
            @JavascriptInterface
            fun postMessage(data: String) {
                val msg = JSONObject(data)
                when (msg.optString("type")) {
                    "tars:navigate" -> handleNavigation(msg.optString("route"))
                    "tars:close" -> finish()
                    "tars:data" -> handleData(msg)
                }
            }
        }, "AndroidBridge")

        webView.loadUrl("https://YOUR_WIDGET_HOST/widget/YOUR_AGENT_ID?region=YOUR_REGION")
    }
}
```

## Message wiring

| Piece                                        | Role                                                                                                                                                           |
| -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `addJavascriptInterface(…, "AndroidBridge")` | Registers the widget → app channel. The interface name must be exactly `AndroidBridge`                                                                         |
| `postMessage(data: String)`                  | Receives widget messages as a JSON string, parse it and branch on `type`                                                                                       |
| `onPageFinished`                             | Installs the relay, then posts `tars:init` into the page. The widget posts its messages inside the page, so without the relay `AndroidBridge` receives nothing |
| `evaluateJavascript`                         | Sends later app → widget messages, such as `tars:inject` or `tars:command`                                                                                     |
| `javaScriptEnabled`, `domStorageEnabled`     | Required. The widget needs script execution and local storage                                                                                                  |

The init runs in `onPageFinished`, which can fire before the widget mounts. A `tars:init` posted that early is dropped and never answered. Resend `tars:init` until a `tars:ready` carrying `capabilities` arrives, because a repeat init after the handshake is ignored. The snippet above posts the init once, so add the resend before you ship it.

Handle at least `tars:navigate`, `tars:data`, and `tars:close`: the three capabilities the bridge declares in its `tars:ready` reply to `tars:init`. Field-level payloads for every message are in the [bridge events reference](/docs/developer/mobile/bridge-events).

## Related pages

* [The WebView bridge](/docs/developer/mobile/webview-bridge)
* [Bridge events reference](/docs/developer/mobile/bridge-events)
* [React Native integration](/docs/developer/mobile/react-native)
* [Swift integration](/docs/developer/mobile/swift)
