What does the Conversations Android Quickstart do? How does it work? How would you add something similar to your own project? We'll cover all of these questions and more in this behind the scenes look at the example application code.
If you haven't had a chance to try out the Conversations Android Quickstart app yet, follow the instructions in the README to get it up and running.
You'll also need to supply an access token with a Chat grant for a Conversations Service in the strings.xml
resource file before running the application. You can generate an access token with the Twilio Command Line Interface (CLI):
_10twilio token:chat --identity <The test username> --chat-service-sid <ISXXX...>
The suggested way to install twilio-cli
on macOS is to use Homebrew. If you don't already have it installed, visit the Homebrew site for installation instructions and then return here.
Once you have installed Homebrew, run the following command to install twilio-cli
:
_10brew tap twilio/brew && brew install twilio
For other installation methods, see the Twilio CLI Quickstart.
The example application code uses Java as the programming language. We also only have one activity class, named MainActivity
. We chose not to use fragments in this quickstart for simplicity, but you can certainly use them with Conversations.
We built a class named QuickstartConversationsManager
to handle the interactions with the Conversations SDK.
Within the quickstart application, you will find examples of the following:
When you build an application that uses Conversations, you may be able to use the QuickstartConversationsManager
and MainActivity
classes as a start for your project. You may also just want to take a look at how the quickstart works, and then build your own solution with the classes in the SDK!
When you build your solutions with Twilio Conversations, you need a Conversations Android SDK for your mobile app. You can install this library using Gradle.
You would typically start by adding the ConversationsClient
from the com.twilio.conversations
package to your project, and then work with Conversation
objects to send and retrieve Message
objects for a given conversation. Other important classes are User
, Participant
, and Media
.
While we cover some of the basics of the Conversations SDK in this Quickstart, you can also find reference Javadocs for each class and interface. We also consider some of these topics in more detail in other pages in our docs, which we will link to in each section that has a corresponding guide.
The Conversations SDK for Android is only one half of the solution. You'll also need to build a server to support your mobile application. Twilio supports six different languages and platforms for you to build with. Java might be the best choice if you are an Android developer looking to try out web application development, but you can use any of these to build your server.
For your chosen language and/or platform, pick the appropriate Twilio Helper Library:
On each of these pages, you will find instructions for setting up the Twilio helper library (also called a "server-side SDK"). We recommend using dependency management for the Twilio libraries, and you'll find directions for the most common build tools for your platform.
If you don't already have a Twilio account, sign up for a Twilio trial account, and then create a new project. You'll also need to create an API Key and API Secret pair to call Twilio's REST API, whether you use one of the Twilio helper libraries, or make the API calls yourself.
Each chat user in your Conversations project needs an identity - this could be their user id, their username, or some kind of other identifier. You could certainly have anonymous users in your Conversations - for instance, a web chat popup with a customer service agent on an e-commerce website - but in that case, you would still want to issue some kind of identifier from your application.
Once you build Twilio Conversations into your project, you should generate an access token with a ChatGrant
for end users, along with the identity value.
With the Conversations Android Quickstart, the easiest way to get started is to create an access token from the Twilio Command Line Interface (CLI).
As part of this project, you will see that there are three different ways of providing credentials for Twilio - access tokens, auth tokens, and API keys. What is the difference between all of these different styles?
Access tokens provide short-lived credentials for a single end user to work with your Twilio service from a Javascript application running in a web browser, or from a native iOS or Android mobile application. Use the Twilio helper libraries in your back end web services to create access tokens for your front end applications to consume. Alternatively, use the Twilio CLI to create access tokens for testing. These access tokens have a built-in expiration, and need to be refreshed from your server if your users have long-running connections. The Conversations client will update your application when access tokens are about to expire, or if they have expired, so that you can refresh the token.
Although the names are similar, authentication (or auth) tokens are not the same as access tokens, and cannot be used in the same way. The auth token pairs with your Twilio account identifier (also called the account SID) to provide authentication for the Twilio REST API. Your auth token should be treated with the same care that you would use to secure your Twilio password, and should never be included directly in source code, made available to a client application, or checked into a file in source control.
Similar to auth tokens, API key/secret pairs secure access to the Twilio REST API for your account. When you create an API key and secret pair from the Twilio console, the secret will only be shown once, and then it won't be recoverable. In your back end application, you would authenticate to Twilio with a combination of your account identifier (also known as the "Account SID"), an API key, and an API secret.
The advantage of API keys over auth tokens is that it is easy to rotate API keys on your server application, especially if you use one API key and secret pair for each application cluster or instance. This way, you can have multiple credentials under your Twilio account, and if you need to swap out a key pair and then deactivate it, you can do it on an application basis, not on an account basis.
Whether you use auth tokens or API keys, we suggest that you store those credentials securely, and do not check them into source control. There are many different options for managing secure credentials that depend on how and where you run your development, staging, and production environments.
When you develop locally, look into using a .env
file with your project, usually in conjunction with a library named dotenv. For .NET Core, read our article on Setting Twilio Environment Variables in Windows 10 with PowerShell and .NET Core 3.0 to learn a lot more about this topic!
For the Conversations Quickstart, you can simply generate an access token using the Twilio Command Line Interface (CLI), and then paste that into the strings.xml
file. While this works for getting the quickstart up and running, you will want to replace this with your own function that retrieves an access token.
You can use OkHttp, Volley or another HTTP library to make an authenticated HTTP request to your server, where the server code would provide an access token with a ChatGrant
that sets the identity for the user based on your own authentication mechanism (such as an API key, or your own token).
Ideally, this method would be usable for three different scenarios:
The first step is to get an access token. Once you have an access token (a string value), you can initialize a Twilio Conversations Client. This client is the central class in the Conversations SDK, and you need to keep it around after initialization. The client is designed to be long-lived, and it will fire events off that your project can subscribe to.
You'll need to create your own listener for the Conversations Client that implements the ConversationsClientListener
interface. In the quick start, we created a class named QuickstartConversationsManager
to encapsulate our usage of the Conversations SDK.
_412package com.twilio.conversationsquickstart;_412_412import android.content.Context;_412import android.util.Log;_412_412import com.google.gson.Gson;_412import com.twilio.conversations.CallbackListener;_412import com.twilio.conversations.Conversation;_412import com.twilio.conversations.ConversationListener;_412import com.twilio.conversations.ConversationsClient;_412import com.twilio.conversations.ConversationsClientListener;_412import com.twilio.conversations.ErrorInfo;_412import com.twilio.conversations.Participant;_412import com.twilio.conversations.Message;_412import com.twilio.conversations.StatusListener;_412import com.twilio.conversations.User;_412_412import org.jetbrains.annotations.Nullable;_412_412import java.io.IOException;_412import java.util.ArrayList;_412import java.util.List;_412_412import okhttp3.OkHttpClient;_412import okhttp3.Request;_412import okhttp3.Response;_412_412interface QuickstartConversationsManagerListener {_412 void receivedNewMessage();_412 void messageSentCallback();_412 void reloadMessages();_412}_412_412interface TokenResponseListener {_412 void receivedTokenResponse(boolean success, @Nullable Exception exception);_412}_412_412interface AccessTokenListener {_412 void receivedAccessToken(@Nullable String token, @Nullable Exception exception);_412}_412_412_412class QuickstartConversationsManager {_412_412 // This is the unique name of the conversation we are using_412 private final static String DEFAULT_CONVERSATION_NAME = "general";_412_412 final private ArrayList<Message> messages = new ArrayList<>();_412_412 private ConversationsClient conversationsClient;_412_412 private Conversation conversation;_412_412 private QuickstartConversationsManagerListener conversationsManagerListener;_412_412 private String tokenURL = "";_412_412 private class TokenResponse {_412 String token;_412 }_412_412 void retrieveAccessTokenFromServer(final Context context, String identity,_412 final TokenResponseListener listener) {_412_412 // Set the chat token URL in your strings.xml file_412 String chatTokenURL = context.getString(R.string.chat_token_url);_412_412 if ("https://YOUR_DOMAIN_HERE.twil.io/chat-token".equals(chatTokenURL)) {_412 listener.receivedTokenResponse(false, new Exception("You need to replace the chat token URL in strings.xml"));_412 return;_412 }_412_412 tokenURL = chatTokenURL + "?identity=" + identity;_412_412 new Thread(new Runnable() {_412 @Override_412 public void run() {_412 retrieveToken(new AccessTokenListener() {_412 @Override_412 public void receivedAccessToken(@Nullable String token,_412 @Nullable Exception exception) {_412 if (token != null) {_412 ConversationsClient.Properties props = ConversationsClient.Properties.newBuilder().createProperties();_412 ConversationsClient.create(context, token, props, mConversationsClientCallback);_412 listener.receivedTokenResponse(true,null);_412 } else {_412 listener.receivedTokenResponse(false, exception);_412 }_412 }_412 });_412 }_412 }).start();_412 }_412_412 void initializeWithAccessToken(final Context context, final String token) {_412_412 ConversationsClient.Properties props = ConversationsClient.Properties.newBuilder().createProperties();_412 ConversationsClient.create(context, token, props, mConversationsClientCallback);_412 }_412_412 private void retrieveToken(AccessTokenListener listener) {_412 OkHttpClient client = new OkHttpClient();_412_412 Request request = new Request.Builder()_412 .url(tokenURL)_412 .build();_412 try (Response response = client.newCall(request).execute()) {_412 String responseBody = "";_412 if (response != null && response.body() != null) {_412 responseBody = response.body().string();_412 }_412 Log.d(MainActivity.TAG, "Response from server: " + responseBody);_412 Gson gson = new Gson();_412 TokenResponse tokenResponse = gson.fromJson(responseBody,TokenResponse.class);_412 String accessToken = tokenResponse.token;_412 Log.d(MainActivity.TAG, "Retrieved access token from server: " + accessToken);_412 listener.receivedAccessToken(accessToken, null);_412_412 }_412 catch (IOException ex) {_412 Log.e(MainActivity.TAG, ex.getLocalizedMessage(),ex);_412 listener.receivedAccessToken(null, ex);_412 }_412 }_412_412 void sendMessage(String messageBody) {_412 if (conversation != null) {_412 Message.Options options = Message.options().withBody(messageBody);_412 Log.d(MainActivity.TAG,"Message created");_412 conversation.sendMessage(options, new CallbackListener<Message>() {_412 @Override_412 public void onSuccess(Message message) {_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.messageSentCallback();_412 }_412 }_412 });_412 }_412 }_412_412_412 private void loadChannels() {_412 if (conversationsClient == null || conversationsClient.getMyConversations() == null) {_412 return;_412 }_412 conversationsClient.getConversation(DEFAULT_CONVERSATION_NAME, new CallbackListener<Conversation>() {_412 @Override_412 public void onSuccess(Conversation conversation) {_412 if (conversation != null) {_412 if (conversation.getStatus() == Conversation.ConversationStatus.JOINED_412 || conversation.getStatus() == Conversation.ConversationStatus.NOT_PARTICIPATING) {_412 Log.d(MainActivity.TAG, "Already Exists in Conversation: " + DEFAULT_CONVERSATION_NAME);_412 QuickstartConversationsManager.this.conversation = conversation;_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 QuickstartConversationsManager.this.loadPreviousMessages(conversation);_412 } else {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + DEFAULT_CONVERSATION_NAME);_412 joinConversation(conversation);_412 }_412 }_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error retrieving conversation: " + errorInfo.getMessage());_412 createConversation();_412 }_412_412 });_412 }_412_412 private void createConversation() {_412 Log.d(MainActivity.TAG, "Creating Conversation: " + DEFAULT_CONVERSATION_NAME);_412_412 conversationsClient.createConversation(DEFAULT_CONVERSATION_NAME,_412 new CallbackListener<Conversation>() {_412 @Override_412 public void onSuccess(Conversation conversation) {_412 if (conversation != null) {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + DEFAULT_CONVERSATION_NAME);_412 joinConversation(conversation);_412 }_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error creating conversation: " + errorInfo.getMessage());_412 }_412 });_412 }_412_412_412 private void joinConversation(final Conversation conversation) {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + conversation.getUniqueName());_412 if (conversation.getStatus() == Conversation.ConversationStatus.JOINED) {_412_412 QuickstartConversationsManager.this.conversation = conversation;_412 Log.d(MainActivity.TAG, "Already joined default conversation");_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 return;_412 }_412_412_412 conversation.join(new StatusListener() {_412 @Override_412 public void onSuccess() {_412 QuickstartConversationsManager.this.conversation = conversation;_412 Log.d(MainActivity.TAG, "Joined default conversation");_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 QuickstartConversationsManager.this.loadPreviousMessages(conversation);_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error joining conversation: " + errorInfo.getMessage());_412 }_412 });_412 }_412_412 private void loadPreviousMessages(final Conversation conversation) {_412 conversation.getLastMessages(100,_412 new CallbackListener<List<Message>>() {_412 @Override_412 public void onSuccess(List<Message> result) {_412 messages.addAll(result);_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.reloadMessages();_412 }_412 }_412 });_412 }_412_412 private final ConversationsClientListener mConversationsClientListener =_412 new ConversationsClientListener() {_412_412 @Override_412 public void onConversationAdded(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onConversationUpdated(Conversation conversation, Conversation.UpdateReason updateReason) {_412_412 }_412_412 @Override_412 public void onConversationDeleted(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onConversationSynchronizationChange(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412_412 }_412_412 @Override_412 public void onUserUpdated(User user, User.UpdateReason updateReason) {_412_412 }_412_412 @Override_412 public void onUserSubscribed(User user) {_412_412 }_412_412 @Override_412 public void onUserUnsubscribed(User user) {_412_412 }_412_412 @Override_412 public void onClientSynchronization(ConversationsClient.SynchronizationStatus synchronizationStatus) {_412 if (synchronizationStatus == ConversationsClient.SynchronizationStatus.COMPLETED) {_412 loadChannels();_412 }_412 }_412_412 @Override_412 public void onNewMessageNotification(String s, String s1, long l) {_412_412 }_412_412 @Override_412 public void onAddedToConversationNotification(String s) {_412_412 }_412_412 @Override_412 public void onRemovedFromConversationNotification(String s) {_412_412 }_412_412 @Override_412 public void onNotificationSubscribed() {_412_412 }_412_412 @Override_412 public void onNotificationFailed(ErrorInfo errorInfo) {_412_412 }_412_412 @Override_412 public void onConnectionStateChange(ConversationsClient.ConnectionState connectionState) {_412_412 }_412_412 @Override_412 public void onTokenExpired() {_412_412 }_412_412 @Override_412 public void onTokenAboutToExpire() {_412 retrieveToken(new AccessTokenListener() {_412 @Override_412 public void receivedAccessToken(@Nullable String token, @Nullable Exception exception) {_412 if (token != null) {_412 conversationsClient.updateToken(token, new StatusListener() {_412 @Override_412 public void onSuccess() {_412 Log.d(MainActivity.TAG, "Refreshed access token.");_412 }_412 });_412 }_412 }_412 });_412 }_412 };_412_412 private final CallbackListener<ConversationsClient> mConversationsClientCallback =_412 new CallbackListener<ConversationsClient>() {_412 @Override_412 public void onSuccess(ConversationsClient conversationsClient) {_412 QuickstartConversationsManager.this.conversationsClient = conversationsClient;_412 conversationsClient.addListener(QuickstartConversationsManager.this.mConversationsClientListener);_412 Log.d(MainActivity.TAG, "Success creating Twilio Conversations Client");_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error creating Twilio Conversations Client: " + errorInfo.getMessage());_412 }_412 };_412_412_412 private final ConversationListener mDefaultConversationListener = new ConversationListener() {_412_412_412 @Override_412 public void onMessageAdded(final Message message) {_412 Log.d(MainActivity.TAG, "Message added");_412 messages.add(message);_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.receivedNewMessage();_412 }_412 }_412_412 @Override_412 public void onMessageUpdated(Message message, Message.UpdateReason updateReason) {_412 Log.d(MainActivity.TAG, "Message updated: " + message.getMessageBody());_412 }_412_412 @Override_412 public void onMessageDeleted(Message message) {_412 Log.d(MainActivity.TAG, "Message deleted");_412 }_412_412 @Override_412 public void onParticipantAdded(Participant participant) {_412 Log.d(MainActivity.TAG, "Participant added: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onParticipantUpdated(Participant participant, Participant.UpdateReason updateReason) {_412 Log.d(MainActivity.TAG, "Participant updated: " + participant.getIdentity() + " " + updateReason.toString());_412 }_412_412 @Override_412 public void onParticipantDeleted(Participant participant) {_412 Log.d(MainActivity.TAG, "Participant deleted: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onTypingStarted(Conversation conversation, Participant participant) {_412 Log.d(MainActivity.TAG, "Started Typing: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onTypingEnded(Conversation conversation, Participant participant) {_412 Log.d(MainActivity.TAG, "Ended Typing: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onSynchronizationChanged(Conversation conversation) {_412_412 }_412 };_412_412 public ArrayList<Message> getMessages() {_412 return messages;_412 }_412_412 public void setListener(QuickstartConversationsManagerListener listener) {_412 this.conversationsManagerListener = listener;_412 }_412}
After you initialize the Conversations client, the client needs to synchronize with the server. The onConversationSynchronizationChange
method on each listener gets called when the synchronization status changes - the completed status is COMPLETED
, which means that the Conversations, Participants and Messages collections are ready to use.
_412package com.twilio.conversationsquickstart;_412_412import android.content.Context;_412import android.util.Log;_412_412import com.google.gson.Gson;_412import com.twilio.conversations.CallbackListener;_412import com.twilio.conversations.Conversation;_412import com.twilio.conversations.ConversationListener;_412import com.twilio.conversations.ConversationsClient;_412import com.twilio.conversations.ConversationsClientListener;_412import com.twilio.conversations.ErrorInfo;_412import com.twilio.conversations.Participant;_412import com.twilio.conversations.Message;_412import com.twilio.conversations.StatusListener;_412import com.twilio.conversations.User;_412_412import org.jetbrains.annotations.Nullable;_412_412import java.io.IOException;_412import java.util.ArrayList;_412import java.util.List;_412_412import okhttp3.OkHttpClient;_412import okhttp3.Request;_412import okhttp3.Response;_412_412interface QuickstartConversationsManagerListener {_412 void receivedNewMessage();_412 void messageSentCallback();_412 void reloadMessages();_412}_412_412interface TokenResponseListener {_412 void receivedTokenResponse(boolean success, @Nullable Exception exception);_412}_412_412interface AccessTokenListener {_412 void receivedAccessToken(@Nullable String token, @Nullable Exception exception);_412}_412_412_412class QuickstartConversationsManager {_412_412 // This is the unique name of the conversation we are using_412 private final static String DEFAULT_CONVERSATION_NAME = "general";_412_412 final private ArrayList<Message> messages = new ArrayList<>();_412_412 private ConversationsClient conversationsClient;_412_412 private Conversation conversation;_412_412 private QuickstartConversationsManagerListener conversationsManagerListener;_412_412 private String tokenURL = "";_412_412 private class TokenResponse {_412 String token;_412 }_412_412 void retrieveAccessTokenFromServer(final Context context, String identity,_412 final TokenResponseListener listener) {_412_412 // Set the chat token URL in your strings.xml file_412 String chatTokenURL = context.getString(R.string.chat_token_url);_412_412 if ("https://YOUR_DOMAIN_HERE.twil.io/chat-token".equals(chatTokenURL)) {_412 listener.receivedTokenResponse(false, new Exception("You need to replace the chat token URL in strings.xml"));_412 return;_412 }_412_412 tokenURL = chatTokenURL + "?identity=" + identity;_412_412 new Thread(new Runnable() {_412 @Override_412 public void run() {_412 retrieveToken(new AccessTokenListener() {_412 @Override_412 public void receivedAccessToken(@Nullable String token,_412 @Nullable Exception exception) {_412 if (token != null) {_412 ConversationsClient.Properties props = ConversationsClient.Properties.newBuilder().createProperties();_412 ConversationsClient.create(context, token, props, mConversationsClientCallback);_412 listener.receivedTokenResponse(true,null);_412 } else {_412 listener.receivedTokenResponse(false, exception);_412 }_412 }_412 });_412 }_412 }).start();_412 }_412_412 void initializeWithAccessToken(final Context context, final String token) {_412_412 ConversationsClient.Properties props = ConversationsClient.Properties.newBuilder().createProperties();_412 ConversationsClient.create(context, token, props, mConversationsClientCallback);_412 }_412_412 private void retrieveToken(AccessTokenListener listener) {_412 OkHttpClient client = new OkHttpClient();_412_412 Request request = new Request.Builder()_412 .url(tokenURL)_412 .build();_412 try (Response response = client.newCall(request).execute()) {_412 String responseBody = "";_412 if (response != null && response.body() != null) {_412 responseBody = response.body().string();_412 }_412 Log.d(MainActivity.TAG, "Response from server: " + responseBody);_412 Gson gson = new Gson();_412 TokenResponse tokenResponse = gson.fromJson(responseBody,TokenResponse.class);_412 String accessToken = tokenResponse.token;_412 Log.d(MainActivity.TAG, "Retrieved access token from server: " + accessToken);_412 listener.receivedAccessToken(accessToken, null);_412_412 }_412 catch (IOException ex) {_412 Log.e(MainActivity.TAG, ex.getLocalizedMessage(),ex);_412 listener.receivedAccessToken(null, ex);_412 }_412 }_412_412 void sendMessage(String messageBody) {_412 if (conversation != null) {_412 Message.Options options = Message.options().withBody(messageBody);_412 Log.d(MainActivity.TAG,"Message created");_412 conversation.sendMessage(options, new CallbackListener<Message>() {_412 @Override_412 public void onSuccess(Message message) {_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.messageSentCallback();_412 }_412 }_412 });_412 }_412 }_412_412_412 private void loadChannels() {_412 if (conversationsClient == null || conversationsClient.getMyConversations() == null) {_412 return;_412 }_412 conversationsClient.getConversation(DEFAULT_CONVERSATION_NAME, new CallbackListener<Conversation>() {_412 @Override_412 public void onSuccess(Conversation conversation) {_412 if (conversation != null) {_412 if (conversation.getStatus() == Conversation.ConversationStatus.JOINED_412 || conversation.getStatus() == Conversation.ConversationStatus.NOT_PARTICIPATING) {_412 Log.d(MainActivity.TAG, "Already Exists in Conversation: " + DEFAULT_CONVERSATION_NAME);_412 QuickstartConversationsManager.this.conversation = conversation;_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 QuickstartConversationsManager.this.loadPreviousMessages(conversation);_412 } else {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + DEFAULT_CONVERSATION_NAME);_412 joinConversation(conversation);_412 }_412 }_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error retrieving conversation: " + errorInfo.getMessage());_412 createConversation();_412 }_412_412 });_412 }_412_412 private void createConversation() {_412 Log.d(MainActivity.TAG, "Creating Conversation: " + DEFAULT_CONVERSATION_NAME);_412_412 conversationsClient.createConversation(DEFAULT_CONVERSATION_NAME,_412 new CallbackListener<Conversation>() {_412 @Override_412 public void onSuccess(Conversation conversation) {_412 if (conversation != null) {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + DEFAULT_CONVERSATION_NAME);_412 joinConversation(conversation);_412 }_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error creating conversation: " + errorInfo.getMessage());_412 }_412 });_412 }_412_412_412 private void joinConversation(final Conversation conversation) {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + conversation.getUniqueName());_412 if (conversation.getStatus() == Conversation.ConversationStatus.JOINED) {_412_412 QuickstartConversationsManager.this.conversation = conversation;_412 Log.d(MainActivity.TAG, "Already joined default conversation");_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 return;_412 }_412_412_412 conversation.join(new StatusListener() {_412 @Override_412 public void onSuccess() {_412 QuickstartConversationsManager.this.conversation = conversation;_412 Log.d(MainActivity.TAG, "Joined default conversation");_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 QuickstartConversationsManager.this.loadPreviousMessages(conversation);_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error joining conversation: " + errorInfo.getMessage());_412 }_412 });_412 }_412_412 private void loadPreviousMessages(final Conversation conversation) {_412 conversation.getLastMessages(100,_412 new CallbackListener<List<Message>>() {_412 @Override_412 public void onSuccess(List<Message> result) {_412 messages.addAll(result);_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.reloadMessages();_412 }_412 }_412 });_412 }_412_412 private final ConversationsClientListener mConversationsClientListener =_412 new ConversationsClientListener() {_412_412 @Override_412 public void onConversationAdded(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onConversationUpdated(Conversation conversation, Conversation.UpdateReason updateReason) {_412_412 }_412_412 @Override_412 public void onConversationDeleted(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onConversationSynchronizationChange(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412_412 }_412_412 @Override_412 public void onUserUpdated(User user, User.UpdateReason updateReason) {_412_412 }_412_412 @Override_412 public void onUserSubscribed(User user) {_412_412 }_412_412 @Override_412 public void onUserUnsubscribed(User user) {_412_412 }_412_412 @Override_412 public void onClientSynchronization(ConversationsClient.SynchronizationStatus synchronizationStatus) {_412 if (synchronizationStatus == ConversationsClient.SynchronizationStatus.COMPLETED) {_412 loadChannels();_412 }_412 }_412_412 @Override_412 public void onNewMessageNotification(String s, String s1, long l) {_412_412 }_412_412 @Override_412 public void onAddedToConversationNotification(String s) {_412_412 }_412_412 @Override_412 public void onRemovedFromConversationNotification(String s) {_412_412 }_412_412 @Override_412 public void onNotificationSubscribed() {_412_412 }_412_412 @Override_412 public void onNotificationFailed(ErrorInfo errorInfo) {_412_412 }_412_412 @Override_412 public void onConnectionStateChange(ConversationsClient.ConnectionState connectionState) {_412_412 }_412_412 @Override_412 public void onTokenExpired() {_412_412 }_412_412 @Override_412 public void onTokenAboutToExpire() {_412 retrieveToken(new AccessTokenListener() {_412 @Override_412 public void receivedAccessToken(@Nullable String token, @Nullable Exception exception) {_412 if (token != null) {_412 conversationsClient.updateToken(token, new StatusListener() {_412 @Override_412 public void onSuccess() {_412 Log.d(MainActivity.TAG, "Refreshed access token.");_412 }_412 });_412 }_412 }_412 });_412 }_412 };_412_412 private final CallbackListener<ConversationsClient> mConversationsClientCallback =_412 new CallbackListener<ConversationsClient>() {_412 @Override_412 public void onSuccess(ConversationsClient conversationsClient) {_412 QuickstartConversationsManager.this.conversationsClient = conversationsClient;_412 conversationsClient.addListener(QuickstartConversationsManager.this.mConversationsClientListener);_412 Log.d(MainActivity.TAG, "Success creating Twilio Conversations Client");_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error creating Twilio Conversations Client: " + errorInfo.getMessage());_412 }_412 };_412_412_412 private final ConversationListener mDefaultConversationListener = new ConversationListener() {_412_412_412 @Override_412 public void onMessageAdded(final Message message) {_412 Log.d(MainActivity.TAG, "Message added");_412 messages.add(message);_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.receivedNewMessage();_412 }_412 }_412_412 @Override_412 public void onMessageUpdated(Message message, Message.UpdateReason updateReason) {_412 Log.d(MainActivity.TAG, "Message updated: " + message.getMessageBody());_412 }_412_412 @Override_412 public void onMessageDeleted(Message message) {_412 Log.d(MainActivity.TAG, "Message deleted");_412 }_412_412 @Override_412 public void onParticipantAdded(Participant participant) {_412 Log.d(MainActivity.TAG, "Participant added: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onParticipantUpdated(Participant participant, Participant.UpdateReason updateReason) {_412 Log.d(MainActivity.TAG, "Participant updated: " + participant.getIdentity() + " " + updateReason.toString());_412 }_412_412 @Override_412 public void onParticipantDeleted(Participant participant) {_412 Log.d(MainActivity.TAG, "Participant deleted: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onTypingStarted(Conversation conversation, Participant participant) {_412 Log.d(MainActivity.TAG, "Started Typing: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onTypingEnded(Conversation conversation, Participant participant) {_412 Log.d(MainActivity.TAG, "Ended Typing: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onSynchronizationChanged(Conversation conversation) {_412_412 }_412 };_412_412 public ArrayList<Message> getMessages() {_412 return messages;_412 }_412_412 public void setListener(QuickstartConversationsManagerListener listener) {_412 this.conversationsManagerListener = listener;_412 }_412}
The Conversation
class is the building block of your Conversations application. In the Quickstart, we've set things up so that the user automatically joins one conversation. For instance, this conversation's unique id could be supplied by a back end service to represent a three way conversation between a restaurant, a customer, and a delivery driver.
Your user may have already joined the conversation, so you should check to see if they have before calling the join()
method on the Conversation
object.
_412package com.twilio.conversationsquickstart;_412_412import android.content.Context;_412import android.util.Log;_412_412import com.google.gson.Gson;_412import com.twilio.conversations.CallbackListener;_412import com.twilio.conversations.Conversation;_412import com.twilio.conversations.ConversationListener;_412import com.twilio.conversations.ConversationsClient;_412import com.twilio.conversations.ConversationsClientListener;_412import com.twilio.conversations.ErrorInfo;_412import com.twilio.conversations.Participant;_412import com.twilio.conversations.Message;_412import com.twilio.conversations.StatusListener;_412import com.twilio.conversations.User;_412_412import org.jetbrains.annotations.Nullable;_412_412import java.io.IOException;_412import java.util.ArrayList;_412import java.util.List;_412_412import okhttp3.OkHttpClient;_412import okhttp3.Request;_412import okhttp3.Response;_412_412interface QuickstartConversationsManagerListener {_412 void receivedNewMessage();_412 void messageSentCallback();_412 void reloadMessages();_412}_412_412interface TokenResponseListener {_412 void receivedTokenResponse(boolean success, @Nullable Exception exception);_412}_412_412interface AccessTokenListener {_412 void receivedAccessToken(@Nullable String token, @Nullable Exception exception);_412}_412_412_412class QuickstartConversationsManager {_412_412 // This is the unique name of the conversation we are using_412 private final static String DEFAULT_CONVERSATION_NAME = "general";_412_412 final private ArrayList<Message> messages = new ArrayList<>();_412_412 private ConversationsClient conversationsClient;_412_412 private Conversation conversation;_412_412 private QuickstartConversationsManagerListener conversationsManagerListener;_412_412 private String tokenURL = "";_412_412 private class TokenResponse {_412 String token;_412 }_412_412 void retrieveAccessTokenFromServer(final Context context, String identity,_412 final TokenResponseListener listener) {_412_412 // Set the chat token URL in your strings.xml file_412 String chatTokenURL = context.getString(R.string.chat_token_url);_412_412 if ("https://YOUR_DOMAIN_HERE.twil.io/chat-token".equals(chatTokenURL)) {_412 listener.receivedTokenResponse(false, new Exception("You need to replace the chat token URL in strings.xml"));_412 return;_412 }_412_412 tokenURL = chatTokenURL + "?identity=" + identity;_412_412 new Thread(new Runnable() {_412 @Override_412 public void run() {_412 retrieveToken(new AccessTokenListener() {_412 @Override_412 public void receivedAccessToken(@Nullable String token,_412 @Nullable Exception exception) {_412 if (token != null) {_412 ConversationsClient.Properties props = ConversationsClient.Properties.newBuilder().createProperties();_412 ConversationsClient.create(context, token, props, mConversationsClientCallback);_412 listener.receivedTokenResponse(true,null);_412 } else {_412 listener.receivedTokenResponse(false, exception);_412 }_412 }_412 });_412 }_412 }).start();_412 }_412_412 void initializeWithAccessToken(final Context context, final String token) {_412_412 ConversationsClient.Properties props = ConversationsClient.Properties.newBuilder().createProperties();_412 ConversationsClient.create(context, token, props, mConversationsClientCallback);_412 }_412_412 private void retrieveToken(AccessTokenListener listener) {_412 OkHttpClient client = new OkHttpClient();_412_412 Request request = new Request.Builder()_412 .url(tokenURL)_412 .build();_412 try (Response response = client.newCall(request).execute()) {_412 String responseBody = "";_412 if (response != null && response.body() != null) {_412 responseBody = response.body().string();_412 }_412 Log.d(MainActivity.TAG, "Response from server: " + responseBody);_412 Gson gson = new Gson();_412 TokenResponse tokenResponse = gson.fromJson(responseBody,TokenResponse.class);_412 String accessToken = tokenResponse.token;_412 Log.d(MainActivity.TAG, "Retrieved access token from server: " + accessToken);_412 listener.receivedAccessToken(accessToken, null);_412_412 }_412 catch (IOException ex) {_412 Log.e(MainActivity.TAG, ex.getLocalizedMessage(),ex);_412 listener.receivedAccessToken(null, ex);_412 }_412 }_412_412 void sendMessage(String messageBody) {_412 if (conversation != null) {_412 Message.Options options = Message.options().withBody(messageBody);_412 Log.d(MainActivity.TAG,"Message created");_412 conversation.sendMessage(options, new CallbackListener<Message>() {_412 @Override_412 public void onSuccess(Message message) {_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.messageSentCallback();_412 }_412 }_412 });_412 }_412 }_412_412_412 private void loadChannels() {_412 if (conversationsClient == null || conversationsClient.getMyConversations() == null) {_412 return;_412 }_412 conversationsClient.getConversation(DEFAULT_CONVERSATION_NAME, new CallbackListener<Conversation>() {_412 @Override_412 public void onSuccess(Conversation conversation) {_412 if (conversation != null) {_412 if (conversation.getStatus() == Conversation.ConversationStatus.JOINED_412 || conversation.getStatus() == Conversation.ConversationStatus.NOT_PARTICIPATING) {_412 Log.d(MainActivity.TAG, "Already Exists in Conversation: " + DEFAULT_CONVERSATION_NAME);_412 QuickstartConversationsManager.this.conversation = conversation;_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 QuickstartConversationsManager.this.loadPreviousMessages(conversation);_412 } else {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + DEFAULT_CONVERSATION_NAME);_412 joinConversation(conversation);_412 }_412 }_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error retrieving conversation: " + errorInfo.getMessage());_412 createConversation();_412 }_412_412 });_412 }_412_412 private void createConversation() {_412 Log.d(MainActivity.TAG, "Creating Conversation: " + DEFAULT_CONVERSATION_NAME);_412_412 conversationsClient.createConversation(DEFAULT_CONVERSATION_NAME,_412 new CallbackListener<Conversation>() {_412 @Override_412 public void onSuccess(Conversation conversation) {_412 if (conversation != null) {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + DEFAULT_CONVERSATION_NAME);_412 joinConversation(conversation);_412 }_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error creating conversation: " + errorInfo.getMessage());_412 }_412 });_412 }_412_412_412 private void joinConversation(final Conversation conversation) {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + conversation.getUniqueName());_412 if (conversation.getStatus() == Conversation.ConversationStatus.JOINED) {_412_412 QuickstartConversationsManager.this.conversation = conversation;_412 Log.d(MainActivity.TAG, "Already joined default conversation");_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 return;_412 }_412_412_412 conversation.join(new StatusListener() {_412 @Override_412 public void onSuccess() {_412 QuickstartConversationsManager.this.conversation = conversation;_412 Log.d(MainActivity.TAG, "Joined default conversation");_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 QuickstartConversationsManager.this.loadPreviousMessages(conversation);_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error joining conversation: " + errorInfo.getMessage());_412 }_412 });_412 }_412_412 private void loadPreviousMessages(final Conversation conversation) {_412 conversation.getLastMessages(100,_412 new CallbackListener<List<Message>>() {_412 @Override_412 public void onSuccess(List<Message> result) {_412 messages.addAll(result);_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.reloadMessages();_412 }_412 }_412 });_412 }_412_412 private final ConversationsClientListener mConversationsClientListener =_412 new ConversationsClientListener() {_412_412 @Override_412 public void onConversationAdded(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onConversationUpdated(Conversation conversation, Conversation.UpdateReason updateReason) {_412_412 }_412_412 @Override_412 public void onConversationDeleted(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onConversationSynchronizationChange(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412_412 }_412_412 @Override_412 public void onUserUpdated(User user, User.UpdateReason updateReason) {_412_412 }_412_412 @Override_412 public void onUserSubscribed(User user) {_412_412 }_412_412 @Override_412 public void onUserUnsubscribed(User user) {_412_412 }_412_412 @Override_412 public void onClientSynchronization(ConversationsClient.SynchronizationStatus synchronizationStatus) {_412 if (synchronizationStatus == ConversationsClient.SynchronizationStatus.COMPLETED) {_412 loadChannels();_412 }_412 }_412_412 @Override_412 public void onNewMessageNotification(String s, String s1, long l) {_412_412 }_412_412 @Override_412 public void onAddedToConversationNotification(String s) {_412_412 }_412_412 @Override_412 public void onRemovedFromConversationNotification(String s) {_412_412 }_412_412 @Override_412 public void onNotificationSubscribed() {_412_412 }_412_412 @Override_412 public void onNotificationFailed(ErrorInfo errorInfo) {_412_412 }_412_412 @Override_412 public void onConnectionStateChange(ConversationsClient.ConnectionState connectionState) {_412_412 }_412_412 @Override_412 public void onTokenExpired() {_412_412 }_412_412 @Override_412 public void onTokenAboutToExpire() {_412 retrieveToken(new AccessTokenListener() {_412 @Override_412 public void receivedAccessToken(@Nullable String token, @Nullable Exception exception) {_412 if (token != null) {_412 conversationsClient.updateToken(token, new StatusListener() {_412 @Override_412 public void onSuccess() {_412 Log.d(MainActivity.TAG, "Refreshed access token.");_412 }_412 });_412 }_412 }_412 });_412 }_412 };_412_412 private final CallbackListener<ConversationsClient> mConversationsClientCallback =_412 new CallbackListener<ConversationsClient>() {_412 @Override_412 public void onSuccess(ConversationsClient conversationsClient) {_412 QuickstartConversationsManager.this.conversationsClient = conversationsClient;_412 conversationsClient.addListener(QuickstartConversationsManager.this.mConversationsClientListener);_412 Log.d(MainActivity.TAG, "Success creating Twilio Conversations Client");_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error creating Twilio Conversations Client: " + errorInfo.getMessage());_412 }_412 };_412_412_412 private final ConversationListener mDefaultConversationListener = new ConversationListener() {_412_412_412 @Override_412 public void onMessageAdded(final Message message) {_412 Log.d(MainActivity.TAG, "Message added");_412 messages.add(message);_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.receivedNewMessage();_412 }_412 }_412_412 @Override_412 public void onMessageUpdated(Message message, Message.UpdateReason updateReason) {_412 Log.d(MainActivity.TAG, "Message updated: " + message.getMessageBody());_412 }_412_412 @Override_412 public void onMessageDeleted(Message message) {_412 Log.d(MainActivity.TAG, "Message deleted");_412 }_412_412 @Override_412 public void onParticipantAdded(Participant participant) {_412 Log.d(MainActivity.TAG, "Participant added: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onParticipantUpdated(Participant participant, Participant.UpdateReason updateReason) {_412 Log.d(MainActivity.TAG, "Participant updated: " + participant.getIdentity() + " " + updateReason.toString());_412 }_412_412 @Override_412 public void onParticipantDeleted(Participant participant) {_412 Log.d(MainActivity.TAG, "Participant deleted: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onTypingStarted(Conversation conversation, Participant participant) {_412 Log.d(MainActivity.TAG, "Started Typing: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onTypingEnded(Conversation conversation, Participant participant) {_412 Log.d(MainActivity.TAG, "Ended Typing: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onSynchronizationChanged(Conversation conversation) {_412_412 }_412 };_412_412 public ArrayList<Message> getMessages() {_412 return messages;_412 }_412_412 public void setListener(QuickstartConversationsManagerListener listener) {_412 this.conversationsManagerListener = listener;_412 }_412}
To send a message (with text content) to a conversation that a user has joined, you need to call the sendMessage()
method on a Conversation
object. To create a message, you can build one up with the Message.Options
class.
_412package com.twilio.conversationsquickstart;_412_412import android.content.Context;_412import android.util.Log;_412_412import com.google.gson.Gson;_412import com.twilio.conversations.CallbackListener;_412import com.twilio.conversations.Conversation;_412import com.twilio.conversations.ConversationListener;_412import com.twilio.conversations.ConversationsClient;_412import com.twilio.conversations.ConversationsClientListener;_412import com.twilio.conversations.ErrorInfo;_412import com.twilio.conversations.Participant;_412import com.twilio.conversations.Message;_412import com.twilio.conversations.StatusListener;_412import com.twilio.conversations.User;_412_412import org.jetbrains.annotations.Nullable;_412_412import java.io.IOException;_412import java.util.ArrayList;_412import java.util.List;_412_412import okhttp3.OkHttpClient;_412import okhttp3.Request;_412import okhttp3.Response;_412_412interface QuickstartConversationsManagerListener {_412 void receivedNewMessage();_412 void messageSentCallback();_412 void reloadMessages();_412}_412_412interface TokenResponseListener {_412 void receivedTokenResponse(boolean success, @Nullable Exception exception);_412}_412_412interface AccessTokenListener {_412 void receivedAccessToken(@Nullable String token, @Nullable Exception exception);_412}_412_412_412class QuickstartConversationsManager {_412_412 // This is the unique name of the conversation we are using_412 private final static String DEFAULT_CONVERSATION_NAME = "general";_412_412 final private ArrayList<Message> messages = new ArrayList<>();_412_412 private ConversationsClient conversationsClient;_412_412 private Conversation conversation;_412_412 private QuickstartConversationsManagerListener conversationsManagerListener;_412_412 private String tokenURL = "";_412_412 private class TokenResponse {_412 String token;_412 }_412_412 void retrieveAccessTokenFromServer(final Context context, String identity,_412 final TokenResponseListener listener) {_412_412 // Set the chat token URL in your strings.xml file_412 String chatTokenURL = context.getString(R.string.chat_token_url);_412_412 if ("https://YOUR_DOMAIN_HERE.twil.io/chat-token".equals(chatTokenURL)) {_412 listener.receivedTokenResponse(false, new Exception("You need to replace the chat token URL in strings.xml"));_412 return;_412 }_412_412 tokenURL = chatTokenURL + "?identity=" + identity;_412_412 new Thread(new Runnable() {_412 @Override_412 public void run() {_412 retrieveToken(new AccessTokenListener() {_412 @Override_412 public void receivedAccessToken(@Nullable String token,_412 @Nullable Exception exception) {_412 if (token != null) {_412 ConversationsClient.Properties props = ConversationsClient.Properties.newBuilder().createProperties();_412 ConversationsClient.create(context, token, props, mConversationsClientCallback);_412 listener.receivedTokenResponse(true,null);_412 } else {_412 listener.receivedTokenResponse(false, exception);_412 }_412 }_412 });_412 }_412 }).start();_412 }_412_412 void initializeWithAccessToken(final Context context, final String token) {_412_412 ConversationsClient.Properties props = ConversationsClient.Properties.newBuilder().createProperties();_412 ConversationsClient.create(context, token, props, mConversationsClientCallback);_412 }_412_412 private void retrieveToken(AccessTokenListener listener) {_412 OkHttpClient client = new OkHttpClient();_412_412 Request request = new Request.Builder()_412 .url(tokenURL)_412 .build();_412 try (Response response = client.newCall(request).execute()) {_412 String responseBody = "";_412 if (response != null && response.body() != null) {_412 responseBody = response.body().string();_412 }_412 Log.d(MainActivity.TAG, "Response from server: " + responseBody);_412 Gson gson = new Gson();_412 TokenResponse tokenResponse = gson.fromJson(responseBody,TokenResponse.class);_412 String accessToken = tokenResponse.token;_412 Log.d(MainActivity.TAG, "Retrieved access token from server: " + accessToken);_412 listener.receivedAccessToken(accessToken, null);_412_412 }_412 catch (IOException ex) {_412 Log.e(MainActivity.TAG, ex.getLocalizedMessage(),ex);_412 listener.receivedAccessToken(null, ex);_412 }_412 }_412_412 void sendMessage(String messageBody) {_412 if (conversation != null) {_412 Message.Options options = Message.options().withBody(messageBody);_412 Log.d(MainActivity.TAG,"Message created");_412 conversation.sendMessage(options, new CallbackListener<Message>() {_412 @Override_412 public void onSuccess(Message message) {_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.messageSentCallback();_412 }_412 }_412 });_412 }_412 }_412_412_412 private void loadChannels() {_412 if (conversationsClient == null || conversationsClient.getMyConversations() == null) {_412 return;_412 }_412 conversationsClient.getConversation(DEFAULT_CONVERSATION_NAME, new CallbackListener<Conversation>() {_412 @Override_412 public void onSuccess(Conversation conversation) {_412 if (conversation != null) {_412 if (conversation.getStatus() == Conversation.ConversationStatus.JOINED_412 || conversation.getStatus() == Conversation.ConversationStatus.NOT_PARTICIPATING) {_412 Log.d(MainActivity.TAG, "Already Exists in Conversation: " + DEFAULT_CONVERSATION_NAME);_412 QuickstartConversationsManager.this.conversation = conversation;_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 QuickstartConversationsManager.this.loadPreviousMessages(conversation);_412 } else {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + DEFAULT_CONVERSATION_NAME);_412 joinConversation(conversation);_412 }_412 }_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error retrieving conversation: " + errorInfo.getMessage());_412 createConversation();_412 }_412_412 });_412 }_412_412 private void createConversation() {_412 Log.d(MainActivity.TAG, "Creating Conversation: " + DEFAULT_CONVERSATION_NAME);_412_412 conversationsClient.createConversation(DEFAULT_CONVERSATION_NAME,_412 new CallbackListener<Conversation>() {_412 @Override_412 public void onSuccess(Conversation conversation) {_412 if (conversation != null) {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + DEFAULT_CONVERSATION_NAME);_412 joinConversation(conversation);_412 }_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error creating conversation: " + errorInfo.getMessage());_412 }_412 });_412 }_412_412_412 private void joinConversation(final Conversation conversation) {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + conversation.getUniqueName());_412 if (conversation.getStatus() == Conversation.ConversationStatus.JOINED) {_412_412 QuickstartConversationsManager.this.conversation = conversation;_412 Log.d(MainActivity.TAG, "Already joined default conversation");_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 return;_412 }_412_412_412 conversation.join(new StatusListener() {_412 @Override_412 public void onSuccess() {_412 QuickstartConversationsManager.this.conversation = conversation;_412 Log.d(MainActivity.TAG, "Joined default conversation");_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 QuickstartConversationsManager.this.loadPreviousMessages(conversation);_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error joining conversation: " + errorInfo.getMessage());_412 }_412 });_412 }_412_412 private void loadPreviousMessages(final Conversation conversation) {_412 conversation.getLastMessages(100,_412 new CallbackListener<List<Message>>() {_412 @Override_412 public void onSuccess(List<Message> result) {_412 messages.addAll(result);_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.reloadMessages();_412 }_412 }_412 });_412 }_412_412 private final ConversationsClientListener mConversationsClientListener =_412 new ConversationsClientListener() {_412_412 @Override_412 public void onConversationAdded(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onConversationUpdated(Conversation conversation, Conversation.UpdateReason updateReason) {_412_412 }_412_412 @Override_412 public void onConversationDeleted(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onConversationSynchronizationChange(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412_412 }_412_412 @Override_412 public void onUserUpdated(User user, User.UpdateReason updateReason) {_412_412 }_412_412 @Override_412 public void onUserSubscribed(User user) {_412_412 }_412_412 @Override_412 public void onUserUnsubscribed(User user) {_412_412 }_412_412 @Override_412 public void onClientSynchronization(ConversationsClient.SynchronizationStatus synchronizationStatus) {_412 if (synchronizationStatus == ConversationsClient.SynchronizationStatus.COMPLETED) {_412 loadChannels();_412 }_412 }_412_412 @Override_412 public void onNewMessageNotification(String s, String s1, long l) {_412_412 }_412_412 @Override_412 public void onAddedToConversationNotification(String s) {_412_412 }_412_412 @Override_412 public void onRemovedFromConversationNotification(String s) {_412_412 }_412_412 @Override_412 public void onNotificationSubscribed() {_412_412 }_412_412 @Override_412 public void onNotificationFailed(ErrorInfo errorInfo) {_412_412 }_412_412 @Override_412 public void onConnectionStateChange(ConversationsClient.ConnectionState connectionState) {_412_412 }_412_412 @Override_412 public void onTokenExpired() {_412_412 }_412_412 @Override_412 public void onTokenAboutToExpire() {_412 retrieveToken(new AccessTokenListener() {_412 @Override_412 public void receivedAccessToken(@Nullable String token, @Nullable Exception exception) {_412 if (token != null) {_412 conversationsClient.updateToken(token, new StatusListener() {_412 @Override_412 public void onSuccess() {_412 Log.d(MainActivity.TAG, "Refreshed access token.");_412 }_412 });_412 }_412 }_412 });_412 }_412 };_412_412 private final CallbackListener<ConversationsClient> mConversationsClientCallback =_412 new CallbackListener<ConversationsClient>() {_412 @Override_412 public void onSuccess(ConversationsClient conversationsClient) {_412 QuickstartConversationsManager.this.conversationsClient = conversationsClient;_412 conversationsClient.addListener(QuickstartConversationsManager.this.mConversationsClientListener);_412 Log.d(MainActivity.TAG, "Success creating Twilio Conversations Client");_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error creating Twilio Conversations Client: " + errorInfo.getMessage());_412 }_412 };_412_412_412 private final ConversationListener mDefaultConversationListener = new ConversationListener() {_412_412_412 @Override_412 public void onMessageAdded(final Message message) {_412 Log.d(MainActivity.TAG, "Message added");_412 messages.add(message);_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.receivedNewMessage();_412 }_412 }_412_412 @Override_412 public void onMessageUpdated(Message message, Message.UpdateReason updateReason) {_412 Log.d(MainActivity.TAG, "Message updated: " + message.getMessageBody());_412 }_412_412 @Override_412 public void onMessageDeleted(Message message) {_412 Log.d(MainActivity.TAG, "Message deleted");_412 }_412_412 @Override_412 public void onParticipantAdded(Participant participant) {_412 Log.d(MainActivity.TAG, "Participant added: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onParticipantUpdated(Participant participant, Participant.UpdateReason updateReason) {_412 Log.d(MainActivity.TAG, "Participant updated: " + participant.getIdentity() + " " + updateReason.toString());_412 }_412_412 @Override_412 public void onParticipantDeleted(Participant participant) {_412 Log.d(MainActivity.TAG, "Participant deleted: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onTypingStarted(Conversation conversation, Participant participant) {_412 Log.d(MainActivity.TAG, "Started Typing: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onTypingEnded(Conversation conversation, Participant participant) {_412 Log.d(MainActivity.TAG, "Ended Typing: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onSynchronizationChanged(Conversation conversation) {_412_412 }_412 };_412_412 public ArrayList<Message> getMessages() {_412 return messages;_412 }_412_412 public void setListener(QuickstartConversationsManagerListener listener) {_412 this.conversationsManagerListener = listener;_412 }_412}
Each Conversation
object from the Conversations SDK represents an individual conversation between one or more users. Inside the Conversations Quickstart, we interact with the Conversation
in the QuickstartConversationManager
class. We use this approach to avoid having an activity or fragment class that does too much. After initializing the Conversations SDK with an access token, waiting for the client to synchronize, and then either creating or joining a conversation, we can start to engage with that conversation by sending or receiving messages. These messages are Message
objects from the Conversations SDK.
We retrieve the last messages using the getLastMessages()
method on the Conversation
class. This returns all of the previous messages (up to a limit, which you can set in code), and you can use that to initialize the display for your class. After loading in any existing messages, the QuickstartConversationsManager
notifies its listener (the MainActivity
) that there is a new batch of messages to display.
_412package com.twilio.conversationsquickstart;_412_412import android.content.Context;_412import android.util.Log;_412_412import com.google.gson.Gson;_412import com.twilio.conversations.CallbackListener;_412import com.twilio.conversations.Conversation;_412import com.twilio.conversations.ConversationListener;_412import com.twilio.conversations.ConversationsClient;_412import com.twilio.conversations.ConversationsClientListener;_412import com.twilio.conversations.ErrorInfo;_412import com.twilio.conversations.Participant;_412import com.twilio.conversations.Message;_412import com.twilio.conversations.StatusListener;_412import com.twilio.conversations.User;_412_412import org.jetbrains.annotations.Nullable;_412_412import java.io.IOException;_412import java.util.ArrayList;_412import java.util.List;_412_412import okhttp3.OkHttpClient;_412import okhttp3.Request;_412import okhttp3.Response;_412_412interface QuickstartConversationsManagerListener {_412 void receivedNewMessage();_412 void messageSentCallback();_412 void reloadMessages();_412}_412_412interface TokenResponseListener {_412 void receivedTokenResponse(boolean success, @Nullable Exception exception);_412}_412_412interface AccessTokenListener {_412 void receivedAccessToken(@Nullable String token, @Nullable Exception exception);_412}_412_412_412class QuickstartConversationsManager {_412_412 // This is the unique name of the conversation we are using_412 private final static String DEFAULT_CONVERSATION_NAME = "general";_412_412 final private ArrayList<Message> messages = new ArrayList<>();_412_412 private ConversationsClient conversationsClient;_412_412 private Conversation conversation;_412_412 private QuickstartConversationsManagerListener conversationsManagerListener;_412_412 private String tokenURL = "";_412_412 private class TokenResponse {_412 String token;_412 }_412_412 void retrieveAccessTokenFromServer(final Context context, String identity,_412 final TokenResponseListener listener) {_412_412 // Set the chat token URL in your strings.xml file_412 String chatTokenURL = context.getString(R.string.chat_token_url);_412_412 if ("https://YOUR_DOMAIN_HERE.twil.io/chat-token".equals(chatTokenURL)) {_412 listener.receivedTokenResponse(false, new Exception("You need to replace the chat token URL in strings.xml"));_412 return;_412 }_412_412 tokenURL = chatTokenURL + "?identity=" + identity;_412_412 new Thread(new Runnable() {_412 @Override_412 public void run() {_412 retrieveToken(new AccessTokenListener() {_412 @Override_412 public void receivedAccessToken(@Nullable String token,_412 @Nullable Exception exception) {_412 if (token != null) {_412 ConversationsClient.Properties props = ConversationsClient.Properties.newBuilder().createProperties();_412 ConversationsClient.create(context, token, props, mConversationsClientCallback);_412 listener.receivedTokenResponse(true,null);_412 } else {_412 listener.receivedTokenResponse(false, exception);_412 }_412 }_412 });_412 }_412 }).start();_412 }_412_412 void initializeWithAccessToken(final Context context, final String token) {_412_412 ConversationsClient.Properties props = ConversationsClient.Properties.newBuilder().createProperties();_412 ConversationsClient.create(context, token, props, mConversationsClientCallback);_412 }_412_412 private void retrieveToken(AccessTokenListener listener) {_412 OkHttpClient client = new OkHttpClient();_412_412 Request request = new Request.Builder()_412 .url(tokenURL)_412 .build();_412 try (Response response = client.newCall(request).execute()) {_412 String responseBody = "";_412 if (response != null && response.body() != null) {_412 responseBody = response.body().string();_412 }_412 Log.d(MainActivity.TAG, "Response from server: " + responseBody);_412 Gson gson = new Gson();_412 TokenResponse tokenResponse = gson.fromJson(responseBody,TokenResponse.class);_412 String accessToken = tokenResponse.token;_412 Log.d(MainActivity.TAG, "Retrieved access token from server: " + accessToken);_412 listener.receivedAccessToken(accessToken, null);_412_412 }_412 catch (IOException ex) {_412 Log.e(MainActivity.TAG, ex.getLocalizedMessage(),ex);_412 listener.receivedAccessToken(null, ex);_412 }_412 }_412_412 void sendMessage(String messageBody) {_412 if (conversation != null) {_412 Message.Options options = Message.options().withBody(messageBody);_412 Log.d(MainActivity.TAG,"Message created");_412 conversation.sendMessage(options, new CallbackListener<Message>() {_412 @Override_412 public void onSuccess(Message message) {_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.messageSentCallback();_412 }_412 }_412 });_412 }_412 }_412_412_412 private void loadChannels() {_412 if (conversationsClient == null || conversationsClient.getMyConversations() == null) {_412 return;_412 }_412 conversationsClient.getConversation(DEFAULT_CONVERSATION_NAME, new CallbackListener<Conversation>() {_412 @Override_412 public void onSuccess(Conversation conversation) {_412 if (conversation != null) {_412 if (conversation.getStatus() == Conversation.ConversationStatus.JOINED_412 || conversation.getStatus() == Conversation.ConversationStatus.NOT_PARTICIPATING) {_412 Log.d(MainActivity.TAG, "Already Exists in Conversation: " + DEFAULT_CONVERSATION_NAME);_412 QuickstartConversationsManager.this.conversation = conversation;_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 QuickstartConversationsManager.this.loadPreviousMessages(conversation);_412 } else {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + DEFAULT_CONVERSATION_NAME);_412 joinConversation(conversation);_412 }_412 }_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error retrieving conversation: " + errorInfo.getMessage());_412 createConversation();_412 }_412_412 });_412 }_412_412 private void createConversation() {_412 Log.d(MainActivity.TAG, "Creating Conversation: " + DEFAULT_CONVERSATION_NAME);_412_412 conversationsClient.createConversation(DEFAULT_CONVERSATION_NAME,_412 new CallbackListener<Conversation>() {_412 @Override_412 public void onSuccess(Conversation conversation) {_412 if (conversation != null) {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + DEFAULT_CONVERSATION_NAME);_412 joinConversation(conversation);_412 }_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error creating conversation: " + errorInfo.getMessage());_412 }_412 });_412 }_412_412_412 private void joinConversation(final Conversation conversation) {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + conversation.getUniqueName());_412 if (conversation.getStatus() == Conversation.ConversationStatus.JOINED) {_412_412 QuickstartConversationsManager.this.conversation = conversation;_412 Log.d(MainActivity.TAG, "Already joined default conversation");_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 return;_412 }_412_412_412 conversation.join(new StatusListener() {_412 @Override_412 public void onSuccess() {_412 QuickstartConversationsManager.this.conversation = conversation;_412 Log.d(MainActivity.TAG, "Joined default conversation");_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 QuickstartConversationsManager.this.loadPreviousMessages(conversation);_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error joining conversation: " + errorInfo.getMessage());_412 }_412 });_412 }_412_412 private void loadPreviousMessages(final Conversation conversation) {_412 conversation.getLastMessages(100,_412 new CallbackListener<List<Message>>() {_412 @Override_412 public void onSuccess(List<Message> result) {_412 messages.addAll(result);_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.reloadMessages();_412 }_412 }_412 });_412 }_412_412 private final ConversationsClientListener mConversationsClientListener =_412 new ConversationsClientListener() {_412_412 @Override_412 public void onConversationAdded(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onConversationUpdated(Conversation conversation, Conversation.UpdateReason updateReason) {_412_412 }_412_412 @Override_412 public void onConversationDeleted(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onConversationSynchronizationChange(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412_412 }_412_412 @Override_412 public void onUserUpdated(User user, User.UpdateReason updateReason) {_412_412 }_412_412 @Override_412 public void onUserSubscribed(User user) {_412_412 }_412_412 @Override_412 public void onUserUnsubscribed(User user) {_412_412 }_412_412 @Override_412 public void onClientSynchronization(ConversationsClient.SynchronizationStatus synchronizationStatus) {_412 if (synchronizationStatus == ConversationsClient.SynchronizationStatus.COMPLETED) {_412 loadChannels();_412 }_412 }_412_412 @Override_412 public void onNewMessageNotification(String s, String s1, long l) {_412_412 }_412_412 @Override_412 public void onAddedToConversationNotification(String s) {_412_412 }_412_412 @Override_412 public void onRemovedFromConversationNotification(String s) {_412_412 }_412_412 @Override_412 public void onNotificationSubscribed() {_412_412 }_412_412 @Override_412 public void onNotificationFailed(ErrorInfo errorInfo) {_412_412 }_412_412 @Override_412 public void onConnectionStateChange(ConversationsClient.ConnectionState connectionState) {_412_412 }_412_412 @Override_412 public void onTokenExpired() {_412_412 }_412_412 @Override_412 public void onTokenAboutToExpire() {_412 retrieveToken(new AccessTokenListener() {_412 @Override_412 public void receivedAccessToken(@Nullable String token, @Nullable Exception exception) {_412 if (token != null) {_412 conversationsClient.updateToken(token, new StatusListener() {_412 @Override_412 public void onSuccess() {_412 Log.d(MainActivity.TAG, "Refreshed access token.");_412 }_412 });_412 }_412 }_412 });_412 }_412 };_412_412 private final CallbackListener<ConversationsClient> mConversationsClientCallback =_412 new CallbackListener<ConversationsClient>() {_412 @Override_412 public void onSuccess(ConversationsClient conversationsClient) {_412 QuickstartConversationsManager.this.conversationsClient = conversationsClient;_412 conversationsClient.addListener(QuickstartConversationsManager.this.mConversationsClientListener);_412 Log.d(MainActivity.TAG, "Success creating Twilio Conversations Client");_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error creating Twilio Conversations Client: " + errorInfo.getMessage());_412 }_412 };_412_412_412 private final ConversationListener mDefaultConversationListener = new ConversationListener() {_412_412_412 @Override_412 public void onMessageAdded(final Message message) {_412 Log.d(MainActivity.TAG, "Message added");_412 messages.add(message);_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.receivedNewMessage();_412 }_412 }_412_412 @Override_412 public void onMessageUpdated(Message message, Message.UpdateReason updateReason) {_412 Log.d(MainActivity.TAG, "Message updated: " + message.getMessageBody());_412 }_412_412 @Override_412 public void onMessageDeleted(Message message) {_412 Log.d(MainActivity.TAG, "Message deleted");_412 }_412_412 @Override_412 public void onParticipantAdded(Participant participant) {_412 Log.d(MainActivity.TAG, "Participant added: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onParticipantUpdated(Participant participant, Participant.UpdateReason updateReason) {_412 Log.d(MainActivity.TAG, "Participant updated: " + participant.getIdentity() + " " + updateReason.toString());_412 }_412_412 @Override_412 public void onParticipantDeleted(Participant participant) {_412 Log.d(MainActivity.TAG, "Participant deleted: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onTypingStarted(Conversation conversation, Participant participant) {_412 Log.d(MainActivity.TAG, "Started Typing: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onTypingEnded(Conversation conversation, Participant participant) {_412 Log.d(MainActivity.TAG, "Ended Typing: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onSynchronizationChanged(Conversation conversation) {_412_412 }_412 };_412_412 public ArrayList<Message> getMessages() {_412 return messages;_412 }_412_412 public void setListener(QuickstartConversationsManagerListener listener) {_412 this.conversationsManagerListener = listener;_412 }_412}
The QuickstartConversationsManager
class implements the ConverstationListener
interface. As events occur with our conversation, our manager object will get notified. One of these events is onMessageAdded.
This event gets fired from the Twilio Conversations SDK when any user sends a message to the conversation.
Our manager appends that message to the messages we already have, and then notifies its delegate that a new message has arrived, and that the view controller should refresh its view of the messages.
In the main activity, we simply tell the recycler view that contains the messages to reload its data.
Receiving New Messages
_412package com.twilio.conversationsquickstart;_412_412import android.content.Context;_412import android.util.Log;_412_412import com.google.gson.Gson;_412import com.twilio.conversations.CallbackListener;_412import com.twilio.conversations.Conversation;_412import com.twilio.conversations.ConversationListener;_412import com.twilio.conversations.ConversationsClient;_412import com.twilio.conversations.ConversationsClientListener;_412import com.twilio.conversations.ErrorInfo;_412import com.twilio.conversations.Participant;_412import com.twilio.conversations.Message;_412import com.twilio.conversations.StatusListener;_412import com.twilio.conversations.User;_412_412import org.jetbrains.annotations.Nullable;_412_412import java.io.IOException;_412import java.util.ArrayList;_412import java.util.List;_412_412import okhttp3.OkHttpClient;_412import okhttp3.Request;_412import okhttp3.Response;_412_412interface QuickstartConversationsManagerListener {_412 void receivedNewMessage();_412 void messageSentCallback();_412 void reloadMessages();_412}_412_412interface TokenResponseListener {_412 void receivedTokenResponse(boolean success, @Nullable Exception exception);_412}_412_412interface AccessTokenListener {_412 void receivedAccessToken(@Nullable String token, @Nullable Exception exception);_412}_412_412_412class QuickstartConversationsManager {_412_412 // This is the unique name of the conversation we are using_412 private final static String DEFAULT_CONVERSATION_NAME = "general";_412_412 final private ArrayList<Message> messages = new ArrayList<>();_412_412 private ConversationsClient conversationsClient;_412_412 private Conversation conversation;_412_412 private QuickstartConversationsManagerListener conversationsManagerListener;_412_412 private String tokenURL = "";_412_412 private class TokenResponse {_412 String token;_412 }_412_412 void retrieveAccessTokenFromServer(final Context context, String identity,_412 final TokenResponseListener listener) {_412_412 // Set the chat token URL in your strings.xml file_412 String chatTokenURL = context.getString(R.string.chat_token_url);_412_412 if ("https://YOUR_DOMAIN_HERE.twil.io/chat-token".equals(chatTokenURL)) {_412 listener.receivedTokenResponse(false, new Exception("You need to replace the chat token URL in strings.xml"));_412 return;_412 }_412_412 tokenURL = chatTokenURL + "?identity=" + identity;_412_412 new Thread(new Runnable() {_412 @Override_412 public void run() {_412 retrieveToken(new AccessTokenListener() {_412 @Override_412 public void receivedAccessToken(@Nullable String token,_412 @Nullable Exception exception) {_412 if (token != null) {_412 ConversationsClient.Properties props = ConversationsClient.Properties.newBuilder().createProperties();_412 ConversationsClient.create(context, token, props, mConversationsClientCallback);_412 listener.receivedTokenResponse(true,null);_412 } else {_412 listener.receivedTokenResponse(false, exception);_412 }_412 }_412 });_412 }_412 }).start();_412 }_412_412 void initializeWithAccessToken(final Context context, final String token) {_412_412 ConversationsClient.Properties props = ConversationsClient.Properties.newBuilder().createProperties();_412 ConversationsClient.create(context, token, props, mConversationsClientCallback);_412 }_412_412 private void retrieveToken(AccessTokenListener listener) {_412 OkHttpClient client = new OkHttpClient();_412_412 Request request = new Request.Builder()_412 .url(tokenURL)_412 .build();_412 try (Response response = client.newCall(request).execute()) {_412 String responseBody = "";_412 if (response != null && response.body() != null) {_412 responseBody = response.body().string();_412 }_412 Log.d(MainActivity.TAG, "Response from server: " + responseBody);_412 Gson gson = new Gson();_412 TokenResponse tokenResponse = gson.fromJson(responseBody,TokenResponse.class);_412 String accessToken = tokenResponse.token;_412 Log.d(MainActivity.TAG, "Retrieved access token from server: " + accessToken);_412 listener.receivedAccessToken(accessToken, null);_412_412 }_412 catch (IOException ex) {_412 Log.e(MainActivity.TAG, ex.getLocalizedMessage(),ex);_412 listener.receivedAccessToken(null, ex);_412 }_412 }_412_412 void sendMessage(String messageBody) {_412 if (conversation != null) {_412 Message.Options options = Message.options().withBody(messageBody);_412 Log.d(MainActivity.TAG,"Message created");_412 conversation.sendMessage(options, new CallbackListener<Message>() {_412 @Override_412 public void onSuccess(Message message) {_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.messageSentCallback();_412 }_412 }_412 });_412 }_412 }_412_412_412 private void loadChannels() {_412 if (conversationsClient == null || conversationsClient.getMyConversations() == null) {_412 return;_412 }_412 conversationsClient.getConversation(DEFAULT_CONVERSATION_NAME, new CallbackListener<Conversation>() {_412 @Override_412 public void onSuccess(Conversation conversation) {_412 if (conversation != null) {_412 if (conversation.getStatus() == Conversation.ConversationStatus.JOINED_412 || conversation.getStatus() == Conversation.ConversationStatus.NOT_PARTICIPATING) {_412 Log.d(MainActivity.TAG, "Already Exists in Conversation: " + DEFAULT_CONVERSATION_NAME);_412 QuickstartConversationsManager.this.conversation = conversation;_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 QuickstartConversationsManager.this.loadPreviousMessages(conversation);_412 } else {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + DEFAULT_CONVERSATION_NAME);_412 joinConversation(conversation);_412 }_412 }_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error retrieving conversation: " + errorInfo.getMessage());_412 createConversation();_412 }_412_412 });_412 }_412_412 private void createConversation() {_412 Log.d(MainActivity.TAG, "Creating Conversation: " + DEFAULT_CONVERSATION_NAME);_412_412 conversationsClient.createConversation(DEFAULT_CONVERSATION_NAME,_412 new CallbackListener<Conversation>() {_412 @Override_412 public void onSuccess(Conversation conversation) {_412 if (conversation != null) {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + DEFAULT_CONVERSATION_NAME);_412 joinConversation(conversation);_412 }_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error creating conversation: " + errorInfo.getMessage());_412 }_412 });_412 }_412_412_412 private void joinConversation(final Conversation conversation) {_412 Log.d(MainActivity.TAG, "Joining Conversation: " + conversation.getUniqueName());_412 if (conversation.getStatus() == Conversation.ConversationStatus.JOINED) {_412_412 QuickstartConversationsManager.this.conversation = conversation;_412 Log.d(MainActivity.TAG, "Already joined default conversation");_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 return;_412 }_412_412_412 conversation.join(new StatusListener() {_412 @Override_412 public void onSuccess() {_412 QuickstartConversationsManager.this.conversation = conversation;_412 Log.d(MainActivity.TAG, "Joined default conversation");_412 QuickstartConversationsManager.this.conversation.addListener(mDefaultConversationListener);_412 QuickstartConversationsManager.this.loadPreviousMessages(conversation);_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error joining conversation: " + errorInfo.getMessage());_412 }_412 });_412 }_412_412 private void loadPreviousMessages(final Conversation conversation) {_412 conversation.getLastMessages(100,_412 new CallbackListener<List<Message>>() {_412 @Override_412 public void onSuccess(List<Message> result) {_412 messages.addAll(result);_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.reloadMessages();_412 }_412 }_412 });_412 }_412_412 private final ConversationsClientListener mConversationsClientListener =_412 new ConversationsClientListener() {_412_412 @Override_412 public void onConversationAdded(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onConversationUpdated(Conversation conversation, Conversation.UpdateReason updateReason) {_412_412 }_412_412 @Override_412 public void onConversationDeleted(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onConversationSynchronizationChange(Conversation conversation) {_412_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412_412 }_412_412 @Override_412 public void onUserUpdated(User user, User.UpdateReason updateReason) {_412_412 }_412_412 @Override_412 public void onUserSubscribed(User user) {_412_412 }_412_412 @Override_412 public void onUserUnsubscribed(User user) {_412_412 }_412_412 @Override_412 public void onClientSynchronization(ConversationsClient.SynchronizationStatus synchronizationStatus) {_412 if (synchronizationStatus == ConversationsClient.SynchronizationStatus.COMPLETED) {_412 loadChannels();_412 }_412 }_412_412 @Override_412 public void onNewMessageNotification(String s, String s1, long l) {_412_412 }_412_412 @Override_412 public void onAddedToConversationNotification(String s) {_412_412 }_412_412 @Override_412 public void onRemovedFromConversationNotification(String s) {_412_412 }_412_412 @Override_412 public void onNotificationSubscribed() {_412_412 }_412_412 @Override_412 public void onNotificationFailed(ErrorInfo errorInfo) {_412_412 }_412_412 @Override_412 public void onConnectionStateChange(ConversationsClient.ConnectionState connectionState) {_412_412 }_412_412 @Override_412 public void onTokenExpired() {_412_412 }_412_412 @Override_412 public void onTokenAboutToExpire() {_412 retrieveToken(new AccessTokenListener() {_412 @Override_412 public void receivedAccessToken(@Nullable String token, @Nullable Exception exception) {_412 if (token != null) {_412 conversationsClient.updateToken(token, new StatusListener() {_412 @Override_412 public void onSuccess() {_412 Log.d(MainActivity.TAG, "Refreshed access token.");_412 }_412 });_412 }_412 }_412 });_412 }_412 };_412_412 private final CallbackListener<ConversationsClient> mConversationsClientCallback =_412 new CallbackListener<ConversationsClient>() {_412 @Override_412 public void onSuccess(ConversationsClient conversationsClient) {_412 QuickstartConversationsManager.this.conversationsClient = conversationsClient;_412 conversationsClient.addListener(QuickstartConversationsManager.this.mConversationsClientListener);_412 Log.d(MainActivity.TAG, "Success creating Twilio Conversations Client");_412 }_412_412 @Override_412 public void onError(ErrorInfo errorInfo) {_412 Log.e(MainActivity.TAG, "Error creating Twilio Conversations Client: " + errorInfo.getMessage());_412 }_412 };_412_412_412 private final ConversationListener mDefaultConversationListener = new ConversationListener() {_412_412_412 @Override_412 public void onMessageAdded(final Message message) {_412 Log.d(MainActivity.TAG, "Message added");_412 messages.add(message);_412 if (conversationsManagerListener != null) {_412 conversationsManagerListener.receivedNewMessage();_412 }_412 }_412_412 @Override_412 public void onMessageUpdated(Message message, Message.UpdateReason updateReason) {_412 Log.d(MainActivity.TAG, "Message updated: " + message.getMessageBody());_412 }_412_412 @Override_412 public void onMessageDeleted(Message message) {_412 Log.d(MainActivity.TAG, "Message deleted");_412 }_412_412 @Override_412 public void onParticipantAdded(Participant participant) {_412 Log.d(MainActivity.TAG, "Participant added: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onParticipantUpdated(Participant participant, Participant.UpdateReason updateReason) {_412 Log.d(MainActivity.TAG, "Participant updated: " + participant.getIdentity() + " " + updateReason.toString());_412 }_412_412 @Override_412 public void onParticipantDeleted(Participant participant) {_412 Log.d(MainActivity.TAG, "Participant deleted: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onTypingStarted(Conversation conversation, Participant participant) {_412 Log.d(MainActivity.TAG, "Started Typing: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onTypingEnded(Conversation conversation, Participant participant) {_412 Log.d(MainActivity.TAG, "Ended Typing: " + participant.getIdentity());_412 }_412_412 @Override_412 public void onSynchronizationChanged(Conversation conversation) {_412_412 }_412 };_412_412 public ArrayList<Message> getMessages() {_412 return messages;_412 }_412_412 public void setListener(QuickstartConversationsManagerListener listener) {_412 this.conversationsManagerListener = listener;_412 }_412}
Now that you've seen how the Conversations Android Quickstart implements several key pieces of functionality, you can see how to add the Conversations SDK to your Java or Kotlin Android project. You can re-use the Quickstart Conversations Manager class within your own project, or extend it to fit your needs.
For more information, check out these helpful links: