Delegates and event listeners
To react to SDK events, integrators have two mechanisms:
ChatCenterUIListener— an interface with 4 callback methods: unread counter, network error, link click, internal SDK exception. Sufficient for most integrations.ChatUpdateProcessor— aSharedFlowbus for message delivery events, status changes, and connection state. Used only when building a custom UI on top of the SDK.
ChatCenterUIListener
fun setChatCenterUIListener(listener: ChatCenterUIListener)
Subscribes your app to UI events and basic SDK events. Call it after init(...) — otherwise the SDK throws IllegalStateException. A repeated call replaces the previous listener (older subscriptions on streams inside the SDK are cancelled).
Interface methods
| Method | Parameter | When called |
|---|---|---|
unreadMessageCountChanged(count: UInt) | unread count | Immediately after setChatCenterUIListener(...) (the current value), then whenever the counter changes — authorize(...), a new incoming message, a read receipt by the operator |
networkErrorReceived(error: Error) | edna.chatcenter.core.main.Error — fields and a handling example are in Errors → networkErrorReceived | A network error from the server or WebSocket |
urlClicked(url: String) | URL | The user tapped a link in a message |
exceptionReceived(trace: String) | stack trace | Internal SDK exception (for logging) |
Example
import android.content.Intent
import edna.chatcenter.core.main.ChatCenterUIListener
import edna.chatcenter.core.main.Error // important: shadows kotlin.Error — if needed, use an alias `as SdkError`
private const val APP_UNREAD_COUNT_BROADCAST = "com.example.app.UNREAD_COUNT"
private const val UNREAD_COUNT_KEY = "unread_count"
chatCenterUI.setChatCenterUIListener(object : ChatCenterUIListener {
override fun unreadMessageCountChanged(count: UInt) {
val intent = Intent(APP_UNREAD_COUNT_BROADCAST)
.putExtra(UNREAD_COUNT_KEY, count.toInt())
applicationContext.sendBroadcast(intent)
}
override fun networkErrorReceived(error: Error) {
Log.e("ChatCenter", "Network error [${error.code}]: ${error.message}")
}
override fun urlClicked(url: String) {
Log.i("UrlClicked", url)
}
})
Callback threads
| Callback | Thread |
|---|---|
unreadMessageCountChanged | main — you can update the UI directly |
exceptionReceived | main |
urlClicked | main (comes from the link click handler in the UI) |
networkErrorReceived | background (OkHttp / Retrofit / @WorkerThread sources) — switch to main explicitly for UI |
Recommended idiom for networkErrorReceived when you need to touch the UI:
override fun networkErrorReceived(error: Error) {
// This callback arrives from a network thread, not main.
// Use your screen's lifecycleScope / viewModelScope.
lifecycleScope.launch(Dispatchers.Main) {
showErrorToUser(error)
}
}
For View-based code outside a coroutine context, view.post { ... } or Handler(Looper.getMainLooper()).post { ... } are acceptable — but if the project already uses coroutines, stick with one idiom (Dispatchers.Main).
A detailed rationale (where and why the callback is invoked) with concrete call sites is in Known limitations.
Advanced scenarios
The section below is needed only if you are building your own message list / status indicators on top of the SDK or monitoring SDK events at a low level. For the unread badge, network errors, and link clicks, the ChatCenterUIListener above is sufficient.
Subscribing to additional events — ChatUpdateProcessor
If your scenario needs events that are not in ChatCenterUIListener (for example, the exact moment a message is delivered to the server or a signal that an offline socket has closed), the reactive ChatUpdateProcessor bus from the edna.chatcenter.core.chatUpdates package is available. The class exposes public MutableSharedFlow streams. Below are 6 base flows for a custom UI; in addition, the class has streams for quick replies, the typing indicator, attachment updates, and file download progress — they are exposed as fields of the same class; see the KDoc in the sources.
| Flow | What is emitted | When useful |
|---|---|---|
newMessageFlow | ChatItem — a chat history item added after the server response (including outgoing items received in GET_MESSAGES) | Custom notification/counter in addition to the standard push |
messageSendSuccessFlow | ChatItemProviderData (uuid, messageId, sentAt) — server confirmation that the outgoing message was accepted | Delivery confirmation on the app side |
messageSendErrorFlow | ChatItemSendErrorModel | Reaction to a send error (retry strategy) |
outgoingMessageStatusChangedFlow | List<Status> — a batch of status updates (Status.status takes a MessageStatus value); a single emit may contain statuses for different messages | Rendering "checkmarks" in a custom UI |
socketIsClosedFlow | Int — the WebSocket close code | Offline mode indicator |
cleanAllOnUiFlow | A signal to clear the UI | Reset custom state — emitted in both logout() scenarios (both the deferred cleanup during a handshake and the full cleanup) |
Get an instance via the inject() delegate of the SDK's public DI container:
import edna.chatcenter.core.chatUpdates.ChatUpdateProcessor
import edna.chatcenter.core.serviceLocator.core.inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
val processor: ChatUpdateProcessor by inject()
// ChatUpdateProcessor emits from Dispatchers.Unconfined — events do not arrive on
// the main thread (they arrive on the thread where the SDK's internal emit() ran).
// Wrap UI calls in withContext(Dispatchers.Main). See Known limitations for details.
lifecycleScope.launch {
processor.newMessageFlow.collect { item ->
withContext(Dispatchers.Main) {
// UI update
}
}
}
inject() is the SDK's public DI container, not Hilt/KoinThe inject() delegate belongs to the public package edna.chatcenter.core.serviceLocator.core and is unrelated to your application's DI frameworks (Hilt, Koin, Dagger).
Additional models for a custom UI
You can work with these types when building your own chat interface (message list, status indicators, file download progress). You do not need to create instances manually — the SDK emits them itself via ChatUpdateProcessor or passes them in callbacks.
FileDescription — attachment model
edna.chatcenter.core.models.FileDescription — a file attachment model that the SDK emits for chat history items (outgoing messages store the list as UserPhrase.files, incoming messages do the same).
Key fields: fileUri: Uri?, size: Long, from: String?, state: MutableStateFlow<AttachmentStateEnum> (default ANY; the SDK updates it itself — read via state.value / state.collect { ... }, do not modify), errorCode: ErrorStateEnum (default ANY), downloadPath: String?.
AttachmentStateEnum (ANY, PENDING, ERROR, EXPIRED, READY) and ErrorStateEnum are public enums from the same package (edna.chatcenter.core.models.enums).
ProgressReceiver — file download events
edna.chatcenter.core.broadcastReceivers.ProgressReceiver is a BroadcastReceiver through which the SDK reports file download progress. Register it if you need to show your own progress indicator. Action constants are declared in the companion object of ProgressReceiver:
ProgressReceiver.PROGRESS_BROADCAST— progress update (0..100).ProgressReceiver.DOWNLOADED_SUCCESSFULLY_BROADCAST— the file has been downloaded.ProgressReceiver.DOWNLOAD_ERROR_BROADCAST— download error.
The intent contains FileDescription as a Parcelable extra under the key FileDownloadWorker.FD_TAG (edna.chatcenter.core.workers.FileDownloadWorker.FD_TAG). For DOWNLOAD_ERROR_BROADCAST, a Throwable is also passed as a Serializable extra under the key ProgressReceiver.DOWNLOAD_ERROR_BROADCAST (the same constant is used as the action).
MessageStatus — message status enum
Described in the canonical section Messages → Message statuses. Used to build custom "checkmark" indicators (the sentState field of sent history items).
Related sections
- Messages —
send(),prefill(), unread counter. - Errors — handling
ErrorfromnetworkErrorReceived. - Known limitations — coroutine scope nuances of the advanced event bus.