In the first part of this guide, ‘Build in app chat using Kotlin: part 1’, we built out the login, and the ability to create and list channels. In this follow-up blog post, we will create the UI for the ChatActivity, build the MessageAdapter which handles setting the UI components, and implement the ChannelActivity class. This guide only covers sending/receiving User Messages. To see how to send File Messages, please visit Sendbird Documentation.
This tutorial assumes prior knowledge of Android and Android concepts, and was built using Android Studio: 4.0.2, Android Version: 10 API 29, Kotlin: 1.3.72, and Sendbird Core SDK: 3.0.148. Here is the completed source code for both part 1 and 2 of this guide.
UI for Channel Activity
The first step is to create a UI for how the chat will look. We will add an AppBarLayout with a “Back” button on the top to return to the ChannelListActivity. Below that we’ll add a RecyclerView to show the actual messages, and finally on the bottom, we’ll add a simple layout to handle entering and sending messages.
Now that the UI skeleton is done, we need to create two different item views for the messages themselves because we will have a different UI for messages that are sent by the current user, and another UI for messages that are sent by others in the chat. (Note: As you implement more types of messages, you’ll have more of these .xmls.)The first .xml is for messages that are from the “Me” perspective, or messages sent by the current user. For this we have opted to have a TextView for the actual message, this is wrapped in a Cardview. Surrounding this TextView are other TextViews for items such as the date.
The second .xml is for messages that are from the “Other” perspective, so any message not from the current user. This UI is similar to the “Me” UI, however, it is left aligned and contains information about the “Other” user. While the views are relatively similar, things like an ImageView for the profile image and a TextField for the user's name are included.
Now that theUI is complete, we are going to implement the MessageAdapter.kt class. This class handles attaching the data that is passed to it, to a particular view in the RecyclerView.
At this point, you will create a class called MessageAdapter.kt. This class extends the RecyclerView.Adapter<RecyclerView.ViewHolder>(), so you need to make sure to implement the following methods:
- onCreateViewHolder: This function returns the customViewHolder that corresponds to the type of message that is there.
- getItemViewType: This figures out what kind of message it is. Currently, we have only implemented UserMessage. In this function we will determine whether it is a “Me” message or an “Other” Message, and return accordingly.
- onBindViewHolder: This function binds the messages to the views.
- getItemCount: This function returns the current position.
There are two additional functions that need to be added:
- loadMessages: This function loads the initial past messages that are received in the ChannelActivity.
- addFirst: This function adds recently sent or received messages to the adapter. Obviously, both of these messages need to call notifyDataSetChanged() to handle updating the RecyclerView.
After handling the essential functions for a RecyclerView adapter, we need to implement our own customViewHolders. There are two inner classes denoted by:
- MyUserHolder:This class simply binds the respective message sent by “Me” to the item, which we created the view for earlier.
- OtherUserHolder: This class simply binds the respective message sent by “Other” to the item, which we created the view for earlier.
For the sake of a cleaner look, we also added an object that has two functions to help with date formatting:
This completes the code for the MessageAdapter class. Here's completed class for MessageAdapter.kt.
Now that we have implemented the UI, and taken care of the adapter to connect the UI to the passed data, we will now implement the ChannelActivity.kt. The following class handles a few things: It sets up the RecyclerView and adapter, handles getting and entering the channel passed by either the CreateChannelActivity or the ChannelListActivity, and handles sending and receiving messages.
First, you need to create a ChannelActivity.kt class. In the onCreate function, set the contentView and call two functions to handle the setup.
The first function sets up the RecyclerView and the MessageAdapter we just created. This code follows a pretty basic implementation for instantiating a RecyclerView. Be sure to pass the context of the activity for the MessageAdapter because you will need that when setting the image, as we did above.
The second function handles setting the two buttons on the activity: Back and Send. The Back button is relatively explanatory, so let's focus on the Send button.
For the Send button we use a method called sendMessage. This function takes the text from the editText, and sets it on the param of UserMessageParams(). There is a lot more you can do with UserMessageParams(), but for the sake of simplicity we will just add the message. We’ll then take the groupChannel instance and .sendMessage(). Upon the return that it was successfully sent, we will add it to the adapter, and then clear the editText.
Now that we have completed the onCreate call, let’s get to the onResume call. This call is where we will handle getting the passed channel, and where we will register the channel handler so we can get various events, in this case the onMessageReceived event.
First, we want to get the channelURL from the intent, and for the sake of visibility we will move this to a separate method.
After we have the channelURL, we need to make a call GroupChannel.getChannel(). This call takes the channelURL and retrieves the channel object so we can do various things like send messages and get relevant channel information. Once it returns successfully with the channel, we need to set the channel, and be sure to call getMessages().
This function is pretty straightforward. It creates a previousMessageListQuery , loads the messages, and then calls loadMessages on the adapter. This gets all the previous messages in the conversation. This function supports pagination so you can get the entire conversation history.
The final step in the onResume call is to set a channel handler. The channel handler is how you can get various events such as typing indicators, message read, and message delivered events, as well as onMessageReceived events. The only method we implemented was onMessageReceived. This event fires every time a message comes in from another user. Once we get the event we simply add it to the adapter, as shown in the following code:
Since we had an onResume call, we can add an onPause. The only thing to do here is remove the channel handler for clean up.
That completes the code for the ChannelActivity class. See the following completed class for ChannelActivity.kt
This blog post demonstrated how to create a chat UI, and hook up that chat UI to be able to send/receive and display messages. This guide serves as a stepping stone to the many things you can do with the Sendbird SDK. From what we have just implemented, you can easily start to incorporate more types of messages, add a more complete view into the message life cycle, add push notifications, typing indicators, translations, and so much more.