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()
    }
}