Nesta página, explicamos como usar o SDK do Android.
Acessar o app de exemplo
Para baixar o app de exemplo para Android, clique em App de exemplo para Android.
Para criar um app de exemplo usando o SDK para dispositivos móveis Android, você precisa do seguinte:
Chave e secret da empresa no portal da plataforma CCAI.
Android 5.0 (nível 21 da API, Lollipop) ou mais recente.
Firebase Cloud Messaging ou Google Cloud Messaging para a notificação push.
O app foi migrado para o AndroidX.
Requisitos de upgrade do SDK da Twilio
Exige que o SDK da Twilio siga versões específicas se o SDK do Android for integrado usando nosso pacote diretamente. Caso contrário, isso pode ser ignorado.
// Twilio VoIP SDK
api 'com.twilio:voice-android:6.1.1'
// Twilio Conversations SDK
api 'com.twilio:conversations-android:3.1.0'
Além disso, as regras do Proguard já estão incluídas no SDK do Android para garantir que a biblioteca Twilio Programmable Voice não seja removida pelo ProGuard e possa ser usada para resolver problemas caso o ProGuard remova a biblioteca acidentalmente.
-keep class com.twilio.** { *; }
-keep class tvo.webrtc.** { *; }
-dontwarn tvo.webrtc.**
-keep class com.twilio.voice.** { *; }
-keepattributes InnerClasses
Para oferecer suporte às versões mais recentes do Twilio, a partir da versão 0.34.0 do SDK do Android, o SDK não é mais compatível com binários de aplicativos que usam o Java 7. Para usar esta e as próximas versões, os desenvolvedores precisam fazer upgrade dos aplicativos para o Java 8. Confira o exemplo de código abaixo:
android {
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
}
Receber a chave e o secret da empresa
Faça login no Admin Portal usando as credenciais de administrador.
Acesse Configurações > Configurações do desenvolvedor > Chave da empresa e código secreto.
Recupere a chave da empresa e o código secreto da empresa.
Instalação
Adicione o repositório do SDK do Android à configuração do Gradle para o projeto raiz.
build.gradle (projeto)
allprojects {
repositories {
google()
jcenter()
maven {
url "https://sdk.ujet.co/android/"
}
}
}
build.gradle (módulo: 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"
}
Definir as configurações da empresa
Insira as configurações da sua empresa como metadados no arquivo 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>
Assinatura de JWT
As informações do usuário final precisam ser assinadas como JWT no seu servidor para fins de segurança.
O app de exemplo contém o APIManager para testes, e você precisa colocar UJET_COMPANY_SECRET em mock/APIManager.java. O APIManager precisa implementar um método que inicie uma chamada assíncrona para assinar o token de autenticação JWT que retorna o token.
Na produção, é necessário implementar o processo de assinatura no servidor.
public class APIManager {
public static final String UJET_COMPANY_SECRET = "Please input your UJET_COMPANY_SECRET from Ujet developer admin page";
// ...
}
Inicializar o SDK
Inicialize o SDK no método onCreate da classe do app Android.
public class ExampleApplication extends Application implements UjetRequestListener {
@Override
public void onCreate() {
super.onCreate();
Ujet.init(this);
}
// ...
}
Autenticação de usuário final
O usuário final é o consumidor que entra em contato com sua equipe de suporte ao cliente pelo aplicativo.
Para autenticar o usuário final no aplicativo, introduzimos um mecanismo de assinatura JWT.
O SDK do Android pede para assinar o payload quando precisa de autenticação. Se a assinatura for bem-sucedida, o aplicativo trocará o JWT assinado pelo token de autenticação do usuário final.
Em ExampleApplication, implemente a interface UjetRequestListener
para assinar o token de autenticação e para dados do cliente.
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();
}
});
}
}
}
Para mais informações, consulte autenticação do usuário final.
Configurar notificações push
Nesta seção, mostramos como ativar as notificações push em dispositivos móveis.
Preparar o Firebase
Prepare um projeto do Firebase.
Se você já tiver um projeto, use-o e pule este processo.
Crie um projeto do Firebase no Console do Firebase.
Faça o download do arquivo google-services.json em Configurações > GERAL no console do Firebase.

- Acesse a chave do servidor em Configurações > CLOUD MESSAGING no console do Firebase.

