•28 days ago
Did you know you can integrate Stream’s Android SDK with other open-source libraries? To show you just how easy it is, in this tutorial you’ll leverage data from Stream’s Chat API to power the messaging UI from ChatKit.
Specifically, this tutorial will cover:
- Stream Setup
- Implementing the ChatKit Interfaces
- Building the Channels Screen
- Building the Messaging Screen
- Paging Data
- Handling Input and Typing Events
- Final Result
Understanding Stream’s Android UI Components Libraries
Before diving in, it may be helpful to get familiar with all of Stream’s Android component libraries.
Currently, Stream offers two sets of fully-featured, open-source UI components for Android messaging apps:
For more clarification on the Jetpack Compose and UI Components libraries, see Jetpack Compose vs. XML-based UI Components for Stream Chat.
However, Stream also offers lower-level libraries you can use on Android:
- An Offline Support Library that provides caching, automatically retries failed calls, and offers observable data as Flows or LiveData objects.
- A Low-level Chat Client that allows you to make API calls and subscribe to real-time chat events.
You'll be building on top of the offline library, observing the Chat state and displaying it using ChatKit.
Note: You can find the source code for this tutorial in the ChatKit Stream repository on GitHub.
Set Up Your Stream Account
For the purposes of this tutorial, it’s quicker and simpler to use the same hardcoded user defined in the Android Chat Messaging Tutorial.
This will allow you to avoid lots of extra steps, like creating user tokens, fetching your API key and Secret from your Stream dashboard, and creating sample data to populate your channels, among other things.
If you’d still like an account, you can sign up for a free Chat trial. Once you’re signed up, be sure to visit the Android Chat First Steps tutorial to learn how to use the Stream dashboard, create your first sample project, and more.
Implement the ChatKit Interfaces
ChatKit defines various interfaces that its Views can interact with, such as
IUser. To use ChatKit in your app, you need to create model objects that implement these interfaces.
The Stream Chat SDK already provides its own models for these types, so you’ll need to wrap these into the given interfaces. Let's take the example of just the
Dialog (which is called a
Channel in Stream's terminology):
This implementation of
IDialog takes a Stream
Channel object as its parameter and then reads the corresponding properties of the
Channel as needed.
It also references implementations of the other two aforementioned ChatKit interfaces, which are similar simple wrappers:
Other than these models, ChatKit also needs an
ImageLoader implementation so it can display user avatars.
Use the Coil library to provide this capability:
The interface is straightforward to implement with Coil:
With the models and image loader prepared, you’re ready to build a Channel List screen.
Build a Channels List Screen
Start with the XML layout, which will contain a full-screen
DialogsList from ChatKit:
DialogsList works with an adapter that will provide it the data that it should display (just like a
RecyclerView). You can create this adapter as a property in your
MainActivity, and then connect it to the view in an
Note: The adapter receives your
CoilImageLoaderimplementation as a parameter.
Initializing Stream Chat
The next step is to initializethe Stream Chat SDK by instantiating the
ChatDomain classes. To do so, you’ll need your app’s API Key and Secret.
For more information about these steps, see the Android In-App Messaging Tutorial. You’ll use the same hardcoded demo user as the tutorial.
With the connection ready, you can load your list of channels with the
In the above code snippet, you:
- Created a filter to get channels that are of the
messagingtype where your current user is a member.
- Sorted these channels by
last_updatedso that channels with the newest messages show at the top. Thanks to the observable nature of the offline library, channels will update in real-time.
QueryChannelsControllerfrom the offline library, which observes the current channel state. This state is automatically updated based on real-time WebSocket events as users send messages.
- Map new values to the
Dialoginstance and pass that list to the
Now, you can build and run the application to see the list of loaded channels!
For comparison, here’s how the same screen looks when implemented with Stream’s UI Components:
Building the Messaging Screen
Next, you’ll create a new Empty Activity called
MessagesActivity. For the XML layout, you’ll use a
MessageList with a
MessageInput below it:
This new screen will need to receive the channel's ID as a parameter, which you'll pass through Intent extras. It'll also use an adapter to display the list of messages, which you'll connect to
cid extracted from the Intent, you can now use
ChatDomain again, this time to grab a
ChannelController that’ll provide you real-time observable data for a single channel:
In the above snippet you:
messagesAdapteras you receive the entire list of messages when the observable data updates.
- Wrap all of your Stream message objects into the
Messagemodel (created earlier) that implements ChatKit’s
- You pass in
trueto reverse the order of messages here so that they display correctly in the list.
- You pass in
The code above sets you up for the initial load, where you load 30 messages for the current channel. However, as you scroll up, you need to load older messages.
You can load older messages by setting a listener on the
To make this simple, you’ll make your
MessagesActivity implement the
OnLoadMoreListener interface that’s expected here.
To load more messages, you’ll use the
loadOlderMessages method of
ChatDomain to load 30 more messages when the listener is triggered:
Handling Input and Typing Events
You have the code to load messages and to load even more when you scroll up. Now, you can set up the
MessageInput so that you can also send messages of your own.
Add two listeners in the
And again, you’ll implement these with
The typing listener is very straightforward, as
ChatDomain has the exact methods you need to call into.
When sending a new message, you perform basic input validation:
- You check if there’s anything typed into the input field.
- If there is, you create a Stream
Messageand send it using
Lastly, you need to set up navigation between screens.
To add screen navigation, add the following code in
If you build and run the app again, you’ll have a functional, basic chat app:
- You have a list of available channels
- You can open any of these channels
- You can read messages in these channels
- You can send your own messages as well!
For comparison, below is a messaging screen built using Stream’s UI Components (check out the library in the Chat UI Components sample app):
That’s it! You can find the completed project’s source code on GitHub.
To sum up, here’s what you’ve implemented in this sample app… :
- Channel list
- Real-time, visible channel updates (try sending a message from a second device to test it out)
- Unread counts in the channel list
- Avatar loading on both screens
- Displaying and sending plain text messages
- Message pagination (older content loads as you scroll up)
… and here are some possible improvements you could work on from here:
- Channel list pagination
- Placeholder images for channels with no set image URL
- Rich messaging (images and other attachments)
- Error handling for Stream API calls (this tutorial skips this step for simplicity)
As always, happy coding!