Using third-party API to implement Chat features

One of the main features of the app will be that of letting all users who have the app installed to communicate with each other through a group chat. In order to achieve this, a third party API had to be used. We have seen a large number of apps being developed in the recent past, all of which help users to connect with each other across different mediums. Apps like Facebook Messenger, Whatsapp, Viber and Couple, bring users of the same app together to be able to communicate with each other. One would be surprised to learn that its rather quite easy to develop a chat app of your own, with some great features which can differ from the already existing solutions on the market.

The chat application can be achieved by building a simple group chat app using Java sockets. This is not the only way to build a chat app, but using this method, one could buld a simple group chat easily. An other differnet approach to achieve such a solution would be using push notifications instead of sockets, but this is a more complex solution and not necessarily needed in our work.

This chat room will only be accessible to users who have the app installed on their mobile device or tablet. Therefore it won’t be accessible through a web application.

We will start by creating the necessary xml files for the application to be visually seen in our development environment. Primarily we will need to create the following xml files:

bg_message_from.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
 android:shape="rectangle" >
<!-- view background color -->
 <solid android:color="@color/bg_msg_from" >
 </solid>
<corners android:radius="2dp" >
 </corners>
</shape>

bg_message_you.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
 android:shape="rectangle" >
<!-- view background color -->
 <solid android:color="@color/bg_msg_you" >
 </solid>
<corners android:radius="2dp" >
 </corners>
</shape>

The above xml files contain details for the different colors of chats coming from other participants and other chats coming from the user of the app. Next are the actual chat bubbles:

left_bubble.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 android:paddingBottom="5dp"
 android:paddingTop="5dp"
 android:paddingLeft="10dp">
<TextView
 android:id="@+id/lblMsgFrom"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:textSize="12dp"
 android:textColor="@color/lblFromName"
 android:textStyle="italic"
 android:padding="5dp"/>
<TextView
 android:id="@+id/txtMsg"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:textSize="16dp"
 android:layout_marginRight="80dp"
 android:textColor="@color/title_gray"
 android:paddingLeft="10dp"
 android:paddingRight="10dp"
 android:paddingTop="5dp"
 android:paddingBottom="5dp"
 android:background="@drawable/bg_msg_from"/>
</LinearLayout>

right_bubble.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:gravity="right"
 android:orientation="vertical"
 android:paddingBottom="5dp"
 android:paddingRight="10dp"
 android:paddingTop="5dp" >
<TextView
 android:id="@+id/lblMsgFrom"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:padding="5dp"
 android:textColor="@color/lblFromName"
 android:textSize="12dp"
 android:textStyle="italic" />
<TextView
 android:id="@+id/txtMsg"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginLeft="80dp"
 android:background="@drawable/bg_msg_you"
 android:paddingBottom="5dp"
 android:paddingLeft="10dp"
 android:paddingRight="10dp"
 android:paddingTop="5dp"
 android:textColor="@color/white"
 android:textSize="16dp" />
</LinearLayout>

Finally, the xml containing the complete fragment content to hold all details for the chat screen in it:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@drawable/tile_bg"
 android:orientation="vertical" >
<ListView
 android:id="@+id/list_view_messages"
 android:layout_width="fill_parent"
 android:layout_height="0dp"
 android:layout_weight="1"
 android:background="@null"
 android:divider="@null"
 android:transcriptMode="alwaysScroll"
 android:stackFromBottom="true">
 </ListView>
<LinearLayout
 android:id="@+id/llMsgCompose"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:background="@color/white"
 android:orientation="horizontal"
 android:weightSum="3" >
<EditText
 android:id="@+id/inputMsg"
 android:layout_width="0dp"
 android:layout_height="fill_parent"
 android:layout_weight="2"
 android:background="@color/bg_msg_input"
 android:textColor="@color/text_msg_input"
 android:paddingLeft="6dp"
 android:paddingRight="6dp"/>
<Button
 android:id="@+id/btnSend"
 android:layout_width="0dp"
 android:layout_height="wrap_content"
 android:layout_weight="1"
 android:background="@color/bg_btn_join"
 android:textColor="@color/white"
 android:text="Send" />
 </LinearLayout>
</LinearLayout>

Moving on to the java class, the following code shows the connection with the websocket. The code below will check for a new message and display it to the user:

private void sendMessageToServer(String message) {
 if (client != null && client.isConnected()) {
 client.send(message);
 }
 }
private void parseMessage(final String msg) {
try {
 JSONObject jObj = new JSONObject(msg);
 String flag = jObj.getString("flag");
 if (flag.equalsIgnoreCase(TAG_SELF)) {
String sessionId = jObj.getString("sessionId");
 utils.storeSessionId(sessionId);
Log.e(TAG, "Your session id: " + utils.getSessionId());
} else if (flag.equalsIgnoreCase(TAG_NEW)) {

 String name = jObj.getString("name");
 String message = jObj.getString("message");
 String onlineCount = jObj.getString("onlineCount");
showToast(name + message + ". Currently " + onlineCount
 + " people online!");
} else if (flag.equalsIgnoreCase(TAG_MESSAGE)) {

 String fromName = name;
 String message = jObj.getString("message");
 String sessionId = jObj.getString("sessionId");
 boolean isSelf = true;
 if (!sessionId.equals(utils.getSessionId())) {
 fromName = jObj.getString("name");
 isSelf = false;
 }
Message m = new Message(fromName, message, isSelf);
 appendMessage(m);
} else if (flag.equalsIgnoreCase(TAG_EXIT)) {

 String name = jObj.getString("name");
 String message = jObj.getString("message");
showToast(name + message);
 }
} catch (JSONException e) {
 e.printStackTrace();
 }
}
@Override
 public void onDestroy() {
 super.onDestroy();
if(client != null & client.isConnected()){
 client.disconnect();
 }
 }
 private void appendMessage(final Message m) {
 getActivity().runOnUiThread(new Runnable() {
private void showToast(final String message) {
getActivity().runOnUiThread(new Runnable() {
@Override
 public void run() {
 Toast.makeText(getActivity().getApplicationContext(), message,
 Toast.LENGTH_LONG).show();
 }
 });
}

In the same class, we need a method which loads the rootView in order to connect the xml file to the java code. The code is the following:

@Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
 View rootView = inflater.inflate(R.layout.fragment_forum, container,false);
btnSend = (Button) rootView.findViewById(R.id.btnSend);
 inputMsg = (EditText) rootView.findViewById(R.id.inputMsg);
 listViewMessages = (ListView) rootView.findViewById(R.id.list_view_messages);
utils = new Utils(getActivity().getApplicationContext());
btnSend.setOnClickListener(new View.OnClickListener() {
@Override
 public void onClick(View v) {

 sendMessageToServer(utils.getSendMessageJSON(inputMsg.getText()
 .toString()));
 inputMsg.setText("");
 }
 });
listMessages = new ArrayList<Message>();
adapter = new MessagesListAdapter(getActivity(), listMessages);
 listViewMessages.setAdapter(adapter);
 client = new WebSocket(URI.create(WsConfig.URL_WEBSOCKET
 + URLEncoder.encode(name)), new WebSocket.Listener() {
public void onConnect() {
}
public void onMessage(String message) {
 Log.d(TAG, String.format("Got string message! %s", message));
parseMessage(message);
}
public void onMessage(byte[] data) {
 Log.d(TAG, String.format("Got binary message! %s",
 bytesToHex(data)));
// Message will be in JSON format
 parseMessage(bytesToHex(data));
 }
 @Override
 public void onDisconnect(int code, String reason) {
String message = String.format(Locale.US,
 "Disconnected! Code: %d Reason: %s", code, reason);
showToast(message);
 utils.storeSessionId(null);
 }
@Override
 public void onError(Exception error) {
 Log.e(TAG, "Error! : " + error);
showToast("Error! : " + error);
 }
}, null);
client.connect();
return rootView;
 }

Below is then the construction of the message in a seperate class:

public class Message {
 private String fromName, message;
 private boolean isSelf;
public Message() {
 }
public Message(String fromName, String message, boolean isSelf) {
 this.fromName = fromName;
 this.message = message;
 this.isSelf = isSelf;
 }
public String getFromName() {
 return fromName;
 }
public void setFromName(String fromName) {
 this.fromName = fromName;
 }
public String getMessage() {
 return message;
 }
public void setMessage(String message) {
 this.message = message;
 }
public boolean isSelf() {
 return isSelf;
 }
public void setSelf(boolean isSelf) {
 this.isSelf = isSelf;
 }
}

The message list adapter takes care of the listview and the chat history, as per the following code:

public class MessagesListAdapter extends BaseAdapter {
private Context context;
 private List<Message> messagesItems;
public MessagesListAdapter(Context context, List<Message> navDrawerItems) {
 this.context = context;
 this.messagesItems = navDrawerItems;
 }
@Override
 public int getCount() {
 return messagesItems.size();
 }
@Override
 public Object getItem(int position) {
 return messagesItems.get(position);
 }
@Override
 public long getItemId(int position) {
 return position;
 }
@Override
 public View getView(int position, View convertView, ViewGroup parent) {
Message m = messagesItems.get(position);
LayoutInflater mInflater = (LayoutInflater) context
 .getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
 if (messagesItems.get(position).isSelf()) {
 convertView = mInflater.inflate(R.layout.list_item_message_right,
 null);
 } else {
 // message belongs to other person, load the left aligned layout
 convertView = mInflater.inflate(R.layout.list_item_message_left,
 null);
 }
TextView lblFrom = (TextView) convertView.findViewById(R.id.lblMsgFrom);
 TextView txtMsg = (TextView) convertView.findViewById(R.id.txtMsg);
txtMsg.setText(m.getMessage());
 lblFrom.setText(m.getFromName());

return convertView;
}
}

On the server side, we need to configure the server to accept web sockets and process the messages throughout all users in the group chat. This is done by configuring a connection to the server, which has already been installed on the machine accepting the connection:

public class WSConfig {
 public static final String URL_WEBSOCKET = "ws://192.168.0.102:8080/WebMobileGroupChatServer/chat?name=";
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.