Skip to main content
Version: 5.21.0

Notification setup

Custom push navigation

To change the behavior when a push notification is tapped (by default, ChatActivity is opened), use PendingIntentCreator. See Configuring the push notification tap action.

Overall flow

Push is delivered only when the SDK's WebSocket connection is inactive (the app is in the background or closed). The points where your app integrates: a FirebaseMessagingService / HmsMessageService subclass (receives push from the OS) and a call to chatCenterUI.handleFCMMessage(...) / handlePushMessage(...) (passes data to the SDK). Then the SDK decides — to update the message list (app in foreground) or to show a system notification (in background).

Permissions

The SDK already declares the required permissions in its AndroidManifest.xml (INTERNET, POST_NOTIFICATIONS, WAKE_LOCK, FOREGROUND_SERVICE, ACCESS_NETWORK_STATE). The Android Gradle Plugin's Manifest Merger correctly merges them with your app's manifest — you do not need to declare them again. The full list is in Android permissions.

Runtime permission POST_NOTIFICATIONS on Android 13+ (API 33)

A manifest declaration alone does not show notifications. On Android 13+, request the permission via ActivityResultContracts.RequestPermission before the user expects their first push. Without an explicit grant, the system silently drops notifications — no SDK error.

// In an Activity
val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
if (!granted) {
// show a rationale or suggest enabling notifications in settings
}
}

// Before the first push subscription
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}

Firebase

FCM server-side setup

The same Firebase project whose google-services.json your app uses is configured by the edna Chat Center administrator in the channel on the server side: the Sender ID (from the Cloud Messaging settings) and the service-account.json (Firebase Console → Project settings → Service accounts → Generate new private key). If the values in the admin panel do not match the local google-services.json — a test push from Firebase Console arrives, but pushes from the edna backend are silently not delivered. Admin panel steps: Connecting the Android mobile chat.

In all the examples below, chatCenterUI is a field of your Application / Activity (the examples use the name MyApp — replace it with your class name; see Quick Start → Step 1).

Initialization order

ChatCenterUI.setFCMToken(...) / setHCMToken(...) can be called at any moment — the token is stored in Preferences and picked up by the SDK after initialization.

chatCenterUI.handleFCMMessage(...) / handlePushMessage(...) require initialization to be complete:

  • With init() (synchronous) — ensure that Application.onCreate has finished before the first push arrives.
  • With initAsync() — defer the handleFCMMessage call until the onInitComplete callback, otherwise the push message will be lost. See Async initialization: initAsync().

Step 1. Create a FirebaseMessagingService

Create a FirebaseMessagingService subclass that forwards the token and messages to the SDK:

class CustomPushFcmIntentService : FirebaseMessagingService() {

override fun onNewToken(token: String) {
super.onNewToken(token)
ChatCenterUI.setFCMToken(token, applicationContext)
}

override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
val chatCenterUI = (application as MyApp).chatCenterUI
chatCenterUI.handleFCMMessage(message.data)
}
}

handleFCMMessage(data) accepts a Map<String, String> and handles only push messages with the origin=threads flag — the rest are silently ignored. An alternative is handlePushMessage(bundle: Bundle) with the same filter.

What's in message.data

The payload structure is determined by the server-side push template setting in the edna Chat Center admin panel. The SDK processes standard fields (origin=threads, message identifiers, preview text) itself; additional keys from the template arrive as strings (nested objects are serialized to a JSON string by the admin panel before sending to Android). See Push notification template setup.

Coexisting with another FirebaseMessagingService

Android allows only one service with the com.google.firebase.MESSAGING_EVENT intent filter. If you already have your own service (for example, for Firebase Analytics or another push source), do not add a second one — add edna proxying inside the existing service:

override fun onNewToken(token: String) {
super.onNewToken(token)
// ... your existing code ...
ChatCenterUI.setFCMToken(token, applicationContext)
}

override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
val chatCenterUI = (application as MyApp).chatCenterUI
if (message.data["origin"] == "threads") {
chatCenterUI.handleFCMMessage(message.data)
} else {
// your handler for non-edna push
}
}

Step 2. Add google-services.json

Two options are possible:

  • Using your Firebase account. Generate google-services.json in the Firebase console. Pass two values from the same Firebase project to the edna Chat Center administrator:

    • Sender ID — from Firebase Console → Project settings → Cloud Messaging;
    • service-account.json — from Firebase Console → Project settings → Service accounts → Generate new private key.

    The administrator enters them into the channel on the "Notifications" tab — step-by-step instructions: Connecting the Android mobile chat.

  • Using edna's Firebase account. Provide your app's applicationId — in return, you will receive a google-services.json.

Add google-services.json to the root of the app module (next to build.gradle). The module's applicationId must match the App_package field in the channel in the edna admin panel.

Step 3. Register the service in the manifest

<service android:name=".push.CustomPushFcmIntentService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>

Notification channel (Android 8+)

The SDK creates the notification channel automatically on Android 8.0+. The channel priority is configured via ChatConfig.notificationImportance (default IMPORTANCE_DEFAULT). The field is marked @RequiresApi(N) — if your app's minSdk is below 24, wrap setting the value in a Build.VERSION.SDK_INT >= Build.VERSION_CODES.N check. The full value table and nuances are in Advanced → Setting the notification channel priority.

Logout and token

On logout(), the SDK clears the locally stored FCM/HCM token. In a multi-account scenario, after the next authorization, pass the token to the SDK again via ChatCenterUI.setFCMToken(...) / setHCMToken(...).

Huawei Mobile Services (HMS)

For Huawei devices without Google Play Services, use HMS Push Kit.

Huawei channel in the admin panel

Before integration, make sure a separate "Mobile chat → Huawei" channel exists in edna Chat Center. The channel has App_package (= the applicationId of the Huawei build module), and on the "Notifications" tab — Huawei App ID and Huawei App Secret (these are the OAuth 2.0 Client ID and Client secret from AppGallery Connect → Project settings → App information, not the Push Kit App ID). Admin panel steps: Connecting the Huawei mobile chat.

Step 1. Add HMS dependencies

implementation 'com.huawei.hms:push:6.11.0.300'

Also add the com.huawei.agconnect plugin to build.gradle and the agconnect-services.json file to the app/ module.

Where to get Huawei credentials
  • agconnect-services.json — from AppGallery Connect → Project settings → App information → the agconnect-services.json section.
  • Huawei App ID and Huawei App Secret for the edna admin panel — in the same place, in the OAuth 2.0 Client ID section (Client ID and Client secret fields).

If the values in the edna admin panel are not updated after OAuth keys are re-created in AppGallery Connect, the backend stops authorizing in HMS Push Kit, and pushes are not delivered without an explicit error in logs. See Connecting the Huawei mobile chat.

Step 2. Create a push notification handler service

import android.os.Bundle
import android.util.Base64
import com.huawei.hms.push.HmsMessageService
import com.huawei.hms.push.RemoteMessage
import edna.chatcenter.ui.visual.core.ChatCenterUI
import org.json.JSONObject

class CustomPushHcmIntentService : HmsMessageService() {

override fun onNewToken(token: String) {
super.onNewToken(token)
ChatCenterUI.setHCMToken(token, applicationContext)
}

override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
val chatCenterUI = (application as MyApp).chatCenterUI
chatCenterUI.handlePushMessage(base64JsonStringToBundle(message.data))
}

private fun base64JsonStringToBundle(base64Str: String): Bundle = try {
val json = JSONObject(String(Base64.decode(base64Str, 0)))
Bundle().apply {
json.keys().forEach { key -> putString(key, json.getString(key)) }
}
} catch (_: Exception) {
Bundle()
}
}

HMS delivers the payload in RemoteMessage.data as a base64 string (with a JSON object inside), so before passing it to the SDK, you need to decode it into a Bundle and use handlePushMessage(bundle), not handleFCMMessage(map).

Step 3. Register the service in AndroidManifest.xml

<service android:name=".push.CustomPushHcmIntentService"
android:exported="false">
<intent-filter>
<action android:name="com.huawei.push.action.MESSAGING_EVENT" />
</intent-filter>
</service>

Step 4. Request the token during initialization

At app startup, request the HMS token:

import com.huawei.agconnect.AGConnectOptionsBuilder
import com.huawei.hms.aaid.HmsInstanceId
import com.huawei.hms.common.ApiException

fun requestHmsToken(context: Context) {
Thread {
try {
val appId = AGConnectOptionsBuilder().build(context).getString("client/app_id")
val token = HmsInstanceId.getInstance(context).getToken(appId, "HCM")
if (!token.isNullOrEmpty()) {
ChatCenterUI.setHCMToken(token, context)
}
} catch (e: ApiException) {
Log.e("HMS", "Token request failed", e)
}
}.start()
}

appId is taken from agconnect-services.json via AGConnectOptionsBuilder — no need to hardcode the value in code.

A complete HMS integration example is available in the demo app.

Verifying the integration

  1. A test push arrives.

    • FCM: Firebase Console → Cloud MessagingSend test message, in the Additional options → Custom data section add the key origin with value threads.
    • HMS: AppGallery Connect → Push KitAdd notification → the Custom message section with the pair origin=threads.

    Without the origin=threads pair, the SDK ignores the push. If in production you need to pass additional payload fields (e.g., the externalId of a message for a deep link), the setup is done on the server side via the push template: Push notification template setup. Custom fields are accessible from message.data before calling handleFCMMessage(...), or can be read by your PendingIntentCreator.

  2. The notification is visible on the device (Android 13+). If the push arrives but the banner does not appear, check that the runtime POST_NOTIFICATIONS permission has been granted by the user (see the warning in the Permissions section).

  3. The initialization order is respected. If the first push after a cold start is lost, ChatCenterUI.init(...) did not complete before handleFCMMessage(...) was called. See Async initialization.