SDK headless para dispositivos móveis para Android: como começar

Este documento explica como integrar e personalizar o SDK no seu aplicativo Android.

Primeiros passos

O SDK móvel sem interface gráfica para Android permite integrar os recursos da central de atendimento da plataforma CCAI à sua própria interface do Android.

Em vez de apresentar um widget com a marca da plataforma CCAI, você:

  • Adicione os módulos do SDK do Android do CCAI como dependências.

  • Crie suas próprias telas, fluxos e design visual em Kotlin (recomendado) ou Java.

  • Use o SDK para:

    • Iniciar e gerenciar sessões de chat e de voz.

    • Ofereça opções de e-mail, ligação e redirecionamento.

    • Suporte a ações inteligentes, anexos e fluxos pós-sessão (CSAT, pesquisas, agente virtual).

A CCAI Platform processa a lógica da central de atendimento (roteamento, filas, canais, configuração, relatórios). Você é o proprietário de toda a experiência no app (UI, fluxos e branding).

Se você preferir uma UI pré-criada com temas configuráveis, use o SDK móvel padrão.

Requisitos e ambientes compatíveis

Esta seção descreve os requisitos e os ambientes compatíveis com o SDK Headless Mobile para Android.

Requisitos da plataforma Android

Os seguintes requisitos de plataforma se aplicam ao Android:

  • Versão mínima do Android: Android 6.0 (nível 23 da API) ou mais recente

  • SDK de compilação: 36 (recomendado)

  • Linguagens compatíveis: Kotlin (recomendado) ou Java

  • Compatibilidade com Java: Java 17 ou mais recente

  • Gradle / Android Studio: uma versão recente do Plug-in do Android para Gradle e do Android Studio compatível com seu app e a versão do SDK que você está usando

  • Versão do Kotlin: 1.6.0 ou mais recente

Requisitos da instância da plataforma CCAI

Você precisa ter acesso às configurações de desenvolvedor da instância da plataforma CCAI para obter:

  • Chave da empresa

  • Código secreto da empresa

  • URL do host: é o nome do host da plataforma CCAI (por exemplo, your_subdomain.ccaiplatform.com)

Esses valores são usados para:

  • Chave da empresa e URL do host no app Android para inicializar o SDK.

  • Código secreto da empresa: no seu back-end para assinar JSON Web Tokens (JWT) usados para autenticar usuários.

Rede e permissões

O app precisa permitir o tráfego de saída para os endpoints da plataforma CCAI e para os endpoints de provedores de voz que você configurou.

As permissões típicas do Android incluem (a lista real pode variar de acordo com a versão do SDK e seu conjunto de atributos):

  • Estado da Internet / rede: para todas as comunicações do SDK.

  • Microfone: para chamadas de voz.

  • Notificações: para notificações push do Firebase Cloud Messaging (FCM).

  • Armazenamento / mídia: para anexos.

  • Câmera: para captura de fotos ou vídeos em ações inteligentes.

  • Captura de tela: para compartilhamento de tela (MediaProjection).

Declare e solicite permissões de execução de acordo com as diretrizes do SO Android.

Notificações push

O SDK do Android Headless usa o FCM para oferecer:

  • Notificações de chamadas recebidas.

  • Algumas mensagens relacionadas a chamadas e ações inteligentes.

  • Atualizações para ajudar a manter o estado quando o app está em segundo plano.

Você precisará dos seguintes itens:

  • um projeto do Firebase;

  • Um arquivo google-services.json válido no módulo do app.

  • Processamento e registro de tokens do FCM no seu app.

  • Notificação personalizada e interface da ligação no seu app.

Como o SDK do Android headless se encaixa no seu app

O SDK Headless Mobile para Android pode ser visto como três camadas:

Plataforma e SDK da CCAI Platform (processados pela CCAI Platform)

