Skip to content

Events

The Kindly SDK provides a unified event system that allows you to subscribe to all SDK events and state changes in real-time. This matches the web API pattern and provides a comprehensive way to monitor SDK behavior using Kotlin Flows.

Event Types

The SDK emits the following event types:

  • kindly:load - Settings and configuration loaded
  • kindly:message - New messages (sent/received)
  • kindly:buttonclick - Button interactions
  • kindly:ui - UI actions (open, close, end chat, etc.)
  • kindly:globalicon - Global icon events (defined but not currently implemented in mobile SDKs)
  • kindly:state - State changes (connection, display status)

Basic Usage

Subscribe to All Events

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import no.kindly.chatsdk.chat.KindlySDK
import no.kindly.chatsdk.chat.models.KindlyEventDetail

class MyEventHandler {
    private val scope = CoroutineScope(Dispatchers.Main)

    fun setupEventListening() {
        scope.launch {
            KindlySDK.events.collect { event ->
                println("Event: ${event.type.value}")
                println("Timestamp: ${event.timestamp}")

                when (val detail = event.detail) {
                    is KindlyEventDetail.Load -> {
                        println("Settings loaded: ${detail.settings.name}")
                    }

                    is KindlyEventDetail.Message -> {
                        println("New message: ${detail.newMessage.text ?: "No text"}")
                    }

                    is KindlyEventDetail.ButtonClick -> {
                        println("Button clicked: ${detail.button.label}")
                    }

                    is KindlyEventDetail.UI -> {
                        println("UI Action: ${detail.action.type.value}")
                    }

                    is KindlyEventDetail.State -> {
                        println("State changed - Connected: ${detail.state.isConnected}")
                    }

                    is KindlyEventDetail.GlobalIcon -> {
                        // Note: globalIcon events are not currently implemented in mobile SDKs
                    }
                }
            }
        }
    }
}

Subscribe to State Changes Only

scope.launch {
    KindlySDK.state.collect { state ->
        println("Chat displayed: ${state.isChatDisplayed}")
        println("SDK started: ${state.isStarted}")
        println("Socket connected: ${state.isSocketConnected}")
        println("Fully connected: ${state.isConnected}")
        println("Connection state: ${state.connectionState}")
    }
}

Event Details

Load Event

Emitted when SDK settings are loaded from the server.

import kotlinx.coroutines.flow.filter
import no.kindly.chatsdk.chat.models.KindlyEventType

scope.launch {
    KindlySDK.events
        .filter { it.type == KindlyEventType.LOAD }
        .collect { event ->
            val detail = event.detail as? KindlyEventDetail.Load
            detail?.let {
                println("Bot: ${it.settings.name} (ID: ${it.settings.id})")
                println("Languages: ${it.settings.languages.map { lang -> lang.code }}")
                val hasCustomBackground = it.settings.style.background != null
                println("Theme: ${if (hasCustomBackground) "Custom" else "Default"}")
            }
        }
}

Message Event

Emitted for both incoming and outgoing messages.

scope.launch {
    KindlySDK.events
        .filter { it.type == KindlyEventType.MESSAGE }
        .collect { event ->
            val detail = event.detail as? KindlyEventDetail.Message
            detail?.let {
                println("Message from ${it.newMessage.from}: ${it.newMessage.text ?: ""}")
                println("Chat has ${it.chatLog.size} total messages")
            }
        }
}

Button Click Event

Emitted when users interact with chat buttons.

scope.launch {
    KindlySDK.events
        .filter { it.type == KindlyEventType.BUTTON_CLICK }
        .collect { event ->
            val detail = event.detail as? KindlyEventDetail.ButtonClick
            detail?.let {
                println("Button: ${it.button.label}")
                println("Type: ${it.buttonType}")
                println("Value: ${it.button.value ?: ""}")
            }
        }
}

UI Events

Emitted for user interface actions like opening/closing chat, language changes, etc.

import no.kindly.chatsdk.chat.models.KindlyUIActionType

scope.launch {
    KindlySDK.events
        .filter { it.type == KindlyEventType.UI }
        .collect { event ->
            val detail = event.detail as? KindlyEventDetail.UI
            detail?.let { uiDetail ->
                when (uiDetail.action.type) {
                    KindlyUIActionType.OPEN_CHAT_BUBBLE -> {
                        println("Chat opened")
                    }
                    KindlyUIActionType.CLOSE_CHAT_BUBBLE -> {
                        println("Chat closed")
                    }
                    KindlyUIActionType.END_CHAT -> {
                        println("Chat ended")
                    }
                    KindlyUIActionType.RESTART_CHAT -> {
                        println("Chat restarted")
                    }
                    KindlyUIActionType.CHANGE_LANGUAGE -> {
                        println("Language changed to: ${uiDetail.action.languageCode ?: ""}")
                    }
                    KindlyUIActionType.DOWNLOAD_CHAT -> {
                        println("Chat downloaded")
                    }
                    KindlyUIActionType.DELETE_CHAT -> {
                        println("Chat deleted")
                    }
                    KindlyUIActionType.SHOW_FEEDBACK -> {
                        println("Feedback form shown")
                    }
                    KindlyUIActionType.SUBMIT_FEEDBACK -> {
                        println("Feedback submitted")
                    }
                    KindlyUIActionType.DISMISS_FEEDBACK -> {
                        println("Feedback dismissed")
                    }
                }
            }
        }
}

