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 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, the handler is already wired for you. When you build your own screen, set it yourself:

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:

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.

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 sets it to true, so screens built on the fragment confirm by default.

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

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

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

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 for more details.

Add Chat to my app: getstream.io/SKILL.md

The fastest way to build with Stream. Start a new project or improve an existing one. Full CLI and documentation integration out of the box.


Ask your agent:

/stream Build me a Social App with Feeds and Moderation.
/stream Any livestream calls running?
/stream Chat Android v7: <Your Question>