Adicionar uma chave de conta de serviço
É necessário adicionar uma chave de conta de serviço ao app para dispositivos móveis para receber notificações push. Para receber uma chave de conta de serviço, consulte Autenticar com uma conta de serviço.
Para adicionar uma chave de conta de serviço ao seu app para dispositivos móveis, siga estas etapas:
No portal da plataforma CCAI, clique em Configurações > Configurações do desenvolvedor. Se o menu Configurações não aparecer, clique em Menu.
Acesse o painel Apps para dispositivos móveis.
Clique em Editar ao lado do app. A caixa de diálogo Editar app para dispositivos móveis vai aparecer.
No campo Conta de serviço, insira a chave da conta de serviço e clique em Salvar.
Configuração do cliente Android
Copie
google-services.jsonpara o diretório do app, por exemplo,PROJECT_ROOT/app/google-services.json.Receba o token
FCMusando o seguinte código: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é chamado, que é registrado no manifesto se o token for atualizado. Para mais informações, consulte Configurar um app cliente do Firebase Cloud Messaging no 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); } }Implemente
UjetRequestListener.onRequestPushTokenna classe Application.UjetRequestListener.onRequestPushTokendeve retornar o tokenFCM/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 -> { }) } }Gerencie a notificação push. Se você quiser que Contact Center AI Platform (plataforma da CCAI) processe as próprias mensagens push, transmita os dados diretamente para
UjetPushHandler.handle().O aplicativo processa apenas mensagens com o campo
ujet_noti_type(ounoti_type, para compatibilidade com versões anteriores) definido.Caso contrário, você pode optar por enviar apenas mensagens com
ujet_noti_typeparaUjetPushHandler.handle()para processamento.
Veja a seguir um exemplo de mensagem de notificação push:
{ "call_id" : 12345, "ujet_noti_type" : "connect_call", "noti_type" : "connect_call", "call_type" : "ScheduledCall", "fail_reason" : "none", "fail_details" : "none" }
Processar mensagem do 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
}
}
}
Processar mensagem do 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
}
}
}
Processar a mensagem do GCM no GcmReceiver (método antigo)
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
}
}
}
Inicie o aplicativo
Adicione a seguinte linha onde você quer iniciar o aplicativo (sem parâmetros):
Ujet.start(new UjetStartOptions.Builder().build());
Também é possível iniciar o SDK do Android sem a tela de apresentação.
UjetStartOptions ujetStartOptions = new UjetStartOptions.Builder()
.setSkipSplashEnabled(true)
.build();
Ujet.start(ujetStartOptions);
Você também pode iniciar o SDK do Android em um ponto específico do menu com essa chave usando um ponto de acesso direto:
String menuKey = "MENU_KEY";
UjetStartOptions ujetStartOptions = new UjetStartOptions.Builder()
.setMenuKey(menuKey)
.build();
Ujet.start(ujetStartOptions);
O menuKey pode ser criado com um ponto de acesso direto no portal da CCAI Platform
(com papel de administrador).
Acesse Configurações > Fila.
Selecione qualquer fila na estrutura do menu.
Marque Criar ponto de acesso direto.
Insira a chave no formulário de texto.
Clique em Salvar.

