# Message Moderation

Stream Chat can moderate messages before they are delivered (bounce) and lets users report messages to moderators (flag). This page shows how to customize the XML UI Components for moderated messages. For how moderated messages are represented in the SDK, see the [Moderation](https://getstream.io/chat/docs/sdk/android/guides/moderation/) guide.

## Bounced Messages

A bounced message renders as a normal message item: `MessageListItemViewTypeMapper` excludes bounces from the error message view type. Only the long click path differs. `MessageListView` recognises the message through `isModerationError` and calls `showModeratedMessageDialog(message)`, which presents a dialog with the **Send anyway**, **Edit message** and **Delete message** options.

Selecting an option does not perform the action by itself. `MessageListView` forwards it to the `ModeratedMessageOptionHandler` you set with `setModeratedMessageHandler`. If you use [`ChannelFragment`](https://getstream.io/chat/docs/sdk/android/ui/message-components/message-list-screen/), the handler is already wired for you. When you build your own screen, set it yourself:

```kotlin
messageListView.setModeratedMessageHandler { message, option ->
    when (option) {
        SendAnyway -> messageListViewModel.onEvent(MessageListViewModel.Event.RetryMessage(message))
        EditMessage -> messageComposerViewModel.performMessageAction(Edit(message))
        DeleteMessage -> messageListViewModel.onEvent(MessageListViewModel.Event.DeleteMessage(message))
        else -> Unit
    }
}
```

>
> **Note:** `MessageListViewModel.bindView` does not set this handler. If it is missing, selecting an option throws an `IllegalStateException`.
>

### Replacing the Dialog

The dialog itself is internal to the SDK and its option list is fixed, so you cannot subclass it or add options to it. To use your own UI, replace the long click behaviour with `setOnModeratedMessageLongClickListener` and return `true` to signal that you handled the click:

```kotlin
messageListView.setOnModeratedMessageLongClickListener { message ->
    MaterialAlertDialogBuilder(requireContext())
        .setTitle("Message not sent")
        .setMessage("This message goes against our community guidelines.")
        .setPositiveButton("Edit message") { _, _ ->
            messageComposerViewModel.performMessageAction(Edit(message))
        }
        .setNegativeButton("Delete message") { _, _ ->
            messageListViewModel.onEvent(MessageListViewModel.Event.DeleteMessage(message))
        }
        .setNeutralButton("Cancel", null)
        .show()
    true
}
```

The return value tells `MessageListView` whether the long click was consumed. Passing `null` instead of a listener removes the moderation handling entirely, so long pressing a bounced message does nothing.

>
> **Note:** If you need custom options in the moderation dialog, use the Compose UI Components, where `ModeratedMessageDialog` accepts a custom option list. See [Message Moderation](https://getstream.io/chat/docs/sdk/android/compose/message-components/message-moderation/).
>

## Flag Action

The **Flag Message** action is added to the message options overlay for messages from other users, when the user has the `flag-message` channel capability.

Whether a confirmation dialog appears first depends on `streamUiFlagMessageConfirmationEnabled`. It is `false` on a bare `MessageListView`, but the layout behind [`ChannelFragment`](https://getstream.io/chat/docs/sdk/android/ui/message-components/message-list-screen/) sets it to `true`, so screens built on the fragment confirm by default.

To hide the action, disable it in your `MessageListView` style:

```xml
<io.getstream.chat.android.ui.feature.messages.list.MessageListView
    android:id="@+id/messageListView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:streamUiFlagMessageEnabled="false" />
```

To flag with a reason, set a `MessageFlagHandler`. Route it through the view model rather than calling `ChatClient.flagMessage` directly, so the result still reaches `handleFlagMessageResult` and the error feedback keeps working:

```kotlin
messageListView.setMessageFlagHandler { message ->
    messageListViewModel.onEvent(
        MessageListViewModel.Event.FlagMessage(
            message = message,
            reason = "inappropriate",
            customData = emptyMap(),
            resultHandler = messageListView::handleFlagMessageResult,
        ),
    )
}
```

>
> **Note:** `MessageListViewModel.bindView` sets this handler itself, and the setter is a plain assignment. Set your own handler **after** `bindView`, otherwise it is silently replaced.
>

Two more handlers let you customize the surrounding flow without replacing the flag call itself:

- `setConfirmFlagMessageHandler` replaces the confirmation dialog. Invoke the `confirmCallback` to proceed with flagging. It only runs when `streamUiFlagMessageConfirmationEnabled` is `true`.
- `setFlagMessageResultHandler` receives the `Result<Flag>` of the flag call, so you can show your own success or error feedback.

```kotlin
messageListView.setConfirmFlagMessageHandler { message, confirmCallback ->
    MaterialAlertDialogBuilder(requireContext())
        .setTitle("Report message?")
        .setMessage("Our moderators will review this message.")
        .setPositiveButton("Report") { _, _ -> confirmCallback() }
        .setNegativeButton("Cancel", null)
        .show()
}
```

For more details about message options, see [MessageListView](https://getstream.io/chat/docs/sdk/android/ui/message-components/message-list/).

## Changing the Texts

If you only need to change the texts, you don't have to set any handler. Redefine the following keys in your app's `strings.xml`:

| Key                                                        | Default value                                                                                                                       |
| ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `stream_ui_moderation_dialog_title`                        | Failed to send message                                                                                                              |
| `stream_ui_moderation_dialog_description`                  | This message might violate our moderation policy. Are you sure you want to send it? Please take a look at our Community Guidelines. |
| `stream_ui_moderation_dialog_send`                         | Send anyway                                                                                                                         |
| `stream_ui_moderation_dialog_edit`                         | Edit message                                                                                                                        |
| `stream_ui_moderation_dialog_delete`                       | Delete message                                                                                                                      |
| `stream_ui_message_list_flag_message`                      | Flag Message                                                                                                                        |
| `stream_ui_message_list_flag_confirmation_title`           | Flag Message                                                                                                                        |
| `stream_ui_message_list_flag_confirmation_message`         | Do you want to send a copy of this message to a moderator for further investigation?                                                |
| `stream_ui_message_list_flag_confirmation_positive_button` | Flag                                                                                                                                |
| `stream_ui_message_list_flag_confirmation_negative_button` | Cancel                                                                                                                              |
| `stream_ui_message_list_error_flag_message`                | Failed to flag message                                                                                                              |

See the [localization guide](https://getstream.io/chat/docs/sdk/android/ui/guides/custom-translations/) for more details.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/android/ui/guides/message-moderation/](https://getstream.io/chat/docs/sdk/android/ui/guides/message-moderation/).