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.
MessageCenterTheme {
MessageCenterScreen()
}MessageCenterTheme {
MessageCenterContent()
}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.
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.
Categories