Também é possível iniciar o SDK do Android com um ID de tíquete específico para transmiti-lo ao CRM. Este ID de tíquete será aberto quando um chat ou uma ligação for conectado.
String ticketId = "TICKET_ID";
UjetStartOptions ujetStartOptions = new UjetStartOptions.Builder()
.setTicketId(ticketId)
.build();
Ujet.start(ujetStartOptions);
Configuração do SDK
É possível configurar várias opções antes de iniciar o SDK do Android. Consulte a
classe UjetOption. As opções de número de telefone de fallback e sensibilidade de rede
descritas na tabela a seguir só funcionam quando o botão de alternância Ativar fallback de PSTN
está ativado no portal da plataforma CCAI em Configurações > Configurações do desenvolvedor >
MMA > Editar. Quando a opção Ativar substituto de PSTN está desativada, não usamos o substituto de PSTN. A CCAI Platform usa a sensibilidade de rede padrão (0,85) para
verificar as conexões de rede.
| Opção | Descrição | Valor | Valor padrão |
|---|---|---|---|
| Nível do registro | Nível de registro a ser impresso no Logcat. | Número inteiro. O mesmo vale para o nível de registro do Android Log. (Mín.: 2, Máx.: 7) | 5 (Log.Warn) |
| Idioma padrão | O código de idioma padrão. | String. Um código de idioma ISO 639. (por exemplo, "en" para inglês) | null |
| Número de telefone substituto | O número de telefone é usado como substituto quando a Internet não está disponível ou o número de telefone do representante da empresa não existe no portal de administração. | String. Número de telefone. | null |
| Gerenciador de exceções não capturadas ativado | Ative o gerenciador de exceções não identificadas. Se for true, o app vai processar todas as exceções não capturadas do SDK no momento da execução usando Thread.setDefaultUncaughtExceptionHandler. No entanto, se a mesma exceção ocorrer duas vezes, o app vai falhar. |
Booleano. | true |
| Sensibilidade da rede | A sensibilidade para verificar o estado da rede. | Double entre 0 e 1, em que 0 é o menos sensível e 1 é o mais sensível. Um valor de 1 sempre vai usar uma chamada PSTN como substituto. Se usado, recomendamos começar com um valor de .97. O valor do portal em Configurações > Configurações do desenvolvedor > Apps para dispositivos móveis > Substituição do limite de números de telefone substitui esse valor. |
0.85 |
| Modo escuro ativado | Ative o tema do modo escuro. Se for true, o SDK vai aplicar um tema de modo escuro quando o usuário ativar esse modo e, caso contrário, vai ignorar a ação. |
Booleano. | false |
| Canal único ativado | Opção de configuração para mostrar ou ignorar a tela de seleção de canal para um único canal. Se for true, o SDK vai mostrar uma única tela de seleção de canal em vez de selecionar o canal automaticamente, mesmo que apenas um canal esteja ativado na fila do menu. |
Booleano. | false |
| Minimizar automaticamente a visualização da chamada | Opção de configuração para minimizar automaticamente a UI da tela de chamada inicial por padrão ou esperar que o usuário a minimize. | Booleano. | false |
| Borda do ícone do agente ativada | Opção de configuração para mostrar ou remover uma borda circular ao redor do ícone do agente. | Booleano. | false |
| Tamanho de fonte estático na visualização do seletor | Opção de configuração para ajustar ou desativar automaticamente o tamanho do texto do item do seletor. | Booleano. | false |
| Ocultar anexo de mídia no chat | Opção de configuração para mostrar ou ocultar o ícone de anexo de mídia na UI do chat. | Booleano. | false |
Ignorar permissão READ_PHONE_STATE |
Se definido como true, o SDK não solicita a permissão READ_PHONE_STATE. Se você não quiser usar chamadas de URA no app, defina essa flag como true para evitar solicitar essa permissão. Além disso, seu aplicativo precisa remover explicitamente a permissão android.permission.READ_PHONE_STATE para o SDK da plataforma CCAI. Não recomendamos definir como true, porque essa permissão é necessária para que as chamadas de IVR no app funcionem. |
Booleano | false |
| Chave de licença do Cobrowse.io (se aplicável) | Opção de configuração para configurar a biblioteca Cobrowse.io. Para encontrar sua chave de licença do Cobrowse, faça login no https://cobrowse.io/dashboard/settings e acesse a seção Chave de licença. |
String | null |
| Título do cabeçalho do chat personalizado | Opção de configuração para personalizar o texto do título do cabeçalho na UI do chat. | String | null |
| Personalizar as respostas rápidas da UI do chat | Opção de configuração para personalizar as respostas rápidas do agente virtual na interface do chat. As respostas rápidas do agente virtual são agrupadas por padrão, mas se você quiser mostrá-las individualmente, use essa opção de configuração para definir QuickReplyButtonsStyle.INDIVIDUAL | UjetStylesOptions | QuickReplyButtonsStyle.GROUPED |
| Personalizar vários atributos UI interface do chat | Opção de configuração para personalizar vários atributos, como fonte, cor de fundo, ícone etc. | UjetStylesOptions | null |
| Ocultar barra de status | Opção de configuração para mostrar ou ocultar a barra de status. Se o valor for verdadeiro, o SDK vai ocultar a barra de status. | Booleano | false |
| Definir o recurso drawable do ícone de carregamento | Opção de configuração para personalizar a visualização do ícone de carregamento em todo o app. Se ela não estiver disponível ou for nula, usaremos a visualização de carregamento padrão. | Inteiro | null |
| Orientação paisagem desativada | Desative a orientação paisagem. Se o valor for verdadeiro, a orientação paisagem não será aplicada. | Booleano | false |
| Ocultar o botão inline para iniciar uma nova conversa no chat | Opção de configuração para mostrar ou ocultar o botão inline de iniciar nova conversa na UI do chat. | Booleano | false |
| Mostrar botão de pular CSAT | Opção de configuração para mostrar ou ocultar o botão de pular na caixa de diálogo de CSAT | Booleano | false |
| Bloquear o encerramento do chat pelo usuário final | Opção de configuração para mostrar ou ocultar o botão de encerrar conversa na UI do chat | Booleano | false |
| Ocultar o download da transcrição do chat | Mostre ou oculte o botão de download da transcrição do chat no menu de ações e na interface do usuário do chat. Se você adicionar um espaço à string ujet_common_hide, o texto Hide vai desaparecer e apenas a seta para trás vai aparecer. |
Número inteiro. Valores:
|
0
|
| Desativar as notificações push no nível global | Definir UjetOption.setPushNotificationsAllowed como false ignora todas as dependências de notificações push e impede que elas cheguem aos usuários finais. |
Booleano | true |
| Ocultar Próxima disponibilidade | Mostrar ou ocultar a mensagem Próxima disponibilidade na tela de confirmação de chat fora do horário comercial. | Booleano | false |
| Ocultar Recomeçar | Mostre ou oculte o botão Começar de novo na tela de check-in do chat fora do horário de atendimento. | Booleano | 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()
)
Permissões do app
O app requer as seguintes permissões e as solicita ao usuário quando necessário.
| Permissão | Descrição |
|---|---|
| CÂMERA | Usado para ações inteligentes de tirar fotos e gravar vídeos |
| MICROPHONE | Permite que o app use chamadas VoIP pelo Twilio |
| ARMAZENAMENTO | Permite que o app salve fotos e vídeos |
Preferência de idioma
O SDK do Android vai usar a seguinte ordem de prioridade para determinar o idioma.
Idioma selecionado na tela de apresentação do app.
Idioma padrão selecionado usando
UjetOptions. É possível definir o idioma padrão usandosetDefaultLanguage("en")emUjetOptions. Consulte Idioma padrão na seção de configuração do SDK para mais detalhes.O idioma selecionado no dispositivo (em Configurações > Geral > Idioma) será usado quando for compatível com o app.
O dialeto mais próximo do idioma do dispositivo será usado quando o app não oferecer suporte ao idioma do dispositivo, mas oferecer suporte ao dialeto principal mais próximo. Por exemplo, se o usuário selecionar o espanhol de Cuba como o idioma no dispositivo, e o app não for compatível com esse dialeto, mas for compatível com o espanhol, o idioma usado será o espanhol.
O inglês será usado como idioma padrão quando o idioma do dispositivo não for compatível com o app.
Canal preferencial
O parâmetro de canal preferencial permite encaminhar os consumidores diretamente para um canal específico. O usuário final pula a etapa de seleção de canal e inicia o contato diretamente pelo canal especificado no parâmetro Preferred Channel.
UjetStartOptions.preferredChannel
Ujet.start(new)
UjetStartOptions.Builder().setPreferredChannel
(UjetPreferredChannel.UjetPreferredChannelChat).build());
Notificações de eventos
Se quiser, defina UjetEventListener para receber notificações de eventos do aplicativo.
Os tipos de evento disponíveis e as descrições estão listados aqui.
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());
}
});
| Tipo de evento | Descrição | Dados incluídos no evento |
|---|---|---|
EmailClicked |
É acionado quando o usuário final clica no canal de e-mail. | Dados do cardápio da fila |
EmailSubmitted |
É acionado quando o usuário final envia um e-mail. | Dados do menu de fila, dados enviados por e-mail |
SessionPaused |
É acionado quando o usuário final minimiza uma sessão de chat ou chamada. | Dados de sessão |
SessionResumed |
É acionado quando o usuário final volta para uma sessão de chat ou chamada em segundo plano. | Dados de sessão |
SessionCreated |
É acionado quando uma sessão de chat ou ligação é criada. | Dados do menu de fila, dados de sessão criada |
SessionEnded |
É acionado quando uma sessão de chat ou ligação é encerrada. | Dados do menu de fila, dados de sessão criada, dados de sessão encerrada |
SdkTerminated |
É acionado quando o SDK é fechado, inclusive de forma inesperada. | Dados de SDKs desativados |
ContentCardClicked |
Acionado quando um card de conteúdo é clicado. | Dados de cliques em cards de conteúdo |
ContentCardButtonClicked |
Acionado quando um botão de card de conteúdo é clicado. | Dados de cliques no botão do card de conteúdo |
QuickReplyClicked |
É acionado quando uma resposta rápida é clicada. | Dados de cliques em respostas rápidas |
MessageLinkClicked |
É acionado quando um link é clicado. | Dados de cliques em links de mensagens |
Dados do cardápio da fila
| Chave | Tipo | Descrição |
|---|---|---|
event_name |
String | Contém o nome do evento. Por exemplo, "E-mail clicado". |
application |
String | Contém o nome do aplicativo. Por exemplo, "Android". |
app_id |
String | Contém o identificador do app, que é igual a Context.getPackageName(). |
app_version |
String | Contém o nome e o código da versão do app. Por exemplo, "0.32.0 (123)". |
sdk_version |
String | Contém a versão do SDK. Por exemplo, "0.32.0". |
timestamp |
String | Contém o carimbo de data/hora em UTC (no formato aaaa-MM-dd'T'HH:mm:ss'Z'). |
device_model |
String | Contém o modelo do dispositivo do usuário. Exemplo: "Google Pixel". |
device_version |
String | Contém a versão do dispositivo do usuário. Por exemplo, "10, Q, SDK 29". |
company |
String | Inclui o nome da empresa. Exemplo: "Empresa". |
menu_name |
String | Contém o nome do nó folha (última seleção de menu do usuário). Por exemplo, "Sub Menu". |
menu_id |
String | Contém o ID do nó folha (última seleção de menu do usuário). Por exemplo, "123". |
menu_path |
String | Contém a sequência completa de menus selecionados pelo usuário. Exemplo: "Pai / Filho / Submenu". |
menu_key |
String | Contém a chave DAP e é opcional. Por exemplo, "special_user_menu". |
Dados enviados por e-mail
| Chave | Tipo | Descrição |
|---|---|---|
has_attachments |
Booleano | Retorna True se o e-mail tiver anexos. Caso contrário, retorna False. |
Dados de sessão
| Chave | Tipo | Descrição |
|---|---|---|
event_name |
String | Contém o nome do evento. Por exemplo, "E-mail clicado". |
type |
String | Contém o tipo de sessão. Por exemplo, "chat" ou "ligação". |
timestamp |
String | Contém o carimbo de data/hora em UTC (no formato aaaa-MM-dd'T'HH:mm:ss'Z'). |
Dados criados na sessão
| Chave | Tipo | Descrição |
|---|---|---|
session_id |
String | Contém o ID da sessão. Por exemplo, "100". |
type |
String | Contém o tipo de sessão. Por exemplo, "chat" ou "ligação". |
end_user_identifier |
String | Contém o identificador de usuário final. Por exemplo, "João". |
messages_end_user |
String | Contém a contagem de mensagens do usuário final e é incluído apenas na sessão de chat. Por exemplo, "3". |
messages_agent |
String | Contém a contagem de mensagens do agente e é incluído apenas na sessão de chat. Por exemplo, "3". |
Dados de sessão encerrada
| Chave | Tipo | Descrição |
|---|---|---|
agent_name |
String | Contém o nome do agente. Por exemplo, "João". |
ended_by |
String | Contém detalhes sobre quem encerrou a sessão. Os valores possíveis são "agent" (quando o agente encerra a sessão), "end_user" (quando o usuário final encerra a sessão), "timeout" (quando o chat expira) ou "dismissed" (quando o chat é dispensado). |
duration |
String | Contém a duração da sessão em segundos e é incluído apenas para a sessão de chamada. Por exemplo, "30 segundos". |
Dados de SDKs desativados
| Chave | Tipo | Descrição |
|---|---|---|
event_name |
String | Contém o nome do evento. Por exemplo, "E-mail clicado". |
Dados de cliques no card de conteúdo
| Chave | Tipo | Descrição |
|---|---|---|
title |
string | O título do card. |
title |
string | O título do card. |
subtitle |
string | O subtítulo do card. |
body |
string | A descrição do card de conteúdo. |
link | string | Um link de página da Web ou um link direto. O SDK usa os recursos do SO para abri-lo. |
event_params | dictionary | Um dicionário com informações extras sobre o evento de clique. O SDK usa isso. |
Dados de cliques no botão do card de conteúdo
| Chave | Tipo | Descrição |
|---|---|---|
title |
string | O título do card. |
title |
string | O título do card. |
link | string | Um link de página da Web ou um link direto. O SDK usa os recursos do SO para abri-lo. |
event_params | dictionary | Um dicionário com informações extras sobre o evento de clique. O SDK usa isso. |
Dados de cliques em respostas rápidas
| Chave | Tipo | Descrição |
|---|---|---|
title |
string | O título do card. |
title |
string | O título do card. |
link | string | Um link de página da Web ou um link direto. O SDK usa os recursos do SO para abri-lo. |
event_params | dictionary | Um dicionário com informações extras sobre o evento de clique. O SDK usa isso. |
Dados de cliques em links de mensagens
| Chave | Tipo | Descrição |
|---|---|---|
event_name |
String | Contém o nome do evento, por exemplo, "Link da mensagem clicado". |
link |
String | Um link de página da Web ou um link direto. O SDK usa os recursos do SO para abrir o link. |
Enviar dados personalizados para seu CRM
Os dados personalizados podem ser enviados aos representantes de suporte e vão aparecer no tíquete de suporte da chamada/chat recebido.
Há dois métodos para enviar dados personalizados:
Método assinado: assinatura de dados predefinida com JWT.
Método não assinado: dados predefinidos com JSON simples (não recomendado).
Como usar o método assinado para enviar dados personalizados
Para enviar dados personalizados usando o método assinado, implemente um método de assinatura.
Primeiro, recupere os dados do cliente para o app host e envie-os ao servidor para assinatura. No servidor, é possível adicionar mais dados usando um formulário definido. Assine-os com o secret da sua empresa e os retorne por 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));
}
// ...
}
}
Como usar o método não assinado para enviar dados personalizados
O Google recomenda que você use o método assinado para enviar dados do cliente no seu aplicativo. Para mais informações, consulte Como usar o método assinado para enviar dados personalizados.
Para enviar dados não assinados, inicie o SDK do Android com opções de início para definir dados personalizados usando UjetStartOptions.Builder#setUnsignedCustomData, e UjetTokenCallback precisa chamar 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);
Como usar dados personalizados não assinados para enviar transcrições de chat externas
É possível enviar a transcrição do chat externo para a CCAI Platform usando
dados personalizados não assinados quando ela é iniciada. Use
UjetCustomData.putObject("external_chat_transfer", hashMapObject) para definir os
dados da transcrição no formato JSON da seguinte maneira:
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);
Formato JSON:
greeting_override: stringagent: dicionárioname: stringavatar: string [URL do avatar do agente, opcional]
transcript: matrizsender: string ["end_user" ou "agent"]timestamp: string [por exemplo, "2021-03-15 12:00:00Z"]content: matriztype: string [text ou media]text: string [obrigatório para o tipo de texto]media: dicionário [obrigatório para o tipo de mídia]type: string [image ou video]url: string [URL público que aponta para o arquivo de mídia]
Exemplo de 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"
}
]
}
]
}
É possível usar o Markdown no tipo de texto. Os seguintes formatos são compatíveis:
Negrito
Itálico
Sublinhado
Quebras de linha
Lista com marcadores
Lista numerada
Links
Formato de dados personalizado
Nesta seção, mostramos o formato dos dados personalizados que podem ser transmitidos no JWT.
JSON codificado para JWT
O JSON precisa incluir iat e exp para validar o JWT. O objeto dos
dados personalizados é o valor da chave 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"
}
}
}
A chave é um identificador exclusivo dos dados. O tipo é o tipo do valor.
string- String JSON
number- número inteiro, ponto flutuante
date- Formato de carimbo de data/hora Unix UTC com 13 dígitos. (contém milissegundos)
url- Formato do URL HTTP
O rótulo é o nome de exibição na página do CRM.
Impedir a exibição de dados personalizados
É possível usar a propriedade invisible_to_agent com um objeto de dados personalizados para
impedir que dados personalizados assinados ou não assinados sejam mostrados no adaptador
do agente. No exemplo anterior, o CPF do usuário final não é
mostrado no adaptador do agente porque "invisible_to_agent" : true está incluído no
objeto ssn.
Quando você inclui a propriedade "invisible_to_agent" : true com um objeto de dados personalizado,
o comportamento esperado é o seguinte:
- Os dados personalizados são incluídos no arquivo de metadados da sessão.
- Os dados do cliente não estão incluídos nos registros do CRM.
Para mais informações, consulte Ver dados da sessão no adaptador do agente.
Propriedades de dados reservadas
É possível enviar propriedades de dados do cliente para a Contact Center AI Platform (plataforma CCAI) como dados personalizados assinados quando uma sessão começa. Para mais informações, consulte Enviar propriedades de dados reservadas.
Confira um exemplo de propriedades de dados reservadas em dados personalizados:
{
"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"
}
}
}
Defina cada valor como true ou false.
reserved_verified_customer:truese você considerar que esse usuário final é um cliente legítimo.reserved_bad_actor:truese você considerar que esse usuário final pode ser um usuário de má-fé.reserved_repeat_customer:truese você determinou que esse usuário final já entrou em contato com seu contact center antes.
Fallback
Use UjetErrorListener para o substituto de erros inesperados. Se
você não definir esse listener ou retornar "false", o SDK do Android vai
processar o erro.
O SDK do Android vai redirecionar os usuários para o discador com um número de fallback somente quando a opção "Ativar fallback de PSTN" estiver ATIVADA em Configurações > Configurações do desenvolvedor > MMA > Editar pop-up. Caso contrário, saia do SDK.
| Tipo de erro | Código do erro | Gatilho |
|---|---|---|
| NETWORK_ERROR | 1 | A rede não está disponível. Esse erro não é acionado quando a rede não está disponível durante o chat, a ligação ou a tela de avaliação. |
| AUTHENTICATION_ERROR | 100 | Ocorreu um erro inesperado durante a autenticação. |
| AUTHENTICATION_JWT_ERROR | 101 | Ocorreu um erro inesperado durante a validação do JWT (por exemplo, erro de análise). |
| VOIP_CONNECTION_ERROR | 1000 | Falha ao estabelecer uma conexão com o provedor de VoIP. Isso é feito pelo callback do SDK de VoIP. |
| VOIP_LIBRARY_NOT_FOUND | 1001 | Uma chamada deveria ser conectada usando um provedor de VoIP, mas nenhum foi encontrado. Isso pode acontecer quando um desenvolvedor integra o SDK errado ou não adiciona a biblioteca do provedor de VoIP às dependências. |
| CHAT_LIBRARY_NOT_FOUND | 1100 | Ocorre quando não é possível encontrar a biblioteca de chat. Isso pode acontecer quando um desenvolvedor integra o SDK errado ou não adiciona a biblioteca do Twilio Chat às dependências. |
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());
}
});
Vinculação direta
Se você quiser usar ações inteligentes para uma chamada de IVR (PSTN), configure links diretos no seu projeto.
O formato de link direto é um URI exclusivo, como:
ujet:// <package_name>/smartchannel.
Além disso, é necessário definir esse link ou qualquer URL que redirecione para ele no Portal de Administração (Configurações > Gerenciamento de operações > Ativar o envio de SMS para baixar o app).
Você precisará adicionar um filtro de intent que contenha o link direto no manifesto.
<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>
Resiliência do chat ao vivo
Para melhorar o tempo de atividade do chat ao vivo, configure a resiliência do chat ao vivo. Isso adiciona uma camada de mídia de mensagens substituta secundária que é usada se a camada de mídia principal falhar.
Para adicionar esse recurso, adicione a seguinte dependência ao arquivo build.gradle do módulo:
dependencies {
implementation "co.ujet.android:chat_blue:VERSION"
}
Substitua VERSION pela versão mais recente do SDK da UJET.
Mudanças no comportamento das chamadas recebidas
A partir dos dispositivos com o Android 10, as ligações recebidas não serão recebidas diretamente pelos usuários finais quando o app host estiver em segundo plano. Em vez disso, usamos notificações para alertar os usuários sobre chamadas recebidas (mesmo quando o smartphone está bloqueado), a opção de aceitar ou recusar a chamada.
Mostramos as mesmas notificações quando o app host está em segundo plano e a tela é bloqueada antes da chegada da chamada recebida. Essa mudança de comportamento é para obedecer às restrições recentes do Google para iniciar atividades quando o app está em segundo plano. O comportamento não é afetado quando o app host está em primeiro plano ou em execução em dispositivos com versões anteriores ao Android 10.
Personalizar a sessão do SDK
Nesta seção, descrevemos como o SDK pode ser ainda mais personalizado.
Verificar se já existe uma sessão
Antes de iniciar uma sessão, use o método descrito para verificar se há alguma sessão em andamento ou já existente. Se houver, você poderá pedir ao usuário final para retomar ou cancelar.
Isso é especialmente importante quando um usuário é alterado.
if (Ujet.getStatus() != UjetStatus.None) {
// Display alert to cancel login or resume existing session
}
Desconectar a sessão
Consulte o método se quiser desconectar qualquer sessão em andamento.
Antes de usar esse método, verifique se essa sessão existe usando
Ujet.getStatus(). Se você quiser realizar uma ou mais ações depois que o SDK
desconectar a sessão, por exemplo, mostrar uma mensagem ou fechar o
app, use o callback de resposta onFinished(), conforme abordado na próxima
seção. Caso contrário, defina o callback como nulo.
Ujet.disconnect(new UjetResponseCallback() {
@Override
public void onFinished() {
// `onFinished()` is triggered after CCAI Platform disconnects the session.
finish(); // Finishes the activity.
}
});
Limpar dados do usuário final do cache
Você é responsável por limpar o cache quando os dados relacionados ao usuário final forem atualizados ou alterados no seu app. Por exemplo, se o usuário final sair da conta, invoque o método para remover o cache desse usuário para que uma nova sessão seja iniciada para o novo usuário final na próxima inicialização do SDK.
Ujet.clearUserData();
Ocultar o SDK
É possível ocultar o SDK usando o método Ujet.hideSDK(). Isso é útil quando o
SDK interfere na interface do usuário do app, por exemplo. Inicialize o SDK
usando Ujet.init() antes de chamar esse método. Ujet.init() vai retornar true se
o SDK for ocultado, e false caso contrário. Depois de ocultar o SDK, você
pode iniciá-lo novamente usando o método Ujet.start().
O exemplo a seguir oculta o SDK:
Ujet.hideSDK();
Configurar ícones de link de desvio externo
Personalize o ícone no canal do link de desvio externo fazendo upload dele em uma pasta drawable do app e use o mesmo nome de ícone ao criar o link de desvio externo no portal da plataforma CCAI em Configurações > Chat > Links de desvio externo > Ver links > Adicionar link de desvio.
Se o nome do ícone no portal da plataforma da CCAI não corresponder ao ícone enviado para o app, o SDK do Android vai usar o ícone padrão.
Configurar o ícone de agradecimento das pesquisas
Para personalizar ou substituir o ícone na página de agradecimento da pesquisa, faça upload de um
ícone na pasta drawable do app e use o nome de arquivo
ujet_survey_thank_you_icon como nome do ícone.
Personalização UI e string
Nesta seção, explicamos como personalizar strings, cards de conteúdo, temas e cabeçalhos de chat.
Strings
É possível personalizar as strings usadas no aplicativo substituindo as chaves de cada string em 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>

