⚠️ The software is available as a Public Preview with release support for select Microsoft partners and customers. To receive support for your release, contact CC-Mobile-Preview@microsoft.com with your release date and plans. Customers must have an agreement with the Contact Center team to guarantee timely support during the Preview period.
-
Clone the repository
git clone https://github.com/microsoft/ContactCenterMessagingSDK-android.git
-
Open the terminal and run
npm install
in the root folder (the providedpackage.json
is copied to the root folder). -
Download the AAR files (optional)
./gradlew downloadAarFiles
-
Sync/configure the app.
-
Build the app based on your build tools.
-
Run the app.
(These instructions were tested with Gradle 8.7, Android Studio Koala | 2024.1.1 Patch 2, OpenJDK 19.0.2)
-
Open Android Studio and select
File -> Open...
, or from the Android Launcher, selectImport project (Eclipse ADT, Gradle, etc.)
and navigate to the root directory of your project. -
Select the directory or drill into the project and select the
build.gradle
file in the cloned repo. -
Click 'OK' to open the project in Android Studio.
-
A Gradle sync should start. You can force a sync and build the 'app' module as needed.
Build the APK:
./gradlew build
-
Select
Run -> Run 'app'
(orDebug 'app'
) from the menu bar. -
Select the device you wish to run the app on and click 'OK'.
-
Paste Your Script (taken from the Chat Workstream Page) or Add the Required Information:
In your app’s landing screen, you will find input fields where you need to enter:
orgId
orgUrl
widgetId
Alternatively, you can paste a script, which will automatically fill in these details for you.
-
Click on the "Let's Chat" Button:
Once you've entered the required information (or pasted the script), look for a button labeled "Let's Chat" on your screen.
Tap on this button to initiate the chat. The app will connect to the specified chat system and load the widget for you.
-
Start Interacting with the Chat:
After clicking the button, you will see the chat interface appear on the screen.
You can now type messages, send media, or interact with the chat in real-time.
The app will allow you to communicate with customer support or any automated services available.
If you face a build issue related to the namespace for the randombytes
package, update the namespace in its build.gradle
:
namespace 'com.bitgo.randombytes' // (path: node_modules -> react-native-randombytes -> android -> build.gradle)
- About
- Installation
- Initialization
- Messaging Widget
- Core Messaging Framework
- Initialization
- Authentication
- Start Chat
- Get PreChat Survey
- Get Live Chat Config
- Get Current Live Chat Context
- Get Data Masking Rules
- Get Chat Reconnect Context
- Get Conversation Details
- Get Chat Token
- Send Message
- On Typing Event
- On New Message
- On Agent End Session
- Send Customer Typing
- Email Live Chat Transcript
- Get Live Chat Transcript
- End Chat
- Get Agent Availability
- Download File Attachment
- Upload File Attachment
- Post Chat Survey Context
- Get Reconnect Context
- LCWMessagingDelegate
- getConversationDetails Logic in ChatActivity
- Troubleshooting
- Push Notifications
The Dynamics 365 Contact Center Messaging SDK enables developers to add Dynamics 365 Contact Center's messaging to their branded mobile applications.
This SDK contains two components:
- Messaging Widget: A pre-built, fully featured native messaging interface that the customer can use wholesale and easily customize to match their branding. The widget is already integrated with Omnichannel and customers that opt to use it do not need to interact with the Core Messaging Functions for almost all of the messaging lifecycle.
- Core Messaging Framework: Enables a developer to build their own messaging interface. This is a full set of functions that interact with the Omnichannel Messaging APIs which the customer can connect with an existing or new interface in their branded app.
For most messaging programs, we recommend the use of our out of the box Messaging Widget:
- Provides messaging with the least amount of development effort
- Configurable look, feel, and customization options so the widget can be branded easily to match your app. The available options are based on what is available with our Web Live Chat Widget (LCW), so developers that are familiar with our web application can quickly match their existing program
If there are any customizations you need that aren't available in our product, please don't hesitate to reach out to us at CC-Mobile-Preview@microsoft.com and we'll add them to our roadmap.
- Dynamics 365 Contact Center License
- Provisioned test organization, retrieving these variables:
- orgID
- orgURL
- Provisioned Chat Channel & Live Chat Widget configuration, retrieving these variables:
- widgetID/applicationID
- For reference, see:
- Provisioned test organization, retrieving these variables:
- Android SDK/API target minimum of 26 or above
- package.json file (included in sample app)
Adding/Configuring package.json
-
Open your project directory: Navigate to the root folder of your Android project.
-
Initialize npm: Run the following command in the terminal inside your project directory. This will create a new
package.json
file:npm init
-
This will ask a series of questions to configure your
package.json
file, such as:-
Project name
-
Version
-
Description
-
Entry point
-
Repository information (if any)
-
License type
-
You can just hit "Enter" for most prompts to accept the default values, or you can customize the values as you see fit.
-
Alternatively, you can skip all questions by using:
npm init -y
-
-
Install dependencies:
npm install react-native@0.70 npm install react-native-randombytes@3.6.1 npm install react-native-get-random-values@1.8.0
-
Alternatively, you can simply refer/copy
package.json
from a sample app and run:npm install
-
Open the build.gradle.kts (project's) or settings.gradle.kt file. Add below code for dependency resolution
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) repositories { mavenCentral() maven(url = "$rootDir/node_modules") maven(url = "$rootDir/node_modules/react-native/android") maven(url = "$rootDir/node_modules/jsc-android/dist") maven(url = "https://maven.google.com") google() } } include(":randombytes") project(":randombytes").projectDir = file("./node_modules/react-native-randombytes/android") include(":randomvalues") project(":randomvalues").projectDir = file("./node_modules/react-native-get-random-values/android")
-
Open the
build.gradle.kts
(app's) file of your app module. -
Add the SDK dependencies under the dependencies section and other snippet:
repositories { flatDir { dirs 'libs' } } dependencies { implementation(files("libs/ContactCenterMessagingWidget.aar")) implementation(files("libs/OmnichannelChatSDK.aar")) implementation("com.google.code.gson:gson:2.10.1") implementation("com.facebook.react:react-native:+") implementation("org.webkit:android-jsc:+") implementation(project(":randombytes")) implementation(project(":randomvalues")) implementation("com.google.android.flexbox:flexbox:3.0.0") implementation("io.adaptivecards:adaptivecards-android:2.9.0") }
-
You can automate the AAR download task by adding the following code in the app's
build.gradle.kts
:// Download AAR files from GitHub // (Run the command ./gradlew downloadAarFiles) val sdkVersion = "v1.0.1" task("downloadAarFiles") { doLast { println("Download AARs task started...") val aar1Url = "https://github.com/microsoft/ContactCenterMessagingSDK-android/releases/download/$sdkVersion/ContactCenterMessagingWidget.aar" val aar2Url = "https://github.com/microsoft/ContactCenterMessagingSDK-android/releases/download/$sdkVersion/OmnichannelChatSDK.aar" val aar1File = file("${project.rootDir}/app/libs/ContactCenterMessagingWidget.aar") val aar2File = file("${project.rootDir}/app/libs/OmnichannelChatSDK.aar") URL(aar1Url).openStream().use { input -> aar1File.outputStream().use { output -> input.copyTo(output) } } URL(aar2Url).openStream().use { input -> aar2File.outputStream().use { output -> input.copyTo(output) } } } } tasks.named("preBuild") { dependsOn("downloadAarFiles") }
-
Build the app based on your build tools.
-
Run the app.
❗ You can also manually download the AAR files for the desired version
Download AAR files here
❗ Place the AAR files in the
libs
folder
Navigate toapp -> libs
and place the downloaded AAR files there.
If you face build issue related to flexbox dependency, add below code to project level buld.gradle (inside allprojects block)
dependencies{
modules {
module("com.google.android:flexbox") {
replacedBy("com.google.android.flexbox:flexbox")
}
}
}
Initialise the SDK with valid OmnichannelConfig parameters.
val omnichannelConfig = OmnichannelConfig(
orgId = “YOUR_ORG_ID”,
orgUrl = “ORG_URL”,
widgetId = “WIDGET_ID”
)
//Request builder
val lcwOmniChannelConfigBuilder =
LCWOmniChannelConfigBuilder.EngagementBuilder(omnichannelConfig, chatSdkConfig).build()
//Invoking
LiveChatMessaging.getInstance().initialize(this, lcwOmniChannelConfigBuilder, "auth_token", "environment")
Customizations available in the out of the box messaging widget are documented here:
This section describes the messaging lifecycle functions in the SDK.
You need to initialize the Omnichannel SDK with your omnichannel credentials before doing any other operations. You can call this method either at startup, or at the desired point in your application flow.
This requires your org details and authentication details for the customer. See Authentication section for more information.
API
LiveChatMessaging.getInstance().initialize(this, lcwOmniChannelConfigBuilder, "auth_token", "environment")
Builders
val omnichannelConfig = OmnichannelConfig(
orgId = “YOUR_ORG_ID”,
orgUrl = “ORG_URL”,
widgetId = “WIDGET_ID”
)
Dynamics Contact Center always recommends using persistent chat for mobile, which requires customer authentication. You identify the customer during initialization by providing an auth token containing an identifier such as a Dynamics Record ID, with the token signed by your identity provider.
Details on setting up a token are available here: https://learn.microsoft.com/en-us/dynamics365/customer-service/administer/create-chat-auth-settings
This starts a Contact Center session, and is used when the customer opens the messaging app or tries to start a new conversation.
val request = StartChatRequestBuilder().buildStartChatRequestParams(
StartChatOptionalParams(
liveChatContext = // EXISTING chat context data
preChatResponse = // PreChatSurvey response,
customContext =//// EXISTING custom context data
os = "Android",
locale = null,
device = Build.DEVICE,
browser = null,
initContext = null,
reconnectId = reconnectId,
sendDefaultInitContext = false,
isProactiveChat = false,
latitude = null,
longitude = null,
portalContactId = null
)
public void startChat(requestParam, completionHandler) {}
Retrieves the Pre-Chat Survey configured in Customer Service Admin Center for the selected Org and App ID.
LiveChatMessaging.getInstance().getPreChatSurvey(request, completionhandler) {}
Retrieves the customized behavior and style settings from Customer Service Admin Center for the selected Org and App ID.
val optionalRequest = GetLiveChatConfigOptionalParams(
sendCacheHeaders = false, // Boolean
useRuntimeCache = null // Boolean
)
LiveChatMessaging.getInstance(). getLiveChatConfig (optionalRequest, completionhandler) {}
Gets the current live chat context information to be used to reconnect to the same conversation.
LiveChatMessaging.getInstance().getLiveChatContextFromApi(CompletionHandler handler) {}
Retrieves the data masking regex patterns that are configured in the Customer Service Admin Center for the selected Org and App ID. See here: https://learn.microsoft.com/en-us/dynamics365/customer-service/administer/data-masking-settings
LiveChatMessaging.getInstance().getDataMaskingRules(LCWRequest requestParam, CompletionHandler handler) {}
It gets the current reconnectable chat context information to connect to a previous existing chat session. Setting reconnection options is required, see here: https://learn.microsoft.com/en-us/dynamics365/customer-service/administer/configure-reconnect-chat?tabs=customerserviceadmincenter#enable-reconnection-to-a-previous-chat-session
val optionalRequest = LCWChatReconnectContextRequestBuilder().buildChatReconnectContextRequestParams(
reconnectId = ""
)
LiveChatMessaging.getInstance().getChatReconnectContext(optionalRequest)
Gets the details of the current conversation such as its state & when the agent joined the conversation.
val optionalParams = GetConversationDetailsOptionalParams(
liveChatContext = LiveChatContext(), // EXISTING chat context data
)
val optionalRequest =
LCWGetConversationDetailsRequestBuilder().buildGetConversationDetailsRequestParams(params);
LiveChatMessaging.getInstance(). getConversationDetails (optionalRequest)
Retrievs the token used to initiate a chat with Contact Center.
LiveChatMessaging.getInstance().getChatToken (CompletionHandler handler) {}
Sends message to the Agent or Customer Service Rep from the current customer.
val option = ChatSDKMessage(
content = text,
tags = tags, // list of strings (tags)
timestamp = timestamp,
metadata = metadata
)
val messageId = “” //unique message id, or random generated id
val request = LCWSendCustomerMessageRequestBuilder().buildSendCustomerMessageRequestParams(option, messageId)
LiveChatMessaging.getInstance().sendMessage(request, completionHandler) {}
Subscribes to an Agent typing event. These events are sent on start of agent typing but no end event is sent, so only render a typing animation for 3-5 seconds.
LiveChatMessaging.getInstance().onAgentTyping(completionHandler) {}
Subscribes to new incoming messages of the current conversation such as system messages, client messages, agent messages, adaptive cards and attachments.
val optional = OnNewMessageOptionalParams (
rehydrate = true/false
) ;
val request= LCWOnNewMessageRequestBuilder().buildOnNewMessageRequestParams(optional);
LiveChatMessaging.getInstance().onNewMessage(request, copletionalhandler) {}
// Completion handler is optional.
Subscribes to an agent ending the session of the conversation.
LiveChatMessaging.getInstance().onAgentEndSession (completionHandler) {}
Tells the agent the customer is typing. Only sent at start of typing activity, not end.
LiveChatMessaging.getInstance().sendTyping () {}
// OR
val request = LCWSentTypingRequestBuilder().buildSentTypingRequestParams();
LiveChatMessaging.getInstance().onNewMessage(request, copletionalhandler) {}
Sends customer an email with a transcript of the conversation. You must collect and supply the customer's email.
val param= ChatTranscriptBody(
emailAddress = “johnsmith@outlook.com”, // valid email
attachmentMessage: String, // custom body
locale = Locale.getDefault().toString() //optional
)
val optional = EmailLiveChatTranscriptOptionaParams(
liveChatContext = LiveChatContext()
)
val request = LCWEmailLiveChatTranscriptRequestBuilder()
.buildEmailLiveChatTranscriptRequestParams(param, optional)
LiveChatMessaging.getInstance().onAgentEndSession (request, completionHandler) {}
Fetches current conversation transcript data in a JSON. Used for populating the transcript for customers returning to an ongoing conversation.
LiveChatMessaging.getInstance().getLiveChatTranscript (completionHandler) {}
OR
val optional = GetLiveChatTranscriptOptionalParams (
liveChatContext = LiveChatContext()
)
val request = LCWGetLiveChatTranscriptRequestBuilder().buildGetLiveChatTranscriptRequestParams(optional)
LiveChatMessaging.getInstance().getLiveChatTranscript (request, completionHandler) {}
Ends the current Contact Center conversation.
Gets information on whether a queue is available, and whether there are agents available in that queue, as well as queue position and average wait time. This call is only supported in an authenticated chat.
val optional = GetAgentAvailabilityOptionalParams()
val request = LCWGetAgentAvailabilityRequestBuilder().buildAgentAvailabilityRequestParams(param)
LiveChatMessaging.getInstance().getAgentAvailability (request, completionHandler) {}
OR
LiveChatMessaging.getInstance().getAgentAvailability (completionHandler) {}
Downloads the file attachment of the incoming message as a Base64 string response.
val fileMataData= FileMetadata (id, name, size, type, url, fileSharingProtocolType)
val request = LCWDownloadAttachmentRequestBuilder().buildDownloadAttachmentRequestParams(fileMetadata)
LiveChatMessaging.getInstance().downloadFileAttachment(request, completionHandler) {}
Sends a file attachment to the current conversation.
val fileMataData= IFileInfo (name, type, size, data)
val request = LCWUploadAttachmentRequestBuilder().buildUploadAttachmentRequestParams(fileMetadata, “base64String")
LiveChatMessaging.getInstance().uploadFileAttachment(request, completionHandler) {}
Gets the participant type that should be used for the survey and both the default and bot survey details.
LiveChatMessaging.getInstance().getPostChatSurveyContext (request, completionHandler) {}
Retrieves the chat context needed for a reconnect
val request = LCWChatReconnectContextRequestBuilder().buildChatReconnectContextRequestParams(
reconnectId = ""
)
LiveChatMessaging.getInstance(). getChatReconnectContext (request, completionHandler) {}
To enable your application to monitor various SDK events, provide an instance of the LCWMessagingDelegate interface and implement the delegate methods. The following delegate methods are available to capture these events.
Example: LiveChatMessaging.getInstance().setLCWMessagingDelegate(object: LCWMessagingDelegate())
override fun onChatMinimizeButtonClick()
- Called when the chat minimize button is clicked.
override fun onViewDisplayed()
- Called when the chat screen starts to visualize.
override fun onChatInitiated()
- Called when the chat is initiated.
override fun onCustomerChatEnded()
- Called when the chat is ended by the customer.
override fun onAgentChatEnded()
- Called when the agent ends the chat.
override fun onAgentAssigned(content: String)
- Called when a live agent is assigned, also gives related system message.
override fun onLinkClicked()
- Called when a URL link in the message is clicked, also gives the URL.
override fun onNewCustomerMessage(message: ChatSDKMessage)
- Called when a new message has arrived, including system messages.
override fun onNewMessageReceived(message: GetMessageResponse?)
- Called when a new message has arrived, including system messages.
override fun onError(error: ErrorResponse?)
- Called when an error occurs with network, API, or generic issues.
override fun onPreChatSurveyDisplayed()
- Called when a pre-chat survey is displayed.
override fun onPostChatSurveyDisplayed()
- Called when a post-chat survey is displayed.
override fun onChatRestored()
- Called when chat is restored or transcript reloaded.
This code snippet retrieves the details of the current live chat conversation and updates the sample app UI accordingly based on the conversation's state.
- Fetching Conversation Details: The LiveChatMessaging.getInstance().getConversationDetails method is called to fetch the current conversation details. This returns a response wrapped in an ApiResult object.
- UI Updates: Update the chat button state or text depending upon the conversation state, ex. If active-> “Restore Chat” else “New Chat”
LiveChatMessaging.getInstance().getConversationDetails { response ->
runOnUiThread {
OLog.d("ChatActivity getConversationDetails: $response")
when (response) {
is ApiResult.Success -> {
val conversationDetail = response.response as? ConversationDetail
conversationDetail?.state?.let {
when (it) {
ConversationStateEnum.Closed.key,
ConversationStateEnum.WrapUp.key -> {
btnText.setText("Let's Chat")
btnText.setTextColor(ContextCompat.getColor(this@ChatActivity, R.color.messagingThemeBackground))
}
ConversationStateEnum.Active.key -> {
btnText.setText("Restore Chat")
btnText.setTextColor(ContextCompat.getColor(this@ChatActivity, R.color.infoColor))
}
else -> {
btnText.setText("Let's Chat")
btnText.setTextColor(ContextCompat.getColor(this@ChatActivity, R.color.messagingThemeBackground))
}
}
}
OLog.d("ChatActivity@getConversationDetails: ${response.response}")
}
is ApiResult.Error -> {
btnText.setText("Let's Chat")
btnText.setTextColor(ContextCompat.getColor(this@ChatActivity, R.color.messagingThemeBackground))
}
}
}
}
Our Android push notifications require accounts with Azure Notification Hub and Google Firebase.
Instructions:
You will also need:
- The App Id for the workstream used by your application.
- The org_url for your Contact Center dataverse
- An authorization token for your Contact Center dataverse API
⚠️ Configuration is currently only available through the API. In the near future we will add a configuration interface to the Customer Service Admin Center. Your configurations will appear there when it is released and it will not require any changes on your part.
Configuration settings only need to be set once per workstream to enable both Android and iOS applications.
- Create a ChannelInstanceSecret entry:
curl --request POST \
--url https://{{org_url}}/api/data/v9.2/msdyn_channelinstancesecrets \
--header 'authorization: Bearer {{token}}' \
--header 'content-type: application/json' \
--data '{
}'
- Get ChannelInstance Secret Id (msdyn_channelinstancesecretid):
curl --request GET \
--url https://{{org_url}}/api/data/v9.2/msdyn_channelinstancesecrets \
--header 'authorization: Bearer {{token}}'
- Update ChannelInstanceSecret entry with the following values:
curl --request PATCH \
--url 'https://{{org_url}}/api/data/v9.2/msdyn_channelinstancesecrets({{msdyn_channelinstancesecretid}})' \
--header 'authorization: Bearer {{token}}' \
--header 'content-type: application/json' \
--data '{
"msdyn_name": "notificationHubConnectionString",
"msdyn_secretvalue": "{{azurenotificationhub_connection_string}}"
}'
- Create an Azure Notification Hub entry:
curl --request POST \
--url https://{{org_url}}/api/data/v9.2/msdyn_azurenotificationhubs \
--header 'authorization: Bearer {{token}}' \
--header 'content-type: application/json' \
--data '{
}'
- Get Azure Notification Hub Id (msdyn_azurenotificationhubid):
curl --request GET \
--url https://{{org_url}}/api/data/v9.2/msdyn_azurenotificationhubs \
--header 'authorization: Bearer {{token}}'
- Update Azure Notification Hub entry with the following values:
curl --request PATCH \
--url 'https://{{org_url}}/api/data/v9.2/msdyn_azurenotificationhubs({{msdyn_azurenotificationhubid}})' \
--header 'authorization: Bearer {{token}}' \
--header 'content-type: application/json' \
--data '{
"msdyn_connectionstringid@odata.bind": "msdyn_channelinstancesecrets({{msdyn_channelinstancesecretid}})",
"msdyn_defaultnotificationbody": "Default Notification Content",
"msdyn_shownotificationtitle": false,
"msdyn_notificationtitle": null,
"msdyn_showmessagepreview": true,
"msdyn_azurenotificationhubname": "{{azurenotificationhub_name}}"
}'
msdyn_defaultnotificationbody: String, this is the default message shown in preview
msdyn_shownotificationtitle: Bool, defines whether or not we show a title above the preview message
msdyn_notificationtitle: String, the title shown if notificationtitle is true
msdyn_showmessagepreview: Bool, defines whether the rep's message is shown in the push notification. If false, defaultnotificationbody is always used. If true, defaultnotificationbody is only used when the agent sends a message without text, such as an attachment.
msdyn_azurenotificationhubname: String, your hub name in Azure Notification Hub.
- Get LiveChatConfig id (msdyn_livechatconfigid) entry linked to the workstream
curl --request GET \
--url 'https://{{org_url}}/api/data/v9.2/msdyn_livechatconfigs?%24filter=msdyn_widgetappid%2520eq%2520{{widget_app_id}}' \
--header 'authorization: Bearer {{token}}'
- Link the Azure Notification Hub to the LiveChatConfig entry:
curl --request PATCH \
--url 'https://{{org_url}}/api/data/v9.2/msdyn_livechatconfigs({{msdyn_livechatconfigid}})' \
--header 'authorization: Bearer {{token}}' \
--header 'content-type: application/json' \
--data '{
"msdyn_azurenotificationhubid@odata.bind": "msdyn_azurenotificationhubs({{msdyn_azurenotificationhubid}})"
}'
Initializing the Token inside SDK:
Once your FCM project is created and the Firebase setup is complete, you can initialize the SDK within your application.
Add the following line of code to set the device token before launching the chat:
LiveChatMessaging.getInstance().setFcmToken("YOUR_DEVICE_TOKEN")