Android SDK 指南

本頁說明如何使用 Android SDK。

存取範例應用程式

如要下載 Android 範例應用程式,請按一下「Android example app」(Android 範例應用程式)。

如要使用 Android 行動 SDK 建立範例應用程式,您需要:

  • CCAI 平台入口網站公司金鑰和公司密鑰。

  • Android 5.0 (API 級別 21,Lollipop) 以上版本。

  • Firebase 雲端通訊或 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 規則,可確保 Twilio Programmable Voice 程式庫不會遭到 ProGuard 移除,並可在 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
    }
}

取得公司金鑰和公司密鑰

  1. 使用管理員憑證登入管理員入口網站。

  2. 依序前往「設定」>「開發人員設定」>「公司金鑰和密鑰」。

  3. 擷取公司金鑰和公司密鑰。

安裝

將 Android SDK 存放區新增至根專案的 Gradle 設定。

build.gradle (Project)

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

build.gradle (module: 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 控制台依序前往「設定」>「雲端通訊」,取得伺服器金鑰。

新增服務帳戶金鑰

您必須在行動應用程式中新增服務帳戶金鑰,才能接收推播通知。如要取得服務帳戶金鑰,請參閱「使用服務帳戶進行驗證」。

如要將服務帳戶金鑰新增至行動應用程式,請按照下列步驟操作:

  1. 在 CCAI Platform 入口網站中,依序點選「Settings」(設定) >「Developer Settings」(開發人員設定)。如果找不到「設定」選單,請按一下「選單」圖示 。

  2. 前往「Mobile Apps」(行動應用程式) 窗格。

  3. 按一下應用程式旁的 「Edit」(編輯)。系統會顯示「Edit Mobile App」(編輯行動應用程式) 對話方塊。

  4. 在「Service Account Field」(服務帳戶欄位) 中輸入服務帳戶金鑰,然後按一下「Save」(儲存)。

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,如果權杖重新整理,則會在資訊清單中註冊。詳情請參閱「在 Android 上設定 Firebase 雲端通訊用戶端應用程式」。

    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. 在應用程式類別中實作 UjetRequestListener.onRequestPushToken。UjetRequestListener.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 Platform) 自行處理推播訊息,可以直接將資料傳遞至 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 平台入口網站中,透過直接存取點建立 (須具備管理員角色)。

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

SDK 設定

啟動 Android SDK 前,您可以設定多個選項。請參閱 UjetOption 類別。只有在 CCAI 平台入口網站中,依序前往「Settings」>「Developer Settings」>「MMA」>「Edit」,並開啟「Enable PSTN Fallback」切換鈕,下表所述的「Fallback Phone Number」和「Network Sensitivity」選項才會生效。如果關閉「Enable PSTN Fallback」(啟用 PSTN 備用) 切換鈕,系統就不會改用 PSTN。CCAI 平台會使用預設網路敏感度 (0.85) 檢查網路連線。

