Skip to main content

UI Kit quickstart

Instant messaging connects people wherever they are and allows them to communicate with others in real time. With built-in user interfaces (UI) for the conversation list and contact list, the Agora Chat UI Kit enables you to quickly embed real-time messaging into your app without requiring extra effort on the UI.

This page shows a sample code to add peer-to-peer messaging into your app by using the Agora Chat UI Kit.

Understand the tech

The following figure shows the workflow of how clients send and receive peer-to-peer messages:

1643335864426

  1. Clients retrieve a token from your app server.
  2. Client A and Client B log in to Agora Chat.
  3. Client A sends a message to Client B. The message is sent to the Agora Chat server, and the server delivers the message to Client B. When Client B receives the message, the SDK triggers an event. Client B listens for the event and gets the message.

Prerequisites

  • An Android simulator or a physical Android device.
  • Android Studio 3.2 or higher.
  • Java Development Kit (JDK). You can refer to the User Guide of Android for applicable versions.

Project setup

Follow the steps to create the environment necessary to add video call into your app.

  1. For new projects, in Android Studio, create a Phone and Tablet Android project with an Empty Activity.

    After creating the project, Android Studio automatically starts gradle sync. Ensure that the sync succeeds before you continue.

  2. Integrate the Chat SDK into your project with Maven Central.

    a. In /Gradle Scripts/build.gradle(Project: <projectname>), add the following lines to add the Maven Central dependency:


    _12
    buildscript {
    _12
    repositories {
    _12
    ...
    _12
    mavenCentral()
    _12
    }
    _12
    }
    _12
    allprojects {
    _12
    repositories {
    _12
    ...
    _12
    mavenCentral()
    _12
    }
    _12
    }

    The way to add the Maven Central dependency can be different if you set dependencyResolutionManagement in your Android project.

    b. In /Gradle Scripts/build.gradle(Module: <projectname>.app), add the following lines to integrate the Chat UI Kit into your Android project:


    _16
    android {
    _16
    defaultConfig {
    _16
    // The Android OS version should be 19 or higher.
    _16
    minSdkVersion 19
    _16
    }
    _16
    compileOptions {
    _16
    sourceCompatibility JavaVersion.VERSION_1_8
    _16
    targetCompatibility JavaVersion.VERSION_1_8
    _16
    }
    _16
    }
    _16
    dependencies {
    _16
    ...
    _16
    // Replace X.Y.Z with the latest version of the Chat UI Kit.
    _16
    // For the latest version, go to https://search.maven.org/.
    _16
    implementation 'io.agora.rtc:chat-uikit:X.Y.Z'
    _16
    }

  3. Add permissions for network and device access.

    In /app/Manifests/AndroidManifest.xml, add the following permissions after </application>:


    _7
    <uses-permission android:name="android.permission.INTERNET" />
    _7
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    _7
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    _7
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    _7
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    _7
    <uses-permission android:name="android.permission.CAMERA"/>
    _7
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>

    These are the minimum permissions you need to add to start Chat. You can also add other permissions according to your use case.

  4. Prevent code obfuscation.

    In /Gradle Scripts/proguard-rules.pro, add the following line:


    _2
    -keep class io.agora.** {*;}
    _2
    -dontwarn io.agora.**

This section shows how to use the Chat UI Kit to implement peer-to-peer messaging in your app step by step.

