Message Input View

Last Edit: Jun 06 2020

The MessageInput View is used to enter a chat message. It implements the following features:

  • Emoticons

  • Attachments

  • Slash Commands

  • Typing events

  • Editing messages

  • Threads

Here's an example layout:


<com.getstream.sdk.chat.view.MessageInputView
    android:id="@+id/messageInput"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
                    

To initialize the message input you pass it a channelViewModel. Here's an example:


// create the channel view model
Channel channel = client.channel(channelType, channelID);
ChannelViewModelFactory viewModelFactory = new ChannelViewModelFactory(this.getApplication(), channel);
viewModel = new ViewModelProvider(this, viewModelFactory).get(ChannelViewModel.class);
// set the viewmodel on the messageInput
MessageInputView messageInput = findViewById(R.id.messageInput);
messageInput.setViewModel(viewModel, this);
                    

// create the channel view model
val channel = client.channel(channelType, channelID)
val viewModelFactory = ChannelViewModelFactory(application, channel)
val viewModel = ViewModelProvider(this, viewModelFactory).get(ChannelViewModel::class.java)
// set the viewmodel on the messageInput
val messageInput = findViewById(R.id.messageInput)
messageInput.setViewModel(viewModel, this)
                    

Styling via attributes

You can style the message input's attachment button, send button and text input using attributes.

Avatar

Name Type Description Default Optional
app:streamAvatarWidth dimension 32dp
app:streamAvatarHeight dimension 32dp
app:streamAvatarBorderWidth dimension 3dp
app:streamAvatarBorderColor color WHITE
app:streamAvatarBackGroundColor color #989898
app:streamAvatarTextSize dimension 14sp
app:streamAvatarTextColor color WHITE
app:streamAvatarTextStyle normal, bold, italic bold
app:streamAvatarTextFont reference -
app:streamAvatarTextFontAssets string -

Attachment Button

Name Type Description Default Optional
app:streamShowAttachmentButton boolean true
app:streamAttachmentButtonDefaultIconColor color stream_gray_dark
app:streamAttachmentButtonDefaultIconPressedColor color WHITE
app:streamAttachmentButtonDefaultIconDisabledColor color LTGRAY
app:streamAttachmentButtonSelectedIconColor color BLACK
app:streamAttachmentButtonIcon reference -
app:streamAttachmentButtonWidth dimension 25dp
app:streamAttachmentButtonHeight dimension 25dp

Input Button

Name Type Description Default Optional
app:streamInputButtonDefaultIconColor color #0076FF
app:streamInputButtonEditIconColor color #0DD25E
app:streamInputButtonDefaultIconPressedColor color WHITE
app:streamInputButtonDefaultIconDisabledColor color LTGRAY
app:streamInputButtonIcon reference -
app:streamInputButtonWidth dimension 25dp
app:streamInputButtonHeight dimension 25dp

Input

Name Type Description Default Optional
app:streamInputHint string Write a message
app:streamInputTextSize dimension 15sp
app:streamInputTextColor color BLACK
app:streamInputHintColor color stream_gray_dark
app:streamInputTextStyle normal, bold, italic normal
app:streamInputTextFont reference -
app:streamInputTextFontAssets string -

Input Background

Name Type Description Default Optional
app:streamInputBackground reference -
app:streamInputSelectedBackground reference -
app:streamInputEditBackground reference -
app:streamInputBackgroundTextSize dimension 15sp
app:streamInputBackgroundTextColor color BLACK
app:streamInputBackgroundTextStyle normal, bold, italic normal
app:streamInputBackgroundTextFont reference -
app:streamInputBackgroundTextFontAssets string -

Listeners

The following listeners can be set

  • setMessageSendListener

  • setOpenCameraViewListener

  • setPermissionRequestListener

Attachments & Permissions

You can send messages with attachments like images, videos and other files. To upload an attachment, you must allow proper permissions to access the camera and storage on your device. If your own application has already granted the permissions, ignore this part and setPermissionRequestListener.

Set PermissionRequestListener


...
MessageInputView messageInput = findViewById(R.id.messageInput);
messageInput.setPermissionRequestListener(this);
...

@Override
public void openPermissionRequest() {
    PermissionChecker.permissionCheck(this, null);
    // If you are writing a Channel Screen in a Fragment, use the code below instead of the code above.
    //   PermissionChecker.permissionCheck(getActivity(), this);
}
                    

...
val messageInput = findViewById(R.id.messageInput)
messageInput.setPermissionRequestListener(this)
...
override fun openPermissionRequest() {
    // if you're in an activity
    PermissionChecker.permissionCheck(this, null)
    // from a fragment
    // PermissionChecker.permissionCheck(activity, this);
}
                    

