Skip to contentSkip to navigationSkip to topbar
Rate this page:
On this page

Chat with Android and Java


(warning)

Warning

As the Programmable Chat API is set to sunset in 2022(link takes you to an external page), we will no longer maintain these chat tutorials.

Please see our Conversations API QuickStart to start building robust virtual spaces for conversation.

(error)

Danger

Programmable Chat has been deprecated and is no longer supported. Instead, we'll be focusing on the next generation of chat: Twilio Conversations. Find out more about the EOL process here(link takes you to an external page).

If you're starting a new project, please visit the Conversations Docs to begin. If you've already built on Programmable Chat, please visit our Migration Guide to learn about how to switch.

Ready to implement a chat application using Twilio Programmable Chat Client? Here is how it works at a high level:

  1. Twilio Programmable Chat(link takes you to an external page) is the core product we'll be using to handle all the chat functionality
  2. We use a server side app to generate a user access token which contains all your Twilio account information. The Programmable chat Client uses this token to connect with the API

Properati built a web and mobile messaging app to help real estate buyers and sellers connect in real time. Learn more here.(link takes you to an external page)

For your convenience, we consolidated the source code for this tutorial in a single GitHub repository(link takes you to an external page). Feel free to clone it and tweak it as required.


Initialize the Client - Part 1: Fetch access token

initialize-the-client---part-1-fetch-access-token page anchor

The first thing you need to create a client is an access token. This token holds information about your Twilio account and Programmable Chat API keys. We have created a web version of Twilio chat in different languages. You can use any of these to generate the token:

We use Volley(link takes you to an external page) to make a request to our server and get the access token.

Fetch Access Token

fetch-access-token page anchor

app/src/main/java/com/twilio/twiliochat/chat/accesstoken/AccessTokenFetcher.java


_73
package com.twilio.twiliochat.chat.accesstoken;
_73
_73
import android.content.Context;
_73
import android.content.res.Resources;
_73
import android.util.Log;
_73
_73
import com.android.volley.Request;
_73
import com.android.volley.Response;
_73
import com.android.volley.VolleyError;
_73
import com.android.volley.toolbox.JsonObjectRequest;
_73
import com.twilio.twiliochat.R;
_73
import com.twilio.twiliochat.application.SessionManager;
_73
import com.twilio.twiliochat.application.TwilioChatApplication;
_73
import com.twilio.twiliochat.chat.listeners.TaskCompletionListener;
_73
_73
import org.json.JSONException;
_73
import org.json.JSONObject;
_73
_73
import java.util.HashMap;
_73
import java.util.Map;
_73
_73
public class AccessTokenFetcher {
_73
_73
private Context context;
_73
_73
public AccessTokenFetcher(Context context) {
_73
this.context = context;
_73
}
_73
_73
public void fetch(final TaskCompletionListener<String, String> listener) {
_73
JSONObject obj = new JSONObject(getTokenRequestParams(context));
_73
String identity = SessionManager.getInstance().getUsername();
_73
String requestUrl = getStringResource(R.string.token_url) + "?identity=" + identity;
_73
Log.d(TwilioChatApplication.TAG, "Requesting access token from: " + requestUrl);
_73
_73
JsonObjectRequest jsonObjReq =
_73
new JsonObjectRequest(Request.Method.GET, requestUrl, obj, new Response.Listener<JSONObject>() {
_73
_73
@Override
_73
public void onResponse(JSONObject response) {
_73
String token = null;
_73
try {
_73
token = response.getString("token");
_73
} catch (JSONException e) {
_73
Log.e(TwilioChatApplication.TAG, e.getLocalizedMessage(), e);
_73
listener.onError("Failed to parse token JSON response");
_73
}
_73
listener.onSuccess(token);
_73
}
_73
}, new Response.ErrorListener() {
_73
_73
@Override
_73
public void onErrorResponse(VolleyError error) {
_73
Log.e(TwilioChatApplication.TAG, error.getLocalizedMessage(), error);
_73
listener.onError("Failed to fetch token");
_73
}
_73
});
_73
jsonObjReq.setShouldCache(false);
_73
TokenRequest.getInstance().addToRequestQueue(jsonObjReq);
_73
}
_73
_73
private Map<String, String> getTokenRequestParams(Context context) {
_73
Map<String, String> params = new HashMap<>();
_73
params.put("identity", SessionManager.getInstance().getUsername());
_73
return params;
_73
}
_73
_73
private String getStringResource(int id) {
_73
Resources resources = TwilioChatApplication.get().getResources();
_73
return resources.getString(id);
_73
}
_73
_73
}

Now it's time to use that token to initialize your Twilio client.


Initialize the Client - Part 2: Build the client

initialize-the-client---part-2-build-the-client page anchor

Before creating a Programmable Chat Client instance we need to decide the region it will connect to and the synchronization strategy used to initialize it.

We can then pass this information along with the access token generated in the previous step and wait for the client to be ready.

app/src/main/java/com/twilio/twiliochat/chat/ChatClientBuilder.java


_42
package com.twilio.twiliochat.chat;
_42
_42
import android.content.Context;
_42
_42
import com.twilio.chat.CallbackListener;
_42
import com.twilio.chat.ChatClient;
_42
import com.twilio.chat.ErrorInfo;
_42
import com.twilio.twiliochat.chat.listeners.TaskCompletionListener;
_42
_42
public class ChatClientBuilder extends CallbackListener<ChatClient> {
_42
_42
private Context context;
_42
private TaskCompletionListener<ChatClient, String> buildListener;
_42
_42
public ChatClientBuilder(Context context) {
_42
this.context = context;
_42
}
_42
_42
public void build(String token, final TaskCompletionListener<ChatClient, String> listener) {
_42
ChatClient.Properties props =
_42
new ChatClient.Properties.Builder()
_42
.setRegion("us1")
_42
.createProperties();
_42
_42
this.buildListener = listener;
_42
ChatClient.create(context.getApplicationContext(),
_42
token,
_42
props,
_42
this);
_42
}
_42
_42
_42
@Override
_42
public void onSuccess(ChatClient chatClient) {
_42
this.buildListener.onSuccess(chatClient);
_42
}
_42
_42
@Override
_42
public void onError(ErrorInfo errorInfo) {
_42
this.buildListener.onError(errorInfo.getMessage());
_42
}
_42
}

The next step will be getting a channel list.


Our ChannelManager class takes care of everything related to channels. The first thing we need to do when the class is initialized, is to store a list of channels of type Channel. To do this we call the method getChannels from the Programmable Chat Client and extract a Channel object from each ChannelDescriptor returned.

app/src/main/java/com/twilio/twiliochat/chat/channels/ChannelManager.java


