Android SDK 指南

本页介绍了如何使用 Android SDK。

访问示例应用

如需下载 Android 示例应用,请点击 Android 示例应用

如需使用 Android 移动 SDK 创建示例应用,您需要具备以下条件:

  • CCAI 平台门户网站的公司密钥和公司密钥。

  • Android 5.0(API 级别 21,Lollipop)或更高版本。

  • Firebase Cloud Messaging 或推送通知的 Google Cloud 消息传递。

  • 应用已迁移到 AndroidX。

Twilio SDK 升级要求

如果 Android SDK 是直接使用我们的软件包集成的,则需要 Twilio SDK 遵循特定版本,否则可以忽略此要求。

// Twilio VoIP SDK
api 'com.twilio:voice-android:6.1.1'
// Twilio Conversations SDK
api 'com.twilio:conversations-android:3.1.0'

此外,Android SDK 中已包含 Proguard 规则,以确保 ProGuard 不会移除 Twilio 可编程语音库,并且在 ProGuard 意外移除该库时可用于问题排查。

-keep class com.twilio.** { *; }
-keep class tvo.webrtc.** { *; }
-dontwarn tvo.webrtc.**
-keep class com.twilio.voice.** { *; }
-keepattributes InnerClasses

为了支持最新的 Twilio 版本,从 Android SDK 版本 0.34.0 开始,SDK 将不再与以 Java 7 为目标平台的应用实现二进制兼容。为了使用此版本和未来的版本,开发者必须升级其应用以面向 Java 8。请参阅以下代码示例:

android {
    compileOptions {
        sourceCompatibility 1.8
        targetCompatibility 1.8
    }
}

获取公司密钥和公司 Secret

  1. 使用管理员凭据登录管理门户。

  2. 依次前往设置 > 开发者设置 > 公司密钥和 Secret Code

  3. 检索公司密钥公司密钥代码

安装

将 Android SDK 制品库添加到根项目的 Gradle 设置中。

build.gradle(项目)

allprojects {
    repositories {
        google()
        jcenter()
        maven {
            url "https://sdk.ujet.co/android/"
        }
    }
}

build.gradle(模块:app)

dependencies {
    // Replace x.y.z with latest version of CCAI Platform SDK
    def ujetSdkVersion = "x.y.z"
    implementation "co.ujet.android:ujet-android:$ujetSdkVersion"

    // CCAI Platform supports co-browse for Web SDK version 0.46.0 or
    // higher.
    // To use co-browse, declare the following dependency.
    implementation "co.ujet.android:cobrowse:$ujetVersion"
}

设置公司设置

在 AndroidManifest.xml 文件中以元数据的形式输入公司设置。

AndroidManifest.xml

<application>
    // ...
    <!-- Company Settings -->
    <meta-data android:name="co.ujet.android.subdomain" android:value="@string/ujet_subdomain"/>
    <meta-data android:name="co.ujet.android.companyKey" android:value="@string/ujet_company_key"/>
    <meta-data android:name="co.ujet.android.companyName" android:value="@string/ujet_company_name"/>
    // ...
</application>

strings.xml

<resources>
    <string name="ujet_subdomain">YOUR_SUBDOMAIN</string>
    <string name="ujet_company_key">YOUR_COMPANY_KEY</string>
    <string name="ujet_company_name">YOUR_COMPANY_NAME</string>
</resources>

JWT 签名

出于安全考虑,最终用户信息必须在您的服务器中以 JWT 格式签名。

示例应用包含用于测试的 APIManager,您需要将 UJET_COMPANY_SECRET 放在 mock/APIManager.java 中。APIManager 必须实现一种方法,该方法会启动异步调用来对返回令牌的 JWT 身份验证令牌进行签名。

在生产环境中,您必须在服务器上实现签名流程。

public class APIManager {
    public static final String UJET_COMPANY_SECRET = "Please input your UJET_COMPANY_SECRET from Ujet developer admin page";
    // ...
}

初始化 SDK

在 Android 应用类的 onCreate 方法中初始化 SDK。

public class ExampleApplication extends Application implements UjetRequestListener {
    @Override
    public void onCreate() {
        super.onCreate();

        Ujet.init(this);
    }
    // ...
}

最终用户身份验证

最终用户是指通过应用与您的客户支持团队联系的消费者。

为了在应用中对最终用户进行身份验证,我们引入了 JWT 签名机制。

当需要身份验证时,Android SDK 会请求对载荷进行签名。如果签名成功,应用会将已签名的 JWT 交换为最终用户的身份验证令牌。

ExampleApplication 中,您应实现用于对身份验证令牌和自定义数据进行签名的 UjetRequestListener 接口。

public class ExampleApplication extends Application implements UjetRequestListener {
    @Override
    public void onCreate() {
        super.onCreate();

        Ujet.init(this);
    }

    @Override
    public void onSignPayloadRequest(Map<String, Object> payload, UjetPayloadType ujetPayloadType, UjetTokenCallback tokenCallback) {
        if (ujetPayloadType == UjetPayloadType.AuthToken) {

            // In production, you should implement this on your server.

            // The server must implement a method that initiates an asynchronous call
to sign and return a JWT auth token.
            APIManager.getHttpManager()
                .getAuthToken(payload, UjetPayloadType.AuthToken, new UjetTokenCallback() {
                    @Override
                    public void onSuccess(@Nullable final String authToken) {
                        tokenCallback.onToken(authToken);
                    }

                    @Override
                    public void onFailure(@Nullable final String authToken) {
                        tokenCallback.onError();
                    }
                });
        }
    }
}

如需了解详情,请参阅最终用户身份验证

设置推送通知

本部分概述了如何启用移动推送通知。

准备 Firebase

您应准备一个 Firebase 项目。

如果您已有项目,则可以使用自己的项目并跳过此流程。

  1. Firebase 控制台中创建一个 Firebase 项目。

  2. 从 Firebase 控制台中的“设置”>“常规”下载 google-services.json。

  1. 在 Firebase 控制台中,依次前往“设置”>“CLOUD MESSAGING”,获取服务器密钥。

添加服务账号密钥

您需要在移动应用中添加服务账号密钥,才能接收推送通知。如需获取服务账号密钥,请参阅使用服务账号进行身份验证

如需向移动应用添加服务账号密钥,请按以下步骤操作:

  1. 在 CCAI 平台门户中,依次点击设置 > 开发者设置。 如果您没有看到设置菜单,请点击 菜单

  2. 前往移动应用窗格。

  3. 点击应用旁边的 修改。系统会显示修改移动应用对话框。

  4. 服务账号字段中,输入您的服务账号密钥,然后点击保存

