Advanced settings
All examples on this page assume you have an initialized ChatCenterUI instance — created by your app once and reused. See SDK initialization for details. In the examples below, the variable chatCenterUI refers to this instance; the variable chatConfig refers to the ChatConfig that was passed to init().
Tracking SDK events
Register a listener to receive notifications about key SDK events: unread counter change, network error, link tap, internal exception.
fun setChatCenterUIListener(listener: ChatCenterUIListener)
Parameters:
listener: ChatCenterUIListener— interface implementation. Passing a new listener replaces the previous one (only one listener is supported at a time).
Throws: IllegalStateException — if the SDK is not initialized (see SDK initialization).
The interface contains default methods with empty bodies — override only the ones you need:
/**
* Methods for the unread message counter,
* receiving network errors, and reacting to link clicks in messages
*/
interface ChatCenterUIListener {
/**
* The unread message counter has changed
* @param count number of unread messages
*/
fun unreadMessageCountChanged(count: UInt) {}
/**
* A server response with an error has been received
* @param error error description (type `edna.chatcenter.core.main.Error`,
* not `java.lang.Error` — add an explicit import of the SDK class)
*/
fun networkErrorReceived(error: Error) {}
/**
* A link in a message has been clicked
* @param url the link string
*/
fun urlClicked(url: String) {}
/**
* An internal SDK error has occurred
* @param trace exception stack trace
*/
fun exceptionReceived(trace: String) {}
}
To subscribe to unread message count changes, set the listener and propagate the value into your own app state (e.g., StateFlow, LiveData, or a DI repository):
import android.util.Log
import edna.chatcenter.core.main.ChatCenterUIListener
import kotlinx.coroutines.flow.MutableStateFlow
// State on your app side, observed by the UI:
val unreadCount = MutableStateFlow(0)
chatCenterUI.setChatCenterUIListener(object : ChatCenterUIListener {
override fun unreadMessageCountChanged(count: UInt) {
unreadCount.value = count.toInt()
}
override fun urlClicked(url: String) {
// The URL may contain sensitive query parameters (token, code, OAuth state).
// In production builds, log only the domain/path without the query, or disable Log.i.
Log.i("UrlClicked", url)
}
})
sendBroadcast with an implicit actionSending a broadcast via Context.sendBroadcast(Intent("your.action")) without the permission argument makes the counter data visible to any app on the device. Additionally, on targetSdk ≥ 34 (Android 14), registering a receiver via Context.registerReceiver() without the RECEIVER_EXPORTED/RECEIVER_NOT_EXPORTED flag throws SecurityException.
For in-process communication, use StateFlow/LiveData or LocalBroadcastManager.
Counter update conditions:
- First request — on
init(), if the user is already authorized. - Real-time updates — delivered only with an active chat (opened and held in memory).
- Counter after leaving the chat screen — requires keeping the WebSocket alive in the background (the
keepWebSocketActiveflag, see the warning below). - Counter before opening the chat — authorize the user immediately after
init()viaauthorize(client, auth)and keep the WebSocket active.
keepWebSocketActive flag is deprecatedThe ChatConfig.keepWebSocketActive parameter is marked @Deprecated: the actual behavior is controlled by server-side edna Chat Center settings. Before using the "background counter" scenario, confirm it with edna technical support and note that the client value may be ignored at runtime.
Do not set keepWebSocketActive = true broadly — a persistent WebSocket in the background increases battery and traffic usage and is subject to background work restrictions (Doze, App Standby, background network restrictions).
Sending and prefilling messages
Programmatic message sending (send(ChatMessage)) and input prefill (prefill(String)) are used when the message is initiated by your app — for example, when opening the chat via a "Contact regarding order №X" button. Full signatures, return values, and ChatMessage.TextMessage / FileMessage / LocationMessage subtypes are described in API reference → Messages and prefill. Delivery is asynchronous; status arrives via ChatCenterUIListener.
By default, the edna server accepts files up to 30 MB. If exceeded, the server returns an error via ChatCenterUIListener.networkErrorReceived() — handle this case in your UI. The limit is enforced on the backend; if needed, coordinate the value with edna support.
ChatMessage.FileMessage and Scoped StorageChatMessage.FileMessage accepts java.io.File. On Android 10+, direct File access to content in shared storage (e.g., /sdcard/...) is restricted — obtain a Uri via the Storage Access Framework (ACTION_OPEN_DOCUMENT) and copy the content into internal storage (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() // delete the temporary file after a successful send
SSL Pinning
Lets the SDK use a specified list of certificates to validate them against the server certificate.
To enable SSL pinning:
- Place the server's public certificates into
res/rawin DER or PEM format (DER is recommended — it is more compact). You can obtain the public certificate with:openssl s_client -connect your.edna.io:443 </dev/null 2>/dev/null \
| openssl x509 -outform DER > primary_cert.der - Pass
ChatNetworkConfig(thenetworkConfigfield) intoChatConfigwith the certificates specified:
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,
//...
)
The allowUntrustedCertificates field disables certificate validation: when true, all certificates are considered trusted.
- Backup certificates. A single certificate is a single point of failure: if it is revoked or expires, the SDK will stop connecting to the server. Keep at least one backup certificate in the
certificatesarray. allowUntrustedCertificates = trueis forbidden in production. The flag disables TLS validation and leaves the connection vulnerable to MITM. Use it only in development against a local server; in release builds, the value must be explicitlyfalse.- A non-empty
certificatesarray together withallowUntrustedCertificates = trueis not allowed — theSSLPinningConfigconstructor throwsIllegalArgumentException(see API reference → SSL Pinning).
Connection timeouts
Timeouts are configured via the HTTPConfig and WSConfig classes, which are passed into ChatNetworkConfig (the networkConfig field in ChatConfig). The unit of measurement is seconds.
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)
)
The full list of fields and default values is in API reference → ChatNetworkConfig.
Internal deeplinks
The SDK allows internal deeplinks. For example, if a message contains text like:
earth://some.params?code={code}
And you create an Activity that is launched via ACTION_VIEW, then clicking such a link will launch that Activity. To enable this, add intent filters to your manifest:
<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="*" does not mean "any host" — it is a literal host with the value "*".
For the filter to match any URI in the `earth://` scheme, omit the host. -->
<data android:scheme="earth" />
</intent-filter>
Any third-party app can declare the same android:scheme="earth" and intercept the deeplink. For sensitive operations (payments, password reset, authorization), use Android App Links with domain signature verification.
App backup settings
The SDK stores device identifiers (device_address, device_uid) and user settings in EncryptedSharedPreferences, whose encryption key lives in the Android Keystore of a specific device and is not transferred via cloud backup or device-to-device transfer. If the encrypted prefs are included in a backup and restored on a different device, the SDK will not be able to decrypt them — this leads to errors or a crash on read.
The exclusion rule described below must be present in every production build.
By default, the SDK declares in its manifest:
<application
android:fullBackupContent="@xml/ecc_backup_rules">
...
</application>
Contents of res/xml/ecc_backup_rules.xml:
<full-backup-content>
<exclude domain="sharedpref" path="edna.chatcenter.ui.internal.utils.EncryptedPrefStore.xml"/>
</full-backup-content>
If you use your own fullBackupContent file, add the same <exclude> rule to it and override the SDK's file via tools:replace (the xmlns:tools attribute must be declared on the manifest root element):
<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 separatelyStarting with Android 12, auto-backup is governed by two attributes: fullBackupContent applies on API ≤ 30, and dataExtractionRules — on API 31+, replacing fullBackupContent for cloud backup and device-to-device transfer. Without dataExtractionRules, the SDK's encrypted prefs will be included in the backup on Android 12+ → decryption error on restore. Details and ready-to-use rules are in the subsection below.
Android 12+ (API 31): dataExtractionRules
Add the following to your app manifest:
<application
android:fullBackupContent="@xml/your_backup_rules"
android:dataExtractionRules="@xml/your_data_extraction_rules"
tools:replace="android:fullBackupContent,android:dataExtractionRules">
...
</application>
Contents of 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>
If your project includes other SDKs with their own android:fullBackupContent, merge their <exclude> rules with the edna SDK rules into a single file — and keep the line <exclude domain="sharedpref" path="edna.chatcenter.ui.internal.utils.EncryptedPrefStore.xml"/>. Take the exclusion rules for other SDKs from their current documentation.
Changing the backend API version
By default, the SDK uses backend API level ChatApiVersion.V16. You can change the value via the apiVersion field of ChatTransportConfig, which is passed into ChatConfig. The field type is the enum edna.chatcenter.core.models.enums.ChatApiVersion; available values: 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.io",
apiVersion = ChatApiVersion.V18 // example: the value is aligned with edna support
)
apiVersion breaks message serializationDo not change apiVersion without confirming with edna technical support — the value must match the version of your edna Chat Center backend. A version mismatch causes message serialization errors in production and partial functionality loss. The full list of differences between versions is in API reference → ChatTransportConfig.
Disabling user input
To temporarily block the input field and the send button, reassign chatConfig.userInputEnabled on an existing ChatConfig instance — this is thread-safe and applied immediately. Full property description and value table are in API reference → Properties modifiable after creation.
Auto-scroll
If the chat history is scrolled to older messages, a "back to latest" button appears in the chat, and the following behavior applies:
- TYPING is received in the chat (indication that an incoming message is being typed) → the user stays at the current history position, nothing moves;
- any incoming message is received in the chat → the user stays at the current history position; the scroll button shows an unread indicator with the message count;
- the user sent a message → the chat scrolls to the user's latest message.
Setting the notification channel priority
The Android notification channel priority is set by the notificationImportance: Int parameter of the ChatConfig constructor (default — IMPORTANCE_DEFAULT). Constructor parameters and default values are in API reference → Constructor parameters.
@RequiresApi(24)The field is marked @RequiresApi(24). For minSdk < 24, wrap the assignment in a Build.VERSION.SDK_INT check or suppress lint with @TargetApi. The actual channel priority effect appears starting from API 26.
Configuring the push notification tap action
By default, when the user taps a push notification, the SDK opens its built-in ChatActivity screen. If your app already has navigation and its own Activity wrapper around the chat, this can result in duplicate chat screens. To avoid the duplicate, pass your own PendingIntentCreator implementation (edna.chatcenter.ui.visual.core.PendingIntentCreator) into the pendingIntentCreator field of the ChatConfig constructor — see a complete example in chat_config.md → PendingIntentCreator.
interface PendingIntentCreator {
fun create(context: Context, appMarker: String?): PendingIntent?
}
The appMarker: String? parameter contains the app identifier passed to the SDK's init() (see SDK initialization). The SDK supports connecting multiple apps within the same process, and appMarker lets you distinguish between them during routing; the parameter may be null for system pushes not tied to a specific app.
FLAG_IMMUTABLE is required on targetSdk ≥ 31Without it, PendingIntent.getActivity() throws IllegalArgumentException. Keep the if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) guard only if you support minSdk < 23; on modern projects, the flag can be added unconditionally.
Android permissions
The SDK automatically declares the following permissions in AndroidManifest.xml:
| Permission | Purpose | When requested |
|---|---|---|
INTERNET | Network connection (WebSocket, HTTP) | Automatically, no request needed |
ACCESS_NETWORK_STATE | Network availability detection | Automatically, no request needed |
CAMERA | Taking photos for sending | When the user taps the camera button in chat |
RECORD_AUDIO | Voice message recording | When the user taps the voice record button |
READ_EXTERNAL_STORAGE | Reading files for attachment upload | When the user picks a file from the gallery |
WRITE_EXTERNAL_STORAGE | Writing files on download (API ≤ 28) | When downloading a file |
POST_NOTIFICATIONS | Showing push notifications (API 33+) | When opening the chat (after history is loaded) |
WAKE_LOCK | Keeping the device awake on push | Automatically |
FOREGROUND_SERVICE | SDK background tasks | Automatically |
DOWNLOAD_WITHOUT_NOTIFICATION | Downloading files without a system notification | On download |
Notes on two rows:
WRITE_EXTERNAL_STORAGE— the SDK declares the permission withandroid:maxSdkVersion="28", so it is not requested on API 29+; writes use Scoped Storage.POST_NOTIFICATIONS— the SDK requests the permission viaActivityCompat.requestPermissions(...)after theHISTORY_LOADEDevent inChatFragment; if your app has its own permission flow, account for this.
Google Play policy requires users to see a clear explanation of why a sensitive permission (CAMERA, RECORD_AUDIO, READ/WRITE_EXTERNAL_STORAGE) is needed before the system request. Without this explanation, the app may be rejected during review.
Enable the SDK's built-in dialogs: set ChatConfig.permissionsDescriptionDialogsEnabled = true (default false). Alternatively, provide your own explanation screen in your app.
Disabling unused permissions
If your app does not use the camera or voice messages, remove the corresponding permissions via merge rules in AndroidManifest.xml (the root <manifest> element must declare 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"/>
Performance and resources
WebSocket
- Active connection: the WebSocket opens when entering the chat and closes when leaving
ChatFragment - keepWebSocketActive (
@Deprecated): whentrue, the connection may be held in the background to update the unread counter (behavior is controlled by the server, see the warning in the Tracking SDK events section). Increases battery and traffic usage - Ping/Pong: keepalive check at the interval of
WSConfig.pingInterval(30 s by default) - Background restrictions: with
keepWebSocketActive = true, the connection is subject to standard Android restrictions (Doze, App Standby, background network restrictions)
Local storage
The full table of SDK storages (in-memory SQLite, three SharedPreferences files, RAM with ChatUser/ChatAuth) including the encryption scheme and what survives logout() is in the canonical section Authorization → User data storage. Exclusion rules for these files from cloud-backup and device-to-device transfer are described above — see App backup settings.
- Logs: size and number of files are configured via
ChatLoggerConfig.
Background work
- WorkManager: used for push notification handling (the SDK's internal WorkManager worker)
- The SDK does not start persistent background services
Recommendations
- Use
initAsync()instead ofinit()for non-blocking initialization. - Call
logout()on user sign-out — this frees SDK resources. - In production, set
ChatLogLevel.ERRORto minimize log output.
Related sections
- SDK initialization — the
ChatCenterUIconstructor,init()/initAsync(). ChatConfigparameters — feature and behavior flags.- Transport —
ChatTransportConfig(host, endpoints,apiVersion). - Network —
HTTPConfig/WSConfig/SSLPinningConfigand defaults. - Logger —
ChatLogLevellevels,ChatLoggerConfig, customLogInterceptor, log collection via shake and Device Explorer. - Authorization and logout —
authorize(),logout(),ChatUser/ChatAuthmodels. - Error handling — diagnostics, common issues and their solutions.
- Troubleshooting — step-by-step diagnostics of common integration errors.