Перейти к основному содержимому
Версия: 5.21.0

Расширенные настройки

Все примеры на этой странице предполагают, что у вас есть инициализированный экземпляр 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.

Лимит размера файла — 30 МБ

По умолчанию edna-сервер принимает файлы до 30 МБ. При превышении сервер вернёт ошибку через ChatCenterUIListener.networkErrorReceived() — обработайте этот случай в UI. Лимит регулируется на стороне backend; при необходимости согласуйте с поддержкой edna.

ChatMessage.FileMessage и Scoped Storage

ChatMessage.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 необходимо:

  1. В папку 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
  2. В 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 все сертификаты будут считаться доверенными.

SSL Pinning — обязательные требования к production
  • Резервные сертификаты. Один сертификат — единая точка отказа: при его отзыве/истечении 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>
Custom URI schemes уязвимы к перехвату

Любое стороннее приложение может объявить тот же android:scheme="earth" и перехватить deeplink. Для чувствительных операций (платежи, восстановление пароля, авторизация) используйте Android App Links с верификацией подписи домена.

Настройки backup для приложения​

Обязательное правило backup для production

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>
Android 12+ (API 31): отдельно настройте 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 review

Политика 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 для минимизации записи логов.

Связанные разделы​