_339
package com.twilio.twiliochat.chat.channels;
_339
_339
import android.content.res.Resources;
_339
import android.os.Handler;
_339
import android.os.Looper;
_339
import android.util.Log;
_339
_339
import com.twilio.chat.CallbackListener;
_339
import com.twilio.chat.Channel;
_339
import com.twilio.chat.Channel.ChannelType;
_339
import com.twilio.chat.ChannelDescriptor;
_339
import com.twilio.chat.Channels;
_339
import com.twilio.chat.ChatClient;
_339
import com.twilio.chat.ChatClientListener;
_339
import com.twilio.chat.ErrorInfo;
_339
import com.twilio.chat.Paginator;
_339
import com.twilio.chat.StatusListener;
_339
import com.twilio.chat.User;
_339
import com.twilio.twiliochat.R;
_339
import com.twilio.twiliochat.application.TwilioChatApplication;
_339
import com.twilio.twiliochat.chat.ChatClientManager;
_339
import com.twilio.twiliochat.chat.accesstoken.AccessTokenFetcher;
_339
import com.twilio.twiliochat.chat.listeners.TaskCompletionListener;
_339
_339
import java.util.ArrayList;
_339
import java.util.Collections;
_339
import java.util.List;
_339
_339
public class ChannelManager implements ChatClientListener {
_339
private static ChannelManager sharedManager = new ChannelManager();
_339
public Channel generalChannel;
_339
private ChatClientManager chatClientManager;
_339
private ChannelExtractor channelExtractor;
_339
private List<Channel> channels;
_339
private Channels channelsObject;
_339
private ChatClientListener listener;
_339
private String defaultChannelName;
_339
private String defaultChannelUniqueName;
_339
private Handler handler;
_339
private Boolean isRefreshingChannels = false;
_339
_339
private ChannelManager() {
_339
this.chatClientManager = TwilioChatApplication.get().getChatClientManager();
_339
this.channelExtractor = new ChannelExtractor();
_339
this.listener = this;
_339
defaultChannelName = getStringResource(R.string.default_channel_name);
_339
defaultChannelUniqueName = getStringResource(R.string.default_channel_unique_name);
_339
handler = setupListenerHandler();
_339
}
_339
_339
public static ChannelManager getInstance() {
_339
return sharedManager;
_339
}
_339
_339
public List<Channel> getChannels() {
_339
return channels;
_339
}
_339
_339
public String getDefaultChannelName() {
_339
return this.defaultChannelName;
_339
}
_339
_339
public void leaveChannelWithHandler(Channel channel, StatusListener handler) {
_339
channel.leave(handler);
_339
}
_339
_339
public void deleteChannelWithHandler(Channel channel, StatusListener handler) {
_339
channel.destroy(handler);
_339
}
_339
_339
public void populateChannels(final LoadChannelListener listener) {
_339
if (this.chatClientManager == null || this.isRefreshingChannels) {
_339
return;
_339
}
_339
this.isRefreshingChannels = true;
_339
_339
handler.post(new Runnable() {
_339
@Override
_339
public void run() {
_339
channelsObject = chatClientManager.getChatClient().getChannels();
_339
_339
channelsObject.getPublicChannelsList(new CallbackListener<Paginator<ChannelDescriptor>>() {
_339
@Override
_339
public void onSuccess(Paginator<ChannelDescriptor> channelDescriptorPaginator) {
_339
extractChannelsFromPaginatorAndPopulate(channelDescriptorPaginator, listener);
_339
}
_339
});
_339
_339
}
_339
});
_339
}
_339
_339
private void extractChannelsFromPaginatorAndPopulate(final Paginator<ChannelDescriptor> channelsPaginator,
_339
final LoadChannelListener listener) {
_339
channels = new ArrayList<>();
_339
ChannelManager.this.channels.clear();
_339
channelExtractor.extractAndSortFromChannelDescriptor(channelsPaginator,
_339
new TaskCompletionListener<List<Channel>, String>() {
_339
@Override
_339
public void onSuccess(List<Channel> channels) {
_339
ChannelManager.this.channels.addAll(channels);
_339
Collections.sort(ChannelManager.this.channels, new CustomChannelComparator());
_339
ChannelManager.this.isRefreshingChannels = false;
_339
chatClientManager.addClientListener(ChannelManager.this);
_339
listener.onChannelsFinishedLoading(ChannelManager.this.channels);
_339
}
_339
_339
@Override
_339
public void onError(String errorText) {
_339
Log.e(TwilioChatApplication.TAG,"Error populating channels: " + errorText);
_339
}
_339
});
_339
}
_339
_339
public void createChannelWithName(String name, final StatusListener handler) {
_339
this.channelsObject
_339
.channelBuilder()
_339
.withFriendlyName(name)
_339
.withType(ChannelType.PUBLIC)
_339
.build(new CallbackListener<Channel>() {
_339
@Override
_339
public void onSuccess(final Channel newChannel) {
_339
handler.onSuccess();
_339
}
_339
_339
@Override
_339
public void onError(ErrorInfo errorInfo) {
_339
handler.onError(errorInfo);
_339
}
_339
});
_339
}
_339
_339
public void joinOrCreateGeneralChannelWithCompletion(final StatusListener listener) {
_339
channelsObject.getChannel(defaultChannelUniqueName, new CallbackListener<Channel>() {
_339
@Override
_339
public void onSuccess(Channel channel) {
_339
ChannelManager.this.generalChannel = channel;
_339
if (channel != null) {
_339
joinGeneralChannelWithCompletion(listener);
_339
} else {
_339
createGeneralChannelWithCompletion(listener);
_339
}
_339
}
_339
});
_339
}
_339
_339
private void joinGeneralChannelWithCompletion(final StatusListener listener) {
_339
if (generalChannel.getStatus() == Channel.ChannelStatus.JOINED) {
_339
listener.onSuccess();
_339
return;
_339
}
_339
this.generalChannel.join(new StatusListener() {
_339
@Override
_339
public void onSuccess() {
_339
listener.onSuccess();
_339
}
_339
_339
@Override
_339
public void onError(ErrorInfo errorInfo) {
_339
listener.onError(errorInfo);
_339
}
_339
});
_339
}
_339
_339
private void createGeneralChannelWithCompletion(final StatusListener listener) {
_339
this.channelsObject
_339
.channelBuilder()
_339
.withFriendlyName(defaultChannelName)
_339
.withUniqueName(defaultChannelUniqueName)
_339
.withType(ChannelType.PUBLIC)
_339
.build(new CallbackListener<Channel>() {
_339
@Override
_339
public void onSuccess(final Channel channel) {
_339
ChannelManager.this.generalChannel = channel;
_339
ChannelManager.this.channels.add(channel);
_339
joinGeneralChannelWithCompletion(listener);
_339
}
_339
_339
@Override
_339
public void onError(ErrorInfo errorInfo) {
_339
listener.onError(errorInfo);
_339
}
_339
});
_339
}
_339
_339
public void setChannelListener(ChatClientListener listener) {
_339
this.listener = listener;
_339
}
_339
_339
private String getStringResource(int id) {
_339
Resources resources = TwilioChatApplication.get().getResources();
_339
return resources.getString(id);
_339
}
_339
_339
@Override
_339
public void onChannelAdded(Channel channel) {
_339
if (listener != null) {
_339
listener.onChannelAdded(channel);
_339
}
_339
}
_339
_339
@Override
_339
public void onChannelUpdated(Channel channel, Channel.UpdateReason updateReason) {
_339
if (listener != null) {
_339
listener.onChannelUpdated(channel, updateReason);
_339
}
_339
}
_339
_339
@Override
_339
public void onChannelDeleted(Channel channel) {
_339
if (listener != null) {
_339
listener.onChannelDeleted(channel);
_339
}
_339
}
_339
_339
@Override
_339
public void onChannelSynchronizationChange(Channel channel) {
_339
if (listener != null) {
_339
listener.onChannelSynchronizationChange(channel);
_339
}
_339
}
_339
_339
@Override
_339
public void onError(ErrorInfo errorInfo) {
_339
if (listener != null) {
_339
listener.onError(errorInfo);
_339
}
_339
}
_339
_339
@Override
_339
public void onClientSynchronization(ChatClient.SynchronizationStatus synchronizationStatus) {
_339
_339
}
_339
_339
@Override
_339
public void onChannelJoined(Channel channel) {
_339
_339
}
_339
_339
@Override
_339
public void onChannelInvited(Channel channel) {
_339
_339
}
_339
_339
@Override
_339
public void onUserUpdated(User user, User.UpdateReason updateReason) {
_339
if (listener != null) {
_339
listener.onUserUpdated(user, updateReason);
_339
}
_339
}
_339
_339
@Override
_339
public void onUserSubscribed(User user) {
_339
_339
}
_339
_339
@Override
_339
public void onUserUnsubscribed(User user) {
_339
_339
}
_339
_339
@Override
_339
public void onNewMessageNotification(String s, String s1, long l) {
_339
_339
}
_339
_339
@Override
_339
public void onAddedToChannelNotification(String s) {
_339
_339
}
_339
_339
@Override
_339
public void onInvitedToChannelNotification(String s) {
_339
_339
}
_339
_339
@Override
_339
public void onRemovedFromChannelNotification(String s) {
_339
_339
}
_339
_339
@Override
_339
public void onNotificationSubscribed() {
_339
_339
}
_339
_339
@Override
_339
public void onNotificationFailed(ErrorInfo errorInfo) {
_339
_339
}
_339
_339
@Override
_339
public void onConnectionStateChange(ChatClient.ConnectionState connectionState) {
_339
_339
}
_339
_339
@Override
_339
public void onTokenExpired() {
_339
refreshAccessToken();
_339
}
_339
_339
@Override
_339
public void onTokenAboutToExpire() {
_339
refreshAccessToken();
_339
}
_339
_339
private void refreshAccessToken() {
_339
AccessTokenFetcher accessTokenFetcher = chatClientManager.getAccessTokenFetcher();
_339
accessTokenFetcher.fetch(new TaskCompletionListener<String, String>() {
_339
@Override
_339
public void onSuccess(String token) {
_339
ChannelManager.this.chatClientManager.getChatClient().updateToken(token, new StatusListener() {
_339
@Override
_339
public void onSuccess() {
_339
Log.d(TwilioChatApplication.TAG, "Successfully updated access token.");
_339
}
_339
});
_339
}
_339
_339
@Override
_339
public void onError(String message) {
_339
Log.e(TwilioChatApplication.TAG,"Error trying to fetch token: " + message);
_339
}
_339
});
_339
}
_339
_339
private Handler setupListenerHandler() {
_339
Looper looper;
_339
Handler handler;
_339
if ((looper = Looper.myLooper()) != null) {
_339
handler = new Handler(looper);
_339
} else if ((looper = Looper.getMainLooper()) != null) {
_339
handler = new Handler(looper);
_339
} else {
_339
throw new IllegalArgumentException("Channel Listener must have a Looper.");
_339
}
_339
return handler;
_339
}
_339
}

Let's see how we can listen to events from the chat client so we can update our app's state.


The Programmable Chat Client will trigger events such as onChannelAdded or onChannelDeleted on our application. Given the creation or deletion of a channel, we'll reload the channel list in the sliding panel. If a channel is deleted, but we were currently on that same channel, the application will automatically join the general channel.

You must set your ChatClient to listen to events(link takes you to an external page) using a ChatClientListener(link takes you to an external page). In this particular case, MainChatActivity implements ChatClientListener, but it's methods are called from the ChannelManager class that also implements ChatClientListener (who is the client's listener). ChannelManager is used as an event handler proxy. Twilio chat sets the listener when loading the channels.

Listen for Client Events

listen-for-client-events page anchor

app/src/main/java/com/twilio/twiliochat/chat/MainChatActivity.java