選項 說明 值 預設值
記錄檔層級 要在 Logcat 中輸出的記錄層級。 整數。Android 記錄的記錄層級也是如此。最高速 5 (Log.Warn)
預設語言 預設語言代碼。 字串。ISO 639 語言代碼。(例如 en 代表英文) null
備用電話號碼 如果無法連上網際網路,或管理員入口網站中沒有公司的代表電話號碼,系統會使用這個電話號碼做為備用號碼。 字串。電話號碼。 null
已啟用未偵測到的例外狀況處理常式 啟用未偵測到的例外狀況處理常式。如果 true,應用程式會在執行階段使用 Thread.setDefaultUncaughtExceptionHandler 處理所有未擷取的 SDK 例外狀況。不過,如果發生兩次相同的例外狀況,應用程式就會異常終止。 布林值。 true
網路敏感度 檢查網路狀態的靈敏度。 介於 0 和 1 之間的雙精度浮點數,其中 0 最不敏感,1 最敏感。如果值為 1,系統一律會改用 PSTN 通話。如要使用,建議先從 .97 開始。在入口網站中,依序前往「設定」>「開發人員設定」>「行動應用程式」>「備用」,即可查看覆寫這個值的電話號碼門檻。 0.85
已啟用深色模式 啟用深色模式主題。如果為 true,SDK 會在使用者開啟深色模式時套用深色模式主題,否則會忽略該動作。 布林值。 false
已啟用單一管道 設定選項,可顯示或略過單一頻道的頻道選取畫面。true:SDK 會顯示單一頻道選取畫面,而不是自動選取頻道,即使選單佇列中只有一個頻道啟用也一樣。 布林值。 false
自動縮小通話畫面 設定選項,預設自動最小化初始通話畫面 UI,或等待使用者最小化。 布林值。 false
啟用代理圖示邊框 設定選項,用於顯示或移除代理圖示周圍的圓形框線。 布林值。 false
挑選器檢視畫面中的靜態字型大小 設定選項,可自動調整或停用挑選器項目文字大小。 布林值。 false
隱藏 Chat 中的媒體附件 設定選項,用於在即時通訊使用者介面中顯示或隱藏媒體附件圖示。 布林值。 false
忽略「READ_PHONE_STATE」權限 如果設為 true,SDK 就不會要求 READ_PHONE_STATE 權限。如果不想使用應用程式內 IVR 通話,請將這個旗標設為 true,避免要求這項權限,而且應用程式必須明確移除 CCAI Platform SDK 的 android.permission.READ_PHONE_STATE 權限。請注意,我們不建議將這項權限設為 true,因為應用程式內 IVR 通話必須取得這項權限才能運作。 布林值 false
Cobrowse.io 授權金鑰 (如適用) 設定 Cobrowse.io 程式庫的設定選項。如要查看 Cobrowse 授權金鑰,請登入 https://cobrowse.io/dashboard/settings,然後前往「License Key」部分。 字串 null
自訂即時通訊標頭標題 設定選項,可在即時通訊 UI 中自訂即時通訊標題文字。 字串 null
自訂即時通訊 UI 快速回覆 設定選項,在對話 UI 中自訂虛擬服務專員的快速回覆。虛擬服務專員快速回覆預設會分組顯示,但如果您想個別顯示,可以使用這個設定選項,將 QuickReplyButtonsStyle 設為 INDIVIDUAL UjetStylesOptions QuickReplyButtonsStyle.GROUPED
自訂即時通訊 UI 的各種屬性 設定選項,可自訂字型、背景顏色、圖示等各種屬性。 UjetStylesOptions null
隱藏狀態列 用於顯示或隱藏狀態列的設定選項。如果值為 true,SDK 會隱藏狀態列。 布林值 false
設定載入中旋轉圖示可繪製資源 設定選項,用於自訂整個應用程式的載入旋轉圖示檢視畫面。如果無法使用或為空值,則會使用預設的載入檢視畫面。 整數 null
已停用橫向顯示比例 停用橫向模式。如果值為 true,則不會套用橫向顯示比例。 布林值 false
隱藏聊天室中的「發起新對話」內嵌按鈕 設定選項,用於在即時通訊 UI 中顯示或隱藏「發起新對話」內嵌按鈕。 布林值 false
顯示 CSAT 略過按鈕 設定選項,用於顯示或隱藏 CSAT 對話方塊中的略過按鈕 布林值 false
禁止使用者終止即時通訊 設定選項,用於在即時通訊使用者介面中顯示或隱藏結束即時通訊按鈕 布林值 false
隱藏下載即時通訊轉錄稿 在即時通訊動作選單和即時通訊使用者介面中,顯示或隱藏「下載即時通訊轉錄稿」按鈕。如果您在 ujet_common_hide 字串中加入空格,Hide 文字就會消失,只會顯示返回箭頭。 整數。值:

0 = 顯示在所有位置

1 = 從選項選單中隱藏

2 = 在即時通訊後畫面中隱藏

3 = 從選項選單和即時通訊後畫面中隱藏。

0

UjetOption.setBlockChatTerminationByEndUser

關閉全域層級的推播通知 如果將 UjetOption.setPushNotificationsAllowed 設為 false,系統會略過所有推播通知依附元件,並防止推播通知傳送給使用者。 布林值 true
隱藏「下一個可預訂的日期」 在非營業時間的即時通訊報到畫面上,顯示或隱藏「下次可提供服務的時間」訊息。 布林值 false
隱藏「重新開始」 在非營業時間的即時通訊報到畫面,顯示或隱藏「重新開始」按鈕。 布林值 false
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)
        .setHideNextAvailability(true)
        .setHideStartOver(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()
        )