Android 客户端设置

  1. google-services.json 复制到您的应用目录中,例如 PROJECT_ROOT/app/google-services.json

  2. 使用以下代码获取 FCM 令牌:

    FirebaseMessaging.getInstance().getToken()
        .addOnCompleteListener(task -> {
            if (!task.isSuccessful() || task.getResult() == null) {
                Log.w("FCM", "Couldn't get FCM token");
                return;
            }
    
            String token = task.getResult();
            Log.i("FCM", "FCM token: " + token);
        });
    

    如果令牌刷新,则会调用在清单中注册的 FirebaseMessagingService。如需了解详情,请参阅设置 Firebase Cloud Messaging 客户端应用 (Android)

    public class YourFirebaseMessagingService extends FirebaseMessagingService {
       /**
           * There are two scenarios when onNewToken is called:
           * 1) When a new token is generated on initial app startup
           * 2) Whenever an existing token is changed
           * Under #2, there are three scenarios when the existing token is changed:
           * A) App is restored to a new device
           * B) User uninstalls/re-installs the app
           * C) User clears app data
           */
       @Override
       public void onNewToken(String token) {
           Log.i("FCM", "FCM token updated: " + token);
       }
    }
    
  3. 在您的 Application 类中实现 UjetRequestListener.onRequestPushTokenUjetRequestListener.onRequestPushToken 应返回 FCM/GCM 令牌。

    public class YourApplication extends Application implements UjetRequestListener {
        /**
            * Return your FCM/GCM token
            */
        @Override
        public String onRequestPushToken() {
            return yourToken(); // As shown in the previous step, you can get your token using FirebaseMessaging.getInstance().getToken().addOnCompleteListener(task -> { })
        }
    }
    
  4. 处理推送通知。如果您希望 Contact Center AI 平台 (CCAI 平台) 处理自己的推送消息,可以直接将数据传递给 UjetPushHandler.handle()

    • 应用仅处理设置了 ujet_noti_type(或 noti_type,为了实现向后兼容性)字段的消息。

    • 否则,您可以选择仅发送包含 ujet_noti_type 的消息以供 UjetPushHandler.handle() 处理。

    以下是推送通知消息的示例:

    {
        "call_id"           : 12345,
        "ujet_noti_type"    : "connect_call",
        "noti_type"         : "connect_call",
        "call_type"         : "ScheduledCall",
        "fail_reason"       : "none",
        "fail_details"      : "none"
    }
    

处理 FCM 消息

public class YourFirebaseMessagingService extends FirebaseMessagingService {
    private UjetPushHandler ujetPushHandler;

    @Override
    public void onCreate() {
        super.onCreate();
        this.ujetPushHandler = new UjetPushHandler(this);
    }

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        if (ujetPushHandler.handle(remoteMessage)) {
            // Handled by CCAI Platform

        } else {
            // Handle your push notification message in here
        }
    }
}

处理 GCM 消息

public class YourGcmListenerService extends GcmListenerService {
    private UjetPushHandler ujetPushHandler;

    @Override
    public void onCreate() {
        super.onCreate();
        this.ujetPushHandler = new UjetPushHandler(this);
    }

    @Override
    public void onMessageReceived(String s, Bundle bundle) {
        if (ujetPushHandler.handle(bundle)) {
            // Handled by CCAI Platform

        } else {
            // Handle your message
        }
    }
}

在 GcmReceiver 中处理 GCM 消息(旧方法)

public class YourGcmReceiver extends WakefulBroadcastReceiver {
    private UjetPushHandler ujetPushHandler;

    @Override
    public void onReceive(Context context, Intent intent) {
        ujetPushHandler = new UjetPushHandler(context);
        if (ujetPushHandler.handle(intent.getExtras())) {
            // Handled by CCAI Platform

        } else {
            // Handle your message
        }
    }
}

开始申请

在您要启动应用的位置添加以下行(不带任何参数):

Ujet.start(new UjetStartOptions.Builder().build());

您也可以在不显示启动画面的情况下启动 Android SDK。

UjetStartOptions ujetStartOptions = new UjetStartOptions.Builder()
        .setSkipSplashEnabled(true)
        .build();

Ujet.start(ujetStartOptions);

您还可以使用直接访问点,通过此键从菜单中的特定点启动 Android SDK:

String menuKey = "MENU_KEY";
UjetStartOptions ujetStartOptions = new UjetStartOptions.Builder()
        .setMenuKey(menuKey)
        .build();

Ujet.start(ujetStartOptions);

menuKey 可在 CCAI Platform 门户中通过直接访问点创建(需要管理员角色)。

  1. 前往设置 > 队列

  2. 从菜单结构中选择任意队列。

  3. 勾选创建直接访问点

  4. 以文本形式输入密钥。

  5. 点击保存

您还可以使用特定支持请求 ID 启动 Android SDK,以将其传递给 CRM。当聊天或通话接通时,系统会打开此工单 ID。

String ticketId = "TICKET_ID";
UjetStartOptions ujetStartOptions = new UjetStartOptions.Builder()
        .setTicketId(ticketId)
        .build();

Ujet.start(ujetStartOptions);

向 CRM 发送自定义数据

自定义数据可以发送给支持人员,并会显示在来电/聊天的支持服务工单中。

您可以通过以下两种方法发送自定义数据:

  • 签名方法:使用 JWT 预定义的数据签名。

  • 未签名方法:使用纯 JSON 的预定义数据(不推荐)。

使用签名方法发送自定义数据

如需使用签名方法发送自定义数据,请实现签名方法。

首先,将自定义数据检索到宿主应用,然后将其发送到服务器进行签名。在服务器上,您可以使用已定义的表单添加其他数据。使用公司密钥对它们进行签名,然后通过 JWT 返回它们。

public class ExampleApplication extends Application implements UjetRequestListener {
    @Override
    public void onCreate() {
        super.onCreate();

        Ujet.init(this);
    }

    @Override
    public void onSignPayloadRequest(Map<String, Object> payload, UjetPayloadType ujetPayloadType, UjetTokenCallback tokenCallback) {
        // ...
        if (ujetPayloadType == UjetPayloadType.CustomData) {
            /**
                * These codes are for providing signed custom data.
                * Add some data from app, and add more sensitive data from server and sign it.
                */
            UjetCustomData appCustomData = new UjetCustomData();
            appCustomData.put("model", "Model", "MODEL1234");
            appCustomData.put("customer_id", "Customer ID", 12345);
            appCustomData.put("temperature", "Temperature", 70.5f);
            appCustomData.put("purchase_date", "Purchase Date", new Date());
            appCustomData.put("battery", "Battery", "52%");
            appCustomData.put("location", "Location", "San Francisco, CA, United States");
            appCustomData.putURL("dashboard_url", "Dashboard URL", "https://internal.dashboard.com/12345");

            payload.put("custom_data", appCustomData.getData());

            tokenCallback.onToken(APIManager.getHttpManager().getSignedCustomData(payload));
        }
        // ...
    }
}