_565
package com.twilio.twiliochat.chat;
_565
_565
import android.app.Activity;
_565
import android.app.ProgressDialog;
_565
import android.content.Context;
_565
import android.content.DialogInterface;
_565
import android.content.Intent;
_565
import android.content.res.Resources;
_565
import android.os.Bundle;
_565
import android.os.Handler;
_565
import android.util.Log;
_565
import android.view.Menu;
_565
import android.view.MenuItem;
_565
import android.view.View;
_565
import android.widget.AdapterView;
_565
import android.widget.Button;
_565
import android.widget.ListView;
_565
import android.widget.TextView;
_565
_565
import androidx.appcompat.app.ActionBarDrawerToggle;
_565
import androidx.appcompat.app.AppCompatActivity;
_565
import androidx.appcompat.widget.Toolbar;
_565
import androidx.core.view.GravityCompat;
_565
import androidx.drawerlayout.widget.DrawerLayout;
_565
import androidx.fragment.app.FragmentTransaction;
_565
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
_565
_565
import com.twilio.chat.Channel;
_565
import com.twilio.chat.ChatClient;
_565
import com.twilio.chat.ChatClientListener;
_565
import com.twilio.chat.ErrorInfo;
_565
import com.twilio.chat.StatusListener;
_565
import com.twilio.chat.User;
_565
import com.twilio.twiliochat.R;
_565
import com.twilio.twiliochat.application.AlertDialogHandler;
_565
import com.twilio.twiliochat.application.SessionManager;
_565
import com.twilio.twiliochat.application.TwilioChatApplication;
_565
import com.twilio.twiliochat.chat.channels.ChannelAdapter;
_565
import com.twilio.twiliochat.chat.channels.ChannelManager;
_565
import com.twilio.twiliochat.chat.channels.LoadChannelListener;
_565
import com.twilio.twiliochat.chat.listeners.InputOnClickListener;
_565
import com.twilio.twiliochat.chat.listeners.TaskCompletionListener;
_565
import com.twilio.twiliochat.landing.LoginActivity;
_565
_565
import java.util.List;
_565
_565
public class MainChatActivity extends AppCompatActivity implements ChatClientListener {
_565
private Context context;
_565
private Activity mainActivity;
_565
private Button logoutButton;
_565
private Button addChannelButton;
_565
private TextView usernameTextView;
_565
private ChatClientManager chatClientManager;
_565
private ListView channelsListView;
_565
private ChannelAdapter channelAdapter;
_565
private ChannelManager channelManager;
_565
private MainChatFragment chatFragment;
_565
private DrawerLayout drawer;
_565
private ProgressDialog progressDialog;
_565
private MenuItem leaveChannelMenuItem;
_565
private MenuItem deleteChannelMenuItem;
_565
private SwipeRefreshLayout refreshLayout;
_565
_565
@Override
_565
protected void onDestroy() {
_565
super.onDestroy();
_565
new Handler().post(new Runnable() {
_565
@Override
_565
public void run() {
_565
chatClientManager.shutdown();
_565
TwilioChatApplication.get().getChatClientManager().setChatClient(null);
_565
}
_565
});
_565
}
_565
_565
@Override
_565
protected void onCreate(Bundle savedInstanceState) {
_565
super.onCreate(savedInstanceState);
_565
setContentView(R.layout.activity_main_chat);
_565
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
_565
setSupportActionBar(toolbar);
_565
_565
drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
_565
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar,
_565
R.string.navigation_drawer_open, R.string.navigation_drawer_close);
_565
drawer.setDrawerListener(toggle);
_565
toggle.syncState();
_565
_565
refreshLayout = (SwipeRefreshLayout) findViewById(R.id.refreshLayout);
_565
_565
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
_565
_565
chatFragment = new MainChatFragment();
_565
fragmentTransaction.add(R.id.fragment_container, chatFragment);
_565
fragmentTransaction.commit();
_565
_565
context = this;
_565
mainActivity = this;
_565
logoutButton = (Button) findViewById(R.id.buttonLogout);
_565
addChannelButton = (Button) findViewById(R.id.buttonAddChannel);
_565
usernameTextView = (TextView) findViewById(R.id.textViewUsername);
_565
channelsListView = (ListView) findViewById(R.id.listViewChannels);
_565
_565
channelManager = ChannelManager.getInstance();
_565
setUsernameTextView();
_565
_565
setUpListeners();
_565
checkTwilioClient();
_565
}
_565
_565
private void setUpListeners() {
_565
logoutButton.setOnClickListener(new View.OnClickListener() {
_565
@Override
_565
public void onClick(View v) {
_565
promptLogout();
_565
}
_565
});
_565
addChannelButton.setOnClickListener(new View.OnClickListener() {
_565
@Override
_565
public void onClick(View v) {
_565
showAddChannelDialog();
_565
}
_565
});
_565
refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
_565
@Override
_565
public void onRefresh() {
_565
refreshLayout.setRefreshing(true);
_565
refreshChannels();
_565
}
_565
});
_565
channelsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
_565
@Override
_565
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
_565
setChannel(position);
_565
}
_565
});
_565
}
_565
_565
@Override
_565
public void onBackPressed() {
_565
if (drawer.isDrawerOpen(GravityCompat.START)) {
_565
drawer.closeDrawer(GravityCompat.START);
_565
} else {
_565
super.onBackPressed();
_565
}
_565
}
_565
_565
@Override
_565
public boolean onCreateOptionsMenu(Menu menu) {
_565
getMenuInflater().inflate(R.menu.main_chat, menu);
_565
this.leaveChannelMenuItem = menu.findItem(R.id.action_leave_channel);
_565
this.leaveChannelMenuItem.setVisible(false);
_565
this.deleteChannelMenuItem = menu.findItem(R.id.action_delete_channel);
_565
this.deleteChannelMenuItem.setVisible(false);
_565
return true;
_565
}
_565
_565
@Override
_565
public boolean onOptionsItemSelected(MenuItem item) {
_565
int id = item.getItemId();
_565
_565
if (id == R.id.action_leave_channel) {
_565
leaveCurrentChannel();
_565
return true;
_565
}
_565
if (id == R.id.action_delete_channel) {
_565
promptChannelDeletion();
_565
}
_565
_565
return super.onOptionsItemSelected(item);
_565
}
_565
_565
private String getStringResource(int id) {
_565
Resources resources = getResources();
_565
return resources.getString(id);
_565
}
_565
_565
private void refreshChannels() {
_565
channelManager.populateChannels(new LoadChannelListener() {
_565
@Override
_565
public void onChannelsFinishedLoading(final List<Channel> channels) {
_565
runOnUiThread(new Runnable() {
_565
@Override
_565
public void run() {
_565
channelAdapter.setChannels(channels);
_565
refreshLayout.setRefreshing(false);
_565
}
_565
});
_565
}
_565
});
_565
}
_565
_565
private void populateChannels() {
_565
channelManager.setChannelListener(this);
_565
channelManager.populateChannels(new LoadChannelListener() {
_565
@Override
_565
public void onChannelsFinishedLoading(List<Channel> channels) {
_565
channelAdapter = new ChannelAdapter(mainActivity, channels);
_565
channelsListView.setAdapter(channelAdapter);
_565
MainChatActivity.this.channelManager
_565
.joinOrCreateGeneralChannelWithCompletion(new StatusListener() {
_565
@Override
_565
public void onSuccess() {
_565
runOnUiThread(new Runnable() {
_565
@Override
_565
public void run() {
_565
channelAdapter.notifyDataSetChanged();
_565
stopActivityIndicator();
_565
setChannel(0);
_565
}
_565
});
_565
}
_565
_565
@Override
_565
public void onError(ErrorInfo errorInfo) {
_565
showAlertWithMessage(getStringResource(R.string.generic_error) + " - " + errorInfo.getMessage());
_565
}
_565
});
_565
}
_565
});
_565
}
_565
_565
private void setChannel(final int position) {
_565
List<Channel> channels = channelManager.getChannels();
_565
if (channels == null) {
_565
return;
_565
}
_565
final Channel currentChannel = chatFragment.getCurrentChannel();
_565
final Channel selectedChannel = channels.get(position);
_565
if (currentChannel != null && currentChannel.getSid().contentEquals(selectedChannel.getSid())) {
_565
drawer.closeDrawer(GravityCompat.START);
_565
return;
_565
}
_565
hideMenuItems(position);
_565
if (selectedChannel != null) {
_565
showActivityIndicator("Joining " + selectedChannel.getFriendlyName() + " channel");
_565
if (currentChannel != null && currentChannel.getStatus() == Channel.ChannelStatus.JOINED) {
_565
this.channelManager.leaveChannelWithHandler(currentChannel, new StatusListener() {
_565
@Override
_565
public void onSuccess() {
_565
joinChannel(selectedChannel);
_565
}
_565
_565
@Override
_565
public void onError(ErrorInfo errorInfo) {
_565
stopActivityIndicator();
_565
}
_565
});
_565
return;
_565
}
_565
joinChannel(selectedChannel);
_565
stopActivityIndicator();
_565
} else {
_565
stopActivityIndicator();
_565
showAlertWithMessage(getStringResource(R.string.generic_error));
_565
Log.e(TwilioChatApplication.TAG,"Selected channel out of range");
_565
}
_565
}
_565
_565
private void joinChannel(final Channel selectedChannel) {
_565
runOnUiThread(new Runnable() {
_565
@Override
_565
public void run() {
_565
chatFragment.setCurrentChannel(selectedChannel, new StatusListener() {
_565
@Override
_565
public void onSuccess() {
_565
MainChatActivity.this.stopActivityIndicator();
_565
}
_565
_565
@Override
_565
public void onError(ErrorInfo errorInfo) {
_565
}
_565
});
_565
setTitle(selectedChannel.getFriendlyName());
_565
drawer.closeDrawer(GravityCompat.START);
_565
}
_565
});
_565
}
_565
_565
private void hideMenuItems(final int position) {
_565
runOnUiThread(new Runnable() {
_565
@Override
_565
public void run() {
_565
MainChatActivity.this.leaveChannelMenuItem.setVisible(position != 0);
_565
MainChatActivity.this.deleteChannelMenuItem.setVisible(position != 0);
_565
}
_565
});
_565
}
_565
_565
private void showAddChannelDialog() {
_565
String message = getStringResource(R.string.new_channel_prompt);
_565
AlertDialogHandler.displayInputDialog(message, context, new InputOnClickListener() {
_565
@Override
_565
public void onClick(String input) {
_565
if (input.length() == 0) {
_565
showAlertWithMessage(getStringResource(R.string.channel_name_required_message));
_565
return;
_565
}
_565
createChannelWithName(input);
_565
}
_565
});
_565
}
_565
_565
private void createChannelWithName(String name) {
_565
name = name.trim();
_565
if (name.toLowerCase()
_565
.contentEquals(this.channelManager.getDefaultChannelName().toLowerCase())) {
_565
showAlertWithMessage(getStringResource(R.string.channel_name_equals_default_name));
_565
return;
_565
}
_565
this.channelManager.createChannelWithName(name, new StatusListener() {
_565
@Override
_565
public void onSuccess() {
_565
refreshChannels();
_565
}
_565
_565
@Override
_565
public void onError(ErrorInfo errorInfo) {
_565
showAlertWithMessage(getStringResource(R.string.generic_error) + " - " + errorInfo.getMessage());
_565
}
_565
});
_565
}
_565
_565
private void promptChannelDeletion() {
_565
String message = getStringResource(R.string.channel_delete_prompt_message);
_565
AlertDialogHandler.displayCancellableAlertWithHandler(message, context,
_565
new DialogInterface.OnClickListener() {
_565
@Override
_565
public void onClick(DialogInterface dialog, int which) {
_565
deleteCurrentChannel();
_565
}
_565
});
_565
}
_565
_565
private void deleteCurrentChannel() {
_565
Channel currentChannel = chatFragment.getCurrentChannel();
_565
channelManager.deleteChannelWithHandler(currentChannel, new StatusListener() {
_565
@Override
_565
public void onSuccess() {
_565
}
_565
_565
@Override
_565
public void onError(ErrorInfo errorInfo) {
_565
showAlertWithMessage(getStringResource(R.string.message_deletion_forbidden));
_565
}
_565
});
_565
}
_565
_565
private void leaveCurrentChannel() {
_565
final Channel currentChannel = chatFragment.getCurrentChannel();
_565
if (currentChannel.getStatus() == Channel.ChannelStatus.NOT_PARTICIPATING) {
_565
setChannel(0);
_565
return;
_565
}
_565
channelManager.leaveChannelWithHandler(currentChannel, new StatusListener() {
_565
@Override
_565
public void onSuccess() {
_565
setChannel(0);
_565
}
_565
_565
@Override
_565
public void onError(ErrorInfo errorInfo) {
_565
stopActivityIndicator();
_565
}
_565
});
_565
}
_565
_565
private void checkTwilioClient() {
_565
showActivityIndicator(getStringResource(R.string.loading_channels_message));
_565
chatClientManager = TwilioChatApplication.get().getChatClientManager();
_565
if (chatClientManager.getChatClient() == null) {
_565
initializeClient();
_565
} else {
_565
populateChannels();
_565
}
_565
}
_565
_565
private void initializeClient() {
_565
chatClientManager.connectClient(new TaskCompletionListener<Void, String>() {
_565
@Override
_565
public void onSuccess(Void aVoid) {
_565
populateChannels();
_565
}
_565
_565
@Override
_565
public void onError(String errorMessage) {
_565
stopActivityIndicator();
_565
showAlertWithMessage("Client connection error: " + errorMessage);
_565
}
_565
});
_565
}
_565
_565
private void promptLogout() {
_565
final String message = getStringResource(R.string.logout_prompt_message);
_565
runOnUiThread(new Runnable() {
_565
@Override
_565
public void run() {
_565
AlertDialogHandler.displayCancellableAlertWithHandler(message, context,
_565
new DialogInterface.OnClickListener() {
_565
@Override
_565
public void onClick(DialogInterface dialog, int which) {
_565
SessionManager.getInstance().logoutUser();
_565
showLoginActivity();
_565
}
_565
});
_565
}
_565
});
_565
_565
}
_565
_565
private void showLoginActivity() {
_565
Intent launchIntent = new Intent();
_565
launchIntent.setClass(getApplicationContext(), LoginActivity.class);
_565
startActivity(launchIntent);
_565
finish();
_565
}
_565
_565
private void setUsernameTextView() {
_565
String username =
_565
SessionManager.getInstance().getUserDetails().get(SessionManager.KEY_USERNAME);
_565
usernameTextView.setText(username);
_565
}
_565
_565
private void stopActivityIndicator() {
_565
runOnUiThread(new Runnable() {
_565
@Override
_565
public void run() {
_565
if (progressDialog.isShowing()) {
_565
progressDialog.dismiss();
_565
}
_565
}
_565
});
_565
}
_565
_565
private void showActivityIndicator(final String message) {
_565
runOnUiThread(new Runnable() {
_565
@Override
_565
public void run() {
_565
progressDialog = new ProgressDialog(MainChatActivity.this.mainActivity);
_565
progressDialog.setMessage(message);
_565
progressDialog.show();
_565
progressDialog.setCanceledOnTouchOutside(false);
_565
progressDialog.setCancelable(false);
_565
}
_565
});
_565
}
_565
_565
private void showAlertWithMessage(final String message) {
_565
runOnUiThread(new Runnable() {
_565
@Override
_565
public void run() {
_565
AlertDialogHandler.displayAlertWithMessage(message, context);
_565
}
_565
});
_565
}
_565
_565
_565
_565
@Override
_565
public void onChannelAdded(Channel channel) {
_565
Log.d(TwilioChatApplication.TAG,"Channel Added");
_565
refreshChannels();
_565
}
_565
_565
@Override
_565
public void onChannelDeleted(final Channel channel) {
_565
Log.d(TwilioChatApplication.TAG,"Channel Deleted");
_565
Channel currentChannel = chatFragment.getCurrentChannel();
_565
if (channel.getSid().contentEquals(currentChannel.getSid())) {
_565
chatFragment.setCurrentChannel(null, null);
_565
setChannel(0);
_565
}
_565
refreshChannels();
_565
}
_565
_565
@Override
_565
public void onChannelInvited(Channel channel) {
_565
_565
}
_565
_565
@Override
_565
public void onChannelSynchronizationChange(Channel channel) {
_565
_565
}
_565
_565
@Override
_565
public void onError(ErrorInfo errorInfo) {
_565
_565
}
_565
_565
@Override
_565
public void onClientSynchronization(ChatClient.SynchronizationStatus synchronizationStatus) {
_565
_565
}
_565
_565
@Override
_565
public void onConnectionStateChange(ChatClient.ConnectionState connectionState) {
_565
_565
}
_565
_565
@Override
_565
public void onTokenExpired() {
_565
_565
}
_565
_565
@Override
_565
public void onTokenAboutToExpire() {
_565
_565
}
_565
_565
@Override
_565
public void onChannelJoined(Channel channel) {
_565
_565
}
_565
_565
@Override
_565
public void onChannelUpdated(Channel channel, Channel.UpdateReason updateReason) {
_565
_565
}
_565
_565
@Override
_565
public void onUserUpdated(User user, User.UpdateReason updateReason) {
_565
_565
}
_565
_565
@Override
_565
public void onUserSubscribed(User user) {
_565
_565
}
_565
_565
@Override
_565
public void onUserUnsubscribed(User user) {
_565
_565
}
_565
_565
@Override
_565
public void onNewMessageNotification(String s, String s1, long l) {
_565
_565
}
_565
_565
@Override
_565
public void onAddedToChannelNotification(String s) {
_565
_565
}
_565
_565
@Override
_565
public void onInvitedToChannelNotification(String s) {
_565
_565
}
_565
_565
@Override
_565
public void onRemovedFromChannelNotification(String s) {
_565
_565
}
_565
_565
@Override
_565
public void onNotificationSubscribed() {
_565
_565
}
_565
_565
@Override
_565
public void onNotificationFailed(ErrorInfo errorInfo) {
_565
_565
}
_565
}