Request Permissions Result


@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    binding.messageInput.permissionResult(requestCode, permissions, grantResults);
}
                    

override fun onRequestPermissionsResult(
    requestCode: Int, permissions: Array<String?>,
    grantResults: IntArray
) { 
    val messageInput = findViewById(R.id.messageInput)
    messageInput.permissionResult(requestCode, permissions, grantResults)
}
                    

Send Images and Videos from camera

After capturing an image or video from the device's camera, it should be processed by ChannelActivity.


@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    // If you are using own MessageInputView please comment this line.
    binding.messageInput.captureMedia(requestCode, resultCode, data);
}
                    

override fun onActivityResult(
    requestCode: Int,
    resultCode: Int,
    data: Intent?
) {
    super.onActivityResult(requestCode, resultCode, data)
    val messageInput = findViewById(R.id.messageInput)
    binding.messageInput.captureMedia(requestCode, resultCode, data)
}
                    

Creating your own message input view

The docs below explain how to create your own (very minimal) message input view. The end result will look like this (note the EditText and the Send Button at the bottom)

To implement your own message input view you need to create the layout and the view class. Let's first create a view class that subclasses constraint layout:


package com.example.chattutorial

import android.content.Context
import android.text.Editable
import android.text.TextWatcher
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.LifecycleOwner
import com.example.chattutorial.databinding.ViewMessageInputBinding
import com.getstream.sdk.chat.rest.Message
import com.getstream.sdk.chat.rest.interfaces.MessageCallback
import com.getstream.sdk.chat.rest.response.MessageResponse
import com.getstream.sdk.chat.viewmodel.ChannelViewModel


class MessageInputView: ConstraintLayout
{
    private lateinit var binding: ViewMessageInputBinding

    constructor(context: Context) : super(context){
        init(context)
    }

    constructor(context: Context, attrs: AttributeSet):    super(context, attrs){
        init(context)
    }

    constructor(context: Context, attrs: AttributeSet?,    defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        init(context)
    }

    fun init(context: Context) {
        val inflater = LayoutInflater.from(context)
        binding = ViewMessageInputBinding.inflate(inflater, this, true)
    }

    fun setViewModel(
        viewModel: ChannelViewModel,
        lifecycleOwner: LifecycleOwner?
    ) {
        binding.lifecycleOwner = lifecycleOwner
        binding.viewModel = viewModel

        // implement message sending
        binding.sendButton.setOnClickListener {
            val message : Message = Message()
            message.text =  binding.messageInput.text.toString()
            viewModel.sendMessage(message, object: MessageCallback {
                override fun onSuccess(response: MessageResponse?) {
                    // hi
                    viewModel.messageInputText.value = ""
                }

                override fun onError(errMsg: String?, errCode: Int) {
                }

            })
        }

        // listen to typing events and connect to the view model
        binding.messageInput.addTextChangedListener(object : TextWatcher {

            override fun afterTextChanged(s: Editable) {
                if (s.toString().isNotEmpty()) viewModel.keystroke()
            }

            override fun beforeTextChanged(s: CharSequence, start: Int,
                                           count: Int, after: Int) {
            }

            override fun onTextChanged(s: CharSequence, start: Int,
                                       before: Int, count: Int) {
            }
        })
    }

}
                    


                    

Note how it implements a setViewModel function that takes the ChannelViewModel and lifecycleOwner as arguments. Next the message input calls viewModel.keystroke() whenever the user enters text. The keystroke calls allows the low level client to show typing indicators. The ViewModel is also used for sending the message using the viewModel.sendMessage call.

The class inflates the ViewMessageInputBinding which we didn't create yet. So let's create view_message_input.xml:


<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <import type="android.text.TextUtils"/>
        <import type="android.view.View"/>
        <variable
            name="viewModel"
            type="com.getstream.sdk.chat.viewmodel.ChannelViewModel" />

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <EditText
            android:id="@+id/messageInput"
            android:layout_width="0dp"
            android:layout_height="30dp"
            android:layout_marginStart="12dp"
            android:background="@android:color/transparent"
            android:hint="Type a message"
            android:text="@={viewModel.messageInputText}"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/sendButton" />

        <Button
            android:id="@+id/sendButton"
            android:layout_width="40dp"
            android:layout_height="30dp"
            android:layout_marginStart="12dp"
            android:text="Send"
            android:background="@null"
            app:layout_constraintLeft_toRightOf="@+id/messageInput"
            app:layout_constraintRight_toRightOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
                    

Note how the text of the EditText is connect to the viewModel using 2 way databinding: android:text="@={viewModel.messageInputText}"