Расширенные настройки
Все примеры на этой странице предполагают, что у вас есть инициализированный экземпляр ChatCenterUI — он создаётся вашим приложением один раз и переиспользуется. Подробнее см. Инициализация SDK. В примерах ниже переменная chatCenterUI ссылается на этот экземпляр; переменная chatConfig — на ChatConfig, который был передан в init().
Отслеживание событий внутри SDK
Зарегистрируйте слушатель, чтобы получать уведомления о ключевых событиях SDK: изменение счётчика непрочитанных, сетевая ошибка, тап на ссылку, внутреннее исключение.
fun setChatCenterUIListener(listener: ChatCenterUIListener)
Параметры:
listener: ChatCenterUIListener— реализация интерфейса. Передача нового listener'а заменяет ранее установленного (одновременно поддерживается только один).
Бросает: IllegalStateException — если SDK не инициализирован (см. Инициализация SDK).
Интерфейс содержит default-методы с пустыми телами — переопределяйте только нужные:
/**
* Описывает методы счётчика непрочитанных сообщений,
* получения сетевой ошибки, реакции на нажатие ссылки в сообщении
*/
interface ChatCenterUIListener {
/**
* Изменился счётчик непрочитанных сообщений
* @param count количество непрочитанных сообщений
*/
fun unreadMessageCountChanged(count: UInt) {}
/**
* Получен ответ от сервера с ошибкой
* @param error описание текущей ошибки (тип `edna.chatcenter.core.main.Error`,
* не `java.lang.Error` — добавьте явный импорт SDK-класса)
*/
fun networkErrorReceived(error: Error) {}
/**
* Нажата ссылка в сообщении
* @param url строка ссылки
*/
fun urlClicked(url: String) {}
/**
* Произошла внутренняя ошибка SDK
* @param trace стектрейс исключения
*/
fun exceptionReceived(trace: String) {}
}
Чтобы подписаться на изменение количества непрочитанных сообщений, задайте слушатель и пробросьте значение в собственное состояние приложения (например, StateFlow, LiveData или DI-репозиторий):
import android.util.Log
import edna.chatcenter.core.main.ChatCenterUIListener
import kotlinx.coroutines.flow.MutableStateFlow
// Состояние на стороне вашего приложения, на которое подписан UI:
val unreadCount = MutableStateFlow(0)
chatCenterUI.setChatCenterUIListener(object : ChatCenterUIListener {
override fun unreadMessageCountChanged(count: UInt) {
unreadCount.value = count.toInt()
}
override fun urlClicked(url: String) {
// URL может содержать чувствительные query-параметры (token, code, OAuth state).
// В production-сборках логируйте только домен/путь без query, либо отключайте Log.i.
Log.i("UrlClicked", url)
}
})
sendBroadcast с неявным actionОтправка широковещательного сообщения через Context.sendBroadcast(Intent("ваш.action")) без permission-аргумента делает данные счётчика видимыми любому приложению на устройстве. Дополнительно, при targetSdk ≥ 34 (Android 14) регистрация receiver'а через Context.registerReceiver() без флага RECEIVER_EXPORTED/RECEIVER_NOT_EXPORTED бросает SecurityException.
Для передачи внутри своего процесса используйте StateFlow/LiveData или LocalBroadcastManager.
Условия обновления счётчика:
- Первый запрос — при
init(), если пользователь уже авторизован. - Обновления в реальном времени — приходят только при активном чате (открытом и удерживаемом в памяти).
- Счётчик после ухода с экрана чата — требуется удерживать WebSocket в фоне (флаг
keepWebSocketActive, см. предупреждение ниже). - Счётчик до открытия чата — авторизуйте пользователя сразу после
init()черезauthorize(client, auth)и удерживайте WebSocket активным.
keepWebSocketActive устаревшийПараметр ChatConfig.keepWebSocketActive помечен @Deprecated: фактическое поведение управляется серверными настройками edna Chat Center. Перед использованием согласуйте сценарий «счётчик в фоне» с технической поддержкой edna и учтите, что значение на клиенте может быть проигнорировано рантаймом.
Не выставляйте keepWebSocketActive = true массово — постоянный WebSocket в фоне увеличивает расход батареи и трафика и подвержен ограничениям фоновой работы (Doze, App Standby, ограничения на фоновую сеть).
Отправка и предзаполнение сообщений
Программная отправка сообщений (send(ChatMessage)) и предзаполнение поля ввода (prefill(String)) применяются, когда сообщение инициирует ваше приложение — например, при открытии чата по кнопке «Связаться по заказу №X». Подробное описание сигнатур, возвращаемых значений и подтипов ChatMessage.TextMessage / FileMessage / LocationMessage — в Справочнике API → Сообщения и prefill. Доставка асинхронная; статус приходит через ChatCenterUIListener.
По умолчанию edna-сервер принимает файлы до 30 МБ. При превышении сервер вернёт ошибку через ChatCenterUIListener.networkErrorReceived() — обработайте этот случай в UI. Лимит регулируется на стороне backend; при необходимости согласуйте с поддержкой edna.
ChatMessage.FileMessage и Scoped StorageChatMessage.FileMessage принимает java.io.File. На Android 10+ прямой доступ через File к контенту в общем хранилище (например, /sdcard/...) ограничен — получите Uri через Storage Access Framework (ACTION_OPEN_DOCUMENT) и скопируйте содержимое во внутреннее хранилище (cacheDir/filesDir):
fun toUploadableFile(context: Context, uri: Uri): File {
val tempFile = File(context.cacheDir, "upload_${System.currentTimeMillis()}")
context.contentResolver.openInputStream(uri)?.use { input ->
tempFile.outputStream().use { output -> input.copyTo(output) }
}
return tempFile
}
val file = toUploadableFile(context, pickedUri)
chatCenterUI.send(ChatMessage.FileMessage(file))
file.delete() // удалите временный файл после успешной отправки
SSL Pinning
Позволяет использовать указанный список сертификатов для проверки их соответствия с сертификатом на сервере.
Для включения SSL pinning необходимо:
- В папку
res/rawпоместить публичные сертификаты сервера в формате DER или PEM (рекомендуется DER — компактнее). Получить публичный сертификат можно командой:openssl s_client -connect your.edna.ru:443 </dev/null 2>/dev/null \
| openssl x509 -outform DER > primary_cert.der - В
ChatConfigпробросить классChatNetworkConfig(полеnetworkConfig), указав сертификаты:
import edna.chatcenter.core.config.transport.ChatNetworkConfig
import edna.chatcenter.core.config.transport.ChatSSLCertificate
import edna.chatcenter.core.config.transport.HTTPConfig
import edna.chatcenter.core.config.transport.SSLPinningConfig
import edna.chatcenter.core.config.transport.WSConfig
val certificates = arrayOf(
ChatSSLCertificate(R.raw.primary_cert),
ChatSSLCertificate(R.raw.backup_cert)
)
val networkConfig = ChatNetworkConfig(
httpConfig = HTTPConfig(),
wsConfig = WSConfig(),
sslPinning = SSLPinningConfig(
certificates = certificates,
allowUntrustedCertificates = false
)
)
val chatConf = ChatConfig(
transportConfig,
networkConfig,
//...
)
Поле allowUntrustedCertificates отключает проверку сертификатов: при true все сертификаты будут считаться доверенными.
- Резервные сертификаты. Один сертификат — единая точка отказа: при его отзыве/истечении SDK перестанет подключаться к серверу. Держите в массиве
certificatesминимум один резервный. allowUntrustedCertificates = trueзапрещён в production. Флаг отключает TLS-проверку и делает соединение уязвимым к MITM. Используйте только в development против локального сервера; в release-сборке значение должно быть явноfalse.- Одновременная установка непустого
certificatesиallowUntrustedCertificates = trueзапрещена — конструкторSSLPinningConfigброситIllegalArgumentException(см. Справочник API → SSL Pinning).
Настройка таймаутов подключения
Таймауты настраиваются через классы HTTPConfig и WSConfig, которые передаются в ChatNetworkConfig (поле networkConfig в ChatConfig). Единица измерения — секунды.
import edna.chatcenter.core.config.transport.ChatNetworkConfig
import edna.chatcenter.core.config.transport.HTTPConfig
import edna.chatcenter.core.config.transport.WSConfig
val networkConfig = ChatNetworkConfig(
httpConfig = HTTPConfig(connectionTimeout = 20, downloadTimeout = 60, uploadTimeout = 60),
wsConfig = WSConfig(connectionTimeout = 20, sendTimeout = 20, pingInterval = 15)
)
Полный список полей и значений по умолчанию см. в Справочнике API → ChatNetworkConfig.
Использование внутренних диплинков
SDK позволяет использовать внутренние диплинки. Например, если в сообщении встречается текст вида:
earth://some.params?code={code}
И если вы создадите Activity, которое будет запускаться через ACTION_VIEW, то при клике на подобную ссылку запустится указанное Activity. Для этого укажите в манифесте intent-фильтры:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- android:host="*" не работает как «любой хост» — это буквальный хост со значением "*".
Чтобы фильтр срабатывал на любой URI в схеме `earth://`, не указывайте host. -->
<data android:scheme="earth" />
</intent-filter>
Любое стороннее приложение может объявить тот же android:scheme="earth" и перехватить deeplink. Для чувствительных операций (платежи, восстановление пароля, авторизация) используйте Android App Links с верификацией подписи домена.
Настройки backup для приложения
SDK хранит идентификаторы устройства (device_address, device_uid) и пользовательские настройки в EncryptedSharedPreferences, ключ шифрования которых лежит в Android Keystore конкретного устройства и не переносится через cloud backup или device-to-device transfer. Если зашифрованные prefs попадут в бэкап и будут восстановлены на другом устройстве, SDK не сможет их расшифровать — это приведёт к ошибкам или крашу при чтении.
Правило исключения, описанное ниже, должно присутствовать в любой production-сборке.
SDK по умолчанию объявляет в своём манифесте:
<application
android:fullBackupContent="@xml/ecc_backup_rules">
...
</application>
Содержимое res/xml/ecc_backup_rules.xml:
<full-backup-content>
<exclude domain="sharedpref" path="edna.chatcenter.ui.internal.utils.EncryptedPrefStore.xml"/>
</full-backup-content>
Если вы используете собственный fullBackupContent-файл, добавьте в него тот же <exclude> и переопределите файл SDK через tools:replace (атрибут xmlns:tools должен быть объявлен в корневом элементе манифеста):
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:fullBackupContent="@xml/your_backup_rules"
tools:replace="android:fullBackupContent">
...
</application>
</manifest>
dataExtractionRulesНачиная с Android 12 авто-бэкап управляется двумя атрибутами: fullBackupContent действует на устройствах с API ≤ 30, dataExtractionRules — на API 31+ и заменяет fullBackupContent для cloud backup и device-to-device transfer. Без dataExtractionRules зашифрованные prefs SDK попадут в бэкап на Android 12+ → ошибка расшифровки при восстановлении. Подробности и готовые правила — в подразделе ниже.
Android 12+ (API 31): dataExtractionRules
Добавьте в манифест приложения:
<application
android:fullBackupContent="@xml/your_backup_rules"
android:dataExtractionRules="@xml/your_data_extraction_rules"
tools:replace="android:fullBackupContent,android:dataExtractionRules">
...
</application>
Содержимое res/xml/your_data_extraction_rules.xml:
<?xml version="1.0" encoding="utf-8"?>
<data-extraction-rules>
<cloud-backup>
<exclude domain="sharedpref"
path="edna.chatcenter.ui.internal.utils.EncryptedPrefStore.xml"/>
</cloud-backup>
<device-transfer>
<exclude domain="sharedpref"
path="edna.chatcenter.ui.internal.utils.EncryptedPrefStore.xml"/>
</device-transfer>
</data-extraction-rules>
Если в проекте подключены другие SDK с собственным android:fullBackupContent, объедините их <exclude>-правила с правилами edna SDK в один файл — обязательно сохраните строку <exclude domain="sharedpref" path="edna.chatcenter.ui.internal.utils.EncryptedPrefStore.xml"/>. Список правил исключения для других SDK берите из их актуальной документации.
Изменение версии backend API
По умолчанию SDK использует уровень backend API ChatApiVersion.V16. Изменить значение можно через поле apiVersion класса ChatTransportConfig, который передаётся в ChatConfig. Тип поля — enum edna.chatcenter.core.models.enums.ChatApiVersion; доступные значения: V16, V17, V18, V19, V20, V21.
import edna.chatcenter.core.config.transport.ChatTransportConfig
import edna.chatcenter.core.models.enums.ChatApiVersion
val transportConfig = ChatTransportConfig(
cloudHost = "https://your.edna.ru",
apiVersion = ChatApiVersion.V18 // пример: версия согласуется с поддержкой edna
)
apiVersion ломает сериализацию сообщенийНе меняйте apiVersion без согласования с технической поддержкой edna — значение должно совпадать с версией вашего backend edna Chat Center. Несовпадение версий приводит к ошибкам сериализации сообщений в production и потере части функциональности. Полный список различий между версиями — в Справочнике API → ChatTransportConfig.
Отключение пользовательского ввода
Для временной блокировки поля ввода и кнопки отправки переприсваивайте chatConfig.userInputEnabled на готовом экземпляре ChatConfig — это потокобезопасно и применяется немедленно. Полное описание свойства и таблица значений — в Справочнике API → Свойства, изменяемые после создания.
Автоскролл
Если история чата прокручена к более старым сообщениям, в чате появляется кнопка возврата к последним сообщениям и действует следующее поведение:
- в чате получен TYPING (индикация набора входящего сообщения) → пользователь остаётся на текущей позиции истории, ничего не перемещается;
- в чате получено любое входящее сообщение → пользователь остаётся на текущей позиции истории, ничего не перемещается; на кнопке прокрутки появляется индикатор непрочитанных сообщений с указанием их количества;
- пользователь отправил сообщение → чат прокручивается к последнему сообщению пользователя.
Установка приоритета для канала уведомлений
Приоритет канала уведомлений Android задаётся параметром notificationImportance: Int конструктора ChatConfig (дефолт — IMPORTANCE_DEFAULT). Параметры конструктора и значения по умолчанию — в Справочнике API → Параметры конструктора.
@RequiresApi(24)Поле помечено @RequiresApi(24). При minSdk < 24 оборачивайте присвоение в Build.VERSION.SDK_INT-проверку или подавляйте lint аннотацией @TargetApi. Фактический эффект приоритета каналов появляется с API 26.
Настройка перехода по Push-уведомлению
При тапе на push-уведомление SDK по умолчанию открывает встроенный экран ChatActivity. Если у приложения уже есть навигация и собственная Activity-обёртка чата, это может привести к задвоению окон чата. Чтобы избежать дубля, передайте свою реализацию PendingIntentCreator (edna.chatcenter.ui.visual.core.PendingIntentCreator) в поле pendingIntentCreator конструктора ChatConfig — готовый пример кода см. chat_config.md → Пример: кастомный PendingIntentCreator.
interface PendingIntentCreator {
fun create(context: Context, appMarker: String?): PendingIntent?
}
В параметре appMarker: String? приходит идентификатор приложения, переданный в init() SDK (см. Инициализация SDK). SDK поддерживает подключение нескольких приложений в одном процессе, и appMarker позволяет отличать их при роутинге; параметр может быть null для системных пушей без привязки к конкретному приложению.
FLAG_IMMUTABLE обязателен при targetSdk ≥ 31Без него PendingIntent.getActivity() бросит IllegalArgumentException. Сохраняйте guard if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) только если поддерживаете minSdk < 23; на современных проектах флаг можно добавлять безусловно.
Разрешения Android (Permissions)
SDK автоматически объявляет следующие разрешения в AndroidManifest.xml:
| Разрешение | Назначение | Когда запрашивается |
|---|---|---|
INTERNET | Сетевое подключение (WebSocket, HTTP) | Автоматически, не требует запроса |
ACCESS_NETWORK_STATE | Определение доступности сети | Автоматически, не требует запроса |
CAMERA | Фотографирование для отправки | При нажатии кнопки камеры в чате |
RECORD_AUDIO | Запись голосовых сообщений | При нажатии кнопки записи голоса |
READ_EXTERNAL_STORAGE | Чтение файлов для отправки вложений | При выборе файла из галереи |
WRITE_EXTERNAL_STORAGE | Запись файлов при скачивании (API ≤ 28) | При скачивании файла |
POST_NOTIFICATIONS | Показ push-уведомлений (API 33+) | При открытии чата (после загрузки истории) |
WAKE_LOCK | Поддержание устройства при push | Автоматически |
FOREGROUND_SERVICE | Фоновые задачи SDK | Автоматически |
DOWNLOAD_WITHOUT_NOTIFICATION | Скачивание файлов без системного уведомления | При скачивании |
Пояснения по двум строкам:
WRITE_EXTERNAL_STORAGE— SDK объявляет разрешение сandroid:maxSdkVersion="28", поэтому на API 29+ оно не запрашивается; для записи используется Scoped Storage.POST_NOTIFICATIONS— SDK запрашивает разрешение черезActivityCompat.requestPermissions(...)после событияHISTORY_LOADEDвChatFragment; если у вашего приложения есть собственный permission flow, учтите этот момент.
Политика Google Play требует, чтобы перед системным запросом чувствительных разрешений (CAMERA, RECORD_AUDIO, READ/WRITE_EXTERNAL_STORAGE) пользователь видел понятное объяснение, зачем разрешение нужно. Без объяснения приложение может быть отклонено при ревью.
Включите встроенные диалоги SDK: установите ChatConfig.permissionsDescriptionDialogsEnabled = true (по умолчанию false). Либо предоставьте собственный экран-объяснение в своём приложении.
Отключение неиспользуемых разрешений
Если приложение не использует камеру или голосовые сообщения, уберите соответствующие разрешения через merge-правила в AndroidManifest.xml (в корневом <manifest> должно быть объявлено xmlns:tools="http://schemas.android.com/tools"):
<uses-permission android:name="android.permission.CAMERA"
tools:node="remove"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"
tools:node="remove"/>
Производительность и ресурсы
WebSocket
- Активное соединение: WebSocket открывается при входе в чат и закрывается при выходе из
ChatFragment - keepWebSocketActive (
@Deprecated): приtrue— соединение может удерживаться в фоне для обновления счётчика непрочитанных (поведение управляется сервером, см. предупреждение в разделе «Отслеживание событий»). Увеличивает расход батареи и трафика - Ping/Pong: keepalive-проверка с интервалом
WSConfig.pingInterval(по умолчанию 30 сек) - Фоновые ограничения: при
keepWebSocketActive = trueсоединение подвержено стандартным ограничениям Android (Doze, App Standby, ограничения на фоновую сеть)
Локальное хранение
Полная таблица хранилищ SDK (in-memory SQLite, три SharedPreferences-файла, RAM с ChatUser/ChatAuth) с указанием схемы шифрования и того, что переживёт logout() — в каноническом разделе Авторизация → Хранение данных пользователя. Правила исключения этих файлов из cloud-backup и device-to-device transfer описаны выше — см. Настройки backup для приложения.
- Логи: размер и число файлов настраиваются через
ChatLoggerConfig.
Фоновая работа
- WorkManager: используется для обработки push-уведомлений (внутренний WorkManager-воркер SDK)
- SDK не запускает постоянных фоновых сервисов
Рекомендации
- Используйте
initAsync()вместоinit()для неблокирующей инициализации. - Вызывайте
logout()при выходе пользователя из аккаунта — это освобождает ресурсы SDK. - В production устанавливайте
ChatLogLevel.ERRORдля минимизации записи логов.
Связанные разделы
- Инициализация SDK — конструктор
ChatCenterUI,init()/initAsync(). - Параметры
ChatConfig— флаги функциональности и поведения. - Транспорт —
ChatTransportConfig(хост, эндпоинты,apiVersion). - Сеть —
HTTPConfig/WSConfig/SSLPinningConfigи значения по умолчанию. - Логгер — уровни
ChatLogLevel,ChatLoggerConfig, кастомныйLogInterceptor, сбор логов через шейк и Device Explorer. - Авторизация и логаут —
authorize(),logout(), моделиChatUser/ChatAuth. - Обработка ошибок — диагностика, типичные проблемы и их решения.
- Решение проблем — пошаговая диагностика частых ошибок интеграции.