Next, we need a default channel.


Join the General Channel

join-the-general-channel page anchor

This application will try to join a channel called "General Channel" when it starts. If the channel doesn't exist, it'll create one with that name. The scope of this example application will show you how to work only with public channels, but the Programmable Chat Client allows you to create private channels and handle invitations.

Notice we set a unique name for the general channel as we don't want to create a new general channel every time we start the application.

Join or Create a General Channel

join-or-create-a-general-channel page anchor

app/src/main/java/com/twilio/twiliochat/chat/channels/ChannelManager.java


_339
package com.twilio.twiliochat.chat.channels;
_339
_339
import android.content.res.Resources;
_339
import android.os.Handler;
_339
import android.os.Looper;
_339
import android.util.Log;
_339
_339
import com.twilio.chat.CallbackListener;
_339
import com.twilio.chat.Channel;
_339
import com.twilio.chat.Channel.ChannelType;
_339
import com.twilio.chat.ChannelDescriptor;
_339
import com.twilio.chat.Channels;
_339
import com.twilio.chat.ChatClient;
_339
import com.twilio.chat.ChatClientListener;
_339
import com.twilio.chat.ErrorInfo;
_339
import com.twilio.chat.Paginator;
_339
import com.twilio.chat.StatusListener;
_339
import com.twilio.chat.User;
_339
import com.twilio.twiliochat.R;
_339
import com.twilio.twiliochat.application.TwilioChatApplication;
_339
import com.twilio.twiliochat.chat.ChatClientManager;
_339
import com.twilio.twiliochat.chat.accesstoken.AccessTokenFetcher;
_339
import com.twilio.twiliochat.chat.listeners.TaskCompletionListener;
_339
_339
import java.util.ArrayList;
_339
import java.util.Collections;
_339
import java.util.List;
_339
_339
public class ChannelManager implements ChatClientListener {
_339
private static ChannelManager sharedManager = new ChannelManager();
_339
public Channel generalChannel;
_339
private ChatClientManager chatClientManager;
_339
private ChannelExtractor channelExtractor;
_339
private List<Channel> channels;
_339
private Channels channelsObject;
_339
private ChatClientListener listener;
_339
private String defaultChannelName;
_339
private String defaultChannelUniqueName;
_339
private Handler handler;
_339
private Boolean isRefreshingChannels = false;
_339
_339
private ChannelManager() {
_339
this.chatClientManager = TwilioChatApplication.get().getChatClientManager();
_339
this.channelExtractor = new ChannelExtractor();
_339
this.listener = this;
_339
defaultChannelName = getStringResource(R.string.default_channel_name);
_339
defaultChannelUniqueName = getStringResource(R.string.default_channel_unique_name);
_339
handler = setupListenerHandler();
_339
}
_339
_339
public static ChannelManager getInstance() {
_339
return sharedManager;
_339
}
_339
_339
public List<Channel> getChannels() {
_339
return channels;
_339
}
_339
_339
public String getDefaultChannelName() {
_339
return this.defaultChannelName;
_339
}
_339
_339
public void leaveChannelWithHandler(Channel channel, StatusListener handler) {
_339
channel.leave(handler);
_339
}
_339
_339
public void deleteChannelWithHandler(Channel channel, StatusListener handler) {
_339
channel.destroy(handler);
_339
}
_339
_339
public void populateChannels(final LoadChannelListener listener) {
_339
if (this.chatClientManager == null || this.isRefreshingChannels) {
_339
return;
_339
}
_339
this.isRefreshingChannels = true;
_339
_339
handler.post(new Runnable() {
_339
@Override
_339
public void run() {
_339
channelsObject = chatClientManager.getChatClient().getChannels();
_339
_339
channelsObject.getPublicChannelsList(new CallbackListener<Paginator<ChannelDescriptor>>() {
_339
@Override
_339
public void onSuccess(Paginator<ChannelDescriptor> channelDescriptorPaginator) {
_339
extractChannelsFromPaginatorAndPopulate(channelDescriptorPaginator, listener);
_339
}
_339
});
_339
_339
}
_339
});
_339
}
_339
_339
private void extractChannelsFromPaginatorAndPopulate(final Paginator<ChannelDescriptor> channelsPaginator,
_339
final LoadChannelListener listener) {
_339
channels = new ArrayList<>();
_339
ChannelManager.this.channels.clear();
_339
channelExtractor.extractAndSortFromChannelDescriptor(channelsPaginator,
_339
new TaskCompletionListener<List<Channel>, String>() {
_339
@Override
_339
public void onSuccess(List<Channel> channels) {
_339
ChannelManager.this.channels.addAll(channels);
_339
Collections.sort(ChannelManager.this.channels, new CustomChannelComparator());
_339
ChannelManager.this.isRefreshingChannels = false;
_339
chatClientManager.addClientListener(ChannelManager.this);
_339
listener.onChannelsFinishedLoading(ChannelManager.this.channels);
_339
}
_339
_339
@Override
_339
public void onError(String errorText) {
_339
Log.e(TwilioChatApplication.TAG,"Error populating channels: " + errorText);
_339
}
_339
});
_339
}
_339
_339
public void createChannelWithName(String name, final StatusListener handler) {
_339
this.channelsObject
_339
.channelBuilder()
_339
.withFriendlyName(name)
_339
.withType(ChannelType.PUBLIC)
_339
.build(new CallbackListener<Channel>() {
_339
@Override
_339
public void onSuccess(final Channel newChannel) {
_339
handler.onSuccess();
_339
}
_339
_339
@Override
_339
public void onError(ErrorInfo errorInfo) {
_339
handler.onError(errorInfo);
_339
}
_339
});
_339
}
_339
_339
public void joinOrCreateGeneralChannelWithCompletion(final StatusListener listener) {
_339
channelsObject.getChannel(defaultChannelUniqueName, new CallbackListener<Channel>() {
_339
@Override
_339
public void onSuccess(Channel channel) {
_339
ChannelManager.this.generalChannel = channel;
_339
if (channel != null) {
_339
joinGeneralChannelWithCompletion(listener);
_339
} else {
_339
createGeneralChannelWithCompletion(listener);
_339
}
_339
}
_339
});
_339
}
_339
_339
private void joinGeneralChannelWithCompletion(final StatusListener listener) {
_339
if (generalChannel.getStatus() == Channel.ChannelStatus.JOINED) {
_339
listener.onSuccess();
_339
return;
_339
}
_339
this.generalChannel.join(new StatusListener() {
_339
@Override
_339
public void onSuccess() {
_339
listener.onSuccess();
_339
}
_339
_339
@Override
_339
public void onError(ErrorInfo errorInfo) {
_339
listener.onError(errorInfo);
_339
}
_339
});
_339
}
_339
_339
private void createGeneralChannelWithCompletion(final StatusListener listener) {
_339
this.channelsObject
_339
.channelBuilder()
_339
.withFriendlyName(defaultChannelName)
_339
.withUniqueName(defaultChannelUniqueName)
_339
.withType(ChannelType.PUBLIC)
_339
.build(new CallbackListener<Channel>() {
_339
@Override
_339
public void onSuccess(final Channel channel) {
_339
ChannelManager.this.generalChannel = channel;
_339
ChannelManager.this.channels.add(channel);
_339
joinGeneralChannelWithCompletion(listener);
_339
}
_339
_339
@Override
_339
public void onError(ErrorInfo errorInfo) {
_339
listener.onError(errorInfo);
_339
}
_339
});
_339
}
_339
_339
public void setChannelListener(ChatClientListener listener) {
_339
this.listener = listener;
_339
}
_339
_339
private String getStringResource(int id) {
_339
Resources resources = TwilioChatApplication.get().getResources();
_339
return resources.getString(id);
_339
}
_339
_339
@Override
_339
public void onChannelAdded(Channel channel) {
_339
if (listener != null) {
_339
listener.onChannelAdded(channel);
_339
}
_339
}
_339
_339
@Override
_339
public void onChannelUpdated(Channel channel, Channel.UpdateReason updateReason) {
_339
if (listener != null) {
_339
listener.onChannelUpdated(channel, updateReason);
_339
}
_339
}
_339
_339
@Override
_339
public void onChannelDeleted(Channel channel) {
_339
if (listener != null) {
_339
listener.onChannelDeleted(channel);
_339
}
_339
}
_339
_339
@Override
_339
public void onChannelSynchronizationChange(Channel channel) {
_339
if (listener != null) {
_339
listener.onChannelSynchronizationChange(channel);
_339
}
_339
}
_339
_339
@Override
_339
public void onError(ErrorInfo errorInfo) {
_339
if (listener != null) {
_339
listener.onError(errorInfo);
_339
}
_339
}
_339
_339
@Override
_339
public void onClientSynchronization(ChatClient.SynchronizationStatus synchronizationStatus) {
_339
_339
}
_339
_339
@Override
_339
public void onChannelJoined(Channel channel) {
_339
_339
}
_339
_339
@Override
_339
public void onChannelInvited(Channel channel) {
_339
_339
}
_339
_339
@Override
_339
public void onUserUpdated(User user, User.UpdateReason updateReason) {
_339
if (listener != null) {
_339
listener.onUserUpdated(user, updateReason);
_339
}
_339
}
_339
_339
@Override
_339
public void onUserSubscribed(User user) {
_339
_339
}
_339
_339
@Override
_339
public void onUserUnsubscribed(User user) {
_339
_339
}
_339
_339
@Override
_339
public void onNewMessageNotification(String s, String s1, long l) {
_339
_339
}
_339
_339
@Override
_339
public void onAddedToChannelNotification(String s) {
_339
_339
}
_339
_339
@Override
_339
public void onInvitedToChannelNotification(String s) {
_339
_339
}
_339
_339
@Override
_339
public void onRemovedFromChannelNotification(String s) {
_339
_339
}
_339
_339
@Override
_339
public void onNotificationSubscribed() {
_339
_339
}
_339
_339
@Override
_339
public void onNotificationFailed(ErrorInfo errorInfo) {
_339
_339
}
_339
_339
@Override
_339
public void onConnectionStateChange(ChatClient.ConnectionState connectionState) {
_339
_339
}
_339
_339
@Override
_339
public void onTokenExpired() {
_339
refreshAccessToken();
_339
}
_339
_339
@Override
_339
public void onTokenAboutToExpire() {
_339
refreshAccessToken();
_339
}
_339
_339
private void refreshAccessToken() {
_339
AccessTokenFetcher accessTokenFetcher = chatClientManager.getAccessTokenFetcher();
_339
accessTokenFetcher.fetch(new TaskCompletionListener<String, String>() {
_339
@Override
_339
public void onSuccess(String token) {
_339
ChannelManager.this.chatClientManager.getChatClient().updateToken(token, new StatusListener() {
_339
@Override
_339
public void onSuccess() {
_339
Log.d(TwilioChatApplication.TAG, "Successfully updated access token.");
_339
}
_339
});
_339
}
_339
_339
@Override
_339
public void onError(String message) {
_339
Log.e(TwilioChatApplication.TAG,"Error trying to fetch token: " + message);
_339
}
_339
});
_339
}
_339
_339
private Handler setupListenerHandler() {
_339
Looper looper;
_339
Handler handler;
_339
if ((looper = Looper.myLooper()) != null) {
_339
handler = new Handler(looper);
_339
} else if ((looper = Looper.getMainLooper()) != null) {
_339
handler = new Handler(looper);
_339
} else {
_339
throw new IllegalArgumentException("Channel Listener must have a Looper.");
_339
}
_339
return handler;
_339
}
_339
}