應用程式權限

應用程式需要下列權限,並會在必要時要求使用者授予這些權限。

權限 說明
CAMERA 用於智慧動作,可拍照及錄影
麥克風 允許應用程式透過 Twilio 進行 VoIP 通話
儲存空間 允許應用程式儲存相片和影片

語言偏好設定

Android SDK 會按照下列優先順序決定語言。

  1. 在應用程式啟動畫面中選取的語言。

  2. 使用 UjetOptions 選取的預設語言。您可以在 UjetOptions 中使用 setDefaultLanguage("en") 設定預設語言。詳情請參閱 SDK 設定一節中的「預設語言」。

  3. 如果應用程式支援,系統會使用在裝置中選取的裝置語言 (依序前往「設定」>「一般」>「語言」)。

  4. 如果應用程式不支援裝置語言,但支援最接近的母方言,則會使用最接近裝置語言的方言。舉例來說,如果使用者在裝置中選取古巴西班牙文做為語言,而應用程式不支援古巴西班牙文,但支援上層方言西班牙文,則系統會使用西班牙文。

  5. 如果應用程式不支援裝置語言,則會預設使用英文。

偏好的管道

「偏好管道」參數可讓您將消費者直接導向特定管道。使用者會略過管道選取步驟,直接透過 Preferred Channel 參數中指定的管道發起聯絡。

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 終止資料
ContentCardClicked 使用者點選內容資訊卡時觸發。 內容資訊卡點擊資料
ContentCardButtonClicked 點選內容資訊卡按鈕時觸發。 內容資訊卡按鈕點擊資料
QuickReplyClicked 使用者點選快速回覆時觸發。 快速回覆點擊資料
MessageLinkClicked 在使用者點選連結時觸發。 郵件連結點擊資料

佇列選單資料

鍵 類型 說明
event_name 字串 包含事件名稱。例如「已點擊電子郵件」。
application 字串 包含應用程式名稱。例如「Android」。
app_id 字串 包含與 Context.getPackageName() 相同的應用程式 ID。
app_version 字串 包含應用程式版本名稱和版本代碼。例如「0.32.0 (123)」。
sdk_version 字串 包含 SDK 版本。例如「0.32.0」。
timestamp 字串 包含世界標準時間的時間戳記 (格式為 yyyy-MM-dd'T'HH:mm:ss'Z')。
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 字串 包含工作階段類型。例如「即時通訊」或「通話」。
timestamp 字串 包含世界標準時間的時間戳記 (格式為 yyyy-MM-dd'T'HH:mm:ss'Z')。

工作階段建立日期

鍵 類型 說明
session_id 字串 包含工作階段 ID。例如「100」。
type 字串 包含工作階段類型。例如「即時通訊」或「通話」。
end_user_identifier 字串 包含使用者 ID。例如「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 會使用 OS 功能開啟。
event_params dictionary 包含點擊事件額外資訊的字典。SDK 會使用這個。

內容資訊卡按鈕點擊資料

鍵 類型 說明
title 字串 資訊卡標題。
title 字串 資訊卡標題。
link 字串 網頁連結或深層連結。SDK 會使用 OS 功能開啟。
event_params dictionary 包含點擊事件額外資訊的字典。SDK 會使用這個。

快速回覆點擊資料

鍵 類型 說明
title 字串 資訊卡標題。
title 字串 資訊卡標題。
link 字串 網頁連結或深層連結。SDK 會使用 OS 功能開啟。
event_params dictionary 包含點擊事件額外資訊的字典。SDK 會使用這個。

郵件連結點擊資料

鍵 類型 說明
event_name 字串 包含事件名稱,例如「Message Link Clicked」。
link 字串 網頁連結或深層連結。SDK 會使用 OS 功能開啟。

將自訂資料傳送至客戶關係管理系統

自訂資料可傳送給客服專員,並顯示在來電/即時通訊的支援單中。

傳送自訂資料的方法有兩種:

  • 簽署方法:使用 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#setUnsignedCustomData 設定自訂資料,而 UjetTokenCallback 應呼叫 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 Platform。使用 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]

      • text:字串 [文字類型必填]

      • media:字典 [媒體類型必填]

        • type:字串 [image 或 video]

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

