This is documentation for Stream Chat Android SDK v5, which is nolonger actively maintained. For up-to-date documentation, see the latest version (v6).

Message Input

MessageInputView is the view used to create a new chat message and send it to a channel.

LightDark
First custom MessageInputView example
First custom MessageInputView example

It supports the following features:

  • Emoticons
  • Attachments
  • Slash Commands
  • Typing events
  • Editing messages
  • Threads
  • Mentions
  • Replies

Usage

To use MessageInputView, include it in your XML layout. It usually goes below a MessageListView.

<io.getstream.chat.android.ui.message.input.MessageInputView
        android:id="@+id/messageInputView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/messageListView"
        />

The recommended way of setting up the input view is by binding it to a MessageInputViewModel. This will make it fully functional by setting up any necessary listeners and data handling.

// Instantiate the ViewModel for a given channel
val factory: MessageListViewModelFactory = MessageListViewModelFactory(cid = "channelType:channelId")
val viewModel: MessageInputViewModel by viewModels { factory }
// Bind it with MessageInputView
viewModel.bindView(messageInputView, viewLifecycleOwner)

Handling Actions

Actions can be handled by setting listeners on this view. For example, you can handle clicks of the send button:

messageInputView.setOnSendButtonClickListener {
    // Handle send button click
}

Typing updates should be sent sparingly as a way of saving valuable API calls. Luckily, we offer such behavior out of the box by using our DefaultTypingUpdatesBuffer in order to intelligently make start and stop typing API calls.

If you want to implement your own buffering mechanism you can pass in an implementation of the TypingUpdatesBuffer interface instead:

messageInputView.setTypingUpdatesBuffer(object : TypingUpdatesBuffer {
    override fun onKeystroke() {
        // Your custom implementation of TypingUpdatesBuffer
    }

    override fun clear() {
        // Your custom implementation of TypingUpdatesBuffer
    }
})

You can show a custom error when the maximum message length is exceeded:

messageInputView.setMaxMessageLengthHandler { messageText, messageLength, maxMessageLength, maxMessageLengthExceeded ->
    if (maxMessageLengthExceeded)  {
        // Show custom max-length error
    } else {
        // Hide custom max-length error
    }
}

You can also change the handler that sends messages. By default, this is set up by bindView and calls into the ViewModel to perform these actions.

messageInputView.setSendMessageHandler(
    object : MessageInputView.MessageSendHandler {
        override fun sendMessage(messageText: String, messageReplyTo: Message?) {
            // Handle send message
        }

        override fun sendMessageWithAttachments(
            message: String,
            attachmentsWithMimeTypes: List<Pair<File, String?>>,
            messageReplyTo: Message?,
        ) {
            // Handle message with attachments
        }

        override fun sendToThreadWithAttachments(
            parentMessage: Message,
            message: String,
            alsoSendToChannel: Boolean,
            attachmentsWithMimeTypes: List<Pair<File, String?>>,
        ) {
           // Handle message to thread with attachments
        }

        override fun sendToThread(parentMessage: Message, messageText: String, alsoSendToChannel: Boolean) {
            // Handle message to thread
        }

        override fun editMessage(oldMessage: Message, newMessageText: String) {
            // Handle edit message
        }

        override fun dismissReply() {
            // Handle dismiss reply
        }
    }
)

Customization

MessageInputView can be customized using:

  • XML attributes
  • Style transformations
  • Methods called on MessageInputView’s instance

Using XML Attributes

Many attributes of this View can be configured, like changing its color, the border and the color of the message input, fonts, components visibility, and so on. The full list of available attributes can be found here.

Here’s an example of setting some custom attributes:

<io.getstream.chat.android.ui.message.input.MessageInputView
    android:id="@+id/messageInputView"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:background="@color/grey_light"
    app:streamUiMessageInputEditTextBackgroundDrawable="@drawable/stream_ui_shape_edit_text_squared"
    app:streamUiMessageInputTextStyle="italic"
    app:streamUiMessageInputDividerBackgroundDrawable="@drawable/stream_ui_divider_green"
    app:streamUiMessageInputTextColor="@color/stream_ui_white"
    />

This will create this version of MessageInputView:

First custom MessageInputView example

Here’s another example with different attributes set:

<io.getstream.chat.android.ui.message.input.MessageInputView
    android:id="@+id/messageInputView"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/messageListView"
    app:streamUiMessageInputEditTextBackgroundDrawable="@drawable/stream_ui_shape_edit_text_rounded"
    app:streamUiMessageInputTextStyle="italic"
    android:background="@color/stream_ui_black"
    app:streamUiMessageInputDividerBackgroundDrawable="@drawable/stream_ui_divider_green"
    app:streamUiMessageInputTextColor="@color/stream_ui_white"
    />

This produces the following styling:

Second custom MessageInputView example

Different configurations can be used to achieve the desired appearance of MessageListView. If you don’t need to change this View appearance at runtime, XML should be enough. But if you need to able to customize it at runtime, then you can use MessageInputViewStyle as described in the next section.

