Socket.IO -The Android Way

Socket.io is event-based. Both server and client emitting an event and listening to events from each other. (It is like we set an OnClickListener for our button in android.) A simple HTTP handshake takes place at the beginning of a Socket.IO connection.

What is the difference between socket IO and WebSocket?

It provides the Connection over TCP, while Socket.io is a library to abstract the WebSocket connections. WebSocket doesn’t have fallback options, while Socket.io supports fallback. WebSocket is the technology, while Socket.io is a library for WebSockets.

Important Notice- This blog covers only the client-side part where we implement sockets in android for the server part I am using the Heroku URL of the tutorial or where I learned it.If you wanna learn SErver Side to there are different and every nice article available where you can easily write the code in NOdeJS and deploy it

  • io.on(‘connection’, function(socket)){-}: Server waiting for connections from clients and we set the callback. Once the connection is successful, this will return the socket with socket id. All the logic of the socket will be written in this callback function.
  • socket.on(‘EVENT_NAME’, CALLBACK): It listens to an event named ‘EVENT_NAME’ from a client.
  • socket.emit(‘EVENT_NAME’, JSON_DATA): It emits an event to the client.
  • socket.join(‘ROOM_NAME’): Joining room with the given name.
  • io.to(‘ROOM_NAME’).emit(‘EVENT_NAME’, JSON_DATA): It emits events to all sockets in the room.
  • socket.broadcast.to(‘ROOM_NAME’).emit(‘EVENT_NAME’, JSON_DATA): It emits an event to the entire sockets in the room except for the socket which is communicating with now. (This might be a little tricky to understand, for example, if you wrote a message in the chat room, everybody except you will get a refreshChatRoom event from the server.)

Now let’s get to work

Add User Activity

package com.demo.socket.socketdemo;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class AddUserActivity extends AppCompatActivity {

private Button setNickName;
private EditText userNickName;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_user);

userNickName = findViewById(R.id.userNickName);
setNickName = findViewById(R.id.setNickName);


userNickName.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}

@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (charSequence.toString().trim().length() > 0) {
setNickName.setEnabled(true);
Log.i(MainActivity.TAG, "onTextChanged: ABLED");
} else {
Log.i(MainActivity.TAG, "onTextChanged: DISABLED");
setNickName.setEnabled(false);
}
}

@Override
public void afterTextChanged(Editable editable) {
}
});


setNickName.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(AddUserActivity.this, MainActivity.class);
intent.putExtra("username", userNickName.getText().toString());
startActivity(intent);
}
});
}
}

MainActivity

package com.demo.socket.socketdemo;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;

import com.github.nkzawa.emitter.Emitter;
import com.github.nkzawa.socketio.client.IO;
import com.github.nkzawa.socketio.client.Socket;

import org.json.JSONException;
import org.json.JSONObject;

import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class MainActivity extends AppCompatActivity {

private EditText textField;
private ImageButton sendButton;

public static final String TAG = "MainActivity";
public static String uniqueId;

private String Username;

private Boolean hasConnection = false;

private ListView messageListView;
private MessageAdapter messageAdapter;

private Thread thread2;
private boolean startTyping = false;
private int time = 2;

private Socket mSocket;
{
try {
mSocket = IO.socket("https://powerful-plateau-14003.herokuapp.com");
} catch (URISyntaxException e) {}
}

@SuppressLint("HandlerLeak")
Handler handler2=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.i(TAG, "handleMessage: typing stopped " + startTyping);
if(time == 0){
setTitle("SocketIO");
Log.i(TAG, "handleMessage: typing stopped time is " + time);
startTyping = false;
time = 2;
}

}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Username = getIntent().getStringExtra("username");

uniqueId = UUID.randomUUID().toString();
Log.i(TAG, "onCreate: " + uniqueId);

if(savedInstanceState != null){
hasConnection = savedInstanceState.getBoolean("hasConnection");
}

if(hasConnection){

}else {
mSocket.connect();
mSocket.on("connect user", onNewUser);
mSocket.on("chat message", onNewMessage);
mSocket.on("on typing", onTyping);

JSONObject userId = new JSONObject();
try {
userId.put("username", Username + " Connected");
mSocket.emit("connect user", userId);
} catch (JSONException e) {
e.printStackTrace();
}
}

Log.i(TAG, "onCreate: " + hasConnection);
hasConnection = true;


Log.i(TAG, "onCreate: " + Username + " " + "Connected");

textField = findViewById(R.id.textField);
sendButton = findViewById(R.id.sendButton);
messageListView = findViewById(R.id.messageListView);

List<MessageFormat> messageFormatList = new ArrayList<>();
messageAdapter = new MessageAdapter(this, R.layout.item_message, messageFormatList);
messageListView.setAdapter(messageAdapter);

onTypeButtonEnable();
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("hasConnection", hasConnection);
}

public void onTypeButtonEnable(){
textField.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}

@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

JSONObject onTyping = new JSONObject();
try {
onTyping.put("typing", true);
onTyping.put("username", Username);
onTyping.put("uniqueId", uniqueId);
mSocket.emit("on typing", onTyping);
} catch (JSONException e) {
e.printStackTrace();
}

if (charSequence.toString().trim().length() > 0) {
sendButton.setEnabled(true);
} else {
sendButton.setEnabled(false);
}
}

@Override
public void afterTextChanged(Editable editable) {
}
});
}

