TUTORIAL

How to Build an Android Chat App with Java or Kotlin

Learn how to use our Android Chat SDK in this comprehensive step-by-step tutorial.

In this tutorial, we’ll go over everything you need to build an Android chat app with the Stream Android SDK. At the end of the tutorial, you will have created a fully functional Chat application that you can continue to build on.

Make sure you take a few moments to learn about the Stream Chat API, the iOS Swift Chat SDK, React Chat and React Native Chat, along with our free Chat UI Kit for more resources on building your app with Stream.

Android Chat SDK Setup

You can use the Android Chat SDK with both Java or Kotlin, this tutorial has code examples in both languages.

This SDK is still in beta stage, both UI and Java client libraries will be production-ready before the end of September 2019

Add Stream Chat to Your Application Example

In order to follow the tutorial, you need Android Studio installed.

To get started with the Android Chat SDK, open Android Studio and create a new project called ChatDemo using the Empty Activity template. Pick your language of choice and then leave all other fields to their default state.

Next, we are going to add the StreamChat SDK library to our project’s dependencies and make some small adjustments to project setup.

Open the project build.gradle file and replace its content with this so that we can use jitpack.io to install our requirements.

buildscript {
  repositories {
      google()
      jcenter()

  }
  dependencies {
      classpath 'com.android.tools.build:gradle:3.4.0'
  }
}

allprojects {
  repositories {
      google()
      jcenter()
+     maven {
+         url "https://jitpack.io"
+     }
  }
}

task clean(type: Delete) {
  delete rootProject.buildDir
}

Next step is to change the application build.gradle script. Here we are going to add StreamChat to the dependencies and enable lambda expressions and data binding.

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.0"
    defaultConfig {
        applicationId "com.example.chatdemo"
        minSdkVersion 22
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
+   dataBinding {
+       enabled = true
+   }
    compileOptions {
+       sourceCompatibility JavaVersion.VERSION_1_8
+       targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
+   implementation 'com.github.getstream:stream-chat-android:1.5.6'
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

When you save gradle files, make sure to sync the project (Android Studio will prompt you for this) with the new changes.

Initialize streamChat

Now that we have StreamChat added to the project, we need to initialize and add chat to our app and set the current user. Open the MainActivity class and change it to this:

package com.example.chatdemo;

import android.os.Bundle;

import com.getstream.sdk.chat.model.Channel;
import com.getstream.sdk.chat.model.User;
import com.getstream.sdk.chat.rest.core.StreamChat;
import com.getstream.sdk.chat.view.activity.ChatActivity;

import java.util.HashMap;

public class MainActivity extends ChatActivity {

    private final String API_KEY = "b67pax5b2wdq";
    private final String USER_NAME = "Noisy frost";
    private final String USER_ID = "noisy-frost-9";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_main);

        StreamChat streamChat = new StreamChat(API_KEY);

        HashMap<String, Object> extraData = new HashMap<>();
        extraData.put("name", USER_NAME);
        extraData.put("image", "https://stepupandlive.files.wordpress.com/2014/09/3d-animated-frog-image.jpg");

        User user = new User(USER_ID, extraData);

        try {
            streamChat.setUser(user, "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoibm9pc3ktZnJvc3QtOSJ9.0kAVE6y6dZRoPSHoATsnSrFAQXMhy73VsVDZ8AWEku8");
        } catch (Exception e){
            e.printStackTrace();
        }

        Channel channel = new Channel("message", "general", null);
        streamChat.setChannel(channel);

        super.onCreate(savedInstanceState);
    }
}
package io.getstream.chat.chatkotlindemo

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.getstream.sdk.chat.model.User
import com.getstream.sdk.chat.rest.core.StreamChat
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)

   val streamChat = StreamChat("b67pax5b2wdq")

   /**
   * You can add additional data to users
   */
   val extraData = HashMap<String, Any>()
   extraData["answer"] = 42

   val user = User("noisy-frost-9", "Noisy frost", "https://stepupandlive.files.wordpress.com/2014/09/3d-animated-frog-image.jpg", extraData)
   streamChat.setUser(user, "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoibm9pc3ktZnJvc3QtOSJ9.0kAVE6y6dZRoPSHoATsnSrFAQXMhy73VsVDZ8AWEku8")
   }
}

Your app is now configured with the API credentials for the demo app.

