Android Chat Bubbles: Building iOS Style Chat in Android
Tags: android, chat, bubbles, ios
Published: 2020-03-10 18:11:43

In this post, we'll explore how to do two things: **1)** create live chat message bubbles in Android that are similar to WhatsApp and iMessage and **2)** customize Stream Chat's [UI Components](https://github.com/GetStream/stream-chat-android#ui-components--chat-views). 

We'll customize [Stream Chat Android](https://github.com/GetStream/stream-chat-android)'s built-in UI components by plugging in a custom message view. This allows us to focus on the text rendering, while Stream does everything else.

The source code is available [here](https://github.com/nparsons08/stream-android-bubble-chat). Once we're finished, we'll have a chat experience that looks like this:

![](https://i.imgur.com/Olclwy8.png)

## Prerequisites

This post assumes a working knowledge of [Android](https://developer.android.com/) and [Kotlin](https://kotlinlang.org/). If you're unfamiliar with either or both, it may be useful to check out a [getting started](https://developer.android.com/training/basics/firstapp) guide. If you'd like to run the code, you'll also need a Stream account. Please [register here](https://getstream.io/chat/trial/). Once you're registered, you'll see a Stream app with an **App ID**, **API Key**, and **API Secret**:

![](https://i.imgur.com/xxWKu5P.png)

We won't be going through how to create the backend to receive our tokens in _this_ tutorial; please refer to [this repo](https://github.com/nparsons08/stream-chat-api) for token generation. You can also use [Stream's CLI](https://www.npmjs.com/package/getstream-cli); if you look in [`MainActivity.kt`](https://github.com/nparsons08/stream-android-bubble-chat/blob/master/app/src/main/java/com/example/bubblechat/MainActivity.kt), you'll see placeholders to fill in run the application.

Let's build!

## Listing Channels

First, we'll create a view which displays a list of channels for a user to select:

![](https://i.imgur.com/TJwpltk.png)

This view is mostly taken care of by Stream's UI Components. To list our channels, the `MainActivity.kt` will leverage Stream's [`ChannelList`](https://github.com/GetStream/stream-chat-android/blob/master/docs/ChannelList.md), and we'll configure it to load all of the channels for our user. Here's the code:

{% gist https://gist.github.com/nparsons08/3b5b0aa26d55a26e252c631efcf2f36a %}

First, we initialize our `StreamChat` instance with our API key. We configure it with the user who'll be using our application. To keep things simple, we'll just declare who's logged in and their frontend token. In a real application, you'd want to perform authentication with a [backend](https://getstream.io/blog/tutorial-user-auth-with-stream-chat-feeds/) that generates this token.

We also give the user an `id`, a `name`, and an `image`(profile image). Once we've done this, we can declare the layout as "`activity_main`". 

Now, the view needs a view model; in this case, we'll simply use the default, Stream-provided "`ChannelListViewModel`". This is great, as it does all the work to interact with Stream's API. We simply need to configure it to [`filter`](https://getstream.io/chat/docs/query_channels/) for our user's channels. 

The last thing we do is set a "click listener" on each channel. We boot a `ChannelActivity`, which is where we'll customize the chat messages. Before we look at the code for `ChannelActivity` let's look at the layout:

https://gist.github.com/nparsons08/e7436c12791e8a123cb1e409628c1597

We use a `ConstraintLayout` to hold our `ChannelListView` and associated `ProgressBar`s. Since the view is mostly taken care of by Stream, we just need to configure when it is that our `ProgressBar`s show and what our `padding` and `margin` are.  

Now, we're ready to view a specific channel!

## Viewing a Channel

Once a user clicks a channel, our `ChannelActivity` starts. Here is the code:

{% gist https://gist.github.com/nparsons08/35183a5aa04869ca2602978f0f76d938 %}

First, we get our channel information off of the `Intent` from our `ChannelActivity.newIntent` call, in `MainActivity.kt`, and, then, below, we set our content view `activity_channel` and `viewModel`:

{% gist https://gist.github.com/nparsons08/26adba68cc016586732096d42eca4a62 %}

You'll notice this was another `ConstraintLayout` with some views and progress bars; we use Stream's UI Components to build the majority of the view. `ChannelHeaderView` gives us a nice header with a channel image and channel name (from the parent). `MessageListView` displays our messages, and `MessageInputView` gives us a helpful default message input.
 
We use the built-in Stream view model and feed that to each view. However, we customize the `MessageListView` view by providing a custom message view "factory", `BubbleMessageViewHolderFactory`. This "factory" hooks into Stream's code to provide a custom view for each message. This class is straightforward since all we're going to do is instantiate our bubble message class, `BubbleMessageViewHolder`:

https://gist.github.com/nparsons08/16ead4c7aa2bf6fe6851646eb9c29179

Now that we're hooked into the view rendering, we can customize our messages to look like iMessage or WhatsApp!

## Rendering Custom Bubble Messages

We're ready to declare our custom bubble view. To hook into Stream's components, we need to be a subclass of `BaseMessageListItemViewHolder`. Here is the basic class definition:

{% gist https://gist.github.com/nparsons08/6a40f48cbd430d22e36bd62fe4fdf9f8 %}

We're passed a `viewGroup` resource from the factory (we will be using "`bubble_message`"). Then, this view is referred to by `itemView`. From this, we grab our message header (which will just be spacing, in our case), our text view that will hold the message, and our username view. We also have a `MessageListItem` and `ChannelState`, which contain data for our Stream message and channel, respectively. Let's check out our view:

{% gist https://gist.github.com/nparsons08/6f76532b2b5f1d1e1e1941cbcf63bac5 %}

This view is quite simple, as it defines the three pieces we referenced before: the header spacing, the username, and the text. Notice there's no bubble logic here. To get the correct colors and shapes, we'll use [drawables](https://developer.android.com/guide/topics/resources/drawable-resource) and some logic in our class to define how the message will look; this layout is simply the scaffolding for us to fill in.

Implementing this abstract class requires us to implement `setStyle` and `bind`. We'll ignore `setStyle` for this tutorial since we'll be defining all of our styles outright. 

> You can check out [`MessageViewListStyle`](https://github.com/GetStream/stream-chat-android/blob/master/library/src/main/java/com/getstream/sdk/chat/view/MessageListViewStyle.java) to see all the ways you can customize the components without building your own class; with that said, in this post, we want to explore how to do it ourselves!

Let's dive in! The `bind` method is where we configure how the message looks:

{% gist https://gist.github.com/nparsons08/d202065567a8d6bea30850f11b1c663e %}

We're given a `ChannelState` and `MessageListItem`, which has everything we need to figure out our display. First, we check the type of message. Stream has a few different message types, which represent different things. For example, you may get a message type that indicates a user is typing or a date separator. In our case, we'll focus on two, the actual messages and date separators; we'll ignore any other message type and remove them from rendering, entirely. 

Let's focus on the actual messages to start; we'll look at how to render the date separators after. Here is our `configMessage` method:

{% gist https://gist.github.com/nparsons08/297de50aefc0ffffaa4edb5a6a8b88db %}

We do three things: 1) configure the actual text display, 2) decide if we need to show a username 3) determine how much spacing we have between messages.

First, let's see how we configure the actual text and render our bubble message:

{% gist https://gist.github.com/nparsons08/720126702da713741e5e57724610261b %}

We set the `TextView`'s text, then decide if it's "our" message or "theirs". If it's "ours", we display the message on the right, in blue. If it's "theirs", it goes on the left, in grey. To set the justification of the message, we use `horizontalBias`; setting it to `1` sets the message to the right, and `0` sets the message to the left. We'll also set the text color based on the background since blue needs white text and grey needs black. Padding is also set differently for the two cases, since the message will have the "tail" on the right or left, depending on who's message it is.

Since we're replicating iMessage's style, they don't render the tail unless it's the bottom message. Conveniently, Stream gives us position information and makes it easy to check message placement within a group of messages:

{% gist https://gist.github.com/nparsons08/1539d3d9b64745d22fd950c9fc158f31 %}

If the message _is_ the bottom message, it means it's the only message or the last in a group of messages by the same user. If it's either of those, we add a tail; if it's not, we just do a simple bubble. We'll also set the background of the text view to a "drawable". Let's look at "our" message drawables; here's the drawable with a tail:

{% gist https://gist.github.com/nparsons08/efdd42dddb5220a0728bb79bf99d32b6 %}

We're doing a bit of a smart trick by defining `layer-list` with two items. One is a regular rectangle with rounding, the other is a rotated rectangle that is covered by the standard rectangle. We pad the regular rectangle out to give a small gutter, so the edge of the rotated rectangle sticks out. This gives us a beautiful "tail" for the message bubble. 

While we chose to keep it simple here with just rectangles, if you'd like a more stylized tail, such as the one you see in iMessage, you can use [vector drawables](https://developer.android.com/guide/topics/graphics/vector-drawable-resources) to convert an SVG to a drawable. 

For messages that don't need a tail, we define a simple shape with some spacing for the tail gutter:

{% gist https://gist.github.com/nparsons08/1006cd761f5d12d2f3483ecdc826e038 %}

Messages for "theirs" is the same pattern but mirrored and grey:

{% gist https://gist.github.com/nparsons08/fe8cba5107b6eeb3c4873d35226e18ee %}

Nice! Now we have some good looking bubbles:

![](https://i.imgur.com/CVQXqfo.png)

Now we can add the username, if necessary, and space the messages correctly. First, we'll look at `configUsername`:

{% gist https://gist.github.com/nparsons08/8677f85c10ee96444dca24a584ff9967 %}

First, we decide if we show the username at all. There are three cases where we want it gone. If it's a 1-on-1 chat, if the message is "ours", or if it's not the top of a group of messages (otherwise, we'd have usernames on every message within a grouping). This logic means we only label groups of messages from other people in group chats. 

If we have that case, we set the text of the username `TextView` to the user's name. We set the bias and margin depending on if it's on the right or the left.

Now we can space our messages depending on their placement in a group of messages:

{% gist https://gist.github.com/nparsons08/1f73cefc33868ea15c36c6befe3cf941 %}

This is simple since our default spacing is in our layout resource. We simply shrink the height if we're not the top message. Now you'll see the nice spacing and appropriate tails with usernames in our group chats:

![](https://i.imgur.com/Olclwy8.png)

All that's left is to deal with our date separator messages, and we're done! These `MessageListItem`s come with no user and just a date. These are used to provide a horizontal separator between messages that are spaced out in time. These messages are automatically created by Stream when it decides the time between messages has been too long, and it'd be nice to group them. For example, if you message back a day later, you can break up the view with a date marker to inform the user of roughly when messages have come in. Here's `configDate`:

{% gist https://gist.github.com/nparsons08/f56319985a500ebfcae6268f4cf23061 %}

This is straightforward since our default text view has no styling and the correct spacing. We just need to turn off the username view and add the date. Now we have a convenient date separator:

![](https://i.imgur.com/XjsqpOl.png)

And we're done! We now have custom bubble messages hooked into Stream.

## Final Thoughts

Replicating iOS style messages on Android is relatively simple with drawables. Stream makes it even simpler with it's out of the box UI Components. You can start with Stream default views and customize them or build your own. If you want full control, you can ignore the UI Components and use the lower level Stream libraries directly. It's easy to peel back the layers when you need it!

For in-depth details on how to use the Stream Chat SDK for Android, check out the interactive tutorial [here](https://getstream.io/tutorials/android-chat/). You'll learn how to build everything that is required for real-time chat – typing indicators, reactions, threads, etc.

Happy coding! 💬

{% twitter 1237441336334139392 %}