Using Style Transformations

You can use TransformStyle to apply global style transformations to all MessageInputView instances. For example, you can create set up a messageInputStyleTransformer like this one to change the input text color:

TransformStyle.messageInputStyleTransformer = StyleTransformer { viewStyle ->
    viewStyle.copy(
        messageInputTextStyle = viewStyle.messageInputTextStyle.copy(
            color = ContextCompat.getColor(
                requireContext(),
                R.color.stream_ui_white,
            ),
        )
    )
}

The transformer should be set before the View is rendered to make sure that the new style was applied.

Changing Send Message Button

Send message button drawable can be changed dynamically based on the current input mode. You can set the MessageInputModeListener on MessageInputView instance and change drawables based on the current input mode. Keep in mind that MessageInputView displays two different send message buttons:

  • sendMessageButtonEnabled - when the user is able to send a message
  • sendMessageButtonDisabled - when the user is not able to send a message (send button is disabled)

Drawable will override the one provided either by attributes or TransformStyle.messageInputStyleTransformer. Make sure to set different drawables in all input modes.

messageInputView.setMessageInputModeListener {
    when(it) {
        is MessageInputView.InputMode.Edit -> {
            messageInputView.setSendMessageButtonEnabledDrawable(context.getDrawable(R.drawable.stream_ui_ic_check_single)!!)
            messageInputView.setSendMessageButtonDisabledDrawable(context.getDrawable(R.drawable.stream_ui_ic_close)!!)
        }
        else -> {
            messageInputView.setSendMessageButtonEnabledDrawable(context.getDrawable(R.drawable.stream_ui_ic_filled_up_arrow)!!)
            messageInputView.setSendMessageButtonDisabledDrawable(context.getDrawable(R.drawable.stream_ui_ic_filled_right_arrow)!!)
        }
    }
}

Creating Custom Suggestion Items

The suggestion list popup is used to provide autocomplete suggestions for commands and mentions. To customize the appearance of suggestion list items you need to provide your own SuggestionListViewHolderFactory. Here’s an example of a custom command item that displays just a command name:

  1. Create the item_command.xml layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?attr/selectableItemBackground"
    android:paddingStart="16dp"
    android:paddingEnd="16dp"
    android:paddingTop="8dp"
    android:paddingBottom="8dp">

    <TextView
        android:id="@+id/commandNameTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</FrameLayout>
  1. Create a custom ViewHolder that extends BaseSuggestionItemViewHolder, and a custom ViewHolder factory which extends SuggestionListItemViewHolderFactory:
class CustomCommandViewHolder(
    private val binding: ItemCommandBinding,
) : BaseSuggestionItemViewHolder<SuggestionListItem.CommandItem>(binding.root) {

    override fun bindItem(item: SuggestionListItem.CommandItem) {
        binding.commandNameTextView.text = item.command.name
    }
}

class CustomSuggestionListViewHolderFactory : SuggestionListItemViewHolderFactory() {
    override fun createCommandViewHolder(
        parent: ViewGroup,
    ): BaseSuggestionItemViewHolder<SuggestionListItem.CommandItem> {
        return ItemCommandBinding
            .inflate(LayoutInflater.from(parent.context), parent, false)
            .let(::CustomCommandViewHolder)
    }
}
  1. Set the custom ViewHolder factory on MessageInputView:
messageInputView.setSuggestionListViewHolderFactory(CustomSuggestionListViewHolderFactory())

This produces the following result:

Custom suggestion item

It is possible to change the search method in the mention list. The default class: DefaultUserLookupHandler removes diacritics and uses Levenstein distance to include similar words in the results.

val defaultUserLookupHandler = DefaultUserLookupHandler(users)
messageInputView.setUserLookupHandler(defaultUserLookupHandler)

Transliteration

You can add transliteration to DefaultUserLookupHandler by setting the desired id for transliteration inside DefaultStreamTransliterator and passing it to DefaultUserLookupHandler constructor.

DefaultStreamTransliterator only supports transliteration prior to API 29 (Android Q) and it will not apply transliteration for APIs smaller than this.

val defaultUserLookupHandler = DefaultUserLookupHandler(
    users,
    DefaultStreamTransliterator("Cyrl-Latn")
)
messageInputView.setUserLookupHandler(defaultUserLookupHandler)

You can also use your own implementation of StreamTransliterator to add support for lowers APIs. An interesting library is ICU4J

Attachment Size Limits

There is a limit for the size of attachments you can send. The default value is 100 MB and selecting a file larger than that limit will notify the user:

Big file feedback

You can use MessageInputViewStyle.attachmentMaxFileSize to change the maximum allowed size of attachments. However, the same default limit applies to Stream Chat’s CDN as well. To send larger attachments, you can use your own CDN.

You can listen for large attachments being added to the list of attachments (for example, to present a custom message to the user in this case) with MessageInputView.listenForBigAttachments.

© Getstream.io, Inc. All Rights Reserved.