使用未签名的方法发送自定义数据

Google 建议您使用签名方法在应用中发送自定义数据。如需了解详情,请参阅使用签名方法发送自定义数据

您可以通过启动 Android SDK 并使用启动选项来设置自定义数据,从而发送未签名的数据,使用 UjetStartOptions.Builder#setUnsignedCustomDataUjetTokenCallback 应调用 onToken(null)

HashMap<String, Object> jsonData = new HashMap<>();
// Convert json string into hashmap object and store it in jsonData
UjetCustomData customData = new UjetCustomData();
customData.putObject("external_chat_transfer", jsonData); // Use `external_chat_transfer` key to send chat transcript data

UjetStartOptions ujetStartOptions = new UjetStartOptions.Builder()
        .setUnsignedCustomData(customData)
        .build();

Ujet.start(ujetStartOptions);

使用未签名的自定义数据发送外部聊天记录

您可以在外部聊天开始时使用未签名的自定义数据将外部聊天记录发送到 CCAI 平台。使用 UjetCustomData.putObject("external_chat_transfer", hashMapObject) 将转写数据设置为 JSON 格式,如下所示:

HashMap<String, Object> jsonData = new HashMap<>();
// Convert json string into hashmap object and store it in jsonData
UjetCustomData customData = new UjetCustomData();
customData.putObject("external_chat_transfer", jsonData); // Use `external_chat_transfer` key to send chat transcript data

UjetStartOptions ujetStartOptions = new UjetStartOptions.Builder()
        .setUnsignedCustomData(customData)
        .build();

Ujet.start(ujetStartOptions);

JSON 格式:

  • greeting_override:字符串

  • agent:字典

    • name:字符串

    • avatar:字符串 [代理头像的网址,可选]

  • transcript:数组

    • sender:字符串 [“end_user”或“agent”]

    • timestamp:字符串 [例如“2021-03-15 12:00:00Z”]

    • content:数组

      • type:字符串 [文本、媒体之一]

      • text:字符串 [文本类型必需]

      • media:字典 [媒体类型必需]

        • type:字符串 [图片、视频之一]

        • url:字符串 [指向媒体文件的公开网址]

JSON 示例:

{
    "greeting_override": "Please hold while we connect you with a human agent.",
    "agent": {
        "name": "Name",
        "avatar": "avatar url"
    },
    "transcript": [
        {
        "sender": "agent",
        "timestamp": "2021-03-15 12:00:15Z",
        "content": [
            {
            "type": "text",
            "text": "**Suggestions shown:**\n\n* Help with batch or delivery\n* Help with metrics or order feedback\n* Help with Instant Cashout"
            }
        ]
        },
        {
        "sender": "end_user",
        "timestamp": "2021-03-15 12:00:16Z",
        "content": [
            {
            "type": "text",
            "text": "Help with batch or delivery"
            }
        ]
        }
    ]
}

您可以在文本类型中使用 Markdown。支持以下格式:

  • 粗体

  • 斜体

  • 下划线

  • 换行符

  • 项目符号列表

  • 编号列表

  • 链接

自定义数据格式

本部分展示了可在 JWT 中传递的自定义数据的格式。

JSON 编码为 JWT

JSON 应包含 iat 和 exp 以验证 JWT。自定义数据的对象是 custom_data 键的值。

{
    "iat" : 1537399656,
    "exp" : 1537400256,
    "custom_data" : {
        "location" : {
            "label" : "Location",
            "value" : "1000 Stockton St, San Francisco, CA, United States",
            "type" : "string"
        },
        "dashboard_url" : {
            "label" : "Dashboard URL",
            "value" : "http://(company_name)/dashboard/device_user_ID",
            "type" : "url"
        },
        "contact_date" : {
            "label" : "Contact Date",
            "value" : 1537399655992,
            "type" : "date"
        },
        "membership_number" : {
            "label" : "Membership Number",
            "value" : 62303,
            "type" : "number"
        },
        "model" : {
            "label" : "Model",
            "value" : "iPhone",
            "type" : "string"
        },
        "os_version" : {
            "label" : "OS Version",
            "value" : "12.0",
            "type" : "string"
        },
        "last_transaction_id" : {
            "label" : "Last Transaction ID",
            "value" : "243324DE-01A1-4F71-BABC-3572B77AC487",
            "type" : "string"
        },
        "battery" : {
            "label" : "Battery",
            "value" : "-100%",
            "type" : "string"
        },
        "bluetooth" : {
            "label" : "Bluetooth",
            "value" : "Bluetooth not supported",
            "type" : "string"
        },
        "wifi" : {
            "label" : "Wi-Fi",
            "value" : "Wi-Fi not connected",
            "type" : "string"
        },
        "ssn" : {
            "invisible_to_agent" : true,
            "label" : "Social Security Number",
            "value" : "102-186-1837",
            "type" : "string"
        }
    }
}

键是数据的唯一标识符。类型是值的类型。

  • string

    • JSON 字符串
  • number

    • 整数、浮点数
  • date

    • 采用 13 位数的 UTC Unix 时间戳格式。(包含毫秒)
  • url

    • HTTP 网址格式

标签是 CRM 页面上的显示名称。

防止显示自定义数据

您可以将 invisible_to_agent 属性与自定义数据对象搭配使用,以防止已签名或未签名的自定义数据在代理适配器中显示。在上述示例中,由于 "invisible_to_agent" : true 包含在 ssn 对象中,因此代理适配器中不会显示最终用户的社会保障号。

如果您在自定义数据对象中添加 "invisible_to_agent" : true 属性,则会发生以下行为:

如需了解详情,请参阅在代理适配器中查看会话数据

预留的数据属性

您可以在会话开始时,以签名自定义数据的形式将预留数据属性发送到 Contact Center AI 平台 (CCAI Platform)。如需了解详情,请参阅发送预留数据属性