Because this is a tutorial, we pre-generated a user token to keep things easy to follow. In a real-world application, your authentication backend would generate such token at log-in / signup and hand it over to the mobile app. More information about this is available on the Chat API docs.

Single Conversation Screen

Adding chat is very simple and the library comes with a built-in ChatActivity.java class that manages all functionality for one channel.

With the previous code we made the main activity of the application subclass of ChatActivity, this way we can easily see all the functionality that comes included with the SDK without adding too much boiler plate code.

Now we’re ready to run our application.

video/screenshot

Chat Features

Did you already try to post a message like this? /giphy awesome

Stream Chat comes with out-of-the-box features such as:

  1. Commands: focus on the composer and type / to use commands, like /giphy
  2. Reactions: tap on a message to add reactions or long press on images
  3. Link preview will be generated automatically when you send a link.
  4. Attach images: use the plus button ⨁ in the composer to attach images or files.
  5. Edit message: long press on your own messages to open the menu with the Edit button.
  6. Typing events: you’ll see typing events at the bottom of messages when someone starts typing.
  7. Threads: you can create threads to any message in a channel, just tap on a message and hit Reply to enter the thread view.

Some of the features are hard to see in action with just one user online. If you are interested, you can open the same channel on the web and try user to user interactions like typing events, reactions and threads.

Multiple Conversations

Most chat applications handle more than just one single conversation. Apps like Facebook Messenger, WhatsApp and Telegram allow you to have multiple one to one and group conversations.

Let’s see how we can change our application so that we first see the list of channels.

Open activity_main.xml (that you want add StreamChat screen) and add a Framelayout

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

Open MainActivity and add ChannelListFragment as following:

package com.example.chatdemo;

import android.os.Bundle;

+ import androidx.appcompat.app.AppCompatActivity;
+ import androidx.fragment.app.FragmentManager;
+ import androidx.fragment.app.FragmentTransaction;
- import com.getstream.sdk.chat.model.Channel;

+ import com.getstream.sdk.chat.component.Component;
import com.getstream.sdk.chat.model.User;
import com.getstream.sdk.chat.rest.core.StreamChat;
- import com.getstream.sdk.chat.view.activity.ChatActivity;

+ import com.getstream.sdk.chat.view.fragment.ChannelListFragment;

import java.util.HashMap;

+ import static com.getstream.sdk.chat.enums.Filters.in;

- public class MainActivity extends ChatActivity {
+ public class MainActivity extends AppCompatActivity {
    private final String API_KEY = "b67pax5b2wdq";
    private final String USER_NAME = "Noisy frost";
    private final String USER_ID = "noisy-frost-9";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        StreamChat streamChat = new StreamChat(API_KEY);

        HashMap<String, Object> extraData = new HashMap<>();
        extraData.put("name", USER_NAME);
        extraData.put("image", "https://stepupandlive.files.wordpress.com/2014/09/3d-animated-frog-image.jpg");

        User user = new User(USER_ID, extraData);

        try {
            streamChat.setUser(user, "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoibm9pc3ktZnJvc3QtOSJ9.0kAVE6y6dZRoPSHoATsnSrFAQXMhy73VsVDZ8AWEku8");
        } catch (Exception e){
            e.printStackTrace();
        }

-       Channel channel = new Channel("message", "general", null);
-       streamChat.setChannel(channel);

+       ChannelListFragment fragment = new ChannelListFragment();
+       fragment.containerResId = R.id.container;

+       Component component = new Component();
+       component.channel.filter(in("members", USER_ID));
+       streamChat.setComponent(component);

+       FragmentManager fragmentManager = getSupportFragmentManager();
+       FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
+       fragmentTransaction.replace(R.id.container, fragment);
+       fragmentTransaction.addToBackStack(null);
+       fragmentTransaction.commit();
    }
}
package io.getstream.chat.chatkotlindemo

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.getstream.sdk.chat.model.User
import com.getstream.sdk.chat.rest.core.StreamChat
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)

 val streamChat = StreamChat("b67pax5b2wdq")
 val user = User("noisy-frost-9", "Noisy frost", "", additionalFields)
       streamChat.setUser(user, "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoibm9pc3ktZnJvc3QtOSJ9.0kAVE6y6dZRoPSHoATsnSrFAQXMhy73VsVDZ8AWEku8")

 val fragment = ChannelListFragment()
   	 fragment.containerResId = R.id.container
   	 fragment.streamChat = streamChat
   	 val fragmentManager = supportFragmentManager
   	 val fragmentTransaction = fragmentManager.beginTransaction()
   	 fragmentTransaction.replace(R.id.container, fragment)
       fragmentTransaction.addToBackStack(null)
       fragmentTransaction.commit()
   }
}

