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=";
}