Migration Guide
Migration Guide
Migration from Classic SDKs to Zendesk SDK
Starting with the Classic SDK update of Messaging 5.3.0, we improved the compatibility with Zendesk SDL to allow for easier migration from Classic SDK to Zendesk SDK.
Only the versions of Classic SDK using Messaging 5.3.0
and onward are compatible with this migration.
Note: messaging
doesn't refer to the messaging channel, this is the name of the Classic SDK UI.
Some features require additional steps in order to function properly when using both Classics and Zendesk SDK.
Push notifications for Chat SDK and Zendesk SDK in the same app
The following steps will guide you, or your development team, to set up push notifications for both Chat SDK and Zendesk SDK in the same app.
Step 1 - Setting up push notifications in Firebase
Set up Firebase cloud messaging in your application so your application is capable of receiving push notifications from Firebase.
You also have to set up the Zendesk side configuration, as documented here:
Step 2 - Create a Firebase Service for both the Zendesk SDK and Chat SDK in your app
In order to receive Push Notifications for both SDKs at the same time, you must create a service for each SDK. The snippets below demonstrate how to create a service for each of the SDK.
Note: This implementation does not work with the default Zendesk SDK Push.
Snippet for the Zendesk SDK Push service:
ZendeskSDKFirebaseMessagingService
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import zendesk.logger.Logger
import zendesk.messaging.android.push.PushNotifications
import zendesk.messaging.android.push.PushResponsibility
class ZendeskSDKFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
Logger.d(LOG_TAG, "Received new FCM push: %s", remoteMessage.data)
when (PushNotifications.shouldBeDisplayed(remoteMessage.data)) {
PushResponsibility.MESSAGING_SHOULD_DISPLAY -> {
// This push belongs to Messaging and the SDK is able to display it to the end user
Logger.d(LOG_TAG, "onMessageReceived: MESSAGING_SHOULD_DISPLAY")
PushNotifications.displayNotification(context = this, messageData = remoteMessage.data)
}
PushResponsibility.MESSAGING_SHOULD_NOT_DISPLAY -> {
// This push belongs to Messaging but it should not be displayed to the end user
Logger.d(LOG_TAG, "onMessageReceived: MESSAGING_SHOULD_NOT_DISPLAY")
}
PushResponsibility.NOT_FROM_MESSAGING -> {
// This push does not belong to Messaging
Logger.d(LOG_TAG, "onMessageReceived: NOT_FROM_MESSAGING")
}
}
}
override fun onNewToken(newToken: String) {
Logger.d(LOG_TAG, "Refreshed token: %s", newToken)
PushNotifications.updatePushNotificationToken(newToken)
}
private companion object {
const val LOG_TAG = "ZendeskSDKFirebaseMessagingService"
}
}
Snippet for the Classic Chat SDK Push service:
ChatSDKFirebaseMessagingService
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.os.Build
import androidx.core.app.NotificationCompat
import com.google.firebase.messaging.FirebaseMessaging
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.zendesk.logger.Logger
import com.zendesk.service.ErrorResponse
import com.zendesk.service.ZendeskCallback
import zendesk.chat.Chat
import zendesk.chat.ChatConfiguration
import zendesk.chat.ChatEngine
import zendesk.chat.PushData
import zendesk.classic.messaging.MessagingActivity
import zendesk.sample.sdksintegration.R
class ChatSDKFirebaseMessagingService : FirebaseMessagingService() {
override fun onNewToken(token: String) {
Logger.d(LOG_TAG, "Refreshed token: %s", token)
}
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
Logger.d(LOG_TAG, "Message received: %s", remoteMessage.data)
handleChatPushData(remoteMessage)
}
private fun handleChatPushData(remoteMessage: RemoteMessage) {
val pushNotificationsProvider = Chat.INSTANCE.providers()?.pushNotificationsProvider()
if (pushNotificationsProvider == null) {
Logger.e(LOG_TAG, "Chat not initialized, unable to process received message")
return
}
pushNotificationsProvider.processPushNotification(remoteMessage.data)?.let { pushData ->
when (pushData.type) {
PushData.Type.MESSAGE -> pushData.message?.let { showNotification(it) }
PushData.Type.END -> showNotification("Session has ended")
}
}
}
private fun showNotification(contentText: String, contentTitle: String = SOURCE_CHAT) {
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channelId = applicationContext.resources.getString(R.string.app_name)
val pendingIntent = setOpenNotificationIntent()
createNotificationChannel(notificationManager, channelId)
val notification = NotificationCompat.Builder(applicationContext, channelId)
.setSmallIcon(R.drawable.ic_expand_more)
.setDefaults(Notification.DEFAULT_ALL)
.setContentTitle(contentTitle)
.setContentText(contentText)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.build()
notificationManager.notify(NOTIFICATION_ID, notification)
}
/**
* Sets a [PendingIntent] to be triggered when the notification has been tapped.
*
* @return the the [MessagingActivity] intent if it exists, intent to lunch instead.
*/
private fun setOpenNotificationIntent(): PendingIntent {
val chatConfiguration = ChatConfiguration.builder()
.withAgentAvailabilityEnabled(false)
.build()
val messagingActivity = MessagingActivity.builder()
.withEngines(ChatEngine.engine())
.intent(this, chatConfiguration)
val intentToLaunch = messagingActivity ?: packageManager.getLaunchIntentForPackage(packageName)
val flags = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
} else {
PendingIntent.FLAG_ONE_SHOT
}
return PendingIntent.getActivity(
this,
INTENT_REQUEST_CODE,
intentToLaunch,
flags
)
}
private fun createNotificationChannel(notificationManager: NotificationManager, channelId: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = getString(R.string.app_name)
val importance = NotificationManager.IMPORTANCE_HIGH
val channel = NotificationChannel(channelId, name, importance)
notificationManager.createNotificationChannel(channel)
}
}
companion object {
private const val LOG_TAG = "ChatSDKFirebaseMessagingService"
private const val NOTIFICATION_ID = 12345
private const val INTENT_REQUEST_CODE = 0
private const val SOURCE_CHAT = "Chat"
fun init() {
FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
if (task.isSuccessful) {
task.result?.let {
registerTokenForChat(it)
}
} else {
Logger.e(LOG_TAG, "Failed to retrieve FCM token")
}
}
}
private fun registerTokenForChat(token: String) {
val pushNotificationsProvider = Chat.INSTANCE.providers()?.pushNotificationsProvider()
if (pushNotificationsProvider == null) {
Logger.e(LOG_TAG, "Chat not initialized, unable to register token")
return
}
pushNotificationsProvider.registerPushToken(
token, object : ZendeskCallback<Void?>() {
override fun onSuccess(result: Void?) {
Logger.d(LOG_TAG, "Chat Push Enabled: %s", token)
}
override fun onError(error: ErrorResponse?) {
Logger.e(LOG_TAG, "Chat Error Enable Push\nReason: ${error?.responseBody}")
}
}
)
}
}
}
Step 3 - Create a Firebase Proxy Service in your app
This is a helper class that allows registering both services ZendeskSDKFirebaseMessagingService
and ChatSDKFirebaseMessagingService
in your app, so each SDK is able to receive push notifications.
FirebaseMessagingProxyService
import android.app.Service
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import java.lang.reflect.Field
class FirebaseMessagingProxyService : FirebaseMessagingService() {
private val messagingServices: List<FirebaseMessagingService> by lazy {
listOf(
ZendeskSDKFirebaseMessagingService(),
ChatSDKFirebaseMessagingService()
).onEach { it.injectContext(this) }
}
override fun onNewToken(token: String) {
super.onNewToken(token)
messagingServices.forEach { it.onNewToken(token) }
}
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
messagingServices.forEach { it.onMessageReceived(remoteMessage) }
}
override fun onDeletedMessages() {
super.onDeletedMessages()
messagingServices.forEach { it.onDeletedMessages() }
}
override fun onMessageSent(message: String) {
super.onMessageSent(message)
messagingServices.forEach { it.onMessageSent(message) }
}
override fun onSendError(message: String, e: Exception) {
super.onSendError(message, e)
messagingServices.forEach { it.onSendError(message, e) }
}
}
private fun <T : Service> T.injectContext(context: T, func: T.() -> Unit = {}) {
setField("mBase", context)
func()
}
private fun Class<*>.findDeclaredField(name: String): Field? {
var clazz: Class<*>? = this
do {
try {
return clazz?.getDeclaredField(name)
} catch (_: Throwable) {
}
clazz = clazz?.superclass
} while (clazz != null)
return null
}
private fun Any.setField(name: String, value: Any): Boolean =
javaClass.findDeclaredField(name)?.let {
try {
it.isAccessible = true
it.set(this, value)
true
} catch (e: Throwable) {
false
}
} ?: false
Step 4 - Register the Firebase Proxy Service in your AndroidManifest.xml file
<application>
<!-- Use the Firebase proxy messaging service -->
<service
android:exported="false"
android:name=".pushnotification.FirebaseMessagingProxyService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
Migrating from version 1.x.x to 2.x.x
The Zendesk SDK version 2.x.x and above has been upgraded in order to enable us provide flexibility in what capabilities are included in your product when you integrate the SDK. There are some new and exciting capabilities being added which will enable you to engage with your customers better.
Due to the updates mentioned above, a migration is required for any integrations that are using any version of the SDK below 2.0.0.
SDK initialization moved from the Messaging
module to the Zendesk
module. See Initialize the SDK for more details.
Initialization snippet now accepts a factory implementation of messaging, and errors are returned as a Throwable
object instead of a ZendeskError
object.
The following Messaging
functions have also been migrated to Zendesk
.
Old | New |
---|---|
Messaging.initialize(...) | Zendesk.initialize(...) |
Messaging.instance().showMessaging() | Zendesk.instance.messaging.showMessaging(...) |
Auto migration
You might be able to automatically migrate your initialization snippet to the most recent version using Android Studio suggestions.
The Messaging functions have been deprecated in the latest version and will be removed in a future version on Android.