Now let's listen for some channel events.


Listen to Channel Events

listen-to-channel-events page anchor

We set a channel's listener to MainChatFragment that implements ChannelListener, and here we implemented the following methods that listen to channel events:

  • onMessageAdded : When someone sends a message to the channel you are connected to.
  • onMemberAdded : When someone joins the channel.
  • onMemberDeleted : When someone leaves the channel.

As you may have noticed, each one of these methods includes useful objects as parameters. One example is the actual message that was added to the channel.

app/src/main/java/com/twilio/twiliochat/chat/MainChatFragment.java


_215
package com.twilio.twiliochat.chat;
_215
_215
import android.app.Activity;
_215
import android.content.Context;
_215
import android.os.Bundle;
_215
import androidx.fragment.app.Fragment;
_215
import android.view.KeyEvent;
_215
import android.view.LayoutInflater;
_215
import android.view.View;
_215
import android.view.ViewGroup;
_215
import android.widget.Button;
_215
import android.widget.EditText;
_215
import android.widget.ListView;
_215
import android.widget.TextView;
_215
_215
import com.twilio.chat.CallbackListener;
_215
import com.twilio.chat.Channel;
_215
import com.twilio.chat.ChannelListener;
_215
import com.twilio.chat.ErrorInfo;
_215
import com.twilio.chat.Member;
_215
import com.twilio.chat.Message;
_215
import com.twilio.chat.Messages;
_215
import com.twilio.chat.StatusListener;
_215
import com.twilio.twiliochat.R;
_215
import com.twilio.twiliochat.chat.messages.JoinedStatusMessage;
_215
import com.twilio.twiliochat.chat.messages.LeftStatusMessage;
_215
import com.twilio.twiliochat.chat.messages.MessageAdapter;
_215
import com.twilio.twiliochat.chat.messages.StatusMessage;
_215
_215
import java.util.List;
_215
_215
public class MainChatFragment extends Fragment implements ChannelListener {
_215
Context context;
_215
Activity mainActivity;
_215
Button sendButton;
_215
ListView messagesListView;
_215
EditText messageTextEdit;
_215
_215
MessageAdapter messageAdapter;
_215
Channel currentChannel;
_215
Messages messagesObject;
_215
_215
public MainChatFragment() {
_215
}
_215
_215
public static MainChatFragment newInstance() {
_215
MainChatFragment fragment = new MainChatFragment();
_215
return fragment;
_215
}
_215
_215
@Override
_215
public void onCreate(Bundle savedInstanceState) {
_215
super.onCreate(savedInstanceState);
_215
context = this.getActivity();
_215
mainActivity = this.getActivity();
_215
}
_215
_215
@Override
_215
public View onCreateView(LayoutInflater inflater, ViewGroup container,
_215
Bundle savedInstanceState) {
_215
View view = inflater.inflate(R.layout.fragment_main_chat, container, false);
_215
sendButton = (Button) view.findViewById(R.id.buttonSend);
_215
messagesListView = (ListView) view.findViewById(R.id.listViewMessages);
_215
messageTextEdit = (EditText) view.findViewById(R.id.editTextMessage);
_215
_215
messageAdapter = new MessageAdapter(mainActivity);
_215
messagesListView.setAdapter(messageAdapter);
_215
setUpListeners();
_215
setMessageInputEnabled(false);
_215
_215
return view;
_215
}
_215
_215
@Override
_215
public void onAttach(Context context) {
_215
super.onAttach(context);
_215
}
_215
_215
@Override
_215
public void onDetach() {
_215
super.onDetach();
_215
}
_215
_215
public Channel getCurrentChannel() {
_215
return currentChannel;
_215
}
_215
_215
public void setCurrentChannel(Channel currentChannel, final StatusListener handler) {
_215
if (currentChannel == null) {
_215
this.currentChannel = null;
_215
return;
_215
}
_215
if (!currentChannel.equals(this.currentChannel)) {
_215
setMessageInputEnabled(false);
_215
this.currentChannel = currentChannel;
_215
this.currentChannel.addListener(this);
_215
if (this.currentChannel.getStatus() == Channel.ChannelStatus.JOINED) {
_215
loadMessages(handler);
_215
} else {
_215
this.currentChannel.join(new StatusListener() {
_215
@Override
_215
public void onSuccess() {
_215
loadMessages(handler);
_215
}
_215
_215
@Override
_215
public void onError(ErrorInfo errorInfo) {
_215
}
_215
});
_215
}
_215
}
_215
}
_215
_215
private void loadMessages(final StatusListener handler) {
_215
this.messagesObject = this.currentChannel.getMessages();
_215
_215
if (messagesObject != null) {
_215
messagesObject.getLastMessages(100, new CallbackListener<List<Message>>() {
_215
@Override
_215
public void onSuccess(List<Message> messageList) {
_215
messageAdapter.setMessages(messageList);
_215
setMessageInputEnabled(true);
_215
messageTextEdit.requestFocus();
_215
handler.onSuccess();
_215
}
_215
});
_215
}
_215
}
_215
_215
private void setUpListeners() {
_215
sendButton.setOnClickListener(new View.OnClickListener() {
_215
@Override
_215
public void onClick(View v) {
_215
sendMessage();
_215
}
_215
});
_215
messageTextEdit.setOnEditorActionListener(new TextView.OnEditorActionListener() {
_215
@Override
_215
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
_215
return false;
_215
}
_215
});
_215
}
_215
_215
private void sendMessage() {
_215
String messageText = getTextInput();
_215
if (messageText.length() == 0) {
_215
return;
_215
}
_215
Message.Options messageOptions = Message.options().withBody(messageText);
_215
this.messagesObject.sendMessage(messageOptions, null);
_215
clearTextInput();
_215
}
_215
_215
private void setMessageInputEnabled(final boolean enabled) {
_215
mainActivity.runOnUiThread(new Runnable() {
_215
@Override
_215
public void run() {
_215
MainChatFragment.this.sendButton.setEnabled(enabled);
_215
MainChatFragment.this.messageTextEdit.setEnabled(enabled);
_215
}
_215
});
_215
}
_215
_215
private String getTextInput() {
_215
return messageTextEdit.getText().toString();
_215
}
_215
_215
private void clearTextInput() {
_215
messageTextEdit.setText("");
_215
}
_215
_215
@Override
_215
public void onMessageAdded(Message message) {
_215
messageAdapter.addMessage(message);
_215
}
_215
_215
@Override
_215
public void onMemberAdded(Member member) {
_215
StatusMessage statusMessage = new JoinedStatusMessage(member.getIdentity());
_215
this.messageAdapter.addStatusMessage(statusMessage);
_215
}
_215
_215
@Override
_215
public void onMemberDeleted(Member member) {
_215
StatusMessage statusMessage = new LeftStatusMessage(member.getIdentity());
_215
this.messageAdapter.addStatusMessage(statusMessage);
_215
}
_215
_215
@Override
_215
public void onMessageUpdated(Message message, Message.UpdateReason updateReason) {
_215
}
_215
_215
@Override
_215
public void onMessageDeleted(Message message) {
_215
}
_215
_215
@Override
_215
public void onMemberUpdated(Member member, Member.UpdateReason updateReason) {
_215
}
_215
_215
@Override
_215
public void onTypingStarted(Channel channel, Member member) {
_215
}
_215
_215
@Override
_215
public void onTypingEnded(Channel channel, Member member) {
_215
}
_215
_215
@Override
_215
public void onSynchronizationChanged(Channel channel) {
_215
}
_215
_215
_215
}