鍵是資料的專屬 ID。類型是指值的類型。

  • string

    • JSON 字串
  • number

    • integer、float
  • date

    • 13 位數的 UTC Unix 時間戳記格式。(包含毫秒)
  • url

    • HTTP 網址格式

標籤是客戶關係管理頁面上的顯示名稱。

禁止顯示自訂資料

您可以搭配自訂資料物件使用 invisible_to_agent 屬性,防止已簽署或未簽署的自訂資料顯示在代理程式轉接器中。在上述範例中,由於 ssn 物件包含 "invisible_to_agent" : true,因此代理程式轉接器不會顯示使用者的身分證字號。

在自訂資料物件中加入 "invisible_to_agent" : true 屬性時,預期會發生下列行為:

詳情請參閱「在代理程式轉接器中查看工作階段資料」。

保留資料屬性

工作階段開始時,您可以將保留的資料屬性,以已簽署自訂資料的形式傳送至 Contact Center AI Platform (CCAI Platform)。詳情請參閱「傳送預留資料屬性」。

以下是自訂資料中保留資料屬性的範例:

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

將每個值設為 true 或 false。

  • reserved_verified_customer:如果您認為這位使用者是合法客戶,請輸入 true。
  • reserved_bad_actor:如果您認為這位使用者可能是惡意人士,請使用 true。
  • reserved_repeat_customer:如果您判定這位使用者先前曾與客服中心聯絡,請設為 true。

備用

您可以使用 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 供應商連線通話,但找不到供應商。如果開發人員整合錯誤的 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());
    }
});

深層連結

如要對 IVR (PSTN) 通話使用智慧動作,您必須在專案中設定深層連結。

深層連結格式是專屬 URI,例如:

ujet:// <package_name>/smartchannel.

此外,您必須在管理入口網站中設定這個連結,或任何會重新導向至這個連結的網址 (依序前往「設定」>「作業管理」>「啟用傳送簡訊下載應用程式」)。

您必須在資訊清單中新增包含深層連結的意圖篩選器。

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

聊天室復原能力

如要提高聊天室的正常運作時間,請設定聊天室復原能力。這會新增次要備用訊息媒體層,在主要媒體層失敗時使用。

如要新增這項功能,請在模組層級的 build.gradle 檔案中加入以下依附元件:

dependencies {
    implementation "co.ujet.android:chat_blue:VERSION"
}

將 VERSION 替換為最新版 UJET SDK。

來電行為變更

從 Android 10 版本裝置開始,當主機應用程式在背景執行時,使用者不會直接收到來電。而是使用通知提醒使用者有來電 (即使手機處於鎖定狀態),讓他們選擇接聽或拒接。

當主機應用程式在背景執行,且在來電前鎖定螢幕時,系統會顯示相同的通知。這項行為變更是為了遵守 Google 最近對應用程式在背景執行時啟動活動的限制。如果主機應用程式在前景執行,或在搭載 Android 10 以下版本的裝置上執行,則行為不受影響。

自訂 SDK 工作階段

本節說明如何進一步自訂 SDK。

檢查現有工作階段

開始工作階段前,請使用所述方法檢查是否有任何現有或進行中的工作階段。如果存在,您可以提示使用者繼續或取消。

變更使用者時,這一點尤其重要。

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

中斷工作階段連線

如要中斷任何進行中的工作階段,請參閱這個方法。

使用這個方法前,請務必透過 Ujet.getStatus() 檢查這類工作階段是否存在。如果想在 SDK 中斷工作階段連線後執行動作(例如顯示訊息或關閉應用程式),可以使用下一節介紹的回應回呼 onFinished(),否則請將回呼設為空值。

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

設定外部轉移連結圖示

如要自訂外部轉移連結管道中的圖示,請將圖示上傳至應用程式的可繪項目資料夾,並確保在 CCAI Platform 入口網站中建立外部轉移連結時,使用相同的圖示名稱。請依序前往「設定」>「Chat」>「外部轉移連結」>「查看連結」>「新增轉移連結」。

如果 CCAI Platform 入口網站中的圖示名稱與上傳至應用程式的圖示不符,Android SDK 會使用預設圖示。

設定問卷調查「感謝」圖示

如要自訂或覆寫問卷調查感謝頁面中的圖示,請將圖示上傳至應用程式的可繪項目資料夾,並使用 ujet_survey_thank_you_icon 做為圖示名稱。

