Настройка уведомлений
Чтобы изменить поведение при нажатии на push-уведомление (по умолчанию открывается ChatActivity), используйте PendingIntentCreator. См. Настройка перехода по push-уведомлению.
Разрешения
SDK уже объявляет необходимые permissions в своём AndroidManifest.xml (INTERNET, POST_NOTIFICATIONS, WAKE_LOCK, FOREGROUND_SERVICE, ACCESS_NETWORK_STATE). Manifest Merger Android Gradle Plugin корректно объединит их с манифестом вашего приложения — повторно объявлять их не нужно. Полный список — в Разрешения Android (Permissions).
POST_NOTIFICATIONS на Android 13+ (API 33)Манифестное объявление само по себе не показывает уведомления. На Android 13+ запросите разрешение через ActivityResultContracts.RequestPermission до того, как пользователь ожидает первый push. Без явного grant система молча дропает уведомления — никакой ошибки в SDK не будет.
// В Activity
val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
if (!granted) {
// покажите rationale или предложите включить уведомления в настройках
}
}
// перед первой подпиской на push
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
Firebase
Тот же Firebase-проект, чьим google-services.json пользуется приложение, администратор edna Chat Center заносит в канал на стороне сервера: Sender ID (из настроек Cloud Messaging) и service-account.json (Firebase Console → Project settings → Service accounts → Generate new private key). Если значения в админке не совпадают с локальным google-services.json — тестовый push из Firebase Console приходит, а push от backend edna молча не доставляется. Шаги в админке: Подключение мобильного чата Android.
Во всех примерах ниже chatCenterUI — поле вашего Application / Activity (в примерах используется имя MyApp — замените на имя вашего класса; см. Quick Start → Шаг 1).
ChatCenterUI.setFCMToken(...) / setHCMToken(...) можно вызывать в любой момент — токен сохраняется в Preferences и подхватывается SDK после инициализации.
chatCenterUI.handleFCMMessage(...) / handlePushMessage(...) требуют завершённой инициализации:
- При
init()(синхронный) — гарантируйте, чтоApplication.onCreateзавершился до получения первого push. - При
initAsync()— отложите вызовhandleFCMMessageдо колбэкаonInitComplete, иначе push-сообщение будет потеряно. См. Асинхронная инициализация:initAsync().
Шаг 1. Создайте FirebaseMessagingService
Создайте подкласс FirebaseMessagingService, передающий токен и сообщения в 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) принимает Map<String, String> и обрабатывает только push с флагом origin=threads — остальные тихо игнорирует. Альтернатива — handlePushMessage(bundle: Bundle) с тем же фильтром.
message.dataСостав payload определяется серверной настройкой шаблона push в админке edna Chat Center. Стандартные поля (origin=threads, идентификаторы сообщения, текст-превью) SDK обрабатывает сам; дополнительные ключи из шаблона приходят как строки (вложенные объекты сериализуются админкой в JSON-строку перед отправкой на Android). Подробнее — Настройка шаблонов пуш-уведомлений.
Сосуществование с другим FirebaseMessagingService
Android разрешает только один сервис с intent-фильтром com.google.firebase.MESSAGING_EVENT. Если у вас уже есть свой сервис (например, для Firebase Analytics или другого push-источника), не добавляйте второй — добавьте проксирование edna в существующий:
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
if (message.data["origin"] == "threads") {
chatCenterUI.handleFCMMessage(message.data)
} else {
// ваш обработчик не-edna push
}
}
Шаг 2. Подключите google-services.json
Возможны два варианта:
-
Используя ваш аккаунт Firebase. Сформируйте
google-services.jsonв консоли Firebase. Администратору edna Chat Center передайте два значения этого же Firebase-проекта:- Sender ID — из Firebase Console → Project settings → Cloud Messaging;
- service-account.json — из Firebase Console → Project settings → Service accounts → Generate new private key.
Администратор заносит их в канал на вкладке «Уведомления» — пошаговая инструкция: Подключение мобильного чата Android.
-
Используя аккаунт Firebase edna. Сообщите
applicationIdвашего приложения — в ответ получитеgoogle-services.json.
google-services.json добавьте в корень модуля приложения (рядом с build.gradle). applicationId модуля должен совпадать с полем App_package в канале admin-панели edna.
Шаг 3. Зарегистрируйте сервис в манифесте
<service android:name=".push.CustomPushFcmIntentService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
Канал уведомлений (Android 8+)
SDK создаёт канал уведомлений автоматически на Android 8.0+. Приоритет канала настраивается через ChatConfig.notificationImportance (по умолчанию IMPORTANCE_DEFAULT). Поле помечено @RequiresApi(N) — если minSdk приложения ниже 24, оборачивайте установку значения в проверку Build.VERSION.SDK_INT >= Build.VERSION_CODES.N. Полная таблица значений и нюансы — в Advanced → Установка приоритета для канала уведомлений.
При logout() SDK очищает локально сохранённый FCM/HCM-токен. В multi-account сценарии после следующей авторизации заново передайте токен в SDK через ChatCenterUI.setFCMToken(...) / setHCMToken(...).
Huawei Mobile Services (HMS)
Для устройств Huawei без Google Play Services используйте HMS Push Kit.
Перед интеграцией убедитесь, что в edna Chat Center заведён отдельный канал типа «Мобильный чат → Huawei». В канале заполняется App_package (= applicationId модуля Huawei-сборки), а на вкладке «Уведомления» — Huawei App ID и Huawei App Secret (это OAuth 2.0 Client ID и Client secret из AppGallery Connect → Project settings → App information, не Push Kit App ID). Шаги в админке: Подключение мобильного чата Huawei.
Шаг 1. Добавьте зависимости HMS
implementation 'com.huawei.hms:push:6.11.0.300'
Также добавьте плагин com.huawei.agconnect в build.gradle и файл agconnect-services.json в модуль app/.
agconnect-services.json— из AppGallery Connect → Project settings → App information → секция agconnect-services.json.- Huawei App ID и Huawei App Secret для админки edna — там же, в секции OAuth 2.0 Client ID (поля Client ID и Client secret).
Если после пересоздания OAuth-ключей в AppGallery Connect значения в админке edna не обновлены, backend перестаёт авторизоваться в HMS Push Kit, и push не приходят без явной ошибки в логах. См. Подключение мобильного чата Huawei.
Шаг 2. Создайте сервис обработки push-уведомлений
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 отдаёт payload в RemoteMessage.data как base64-строку (JSON-объект внутри), поэтому перед передачей в SDK её нужно декодировать в Bundle и использовать handlePushMessage(bundle), а не handleFCMMessage(map).
Шаг 3. Зарегистрируйте сервис в AndroidManifest.xml
<service android:name=".push.CustomPushHcmIntentService"
android:exported="false">
<intent-filter>
<action android:name="com.huawei.push.action.MESSAGING_EVENT" />
</intent-filter>
</service>
Шаг 4. Запросите токен при инициализации
При старте приложения запросите HMS-токен:
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 берётся из agconnect-services.json через AGConnectOptionsBuilder — не нужно хардкодить значение в коде.
Полный пример интеграции HMS доступен в демо-приложении.
Проверка интеграции
-
Тестовый push приходит.
- FCM: Firebase Console → Cloud Messaging → Send test message, в разделе Additional options → Custom data добавьте ключ
originсо значениемthreads. - HMS: AppGallery Connect → Push Kit → Add notification → секция Custom message с парой
origin=threads.
Без пары
origin=threadsSDK проигнорирует push. Если в production нужно передавать дополнительные поля в payload (например,externalIdсообщения для дип-линка) — настройка делается на стороне сервера через шаблон push: Настройка шаблонов пуш-уведомлений. Кастомные поля доступны изmessage.dataдо вызоваhandleFCMMessage(...)либо могут читаться вашимPendingIntentCreator. - FCM: Firebase Console → Cloud Messaging → Send test message, в разделе Additional options → Custom data добавьте ключ
-
Уведомление видно на устройстве (Android 13+). Если push приходит, но баннер не появляется — проверьте, что runtime-разрешение
POST_NOTIFICATIONSвыдано пользователем (см. предупреждение в разделе Разрешения). -
Порядок инициализации соблюдён. Если первый push после холодного старта теряется —
ChatCenterUI.init(...)не завершился до вызоваhandleFCMMessage(...). См. Асинхронная инициализация.