Android 版无头移动 SDK:使用入门

本文档介绍了如何在 Android 应用中集成和自定义 SDK。

开始使用

借助适用于 Android 的无头移动 SDK,您可以将 CCAI Platform 的联络中心功能集成到自己的内置 Android 界面中。

您无需展示 CCAI Platform 品牌的 widget,而是:

  • 将 CCAI Android SDK 模块添加为依赖项。

  • 使用 Kotlin(推荐)或 Java 构建您自己的界面、流程和视觉设计。

  • 使用 SDK 可执行以下操作:

    • 开始和管理聊天会话和语音会话。

    • 提供电子邮件、语音通话和分流选项。

    • 支持智能操作、附件和会话后流程(客户满意度 [CSAT]、调查问卷、虚拟客服)。

CCAI Platform 可处理联络中心逻辑(路由、队列、渠道、配置、报告)。您拥有完整的应用体验(界面、流程和品牌)。

如果您希望使用具有可配置主题的预构建界面,请使用标准移动 SDK。

要求和支持的环境

本部分介绍了适用于 Android 的无头移动 SDK 的要求和支持的环境。

Android 平台要求

以下平台要求适用于 Android:

  • 最低 Android 版本:Android 6.0(API 级别 23)或更高版本

  • 编译 SDK:36(推荐)

  • 支持的语言:Kotlin(推荐)或 Java

  • Java 兼容性:Java 17 及更高版本

  • Gradle / Android Studio:与您的应用和所用 SDK 版本兼容的最新 Android Gradle 插件和 Android Studio 版本

  • Kotlin 版本:1.6.0 或更高版本

CCAI Platform 实例要求

您需要访问 CCAI 平台实例的开发者设置,才能获取以下信息:

  • 公司密钥

  • 公司密令

  • 主机网址 - CCAI 平台的主机名(例如 your_subdomain.ccaiplatform.com)

这些值用于:

  • 公司密钥和宿主网址 - 在 Android 应用中初始化 SDK。

  • 公司密钥 - 在后端用于对 JSON Web 令牌 (JWT) 进行签名,以对用户进行身份验证。

网络和权限

您的应用必须允许出站流量流向 CCAI 平台端点以及您配置的任何语音提供商端点。

典型的 Android 权限包括(实际列表可能因 SDK 版本和功能集而异):

  • 互联网 / 网络状态:用于所有 SDK 通信。

  • 麦克风:用于语音通话。

  • 通知:用于 Firebase Cloud Messaging (FCM) 推送通知。

  • 存储空间 / 媒体:用于附件。

  • 相机:用于在智能操作中拍摄照片或视频。

  • 屏幕截图:用于屏幕共享 (MediaProjection)。

根据 Android OS 指南声明和请求运行时权限。

推送通知

无头 Android SDK 使用 FCM 来传送:

  • 来电通知。

  • 某些智能操作和通话相关消息。

  • 更新以帮助在应用进入后台时保持状态。

您需要有:

  • Firebase 项目。

  • 应用模块中包含有效的 google-services.json。

  • 应用中的 FCM 令牌处理和注册。

  • 应用中的自定义通知和通话中界面。

无头 Android SDK 如何融入您的应用

Android 版无头移动 SDK 可以视为三个层:

CCAI Platform 平台和 SDK(由 CCAI Platform 处理)

该 SDK 可与 CCAI Platform 平台安全地互动,并提供以下功能:

  • 使用 JSON Web 令牌 (JWT) 进行身份验证。

  • 队列和渠道路由元数据(即时通讯、语音通话、电子邮件、外部改道链接)。

  • 使用服务对象(chatService、queueMenuService 等)进行实时会话状态管理。

  • 用于智能操作、文件附件和屏幕共享的安全传输流水线。

  • 分流逻辑和会话后层次结构评估。

您的 Android 应用(界面和逻辑 - 由您处理)

您可以控制整个视觉和导航体验:

  • 入口点:例如帮助标签页或与我们联系按钮。

  • 菜单:您如何显示队列(例如结算、技术支持)。

  • 会话内界面:自定义聊天气泡、通话界面和控件。

  • 应用的功能:调用 SDK 服务 API 来开始和结束会话,使用 Kotlin 协程和 Flow 观察 SDK 事件,并驱动组件导航。

