Android Introduction Confused about "Android Introduction"?
Let us know how we can improve our documentation:
Confused about "Android Introduction"?
Let us know how we can improve our documentation:
The Android SDK enables you to build any type of chat or messaging experience for Android. It consists of three major components:
Client: The client handles all API calls and receives events.
Offline: The offline library stores the data, implements optimistic UI updates, handles network failures, and exposes LiveData or StateFlow objects that make it easy to build your own UI on.
UI : The UI package includes ViewModels and custom views for common things like a channel list, message list, message input etc.
We typically recommend building a prototype with the full UI package. This is the fastest way to ensure that you have the integration set up correctly in combination with backend and other teams. After that, about half of our customers launch in production with our UI package.
There are some limitation to Android UI reusability, so if you have more complicated design needs, you'll want to drop down a level to the offline package. For most applications, you won't have to use the client directly (note that it's a lot of work to keep chat state updated manually, which you'd have to do when using the client).
Before going through these docs, we recommend trying out the Android Chat tutorial and taking a look at the Android Chat Sample App. You can also check out our API docs for more information, as well our source code on GitHub.
The online API tour is also a nice way to learn about how the API works. It's in-browser, and therefore Javascript based, but the ideas are pretty much the same in Kotlin.
Getting StartedCopied!Confused about "Getting Started"?
Let us know how we can improve our documentation:
Confused about "Getting Started"?
Let us know how we can improve our documentation:
This guide quickly brings you up to speed on Stream’s Chat API. The API is flexible and allows you to build any type of chat or messaging. Note that if you build on our UI components, you won't need to call into this API too often after the initilization of the library.
Add one of the three packages below to your dependencies for your module/app level build.gradle
file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
repositories {
google()
mavenCentral()
maven { url "https://jitpack.io" }
jcenter()
}
dependencies {
// Client + offline + UI components
implementation "io.getstream:stream-chat-android-ui-components:$stream_version"
// Client + offline
implementation "io.getstream:stream-chat-android-offline:$stream_version"
// Client only
implementation "io.getstream:stream-chat-android-client:$stream_version"
}
For the latest version, check our GitHub releases page.
Chat ClientCopied!Confused about "Chat Client"?
Let us know how we can improve our documentation:
Confused about "Chat Client"?
Let us know how we can improve our documentation:
Let's get started by initializing the client and connecting the current user:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
String apiKey = "YOUR_API_KEY";
String token = "CHAT_USER_TOKEN";
// Step 1 - Set up the client for API calls
ChatClient client = new ChatClient.Builder(apiKey, applicationContext)
// Change log level
.logLevel(ChatLogLevel.ALL)
.build();
// Step 2 - Set up the domain for offline storage
ChatDomain domain = new ChatDomain.Builder(applicationContext, client)
// Enable offline support
.offlineEnabled()
.build();
// Step 3 - Set up UI components
ChatUI ui = new ChatUI.Builder(applicationContext).build();
// Step 2 - Authenticate and connect the user
User user = new User();
user.setId("summer-brook-2");
user.getExtraData().put("name", "Paranoid Android");
user.getExtraData().put("image", "https://bit.ly/2TIt8NR");
// You can use client.devToken(userId) if auth is disabled for your app
client.connectUser(user, token).enqueue((result) -> {
if (result.isSuccess()) {
// Handle success
} else {
// Handler error
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
val apiKey = "YOUR_API_KEY"
val token = "CHAT_USER_TOKEN"
// Step 1 - Set up the client for API calls
val client = ChatClient.Builder(apiKey, applicationContext)
// Change log level
.logLevel(ChatLogLevel.ALL)
.build()
// Step 2 - Set up the domain for offline storage
val domain = ChatDomain.Builder(applicationContext, client)
// Enable offline support
.offlineEnabled()
.build()
// Step 3 - Set up UI components
val ui = ChatUI.Builder(applicationContext).build()
// Step 2 - Authenticate and connect the user
val user = User(
id = "summer-brook-2",
extraData = mutableMapOf(
"name" to "Paranoid Android",
"image" to "https://bit.ly/2TIt8NR",
),
)
client.connectUser(
user = user,
token = token, // or client.devToken(userId); if auth is disabled for your app
).enqueue { result ->
if (result.isSuccess) {
// Handle success
} else {
// Handler error
}
}
The user token is typically provided by your backend when you login or register in the app. If authentication is disabled for your app, you can also use a ChatClient#devToken
to generate an insecure token for development. Of course, you should never launch into production with authentication disabled.
For more complex token generation and expiration examples, have a look at Token Expiration.
ChannelsCopied!Confused about "Channels"?
Let us know how we can improve our documentation:
Confused about "Channels"?
Let us know how we can improve our documentation:
Let’s continue by creating your first channel. A channel contains messages, a list of members who are permanently associated with the channel, and a list of watchers currently watching the channel. The example below shows how to set up a channel:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
ChannelClient channelClient = client.channel("messaging", "travel");
Map<String, Object> extraData = new HashMap<>();
extraData.put("name", "Awesome channel about traveling");
// Creating a channel with the low level client
channelClient.create(extraData).enqueue(result -> {
if (result.isSuccess()) {
Channel channel = result.data();
// Use channel by calling methods on channelClient
} else {
// Handle result.error()
}
});
// Watching a channel's state using the offline library
chatDomain.getUseCases()
.getWatchChannel()
.invoke("messaging:travel", 10)
.enqueue(result -> {
if (result.isSuccess()) {
ChannelController channelController = result.data();
// LiveData objects to observe
channelController.getMessages();
channelController.getReads();
channelController.getTyping();
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
val channelClient = client.channel(channelType = "messaging", channelId = "travel")
val extraData = mutableMapOf<String, Any>(
"name" to "Awesome channel about traveling"
)
// Creating a channel with the low level client
channelClient.create(extraData).enqueue { result ->
if (result.isSuccess) {
val channel: Channel = result.data()
// Use channel by calling methods on channelClient
} else {
// Handle result.error()
}
}
// Watching a channel's state using the offline library
chatDomain.useCases.watchChannel(cid = "messaging:travel", messageLimit = 0)
.enqueue { result ->
if (result.isSuccess) {
val channelController = result.data()
// LiveData objects to observe
channelController.messages
channelController.reads
channelController.typing
}
}
The first two arguments are the channel type and the channel ID (messaging
and travel
in this case). In this case, the channel ID is explicit, but channels can also be created by specifying a list of members, in that case, an ID will be generated automatically.
The channel type controls the settings we’re using for this channel. There are four default types of channels:
- livestream
- messaging
- team
- commerce
These options provide you with the most sensible defaults for the given use cases. You can also define custom channel types if the defaults don’t work for your use case. See Channel Types for more info.
The extraData
argument is an object containing custom channel data. You can add as many custom fields as you would like, as long as the total size of the object is less than 5KB. This applies not only to channels, but also messages, users, attachments, and reactions.
MessagesCopied!Confused about "Messages"?
Let us know how we can improve our documentation:
Confused about "Messages"?
Let us know how we can improve our documentation:
Now that we have the channel set up, let's send our first chat message:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Message message = new Message();
message.setText("Hello world");
message.setCid("messaging:travel");
message.putExtraValue("customField", "123");
// Using the low level client
channelClient.sendMessage(message).enqueue(result -> {
if (result.isSuccess()) {
Message sentMessage = result.data();
} else {
// Handle result.error()
}
});
// Using the offline support library
chatDomain.getUseCases().getSendMessage().invoke(message).enqueue(result -> {
if (result.isSuccess()) {
Message sentMessage = result.data();
} else {
// Handle result.error()
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
val message = Message(
text = "I’m mowing the air Rand, I’m mowing the air.",
cid = "messaging:travel",
extraData = mutableMapOf("customField" to "123")
)
// Using the low level client
channelClient.sendMessage(message).enqueue { result ->
if (result.isSuccess) {
val message: Message = result.data()
} else {
// Handle result.error()
}
}
// Using the offline support library
chatDomain.useCases.sendMessage(message).enqueue { result ->
if (result.isSuccess) {
val message: Message = result.data()
} else {
// Handle result.error()
}
}
When you send a message to a channel, Stream Chat automatically broadcasts to all the people that are watching this channel and updates in real-time.
Querying ChannelsCopied!Confused about "Querying Channels"?
Let us know how we can improve our documentation:
Confused about "Querying Channels"?
Let us know how we can improve our documentation:
The client.queryChannels method enables you to retrieve a list of channels. You can specify a filter and sort order. The offline support library keeps the channels list updated as new messages, reactions and new channels arrive.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
FilterObject filter = Filters.and(
Filters.eq("type", "messaging"),
Filters.in("members", "john")
);
QuerySort<Channel> sort = new QuerySort<Channel>().desc("last_message_at");
int offset = 0;
int limit = 10;
int memberLimit = 0;
int messageLimit = 0;
// Using the low level client to query channels
QueryChannelsRequest request = new QueryChannelsRequest(filter, offset, limit, sort, messageLimit, memberLimit).withWatch().withState();
client.queryChannels(request).enqueue(result -> {
if (result.isSuccess()) {
List<Channel> channels = result.data();
} else {
// Handle result.error()
}
});
// Using the offline library to query channels
chatDomain.getUseCases().getQueryChannels()
.invoke(filter, sort, limit, messageLimit)
.enqueue(result -> {
if (result.isSuccess()) {
QueryChannelsController queryChannelsController = result.data();
// LiveData objects to observe
queryChannelsController.getChannels();
queryChannelsController.getLoading();
queryChannelsController.getEndOfChannels();
} else {
// Handle result.error()
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
val filter = Filters.and(
Filters.eq("type", "messaging"),
Filters.`in`("members", "john"),
)
val sort = QuerySort<Channel>().desc("last_message_at")
// Using the low level client to query channels
val request = QueryChannelsRequest(
filter = filter,
offset = 0,
limit = 10,
querySort = sort
).withWatch().withState()
client.queryChannels(request).enqueue { result ->
if (result.isSuccess) {
val channels: List<Channel> = result.data()
} else {
// Handle result.error()
}
}
// Using the offline library to query channels
chatDomain.useCases.queryChannels(filter, sort)
.enqueue { result ->
if (result.isSuccess) {
val queryChannelsController = result.data()
// LiveData objects to observe
queryChannelsController.channels
queryChannelsController.loading
queryChannelsController.endOfChannels
} else {
// Handle result.error()
}
}
To learn more about which fields you can query and sort on have a look at the query channels documentation.
ConclusionCopied!Confused about "Conclusion"?
Let us know how we can improve our documentation:
Confused about "Conclusion"?
Let us know how we can improve our documentation:
Now that you understand the building blocks of a fully functional chat integration, let’s move on to the next sections of the documentation, where we dive deeper into details on each API endpoint.