Mobile SDKs
Kotlin (Android)
com.primoia.vocall -- Coroutines, StateFlow
Kotlin SDK
The Kotlin SDK provides a coroutine-based client for integrating Vocall into Android and JVM applications. It uses OkHttp for WebSocket transport and Kotlin StateFlow for reactive state management.
Installation
Add the dependency to your build.gradle.kts:
dependencies {
implementation("com.primoia.vocall:vocall-sdk:0.1.0")
}
Requirements
| Dependency | Minimum Version | |-------------------------|-----------------| | Java | 17+ | | Kotlin | 1.9.22+ | | OkHttp | 4.12+ | | kotlinx-serialization | 1.6.2+ | | kotlinx-coroutines | 1.7.3+ |
Creating a Client
val client = VocallClient(
serverUrl = "wss://engine.example.com",
token = "your-auth-token",
visitorId = "user-abc-123",
scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
)
State Observables
The client exposes StateFlow properties for reactive observation:
// Collect status changes
client.status.collect { status -> updateUI(status) }
// Other observable flows
client.messages // StateFlow<List<ChatMessage>>
client.sessionId // StateFlow<String?>
client.connected // StateFlow<Boolean>
client.pendingConfirmSeq // StateFlow<Int?>
client.pendingConfirmMessage // StateFlow<String?>
VocallStatus Enum
| Status | Description |
|----------------|------------------------------------------|
| IDLE | Client created, not yet connected |
| CONNECTING | WebSocket connection in progress |
| CONNECTED | WebSocket open, manifest not yet sent |
| READY | Manifest acknowledged, ready for input |
| PROCESSING | Engine is processing a request |
| DISCONNECTED | Connection closed |
| ERROR | Connection or protocol error occurred |
Callbacks
Register callbacks for engine-driven events:
client.onConnected = { sessionId ->
Log.d("Vocall", "Connected: $sessionId")
}
client.onDisconnected = { reason ->
Log.d("Vocall", "Disconnected: $reason")
}
client.onStatusChanged = { status ->
Log.d("Vocall", "Status: $status")
}
client.onToast = { message, level ->
showToast(message, level)
}
client.onConfirm = { seq, message ->
showConfirmDialog(seq, message)
}
Methods
// Connect with a manifest
client.connect(manifest)
// Send a text message
client.sendText("Emitir nota fiscal para o tomador João")
// Respond to a confirmation prompt
client.sendConfirm(seq = 1, confirmed = true)
// Send a command execution result back to the engine
client.sendResult(seq = 2, success = true, data = mapOf("id" to "123"))
// Interrupt assistant mid-response
client.interrupt()
// Close the connection
client.disconnect()
// Release all resources
client.dispose()
Field Registry
Bind your UI fields so the engine can read and write values:
// Register a text field
client.fieldRegistry.registerField(
screenId = "emitir_nfse",
fieldId = "valor_servico",
getValue = { valorInput.text.toString() },
setValue = { value -> valorInput.setText(value) }
)
// Register a clickable action
client.fieldRegistry.registerAction(
screenId = "emitir_nfse",
actionId = "submit",
handler = { submitForm() }
)
// Navigation callback
client.fieldRegistry.onNavigate = { screenId ->
navController.navigate(screenId)
}
// Modal callbacks
client.fieldRegistry.onOpenModal = { modalId, data ->
showModal(modalId, data)
}
Manifest Builder
Use the Kotlin DSL to declare your app structure:
val manifest = manifest {
screen("dashboard") {
label = "Dashboard"
field("search") {
type = FieldType.TEXT
label = "Buscar"
}
action("refresh") {
label = "Atualizar"
}
}
screen("emitir_nfse") {
label = "Emitir NFS-e"
field("tomador") {
type = FieldType.TEXT
label = "Tomador"
}
field("valor_servico") {
type = FieldType.NUMBER
label = "Valor do Servico"
}
action("submit") {
label = "Emitir"
}
}
}
client.connect(manifest)
Android Integration Example
class MainActivity : AppCompatActivity() {
private lateinit var vocall: VocallClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
vocall = VocallClient(
serverUrl = "wss://engine.example.com",
token = "tok_abc",
visitorId = "visitor-1",
scope = lifecycleScope
)
lifecycleScope.launch {
vocall.status.collect { status ->
binding.statusLabel.text = status.name
}
}
}
override fun onDestroy() {
super.onDestroy()
vocall.dispose()
}
}