State Events

Emitted when SDK connection or display state changes.

scope.launch {
    KindlySDK.state.collect { state ->
        when {
            state.isConnected -> {
                println("✅ SDK is fully connected and ready!")
            }
            state.isStarted && !state.isSocketConnected -> {
                println("⚠️ SDK started but socket disconnected")
            }
            !state.isStarted -> {
                println("⏳ SDK not started yet")
            }
        }
    }
}

Advanced Usage

Track Chat Display Changes

import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map

scope.launch {
    KindlySDK.state
        .map { it.isChatDisplayed }
        .distinctUntilChanged() // Only emit when value changes
        .collect { isDisplayed ->
            if (isDisplayed) {
                println("📱 Chat interface is now visible")
            } else {
                println("🙈 Chat interface is now hidden")
            }
        }
}

Monitor Connection Health

scope.launch {
    KindlySDK.state
        .map { state -> Triple(state.isStarted, state.isSocketConnected, state.isConnected) }
        .distinctUntilChanged()
        .collect { (isStarted, isSocketConnected, isConnected) ->
            when {
                isStarted && isSocketConnected && isConnected -> {
                    println("🟢 Fully connected")
                }
                isStarted && !isSocketConnected -> {
                    println("🟡 Started but disconnected")
                }
                !isStarted -> {
                    println("🔴 Not started")
                }
                else -> {
                    println("🟡 Partial connection")
                }
            }
        }
}

Filter Multiple Event Types

scope.launch {
    KindlySDK.events
        .filter { event ->
            listOf(
                KindlyEventType.MESSAGE,
                KindlyEventType.BUTTON_CLICK,
                KindlyEventType.UI
            ).contains(event.type)
        }
        .collect { event ->
            println("User interaction event: ${event.type.value}")
        }
}

Combine Events with Other Flows

import kotlinx.coroutines.flow.combine

scope.launch {
    combine(
        KindlySDK.state,
        KindlySDK.events.filter { it.type == KindlyEventType.MESSAGE }
    ) { state, messageEvent ->
        Pair(state, messageEvent)
    }.collect { (state, messageEvent) ->
        if (state.isConnected) {
            println("Received message while connected: $messageEvent")
        }
    }
}

Lifecycle Management

In Activities/Fragments

class ChatActivity : AppCompatActivity() {
    private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Setup event listening
        scope.launch {
            KindlySDK.events.collect { event ->
                // Handle events
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        scope.cancel() // Cancel all coroutines
    }
}

In ViewModels

class ChatViewModel : ViewModel() {

    init {
        // Subscribe to events in ViewModel scope
        viewModelScope.launch {
            KindlySDK.events.collect { event ->
                // Handle events
            }
        }
    }
}

Event Data Models

KindlyEvent

data class KindlyEvent(
    val type: KindlyEventType,
    val timestamp: Long,
    val detail: KindlyEventDetail
)

KindlyEventState

data class KindlyEventState(
    val isChatDisplayed: Boolean,
    val isStarted: Boolean,
    val isSocketConnected: Boolean,
    val isConnected: Boolean,
    val connectionState: String
)

Event Types Enum

enum class KindlyEventType(val value: String) {
    LOAD("kindly:load"),
    MESSAGE("kindly:message"),
    BUTTON_CLICK("kindly:buttonclick"),
    UI("kindly:ui"),
    STATE("kindly:state")
    // Note: GLOBAL_ICON is defined but not implemented in mobile SDKs
}

For more detailed information about event data structures, see the SDK source code documentation.

Best Practices

  1. Use appropriate scope: Use viewModelScope in ViewModels, activity/fragment lifecycle scope in UI components
  2. Handle cancellation: Always cancel coroutines when they're no longer needed
  3. Use Flow operators: Leverage filter, map, distinctUntilChanged to process events efficiently
  4. Error handling: Wrap event collection in try-catch blocks for production apps
  5. Performance: Use conflate() or sample() for high-frequency events if needed

Error Handling

scope.launch {
    try {
        KindlySDK.events.collect { event ->
            // Handle events
        }
    } catch (e: Exception) {
        Log.e("EventHandler", "Error collecting events", e)
    }
}