Create the UI

  1. To add the text strings used by the UI, open app/res/values/strings.xml and replace the content with the following:


    _23
    <resources>
    _23
    <string name="app_name">AgoraChatUIKitQuickstart</string>
    _23
    _23
    <string name="username_or_pwd_miss">Username or password is empty</string>
    _23
    <string name="sign_up_success">Sign up success!</string>
    _23
    <string name="sign_in_success">Sign in success!</string>
    _23
    <string name="sign_out_success">Sign out success!</string>
    _23
    <string name="send_message_success">Send message success!</string>
    _23
    <string name="enter_username">Enter username</string>
    _23
    <string name="enter_password">Enter password</string>
    _23
    <string name="sign_in">Sign in</string>
    _23
    <string name="sign_out">Sign out</string>
    _23
    <string name="sign_up">Sign up</string>
    _23
    <string name="enter_to_username">Enter to username</string>
    _23
    <string name="start_chat">Start chat</string>
    _23
    <string name="enter_content">Enter content</string>
    _23
    <string name="log_hint">Show log area...</string>
    _23
    <string name="has_login_before">An account has been signed in, please sign out first and then sign in</string>
    _23
    <string name="sign_in_first">Please sign in first</string>
    _23
    <string name="not_find_send_name">Please enter the username who you want to send first!</string>
    _23
    _23
    <string name="app_key">41117440#383391</string>
    _23
    </resources>

  2. To add the UI framework, open app/res/layout/activity_main.xml and replace the content with the following:


    _126
    <?xml version="1.0" encoding="utf-8"?>
    _126
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    _126
    xmlns:app="http://schemas.android.com/apk/res-auto"
    _126
    xmlns:tools="http://schemas.android.com/tools"
    _126
    android:layout_width="match_parent"
    _126
    android:layout_height="match_parent"
    _126
    android:orientation="vertical"
    _126
    tools:context=".MainActivity">
    _126
    _126
    <androidx.constraintlayout.widget.ConstraintLayout
    _126
    android:layout_width="match_parent"
    _126
    android:layout_height="0dp"
    _126
    android:layout_weight="1">
    _126
    _126
    <EditText
    _126
    android:id="@+id/et_username"
    _126
    android:layout_width="0dp"
    _126
    android:layout_height="wrap_content"
    _126
    android:hint="@string/enter_username"
    _126
    app:layout_constraintLeft_toLeftOf="parent"
    _126
    app:layout_constraintRight_toLeftOf="@id/et_pwd"
    _126
    app:layout_constraintTop_toTopOf="parent"
    _126
    android:layout_marginStart="20dp"
    _126
    android:layout_marginEnd="20dp"
    _126
    android:layout_marginTop="20dp"/>
    _126
    _126
    <EditText
    _126
    android:id="@+id/et_pwd"
    _126
    android:layout_width="0dp"
    _126
    android:layout_height="wrap_content"
    _126
    android:hint="@string/enter_password"
    _126
    android:inputType="textPassword"
    _126
    app:layout_constraintLeft_toRightOf="@id/et_username"
    _126
    app:layout_constraintRight_toRightOf="parent"
    _126
    app:layout_constraintTop_toTopOf="@id/et_username"
    _126
    android:layout_marginEnd="20dp"/>
    _126
    _126
    <Button
    _126
    android:id="@+id/btn_sign_in"
    _126
    android:layout_width="0dp"
    _126
    android:layout_height="wrap_content"
    _126
    android:textSize="12sp"
    _126
    android:text="@string/sign_in"
    _126
    android:onClick="signInWithToken"
    _126
    app:layout_constraintLeft_toLeftOf="parent"
    _126
    app:layout_constraintTop_toBottomOf="@id/et_pwd"
    _126
    app:layout_constraintRight_toLeftOf="@id/btn_sign_out"
    _126
    android:layout_marginStart="10dp"
    _126
    android:layout_marginEnd="5dp"
    _126
    android:layout_marginTop="10dp"/>
    _126
    _126
    <Button
    _126
    android:id="@+id/btn_sign_out"
    _126
    android:layout_width="0dp"
    _126
    android:layout_height="wrap_content"
    _126
    android:textSize="12sp"
    _126
    android:text="@string/sign_out"
    _126
    android:onClick="signOut"
    _126
    app:layout_constraintLeft_toRightOf="@id/btn_sign_in"
    _126
    app:layout_constraintTop_toBottomOf="@id/et_pwd"
    _126
    app:layout_constraintRight_toLeftOf="@id/btn_sign_up"
    _126
    android:layout_marginStart="5dp"
    _126
    android:layout_marginEnd="5dp"
    _126
    android:layout_marginTop="10dp"/>
    _126
    _126
    <Button
    _126
    android:id="@+id/btn_sign_up"
    _126
    android:layout_width="0dp"
    _126
    android:layout_height="wrap_content"
    _126
    android:text="@string/sign_up"
    _126
    android:textSize="12sp"
    _126
    android:onClick="signUp"
    _126
    app:layout_constraintLeft_toRightOf="@id/btn_sign_out"
    _126
    app:layout_constraintRight_toRightOf="parent"
    _126
    app:layout_constraintTop_toBottomOf="@id/et_pwd"
    _126
    app:layout_constraintTop_toTopOf="@id/btn_sign_in"
    _126
    android:layout_marginStart="5dp"
    _126
    android:layout_marginEnd="10dp"/>
    _126
    _126
    <EditText
    _126
    android:id="@+id/et_to_username"
    _126
    android:layout_width="0dp"
    _126
    android:layout_height="wrap_content"
    _126
    android:hint="@string/enter_to_username"
    _126
    app:layout_constraintLeft_toLeftOf="parent"
    _126
    app:layout_constraintRight_toLeftOf="@id/btn_start_chat"
    _126
    app:layout_constraintTop_toBottomOf="@id/btn_sign_up"
    _126
    app:layout_constraintHorizontal_weight="2"
    _126
    android:layout_marginStart="20dp"
    _126
    android:layout_marginEnd="20dp"
    _126
    android:layout_marginTop="10dp"
    _126
    android:layout_marginBottom="10dp"/>
    _126
    _126
    <Button
    _126
    android:id="@+id/btn_start_chat"
    _126
    android:layout_width="0dp"
    _126
    android:layout_height="wrap_content"
    _126
    android:text="@string/start_chat"
    _126
    android:onClick="startChat"
    _126
    android:textSize="12sp"
    _126
    app:layout_constraintLeft_toRightOf="@id/et_to_username"
    _126
    app:layout_constraintRight_toRightOf="parent"
    _126
    app:layout_constraintBottom_toBottomOf="@id/et_to_username"
    _126
    app:layout_constraintHorizontal_weight="1"
    _126
    android:layout_marginEnd="10dp"/>
    _126
    _126
    <FrameLayout
    _126
    android:id="@+id/fl_fragment"
    _126
    android:layout_width="match_parent"
    _126
    android:layout_height="0dp"
    _126
    app:layout_constraintLeft_toLeftOf="parent"
    _126
    app:layout_constraintRight_toRightOf="parent"
    _126
    app:layout_constraintTop_toBottomOf="@id/btn_start_chat"
    _126
    app:layout_constraintBottom_toBottomOf="parent"/>
    _126
    _126
    </androidx.constraintlayout.widget.ConstraintLayout>
    _126
    _126
    <TextView
    _126
    android:id="@+id/tv_log"
    _126
    android:layout_width="match_parent"
    _126
    android:layout_height="100dp"
    _126
    android:hint="@string/log_hint"
    _126
    android:scrollbars="vertical"
    _126
    android:padding="10dp"/>
    _126
    _126
    </LinearLayout>