We've actually got a real chat app going here, but let's make it more interesting with multiple channels.


The application uses a Drawer Layout(link takes you to an external page) to show a list of the channels created for that Twilio account.

When you tap on the name of a channel, from the sidebar, that channel is set on the MainChatFragment. The setCurrentChannel method takes care of joining to the selected channel and loading the messages.

app/src/main/java/com/twilio/twiliochat/chat/MainChatFragment.java


_215
package com.twilio.twiliochat.chat;
_215
_215
import android.app.Activity;
_215
import android.content.Context;
_215
import android.os.Bundle;
_215
import androidx.fragment.app.Fragment;
_215
import android.view.KeyEvent;
_215
import android.view.LayoutInflater;
_215
import android.view.View;
_215
import android.view.ViewGroup;
_215
import android.widget.Button;
_215
import android.widget.EditText;
_215
import android.widget.ListView;
_215
import android.widget.TextView;
_215
_215
import com.twilio.chat.CallbackListener;
_215
import com.twilio.chat.Channel;
_215
import com.twilio.chat.ChannelListener;
_215
import com.twilio.chat.ErrorInfo;
_215
import com.twilio.chat.Member;
_215
import com.twilio.chat.Message;
_215
import com.twilio.chat.Messages;
_215
import com.twilio.chat.StatusListener;
_215
import com.twilio.twiliochat.R;
_215
import com.twilio.twiliochat.chat.messages.JoinedStatusMessage;
_215
import com.twilio.twiliochat.chat.messages.LeftStatusMessage;
_215
import com.twilio.twiliochat.chat.messages.MessageAdapter;
_215
import com.twilio.twiliochat.chat.messages.StatusMessage;
_215
_215
import java.util.List;
_215
_215
public class MainChatFragment extends Fragment implements ChannelListener {
_215
Context context;
_215
Activity mainActivity;
_215
Button sendButton;
_215
ListView messagesListView;
_215
EditText messageTextEdit;
_215
_215
MessageAdapter messageAdapter;
_215
Channel currentChannel;
_215
Messages messagesObject;
_215
_215
public MainChatFragment() {
_215
}
_215
_215
public static MainChatFragment newInstance() {
_215
MainChatFragment fragment = new MainChatFragment();
_215
return fragment;
_215
}
_215
_215
@Override
_215
public void onCreate(Bundle savedInstanceState) {
_215
super.onCreate(savedInstanceState);
_215
context = this.getActivity();
_215
mainActivity = this.getActivity();
_215
}
_215
_215
@Override
_215
public View onCreateView(LayoutInflater inflater, ViewGroup container,
_215
Bundle savedInstanceState) {
_215
View view = inflater.inflate(R.layout.fragment_main_chat, container, false);
_215
sendButton = (Button) view.findViewById(R.id.buttonSend);
_215
messagesListView = (ListView) view.findViewById(R.id.listViewMessages);
_215
messageTextEdit = (EditText) view.findViewById(R.id.editTextMessage);
_215
_215
messageAdapter = new MessageAdapter(mainActivity);
_215
messagesListView.setAdapter(messageAdapter);
_215
setUpListeners();
_215
setMessageInputEnabled(false);
_215
_215
return view;
_215
}
_215
_215
@Override
_215
public void onAttach(Context context) {
_215
super.onAttach(context);
_215
}
_215
_215
@Override
_215
public void onDetach() {
_215
super.onDetach();
_215
}
_215
_215
public Channel getCurrentChannel() {
_215
return currentChannel;
_215
}
_215
_215
public void setCurrentChannel(Channel currentChannel, final StatusListener handler) {
_215
if (currentChannel == null) {
_215
this.currentChannel = null;
_215
return;
_215
}
_215
if (!currentChannel.equals(this.currentChannel)) {
_215
setMessageInputEnabled(false);
_215
this.currentChannel = currentChannel;
_215
this.currentChannel.addListener(this);
_215
if (this.currentChannel.getStatus() == Channel.ChannelStatus.JOINED) {
_215
loadMessages(handler);
_215
} else {
_215
this.currentChannel.join(new StatusListener() {
_215
@Override
_215
public void onSuccess() {
_215
loadMessages(handler);
_215
}
_215
_215
@Override
_215
public void onError(ErrorInfo errorInfo) {
_215
}
_215
});
_215
}
_215
}
_215
}
_215
_215
private void loadMessages(final StatusListener handler) {
_215
this.messagesObject = this.currentChannel.getMessages();
_215
_215
if (messagesObject != null) {
_215
messagesObject.getLastMessages(100, new CallbackListener<List<Message>>() {
_215
@Override
_215
public void onSuccess(List<Message> messageList) {
_215
messageAdapter.setMessages(messageList);
_215
setMessageInputEnabled(true);
_215
messageTextEdit.requestFocus();
_215
handler.onSuccess();
_215
}
_215
});
_215
}
_215
}
_215
_215
private void setUpListeners() {
_215
sendButton.setOnClickListener(new View.OnClickListener() {
_215
@Override
_215
public void onClick(View v) {
_215
sendMessage();
_215
}
_215
});
_215
messageTextEdit.setOnEditorActionListener(new TextView.OnEditorActionListener() {
_215
@Override
_215
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
_215
return false;
_215
}
_215
});
_215
}
_215
_215
private void sendMessage() {
_215
String messageText = getTextInput();
_215
if (messageText.length() == 0) {
_215
return;
_215
}
_215
Message.Options messageOptions = Message.options().withBody(messageText);
_215
this.messagesObject.sendMessage(messageOptions, null);
_215
clearTextInput();
_215
}
_215
_215
private void setMessageInputEnabled(final boolean enabled) {
_215
mainActivity.runOnUiThread(new Runnable() {
_215
@Override
_215
public void run() {
_215
MainChatFragment.this.sendButton.setEnabled(enabled);
_215
MainChatFragment.this.messageTextEdit.setEnabled(enabled);
_215
}
_215
});
_215
}
_215
_215
private String getTextInput() {
_215
return messageTextEdit.getText().toString();
_215
}
_215
_215
private void clearTextInput() {
_215
messageTextEdit.setText("");
_215
}
_215
_215
@Override
_215
public void onMessageAdded(Message message) {
_215
messageAdapter.addMessage(message);
_215
}
_215
_215
@Override
_215
public void onMemberAdded(Member member) {
_215
StatusMessage statusMessage = new JoinedStatusMessage(member.getIdentity());
_215
this.messageAdapter.addStatusMessage(statusMessage);
_215
}
_215
_215
@Override
_215
public void onMemberDeleted(Member member) {
_215
StatusMessage statusMessage = new LeftStatusMessage(member.getIdentity());
_215
this.messageAdapter.addStatusMessage(statusMessage);
_215
}
_215
_215
@Override
_215
public void onMessageUpdated(Message message, Message.UpdateReason updateReason) {
_215
}
_215
_215
@Override
_215
public void onMessageDeleted(Message message) {
_215
}
_215
_215
@Override
_215
public void onMemberUpdated(Member member, Member.UpdateReason updateReason) {
_215
}
_215
_215
@Override
_215
public void onTypingStarted(Channel channel, Member member) {
_215
}
_215
_215
@Override
_215
public void onTypingEnded(Channel channel, Member member) {
_215
}
_215
_215
@Override
_215
public void onSynchronizationChanged(Channel channel) {
_215
}
_215
_215
_215
}

If we can join other channels, we'll need some way for a super user to create new channels (and delete old ones).


We use an input dialog so the user can type the name of the new channel. The only restriction here is that the user can't create a channel called "General Channel". Other than that, creating a channel is as simple as using the channelBuilder as shown and providing at the very least a channel type.

You can provide additional parameters to the builder as we did with a general channel to set a unique name. There's a list of methods you can use in the client library API docs(link takes you to an external page).

app/src/main/java/com/twilio/twiliochat/chat/channels/ChannelManager.java


