Embed the Message Center

Embed the Message Center directly in your app’s navigation, create custom implementations with full control over design and functionality, and filter messages by named user.

This guide covers how to embed the Message Center directly in your app’s navigation instead of displaying it as an overlay, create custom implementations, and filter messages.

Handling Display Requests

To use a custom Message Center implementation or navigate to your embedded Message Center instead of the default overlay activity, set a listener to handle display requests:

Override default display

Airship.messageCenter.setOnShowMessageCenterListener { messageId: String? ->
    // Navigate to your custom Message Center UI
    // messageId is optional - null means show the full message list
    
    // Return true to prevent the default SDK display
    true
}
MessageCenter.shared().setOnShowMessageCenterListener(messageId -> {
    // Navigate to your custom Message Center UI
    // messageId is optional - null means show the full message list
    
    // Return true to prevent the default SDK display
    return true;
});

Embedding with Jetpack Compose

When embedding Message Center composables, you can choose between an all-in-one screen with a customizable top bar and a content-only Message Center view, without a top bar.

Both composables must be wrapped in a MessageCenterTheme, which allows the theme to be customized.

MessageCenterScreen
MessageCenterTheme {
    MessageCenterScreen()
}

MessageCenterContent
MessageCenterTheme {
    MessageCenterContent()
}

Customizing the theme
val lightColors = MessageCenterColors.lightDefaults(
    background = Color(0xDEDEDE),
    surface = Color(0xFFFFFF),
    accent = Color(0x6200EE),
)

val darkColors = MessageCenterColors.darkDefaults(
    background = Color(0x121212),
    surface = Color(0x1E1E1E),
    accent = Color(0xBB86FC),
)

val typography = MessageCenterTypography.defaults(
    fontFamily = FontFamily(context.resources.getFont(R.font.roboto_regular))
)

MessageCenterTheme(
    colors = if (isSystemInDarkTheme()) darkColors else lightColors,
    typography = typography
) {
    // MessageCenterScreen OR MessageCenterContent
}

Embedding with XML Views

The Message Center UI can be embedded in any FragmentActivity or Fragment using MessageCenterFragment. When embedding the MessageCenterFragment, either use a FragmentContainerView or create the fragment directly.

Using FragmentContainerView

Add the MessageCenterFragment directly in your layout XML:

<androidx.fragment.app.FragmentContainerView
    android:id="@+id/fragment_container_view"
    android:name="com.urbanairship.messagecenter.ui.MessageCenterFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Creating Fragment Programmatically

Alternatively, create and add the fragment programmatically:

Instantiating a MessageCenterFragment

val fragment = MessageCenterFragment.newInstance()
MessageCenterFragment fragment = MessageCenterFragment.newInstance();

You will need to set up display request handling to navigate to your embedded fragment instead of letting Airship launch the MessageCenterActivity.

Integrating with Navigation

For more control over the UI, MessageCenterListFragment and MessageCenterMessageFragment can be used to embed the list and message views separately, each maintaining its own Toolbar.

This example assumes that Jetpack Navigation is being used to navigate between the list and message views, but any navigation method can be used.

Custom Message List Fragment

class CustomMessageListFragment() : MessageCenterListFragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val toolbar = view.findViewById<Toolbar>(R.id.toolbar)
        toolbar.inflateMenu(messageCenterR.menu.ua_message_center_list_pane_menu)

        // Set up the toolbar, if desired.
        setupWithNavController(toolbar, findNavController())

        onMessageClickListener = OnMessageClickListener {
            // Handle message clicks by navigating to the message fragment 
            // (or replace with custom navigation logic).
            findNavController().navigate(
                R.id.action_messageCenterFragment_to_messageFragment, bundleOf(
                    MessageCenterMessageFragment.ARG_MESSAGE_ID to it.id,
                    MessageCenterMessageFragment.ARG_MESSAGE_TITLE to it.title
                )
            )
        }
    }
}

Custom Message Fragment

class CustomMessageFragment : MessageCenterMessageFragment(R.layout.fragment_inbox_message) {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        toolbar?.run {
            // Inflate the default menu
            inflateMenu(messageCenterR.menu.ua_message_center_message_pane_menu)

            // Set up the toolbar, if desired.
            setupWithNavController(toolbar, findNavController(view))
        }

        // Handle message deletion from the message view
        onMessageDeletedListener = OnMessageDeletedListener {
            // Handle message deletion by navigating back to the message list fragment 
            // (or replace with custom navigation logic).
            findNavController().popBackStack()

            // Optionally show a toast confirmation message
            context?.run {
                val msg = getQuantityString(messageCenterR.plurals.ua_mc_description_deleted, 1, 1)
                Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
            }
        }
    }
}

These custom fragments can then be embedded into your app UI using FragmentContainerView or by using FragmentManager programmatically. You will need to set up display request handling to navigate to your custom message list fragment instead of letting Airship launch the MessageCenterActivity.

Layout Support

The Message Center provides automatic support for both single-pane and two-pane layouts, when on large display sizes or foldable devices. In two-pane mode, the selected message is highlighted in the list and displayed in the detail pane. Right-to-left (RTL) layout is also supported when android:supportsRtl="true" is set in your application manifest.

Message Center Filtering

Sometimes it can be useful to filter the contents of the Message Center according to some predetermined pattern. To facilitate this, use the shared MessageCenter instance to set a predicate. Once set, only messages that match the predicate will be displayed.

Filter by Named User

Filter messages to show only those for the current named user:

Filter by named user

MessageCenter.shared().predicate = Predicate { message ->
    val namedUserID = Airship.shared().contact.namedUserID
    if (namedUserID == null) {
        return@Predicate false
    }
    
    // Check if message has matching named_user_id in extras
    val extras = message.extras
    val messageNamedUserID = extras?.get("named_user_id") as? String
    messageNamedUserID == namedUserID
}
MessageCenter.shared().setPredicate(message -> {
    String namedUserID = Airship.shared().getContact().getNamedUserID();
    if (namedUserID == null) {
        return false;
    }
    
    // Check if message has matching named_user_id in extras
    Map<String, String> extras = message.getExtras();
    String messageNamedUserID = extras != null ? extras.get("named_user_id") : null;
    return messageNamedUserID != null && messageNamedUserID.equals(namedUserID);
});

Custom Filtering

Create custom predicates for any filtering logic:

Custom filtering

MessageCenter.shared().predicate = Predicate { message ->
    // Example: Only show messages with "cool" in the title
    message.title.contains("cool")
}
MessageCenter.shared().setPredicate(message -> 
    // Example: Only show messages with "cool" in the title
    message.getTitle().contains("cool")
);

Custom Message Center Implementation

For complete control over Message Center placement and navigation, create a custom implementation using the Message Center components.

Key Components

MessageCenter
The main entry point for fetching messages and handling callbacks. Access via MessageCenter.shared().
Inbox
Provides an interface for retrieving messages asynchronously and accessing the local message array. Access via MessageCenter.shared().inbox.
 Note

The message list uses a local database. Message objects are refreshed with the list. Don’t hold onto individual message instances indefinitely.

Message
Model object representing an individual message. Instances don’t contain the message body—they point to authenticated URLs that should be displayed in a webview.
Display Callbacks
Set MessageCenter.shared().setOnShowMessageCenterListener() to handle when messages should be displayed.