Emitter.Listener onNewMessage = new Emitter.Listener() {
@Override
public void call(final Object... args) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.i(TAG, "run: ");
Log.i(TAG, "run: " + args.length);
JSONObject data = (JSONObject) args[0];
String username;
String message;
String id;
try {
username = data.getString("username");
message = data.getString("message");
id = data.getString("uniqueId");

Log.i(TAG, "run: " + username + message + id);

MessageFormat format = new MessageFormat(id, username, message);
Log.i(TAG, "run:4 ");
messageAdapter.add(format);
Log.i(TAG, "run:5 ");

} catch (Exception e) {
return;
}
}
});
}
};

Emitter.Listener onNewUser = new Emitter.Listener() {
@Override
public void call(final Object... args) {
runOnUiThread(new Runnable() {
@Override
public void run() {
int length = args.length;

if(length == 0){
return;
}
//Here i'm getting weird error..................///////run :1 and run: 0
Log.i(TAG, "run: ");
Log.i(TAG, "run: " + args.length);
String username =args[0].toString();
try {
JSONObject object = new JSONObject(username);
username = object.getString("username");
} catch (JSONException e) {
e.printStackTrace();
}
MessageFormat format = new MessageFormat(null, username, null);
messageAdapter.add(format);
messageListView.smoothScrollToPosition(0);
messageListView.scrollTo(0, messageAdapter.getCount()-1);
Log.i(TAG, "run: " + username);
}
});
}
};


Emitter.Listener onTyping = new Emitter.Listener() {
@Override
public void call(final Object... args) {
runOnUiThread(new Runnable() {
@Override
public void run() {
JSONObject data = (JSONObject) args[0];
Log.i(TAG, "run: " + args[0]);
try {
Boolean typingOrNot = data.getBoolean("typing");
String userName = data.getString("username") + " is Typing......";
String id = data.getString("uniqueId");

if(id.equals(uniqueId)){
typingOrNot = false;
}else {
setTitle(userName);
}

if(typingOrNot){

if(!startTyping){
startTyping = true;
thread2=new Thread(
new Runnable() {
@Override
public void run() {
while(time > 0) {
synchronized (this){
try {
wait(1000);
Log.i(TAG, "run: typing " + time);
} catch (InterruptedException e) {
e.printStackTrace();
}
time--;
}
handler2.sendEmptyMessage(0);
}

}
}
);
thread2.start();
}else {
time = 2;
}

}
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
};

private void addMessage(String username, String message) {

}

public void sendMessage(View view){
Log.i(TAG, "sendMessage: ");
String message = textField.getText().toString().trim();
if(TextUtils.isEmpty(message)){
Log.i(TAG, "sendMessage:2 ");
return;
}
textField.setText("");
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("message", message);
jsonObject.put("username", Username);
jsonObject.put("uniqueId", uniqueId);
} catch (JSONException e) {
e.printStackTrace();
}
Log.i(TAG, "sendMessage: 1"+ mSocket.emit("chat message", jsonObject));
}

@Override
public void onDestroy() {
super.onDestroy();

if(isFinishing()){
Log.i(TAG, "onDestroy: ");

JSONObject userId = new JSONObject();
try {
userId.put("username", Username + " DisConnected");
mSocket.emit("connect user", userId);
} catch (JSONException e) {
e.printStackTrace();
}

mSocket.disconnect();
mSocket.off("chat message", onNewMessage);
mSocket.off("connect user", onNewUser);
mSocket.off("on typing", onTyping);
Username = "";
messageAdapter.clear();
}else {
Log.i(TAG, "onDestroy: is rotating.....");
}

}


}

Message Adapter

package com.demo.socket.socketdemo;

import android.app.Activity;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import java.util.List;

public class MessageAdapter extends ArrayAdapter<MessageFormat> {
public MessageAdapter(Context context, int resource, List<MessageFormat> objects) {
super(context, resource, objects);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
Log.i(MainActivity.TAG, "getView:");

MessageFormat message = getItem(position);

if(TextUtils.isEmpty(message.getMessage())){


convertView = ((Activity) getContext()).getLayoutInflater().inflate(R.layout.user_connected, parent, false);

TextView messageText = convertView.findViewById(R.id.message_body);

Log.i(MainActivity.TAG, "getView: is empty ");
String userConnected = message.getUsername();
messageText.setText(userConnected);

}else if(message.getUniqueId().equals(MainActivity.uniqueId)){
Log.i(MainActivity.TAG, "getView: " + message.getUniqueId() + " " + MainActivity.uniqueId);


convertView = ((Activity) getContext()).getLayoutInflater().inflate(R.layout.my_message, parent, false);
TextView messageText = convertView.findViewById(R.id.message_body);
messageText.setText(message.getMessage());

}else {
Log.i(MainActivity.TAG, "getView: is not empty");

convertView = ((Activity) getContext()).getLayoutInflater().inflate(R.layout.their_message, parent, false);

TextView messageText = convertView.findViewById(R.id.message_body);
TextView usernameText = (TextView) convertView.findViewById(R.id.name);

messageText.setVisibility(View.VISIBLE);
usernameText.setVisibility(View.VISIBLE);

messageText.setText(message.getMessage());
usernameText.setText(message.getUsername());
}

return convertView;
}
}

Message Model Class

package com.demo.socket.socketdemo;

public class MessageFormat {

private String Username;
private String Message;
private String UniqueId;

public MessageFormat(String uniqueId, String username, String message) {
Username = username;
Message = message;
UniqueId = uniqueId;
}

public String getUsername() {
return Username;
}

public void setUsername(String username) {
Username = username;
}

public String getMessage() {
return Message;
}

public void setMessage(String message) {
Message = message;
}

public String getUniqueId() {
return UniqueId;
}

public void setUniqueId(String uniqueId) {
UniqueId = uniqueId;
}
}

Ta-da!!Hope that makes it easier for you. I haven't added XML you can design it the way you like. Keep Coding!!