CCAI 平台管理门户配置

CCAI 平台管理员配置互动规则(营业时间、可用渠道、等待时间阈值和调查问卷)。SDK 会读取此配置,并以原始数据和事件的形式将其公开给您的应用。您可以决定如何在屏幕上绘制该配置。

检索公司凭据

在集成 SDK 之前,请从 CCAI 平台实例获取凭据:

  1. 使用管理员账号登录 CCAI Platform 管理员门户。

  2. 依次前往设置 > 开发者设置。

  3. 在公司密钥和密钥令牌下方,复制以下内容:

    • 公司密钥

    • 公司密令

  4. 记下您的主机网址,即 CCAI 平台的主机名(例如 your_subdomain.ccaiplatform.com)。

您将使用:

  • 在 Android 应用中:公司密钥和主机网址。

  • 在后端服务器中:用于对 JWT 进行签名的公司密钥代码,以进行最终用户身份验证,以及 CCAI 平台使用的可选自定义数据和上下文。

将 SDK 添加到您的 Android 应用

Headless SDK 采用模块化架构。您需要安装核心 CCAIKit 以及应用所需的特定功能模块(例如 CCAIChat 或 CCAIScreenShare)。

添加 CCAI Maven 制品库

将 CCAI Maven 制品库添加到项目的 settings.gradle.kts(或根级 build.gradle)中:

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

配置 Gradle 版本目录(推荐)

Google Cloud 建议使用 Gradle 版本目录来管理 SDK 依赖项。将以下内容添加到 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" }

添加依赖项

在应用级 build.gradle.kts 中,添加核心 SDK 和特定功能模块:

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

如果您未使用版本目录,可以直接声明依赖项:

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
}

初始化和设置

在应用启动期间初始化 SDK。由于 SDK 采用模块化架构,因此请先初始化核心 CCAI 平台系统,然后注册特定的渠道提供商(例如 chat 或 screen share)。

实现 CCAIDelegate 接口

该 SDK 使用 CCAIDelegate 接口进行身份验证回调。关键方法是 Kotlin 挂起函数 ccaiShouldAuthenticate(),SDK 在需要 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
        }
    }
}

上述代码段展示了最简单的两步流程(签名 → 身份验证 → 返回)。

如需了解完整的身份验证合约,请参阅对最终用户进行身份验证并传递自定义数据,其中包括:

  • 如何在后端对 JWT 进行签名,
  • authenticate(jwt) 如何将其换成身份验证令牌,
  • 令牌缓存和失效,以及
  • 包含错误处理的示例。

在应用类中初始化 SDK

使用 CCAI singleton 对象在自定义应用类中初始化 SDK:

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"
        //     )
        // )
    }
}

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
)

更新“AndroidManifest.xml”

确保已注册自定义应用类:

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

可用的 SDK 服务

初始化后,您可以通过 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()

正在提取公司配置

初始化后,您可以使用 companyService 获取公司级配置。这对于填充语言选择器、显示公司名称或在用户进入队列之前读取支持联系详情非常有用。

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

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

对最终用户进行身份验证并传递自定义数据

适用于 Android 的 Headless Mobile SDK 使用 JSON Web 令牌 (JWT) 对用户进行身份验证,并安全地将情境信息传递给客服人员的 CRM。

工作原理

该 SDK 使用简化的两步异步身份验证流程:

  1. SDK 确定需要对用户进行身份验证。

  2. 它会调用您的 CCAIDelegate 方法 - ccaiShouldAuthenticate() - 这是一个挂起函数。

  3. 您的应用使用您的 `Company

  4. 您的应用使用 Company Secret Code 在后端服务器上远程签署 JWT。

  5. 然后,您的应用会将已签名的 JWT 传递给 CCAI.authService?.authenticate(jwt),以换取身份验证令牌。

  6. 身份验证令牌会返回到 SDK 以完成连接。

实现用于身份验证的 CCAIDelegate

您的应用必须实现 CCAIDelegate 接口。当 SDK 需要进行身份验证时,会调用第一个 suspend 方法。

CCAIDelegate 接口

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

实现示例 (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
        }
    }
}

包含 JWT 签名的完整示例(仅供参考和测试)

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

将自定义数据传递给 CRM

如果您想将情境数据(例如用户的当前设备操作系统、位置信息或账号层级)传递给代理,您的后端必须在对 JWT 载荷进行签名之前,将 custom_data 对象注入到该载荷中。

每条自定义数据都必须采用 JSON 对象的格式,其中包含标签(客服人员看到的内容)、值和类型。

支持的数据类型

  • string:标准文本(例如“Pixel 8 Pro”)。

  • number:整数或浮点数(例如 1234 或 99.99)。

  • date:一个 13 位数的 UTC Unix 时间戳,包含毫秒(例如 1537399655992)。

  • url:标准 HTTP/HTTPS 网址格式。

  • boolean:标准 true 或 false 值。

客服可见性:您可以选择性地将数据传递给 CCAI 平台,这些数据对人工客服隐藏,但可用于路由或后端分析。为此,请在数据对象中添加 "invisible_to_agent": true。

预留的 CRM 键

CCAI Platform 支持特定的预留键,这些键可触发平台中的内置行为,例如将用户标记为 VIP 或向客服人员发出有关作恶方的警告。这些必须采用 boolean 类型格式,并且只有在使用安全的 JWT 方法对载荷进行签名时才会被接受。

  • reserved_verified_customer:表示客户是否已通过您的内部系统成功完成身份验证。

  • reserved_bad_actor:向代理标记用户,表明其可能是垃圾信息发送者或欺诈性账号。

  • reserved_repeat_customer:表示相应客户最近是否经常与支持人员联系。

包含预留键的载荷示例

{
  "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"
    }
  }
}

管理身份验证令牌

SDK 提供了一些方法来手动更新或清除缓存的身份验证令牌:

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

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

自定义数据 JWT 载荷架构

当后端构建要使用 Company Secret Code 签名的最终载荷时,必须严格遵循此架构。请注意强制性 iat(签发时间)和 exp(失效时间)时间戳。

{
  "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"
    }
  }
}

启用推送通知

该 SDK 使用推送通知来执行以下操作:

  • 来电。

  • 某些智能操作和与通话相关的事件。

  • 在应用处于后台时保持状态。

Firebase 设置

  1. 创建或使用现有 Firebase 项目。

  2. 注册您的 Android 应用并下载 google-services.json 文件。

  3. 将 google-services.json 放在应用模块目录中。

  4. 在根级 build.gradle.kts 中,添加 Google 服务插件:

    // build.gradle.kts (Project)
    plugins {
        id("com.google.gms.google-services") version "4.4.2" apply false
    }
    
  5. 在应用级 build.gradle.kts 中,应用插件并添加 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. 同步项目。

向 SDK 注册 FCM 令牌

通过 pushNotificationService 注册 FCM 推送令牌,并将传入的 FCM 数据载荷转发到 SDK。您的应用负责渲染面向客户的任何通知或通话中界面。

实现 onMessageReceived

如果您使用的是 FCM,请在 FirebaseMessagingService 类中为推送通知实现监听器。如果不实现这些功能,该服务将无法正常运行。

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

实现 onNewToken

此外,还需实现 onNewToken 方法来处理令牌更新。这样可确保 Android 版无头移动 SDK 收到最新的推送通知令牌:

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

在 AndroidManifest.xml 中注册该服务

将 Firebase Messaging 服务添加到您的 AndroidManifest.xml,以便系统可以将推送消息传递给您的服务:

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

初始令牌注册

在应用启动时(SDK 初始化后),主动注册当前的 FCM 令牌:

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

通知权限处理(Android 13 及更高版本)

在 Android 13(API 级别 33)及更高版本中,上述应用必须请求 POST_NOTIFICATIONS 运行时权限。SDK 为此提供了一项内置实用程序:

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

在注册 FCM 令牌之前,在应用生命周期的早期(例如,在初始配置或首次启动期间)调用此方法,以便能够传送推送通知。