Android custom inbox mobile

Implement a custom inbox in your Android application.

By default, the XtremePush SDK renders the inbox as a built-in WebView. A custom inbox replaces that with your own native UI, giving you full control over layout and interaction.

When you implement a custom inbox, the SDK continues to handle:

  • Fetching and paginating inbox messages from the platform
  • Deleting messages
  • Tracking badge counts
  • Sending opened/clicked analytics events (when you call the reporting methods)

Your app becomes responsible for:

  • Rendering each message (title, body, image, icon, card vs alert layout)
  • Handling tap actions (deeplink navigation, opening URLs)
  • Calling the reporting methods at the right time

Setup

  1. Implement the InboxListListener interface in your Application class:
public class XPushApplication extends Application implements InboxListListener {

    @Override
    public void inboxListReceived(ArrayList<InboxMessageListItem> inboxList, WeakReference<Context> uiReference) {
        // render your inbox UI
    }

    @Override
    public void inboxListFailed() {
        // handle error
    }
}
class XPushApplication : Application(), InboxListListener {

    override fun inboxListReceived(inboxList: ArrayList<InboxMessageListItem>, uiReference: WeakReference<Context>) {
        // render your inbox UI
    }

    override fun inboxListFailed() {
        // handle error
    }
}
  1. Register the listener when initialising the SDK. If you don't call setInboxListListener, the SDK uses its built-in WebView inbox.
new PushConnector.Builder(XPUSH_APP_KEY, GOOGLE_PROJECT_NUMBER)
    .setInboxListListener(this)
    .create(this);

Retrieve inbox messages

Call inboxListWithOffset to fetch messages. Results are delivered to the inboxListReceived callback.

mPushConnector.inboxListWithOffset(context, LIMIT, OFFSET);

InboxMessageListItem

Each item in the list has the following fields:

FieldTypeDescription
identifierintUnique message ID
isOpenedbooleanWhether the message has been marked as opened
isClickedbooleanWhether the message has been marked as clicked
isDeliveredbooleanWhether the message has been delivered
createTimestampLongUnix timestamp of when the message was created
expirationTimestampLongUnix timestamp of expiry, or null if the message does not expire
styleHashMap<String, String>Style values — keys: bg (background colour), title_bg (title bar colour)
isCardbooleantrue for card layout (full-width banner image), false for alert layout (small thumbnail)
messageMessageContains message content and tap action — see below

Working with message content

The message field on each InboxMessageListItem contains the full message content:

FieldTypeSet by
idStringPlatform (system-assigned)
campaignIdStringPlatform (system-assigned)
titleStringCampaign — Push title field
textStringCampaign — Push text / body field
iconStringCampaign — Push icon field (falls back to app icon, then project icon, if not set)
deeplinkStringCampaign — present when action type is Deeplink; null otherwise
urlStringCampaign — present when action type is URL; null otherwise
dataHashMap<String, String>Campaign — Custom payload fields (from the data key in the message JSON)

deeplink and url are mutually exclusive — only one will be set depending on the action type configured in the campaign. If neither is set, the message has no tap action.

Layout and style are on the list item:

FieldTypeSet by
isCardbooleanCampaign — inbox type setting. true = card layout (full-width banner), false = alert layout (small thumbnail)
style.get("bg")StringCampaign — inbox style background colour
style.get("title_bg")StringCampaign — inbox style title bar colour

Custom payload fields

If your inbox UI requires data beyond the standard fields — for example a CTA label, a secondary URL, or a badge value — add custom fields in the campaign's Custom payload (payload_add) section. These are delivered in item.message.data.

// Reading a custom payload field
String ctaLabel = item.message.data != null ? item.message.data.get("cta_label") : null;
if (ctaLabel != null) {
    button.setText(ctaLabel);
}
val ctaLabel = item.message.data?.get("cta_label")
ctaLabel?.let { button.text = it }
📘

Campaign Templates

Because your custom inbox UI is built to render a specific content structure, consider defining a reusable inbox campaign template in the Xtremepush platform that consistently populates the fields your UI expects. For example, if your card layout always shows a title, body, background colour, deeplink action, and a custom CTA label, define these as required fields for any inbox campaign targeting your custom implementation.

Handle message actions (deeplink & URL)

When a user taps a message, inspect item.message to determine what to do. The SDK does not fire DeeplinkListener callbacks automatically in a custom inbox — your app handles navigation directly.

Always call reportMessageClicked so that the interaction is registered on the platform.

private void onInboxItemTapped(InboxMessageListItem item) {

    // Register the click with the platform
    mPushConnector.reportMessageClicked(item.message, null, null);

    if (item.message.deeplink != null && !item.message.deeplink.isEmpty()) {
        // Route through your app's navigation handler
        // MyRouter.navigate(item.message.deeplink);

    } else if (item.message.url != null && !item.message.url.isEmpty()) {
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(item.message.url));
        context.startActivity(intent);

    } else {
        // No tap action configured — message is informational only
    }
}
private fun onInboxItemTapped(item: InboxMessageListItem) {

    // Register the click with the platform
    mPushConnector.reportMessageClicked(item.message, null, null)

    when {
        !item.message.deeplink.isNullOrEmpty() -> {
            // Route through your app's navigation handler
            // MyRouter.navigate(item.message.deeplink)
        }
        !item.message.url.isNullOrEmpty() -> {
            val intent = Intent(Intent.ACTION_VIEW, Uri.parse(item.message.url))
            startActivity(intent)
        }
        else -> {
            // No tap action configured — message is informational only
        }
    }
}

Report inbox message opened

Call this when a message becomes visible to the user (e.g. scrolled into view in a feed) without a deliberate tap.

mPushConnector.reportMessageOpened(item.message, null, null);
mPushConnector.reportMessageOpened(item.message, null, null)

Report inbox message clicked

Call this when a user deliberately taps a message. This also automatically marks the message as opened. Do not call reportMessageOpened separately for tapped messages.

mPushConnector.reportMessageClicked(item.message, null, null);
mPushConnector.reportMessageClicked(item.message, null, null)

Delete an inbox message

mPushConnector.deleteInboxMessage(String.valueOf(item.identifier), activity);
mPushConnector.deleteInboxMessage(item.identifier.toString(), activity)

Get inbox badge number

Returns the cached unread count — use this to display a badge without making a network request.

int badge = mPushConnector.getInboxBadge();
val badge = mPushConnector.getInboxBadge()