#
ConfigurationThe SDK provides an API for general configuration of the UI Component library's behavior and appearance, which is exposed via the ChatUI
object.
ChatUI
allows you to override default implementations of commonly used parts of SDK like:
- Available message reactions
- MIME type icons for attachments
- Default font used across the UI components
- Avatar bitmap loading logic
- Attachments URLs
The full list of ChatUI
properties you can override include:
supportedReactions
: The set of supported message reactions.mimeTypeIconProvider
: The icons used for different mime types.fonts
: The default font for TextViews displayed by UI Components.avatarBitmapFactory
: The factory responsible for creating user and channel avatar bitmaps displayed byAvatarView
. Can be used to modify the default bitmaps that are loaded, or to add custom avatar loading logic.imageHeadersProvider
: Allows adding extra headers to image loading requests.markdown
: Interface to customize the markdown parsing behaviour, useful if you want to provide your custom markdown parsing logic or use more markdown modules. This property is deprecated and ChatUI.messageTextTransformer should be used instead.style
: Allows overriding the global, default style of UI components, like the TextStyle.navigator
: Allows intercepting and modifying default navigation between SDKs components (e.g. redirection fromMessageListView
toAttachmentGalleryActivity
).messageTextTransformer
: Allows customizing the message text's format or style. For example, you can use it to provide markdown support or highlight specific messages by making them bold.channelNameFormatter
: Allows customizing the way channel names are formatted.messagePreviewFormatter
: Allows to generate a preview text for the given message.dateFormatter
: Allows changing the way dates are formatted.showThreadSeparatorInEmptyThread
: Allows showing theThreadSeparatorItem
in the MessageListView, when opening an empty thread. This helps implement more complex use cases for empty threads andThreadPlaceholderItem
s.
note
ChatUI
is initialized out-of-the-box with default implementations - no initialization is required on app startup.
#
Custom ReactionsAs shown below, by default the SDK provides 5 built-in reactions.
You can override the default set of reactions. In order to define a custom set of reactions for your app, you need to pass the the following data to the ChatUI.supportedReactions
property:
- Kotlin
- Java
val loveDrawable = ContextCompat.getDrawable(this, R.drawable.ic_reaction_love)!!
val loveDrawableSelected = ContextCompat.getDrawable(this, R.drawable.ic_reaction_love)!!.apply { setTint(Color.RED) }
val supportedReactionsData = mapOf(
"love" to SupportedReactions.ReactionDrawable(loveDrawable, loveDrawableSelected)
)
ChatUI.supportedReactions = SupportedReactions(this, supportedReactionsData)
Drawable loveDrawable = ContextCompat.getDrawable(context, R.drawable.stream_ui_ic_reaction_love);
Drawable loveDrawableSelected = ContextCompat.getDrawable(context, R.drawable.stream_ui_ic_reaction_love);
loveDrawableSelected.setTint(Color.RED);
Map<String, SupportedReactions.ReactionDrawable> supportedReactionsData = new HashMap<>();
supportedReactionsData.put("love", new SupportedReactions.ReactionDrawable(loveDrawable, loveDrawableSelected));
ChatUI.setSupportedReactions(new SupportedReactions(context, supportedReactionsData));
As a result, there will only be a love reaction available in the chat, and when selected, the reaction icon will have a red tint.
Normal state - available reactions | Active state - reaction selected |
---|---|
![]() | ![]() |
#
Custom MIME Type IconsWhen possible, the SDK displays thumbnails for image files. For other files, mime type icons are displayed in MessageListView
and in the file browser UI.
By default, the SDK provides built-in MIME type icons for the most popular file types and displays a generic file icon for others.
To customize these icons, you need to override ChatUI.mimeTypeIconProvider
in the following way:
- Kotlin
- Java
ChatUI.mimeTypeIconProvider = MimeTypeIconProvider { mimeType ->
when {
// Generic icon for missing MIME type
mimeType == null -> R.drawable.stream_ui_ic_file
// Special icon for XLS files
mimeType == "application/vnd.ms-excel" -> R.drawable.ic_file_xls
// Generic icon for audio files
mimeType.contains("audio") -> R.drawable.ic_file_mp3
// Generic icon for video files
mimeType.contains("video") -> R.drawable.ic_file_mov
// Generic icon for other files
else -> R.drawable.stream_ui_ic_file
}
}
ChatUI.setMimeTypeIconProvider(mimeType -> {
if (mimeType == null) {
// Generic icon for missing MIME type
return R.drawable.stream_ui_ic_file;
} else if (mimeType.equals("application/vnd.ms-excel")) {
// Special icon for XLS files
return R.drawable.stream_ui_ic_file_xls;
} else if (mimeType.contains("audio")) {
// Generic icon for audio files
return R.drawable.stream_ui_ic_file_mp3;
} else if (mimeType.contains("video")) {
// Generic icon for video files
return R.drawable.stream_ui_ic_file_mov;
} else {
// Generic icon for other files
return R.drawable.stream_ui_ic_file;
}
});
#
Customizing AvatarThe AvatarView
is used in the lists of channels, users, and messages. The avatar is a small picture that identifies a specific channel or user.
The image in the AvatarView
is being displayed based on image
property, present in both Channel
and User
objects:
The AvatarView
will use default gradient color with initials if the image
property cannot be loaded:
#
Customizing Avatar Using StylesYou can configure avatar shape, border width, online indicator and other aspects using AvatarStyle. You can create this kind of avatar by changing the shape and border-radius:
You can also change the background and the picture using AvatarBitmapFactory
.
#
Customizing Avatar Using AvatarBitmapFactoryOverriding the AvatarBitmapFactory
allows you to add custom logic for the channel's and user's avatars displayed using AvatarView
.
The factory has a pair of methods for both user and channel avatars, such as createUserBitmap
and createDefaultUserBitmap
. The first should load the avatar from the network, this is attempted first when an avatar is requested. If the first method fails, the Default
variant is called as a fallback. This should have an implementation that never fails, and always returns a valid bitmap.
note
The methods mentioned above are suspending functions. If you don't use coroutines, you'll find variants with Blocking
in their name that you can use instead.
If you don't want to use avatars from the internet and only use the default avatar, you can call createDefaultUserBitmap
inside createUserBitmap
in your custom implementation of AvatarBitmapFactory
:
- Kotlin
- Java
object : AvatarBitmapFactory(context) {
override suspend fun createUserBitmap(user: User, style: AvatarStyle, avatarSize: Int): Bitmap? {
return createDefaultUserBitmap(user, style, avatarSize)
}
}
new AvatarBitmapFactory(context) {
@Nullable
@Override
public Object createUserBitmap(@NonNull User user, @NonNull AvatarStyle style, int avatarSize, @NonNull Continuation<? super Bitmap> $completion) {
return createDefaultUserBitmap(user, style, avatarSize, $completion);
}
};
If you only would like to change the gradient colors for the default avatar, you can use stream_ui_avatar_gradient_colors
.
The default color set includes a variety of colors:
The set can be overridden in the color.xml
file - you can expand or reduce the number of supported colors, like in the example below:
<array name="stream_ui_avatar_gradient_colors">
<item>@color/stream_ui_literal_white</item>
</array>
Which creates:
If you add custom loading logic for your avatars, you can also override the userBitmapKey
and channelBitmapKey
methods to get correct caching behavior. The cache keys returned from these methods should include a representation of the pieces of data you use to load the avatars.
Here's an example of a custom factory that extends AvatarBitmapFactory
, which applies a grayscale transformation to the avatars of users who are currently offline:
ChatUI.avatarBitmapFactory = object : AvatarBitmapFactory(context) {
override suspend fun createUserBitmap(user: User, style: AvatarStyle, avatarSize: Int): Bitmap? {
val imageResult = context.imageLoader.execute(
ImageRequest.Builder(context)
.data(user.image)
.apply {
if (!user.online) {
transformations(GrayscaleTransformation())
}
}
.build()
)
return (imageResult.drawable as? BitmapDrawable)?.bitmap
}
}
note
This example uses the Coil library for loading the image URL and applying transformations to it. If you use Coil version 2.x, you should copy-paste GrayscaleTransformation.kt manually in your project for building this example.
This results in avatars rendered like this:
#
Excluding and Including the Current User in ChannelAvatarsThe default setting for our ChannelAvatar
s is to exclude the current user's image from the bitmap matrix.
If you want to disable this setting, you can do so by overriding a resource in your app, like so:
<resources>
<bool name="stream_ui_excludeCurrentUserFromChannelAvatars">false</bool>
</resources>
By passing false
to stream_ui_excludeCurrentUserFromChannelAvatars
, you'll stop the current user from being excluded in the list of last active users in a channel, when displaying the Channel
members bitmap.
#
Adding Extra Headers to Image RequestsIf you're using your own CDN, you might also need to add extra headers to image loading requests. You can do this by creating your own implementation of the ImageHeadersProvider
interface and then setting it on ChatUI
:
- Kotlin
- Java
ChatUI.imageHeadersProvider = object : ImageHeadersProvider {
override fun getImageRequestHeaders(): Map<String, String> {
return mapOf("token" to "12345")
}
}
ChatUI.setImageHeadersProvider(() -> {
Map<String, String> headers = new HashMap<>();
headers.put("token", "12345");
return headers;
});
#
Changing the Default FontYou can customize the default fonts used by all of the UI components. To change the fonts, implement the ChatFont
interface and set the new implementation on ChatUI
:
- Kotlin
- Java
ChatUI.fonts = object : ChatFonts {
override fun setFont(textStyle: TextStyle, textView: TextView) {
textStyle.apply(textView)
}
override fun setFont(textStyle: TextStyle, textView: TextView, defaultTypeface: Typeface) {
textStyle.apply(textView)
}
override fun getFont(textStyle: TextStyle): Typeface? = textStyle.font
}
ChatUI.setFonts(new ChatFonts() {
@Override
public void setFont(@NonNull TextStyle textStyle, @NonNull TextView textView) {
textStyle.apply(textView);
}
@Override
public void setFont(@NonNull TextStyle textStyle, @NonNull TextView textView, @NonNull Typeface defaultTypeface) {
textStyle.apply(textView);
}
@Nullable
@Override
public Typeface getFont(@NonNull TextStyle textStyle) {
return textStyle.getFont();
}
});
#
Transforming Message TextYou can easily provide a transformer that can transform and apply the message text to a given TextView
. You need to override ChatUI.messageTextTransformer
to an instance of ChatMessageTextTransformer
s implementation.
- Kotlin
- Java
ChatUI.messageTextTransformer = ChatMessageTextTransformer { textView: TextView, messageItem: MessageItem ->
// Transform messages to upper case.
textView.text = messageItem.message.text.uppercase()
}
ChatUI.setMessageTextTransformer((textView, messageItem) -> {
textView.setText(messageItem.getMessage().getText().toUpperCase(Locale.ROOT));
});
Stream UI TextView components don't have android:autoLink
property set because it conflicts with Markdown plugins.
note
You can use AutoLinkableTextTransformer
if you want to apply custom transformation but keep links clickable.
#
MarkdownThe SDK provides a standalone Markdown module stream-chat-android-markdown-transformer
that contains MarkdownTextTransformer
which is an implementation of ChatMessageTextTransformer
. It uses Markwon library internally.
- Kotlin
- Java
ChatUI.messageTextTransformer = MarkdownTextTransformer(context)
ChatUI.setMessageTextTransformer(new MarkdownTextTransformer(context));
If you use MarkdownTextTransformer
, don't use android:autoLink
attribute because it'll break the markdown Linkify implementation.
Then the SDK will parse Markdown automatically:
#
NavigatorThe SDK performs navigation in certain cases:
- Navigating to
AttachmentGalleryActivity
to display the gallery of pictures in the chat. - Opening the browser after clicking a link in the chat.
This is done performed by ChatNavigator
, which you can provide a custom ChatNavigationHandler
implementation to override its behavior:
- Kotlin
- Java
val navigationHandler = ChatNavigationHandler { destination: ChatDestination ->
// Perform some custom action here
true
}
ChatUI.navigator = ChatNavigator(navigationHandler)
ChatNavigationHandler chatNavigatorHandler = destination -> {
// Perform some custom action here
return true;
};
ChatUI.setNavigator(new ChatNavigator(chatNavigatorHandler));
#
Customizing ChannelNameFormatterYou can customize the way channel names are formatted by overriding the default ChannelNameFormatter
:
- Kotlin
- Java
ChatUI.channelNameFormatter = ChannelNameFormatter { channel, currentUser ->
channel.name
}
ChatUI.setChannelNameFormatter((channel, currentUser) -> channel.getName());
#
Customizing MessagePreviewFormatterYou can change the way the last messages are formatted in the channel list by overriding the default MessagePreviewFormatter
:
- Kotlin
- Java
ChatUI.messagePreviewFormatter = MessagePreviewFormatter { channel, message, currentUser ->
message.text
}
ChatUI.setMessagePreviewFormatter((channel, message, currentUser) -> message.getText());
#
Customizing DateFormatterOverriding the DateFormatter
allows you to change the way dates are formatted in the application:
- Kotlin
- Java
ChatUI.dateFormatter = object: DateFormatter {
private val dateFormatter = DateTimeFormatter.ofPattern("yy MM dd")
private val dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm")
override fun formatDate(localDateTime: LocalDateTime?): String {
localDateTime ?: return ""
return dateFormatter.format(localDateTime)
}
override fun formatTime(localTime: LocalTime?): String {
localTime ?: return ""
return dateTimeFormatter.format(localTime)
}
}
ChatUI.setDateFormatter(new DateFormatter() {
private DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yy MM dd");
private DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm");
@NonNull
@Override
public String formatDate(@Nullable LocalDateTime localDateTime) {
if (localDateTime == null) {
return "";
}
return dateFormatter.format(localDateTime);
}
@NonNull
@Override
public String formatTime(@Nullable LocalTime localTime) {
if (localTime == null) {
return "";
}
return dateTimeFormatter.format(localTime);
}
});
#
Showing ThreadSeparatorItem in empty threadsOverriding the showThreadSeparatorInEmptyThread
allows you to show the separator in empty threads:
- Kotlin
- Java
ChatUI.showThreadSeparatorInEmptyThread = true
ChatUI.setShowThreadSeparatorInEmptyThread(true);
Using this feature, you get the following behavior in empty threads.
On its own, it might not look like much, but when customizing the ThreadPlaceholderItem
, you can achieve powerful expanded behavior of threads, where you denote to the user that they're in an empty thread where they can leave replies.