Skip to content

Notification Support

Kindly notification support in handover

First provide the SDK with the FCM token using:

KindlySDK.saveNotificationToken(token = "firebase_token")

When a push notification is received

Forward the notification to SDK using

KindlySDK.handleNotification(remoteMessage = remoteMessage)

The SDK will double-check if notification type is a Kindly notification before handling it.

Check if a notification is from Kindly

To determine if a notification should be handled by the Kindly SDK or another notification provider, use the following helper method:

val isKindlyNotification = KindlySDK.isKindlyNotification(remoteMessage)

This can be particularly useful when integrating with multiple push notification providers. Example usage:

override fun onMessageReceived(remoteMessage: RemoteMessage) {
    if (KindlySDK.isKindlyNotification(remoteMessage)) {
        // Handle with Kindly SDK
        KindlySDK.handleNotification(remoteMessage)
    } else {
        // Handle with another notification provider (e.g., Braze, mParticle)
        otherNotificationProvider.handleNotification(remoteMessage)
    }
}

When the SDK Displays the Notification

Kindly silent pushes are encrypted; the SDK decrypts each one and posts a system notification with the decrypted title/body — except when the user is already actively reading the chat conversation. The display rule is:

App state Active screen SDK presents the banner?
Background any ✅ Yes
Foreground Chat conversation ❌ No (the user can already see the message)
Foreground Settings, language, image preview, host-app screens, etc. ✅ Yes

Heads-up display in the foreground requires the kindly_channel notification channel to remain at IMPORTANCE_HIGH (the SDK creates it that way) and the user to have granted the POST_NOTIFICATIONS runtime permission on Android 13+.

Intercepting Notifications (shouldHandleNotification)

Every Kindly silent push that the SDK successfully decrypts is forwarded to the host app via the shouldHandleNotification(notification) method on KindlySDKInteraction — regardless of foreground/background state or which screen is on top. Use it to run side effects (analytics, logging, in-app indicators) or to take over the presentation entirely with your own UI.

Scenario Result
Interface not set SDK presents the notification (subject to the display rule above)
shouldHandleNotification returns true (default) SDK presents the notification (subject to the display rule above)
shouldHandleNotification returns false SDK does not present anything — your app handles it

The interface receives a ExternalNotification containing the decrypted fields plus the original RemoteMessage.data map for any extra fields your backend attached:

data class ExternalNotification(
    val id: String,                   // decrypted chat_id
    val title: String,                // decrypted title
    val body: String,                 // decrypted body
    val data: Map<String, String>,    // original RemoteMessage.data
)

Implementation

KindlySDK.`interface` = object : KindlySDKInteraction {
    override fun didPressButton(chatButton: ButtonChat, chatLog: List<MessageChat>) {
        // Handle button press if needed
    }

    override fun shouldHandleNotification(notification: ExternalNotification): Boolean {
        // Always log every Kindly notification, regardless of who presents it
        Analytics.track("kindly_notification_received", notification.id)

        // Example: take over presentation when the app is in a custom in-app inbox
        if (isShowingCustomInbox) {
            customInbox.add(notification)
            return false   // SDK does not present its banner
        }

        return true        // let the SDK present the banner
    }
}

Notes

  • The method has a default implementation that returns true, so you only need to override it if you want to observe or intercept notifications.
  • The callback fires for every successfully decrypted Kindly push — it is not gated by the SDK's foreground/chat-screen display rule. Use it for analytics that need to run even when the SDK is on screen.
  • The polarity matches shouldHandleLink: returning true means "the SDK should handle this", returning false means "I'll handle it myself".
  • Returning false only suppresses the system notification — the underlying message has already been delivered to the chat session via the websocket / /latest endpoint, so it will appear in the chat history when the user opens it.