---
title: "Android custom inbox mobile"
slug: "android-custom-inbox"
updated: 2026-06-09T22:53:42Z
published: 2026-06-09T22:53:42Z
canonical: "docs.xtremepush.com/android-custom-inbox"
stale: true
---

> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xtremepush.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 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:

**Java**

```java
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
    }
}
```

**Kotlin**

```kotlin
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.

```java
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.

```java
mPushConnector.inboxListWithOffset(context, LIMIT, OFFSET);
```

## InboxMessageListItem

Each item in the list has the following fields:

| Field | Type | Description |
| --- | --- | --- |
| `identifier` | `int` | Unique message ID |
| `isOpened` | `boolean` | Whether the message has been marked as opened |
| `isClicked` | `boolean` | Whether the message has been marked as clicked |
| `isDelivered` | `boolean` | Whether the message has been delivered |
| `createTimestamp` | `Long` | Unix timestamp of when the message was created |
| `expirationTimestamp` | `Long` | Unix timestamp of expiry, or null if the message does not expire |
| `style` | `HashMap&lt;String, String&gt;` | Style values — keys: `bg` (background colour), `title_bg` (title bar colour) |
| `isCard` | `boolean` | `true` for card layout (full-width banner image), `false` for alert layout (small thumbnail) |
| `message` | `Message` | Contains message content and tap action — see below |

## Working with message content

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

| Field | Type | Set by |
| --- | --- | --- |
| `id` | `String` | Platform (system-assigned) |
| `campaignId` | `String` | Platform (system-assigned) |
| `title` | `String` | Campaign — Push title field |
| `text` | `String` | Campaign — Push text / body field |
| `icon` | `String` | Campaign — Push icon field (falls back to app icon, then project icon, if not set) |
| `deeplink` | `String` | Campaign — present when action type is **Deeplink**; `null` otherwise |
| `url` | `String` | Campaign — present when action type is **URL**; `null` otherwise |
| `data` | `HashMap&lt;String, String&gt;` | 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:

| Field | Type | Set by |
| --- | --- | --- |
| `isCard` | `boolean` | Campaign — inbox type setting. `true` = card layout (full-width banner), `false` = alert layout (small thumbnail) |
| `style.get("bg")` | `String` | Campaign — inbox style background colour |
| `style.get("title_bg")` | `String` | Campaign — 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`.

**Java**

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

**Kotlin**

```kotlin
val ctaLabel = item.message.data?.get("cta_label")
ctaLabel?.let { button.text = it }
```

> [!NOTE]
> 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.

**Java**

```java
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
    }
}
```

**Kotlin**

```kotlin
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.

**Java**

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

**Kotlin**

```kotlin
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.

**Java**

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

**Kotlin**

```kotlin
mPushConnector.reportMessageClicked(item.message, null, null)
```

## Delete an inbox message

**Java**

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

**Kotlin**

```kotlin
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.

**Java**

```java
int badge = mPushConnector.getInboxBadge();
```

**Kotlin**

```kotlin
val badge = mPushConnector.getInboxBadge()
```