Personalização do tamanho do texto
Personalize o título, a descrição e o tamanho do texto do seletor usados no aplicativo substituindo as seguintes chaves em dimens.xml.
Os tamanhos de texto personalizáveis são descritos:
<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>

Tema
Siga as etapas abaixo para personalizar o tema e o plano de fundo. A etapa 1 é para o tema, e a etapa 2 é para o plano de fundo.
Personalize o tema substituindo as chaves de cada item de estilo em style.xml. Por exemplo,
<!--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>É possível personalizar a cor de fundo no aplicativo substituindo as chaves de cada item de estilo em style.xml. A cor de fundo personalizável é mostrada na captura de tela.
<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>
Personalizar o título do cabeçalho do chat
Há opções disponíveis para personalizar o texto do título do cabeçalho do chat na UI.
É possível personalizar o texto do título do cabeçalho da conversa usando as seguintes opções:
<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>
É possível personalizar as respostas rápidas do agente virtual UI do chat usando as seguintes opções:
<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>
Cards de conteúdo
É possível personalizar os cards de conteúdo e o chat. Para
isso, use o arquivo JSON (consulte content_card property
no arquivo app/src/main/assets/json/ujet_styles.json) ou a
classe 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",
)
)
)
)
Tema do formulário da Web
É possível personalizar o card do formulário da Web junto com a personalização do chat. Para isso,
use o arquivo JSON (consulte a propriedade form_card no
arquivo app/src/main/assets/json/ujet_styles.json) ou a
classe 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,
),
)
)
Pesquisas
Para mudar o ícone na página de agradecimento da pesquisa, faça upload de um ícone para a pasta drawable do app.
Use ujet_survey_thank_you_icon como o nome do ícone.
Personalizar a caixa de diálogo de consentimento para compartilhamento de tela
Para personalizar a caixa de diálogo de consentimento de compartilhamento de tela, implemente a
classe abstrata CoBrowseAlertProvider. Use DialogFragment ou AlertDialog para implementar a caixa de diálogo de consentimento personalizada.
Se você usar
DialogFragment, inclua dois botões, um para aceitar e outro para negar o consentimento. Transmita uma instância personalizada deDialogFragmentpara o callbackdialogInstance.Se você usar
AlertDialog, não transmita a instânciaDialogFragmentpara o callbackdialogInstance.
Depois de receber o consentimento, transmita-o ao SDK invocando o callback consentStatus
e transmitindo o valor booleano correspondente.
Use o exemplo de código a seguir para implementar a caixa de diálogo de consentimento personalizada:
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
}
}
Transmita essa implementação ao SDK usando a seguinte API:
Ujet.setCoBrowseAlertProvider(CoBrowseAlertProviderImpl())
Configurar formulários da Web
Para configurar um formulário da Web, implemente o método ujetWebFormDidReceive da
interface UjetWebFormListener. Esse método recebe um evento (um mapa FormMessageReceived) como parâmetro, contendo informações relacionadas ao formulário.
O evento inclui a seguinte estrutura JSON:
{
"type": "form_message_received",
"smart_action_id": 1,
"external_form_id": "external_foobar",
"signature": "4868a7e1dcb5..."
}
Processar o evento
Para processar o evento, siga estas etapas:
- Extraia as informações relevantes do mapa de eventos (
smart_action_id,external_form_idesignature). - Gere um URI de formulário e uma assinatura para os dados do formulário.
- Transmita os dados do formulário para o SDK como um mapa
FormDataEventusandocallback.onEvent(formDataEvent). - Se ocorrer um erro durante a geração do URI ou da assinatura, invoque o callback de erro usando
callback.onError().
O mapa (FormDataEvent) transmitido ao SDK precisa ter a seguinte estrutura:
{
"type": "form_data",
"signature": "4868a7e1dcb5...",
"data": {
"external_form_id": "form_id",
"smart_action_id": 1,
"uri": "foobar"
}
}
A assinatura (HMAC-SHA256) precisa ser gerada usando os dados, assinados com uma chave secreta compartilhada. As chaves de objeto dos dados precisam ser ordenadas em ordem alfabética antes de gerar assinaturas, e os mesmos dados precisam ser enviados ao SDK.
O exemplo de código a seguir mostra como implementar o listener de formulário da Web:
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()
}
}
}
Transmita essa implementação ao SDK usando a seguinte API:
Ujet.setUjetWebFormListener(UjetWebFormListenerImpl())
Solução de problemas
O início de uma nova conversa levou mais de 30 segundos
Verifique se você está respondendo a um método delegado de dados do cliente. Você precisa retornar dados personalizados válidos mediante solicitação ou apenas retornar nulo com um callback. Use o código a seguir como referência.
@Override
public void onSignPayloadRequest(Map<String, Object> payload, UjetPayloadType ujetPayloadType, UjetTokenCallback tokenCallback) {
if (ujetPayloadType == UjetPayloadType.CustomData) {
tokenCallback.onToken(null);
}
}
Explicação da declaração de política
Se você receber uma notificação no Google Play Console pedindo para declarar uma política para os serviços ou permissões usados pelo SDK do Android, use uma das explicações a seguir.
Declarar serviços em primeiro plano usados pelo SDK do Android
O Google introduziu tipos de serviço em primeiro plano no Android 14 e determinou que eles sejam especificados ao iniciar serviços em primeiro plano, de acordo com https://developer.android.com/about/versions/14/changes/fgs-types-required#remote-messaging. O SDK do Android da plataforma Contact Center AI (plataforma CCAI) usa serviços em primeiro plano para iniciar chats e ligações. Por isso, usamos o tipo de serviço FOREGROUND_SERVICE_REMOTE_MESSAGING para chats, já que lidamos com mensagens de texto, e o tipo de serviço FOREGROUND_SERVICE_MICROPHONE para ligações. Sem esses tipos de serviço, o SDK vai falhar ao iniciar um chat ou uma chamada em dispositivos com o Android 14 ou versões mais recentes.
Declarar a permissão de intent para tela cheia usada pelo SDK do Android
A permissão USE_FULL_SCREEN_INTENT é necessária para mostrar a notificação push de chamada recebida quando o dispositivo está bloqueado. Ele alerta o usuário final e mostra a notificação de chamada recebida em tela cheia (é assim que o app de telefone integrado notifica o usuário final sobre a chamada recebida).
Suporte ao Android 15
Para oferecer suporte ao Android 15, siga estas etapas:
Defina
compileSdkVersion = 35etargetSdkVersion = 35nobuild.gradle(:app).Verifique se a versão do Plug-in do Android para Gradle (AGP) do seu projeto é 8.5.1 ou mais recente. Para mais informações, consulte Atualizar o empacotamento das bibliotecas compartilhadas.
Instale o Java Development Kit (JDK) versão 17 ou mais recente para criar projetos do Android usando o AGP 8.0 ou mais recente. Para mais informações, consulte JDK 17 necessário para executar o AGP 8.0.