_339
package com.twilio.twiliochat.chat.channels;
_339
_339
import android.content.res.Resources;
_339
import android.os.Handler;
_339
import android.os.Looper;
_339
import android.util.Log;
_339
_339
import com.twilio.chat.CallbackListener;
_339
import com.twilio.chat.Channel;
_339
import com.twilio.chat.Channel.ChannelType;
_339
import com.twilio.chat.ChannelDescriptor;
_339
import com.twilio.chat.Channels;
_339
import com.twilio.chat.ChatClient;
_339
import com.twilio.chat.ChatClientListener;
_339
import com.twilio.chat.ErrorInfo;
_339
import com.twilio.chat.Paginator;
_339
import com.twilio.chat.StatusListener;
_339
import com.twilio.chat.User;
_339
import com.twilio.twiliochat.R;
_339
import com.twilio.twiliochat.application.TwilioChatApplication;
_339
import com.twilio.twiliochat.chat.ChatClientManager;
_339
import com.twilio.twiliochat.chat.accesstoken.AccessTokenFetcher;
_339
import com.twilio.twiliochat.chat.listeners.TaskCompletionListener;
_339
_339
import java.util.ArrayList;
_339
import java.util.Collections;
_339
import java.util.List;
_339
_339
public class ChannelManager implements ChatClientListener {
_339
private static ChannelManager sharedManager = new ChannelManager();
_339
public Channel generalChannel;
_339
private ChatClientManager chatClientManager;
_339
private ChannelExtractor channelExtractor;
_339
private List<Channel> channels;
_339
private Channels channelsObject;
_339
private ChatClientListener listener;
_339
private String defaultChannelName;
_339
private String defaultChannelUniqueName;
_339
private Handler handler;
_339
private Boolean isRefreshingChannels = false;
_339
_339
private ChannelManager() {
_339
this.chatClientManager = TwilioChatApplication.get().getChatClientManager();
_339
this.channelExtractor = new ChannelExtractor();
_339
this.listener = this;
_339
defaultChannelName = getStringResource(R.string.default_channel_name);
_339
defaultChannelUniqueName = getStringResource(R.string.default_channel_unique_name);
_339
handler = setupListenerHandler();
_339
}
_339
_339
public static ChannelManager getInstance() {
_339
return sharedManager;
_339
}
_339
_339
public List<Channel> getChannels() {
_339
return channels;
_339
}
_339
_339
public String getDefaultChannelName() {
_339
return this.defaultChannelName;
_339
}
_339
_339
public void leaveChannelWithHandler(Channel channel, StatusListener handler) {
_339
channel.leave(handler);
_339
}
_339
_339
public void deleteChannelWithHandler(Channel channel, StatusListener handler) {
_339
channel.destroy(handler);
_339
}
_339
_339
public void populateChannels(final LoadChannelListener listener) {
_339
if (this.chatClientManager == null || this.isRefreshingChannels) {
_339
return;
_339
}
_339
this.isRefreshingChannels = true;
_339
_339
handler.post(new Runnable() {
_339
@Override
_339
public void run() {
_339
channelsObject = chatClientManager.getChatClient().getChannels();
_339
_339
channelsObject.getPublicChannelsList(new CallbackListener<Paginator<ChannelDescriptor>>() {
_339
@Override
_339
public void onSuccess(Paginator<ChannelDescriptor> channelDescriptorPaginator) {
_339
extractChannelsFromPaginatorAndPopulate(channelDescriptorPaginator, listener);
_339
}
_339
});
_339
_339
}
_339
});
_339
}
_339
_339
private void extractChannelsFromPaginatorAndPopulate(final Paginator<ChannelDescriptor> channelsPaginator,
_339
final LoadChannelListener listener) {
_339
channels = new ArrayList<>();
_339
ChannelManager.this.channels.clear();
_339
channelExtractor.extractAndSortFromChannelDescriptor(channelsPaginator,
_339
new TaskCompletionListener<List<Channel>, String>() {
_339
@Override
_339
public void onSuccess(List<Channel> channels) {
_339
ChannelManager.this.channels.addAll(channels);
_339
Collections.sort(ChannelManager.this.channels, new CustomChannelComparator());
_339
ChannelManager.this.isRefreshingChannels = false;
_339
chatClientManager.addClientListener(ChannelManager.this);
_339
listener.onChannelsFinishedLoading(ChannelManager.this.channels);
_339
}
_339
_339
@Override
_339
public void onError(String errorText) {
_339
Log.e(TwilioChatApplication.TAG,"Error populating channels: " + errorText);
_339
}
_339
});
_339
}
_339
_339
public void createChannelWithName(String name, final StatusListener handler) {
_339
this.channelsObject
_339
.channelBuilder()
_339
.withFriendlyName(name)
_339
.withType(ChannelType.PUBLIC)
_339
.build(new CallbackListener<Channel>() {
_339
@Override
_339
public void onSuccess(final Channel newChannel) {
_339
handler.onSuccess();
_339
}
_339
_339
@Override
_339
public void onError(ErrorInfo errorInfo) {
_339
handler.onError(errorInfo);
_339
}
_339
});
_339
}
_339
_339
public void joinOrCreateGeneralChannelWithCompletion(final StatusListener listener) {
_339
channelsObject.getChannel(defaultChannelUniqueName, new CallbackListener<Channel>() {
_339
@Override
_339
public void onSuccess(Channel channel) {
_339
ChannelManager.this.generalChannel = channel;
_339
if (channel != null) {
_339
joinGeneralChannelWithCompletion(listener);
_339
} else {
_339
createGeneralChannelWithCompletion(listener);
_339
}
_339
}
_339
});
_339
}
_339
_339
private void joinGeneralChannelWithCompletion(final StatusListener listener) {
_339
if (generalChannel.getStatus() == Channel.ChannelStatus.JOINED) {
_339
listener.onSuccess();
_339
return;
_339
}
_339
this.generalChannel.join(new StatusListener() {
_339
@Override
_339
public void onSuccess() {
_339
listener.onSuccess();
_339
}
_339
_339
@Override
_339
public void onError(ErrorInfo errorInfo) {
_339
listener.onError(errorInfo);
_339
}
_339
});
_339
}
_339
_339
private void createGeneralChannelWithCompletion(final StatusListener listener) {
_339
this.channelsObject
_339
.channelBuilder()
_339
.withFriendlyName(defaultChannelName)
_339
.withUniqueName(defaultChannelUniqueName)
_339
.withType(ChannelType.PUBLIC)
_339
.build(new CallbackListener<Channel>() {
_339
@Override
_339
public void onSuccess(final Channel channel) {
_339
ChannelManager.this.generalChannel = channel;
_339
ChannelManager.this.channels.add(channel);
_339
joinGeneralChannelWithCompletion(listener);
_339
}
_339
_339
@Override
_339
public void onError(ErrorInfo errorInfo) {
_339
listener.onError(errorInfo);
_339
}
_339
});
_339
}
_339
_339
public void setChannelListener(ChatClientListener listener) {
_339
this.listener = listener;
_339
}
_339
_339
private String getStringResource(int id) {
_339
Resources resources = TwilioChatApplication.get().getResources();
_339
return resources.getString(id);
_339
}
_339
_339
@Override
_339
public void onChannelAdded(Channel channel) {
_339
if (listener != null) {
_339
listener.onChannelAdded(channel);
_339
}
_339
}
_339
_339
@Override
_339
public void onChannelUpdated(Channel channel, Channel.UpdateReason updateReason) {
_339
if (listener != null) {
_339
listener.onChannelUpdated(channel, updateReason);
_339
}
_339
}
_339
_339
@Override
_339
public void onChannelDeleted(Channel channel) {
_339
if (listener != null) {
_339
listener.onChannelDeleted(channel);
_339
}
_339
}
_339
_339
@Override
_339
public void onChannelSynchronizationChange(Channel channel) {
_339
if (listener != null) {
_339
listener.onChannelSynchronizationChange(channel);
_339
}
_339
}
_339
_339
@Override
_339
public void onError(ErrorInfo errorInfo) {
_339
if (listener != null) {
_339
listener.onError(errorInfo);
_339
}
_339
}
_339
_339
@Override
_339
public void onClientSynchronization(ChatClient.SynchronizationStatus synchronizationStatus) {
_339
_339
}
_339
_339
@Override
_339
public void onChannelJoined(Channel channel) {
_339
_339
}
_339
_339
@Override
_339
public void onChannelInvited(Channel channel) {
_339
_339
}
_339
_339
@Override
_339
public void onUserUpdated(User user, User.UpdateReason updateReason) {
_339
if (listener != null) {
_339
listener.onUserUpdated(user, updateReason);
_339
}
_339
}
_339
_339
@Override
_339
public void onUserSubscribed(User user) {
_339
_339
}
_339
_339
@Override
_339
public void onUserUnsubscribed(User user) {
_339
_339
}
_339
_339
@Override
_339
public void onNewMessageNotification(String s, String s1, long l) {
_339
_339
}
_339
_339
@Override
_339
public void onAddedToChannelNotification(String s) {
_339
_339
}
_339
_339
@Override
_339
public void onInvitedToChannelNotification(String s) {
_339
_339
}
_339
_339
@Override
_339
public void onRemovedFromChannelNotification(String s) {
_339
_339
}
_339
_339
@Override
_339
public void onNotificationSubscribed() {
_339
_339
}
_339
_339
@Override
_339
public void onNotificationFailed(ErrorInfo errorInfo) {
_339
_339
}
_339
_339
@Override
_339
public void onConnectionStateChange(ChatClient.ConnectionState connectionState) {
_339
_339
}
_339
_339
@Override
_339
public void onTokenExpired() {
_339
refreshAccessToken();
_339
}
_339
_339
@Override
_339
public void onTokenAboutToExpire() {
_339
refreshAccessToken();
_339
}
_339
_339
private void refreshAccessToken() {
_339
AccessTokenFetcher accessTokenFetcher = chatClientManager.getAccessTokenFetcher();
_339
accessTokenFetcher.fetch(new TaskCompletionListener<String, String>() {
_339
@Override
_339
public void onSuccess(String token) {
_339
ChannelManager.this.chatClientManager.getChatClient().updateToken(token, new StatusListener() {
_339
@Override
_339
public void onSuccess() {
_339
Log.d(TwilioChatApplication.TAG, "Successfully updated access token.");
_339
}
_339
});
_339
}
_339
_339
@Override
_339
public void onError(String message) {
_339
Log.e(TwilioChatApplication.TAG,"Error trying to fetch token: " + message);
_339
}
_339
});
_339
}
_339
_339
private Handler setupListenerHandler() {
_339
Looper looper;
_339
Handler handler;
_339
if ((looper = Looper.myLooper()) != null) {
_339
handler = new Handler(looper);
_339
} else if ((looper = Looper.getMainLooper()) != null) {
_339
handler = new Handler(looper);
_339
} else {
_339
throw new IllegalArgumentException("Channel Listener must have a Looper.");
_339
}
_339
return handler;
_339
}
_339
}

Cool, we now know how to create a channel, let's say that we created a lot of channels by mistake. In that case, it would be useful to be able to delete those unnecessary channels. That's our next step!


Deleting a channel is easier than creating one. The application lets the user delete the channel they are currently joined to through a menu option. In order to delete the channel from Twilio you have to call the destroy method on the channel you are trying to delete. But you still need to provide a StatusListener to handle the success or failure of the operation.

app/src/main/java/com/twilio/twiliochat/chat/channels/ChannelManager.java


