# Add chat capability to the Contact Center SDK for Android
After initializing the SDK, call the `getZoomCCChatService` (Java) or `zoomCCChatService` (Kotlin) function to get `ZoomCCChatService` and initialize with the `ZoomCCItem` class. Then call `fetchUI` to open the chat view.
## Show the chat view controller
```java
ZoomCCChatService service = ZoomCCInterface.INSTANCE.getZoomCCChatService();
ZoomCCItem item = new ZoomCCItem(ChatEntryId, null, ZoomCCIInterfaceType.CHAT, CCServerType.CCServerWWW);
service.init(item);
service.login();
service.fetchUI();
```
```kotlin
val service = ZoomCCInterface.INSTANCE.zoomCCChatService
val item = ZoomCCItem(
entryId = ChatEntryId,
sdkType = ZoomCCIInterfaceType.CHAT,
serverType = CCServerType.CCServerWWW
)
service.init(item)
service.login()
service.fetchUI()
```
## Pass engagement variables (optional)
To pass host application context variables to a chat engagement, configure custom variables in Contact Center Management and set its value source to **Application**. Before calling `init()`, use the configured `attributeName` as the key and the application-provided value as the map value in `ZoomCCItem.engagementVariables`.
> **Missing variables**
>
> Missing variables do not block the engagement flow, but nodes that depend on them may not work as expected.
```java
ZoomCCChatService service = ZoomCCInterface.INSTANCE.getZoomCCChatService();
ZoomCCItem item = new ZoomCCItem(ChatEntryId, null, ZoomCCIInterfaceType.CHAT, CCServerType.CCServerWWW);
item.setEngagementVariables(Map.of("customAttribute", "customValue"));
service.init(item);
service.login();
service.fetchUI();
```
```kotlin
val item = ZoomCCItem(
entryId = ChatEntryId,
sdkType = ZoomCCIInterfaceType.CHAT,
serverType = CCServerType.CCServerWWW
).apply {
engagementVariables = mapOf("customAttribute" to "customValue")
}
service.init(item)
service.login()
service.fetchUI()
```
## End chat
```java
ZoomCCChatService service = ZoomCCInterface.INSTANCE.getZoomCCChatService();
service.endChat();
```
```kotlin
val service = ZoomCCInterface.INSTANCE.zoomCCChatService
service.endChat()
```
### Log off of the chat service
If you don't want to receive further callbacks from the SDK, call `logoff()`.
```java
ZoomCCChatService chatService = ZoomCCInterface.INSTANCE.getZoomCCChatService();
chatService.logoff();
```
```kotlin
var chatService = ZoomCCInterface.INSTANCE.zoomCCChatService
chatService.logoff()
```
### Release SDK resources
To release SDK resources, call `releaseZoomCCService` in `onDestroy()`.
```java
protected void onDestroy() {
ZoomCCInterface.INSTANCE.releaseZoomCCService("ChatEntryId");
super.onDestroy();
}
```
```kotlin
override fun onDestroy() {
ZoomCCInterface.INSTANCE.releaseZoomCCService("ChatEntryId")
super.onDestroy()
}
```
## Forcibly end an active chat engagement
To forcibly end a chat engagement, call the `endChat` method. This closes the chat view presented by the SDK and returns control to your application. Any service instances created by the Contact Center SDK for this chat will then be released.
```java
ZoomCCChatService service = ZoomCCInterface.INSTANCE.getZoomCCChatService();
service.endChat();
```
```kotlin
val service = ZoomCCInterface.INSTANCE.zoomCCChatService
service.endChat()
```
## Add Zoom Virtual Agent
To add a Virtual Agent, call the `getZoomCCZVAService` (Java) or `zoomCCZVAService` (Kotlin) function to get `ZoomCCChatService` and initialize with the `ZoomCCItem` class. Then, call `fetchUI` to open the chat view.
### Show the chat view controller
```java
ZoomCCChatService service = ZoomCCInterface.INSTANCE.getZoomCCZVAService();
ZoomCCItem item = new ZoomCCItem(ZVAEntryId, null, ZoomCCIInterfaceType.ZVA, CCServerType.CCServerWWW);
service.init(item);
service.fetchUI();
```
```kotlin
val service = ZoomCCInterface.INSTANCE.zoomCCZVAService
val item = ZoomCCItem(
entryId = ZVAEntryId,
sdkType = ZoomCCIInterfaceType.ZVA,
serverType = CCServerType.CCServerWWW
)
service.init(item)
service.fetchUI()
```
### Pass engagement variables (optional)
To pass host application context variables to a Virtual Agent engagement, configure custom variables in Contact Center Management and set its value source to **Application**. Before calling `init()`, use the configured `attributeName` as the key and the application-provided value as the map value in `ZoomCCItem.engagementVariables`.
> **Missing variables**
>
> Missing variables do not block the engagement flow, but nodes that depend on them may not work as expected.
```java
ZoomCCChatService service = ZoomCCInterface.INSTANCE.getZoomCCZVAService();
ZoomCCItem item = new ZoomCCItem(ZVAEntryId, null, ZoomCCIInterfaceType.ZVA, CCServerType.CCServerWWW);
item.setEngagementVariables(Map.of("customAttribute", "customValue"));
service.init(item);
service.fetchUI();
```
```kotlin
val item = ZoomCCItem(
entryId = ZVAEntryId,
sdkType = ZoomCCIInterfaceType.ZVA,
serverType = CCServerType.CCServerWWW
).apply {
engagementVariables = mapOf("customAttribute" to "customValue")
}
service.init(item)
service.fetchUI()
```
### End chat
```java
ZoomCCChatService service = ZoomCCInterface.INSTANCE.getZoomCCZVAService();
service.endChat();
```
```kotlin
val service = ZoomCCInterface.INSTANCE.zoomCCZVAService
service.endChat()
```
To stop receiving callbacks from the SDK, call `logoff()`.
```java
ZoomCCChatService service = ZoomCCInterface.INSTANCE.getZoomCCZVAService();
service.logoff();
```
```kotlin
var service = ZoomCCInterface.INSTANCE.zoomCCZVAService
service.logoff()
```
To release SDK resources, call `releaseZoomCCService` in `onDestroy()`.
```java
protected void onDestroy() {
ZoomCCInterface.INSTANCE.releaseZoomCCService("ZVAEntryId");
super.onDestroy();
}
```
```kotlin
override fun onDestroy() {
ZoomCCInterface.INSTANCE.releaseZoomCCService("ZVAEntryId")
super.onDestroy()
}
```
## Forcibly end an active Virtual Agent engagement
To forcibly end a Virtual Agent engagement, call the endChat method. This closes the chat view presented by the SDK and returns control to your application. Any service instances created by the Contact Center SDK for this chat are released.
```java
ZoomCCChatService service = ZoomCCInterface.INSTANCE.getZoomCCZVAService
();
service.endChat();
```
```kotlin
val service = ZoomCCInterface.INSTANCE.zoomCCZVAService
service.endChat()
```
## Embed chat in Android apps
To embed chat functionality in your Android apps, first create a webview and give it accesses. Next, pass the data to the webview, then pass control back to the app. Finally, allow file downloads.
### Create a webview
In Android Studio, go to your `manifests/AndroidManifest.xml` file and add this config to let the app access the internet.
```xml
```
To support sending files, add this line to your `AndroidManifest.xml` file.
```xml
```
To create a webview component, add this code in your `layout/fragment_second.xml` file.
```xml
```
To add an init to the webview, add this code in your `SecondFragment.kt` file.
```java
public class SecondFragment extends Fragment {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initView();
}
private void initView() {
WebSettings webSettings = binding.webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setDomStorageEnabled(true);
if (viewModel.getUrl().getValue() != null) {
binding.webView.loadUrl(viewModel.getUrl().getValue());
}
}
}
```
```kotlin
class SecondFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
}
private fun initView() {
val webSettings: WebSettings = binding.webView.settings
webSettings.javaScriptEnabled = true
webSettings.domStorageEnabled = true
viewModel.url.value?.let {
binding.webView.loadUrl(it)
}
}
}
```
### Pass data to the webview
In certain situations, such as specifying which language to use or passing helpful metadata like the user's language, first and last name, and email, you need to pass data to the WebSDK running in the webview,. Do this by setting JS variables on the webpage when launching the webview. Put all your variables in the window.zoomCampaignSdkConfig object. To pass the data directly into tickets as certain custom fields, use the custom field ID as the variable name.
```java
binding.webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
if (url.equals(viewModel.getUrl().getValue())) {
injectJavaScriptFunction();
}
}
});
private void injectJavaScriptFunction() {
String url = "javascript: window.zoomCampaignSdkConfig = window.zoomCampaignSdkConfig ||{\n" +
"language: 'user language', \n" +
"firstName: 'user first name', \n" +
"lastName: 'user last name', \n" +
"nickName: 'user nick name', \n" +
"address: 'user address', \n" +
"company: 'user company', \n" +
"email: 'user email', \n" +
"phoneNumber: 'user phone number'};";
binding.webView.loadUrl(url);
}
```
```kotlin
binding.webView.webViewClient = object :WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
if (url == viewModel.url.value) {
injectJavaScriptFunction()
}
}
}
private fun injectJavaScriptFunction() {
var url = "javascript: window.zoomCampaignSdkConfig = window.zoomCampaignSdkConfig ||{\n" +
"language: 'user language', \n" +
"firstName: 'user first name', \n" +
"lastName: 'user last name', \n" +
"nickName: 'user nick name', \n" +
"address: 'user address', \n" +
"company: 'user company', \n" +
"email: 'user email', \n" +
"phoneNumber: 'user phone number'};"
binding.webView.loadUrl(url)
}
```
Currently, for the key `language` in the transmit data, we support these languages:
```plaintext
"en-US"
"da-DK"
"de-DE"
"en-AU"
"en-GB"
"en-NZ"
"es-ES"
"es-MX"
"es-US"
"fr-CA"
"fr-FR"
"id-ID"
"it-IT"
"ja-JP"
"ko-KR"
"nl-NL"
"pl-PL"
"pt-BR"
"pt-PT"
"ro-RO"
"ru-RU"
"sv-SE"
"tr-TR"
"vi-VN"
"zh-CN"
"zh-TW"
"ar-AE"
"ar-SA"
"ca-ES"
"cs-CZ"
"fi-FI"
"he-IL"
"hi-IN"
"ka-GE"
"ms-MY"
"nb-NO"
"th-TH"
```
### Pass control back to the app
When a user wants to end a chat, the webview needs to pass control back to the native app.
This happens through another callback function where the WebSDK modal calls when that
**End Chat** button is clicked. Use this code to handle that callback.
```java
// ...
binding.webView.addJavascriptInterface(this, EXIT_HANDLER_NAME);
// ...
@JavascriptInterface
public void handleExit() {
new Handler(Looper.getMainLooper()).post(() -> NavHostFragment.findNavController(this).popBackStack());
}
private void injectJavaScriptFunction() {
String url = "javascript: window.addEventListener('zoomCampaignSdk:ready', () => {" +
"if (window.zoomCampaignSdk) {" +
"window.zoomCampaignSdk.native = {" +
"exitHandler: {" +
"handle: function() {"+
EXIT_HANDLER_NAME + ".handleExit();}}};}});";
binding.webView.loadUrl(url);
}
@Override
public void onDestroyView() {
binding.webView.removeJavascriptInterface(EXIT_HANDLER_NAME);
super.onDestroyView();
}
```
```kotlin
// ...
binding.webView.addJavascriptInterface(this, EXIT_HANDLER_NAME)
// ...
private fun injectJavaScriptFunction() {
var url = "javascript: window.addEventListener('zoomCampaignSdk:ready', () => {" +
"if (window.zoomCampaignSdk) {" +
"window.zoomCampaignSdk.native = {" +
"exitHandler: {" +
"handle: function() {"+
EXIT_HANDLER_NAME + ".handleExit();}}};}});"
binding.webView.loadUrl(url)
}
@JavascriptInterface
fun handleExit() {
Handler(Looper.getMainLooper()).post {
findNavController().popBackStack()
}
}
override fun onDestroyView() {
binding.webView.removeJavascriptInterface(EXIT_HANDLER_NAME)
super.onDestroyView()
}
companion object {
private const val EXIT_HANDLER_NAME = "exitHandler"
}
```
### Allow attachments or file downloads
To let users send files from their local device or download files from a URL in a chat, add this code to your `SecondFragment.kt` file.
```java
public class SecondFragment extends Fragment {
private static final int REQUEST_CODE_INTENT = 1;
private ValueCallback mFilePathCallback = null;
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initView();
}
private void initView() {
WebSettings webSettings = binding.webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setDomStorageEnabled(true);
binding.webView.addJavascriptInterface(this, EXIT_HANDLER_NAME);
binding.webView.setWebChromeClient( new WebChromeClient() {
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
Log.d("WebView", consoleMessage.message());
return true;
}
//For Android 5.0 above
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) {
return SecondFragment.this.onShowFileChooser(
webView, filePathCallback, fileChooserParams
);
}
});
binding.webView.setDownloadListener((url, userAgent, contentDisposition, mimetype, contentLength) -> downloadFileFromUrl(url));
if (viewModel.getUrl().getValue() != null) {
binding.webView.loadUrl(viewModel.getUrl().getValue());
}
}
private void downloadFileFromUrl(@Nullable String url) {
// Write your code logic to download file from url, you can use DownloadManager, Retrofit or any other tools.
}
private boolean onShowFileChooser(
WebView webView, ValueCallback filePathCallback, WebChromeClient.FileChooserParams fileChooserParams
) {
mFilePathCallback.onReceiveValue(null);
mFilePathCallback = filePathCallback;
Intent intent = fileChooserParams.createIntent();
startActivityForResult(intent, REQUEST_CODE_INTENT);
return true;
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_INTENT) {
Uri[] uris = WebChromeClient.FileChooserParams.parseResult(resultCode, data);
mFilePathCallback.onReceiveValue(uris);
mFilePathCallback = null;
}
}
}
```
```kotlin
class SecondFragment : Fragment() {
companion object {
private const val REQUEST_CODE_INTENT = 1
}
private var mFilePathCallback: ValueCallback>? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
}
private fun initView() {
val webSettings: WebSettings = binding.webView.settings
webSettings.javaScriptEnabled = true
webSettings.domStorageEnabled = true
binding.webView.addJavascriptInterface(this, EXIT_HANDLER_NAME)
binding.webView.webChromeClient = object : WebChromeClient() {
override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
Log.d("WebView", consoleMessage.message())
return true
}
//For Android 5.0 above
override fun onShowFileChooser(
webView: WebView?,
filePathCallback: ValueCallback>?,
fileChooserParams: FileChooserParams?
): Boolean {
return this@SecondFragment.onShowFileChooser(
webView, filePathCallback, fileChooserParams
)
}
}
binding.webView.setDownloadListener { url, _, _, _, _ ->
downloadFileFromUrl(url)
}
viewModel.url.value?.let {
binding.webView.loadUrl(it)
}
}
private fun downloadFileFromUrl(url: String?) {
// Write your code logic to download file from url, you can use DownloadManager, Retrofit or any other tools.
}
fun onShowFileChooser(
webView: WebView?,
filePathCallback: ValueCallback>?,
fileChooserParams: WebChromeClient.FileChooserParams?
): Boolean {
mFilePathCallback?.onReceiveValue(null)
mFilePathCallback = filePathCallback
fileChooserParams?.createIntent()?.let {
startActivityForResult(it, REQUEST_CODE_INTENT)
}
return true
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_CODE_INTENT -> {
val results = WebChromeClient.FileChooserParams.parseResult(resultCode, data)
mFilePathCallback?.onReceiveValue(results)
mFilePathCallback = null
}
}
}
}
```
## Callbacks
These callbacks are available for both chat and video.
Implement listeners and see the available callback functions.
### Implement a listener
To subscribe to these events, define your own instance of the `ZoomCCChatListener` object and add it to the SDK by calling the `addListener` function. See how to implement this listener and assign it to the SDK instance, along with examples of the event handlers associated with the SDK.
#### Add a listener
```java
ZoomCCChatService service = ZoomCCInterface.INSTANCE.getZoomCCChatService();
service.addListener(your listener);
```
```kotlin
val service = ZoomCCInterface.INSTANCE.zoomCCChatService
service.addListener(your listener)
```
### Callback functions
These examples show callback functions provided by the Contact Center SDK. Use these functions and implement any additional operations as needed after receiving the result of the callback function.
#### `onEngagementVariablesRequired`
Called after login when the engagement application variables are known. Each map can contain `variableGroupName`, `variableName`, and `attributeName`. Use `attributeName` as the key in `ZoomCCItem.engagementVariables`. This callback only reports the variable definitions; the SDK sends matching values when the engagement is created.
- `variables` - (`List