以下是自定义数据中预留的数据属性的示例:

  {
    "custom_data": {
      "reserved_verified_customer": {
        "label": "Verified Customer",
        "value": "VERIFIED_CUSTOMER_BOOLEAN": ,
        "type": "boolean"
      },
      "reserved_bad_actor": {
        "label": "Bad Actor",
        "value": "VERIFIED_BAD_ACTOR_BOOLEAN": ,
        "type": "boolean"
      },
      "reserved_repeat_customer": {
        "label": "Repeat Customer",
        "value": "REPEAT_CUSTOMER_BOOLEAN": ,
        "type": "boolean"
      }
    }
  }
  

替换以下内容:

  • VERIFIED_CUSTOMER_BOOLEAN:如果您认为相应最终用户是合法客户,则为 True。
  • VERIFIED_BAD_ACTOR_BOOLEAN:如果您认为此最终用户可能是恶意行为者,则为 True。
  • REPEAT_CUSTOMER_BOOLEAN:如果已确定相应最终用户之前曾联系过您的联络中心,则为 True。

SDK 配置

您可以在启动 Android SDK 之前配置多个选项。请参阅 UjetOption 类。下表中所述的“后备电话号码”和“网络敏感度”选项仅在 CCAI 平台门户网站中依次选择设置 > 开发者设置 > MMA > 修改,然后开启启用 PSTN 回退切换开关时才有效。当启用 PSTN 回退切换开关处于关闭状态时,我们不会回退到 PSTN。CCAI 平台使用默认网络敏感度 (0.85) 来检查网络连接。

选项 说明 默认值
日志级别 要在 Logcat 中输出的日志级别。 整数。与 Android 日志的日志级别相同。(最小值:2,最大值:7) 5 (Log.Warn)
默认语言 默认语言代码。 字符串。ISO 639 语言代码。(例如,en 表示英语) null
备用电话号码 当无法连接到互联网或公司在管理门户中没有代表手机号码时,系统会使用此手机号码作为后备方案。 字符串。电话号码。 null
未捕获的异常处理程序已启用 启用未捕获的异常处理程序。如果值为 true,应用会在运行时使用 Thread.setDefaultUncaughtExceptionHandler 处理所有未捕获的 SDK 异常。不过,如果同一异常发生两次,应用就会崩溃。 布尔值。 true
网络敏感性 用于检查网络状态的敏感度。 介于 01 之间,其中 0 是最低敏感度,1 是最高敏感度。值为 1 时,系统始终会回退到 PSTN 通话。如果使用,我们建议从 .97 的值开始。门户网站中设置 > 开发者设置 > 移动应用 > 后备手机号码阈值中的值会覆盖此值。 0.85
深色模式已启用 启用深色模式主题。如果值为 true,则当用户开启深色模式时,SDK 会应用深色模式主题,否则会忽略该操作。 布尔值。 false
已启用单渠道 配置选项,用于显示或绕过单个渠道的渠道选择界面。如果此属性为 true,则 SDK 会显示单个频道选择界面,而不是自动选择频道,即使菜单队列中仅启用了一个频道也是如此。 布尔值。 false
自动最小化通话视图 配置选项,用于自动将初始通话界面最小化(默认)或等待用户将其最小化。 布尔值。 false
已启用代理图标边框 用于显示或移除代理图标周围的圆形边框的配置选项。 布尔值。 false
选择器视图中的静态字体大小 用于自动调整或停用选择器项文本大小的配置选项。 布尔值。 false
隐藏聊天中的媒体附件 用于在聊天界面中显示或隐藏媒体附件图标的配置选项。 布尔值。 false
忽略 READ_PHONE_STATE 权限 如果设置为 true,则 SDK 不会请求 READ_PHONE_STATE 权限。如果您不想使用应用内 IVR 调用,请将此标志设置为 true,以避免请求此权限,并且您的应用需要明确移除 CCAI 平台 SDK 的 android.permission.READ_PHONE_STATE 权限。请注意,我们不建议将此权限设置为 true,因为应用内 IVR 调用需要此权限才能正常运行。 布尔值 false
Cobrowse.io 许可密钥(如适用) 用于设置 Cobrowse.io 库的配置选项。您可以登录 https://cobrowse.io/dashboard/settings,然后前往许可密钥部分,找到您的 Cobrowse 许可密钥。 字符串 null
自定义聊天标题 用于自定义聊天界面中聊天标题文字的配置选项。 字符串 null
自定义聊天界面快速回复 配置选项,用于在聊天界面中自定义虚拟客服快速回复。虚拟客服快速回复默认分组显示,但如果您想单独显示它们,可以使用此配置选项将 QuickReplyButtonsStyle 设置为 INDIVIDUAL UjetStylesOptions QuickReplyButtonsStyle.GROUPED
自定义聊天界面各种属性 用于自定义各种属性(例如字体、背景颜色、图标等)的配置选项。 UjetStylesOptions null
隐藏状态栏 用于显示或隐藏状态栏的配置选项。如果值为 true,则 SDK 会隐藏状态栏。 布尔值 false
设置加载旋转图标可绘制资源 用于在整个应用中自定义加载微调框视图的配置选项。如果该选项不可用或为 null,则使用默认加载视图。 整数 null
横向模式已停用 停用横屏模式。如果值为 true,则不应用横向屏幕方向。 布尔值 false
隐藏聊天中的“发起新对话”内嵌按钮 用于在聊天界面中显示或隐藏“发起新对话”内嵌按钮的配置选项。 布尔值 false
显示 CSAT 跳过按钮 用于在 CSAT 对话框中显示或隐藏“跳过”按钮的配置选项 布尔值 false
禁止最终用户终止聊天 配置选项,用于在聊天界面中显示或隐藏结束聊天按钮 布尔值 false
隐藏“下载聊天转写内容” 在聊天操作菜单和聊天界面中显示或隐藏“下载聊天记录”按钮。如果您向 ujet_common_hide 字符串添加空格,Hide 文本会消失,并且仅显示返回箭头。 整数。值:

0 = 在所有位置显示

1 = 从选项菜单中隐藏

2 = 在聊天后界面中隐藏

3 = 从选项菜单和帖子聊天界面中隐藏。

0

UjetOption.setBlockChatTerminationByEndUser