Not all applications have the same logic when it comes to listing channels. Both the APIs and SDK allow you to provide your own filtering and ordering parameters.

You can find more about how the list of channels and users can be filtered and ordered in the API and SDK docs.

Customize UI Components

So far you’ve learned how to use the default components. The library has been designed to be fully extendable, enabling you to build any type of chat. In particular we aim to support these 5 use cases:

  • Social/Messaging chat
  • Team/Slack style chat
  • Customer support chat
  • Livestream chat
  • Gaming

The SDK allows you to change UI styling as well as rendering and behavior logic.

Custom Channel Preview

Out of the box, the channel previews shows the avatar for the users that read the whole conversation. The preview can be changed to show the total count of messages instead of the read indicator.

package com.example.chatdemo;

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;

import com.getstream.sdk.chat.component.Component;
import com.getstream.sdk.chat.model.User;
+ import com.getstream.sdk.chat.enums.ReadIndicator;
import com.getstream.sdk.chat.rest.core.StreamChat;

import com.getstream.sdk.chat.view.fragment.ChannelListFragment;

import java.util.HashMap;

import static com.getstream.sdk.chat.enums.Filters.in;

public class MainActivity extends AppCompatActivity {
    private final String API_KEY = "b67pax5b2wdq";
    private final String USER_NAME = "Noisy frost";
    private final String USER_ID = "noisy-frost-9";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        StreamChat streamChat = new StreamChat(API_KEY);

        HashMap<String, Object> extraData = new HashMap<>();
        extraData.put("name", USER_NAME);
        extraData.put("image", "https://stepupandlive.files.wordpress.com/2014/09/3d-animated-frog-image.jpg");

        User user = new User(USER_ID, extraData);

        try {
            streamChat.setUser(user, "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoibm9pc3ktZnJvc3QtOSJ9.0kAVE6y6dZRoPSHoATsnSrFAQXMhy73VsVDZ8AWEku8");
        } catch (Exception e){
            e.printStackTrace();
        }

       ChannelListFragment fragment = new ChannelListFragment();
       fragment.containerResId = R.id.container;

       Component component = new Component();
       component.channel.filter(in("members", USER_ID));
+      component.channel.setReadIndicatorType(ReadIndicator.UNREAD_COUNT);
       streamChat.setComponent(component);

       FragmentManager fragmentManager = getSupportFragmentManager();
       FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
       fragmentTransaction.replace(R.id.container, fragment);
       fragmentTransaction.addToBackStack(null);
       fragmentTransaction.commit();
    }
}
video/screenshot

Custom Message

colors

  • message_background_incoming
  • message_background_outgoing
  • message_text_incoming
  • message_text_outgoing
  • mesage_border

dimensions

  • message_text_font_size
<color name="message_background_incoming"><Your color></color>
<color name="message_background_outgoing"><Your color></color>
<color name="message_text_incoming"><Your color></color>
<color name="message_text_outgoing"><Your color></color>
<color name="mesage_border"><Your color></color>
<dimen name="message_text_font_size"><Your size></dimen>
Figure14. Custom message screen

Custom Chat Theme

Replacing the default UI implementation with own is great, but it required some time to develop. What if we only need to change some colors, fonts and borders? It’s easy to do with styles objects. Stream Chat provide light and dark styles. The light is by default.

Let’s change styles for in the last our DemoViewController. But before that we should move back to the default UI implementation of messages. Just remove the function messageCell.

You should setup the style property of chat or channels before showing them or setup it in some init method. Let’s change init method of our DemoChatViewController:

  • chat_theme
<color name="chat_theme"><Your color></color>
Figure15. Custom message background color

Final thoughts

In this Android Chat App tutorial we learned how to build a fully functional chat app using Java or Kotlin. We also showed how easy it is to customize the behavior and build any type of chat or messaging experience.

The underlying chat API is based on Go, RocksDB and Raft. This makes the chat experience extremely fast with response times that are often below 10ms. Stream powers activity feeds and chat for over 500 million end users, so you can feel confident about our APIs.

Both the Java Chat SDK and the Chat API have plenty more features available to support more advanced use-cases such as push notifications, content moderation, rich messages and more.

Next Steps