O SDK interage com segurança com a plataforma CCAI Platform e oferece:

  • Autenticação com JSON Web Tokens (JWT).

  • Metadados de roteamento de fila e canal (chat, ligação, e-mail, links de redirecionamento externo).

  • Gerenciamento de estado de sessão em tempo real usando objetos de serviço (chatService, queueMenuService e muito mais).

  • O pipeline de transporte seguro para ações inteligentes, anexos de arquivos e compartilhamento de tela.

  • Lógica de evasão e avaliação de hierarquia pós-sessão.

Seu app Android (UI e lógica, processadas por você)

Você controla toda a experiência visual e de navegação:

  • Pontos de entrada: por exemplo, uma guia Ajuda ou um botão Entre em contato.

  • Menus: como você mostra as filas (por exemplo, Faturamento, Suporte técnico).

  • Interface na sessão: seus balões de chat, telas de chamada e controles personalizados.

  • O que o app faz: chama APIs de serviço do SDK para iniciar e encerrar sessões, observa eventos do SDK usando corrotinas e fluxos do Kotlin e impulsiona a navegação de componentes.

Configuração do portal de administrador da plataforma CCAI

Os administradores da Plataforma CCAI configuram as regras de engajamento (horário de funcionamento, canais disponíveis, limites de tempo de espera e pesquisas). O SDK lê essa configuração e a expõe ao app como dados e eventos brutos. Você decide como desenhar essa configuração na tela.

Recuperar credenciais da empresa

Antes de integrar o SDK, extraia as credenciais da sua instância da plataforma CCAI:

  1. Faça login no portal de administração da CCAI Platform com uma conta de administrador.

  2. Acesse Configurações > Configurações do desenvolvedor.

  3. Em Chave da empresa e código secreto, copie:

    • Chave da empresa

    • Código secreto da empresa

  4. Anote o URL do host, que é o nome do host da plataforma CCAI (por exemplo, your_subdomain.ccaiplatform.com).

Você usará:

  • No seu app Android: Chave da empresa e URL do host.

  • No seu servidor de back-end: código secreto da empresa para assinar JWTs para autenticação do usuário final e dados e contexto personalizados opcionais usados pela plataforma da CCAI.

Adicionar o SDK ao seu app Android

O SDK Headless usa uma arquitetura modular. Você instala o CCAIKit principal junto com os módulos de recursos específicos que o app exige (por exemplo, CCAIChat ou CCAIScreenShare).

Adicionar o repositório Maven da CCAI

Adicione o repositório Maven da CCAI ao settings.gradle.kts do projeto (ou build.gradle raiz):

// settings.gradle.kts
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://sdk.ujet.co/ccaip/android/") }
    }
}

Configurar o catálogo de versões do Gradle (recomendado)

OGoogle Cloud recomenda usar um catálogo de versões do Gradle para gerenciar dependências do SDK. Adicione o seguinte ao seu gradle/libs.versions.toml:

[versions]
ccaiVersion = "3.3.1"

[libraries]
ccai-kit       = { group = "com.ccaiplatform.android", name = "CCAIKit",        version.ref = "ccaiVersion" }
ccai-chat      = { group = "com.ccaiplatform.android", name = "CCAIChat",       version.ref = "ccaiVersion" }
ccai-chat-red  = { group = "com.ccaiplatform.android", name = "CCAIChatRed",    version.ref = "ccaiVersion" }
ccai-call      = { group = "com.ccaiplatform.android", name = "CCAICall",       version.ref = "ccaiVersion" }
ccai-call-red  = { group = "com.ccaiplatform.android", name = "CCAICallRed",    version.ref = "ccaiVersion" }
ccai-screenshare = { group = "com.ccaiplatform.android", name = "CCAIScreenShare", version.ref = "ccaiVersion" }

Adicionar dependências

No build.gradle.kts do nível do app, adicione o SDK principal e os módulos de recursos específicos:

// app/build.gradle.kts
android {
    compileSdk = 36

    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }

    kotlinOptions {
        jvmTarget = "17"
    }
}

dependencies {
    // 1. Core (always required)
    implementation(libs.ccai.kit)

    // 2. Chat (required for chat sessions)
    implementation(libs.ccai.chat)
    implementation(libs.ccai.chat.red)   // media layer paired with CCAIChat

    // 3. Voice (required for instant calls, voicemail, and scheduled calls)
    implementation(libs.ccai.call)
    implementation(libs.ccai.call.red)   // media/transport layer paired with CCAICall

    // 4. Screen Share (optional)
    // implementation(libs.ccai.screenshare)
}

Se você não estiver usando o catálogo de versões, declare as dependências diretamente:

val ccaiVersion = "3.3.1"

dependencies {
    implementation("com.ccaiplatform.android:CCAIKit:$ccaiVersion")
    implementation("com.ccaiplatform.android:CCAIChat:$ccaiVersion")
    implementation("com.ccaiplatform.android:CCAIChatRed:$ccaiVersion")
    implementation("com.ccaiplatform.android:CCAICall:$ccaiVersion")        // Voice calls
    implementation("com.ccaiplatform.android:CCAICallRed:$ccaiVersion")     // Twilio VoIP backend
    implementation("com.ccaiplatform.android:CCAIScreenShare:$ccaiVersion") // Optional, for screen share
}

Inicialização e configuração

Inicialize o SDK durante a inicialização do app. Devido à arquitetura modular do SDK, inicialize primeiro o sistema principal da plataforma CCAI e depois registre os provedores de canais específicos (como chat ou screen share).

Implementar a interface CCAIDelegate

O SDK usa uma interface CCAIDelegate para callbacks de autenticação. O método principal é uma função de suspensão do Kotlin ccaiShouldAuthenticate() que o SDK chama quando precisa de um JWT.

import com.ccaiplatform.ccaikit.CCAIDelegate

class MyCCAIDelegate : CCAIDelegate {

    /**
     *   Called by the SDK when it needs a signed JWT for authentication.
     *   This is a suspend function - you can make network calls here.
     *
     *   @return The auth token returned by `authService.authenticate(jwt)`,

*   or null on failure. This is what the SDK expects - not the raw JWT.
     */
    override suspend fun ccaiShouldAuthenticate(): String? {
        return try {
            // 1. Call your backend server to get a signed JWT
            val jwt = MyBackendApi.getSignedJwt() ?: return null

            // 2. Exchange the JWT for an auth token using authService - this is
            //    what the SDK expects to be returned (not the raw JWT).
            CCAI.authService?.authenticate(jwt)
        } catch (e: Exception) {
            null // Return null to signal authentication failure
        }
    }
}

O snippet anterior mostra o fluxo mínimo de duas etapas (sign → authenticate → return).

Consulte Autenticar usuários finais e transmitir dados personalizados para conferir o contrato de autenticação completo, incluindo:

  • como assinar o JWT no back-end,
  • como o authenticate(jwt) troca por um token de autenticação,
  • armazenamento em cache e invalidação de tokens;
  • exemplos práticos com tratamento de erros.

Inicializar o SDK na classe do aplicativo

Inicialize o SDK na sua classe de aplicativo personalizada usando o objeto singleton CCAI:

import android.app.Application
import com.ccaiplatform.ccaikit.CCAI
import com.ccaiplatform.ccaikit.InitOptions
import com.ccaiplatform.ccaichat.initializeChat
import com.ccaiplatform.ccaichat.model.ChatOptions
import com.ccaiplatform.ccaicall.initializeCall
import com.ccaiplatform.ccaikit.initializeScreenShare
import com.ccaiplatform.ccaikit.models.screenShare.ScreenShareOptions

class MainApplication : Application() {

    private val delegate = MyCCAIDelegate()

    override fun onCreate() {
        super.onCreate()

        // 1. Build InitOptions
        val initOptions = InitOptions(
            key = "YOUR_COMPANY_KEY",
            urlHost = "your_subdomain.ccaiplatform.com",
            languageCode = "en",       // Optional: ISO 639 code
            delegate = delegate        // Your delegate implementation
        )

        // 2. Initialize the core SDK
        CCAI.initialize(
            context = this,
            options = initOptions
        )

        // 3. Initialize chat (minimal)
        CCAI.initializeChat(context = this)

        // Initialize chat (with optional configuration)
        // val chatOptions = ChatOptions(
        //     webFormInterface = null,              // Implement to intercept and render custom web forms within your own UI
        //     downloadTranscriptVisibility = DownloadTranscriptVisibility.SHOW_ALL,
        //     greeting = "Hello! How can I help you?"
        // )
        // CCAI.initializeChat(
        //     context = this,
        //     options = chatOptions
        // )

        // 4. Initialize call (required whenever your app uses voice or scheduled calls)
        CCAI.initializeCall(context = this)

        // 5. Initialize screen share (optional)
        // CCAI.initializeScreenShare(
        //     context = this,
        //     options = ScreenShareOptions(
        //         key = "YOUR_COMPANY_KEY",
        //         domain = "your_subdomain.ccaiplatform.com"
        //     )
        // )
    }
}

Referência de configuração do InitOptions

data class InitOptions(
    /// The company key used for authentication
    var key: String,

    /// The host URL for CCAI platform (for example, "my-unique-instance.uc1.ccaiplatform.com")
    var urlHost: String,

    /// The preferred language code for localization (defaults to "en")
    var languageCode: String? = "en",

    /// Listener that handles authentication
    var delegate: CCAIDelegate? = null,

    /// Whether to cache the authentication token (defaults to true)
    var cacheAuthToken: Boolean = true
)

Atualizar AndroidManifest.xml

Confira se a classe do aplicativo personalizado está registrada:

<application
    android:name=".MainApplication"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/Theme.YourApp">
</application>

Serviços de SDK disponíveis

Depois da inicialização, você pode acessar vários serviços usando o objeto singleton CCAI:

val authService = CCAI.authService
val companyService = CCAI.companyService
val queueMenuService = CCAI.queueMenuService
val optionsService = CCAI.optionsService
val languageService = CCAI.languageService
val pushNotificationService = CCAI.pushNotificationService
val chatService = CCAI.chatService              // Only available after initializeChat()
val screenShareService = CCAI.screenShareService // Only available after initializeScreenShare()
val rateService = CCAI.rateService              // For CSAT ratings and survey submission
val callService = CCAI.callService              // Only available after initializeCall()

Buscando configuração da empresa

Após a inicialização, é possível buscar a configuração no nível da empresa usando o companyService. Isso é útil para preencher seletores de idioma, mostrar o nome da empresa ou ler os detalhes de contato do suporte antes que o usuário entre em uma fila.

// Fetch company details
val company = CCAI.companyService?.get()

// CompanyResponse contains:
// - displayName: String
// - supportEmail: String?
// - languages: List<String>
// - phoneNumber: String?
// ...

Autenticar usuários finais e transmitir dados personalizados

O SDK Headless Mobile para Android usa JSON Web Tokens (JWT) para autenticar usuários e transmitir informações contextuais com segurança ao CRM do agente.

Como funciona

O SDK usa um fluxo de autenticação assíncrono simplificado de duas etapas:

  1. O SDK determina que precisa autenticar o usuário.

  2. Ele chama o método CCAIDelegate, ccaiShouldAuthenticate(), que é uma função de suspensão.

  3. Seu app assina um JWT remotamente no servidor de back-end usando o `Company

  4. O app assina um JWT remotamente no servidor de back-end usando seu Company Secret Code.

  5. Em seguida, o app transmite o JWT assinado para CCAI.authService?.authenticate(jwt) para trocá-lo por um token de autenticação.

  6. O token de autenticação é retornado ao SDK para concluir a conexão.

Implementar o CCAIDelegate para autenticação

O app precisa implementar a interface CCAIDelegate. O SDK chama o primeiro método suspend quando precisa de autenticação.

Interface CCAIDelegate

interface CCAIDelegate {
    suspend fun ccaiShouldAuthenticate(): String?
}

Exemplo de implementação (Kotlin)

class CCAIDelegate : CCAIDelegate {
    override suspend fun ccaiShouldAuthenticate(): String? {
        // 1. Sign JWT remotely on your backend server
        val jwt = signJWTRemotely() ?: return null

        // 2. Authenticate JWT using authService to get an auth token
        return try {
            CCAI.authService?.authenticate(jwt)
        } catch (e: Exception) {
            null
        }
    }
}

Exemplo completo com assinatura JWT (apenas para referência e teste)

class AuthController : CCAIDelegate {
    override suspend fun ccaiShouldAuthenticate(): String? {
        // First, sign JWT with company secret (do this on your backend in production)
        val jwt = Jwts.builder()
            .setClaims(claims)
            .signWith(SignatureAlgorithm.HS384, companySecret?.encodeToByteArray())
            .compact()

        // Then authenticate JWT using authService to get an auth token
        return try {
            CCAI.authService?.authenticate(jwt)
        } catch (e: Exception) {
            null
        }
    }
}

Transmitir dados personalizados ao CRM

Se você quiser transmitir dados contextuais ao agente (por exemplo, o SO do dispositivo atual, a localização ou o nível da conta do usuário), seu back-end precisará injetar um objeto custom_data no payload do JWT antes de assiná-lo.

Cada parte de dados personalizados precisa ser formatada como um objeto JSON que contenha um rótulo (o que o agente vê), um valor e um tipo.

Tipos de dados compatíveis

  • string: texto padrão (por exemplo, "Pixel 8 Pro").

  • number: números inteiros ou de ponto flutuante (por exemplo, 1234 ou 99,99).

  • date: um carimbo de data/hora Unix UTC de 13 dígitos, incluindo milissegundos (por exemplo, 1537399655992).

  • url: formato padrão de URL HTTP/HTTPS.

  • boolean: valor padrão verdadeiro ou falso.

Visibilidade do agente: é possível transmitir dados para a plataforma CCAI que ficam ocultos do agente humano, mas disponíveis para roteamento ou análise de back-end. Para fazer isso, inclua "invisible_to_agent": true no objeto de dados.

Chaves de CRM reservadas

A plataforma CCAI é compatível com chaves reservadas específicas que acionam comportamentos integrados na plataforma, como sinalizar um usuário como VIP ou avisar um agente sobre um usuário de má-fé. Eles precisam ser formatados como tipos boolean e só serão aceitos se o payload for assinado usando o método seguro JWT.

  • reserved_verified_customer: indica se um cliente foi autenticado com sucesso pelos seus sistemas internos.

  • reserved_bad_actor: sinaliza o usuário para o agente como um possível spammer ou conta fraudulenta.

  • reserved_repeat_customer: indica se o cliente entrou em contato com o suporte com frequência recentemente.

Exemplo de payload com chaves reservadas

{
  "iat": 1537399656,
  "exp": 1537400256,
  "custom_data": {
    "reserved_verified_customer": {
      "label": "Verified Customer",
      "value": true,
      "type": "boolean"
    },
    "reserved_bad_actor": {
      "label": "Bad Actor",
      "value": false,
      "type": "boolean"
    },
    "reserved_repeat_customer": {
      "label": "Repeat Customer",
      "value": false,
      "type": "boolean"
    }
  }
}

Gerenciar tokens de autenticação

O SDK oferece métodos para atualizar ou limpar manualmente o token de autenticação em cache:

// Set a new auth token
CCAI.authService?.updateAuthToken("new_auth_token")

// Clear the current token (for example, on user logout)
CCAI.authService?.updateAuthToken(null)

Esquema de payload JWT de dados personalizados

Quando o back-end constrói o payload final a ser assinado com seu Company Secret Code, ele precisa seguir estritamente este esquema. Observe os carimbos de data/hora obrigatórios iat (emitido em) e exp (validade).

{
  "iat": 1537399656,
  "exp": 1537400256,
  "custom_data": {
    "os_version": {
      "label": "OS Version",
      "value": "14.0",
      "type": "string"
    },
    "membership_tier": {
      "label": "Membership Tier",
      "value": "Platinum",
      "type": "string"
    },
    "ssn_last_four": {
      "label": "SSN",
      "value": "1234",
      "type": "string",
      "invisible_to_agent": true
    },
    "reserved_verified_customer": {
      "label": "Verified Customer",
      "value": true,
      "type": "boolean"
    }
  }
}

Ativar notificações push

O SDK usa notificações push para:

  • Ligações recebidas.

  • Algumas ações inteligentes e eventos relacionados a chamadas.

  • Manter o estado quando o app está em segundo plano.

Configuração do Firebase

  1. Crie ou use um projeto do Firebase.

  2. Registre seu app Android e faça o download do arquivo google-services.json.

  3. Coloque google-services.json no diretório do módulo do app.

  4. No build.gradle.kts no nível raiz, adicione o plug-in dos Serviços do Google:

    // build.gradle.kts (Project)
    plugins {
        id("com.google.gms.google-services") version "4.4.2" apply false
    }
    
  5. No arquivo build.gradle.kts no nível do app, aplique o plug-in e adicione dependências de Firebase:

    // app/build.gradle.kts
    plugins {
        id("com.google.gms.google-services")
    }
    
    dependencies {
        // Firebase BoM for version management
        implementation(platform("com.google.firebase:firebase-bom:33.5.1"))
        implementation("com.google.firebase:firebase-messaging")
    }
    
  6. Sincronize o projeto.

Registrar o token do FCM com o SDK

Registre o token push do FCM usando o pushNotificationService e encaminhe os payloads de dados do FCM recebidos para o SDK. Seu app é responsável por renderizar qualquer notificação voltada ao cliente ou UI de chamada.

Implementar onMessageReceived

Se você estiver usando o FCM, implemente um listener na classe FirebaseMessagingService para notificações push. Se eles não forem implementados, o serviço não vai funcionar corretamente.

import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.ccaiplatform.ccaikit.CCAI
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class MyFirebaseMessagingService : FirebaseMessagingService() {

    private val scope = CoroutineScope(Dispatchers.IO)

    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        scope.launch {
            // Forward CCAI platform push notifications to the SDK.
            CCAI.pushNotificationService?.handlePushNotification(remoteMessage.data)

            // Render any customer-facing notification UI in your own app code.
            MyNotificationRenderer.showIfNeeded(application, remoteMessage.data)
        }
    }
}

Implementar onNewToken

Implemente também o método onNewToken para processar atualizações de token. Isso garante que o SDK móvel sem interface gráfica para Android receba o token de notificação push mais recente:

class MyFirebaseMessagingService : FirebaseMessagingService() {
    // ...
    override fun onNewToken(token: String) {
        // Fetch the updated token from Firebase and update it in CCAI
        CCAI.pushNotificationService?.updatePushToken(token)
    }
}

Registre o serviço em AndroidManifest.xml.

Adicione o serviço de mensagens do Firebase ao seu AndroidManifest.xml para que o sistema possa enviar mensagens push ao seu serviço:

<application>
    <service
        android:name=".firebase.MyFirebaseMessagingService"
        android:exported="true">
        <intent-filter>
            <action android:name="com.google.firebase.MESSAGING_EVENT" />
        </intent-filter>
    </service>
</application>

Registro inicial do token

Na inicialização do app (após a inicialização do SDK), registre proativamente o token do FCM atual:

import com.google.firebase.messaging.FirebaseMessaging

// In your Application.onCreate(), after CCAI.initialize()
FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
    if (task.isSuccessful) {
        val token = task.result
        CCAI.pushNotificationService?.updatePushToken(token)
    }
}

Processamento de permissões de notificação (Android 13 ou mais recente)

No Android 13 (nível 33 da API), os apps mencionados precisam solicitar a permissão de execução POST_NOTIFICATIONS. O SDK oferece um utilitário integrado para isso:

import com.ccaiplatform.ccaikit.util.PermissionUtil

// Request notification permissions from the user
PermissionUtil.requestPermissionsForNotifications(activity)

// Check if permissions have been granted
val isGranted = PermissionUtil.isPermissionsForNotificationsGranted(context)
if (isGranted) {
    Log.d("CCAI", "Push notifications permitted")
}

Chame isso no início do ciclo de vida do app (por exemplo, durante a integração ou o primeiro lançamento) antes de registrar o token do FCM para que as notificações push possam ser entregues.