在全局层面关闭推送通知 UjetOption.setPushNotificationsAllowed 设置为 false 会绕过所有推送通知依赖项,并阻止推送通知到达最终用户。 布尔值 true
UjetOption ujetOption = new UjetOption.Builder()
        .setLogLevel(Log.INFO)
        .setDefaultLanguage("en")
        .setFallbackPhoneNumber("+18001112222")
        .setUncaughtExceptionHandlerEnabled(false)
        .setNetworkSensitivity(0)
        .setDarkModeEnabled(true)
        .setShowSingleChannelEnabled(true)
        .setAutoMinimizeCallView(true)
        .setShowAgentIconBorderEnabled(true)
        .setStaticFontSizeInPickerView(true)
        .setHideMediaAttachmentInChat(true)
        .setIgnoreReadPhoneStatePermission(true)
        .setCobrowseLicenseKey("COBROWSE_IO_LICENSE_KEY_HERE")
        .setCobrowseURL("COBROWSE_IO_API_URL_HERE")
        .setCustomChatHeaderTitle("CHAT_HEADER_TITLE_TEXT")
        .setUjetStylesOptions(
             new UjetStylesOptions.Builder()
            .setChatQuickReplyButtonsStyle(QuickReplyButtonsStyle.INDIVIDUAL)
            .setChatStyles(new ChatStyles(...)) // See `Content Cards Theme` item
            .build()            )
        .setBlockChatTerminationByEndUser(true)
        .setHideStatusBar(true)
        .setLoadingSpinnerDrawableRes(R.drawable.RESOURCE_NAME)
        .setLandscapeOrientationDisabled(true)
        .setShowCsatSkipButton(false)
        .setHideDownloadChatTranscript(0) // 0 to 3. 0 = Show everywhere, 1 = Hide from the options menu, 2 = Hide from the post chat screen, 3 = Hide from both the options menu and the post chat screen.
        .setPushNotificationsAllowed(true)
        .build();

//The following customizes various attributes in chat UI
ChatStyles chatStyles = new ChatStyles();
chatStyles.setBackButton(new BackButtonStyle(false, "ujet_agent_sample")); //customizes back button styles
chatStyles.setHeader(...); //customizes chat header styles
chatStyles.setAgentMessageBubbles(...); //customizes agent messages styles
chatStyles.setConsumerMessageBubbles(...); //customizes consumer messages styles
chatStyles.setSystemMessages(...); //customizes system messages styles
chatStyles.setEndChatButton(...); //customizes end chat button styles
chatStyles.setTimeStamps(...); //customizes timestamp styles
chatStyles.setUserInputBar(...); //customizes user input bar styles

UjetOption ujetOption = new UjetOption.Builder()
        .setUjetStylesOptions(
            new UjetStylesOptions.Builder()
            .setChatStyles(chatStyles)
            .build()
        )

//The following customizes various attributes in chat UI using json file. Store json file in assets folder
//and create a method to read json file contents and convert it into json string.
String chatStylesFromJson = parseJsonContentsFromAssetsFolder();

UjetOption ujetOption = new UjetOption.Builder()
        .setUjetStylesOptions(
            new UjetStylesOptions.Builder()
            .setChatStyles(chatStylesFromJson)
            .build()
        )

后备

您可以使用 UjetErrorListener 作为意外错误的后备。如果您未设置此监听器或返回 false,Android SDK 将处理该错误。

仅当在设置 > 开发者设置 > MMA > 编辑弹出式窗口中将“启用 PSTN 回退”切换开关设为开启状态时,Android SDK 才会将用户重定向到拨号器并提供回退号码;否则,SDK 会退出。

错误类型 错误代码 触发器
NETWORK_ERROR 1 网络不可用。请注意,如果聊天、通话或评分界面期间网络不可用,系统不会触发此错误。
AUTHENTICATION_ERROR 100 身份验证期间发生了意外错误。
AUTHENTICATION_JWT_ERROR 101 在 JWT 验证期间发生了意外错误(例如解析错误)。
VOIP_CONNECTION_ERROR 1000 无法建立与 VoIP 提供商的连接。通过 VoIP SDK 的回调处理。
VOIP_LIBRARY_NOT_FOUND 1001 系统本应使用 VoIP 服务提供商接通电话,但找不到任何 VoIP 服务提供商。如果开发者集成了错误的 SDK 或未在其依赖项中添加 VoIP 提供程序库,就可能会发生这种情况。
CHAT_LIBRARY_NOT_FOUND 1100 在找不到聊天库时发生。如果开发者集成了错误的 SDK 或未在其依赖项中添加 Twilio Chat 库,就可能会发生这种情况。
Ujet.setUjetEventListener(new UjetEventListener() {
    @Override
    public void onEvent(UjetEventType eventType, HashMap<String, Object> eventData) {
        // eventType specifies the event type and eventData holds the data related to the event.
        // You can parse the eventData and here we are just logging the event type and event data.
        Log.i("CCAI Platform Event Type", eventType.getValue());

        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, Object> entry : eventData.entrySet()) {
            builder.append(entry.getKey()).append(" : ").append(entry.getValue()).append("\n");
        }

        Log.i("CCAI Platform Event Data", builder.toString());
    }
});

应用权限

应用需要以下权限,并在需要时向用户请求这些权限。

权限 说明
CAMERA 用于智能操作,以便拍摄照片和录制视频
麦克风 允许应用通过 Twilio 使用 VoIP 通话
存储 允许应用保存照片和视频

深层链接设置(可选

如果您想在 IVR (PSTN) 通话中使用智能操作,需要在项目中设置深层链接。

深层链接格式是一种唯一的 URI,例如:

ujet:// <package_name>/smartchannel.

此外,您还必须在管理门户中设置此链接或将重定向到此链接的任何网址(设置 > 运营管理 > 启用“发送短信以下载应用”功能)

您需要在清单中添加包含深层链接的 intent 过滤器。

<activity android:name="co.ujet.android.activity.UjetActivity">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:host="<package_name>"
                android:scheme="ujet"
                android:path="/smartchannel" />
    </intent-filter>
</activity>

首选渠道

借助“首选渠道”参数,您可以将消费者直接路由到特定渠道。然后,最终用户会跳过渠道选择步骤,直接通过“首选渠道”参数中指定的渠道发起联系。

UjetStartOptions.preferredChannel

Ujet.start(new)
UjetStartOptions.Builder().setPreferredChannel
(UjetPreferredChannel.UjetPreferredChannelChat).build());

活动通知

您可以选择设置 UjetEventListener 以接收应用事件通知。

此处列出了可用的事件类型和说明。

