# Android SDK

A Kotlin library with no UI. It keeps the conversation — messages, typing,
connection — as `StateFlow`s, and you draw it however your app looks, in
Compose or Views. Android 8.0 (API 26) or later; its only dependencies are
OkHttp and kotlinx.coroutines.

## Setup

Create a publishable token that allows your app. Its origin is
`android-app://` plus its package name:

```bash
vatio tokens create --env live --label android --origin android-app://com.acme.app
```

To add it to an existing token instead, use `vatio tokens origins PREFIX URL...`
or the tokens section of **Channels → Web widget** in the inbox.

The library is built by JitPack from
[github.com/vatio-ai/android](https://github.com/vatio-ai/android). Add the
repository and the dependency:

```kotlin
// settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven("https://jitpack.io")
    }
}

// app/build.gradle.kts
dependencies {
    implementation("com.github.vatio-ai:android:0.2.1")
}
```

Then create a client with the `vatpub_` token. It is meant to ship inside the
app; a `vat_` token must never.

```kotlin
import ai.vatio.Vatio
import ai.vatio.VatioChat

val vatio = Vatio(context, workspace = "acme", token = "vatpub_REPLACE_ME")
val chat = VatioChat(vatio)
```

## Using it

```kotlin
chat.messages        // StateFlow<List<VatioMessage>>, oldest first: id, role, content, createdAt, isPending
chat.isTyping        // the agent is writing
chat.status          // IDLE, CONNECTING, CONNECTED, RECONNECTING, CLOSED

chat.send("What does Acme sell?")   // suspends
```

Collect the flows (`collectAsStateWithLifecycle()` in Compose) and the UI
redraws as the reply arrives. Creating a chat opens nothing: the conversation
starts on the first `send`, and the visitor's message is in `messages` at once,
with `isPending` set until the server confirms it. Call `chat.resume()` when
the screen appears to reopen the visitor's conversation, if they have one.
Keep the chat in a `ViewModel` and call `chat.close()` from `onCleared()`.

A common layout is a text bar at the bottom of a screen that, on submit,
switches to a conversation tab and sends:

```kotlin
AskBar { question ->
    tab = Tab.Conversation
    viewModelScope.launch { chat.send(question) }
}
```

The [library README](https://github.com/vatio-ai/android#readme) has the
complete Compose example.

| Method | Purpose |
|---|---|
| `vatio.config()` | Agent name, avatar, accent color, greeting and suggestions |
| `vatio.conversations(visitorToken)` | This visitor's conversations on this device, newest first |
| `chat.send(text, attachments)` | Send a message, optionally with files; starts the conversation on first call |
| `chat.resume()` | Reopen the stored conversation; never starts a new one |
| `chat.open(conversation)` | Switch to a conversation from `conversations()` |
| `chat.startOver()` | Forget the stored conversation; server history is kept |
| `chat.rate(rating, comment)` | Answer the feedback moment: `GOOD`, `NEUTRAL`, `BAD` |
| `chat.dismissFeedback()` | Decline the feedback moment |
| `chat.close()` | Disconnect |

`send` throws a `VatioException` (`code`, `message`, `status`) and removes the
pending message, so put the text back in your field. Failures outside a
`send` land in `chat.lastError`.

## Files and voice notes

Attach up to four `VatioUpload`s — images, PDFs, text files or audio, up to
8 MB each, as on the [web](https://vatio.ai/docs/sdk/reference#files-and-voice-notes). With files,
the text may be empty:

```kotlin
chat.send(attachments = listOf(VatioUpload.fromUri(context, pickedUri)))   // off the main thread: it reads the file
chat.send(attachments = listOf(VatioUpload.fromFile(recording)))           // a MediaRecorder .m4a
```

Once the server answers, the pending message is replaced by the stored one,
and each `VatioAttachment` has a `url`. A voice note is transcribed in the
background: the message reads "Audio" at first, and its `content` becomes the
transcript a few seconds later, in place; the agent answers after that. When
`isMediaLabel` is true, `content` is only a stand-in ("Image") — draw the
attachment instead. The example app has a composer with a photo picker and a
voice recorder.

## Visitor feedback

When a request looks resolved, Vatio offers a feedback moment, at most once
per conversation (see [the widget guide](https://vatio.ai/docs/channels/widget#visitor-feedback)).
`chat.feedback` holds it; show your own prompt while `isOpen`:

```kotlin
val feedback by chat.feedback.collectAsStateWithLifecycle()
feedback?.takeIf { it.isOpen }?.let {
    RatingCard(question = it.question ?: "How did ${it.agentName} do?") { rating, comment ->
        scope.launch { chat.rate(rating, comment) }
    }
}
```

`question` is how the agent asks, written for that conversation, or `null`
when there is none. It goes back to `null` when the visitor writes again
without answering.

## Signed-in users

Pass a visitor token your backend signed to say who the user is, as described
in [Sessions and channels](https://vatio.ai/docs/authentication/sessions):

```kotlin
val chat = VatioChat(vatio, visitorToken = signedToken)
```

A different token subject is a different person with their own conversation,
so signing out of your app signs out of the chat. An anonymous conversation is
kept when the user signs in.

## Delivery and storage

Replies arrive over a WebSocket. When it drops, the SDK reconnects, polls in
the meantime, and fetches what it missed; messages are deduplicated by id.
`replyStyle` (`STREAM` by default, `PACED`, `INSTANT`) works as on the
[web](https://vatio.ai/docs/sdk/limits#reply-style).

The conversation and the visitor id are stored in `SharedPreferences`, per
workspace and signed-in user. Chat credentials last 12 hours. Rate limits
and message length are the [same as on the web](https://vatio.ai/docs/sdk/limits).