自訂 UI 和字串

本節說明如何自訂字串、內容資訊卡、主題和即時通訊標頭。

字串

您可以覆寫 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,
        ),
    )
)

問卷調查

如要變更問卷調查感謝頁面上的圖示,請將圖示上傳至應用程式的可繪項目資料夾。

請務必使用 ujet_survey_thank_you_icon 做為圖示名稱。

如要自訂分享螢幕畫面同意聲明對話方塊,請實作 CoBrowseAlertProvider 抽象類別。您可以使用 DialogFragment 或 AlertDialog 導入自訂同意聲明對話方塊。

  • 如果使用 DialogFragment,請加入兩個按鈕,一個用於接受同意聲明,另一個用於拒絕。將自訂 DialogFragment 例項傳遞至 dialogInstance 回呼。

  • 如果使用 AlertDialog,請勿將 DialogFragment 執行個體傳遞至 dialogInstance 回呼。

取得同意聲明後,請叫用 consentStatus 回呼並傳遞相應的布林值,將同意聲明傳遞至 SDK。

請使用下列程式碼範例,導入自訂同意聲明對話方塊:

class CoBrowseAlertProviderImpl : CoBrowseAlertProvider() {
    // You should only implement the cobrowse consent dialog methods that you want to customize and replace the default UI.
    override fun showCoBrowseSessionActivationRequestDialog(context: Context, dialogInstance: (DialogFragment) -> Unit, consentStatus: (Boolean) -> Unit) {

        // Using DialogFragment for customization
        val dialog = ConsentDialog.newInstance(
                title = context.getString(co.ujet.android.R.string.ujet_cobrowse_session_request_alert_title),
                message = context.getString(co.ujet.android.R.string.ujet_cobrowse_session_request_alert_message),
                positiveButton = context.getString(co.ujet.android.R.string.ujet_cobrowse_session_request_alert_button_allow),
                negativeButton = context.getString(co.ujet.android.R.string.ujet_cobrowse_session_request_alert_button_deny),
                cancelable = false,
                onNegativeClick = { consentStatus(false) }, // Pass consent value
                onPositiveClick = { consentStatus(true) }
            )
        // Pass DialogFragment instance
        dialogInstance(dialog)

        // Using AlertDialog for customization, if you use AlertDialog instead of DialogFragment then you don't need to call `dialogInstance(dialog)`, just pass consent value using `consentStatus()`
        val alertDialog = AlertDialog.Builder(context).apply {
           setTitle("Co-Browse Session Request") // Set dialog title
           setMessage("Allow the agent to co-browse with you?") // Set dialog message
           setPositiveButton("Allow"){dialogInterface, _ ->
               // Pass consent
               consentStatus(true)
           }
           setNegativeButton("Deny"){dialogInterface, _ ->
               // Pass consent
               consentStatus(false)
           }
        }.create()
        alertDialog.show()
    }

    override fun showCoBrowseSessionRemoteControlRequestDialog(context: Context, dialogInstance: (DialogFragment) -> Unit, consentStatus: (Boolean) -> Unit) {
        // Same as showCoBrowseSessionActivationRequestDialog
    }
    override fun showCoBrowseSessionFullDeviceRequestDialog(context: Context, dialogInstance: (DialogFragment) -> Unit, consentStatus: (Boolean) -> Unit) {
        // Same as showCoBrowseSessionActivationRequestDialog
    }
    override fun showCoBrowseConfirmationDialog(context: Context, dialogInstance: (DialogFragment) -> Unit, consentStatus: (Boolean) -> Unit) {
        // Same as showCoBrowseSessionActivationRequestDialog
    }
    override fun showStopCoBrowseConfirmationDialog(context: Context, dialogInstance: (DialogFragment) -> Unit, consentStatus: (Boolean) -> Unit) {
        // Same as showCoBrowseSessionActivationRequestDialog
    }
}
// A generic DialogFragment class
class ConsentDialog : DialogFragment() {
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        // Implement your custom dialog UI here
    }
}

使用下列 API 將這項實作傳遞至 SDK:

Ujet.setCoBrowseAlertProvider(CoBrowseAlertProviderImpl())

設定網頁表單

如要設定網頁表單,請實作 UjetWebFormListener 介面的 ujetWebFormDidReceive 方法。這個方法會接收事件 (FormMessageReceived 對應) 做為參數,其中包含表單相關資訊。事件包含下列 JSON 結構:

{
    "type": "form_message_received",
    "smart_action_id": 1,
    "external_form_id": "external_foobar",
    "signature": "4868a7e1dcb5..."
}

處理事件

如要處理事件,請按照下列步驟操作:

  1. 從事件對應 (smart_action_id、external_form_id 和 signature) 擷取相關資訊。
  2. 產生表單 URI 和表單資料的簽章。
  3. 使用 callback.onEvent(formDataEvent) 將表單資料以 FormDataEvent 對應的形式傳遞至 SDK。
  4. 如果在產生 URI 或簽章時發生錯誤,請使用 callback.onError() 叫用錯誤回呼。

傳遞至 SDK 的對應 (FormDataEvent) 應採用下列結構:

{
    "type": "form_data",
    "signature": "4868a7e1dcb5...",
    "data": {
        "external_form_id": "form_id",
        "smart_action_id": 1,
        "uri": "foobar"
    }
}

簽章 (HMAC-SHA256) 必須使用資料產生,並以共用 Secret 金鑰簽署。資料的物件鍵必須先按字母順序排列,才能產生簽章,且應將相同資料傳送至 SDK。

以下程式碼範例說明如何導入網路表單事件監聽器:

class UjetWebFormListenerImpl : UjetWebFormListener {
    override fun ujetWebFormDidReceive(event: Map<String, Any?>, callback: UjetWebFormCallback) {
        try {
            // Extract required fields from the event
            val smartActionId = event["smart_action_id"]
            val externalFormId = event["external_form_id"]
            val signature = event["signature"]
            // Simulate form URI generation (replace with actual implementation)
            val formUri = "generated_form_uri"
            // Prepare the data map, key should be alphabetically ordered
            val data = mapOf(
                "external_form_id" to externalFormId,
                "smart_action_id" to smartActionId,
                "uri" to formUri
            )
            // Generate the signature using `data`
            val generatedSignature = "generated_signature"
            // Create the FormDataEvent map
            val formDataEvent = mapOf(
                "type" to "form_data",
                "signature" to generatedSignature,
                "data" to data
            )
            // Pass the FormDataEvent to the SDK
            callback.onEvent(formDataEvent)
        } catch (e: Throwable) {
            // If any error occurs, invoke the error callback
            callback.onError()
        }
    }
}

使用下列 API 將這項實作傳遞至 SDK:

Ujet.setUjetWebFormListener(UjetWebFormListenerImpl())

疑難排解

發起新對話的時間超過 30 秒

請確認您是否正在回應自訂資料的委派方法。您應在收到要求時傳回有效的自訂資料,或直接透過回呼傳回空值。請參考下列程式碼。

    @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 使用的前景服務

根據 https://developer.android.com/about/versions/14/changes/fgs-types-required#remote-messaging,Google 在 Android 14 中推出前景服務類型,並強制要求在啟動前景服務時指定類型。Contact Center AI 平台 (CCAI Platform) Android SDK 會使用前景服務啟動即時通訊和通話,因此我們在處理文字訊息時,會使用 FOREGROUND_SERVICE_REMOTE_MESSAGING 服務類型進行即時通訊,並使用 FOREGROUND_SERVICE_MICROPHONE 服務類型進行通話。如果沒有這些服務類型,SDK 在 Android 14 以上版本裝置上啟動即時通訊或通話時會異常終止。

宣告 Android SDK 使用的全螢幕意圖權限

裝置鎖定時,必須具備 USE_FULL_SCREEN_INTENT 權限,才能顯示來電推播通知。系統會提醒使用者,並以全螢幕顯示來電通知 (內建的電話應用程式就是這樣通知使用者有來電)。

支援 Android 15

如要支援 Android 15,請按照下列步驟操作:

  1. 在 build.gradle(:app) 中設定 compileSdkVersion = 35 和 targetSdkVersion = 35。

  2. 確認專案的 Android Gradle 外掛程式 (AGP) 版本為 8.5.1 以上。詳情請參閱「更新共用程式庫的封裝」。

  3. 安裝 Java Development Kit (JDK) 17 以上版本,使用 AGP 8.0 以上版本建構 Android 專案。詳情請參閱「執行 AGP 8.0 必須使用 JDK 17」。