Ujet.setUjetEventListener(new UjetEventListener() {
    @Override
    public void onEvent(UjetEventType eventType, HashMap<String, Object> eventData) {
        // eventType specifies the event type and eventData holds the data related to the event.
        // You can parse the eventData and here we are just logging the event type and event data.
        Log.i("CCAI Platform Event Type", eventType.getValue());

        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, Object> entry : eventData.entrySet()) {
            builder.append(entry.getKey()).append(" : ").append(entry.getValue()).append("\n");
        }

        Log.i("CCAI Platform Event Data", builder.toString());
    }
});
事件类型 说明 活动中包含的数据
EmailClicked 当最终用户点击电子邮件渠道时触发。 队列菜单数据
EmailSubmitted 当最终用户发送电子邮件时触发。 排队菜单数据、通过电子邮件提交的数据
SessionPaused 当最终用户最小化聊天或通话会话时触发。 会话数据
SessionResumed 当最终用户从后台切换回聊天或通话会话时触发。 会话数据
SessionCreated 在创建聊天或通话会话时触发。 队列菜单数据、会话创建数据
SessionEnded 当聊天或通话会话结束时触发。 队列菜单数据、会话创建数据、会话结束数据
SdkTerminated 在 SDK 关闭时触发,包括在 SDK 意外关闭时触发。 SDK 终止数据
ContentCardClicked 在点击内容卡片时触发。 内容卡片点击数据
ContentCardButtonClicked 当用户点击内容卡片按钮时触发。 内容卡片按钮点击数据
QuickReplyClicked 当用户点击快速回复时触发。 快速回复点击数据
MessageLinkClicked 在点击链接时触发。 “已点击邮件链接”数据

队列菜单数据

类型 说明
event_name 字符串 包含活动名称。例如,“电子邮件点击”。
application 字符串 包含应用名称。例如,“Android”。
app_id 字符串 包含与 Context.getPackageName() 相同的应用标识符。
app_version 字符串 包含应用版本名称和版本代码。例如,“0.32.0 (123)”。
sdk_version 字符串 包含 SDK 版本。例如,“0.32.0”。
timestamp 字符串 包含采用 yyyy-MM-dd'T'HH:mm:ss'Z' 格式的世界协调时间 (UTC) 时间戳。
device_model 字符串 包含用户设备型号。例如“Google Pixel”。
device_version 字符串 包含用户设备版本。例如,“10、Q、SDK 29”。
company 字符串 包含公司名称。例如,“公司”。
menu_name 字符串 包含叶节点的名称(用户上次选择的菜单)。例如,“子菜单”。
menu_id 字符串 包含叶节点的 ID(用户上次选择的菜单)。例如,“123”。
menu_path 字符串 包含用户选择的完整菜单序列。例如,“父菜单 / 子菜单 / 子菜单”。
menu_key 字符串 包含 DAP 密钥,是可选的。例如,“special_user_menu”。

通过电子邮件提交的数据

类型 说明
has_attachments 布尔值 如果电子邮件有任何附件,则返回 True,否则返回 False。

会话数据

类型 说明
event_name 字符串 包含活动名称。例如,“电子邮件点击”。
type 字符串 包含会话类型。例如,“chat”或“call”。
timestamp 字符串 包含采用 yyyy-MM-dd'T'HH:mm:ss'Z' 格式的世界协调时间 (UTC) 时间戳。

创建会话数据

类型 说明
session_id 字符串 包含会话 ID。例如,“100”。
type 字符串 包含会话类型。例如,“chat”或“call”。
end_user_identifier 字符串 包含最终用户标识符。例如,“John”。
messages_end_user 字符串 包含最终用户消息数量,仅适用于聊天会话。例如,“3”。
messages_agent 字符串 包含客服人员消息数量,仅适用于聊天会话。例如,“3”。

会话结束数据

类型 说明
agent_name 字符串 包含客服人员姓名。例如,“John”。
ended_by 字符串 包含会话结束者的详细信息。可能的值包括“agent”(当客服结束会话时)、“end_user”(当最终用户结束会话时)、“timeout”(当聊天超时时)或“dismissed”(当聊天被关闭时)。
duration 字符串 包含通话时长(以秒为单位),仅适用于通话会话。例如,“30 秒”。

SDK 终止数据

类型 说明
event_name 字符串 包含活动名称。例如,“电子邮件点击”。

内容卡片点击数据

类型 说明
title 字符串 卡片的标题。
title 字符串 卡片的标题。
subtitle 字符串 卡片副标题。
body 字符串 内容卡的说明。
link 字符串 网页链接或深层链接。SDK 会使用操作系统功能来打开该文件。
event_params 字典 一个字典,包含有关点击事件的额外信息。SDK 会使用此信息。

内容卡片按钮点击数据

类型 说明
title 字符串 卡片的标题。
title 字符串 卡片的标题。
link 字符串 网页链接或深层链接。SDK 会使用操作系统功能来打开该文件。
event_params 字典 一个字典,包含有关点击事件的额外信息。SDK 会使用此信息。

快速回复点击数据

类型 说明
title 字符串 卡片的标题。
title 字符串 卡片的标题。
link 字符串 网页链接或深层链接。SDK 会使用操作系统功能来打开该文件。
event_params 字典 一个字典,包含有关点击事件的额外信息。SDK 会使用此信息。

“已点击邮件链接”数据

类型 说明
event_name 字符串 包含事件名称,例如“点击了消息链接”。
link 字符串 网页链接或深层链接。SDK 会使用操作系统功能来打开该文件。

来电行为变更

Android 10 版设备开始,当宿主应用在后台运行时,最终用户将不会直接收到来电。我们会使用通知来提醒用户有来电(即使手机已锁定),并让用户选择接听或拒接来电。

当宿主应用在后台运行且在来电到达之前锁定了屏幕时,我们会显示相同的通知。此行为变更旨在遵守 Google 最近针对应用在后台时启动 activity 的限制。当宿主应用在前台运行或在低于 Android 10 版本的设备上运行时,行为不受影响。

自定义 SDK 会话

本部分简要介绍了如何进一步自定义 SDK。

检查是否存在现有会话

在开始会话之前,请使用上述方法检查是否存在任何现有会话或正在进行的会话。如果存在,您可以提示最终用户恢复或取消该任务。

当用户发生变化时,这一点尤其重要。

if (Ujet.getStatus() != UjetStatus.None) {
    // Display alert to cancel login or resume existing session
}

断开会话

如果您想断开任何正在进行的会话,请参阅此方法。

在使用此方法之前,请务必使用 Ujet.getStatus() 检查是否存在此类会话。如果您想在 SDK 断开会话后执行操作,例如显示消息或关闭应用,则可以使用下一部分中介绍的响应回调 onFinished();否则,请将回调设置为 null。

Ujet.disconnect(new UjetResponseCallback() {
    @Override
    public void onFinished() {
        // `onFinished()` is triggered after CCAI Platform disconnects the session.
        finish(); // Finishes the activity.
    }
});

从缓存中清除最终用户数据

当最终用户相关数据在您的应用中更新或更改时,您有责任清除缓存。例如,如果最终用户已退出登录,请调用相应方法来移除该用户的缓存,以便在下次启动 SDK 时为新最终用户启动新会话。

Ujet.clearUserData();

隐藏 SDK

您可以使用 Ujet.hideSDK() 方法隐藏 SDK。例如,当 SDK 干扰应用用户界面时,此功能非常有用。在调用此方法之前,请使用 Ujet.init() 初始化 SDK。如果成功隐藏 SDK,Ujet.init() 会返回 true;否则,返回 false。隐藏 SDK 后,您可以使用 Ujet.start() 方法再次启动它。

以下示例会隐藏 SDK:

Ujet.hideSDK();

语言偏好设置

Android SDK 将使用以下优先级顺序来确定语言。

  1. 从应用内的启动画面中选择的语言。

  2. 使用 UjetOptions 选择的默认语言。您可以在 UjetOptions 中使用 setDefaultLanguage("en") 设置默认语言。如需了解详情,请参阅“SDK 配置”部分中的“默认语言”。

  3. 如果应用支持,系统将使用在设备中选择的设备语言(通过设置 > 常规 > 语言进行选择)。

  4. 如果应用不支持设备语言,但支持最接近的父方言,则会使用最接近的设备语言方言。例如,如果用户在设备中选择的语言是“西班牙语(古巴)”,但应用不支持“西班牙语(古巴)”,却支持父方言“西班牙语”,那么系统将使用“西班牙语”。

  5. 如果应用不支持设备语言,系统将使用英语作为默认语言。

配置外部分流链接图标

通过将图标上传到应用的 drawable 文件夹中,自定义“外部分流链接”渠道中的图标,并确保在 CCAI 平台门户中创建外部分流链接时使用相同的图标名称,具体路径为设置 > Chat > 外部分流链接 > 查看链接 > 添加分流链接

如果 CCAI 平台门户中的图标名称与上传到应用中的图标不一致,Android SDK 将使用默认图标。

配置调查问卷的感谢图标

您可以自定义或替换调查问卷感谢页面中的图标,方法是将图标上传到应用的 drawable 文件夹中,并使用文件名 ujet_survey_thank_you_icon 作为图标名称。

自定义(可选)

本部分概述了如何自定义 SDK 中的特定值。

字符串

您可以通过替换 strings.xml 中每个字符串的键来自定义应用中使用的字符串。

<resources>
    <!--Greeting title and message in splash screen-->
    <string name="ujet_greeting_title">Customer Support</string>
    <string name="ujet_greeting_description">runs on UJET</string>
</resources>

调查配置

文字大小自定义

通过替换 dimens.xml 中的以下键,自定义应用中使用的标题、说明和选择器文字大小。

可自定义的文字大小如下所示:

<resources>
    <!-- Don't include the following tags if you don't want to customize any of these keys and prefer to use the CCAI Platform default values instead. -->

    <!-- You can customize title text size by updating value here. -->
    <dimen name="ujet_title">10sp</dimen>

    <!-- You can customize description text size by updating value here. -->
    <dimen name="ujet_description">10sp</dimen>

    <!-- You can customize picker text size by updating value here. -->
    <dimen name="ujet_picker_item_text_size">10sp</dimen>
</resources>

主题

按照以下步骤自定义主题和背景。第 1 步用于主题,第 2 步用于背景。

  1. 通过替换 style.xml 中每个样式项的键来自定义主题。例如,

    <!--Default style applies to both Light and Dark Mode Themes-->
    <style name="Ujet">
        <item name="ujet_typeFace">ProximaNova-Reg.otf</item>
        <item name="ujet_colorPrimary">@color/primaryDefault</item>
        <item name="ujet_colorPrimaryDark">@color/primaryDarkDefault</item>
        <item name="ujet_buttonRadius">10dp</item>
        <item name="ujet_companyLogo">@drawable/your_company_logo_default</item>
    
        <!-- You can customize the avatar in waiting UI before call or chat is connected by using the following option. -->
        <item name="ujet_defaultAvatar">@drawable/your_default_avatar</item>
    </style>
    
    <!--This is optional and can be used to update style in Light Mode Theme only-->
    <style name="Ujet.Light">
        <item name="ujet_typeFace">ProximaNova-Reg.otf</item>
        <item name="ujet_colorPrimary">@color/primaryLightMode</item>
        <item name="ujet_colorPrimaryDark">@color/primaryDarkLightMode</item>
        <item name="ujet_buttonRadius">10dp</item>
        <item name="ujet_companyLogo">@drawable/your_company_logo_light_mode</item>
    
        <!-- You can customize the avatar in waiting UI before call or chat is connected by using the following option. -->
        <item name="ujet_defaultAvatar">@drawable/your_default_avatar</item>
    </style>
    
    <!--This is optional and can be used to update style in Dark Mode Theme only-->
    <style name="Ujet.Dark">
        <item name="ujet_typeFace">ProximaNova-Reg.otf</item>
        <item name="ujet_colorPrimary">@color/primaryDarkMode</item>
        <item name="ujet_colorPrimaryDark">@color/primaryDarkForDarkMode</item>
        <item name="ujet_buttonRadius">10dp</item>
        <item name="ujet_companyLogo">@drawable/your_company_logo</item>
    
        <!-- You can customize the avatar in waiting UI before call or chat is connected by using the following option. -->
        <item name="ujet_defaultAvatar">@drawable/your_default_avatar</item>
    </style>
    
  2. 您可以通过替换 style.xml 中每个样式项的键来在应用中自定义背景颜色。屏幕截图中显示了可自定义的背景颜色。

    <style name="Ujet">
        <!-- Don't include the following tags if you don't want to customize any of these keys and prefer to use the CCAI Platform default values instead. -->
        <!-- You can customize light mode theme background color by updating value here in hex. -->
        <item name="ujet_colorBackground">@color/backgroundDefault</item>
        <!-- You can customize dark mode theme background color by updating value here in hex. -->
        <item name="ujet_colorBackgroundDark">@color/backgroundDefaultDark</item>
    </style>
    

自定义聊天标题

您可以选择自定义界面中聊天窗口的聊天标题文字。

您可以使用以下选项自定义聊天标题文字:

<item name="ujet_chatCustomHeaderTextColor">@color/chatHeaderTextLightMode</item>
<item name="ujet_chatCustomHeaderTextColowDark">@color/chatHeaderTextDarkMode</item>
<item name="ujet_chatCustomHeaderTextSize">16sp</item>
<item name="ujet_chatCustomHeaderTextStyle">bold</item>

您可以使用以下选项在聊天界面中自定义虚拟客服快速回复:

<item name="ujet_colorChatQuickReplyButtonBackground">@color/chatQuickReplyButtonBackgroundLightMode</item>
<item name="ujet_colorChatQuickReplyButtonBackgroundDark">@color/chatQuickReplyButtonBackgroundDarkMode</item>
<item name="ujet_colorChatQuickReplyButtonPressedBackground">@color/chatQuickReplyButtonPressedBackgroundLightMode</item>
<item name="ujet_colorChatQuickReplyButtonPressedBackgroundDark">@color/chatQuickReplyButtonPressedBackgroundDarkMode</item>
<item name="ujet_colorChatQuickReplyButtonText">@color/chatQuickReplyButtonTextLightMode</item>
<item name="ujet_colorChatQuickReplyButtonTextDark">@color/chatQuickReplyButtonTextDarkMode</item>
<item name="ujet_colorChatQuickReplyButtonPressedText">@color/chatQuickReplyButtonPressedTextLightMode</item>
<item name="ujet_colorChatQuickReplyButtonPressedTextDark">@color/chatQuickReplyButtonPressedTextDarkMode</item>
<item name="ujet_colorChatQuickReplyButtonStroke">@color/chatQuickReplyButtonStrokeLightMode</item>
<item name="ujet_colorChatQuickReplyButtonStrokeDark">@color/chatQuickReplyButtonStrokeDarkMode</item>
<item name="ujet_chatQuickReplyButtonTypeFace">Kreon-Regular.ttf</item>
<item name="ujet_chatQuickReplyButtonStrokeWidth">3dp</item>
<item name="ujet_chatQuickReplyButtonCornerRadius">3dp</item>
<item name="ujet_chatQuickReplyButtonVerticalMargin">0dp</item>
<item name="ujet_chatQuickReplyButtonHorizontalPadding">10dp</item>
<item name="ujet_chatQuickReplyButtonVerticalPadding">1dp</item>
<item name="ujet_chatQuickReplyButtonAlignment">right</item>

内容卡片

您可以添加内容卡片自定义设置以及聊天自定义设置。您可以使用 JSON 文件(请参阅 app/src/main/assets/json/ujet_styles.json 文件中的 content_card property)或使用 ContentCardStyle 类来执行此操作。

ChatStyles(
    ...
    contentCard = ContentCardStyle(
        backgroundColor = "color_reference",
        cornerRadius = 8,
        font = FontStyle(
            colorReference = "color_reference",
            size = 16,
            style = "bold|italic",
            family = "Roboto-Black.ttf",
        ),
        border = BorderStyle(
            color = "color_reference",
            width = 2,
        ),
        title = TextStyle(
            FontStyle(
                colorReference = "color_reference",
                size = 18,
                style = "bold|italic",
                family = "Roboto-Black.ttf",
            )
        ),
        subtitle = TextStyle(
            FontStyle(
                colorReference = "color_reference",
                size = 16,
                style = "bold|italic",
                family = "Roboto-Black.ttf",
            )
        ),
        body = TextStyle(
            FontStyle(
                colorReference = "color_reference",
                size = 16,
                style = "bold|italic",
                family = "Roboto-Black.ttf",
            )
        )
    )
)

网页表单主题

您可以自定义网络表单卡片以及聊天自定义设置。您可以使用 JSON 文件(请参阅 app/src/main/assets/json/ujet_styles.json 文件中的 form_card 属性)或使用 FormCardStyle 类来执行此操作。

ChatStyles(
    ...
    formCard = FormCardStyle(
        backgroundColor = "color_reference",
        cornerRadius = 8,
        font = FontStyle(
            colorReference = "color_reference",
            size = 16,
            style = "bold|italic",
            family = "Roboto-Black.ttf",
        ),
        border = BorderStyle(
            color = "color_reference",
            width = 2,
        ),
        title = TextStyle(
            FontStyle(
                colorReference = "color_reference",
                size = 18,
                style = "bold|italic",
                family = "Roboto-Black.ttf",
            )
        ),
        subtitle = TextStyle(
            FontStyle(
                colorReference = "color_reference",
                size = 16,
                style = "bold|italic",
                family = "Roboto-Black.ttf",
            )
        ),
        image = ImageStyle (
           height = 94,
        ),
    )
)

问卷调查

您可以将图标上传到应用的 drawable 文件夹,以更改调查问卷感谢页面上的图标。

请确保您使用 ujet_survey_thank_you_icon 作为图标名称。

问题排查

发起新对话的时间超过 30 秒

检查您是否正在响应自定义数据的委托方法。您应在收到请求时返回有效的自定义数据,或者仅通过回调返回 null。请使用以下代码作为参考。

    @Override
    public void onSignPayloadRequest(Map<String, Object> payload, UjetPayloadType ujetPayloadType, UjetTokenCallback tokenCallback) {
        if (ujetPayloadType == UjetPayloadType.CustomData) {
            tokenCallback.onToken(null);
        }
    }

政策声明说明

如果您在 Google Play 管理中心内收到通知,要求您声明 Android SDK 所用服务或权限的政策,请使用以下说明之一。

声明 Android SDK 使用的前台服务

Google 在 Android 14 中引入了前台服务类型,并强制规定在启动前台服务时必须指定类型,详情请参阅 https://developer.android.com/about/versions/14/changes/fgs-types-required#remote-messaging。 Contact Center AI 平台 (CCAI 平台) Android SDK 使用前台服务来发起聊天和通话,因此我们为聊天使用了 FOREGROUND_SERVICE_REMOTE_MESSAGING 服务类型,因为我们处理的是文本消息,而为通话使用了 FOREGROUND_SERVICE_MICROPHONE 服务类型。如果没有这些服务类型,SDK 将在从 Android 14 版设备开始发起聊天或通话时崩溃。

声明 Android SDK 使用的全屏 intent 权限

需要 USE_FULL_SCREEN_INTENT 权限才能在设备锁定时显示来电推送通知。它会提醒最终用户,并全屏显示来电通知(这是内置手机应用向最终用户通知来电的方式)。

支持 Android 15

如需支持 Android 15,请按以下步骤操作:

  1. build.gradle(:app) 中设置 compileSdkVersion = 35targetSdkVersion = 35

  2. 确保您的项目 Android Gradle 插件 (AGP) 版本为 8.5.1 或更高版本。如需了解详情,请参阅更新共享库的封装

  3. 安装 Java 开发工具包 (JDK) 17 版或更高版本,以便使用 AGP 8.0 或更高版本构建 Android 项目。如需了解详情,请参阅需要 JDK 17 才能运行 AGP 8.0