# Setting up Pushwoosh Inbox UI Android

Pushwoosh Inbox UI ships a ready-made Android inbox screen (the "bell icon" App Inbox) on top of the Pushwoosh inbox backend. It renders messages in one of five card types, supports inline CTA buttons, and is open for style customization through XML attributes or code.

## Prerequisites

- The [base Pushwoosh Android SDK](/developer/pushwoosh-sdk/android-sdk/firebase-integration/integrate-pushwoosh-android-sdk/) already integrated and sending pushes.
- Kotlin support in your app module (`apply plugin: 'kotlin-android'`).

## Add the library

Add the Kotlin plugin and the two Pushwoosh modules to your app's `build.gradle`:

```groovy title="build.gradle"
apply plugin: 'kotlin-android'

dependencies {
    implementation 'com.pushwoosh:pushwoosh-inbox:6.+'
    implementation 'com.pushwoosh:pushwoosh-inbox-ui:6.+'
}
```

Pin `pushwoosh-inbox` and `pushwoosh-inbox-ui` to the same version as your existing `com.pushwoosh:pushwoosh` dependency. Replace `+` with the current version of [Pushwoosh Android SDK](https://github.com/Pushwoosh/pushwoosh-android-sdk/releases/latest).

If your app uses ProGuard for code shrinking, keep the inbox plugin class:

```proguard title="proguard-rules.pro"
-keep public class com.pushwoosh.inbox.PushwooshInboxPlugin {
 *;
}
```

## Show the inbox

Present the inbox as a standalone screen, or embed it as a fragment inside your own layout.

As an activity:

```kotlin
startActivity(Intent(this, InboxActivity::class.java))
```

As a fragment:

```kotlin
supportFragmentManager.beginTransaction()
    .replace(R.id.inboxContainer, PushwooshInboxUi.createInboxFragment())
    .commitAllowingStateLoss()
```

## Card types

Inbox UI resolves a card type per message. The resolver reads `displayType` from the `data` object of the push payload, which the SDK delivers under `actionParams`. A `displayType` that names a known card type always renders that type, falling back to `classic` only if the type's required fields are missing. This resolution does not depend on the heuristic setting below.

When `displayType` is absent, the message renders as a plain row unless you opt in to the image/text heuristic:

```kotlin
PushwooshInboxStyle.richCardsHeuristicEnabled = true
```

With the heuristic on, an image with no title renders `banner`, an image with title and body renders `captioned`, and anything else falls back to `classic`.

| `displayType` | Appearance | Required payload field | Degrades to |
|---|---|---|---|
| `banner` | Full-bleed image, no text | image (message icon or `data.attachment`) | `classic` when no image |
| `captioned` | Image on top, title + body below | image, message `title` and `content` | `classic` when the image, title or body is missing |
| `classic` | Icon + title + body | — (title, body and icon expected) | — |
| `carousel` | Swipeable multi-image gallery | message `title` and `content`, `data.carousel` (1–5 slides) | `classic` when no slides or no title/body |
| `video` | Poster with play badge, full-screen player on tap | `data.video` (`url` + optional `poster`) | `classic` when no descriptor |

The Apple Wallet card from the iOS InboxKit has no Android counterpart. A message with `displayType: "wallet"` always renders as `classic` on Android.

### Carousel card

Slides live in `data.carousel`. Each slide needs an `image`. `title` (caption overlay) and `url` (opened on tap) are optional. A slide without an image is dropped, and at most 5 slides are shown.

```json title="POST https://api.pushwoosh.com/json/1.3/createMessage"
{
  "request": {
    "application": "XXXXX-XXXXX",
    "auth": "API_TOKEN",
    "notifications": [{
      "send_date": "now",
      "content": "Swipe through this week's drops",
      "inbox_days": 7,
      "data": {
        "displayType": "carousel",
        "carousel": [
          { "image": "https://cdn.example.com/inbox/1.jpg", "title": "New in", "url": "myapp://product/1" },
          { "image": "https://cdn.example.com/inbox/2.jpg", "title": "On sale", "url": "myapp://product/2" }
        ]
      },
      "platforms": [3]
    }]
  }
}
```

### Video card

The descriptor lives in `data.video`: `url` is required, `poster` is an optional preview image. Tapping the poster opens a full-screen player.

```json title="POST https://api.pushwoosh.com/json/1.3/createMessage"
{
  "request": {
    "application": "XXXXX-XXXXX",
    "auth": "API_TOKEN",
    "notifications": [{
      "send_date": "now",
      "content": "Tap to play",
      "inbox_days": 7,
      "data": {
        "displayType": "video",
        "video": {
          "url": "https://cdn.example.com/inbox/clip.mp4",
          "poster": "https://cdn.example.com/inbox/poster.jpg"
        }
      },
      "platforms": [3]
    }]
  }
}
```

## Read custom data from a message

To make a push appear in the inbox, the [Messages API](/developer/api-reference/messages-api/) `createMessage` request must include `inbox_image`, `inbox_date`, or `inbox_days`. Without one of those fields, the push is delivered as a regular notification and never reaches the inbox feed. Free-form custom data goes under `data`, which the SDK exposes as `actionParams` on `InboxMessage`:

```kotlin
PushwooshInboxUi.onMessageClickListener = OnInboxMessageClickListener { message ->
    val params = message.actionParams?.let { JSONObject(it) }
    val promoId = params?.optString("promo_id")
    if (!promoId.isNullOrEmpty()) {
        navigateToPromo(promoId)
    }
}
```

## Add inline CTA buttons

A message can carry inline call-to-action buttons inside `data.buttons`:

```json title="POST https://api.pushwoosh.com/json/1.3/createMessage"
{
  "request": {
    "application": "XXXXX-XXXXX",
    "auth": "API_TOKEN",
    "notifications": [{
      "send_date": "now",
      "content": "Tap a button to claim or save",
      "inbox_image": "https://cdn.example.com/inbox/promo.png",
      "inbox_days": 7,
      "data": {
        "displayType": "captioned",
        "promo_id": "SUMMER2026",
        "buttons": [
          { "title": "Claim", "url": "https://example.com/promo/SUMMER2026" },
          { "title": "Read",  "action": "markRead" },
          { "title": "Save",  "action": "custom", "tag": "save_promo" }
        ]
      },
      "platforms": [3]
    }]
  }
}
```

Each button needs a `title`. Resolution follows this priority:

- `action` set to `dismiss` or `markRead` (case-insensitive) runs that action.
- Otherwise, a non-empty, parseable `url` resolves to an `openURL` action.
- Otherwise, the tap resolves to a custom action, and every extra key on the button object is forwarded to your listener as its payload.

Intercept taps from `PushwooshInboxUi.onButtonClickListener`. Return `true` to let the SDK perform the button's default action, `false` to suppress it:

```kotlin
PushwooshInboxUi.onButtonClickListener = OnInboxButtonClickListener { message, button ->
    when (val action = button.action) {
        is InboxCardButton.Action.OpenUrl -> true
        InboxCardButton.Action.Dismiss, InboxCardButton.Action.MarkRead -> true
        is InboxCardButton.Action.Custom -> {
            when (action.payload.optString("tag")) {
                "save_promo" -> saveCurrentPromoLocally(message)
            }
            true
        }
    }
}
```

## Customize the style

Set colors, fonts, and empty/error states from code through `PushwooshInboxStyle`:

```kotlin
PushwooshInboxStyle.accentColor = ContextCompat.getColor(this, R.color.brand_accent)
PushwooshInboxStyle.titleColor = ContextCompat.getColor(this, R.color.brand_title)
PushwooshInboxStyle.listEmptyText = "You have no messages yet"
PushwooshInboxStyle.showToolbar = false
```

Or apply the same set of attributes as a theme, listed in [attrs.xml](https://github.com/Pushwoosh/pushwoosh-inbox-ui-android-sdk/blob/master/InboxUiLibrary/pushwoosh-inbox-ui/src/main/res/values/attrs.xml): `inboxAccentColor`, `inboxTitleColor`, `inboxBackgroundColor`, `inboxDefaultIcon`, and the rest of the color and appearance attributes. Both approaches, plus a full sample app, are in the [pushwoosh-inbox-ui-android-sdk InboxSample](https://github.com/Pushwoosh/pushwoosh-inbox-ui-android-sdk/tree/master/InboxSample) repo.

## Unread messages badge

<Aside type="note">
This is an in-app badge, for example on your own Inbox icon inside the app's UI. It does not touch the app icon badge. No Pushwoosh SDK ties the unread count to the app icon.
</Aside>

```kotlin
PushwooshInbox.unreadMessagesCount { result ->
    if (result.isSuccess) {
        val count = result.data
    } else {
        Log.e("App", "Failed to get unread count", result.exception)
    }
}
```