_339
package com.twilio.twiliochat.chat.channels;
_339
_339
import android.content.res.Resources;
_339
import android.os.Handler;
_339
import android.os.Looper;
_339
import android.util.Log;
_339
_339
import com.twilio.chat.CallbackListener;
_339
import com.twilio.chat.Channel;
_339
import com.twilio.chat.Channel.ChannelType;
_339
import com.twilio.chat.ChannelDescriptor;
_339
import com.twilio.chat.Channels;
_339
import com.twilio.chat.ChatClient;
_339
import com.twilio.chat.ChatClientListener;
_339
import com.twilio.chat.ErrorInfo;
_339
import com.twilio.chat.Paginator;
_339
import com.twilio.chat.StatusListener;
_339
import com.twilio.chat.User;
_339
import com.twilio.twiliochat.R;
_339
import com.twilio.twiliochat.application.TwilioChatApplication;
_339
import com.twilio.twiliochat.chat.ChatClientManager;
_339
import com.twilio.twiliochat.chat.accesstoken.AccessTokenFetcher;
_339
import com.twilio.twiliochat.chat.listeners.TaskCompletionListener;
_339
_339
import java.util.ArrayList;
_339
import java.util.Collections;
_339
import java.util.List;
_339
_339
public class ChannelManager implements ChatClientListener {
_339
private static ChannelManager sharedManager = new ChannelManager();
_339
public Channel generalChannel;
_339
private ChatClientManager chatClientManager;
_339
private ChannelExtractor channelExtractor;
_339
private List<Channel> channels;
_339
private Channels channelsObject;
_339
private ChatClientListener listener;
_339
private String defaultChannelName;
_339
private String defaultChannelUniqueName;
_339
private Handler handler;
_339
private Boolean isRefreshingChannels = false;
_339
_339
private ChannelManager() {
_339
this.chatClientManager = TwilioChatApplication.get().getChatClientManager();
_339
this.channelExtractor = new ChannelExtractor();
_339
this.listener = this;
_339
defaultChannelName = getStringResource(R.string.default_channel_name);
_339
defaultChannelUniqueName = getStringResource(R.string.default_channel_unique_name);
_339
handler = setupListenerHandler();
_339
}
_339
_339
public static ChannelManager getInstance() {
_339
return sharedManager;
_339
}
_339
_339
public List<Channel> getChannels() {
_339
return channels;
_339
}
_339
_339
public String getDefaultChannelName() {
_339
return this.defaultChannelName;
_339
}
_339
_339
public void leaveChannelWithHandler(Channel channel, StatusListener handler) {
_339
channel.leave(handler);
_339
}
_339
_339
public void deleteChannelWithHandler(Channel channel, StatusListener handler) {
_339
channel.destroy(handler);
_339
}
_339
_339
public void populateChannels(final LoadChannelListener listener) {
_339
if (this.chatClientManager == null || this.isRefreshingChannels) {
_339
return;
_339
}
_339
this.isRefreshingChannels = true;
_339
_339
handler.post(new Runnable() {
_339
@Override
_339
public void run() {
_339
channelsObject = chatClientManager.getChatClient().getChannels();
_339
_339
channelsObject.getPublicChannelsList(new CallbackListener<Paginator<ChannelDescriptor>>() {
_339
@Override
_339
public void onSuccess(Paginator<ChannelDescriptor> channelDescriptorPaginator) {
_339
extractChannelsFromPaginatorAndPopulate(channelDescriptorPaginator, listener);
_339
}
_339
});
_339
_339
}
_339
});
_339
}
_339
_339
private void extractChannelsFromPaginatorAndPopulate(final Paginator<ChannelDescriptor> channelsPaginator,
_339
final LoadChannelListener listener) {
_339
channels = new ArrayList<>();
_339
ChannelManager.this.channels.clear();
_339
channelExtractor.extractAndSortFromChannelDescriptor(channelsPaginator,
_339
new TaskCompletionListener<List<Channel>, String>() {
_339
@Override
_339
public void onSuccess(List<Channel> channels) {
_339
ChannelManager.this.channels.addAll(channels);
_339
Collections.sort(ChannelManager.this.channels, new CustomChannelComparator());
_339
ChannelManager.this.isRefreshingChannels = false;
_339
chatClientManager.addClientListener(ChannelManager.this);
_339
listener.onChannelsFinishedLoading(ChannelManager.this.channels);
_339
}
_339
_339
@Override
_339
public void onError(String errorText) {
_339
Log.e(TwilioChatApplication.TAG,"Error populating channels: " + errorText);
_339
}
_339
});
_339
}
_339
_339
public void createChannelWithName(String name, final StatusListener handler) {
_339
this.channelsObject
_339
.channelBuilder()
_339
.withFriendlyName(name)
_339
.withType(ChannelType.PUBLIC)
_339
.build(new CallbackListener<Channel>() {
_339
@Override
_339
public void onSuccess(final Channel newChannel) {
_339
handler.onSuccess();
_339
}
_339
_339
@Override
_339
public void onError(ErrorInfo errorInfo) {
_339
handler.onError(errorInfo);
_339
}
_339
});
_339
}
_339
_339
public void joinOrCreateGeneralChannelWithCompletion(final StatusListener listener) {
_339
channelsObject.getChannel(defaultChannelUniqueName, new CallbackListener<Channel>() {
_339
@Override
_339
public void onSuccess(Channel channel) {
_339
ChannelManager.this.generalChannel = channel;
_339
if (channel != null) {
_339
joinGeneralChannelWithCompletion(listener);
_339
} else {
_339
createGeneralChannelWithCompletion(listener);
_339
}
_339
}
_339
});
_339
}
_339
_339
private void joinGeneralChannelWithCompletion(final StatusListener listener) {
_339
if (generalChannel.getStatus() == Channel.ChannelStatus.JOINED) {
_339
listener.onSuccess();
_339
return;
_339
}
_339
this.generalChannel.join(new StatusListener() {
_339
@Override
_339
public void onSuccess() {
_339
listener.onSuccess();
_339
}
_339
_339
@Override
_339
public void onError(ErrorInfo errorInfo) {
_339
listener.onError(errorInfo);
_339
}
_339
});
_339
}
_339
_339
private void createGeneralChannelWithCompletion(final StatusListener listener) {
_339
this.channelsObject
_339
.channelBuilder()
_339
.withFriendlyName(defaultChannelName)
_339
.withUniqueName(defaultChannelUniqueName)
_339
.withType(ChannelType.PUBLIC)
_339
.build(new CallbackListener<Channel>() {
_339
@Override
_339
public void onSuccess(final Channel channel) {
_339
ChannelManager.this.generalChannel = channel;
_339
ChannelManager.this.channels.add(channel);
_339
joinGeneralChannelWithCompletion(listener);
_339
}
_339
_339
@Override
_339
public void onError(ErrorInfo errorInfo) {
_339
listener.onError(errorInfo);
_339
}
_339
});
_339
}
_339
_339
public void setChannelListener(ChatClientListener listener) {
_339
this.listener = listener;
_339
}
_339
_339
private String getStringResource(int id) {
_339
Resources resources = TwilioChatApplication.get().getResources();
_339
return resources.getString(id);
_339
}
_339
_339
@Override
_339
public void onChannelAdded(Channel channel) {
_339
if (listener != null) {
_339
listener.onChannelAdded(channel);
_339
}
_339
}
_339
_339
@Override
_339
public void onChannelUpdated(Channel channel, Channel.UpdateReason updateReason) {
_339
if (listener != null) {
_339
listener.onChannelUpdated(channel, updateReason);
_339
}
_339
}
_339
_339
@Override
_339
public void onChannelDeleted(Channel channel) {
_339
if (listener != null) {
_339
listener.onChannelDeleted(channel);
_339
}
_339
}
_339
_339
@Override
_339
public void onChannelSynchronizationChange(Channel channel) {
_339
if (listener != null) {
_339
listener.onChannelSynchronizationChange(channel);
_339
}
_339
}
_339
_339
@Override
_339
public void onError(ErrorInfo errorInfo) {
_339
if (listener != null) {
_339
listener.onError(errorInfo);
_339
}
_339
}
_339
_339
@Override
_339
public void onClientSynchronization(ChatClient.SynchronizationStatus synchronizationStatus) {
_339
_339
}
_339
_339
@Override
_339
public void onChannelJoined(Channel channel) {
_339
_339
}
_339
_339
@Override
_339
public void onChannelInvited(Channel channel) {
_339
_339
}
_339
_339
@Override
_339
public void onUserUpdated(User user, User.UpdateReason updateReason) {
_339
if (listener != null) {
_339
listener.onUserUpdated(user, updateReason);
_339
}
_339
}
_339
_339
@Override
_339
public void onUserSubscribed(User user) {
_339
_339
}
_339
_339
@Override
_339
public void onUserUnsubscribed(User user) {
_339
_339
}
_339
_339
@Override
_339
public void onNewMessageNotification(String s, String s1, long l) {
_339
_339
}
_339
_339
@Override
_339
public void onAddedToChannelNotification(String s) {
_339
_339
}
_339
_339
@Override
_339
public void onInvitedToChannelNotification(String s) {
_339
_339
}
_339
_339
@Override
_339
public void onRemovedFromChannelNotification(String s) {
_339
_339
}
_339
_339
@Override
_339
public void onNotificationSubscribed() {
_339
_339
}
_339
_339
@Override
_339
public void onNotificationFailed(ErrorInfo errorInfo) {
_339
_339
}
_339
_339
@Override
_339
public void onConnectionStateChange(ChatClient.ConnectionState connectionState) {
_339
_339
}
_339
_339
@Override
_339
public void onTokenExpired() {
_339
refreshAccessToken();
_339
}
_339
_339
@Override
_339
public void onTokenAboutToExpire() {
_339
refreshAccessToken();
_339
}
_339
_339
private void refreshAccessToken() {
_339
AccessTokenFetcher accessTokenFetcher = chatClientManager.getAccessTokenFetcher();
_339
accessTokenFetcher.fetch(new TaskCompletionListener<String, String>() {
_339
@Override
_339
public void onSuccess(String token) {
_339
ChannelManager.this.chatClientManager.getChatClient().updateToken(token, new StatusListener() {
_339
@Override
_339
public void onSuccess() {
_339
Log.d(TwilioChatApplication.TAG, "Successfully updated access token.");
_339
}
_339
});
_339
}
_339
_339
@Override
_339
public void onError(String message) {
_339
Log.e(TwilioChatApplication.TAG,"Error trying to fetch token: " + message);
_339
}
_339
});
_339
}
_339
_339
private Handler setupListenerHandler() {
_339
Looper looper;
_339
Handler handler;
_339
if ((looper = Looper.myLooper()) != null) {
_339
handler = new Handler(looper);
_339
} else if ((looper = Looper.getMainLooper()) != null) {
_339
handler = new Handler(looper);
_339
} else {
_339
throw new IllegalArgumentException("Channel Listener must have a Looper.");
_339
}
_339
return handler;
_339
}
_339
}

That's it! We've built an Android application with the Twilio Chat SDK. Now you are more than prepared to set up your own chat application.


If you're a Java developer working with Twilio, you might want to check out these other tutorials:

Two-Factor Authentication with Authy

Learn to implement two-factor authentication (2FA) in your web app with Twilio-powered Authy. 2FA helps further secure your users' data by validating more than just a password.

Did this help?

did-this-help page anchor

Thanks for checking out this tutorial! If you have any feedback to share with us, we'd love to hear it. Tweet @twilio(link takes you to an external page) to let us know what you think.


Rate this page: