Listening to Inbox Events
This tutorial explains how to listen to events from the Inbox in your application.
Overview
The Messaging extension provides ways to monitor:
- Inbox-Level Events: Track inbox display events using
InboxEventObserver - Inbox State Changes: Track loading, success, and error states of the inbox container
- Content Card Events: Listen to user interactions with individual cards within the inbox
Inbox-Level Events
The AepInbox composable requires an AepInboxEventObserver to handle inbox-level events such as when the inbox is displayed. The Messaging extension provides InboxEventObserver which implements this interface and automatically tracks inbox display events.
Using InboxEventObserver
Create an InboxEventObserver in your ViewModel with a reference to the provider:
Copied to your clipboardimport androidx.lifecycle.ViewModelimport androidx.lifecycle.viewModelScopeimport com.adobe.marketing.mobile.messaging.InboxEventObserverimport com.adobe.marketing.mobile.messaging.MessagingInboxProviderimport com.adobe.marketing.mobile.messaging.Surfaceimport kotlinx.coroutines.flow.SharingStartedimport kotlinx.coroutines.flow.stateInclass InboxViewModel : ViewModel() {val inboxProvider = MessagingInboxProvider(Surface("inbox"))val observer = InboxEventObserver(inboxProvider)val inboxUIState = inboxProvider.getInboxUI().stateIn(scope = viewModelScope,started = SharingStarted.WhileSubscribed(5000),initialValue = InboxUIState.Loading)}@Composablefun InboxScreen(viewModel: InboxViewModel) {val inboxUIState by viewModel.inboxUIState.collectAsStateWithLifecycle()AepInbox(uiState = inboxUIState,inboxStyle = InboxUIStyle.Builder().build(),observer = viewModel.observer)}
The InboxEventObserver automatically:
- Tracks inbox display events when the inbox is shown
- Prevents duplicate display events for the same inbox state
- Sends tracking data to Adobe Journey Optimizer
- Survives configuration changes (screen rotation, theme changes, etc.)
Inbox State Changes
The InboxUIState sealed interface represents the different states of the inbox. By collecting the state flow from MessagingInboxProvider, you can respond to state transitions.
InboxUIState Types
| State | Description |
|---|---|
InboxUIState.Loading | The inbox is fetching content from the device cache |
InboxUIState.Success | Content loaded successfully (may contain cards or be empty) |
InboxUIState.Error | An error occurred while loading content |
Observing State Changes
Use the Flow returned by getInboxUI() to observe state changes:
Copied to your clipboardclass InboxViewModel : ViewModel() {val inboxProvider = MessagingInboxProvider(Surface("inbox"))val observer = InboxEventObserver(inboxProvider)val inboxUIState: StateFlow<InboxUIState> = inboxProvider.getInboxUI().stateIn(scope = viewModelScope,started = SharingStarted.WhileSubscribed(5000),initialValue = InboxUIState.Loading)}
Responding to State Changes in UI
You can respond to state changes directly in your composable:
Copied to your clipboard@Composablefun InboxScreen(viewModel: InboxViewModel) {val inboxUIState by viewModel.inboxUIState.collectAsStateWithLifecycle()// Respond to state changesLaunchedEffect(inboxUIState) {when (inboxUIState) {is InboxUIState.Loading -> {Log.d("Inbox", "Loading inbox...")}is InboxUIState.Success -> {val successState = inboxUIState as InboxUIState.SuccessLog.d("Inbox", "Loaded ${successState.items.size} cards")}is InboxUIState.Error -> {val errorState = inboxUIState as InboxUIState.ErrorLog.e("Inbox", "Error: ${errorState.error.message}")}}}AepInbox(uiState = inboxUIState,inboxStyle = InboxUIStyle.Builder().build(),observer = viewModel.observer)}
Accessing Success State Data
When the inbox loads successfully, InboxUIState.Success contains:
template: The inbox template with heading, layout, and styling informationitems: List ofAepUIobjects representing the content cards
Copied to your clipboardwhen (val state = inboxUIState) {is InboxUIState.Success -> {val cardCount = state.items.sizeval heading = state.template.headingval layout = state.template.layout// Check if inbox is emptyif (state.items.isEmpty()) {Log.d("Inbox", "Inbox is empty")}}// ...}
Handling Errors
When an error occurs, InboxUIState.Error contains the throwable with error details:
Copied to your clipboardwhen (val state = inboxUIState) {is InboxUIState.Error -> {val errorMessage = state.error.message ?: "Unknown error"// Show error UI or retryshowErrorDialog(errorMessage) {viewModel.refresh()}}// ...}
Content Card Events
Individual content cards within the inbox emit events when users interact with them. To listen to these events:
- Implement a
ContentCardUIEventListenerto handle card events - Wrap it in a
ContentCardEventObserver - Pass the
ContentCardEventObservertoInboxEventObserverin your ViewModel
Example
Copied to your clipboardimport androidx.lifecycle.ViewModelimport androidx.lifecycle.viewModelScopeimport com.adobe.marketing.mobile.aepcomposeui.AepUIimport com.adobe.marketing.mobile.messaging.ContentCardEventObserverimport com.adobe.marketing.mobile.messaging.ContentCardUIEventListenerimport com.adobe.marketing.mobile.messaging.InboxEventObserverimport com.adobe.marketing.mobile.messaging.MessagingInboxProviderimport com.adobe.marketing.mobile.messaging.Surfaceimport kotlinx.coroutines.flow.SharingStartedimport kotlinx.coroutines.flow.stateIn// In ViewModel - observer survives configuration changesclass InboxViewModel : ViewModel() {private val cardCallback = object : ContentCardUIEventListener {override fun onDisplay(aepUI: AepUI<*, *>) {// Handle card display}override fun onInteract(aepUI: AepUI<*, *>,interactionId: String?,actionUrl: String?): Boolean {// Handle card interactionreturn false}override fun onDismiss(aepUI: AepUI<*, *>) {// Handle card dismissal}}val inboxProvider = MessagingInboxProvider(Surface("inbox"))val observer = InboxEventObserver(inboxProvider,ContentCardEventObserver(cardCallback))val inboxUIState = inboxProvider.getInboxUI().stateIn(scope = viewModelScope,started = SharingStarted.WhileSubscribed(5000),initialValue = InboxUIState.Loading)}@Composablefun InboxScreen(viewModel: InboxViewModel) {val inboxUIState by viewModel.inboxUIState.collectAsStateWithLifecycle()AepInbox(uiState = inboxUIState,inboxStyle = InboxUIStyle.Builder().build(),observer = viewModel.observer)}
For detailed information on content card events, implementing ContentCardUIEventListener, and handling actionable URLs, see Listening to Content Card Events.
Best Practices
- Create Observer in ViewModel: Always create the
InboxEventObserverin your ViewModel with a reference to the provider. This ensures the observer survives configuration changes (screen rotation, theme changes, etc.):
Copied to your clipboardclass InboxViewModel : ViewModel() {val inboxProvider = MessagingInboxProvider(Surface("inbox"))val observer = InboxEventObserver(inboxProvider)}
Avoid Heavy Work in Event Handlers: Event handlers are called on the main thread. Keep them lightweight and dispatch heavy work to background threads.
Handle Errors Gracefully: Provide user-friendly error messages and retry options when the inbox fails to load.
Use Lifecycle-aware Collection: Use
collectAsStateWithLifecycle()to automatically stop collecting when the UI is not visible.