Implementation

To enable your app to send and receive messages between individual users, do the following:

  1. Implement sending and receiving messages.

    In app/java/io.agora.agorachatquickstart/MainActivity, replace the code with the following:


    _359
    package io.agora.chatuikitquickstart;
    _359
    _359
    import android.Manifest;
    _359
    import android.os.Bundle;
    _359
    import android.text.TextUtils;
    _359
    import android.text.method.ScrollingMovementMethod;
    _359
    import android.view.MotionEvent;
    _359
    import android.view.View;
    _359
    import android.widget.EditText;
    _359
    import android.widget.TextView;
    _359
    import android.widget.Toast;
    _359
    _359
    import androidx.appcompat.app.AppCompatActivity;
    _359
    import androidx.core.content.ContextCompat;
    _359
    _359
    import org.json.JSONObject;
    _359
    _359
    import java.util.HashMap;
    _359
    import java.util.Map;
    _359
    _359
    import io.agora.CallBack;
    _359
    import io.agora.ConnectionListener;
    _359
    import io.agora.Error;
    _359
    import io.agora.chat.ChatClient;
    _359
    import io.agora.chat.ChatMessage;
    _359
    import io.agora.chat.ChatOptions;
    _359
    import io.agora.chat.uikit.EaseUIKit;
    _359
    import io.agora.chat.uikit.chat.EaseChatFragment;
    _359
    import io.agora.chat.uikit.chat.interfaces.OnChatExtendMenuItemClickListener;
    _359
    import io.agora.chat.uikit.chat.interfaces.OnChatInputChangeListener;
    _359
    import io.agora.chat.uikit.chat.interfaces.OnChatRecordTouchListener;
    _359
    import io.agora.chat.uikit.chat.interfaces.OnMessageSendCallBack;
    _359
    import io.agora.chatuikitquickstart.utils.LogUtils;
    _359
    import io.agora.chatuikitquickstart.utils.PermissionsManager;
    _359
    import io.agora.cloud.HttpClientManager;
    _359
    import io.agora.cloud.HttpResponse;
    _359
    import io.agora.util.EMLog;
    _359
    _359
    import static io.agora.cloud.HttpClientManager.Method_POST;
    _359
    _359
    _359
    public class MainActivity extends AppCompatActivity {
    _359
    private static final String NEW_LOGIN = "NEW_LOGIN";
    _359
    private static final String RENEW_TOKEN = "RENEW_TOKEN";
    _359
    private static final String LOGIN_URL = "https://a41.chat.agora.io/app/chat/user/login";
    _359
    private static final String REGISTER_URL = "https://a41.chat.agora.io/app/chat/user/register";
    _359
    private EditText et_username;
    _359
    private TextView tv_log;
    _359
    private ConnectionListener connectionListener;
    _359
    _359
    @Override
    _359
    protected void onCreate(Bundle savedInstanceState) {
    _359
    super.onCreate(savedInstanceState);
    _359
    setContentView(R.layout.activity_main);
    _359
    initView();
    _359
    requestPermissions();
    _359
    initSDK();
    _359
    addConnectionListener();
    _359
    }
    _359
    _359
    private void initView() {
    _359
    et_username = findViewById(R.id.et_username);
    _359
    tv_log = findViewById(R.id.tv_log);
    _359
    tv_log.setMovementMethod(new ScrollingMovementMethod());
    _359
    }
    _359
    _359
    private void requestPermissions() {
    _359
    checkPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE, 110);
    _359
    }
    _359
    _359
    //=================== init SDK start ========================
    _359
    private void initSDK() {
    _359
    ChatOptions options = new ChatOptions();
    _359
    // Set your appkey applied from Agora Console
    _359
    String sdkAppkey = getString(R.string.app_key);
    _359
    if(TextUtils.isEmpty(sdkAppkey)) {
    _359
    Toast.makeText(MainActivity.this, "You should set your AppKey first!", Toast.LENGTH_SHORT).show();
    _359
    return;
    _359
    }
    _359
    // Set your appkey to options
    _359
    options.setAppKey(sdkAppkey);
    _359
    // Set whether confirmation of delivery is required by the recipient. Default: false
    _359
    options.setRequireDeliveryAck(true);
    _359
    // Set not to log in automatically
    _359
    options.setAutoLogin(false);
    _359
    // Use uikit to initialize Chat SDK
    _359
    EaseUIKit.getInstance().init(this, options);
    _359
    // Make Chat SDK debuggable
    _359
    ChatClient.getInstance().setDebugMode(true);
    _359
    }
    _359
    //=================== init SDK end ========================
    _359
    //================= SDK listener start ====================
    _359
    private void addConnectionListener() {
    _359
    connectionListener = new ConnectionListener() {
    _359
    @Override
    _359
    public void onConnected() {
    _359
    }
    _359
    _359
    @Override
    _359
    public void onDisconnected(int error) {
    _359
    if (error == Error.USER_REMOVED) {
    _359
    onUserException("account_removed");
    _359
    } else if (error == Error.USER_LOGIN_ANOTHER_DEVICE) {
    _359
    onUserException("account_conflict");
    _359
    } else if (error == Error.SERVER_SERVICE_RESTRICTED) {
    _359
    onUserException("account_forbidden");
    _359
    } else if (error == Error.USER_KICKED_BY_CHANGE_PASSWORD) {
    _359
    onUserException("account_kicked_by_change_password");
    _359
    } else if (error == Error.USER_KICKED_BY_OTHER_DEVICE) {
    _359
    onUserException("account_kicked_by_other_device");
    _359
    } else if(error == Error.USER_BIND_ANOTHER_DEVICE) {
    _359
    onUserException("user_bind_another_device");
    _359
    } else if(error == Error.USER_DEVICE_CHANGED) {
    _359
    onUserException("user_device_changed");
    _359
    } else if(error == Error.USER_LOGIN_TOO_MANY_DEVICES) {
    _359
    onUserException("user_login_too_many_devices");
    _359
    }
    _359
    }
    _359
    _359
    @Override
    _359
    public void onTokenExpired() {
    _359
    //login again
    _359
    signInWithToken(null);
    _359
    LogUtils.showLog(tv_log,"ConnectionListener onTokenExpired");
    _359
    }
    _359
    _359
    @Override
    _359
    public void onTokenWillExpire() {
    _359
    getTokenFromAppServer(RENEW_TOKEN);
    _359
    LogUtils.showLog(tv_log, "ConnectionListener onTokenWillExpire");
    _359
    }
    _359
    };
    _359
    // Call removeConnectionListener(connectionListener) when the activity is destroyed
    _359
    ChatClient.getInstance().addConnectionListener(connectionListener);
    _359
    }
    _359
    _359
    //================= SDK listener end ====================
    _359
    //=================== click event start ========================
    _359
    _359
    /**
    _359
    * Sign up with username and password.
    _359
    */
    _359
    public void signUp(View view) {
    _359
    String username = et_username.getText().toString().trim();
    _359
    String pwd = ((EditText) findViewById(R.id.et_pwd)).getText().toString().trim();
    _359
    if(TextUtils.isEmpty(username) || TextUtils.isEmpty(pwd)) {
    _359
    LogUtils.showErrorToast(this, tv_log, getString(R.string.username_or_pwd_miss));
    _359
    return;
    _359
    }
    _359
    execute(()-> {
    _359
    try {
    _359
    Map<String, String> headers = new HashMap<>();
    _359
    headers.put("Content-Type", "application/json");
    _359
    JSONObject request = new JSONObject();
    _359
    request.putOpt("userAccount", username);
    _359
    request.putOpt("userPassword", pwd);
    _359
    _359
    LogUtils.showErrorLog(tv_log,"begin to signUp...");
    _359
    _359
    HttpResponse response = HttpClientManager.httpExecute(REGISTER_URL, headers, request.toString(), Method_POST);
    _359
    int code= response.code;
    _359
    String responseInfo = response.content;
    _359
    if (code == 200) {
    _359
    if (responseInfo != null && responseInfo.length() > 0) {
    _359
    JSONObject object = new JSONObject(responseInfo);
    _359
    String resultCode = object.getString("code");
    _359
    if(resultCode.equals("RES_OK")) {
    _359
    LogUtils.showToast(MainActivity.this, tv_log, getString(R.string.sign_up_success));
    _359
    }else{
    _359
    String errorInfo = object.getString("errorInfo");
    _359
    LogUtils.showErrorLog(tv_log,errorInfo);
    _359
    }
    _359
    } else {
    _359
    LogUtils.showErrorLog(tv_log,responseInfo);
    _359
    }
    _359
    } else {
    _359
    LogUtils.showErrorLog(tv_log,responseInfo);
    _359
    }
    _359
    } catch (Exception e) {
    _359
    e.printStackTrace();
    _359
    LogUtils.showErrorLog(tv_log, e.getMessage());
    _359
    }
    _359
    });
    _359
    }
    _359
    _359
    /**
    _359
    * Log in with token.
    _359
    */
    _359
    public void signInWithToken(View view) {
    _359
    getTokenFromAppServer(NEW_LOGIN);
    _359
    }
    _359
    _359
    /**
    _359
    * Sign out.
    _359
    */
    _359
    public void signOut(View view) {
    _359
    if(ChatClient.getInstance().isLoggedInBefore()) {
    _359
    ChatClient.getInstance().logout(true, new CallBack() {
    _359
    @Override
    _359
    public void onSuccess() {
    _359
    LogUtils.showToast(MainActivity.this, tv_log, getString(R.string.sign_out_success));
    _359
    }
    _359
    _359
    @Override
    _359
    public void onError(int code, String error) {
    _359
    LogUtils.showErrorToast(MainActivity.this, tv_log, "Sign out failed! code: "+code + " error: "+error);
    _359
    }
    _359
    _359
    @Override
    _359
    public void onProgress(int progress, String status) {
    _359
    _359
    }
    _359
    });
    _359
    }
    _359
    }
    _359
    _359
    public void startChat(View view) {
    _359
    EditText et_to_username = findViewById(R.id.et_to_username);
    _359
    String toChatUsername = et_to_username.getText().toString().trim();
    _359
    // check username
    _359
    if(TextUtils.isEmpty(toChatUsername)) {
    _359
    LogUtils.showErrorToast(this, tv_log, getString(R.string.not_find_send_name));
    _359
    return;
    _359
    }
    _359
    // 1: single chat; 2: group chat; 3: chat room
    _359
    EaseChatFragment fragment = new EaseChatFragment.Builder(toChatUsername, 1)
    _359
    .useHeader(false)
    _359
    .setOnChatExtendMenuItemClickListener(new OnChatExtendMenuItemClickListener() {
    _359
    @Override
    _359
    public boolean onChatExtendMenuItemClick(View view, int itemId) {
    _359
    if(itemId == io.agora.chat.uikit.R.id.extend_item_take_picture) {
    _359
    return !checkPermissions(Manifest.permission.CAMERA, 111);
    _359
    }else if(itemId == io.agora.chat.uikit.R.id.extend_item_picture || itemId == io.agora.chat.uikit.R.id.extend_item_file || itemId == io.agora.chat.uikit.R.id.extend_item_video) {
    _359
    return !checkPermissions(Manifest.permission.READ_EXTERNAL_STORAGE, 112);
    _359
    }
    _359
    return false;
    _359
    }
    _359
    })
    _359
    .setOnChatRecordTouchListener(new OnChatRecordTouchListener() {
    _359
    @Override
    _359
    public boolean onRecordTouch(View v, MotionEvent event) {
    _359
    return !checkPermissions(Manifest.permission.RECORD_AUDIO, 113);
    _359
    }
    _359
    })
    _359
    .setOnMessageSendCallBack(new OnMessageSendCallBack() {
    _359
    @Override
    _359
    public void onSuccess(ChatMessage message) {
    _359
    LogUtils.showLog(tv_log, "Send success: message type: " + message.getType().name());
    _359
    }
    _359
    _359
    @Override
    _359
    public void onError(int code, String errorMsg) {
    _359
    LogUtils.showErrorLog(tv_log, "Send failed: error code: "+code + " errorMsg: "+errorMsg);
    _359
    }
    _359
    })
    _359
    .build();
    _359
    getSupportFragmentManager().beginTransaction().replace(R.id.fl_fragment, fragment).commit();
    _359
    }
    _359
    //=================== click event end ========================
    _359
    //=================== get token from server start ========================
    _359
    _359
    private void getTokenFromAppServer(String requestType) {
    _359
    if(ChatClient.getInstance().getOptions().getAutoLogin() && ChatClient.getInstance().isLoggedInBefore()) {
    _359
    LogUtils.showErrorLog(tv_log, getString(R.string.has_login_before));
    _359
    return;
    _359
    }
    _359
    String username = et_username.getText().toString().trim();
    _359
    String pwd = ((EditText) findViewById(R.id.et_pwd)).getText().toString().trim();
    _359
    if(TextUtils.isEmpty(username) || TextUtils.isEmpty(pwd)) {
    _359
    LogUtils.showErrorToast(MainActivity.this, tv_log, getString(R.string.username_or_pwd_miss));
    _359
    return;
    _359
    }
    _359
    execute(()-> {
    _359
    try {
    _359
    Map<String, String> headers = new HashMap<>();
    _359
    headers.put("Content-Type", "application/json");
    _359
    _359
    JSONObject request = new JSONObject();
    _359
    request.putOpt("userAccount", username);
    _359
    request.putOpt("userPassword", pwd);
    _359
    _359
    LogUtils.showErrorLog(tv_log,"begin to getTokenFromAppServer ...");
    _359
    _359
    HttpResponse response = HttpClientManager.httpExecute(LOGIN_URL, headers, request.toString(), Method_POST);
    _359
    int code = response.code;
    _359
    String responseInfo = response.content;
    _359
    if (code == 200) {
    _359
    if (responseInfo != null && responseInfo.length() > 0) {
    _359
    JSONObject object = new JSONObject(responseInfo);
    _359
    String token = object.getString("accessToken");
    _359
    if(TextUtils.equals(requestType, NEW_LOGIN)) {
    _359
    ChatClient.getInstance().loginWithAgoraToken(username, token, new CallBack() {
    _359
    @Override
    _359
    public void onSuccess() {
    _359
    LogUtils.showToast(MainActivity.this, tv_log, getString(R.string.sign_in_success));
    _359
    }
    _359
    _359
    @Override
    _359
    public void onError(int code, String error) {
    _359
    LogUtils.showErrorToast(MainActivity.this, tv_log, "Login failed! code: " + code + " error: " + error);
    _359
    }
    _359
    _359
    @Override
    _359
    public void onProgress(int progress, String status) {
    _359
    _359
    }
    _359
    });
    _359
    }else if(TextUtils.equals(requestType, RENEW_TOKEN)) {
    _359
    ChatClient.getInstance().renewToken(token);
    _359
    }
    _359
    _359
    } else {
    _359
    LogUtils.showErrorToast(MainActivity.this, tv_log, "getTokenFromAppServer failed! code: " + code + " error: " + responseInfo);
    _359
    }
    _359
    } else {
    _359
    LogUtils.showErrorToast(MainActivity.this, tv_log, "getTokenFromAppServer failed! code: " + code + " error: " + responseInfo);
    _359
    }
    _359
    } catch (Exception e) {
    _359
    e.printStackTrace();
    _359
    LogUtils.showErrorToast(MainActivity.this, tv_log, "getTokenFromAppServer failed! code: " + 0 + " error: " + e.getMessage());
    _359
    _359
    }
    _359
    });
    _359
    }
    _359
    //=================== get token from server end ========================
    _359
    _359
    /**
    _359
    * Check and request permission
    _359
    * @param permission
    _359
    * @param requestCode
    _359
    * @return
    _359
    */
    _359
    private boolean checkPermissions(String permission, int requestCode) {
    _359
    if(!PermissionsManager.getInstance().hasPermission(this, permission)) {
    _359
    PermissionsManager.getInstance().requestPermissions(this, new String[]{permission}, requestCode);
    _359
    return false;
    _359
    }
    _359
    return true;
    _359
    }
    _359
    /**
    _359
    * user met some exception: conflict, removed or forbidden, goto login activity
    _359
    */
    _359
    protected void onUserException(String exception) {
    _359
    LogUtils.showLog(tv_log, "onUserException: " + exception);
    _359
    ChatClient.getInstance().logout(false, null);
    _359
    }
    _359
    _359
    public void execute(Runnable runnable) {
    _359
    new Thread(runnable).start();
    _359
    }
    _359
    _359
    @Override
    _359
    protected void onDestroy() {
    _359
    super.onDestroy();
    _359
    if(connectionListener != null) {
    _359
    ChatClient.getInstance().removeConnectionListener(connectionListener);
    _359
    }
    _359
    }
    _359
    }

  2. Add LogUtils and PermissionsManager.

    To make troubleshooting less time-consuming, this quickstart also uses LogUtils class for logs. Navigate to app/java/io.agora.agorachatquickstart/, create a folder named utils. In this new folder, create a .java file, name it LogUtils, and copy the following codes into the file.


    _69
    package io.agora.chatuikitquickstart.utils;
    _69
    _69
    import android.app.Activity;
    _69
    import android.text.TextUtils;
    _69
    import android.util.Log;
    _69
    import android.widget.TextView;
    _69
    import android.widget.Toast;
    _69
    _69
    import java.text.SimpleDateFormat;
    _69
    import java.util.Date;
    _69
    import java.util.Locale;
    _69
    _69
    public class LogUtils {
    _69
    private static final String TAG = LogUtils.class.getSimpleName();
    _69
    _69
    public static void showErrorLog(TextView tvLog, String content) {
    _69
    showLog(tvLog, content);
    _69
    }
    _69
    _69
    public static void showNormalLog(TextView tvLog, String content) {
    _69
    showLog(tvLog, content);
    _69
    }
    _69
    _69
    public static void showLog(TextView tvLog, String content) {
    _69
    if(TextUtils.isEmpty(content) || tvLog == null) {
    _69
    return;
    _69
    }
    _69
    String preContent = tvLog.getText().toString().trim();
    _69
    StringBuilder builder = new StringBuilder();
    _69
    builder.append(formatCurrentTime())
    _69
    .append(" ")
    _69
    .append(content)
    _69
    .append("\n")
    _69
    .append(preContent);
    _69
    tvLog.post(()-> {
    _69
    tvLog.setText(builder);
    _69
    });
    _69
    }
    _69
    _69
    public static void showErrorToast(Activity activity, TextView tvLog, String content) {
    _69
    if(activity == null || activity.isFinishing()) {
    _69
    Log.e(TAG, "Context is null...");
    _69
    return;
    _69
    }
    _69
    if(TextUtils.isEmpty(content)) {
    _69
    return;
    _69
    }
    _69
    activity.runOnUiThread(()-> {
    _69
    Toast.makeText(activity, content, Toast.LENGTH_SHORT).show();
    _69
    showErrorLog(tvLog,content);
    _69
    });
    _69
    }
    _69
    _69
    public static void showToast(Activity activity, TextView tvLog, String content) {
    _69
    if(TextUtils.isEmpty(content) || activity == null || activity.isFinishing()) {
    _69
    return;
    _69
    }
    _69
    activity.runOnUiThread(()-> {
    _69
    Toast.makeText(activity, content, Toast.LENGTH_SHORT).show();
    _69
    showNormalLog(tvLog, content);
    _69
    });
    _69
    }
    _69
    _69
    private static String formatCurrentTime() {
    _69
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
    _69
    return sdf.format(new Date());
    _69
    }
    _69
    _69
    }

    When your app launches, check if the permissions necessary to insert real-time chat into the app are granted. In the utils file, create a .java file, name it PermissionsManager, and copy the following codes into the file.


    _44
    package io.agora.chatuikitquickstart.utils;
    _44
    _44
    import android.app.Activity;
    _44
    import android.content.Context;
    _44
    import android.content.pm.PackageManager;
    _44
    _44
    import androidx.annotation.NonNull;
    _44
    import androidx.annotation.Nullable;
    _44
    import androidx.core.app.ActivityCompat;
    _44
    _44
    public class PermissionsManager {
    _44
    private static PermissionsManager mInstance = null;
    _44
    _44
    public static PermissionsManager getInstance() {
    _44
    if (mInstance == null) {
    _44
    mInstance = new PermissionsManager();
    _44
    }
    _44
    return mInstance;
    _44
    }
    _44
    _44
    private PermissionsManager() {}
    _44
    _44
    /**
    _44
    * Check if has permission
    _44
    * @param context
    _44
    * @param permission
    _44
    * @return
    _44
    */
    _44
    @SuppressWarnings("unused")
    _44
    public synchronized boolean hasPermission(@Nullable Context context, @NonNull String permission) {
    _44
    return context != null && ActivityCompat.checkSelfPermission(context, permission)
    _44
    == PackageManager.PERMISSION_GRANTED;
    _44
    }
    _44
    _44
    /**
    _44
    * Request permissions
    _44
    * @param activity
    _44
    * @param permissions
    _44
    * @param requestCode
    _44
    */
    _44
    public synchronized void requestPermissions(Activity activity, String[] permissions, int requestCode) {
    _44
    ActivityCompat.requestPermissions(activity, permissions, requestCode);
    _44
    }
    _44
    }

  3. To send image and file messages, take the following configurations:

    Under /app/src/main/res/, create a folder, name it xml, and create an xml file named file_paths.xml under xml. Open file_paths.xml, replace the code with the following:


    _5
    <?xml version="1.0" encoding="utf-8"?>
    _5
    <paths>
    _5
    <external-path path="Android/data/io/agora/chatuikitquickstart/" name="files_root" />
    _5
    <external-path path="." name="external_storage_root" />
    _5
    </paths>

    In /app/Manifests/AndroidManifest.xml, add the following lines before </application>:


    _10
    <!-- After android 7.0, you should add -->
    _10
    <provider
    _10
    android:name="androidx.core.content.FileProvider"
    _10
    android:authorities="${applicationId}.fileProvider"
    _10
    android:grantUriPermissions="true"
    _10
    android:exported="false">
    _10
    <meta-data
    _10
    android:name="android.support.FILE_PROVIDER_PATHS"
    _10
    android:resource="@xml/file_paths" />
    _10
    </provider>

  4. Click Sync Project with Gradle Files to sync your project. Now you are ready to test your app.

Test your app

To validate the peer-to-peer messaging you have just integrated into your app using Chat:

  1. In Android Studio, click Run 'app'.

    You see the following interface on your simulator or physical device:

  2. Create a user account and click SIGN UP. Click Sign in and you will see a log that says Sign in success.

  3. Run the app on another Android device or simulator and create another user account. Ensure that the usernames you created are unique.

  4. On the first device or simulator, enter the username you just created and click START CHAT. You can now start chatting between the two clients.

Next steps

For demonstration purposes, Chat provides an app server that enables you to quickly retrieve a token using the App Key given in this guide. In a production context, the best practice is for you to deploy your own token server, use your own App Key to generate a token, and retrieve the token on the client side to log in to Agora. To see how to implement a server that generates and serves tokens on request, see Generate a User Token.

Reference

Agora provides the fully featured AgoraChat-Starter-Kit-Android demo app as an implementation reference.

  • Android

    1. Enable the Developer options on your Android device, and then connect it.
    2. Run npx react-native run-android in the project root directory.
  • iOS:

    1. In Xcode, run your project.

Page Content