How to make a personal Chat Application like WhatsApp

Written by pradyumandixit | Published 2019/01/21
Tech Story Tags: android | whatsapp | android-app-development | chat | messaging

TLDRvia the TL;DR App

Let’s make a personal, secure android app like WhatsApp 😎.

Do you use WhatsApp/Facebook Messenger? Do you have interest in Java or Android? Are you interested in making a personal chat application like WhatsApp?

If yes, then you’re at the perfect place.

It will be your own app with no ads, no privacy issues and nothing. You would be in almost full control.

So, here we will learn making a simple chat application that has login/signup feature, and chat messages implementation that you can send to talk with your friends.

For this we will be using Firebase-Realtime-Database, as the backend to store the chats and send them to other device.

You can find the entire code for the app here.

First off, let’s understand our app using a simple flow chart, that’d help us in our way, if we are stuck at some point.

Our android app → User signs up or logs in → User can choose the friend he wants to send message to → User can type and send the message → The friend would receive the message on his device.

Internally we would store the message on our database and then send it to the desired user.

This is a demo purpose app and so we would only be doing this much in it. But you can add much more cooler aspects in the app, once you’re done with this.

Firebase provides tons of amazing facilities you can use in your app like ML Kit, Realtime notifications and lot more.

Setting Up Firebase

First of all, create a firebase account. Follow following steps:

Here the name is for demo purposes, you can use whatever name or titles you want, and go with them.

  1. Go to Firebase website from this link, and create a firebase account to start with. Go to Firebase console and Create a New Project by clicking on the “Create New Project” Button as shown below.

Firebase

2. Give the Project name and country you are currently in, Once you are done click on “Create Project” button.

Project

3. In the next screen choose “Add Firebase to your Android app” and then add the package details and Debug signing certificate SHA-1 key( This is required if you want to enable certain features like Dynamic Links, Invites, and Google Sign-In etc. otherwise it is an optional field).

Android chatting app

This will download the google-services.json file. Download it to your computer. We will add it to our android app later.

This is required because the default security rules for the Android Firebase allows only authenticated users to read and write.

Then click on the Database tab in the Firebase Menu.

It shows the root of the JSON tree, we would be adding a child node called listItems and then will add each item under it.

When we add data to the JSON tree, it becomes a new node in the existing JSON structure with an associated key.

Also copy the URL of database. And don’t forget to change them in the codes below.

You can check the Rules tab to see or change the security rules for reading and writing on Android Firebase Database. Below figure shows the default settings.

Firebase rules

You can change these to true, if you want free unauthenticated access to your Firebase.

Once you are done with this, Let’s create our Android chat Application that will connect to Firebase Database we have just created.

Now that we’re almost done setting up Firebase, let’s dive into some android code.

First make a new Android Project in your IDE like android studio, and name your first activity as Register.

Needless to say, we’re naming this activity like that, because we have more activities in the app that have different functions.

Add the following code in your Register activity.

public class Register extends AppCompatActivity {EditText username, password;Button registerButton;String user, pass;TextView login;

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

    username = (EditText)findViewById(R.id.username);  
    password = (EditText)findViewById(R.id.password);  
    registerButton = (Button)findViewById(R.id.registerButton);  
    login = (TextView)findViewById(R.id.login);  

    Firebase._setAndroidContext_(this);  

    login.setOnClickListener(new View.OnClickListener() {  
        @Override  
        public void onClick(View v) {  
            startActivity(new Intent(Register.this, Login.class));  
        }  
    });  

    registerButton.setOnClickListener(new View.OnClickListener() {  
        @Override  
        public void onClick(View v) {  
            user = username.getText().toString();  
            pass = password.getText().toString();  

            if(user.equals("")){  
                username.setError("can't be blank");  
            }  
            else if(pass.equals("")){  
                password.setError("can't be blank");  
            }  
            else if(!user.matches("\[A-Za-z0-9\]+")){  
                username.setError("only alphabet or number allowed");  
            }  
            else if(user.length()<5){  
                username.setError("at least 5 characters long");  
            }  
            else if(pass.length()<5){  
                password.setError("at least 5 characters long");  
            }  
            else {  
                final ProgressDialog pd = new ProgressDialog(Register.this);  
                pd.setMessage("Loading...");  
                pd.show();  

                String url = "https://chatapp-60323.firebaseio.com/users.json";  

                StringRequest request = new StringRequest(Request.Method._GET_, url, new Response.Listener<String>(){  
                    @Override  
                    public void onResponse(String s) {  
                        Firebase reference = new Firebase("https://chatapp-60323.firebaseio.com/users");  

                        if(s.equals("null")) {  
                            reference.child(user).child("password").setValue(pass);  
                            Toast._makeText_(Register.this, "registration successful", Toast._LENGTH\_LONG_).show();  
                        }  
                        else {  
                            try {  
                                JSONObject obj = new JSONObject(s);  

                                if (!obj.has(user)) {  
                                    reference.child(user).child("password").setValue(pass);  
                                    Toast._makeText_(Register.this, "registration successful", Toast._LENGTH\_LONG_).show();  
                                } else {  
                                    Toast._makeText_(Register.this, "username already exists", Toast._LENGTH\_LONG_).show();  
                                }  

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

                        pd.dismiss();  
                    }  

                },new Response.ErrorListener(){  
                    @Override  
                    public void onErrorResponse(VolleyError volleyError) {  
                        System._out_.println("" + volleyError );  
                        pd.dismiss();  
                    }  
                });  

                RequestQueue rQueue = Volley._newRequestQueue_(Register.this);  
                rQueue.add(request);  
            }  
        }  
    });  
}  

}

This code sets up your registering in your firebase database.

As you might wonder, the Firebase database is a NoSQL which essentially means, you don’t need to query stuff using SQL.

Firebase uses reference method to retrieve and put the values.

So we have to decide on what and how to make our database structure. Here we also are going to make user sign up and sign in using a custom username and password, so we’ve to keep this in mind too.

The database structure that I’ve come up for this app is based on simplicity. We keep authentication details in a parent node named “users” and messages in another parent node named “messages”.

Inside messages we also keep track of who messaged whom by the order of the usernames.

So in image, this structure would look something like this in action.

Used for authentication:

Users database structure

Used for messaging:

Messages database structure

Please keep in mind, that this a demo app for tutorial purposes, so here I have not used any method to keep this secure. But this can be made very secure using Firebase and you may do so after building this, on your own.

Let’s go back to our android code again. So yes, errors.

Do you see errors in your project and tons of red lines, after adding that code in the register activity?

Don’t worry, I am not buffing about anything. The errors will go away.

First let’s add our dependencies. You might also want to connect your app to the firebase account you’ve made.

To connect it to your firebase project, go to Tools → Firebase → Realtime database → Save and retrieve data → Connect your app to Firebase.

Just sign in again and select your project, and it should get connected. You might also want to follow other steps in the same tab, like adding database to your app.

Or, you can just copy paste these set of dependencies in your module app of the project.

dependencies {implementation fileTree(dir: 'libs', include: ['*.jar'])implementation 'com.android.support:appcompat-v7:28.0.0'implementation 'com.firebase:firebase-client-android:2.5.2'implementation 'com.android.volley:volley:1.0.0'implementation 'com.android.support.constraint:constraint-layout:1.1.3'implementation 'com.google.firebase:firebase-database:16.0.5'implementation 'com.google.firebase:firebase-auth:16.1.0'testImplementation 'junit:junit:4.12'androidTestImplementation 'com.android.support.test🏃1.0.2'androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'}

Now, just hit alt+Enter, on the errors, and most of them should go away. Others will also, as we proceed.

Let’s add the Login activity code, which looks something like this:

public class Login extends AppCompatActivity {TextView registerUser;EditText username, password;Button loginButton;String user, pass;

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

    registerUser = findViewById(R.id.register);  
    username = findViewById(R.id.username);  
    password = findViewById(R.id.password);  
    loginButton = findViewById(R.id.loginButton);  

    registerUser.setOnClickListener(new View.OnClickListener() {  
        @Override  
        public void onClick(View v) {  
            startActivity(new Intent(Login.this, Register.class));  
        }  
    });  

    loginButton.setOnClickListener(new View.OnClickListener() {  
        @Override  
        public void onClick(View v) {  
            user = username.getText().toString();  
            pass = password.getText().toString();  

            if(user.equals("")){  
                username.setError("can't be blank");  
            }  
            else if(pass.equals("")){  
                password.setError("can't be blank");  
            }  
            else{  
                String url = "https://chatapp-60323.firebaseio.com/users.json";  
                final ProgressDialog pd = new ProgressDialog(Login.this);  
                pd.setMessage("Loading...");  
                pd.show();  

                StringRequest request = new StringRequest(Request.Method._GET_, url, new Response.Listener<String>(){  
                    @Override  
                    public void onResponse(String s) {  
                        if(s.equals("null")){  
                            Toast._makeText_(Login.this, "user not found", Toast._LENGTH\_LONG_).show();  
                        }  
                        else{  
                            try {  
                                JSONObject obj = new JSONObject(s);  

                                if(!obj.has(user)){  
                                    Toast._makeText_(Login.this, "user not found", Toast._LENGTH\_LONG_).show();  
                                }  
                                else if(obj.getJSONObject(user).getString("password").equals(pass)){  
                                    UserDetails._username_ \= user;  
                                    UserDetails._password_ \= pass;  
                                    startActivity(new Intent(Login.this, Users.class));  
                                }  
                                else {  
                                    Toast._makeText_(Login.this, "incorrect password", Toast._LENGTH\_LONG_).show();  
                                }  
                            } catch (JSONException e) {  
                                e.printStackTrace();  
                            }  
                        }  

                        pd.dismiss();  
                    }  
                },new Response.ErrorListener(){  
                    @Override  
                    public void onErrorResponse(VolleyError volleyError) {  
                        System._out_.println("" + volleyError);  
                        pd.dismiss();  
                    }  
                });  

                RequestQueue rQueue = Volley._newRequestQueue_(Login.this);  
                rQueue.add(request);  
            }  

        }  
    });  
}  

}

We may also now add our chat activity code, which will be the place where the chats are seen.

public class Chat extends AppCompatActivity {LinearLayout layout;RelativeLayout layout_2;ImageView sendButton;EditText messageArea;ScrollView scrollView;Firebase reference1, reference2;

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

    layout = findViewById(R.id.layout1);  
    layout\_2 = findViewById(R.id.layout2);  
    sendButton = findViewById(R.id.sendButton);  
    messageArea = findViewById(R.id.messageArea);  
    scrollView = findViewById(R.id.scrollView);  

    Firebase._setAndroidContext_(this);  
    reference1 = new Firebase("https://chatapp-60323.firebaseio.com/messages/" + UserDetails._username_ \+ "\_" + UserDetails._chatWith_);  
    reference2 = new Firebase("https://chatapp-60323.firebaseio.com/messages/" + UserDetails._chatWith_ \+ "\_" + UserDetails._username_);  

    sendButton.setOnClickListener(new View.OnClickListener() {  
        @Override  
        public void onClick(View v) {  
            String messageText = messageArea.getText().toString();  

            if(!messageText.equals("")){  
                Map<String, String> map = new HashMap<String, String>();  
                map.put("message", messageText);  
                map.put("user", UserDetails._username_);  
                reference1.push().setValue(map);  
                reference2.push().setValue(map);  
                messageArea.setText("");  
            }  
        }  
    });  

    reference1.addChildEventListener(new ChildEventListener() {  
        @Override  
        public void onChildAdded(DataSnapshot dataSnapshot, String s) {  
            Map map = dataSnapshot.getValue(Map.class);  
            String message = map.get("message").toString();  
            String userName = map.get("user").toString();  

            if(userName.equals(UserDetails._username_)){  
                addMessageBox(message, 1);  
            }  
            else{  
                addMessageBox(message, 2);  
            }  
        }  

        @Override  
        public void onChildChanged(DataSnapshot dataSnapshot, String s) {  

        }  

        @Override  
        public void onChildRemoved(DataSnapshot dataSnapshot) {  

        }  

        @Override  
        public void onChildMoved(DataSnapshot dataSnapshot, String s) {  

        }  

        @Override  
        public void onCancelled(FirebaseError firebaseError) {  

        }  
    });  
}  

public void addMessageBox(String message, int type){  
    TextView textView = new TextView(Chat.this);  
    textView.setText(message);  

    LinearLayout.LayoutParams lp2 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams._WRAP\_CONTENT_, ViewGroup.LayoutParams._WRAP\_CONTENT_);  
    lp2.weight = 7.0f;  

    if(type == 1) {  
        lp2.gravity = Gravity._LEFT_;  
        textView.setBackgroundResource(R.drawable.bubble\_in);  
    }  
    else{  
        lp2.gravity = Gravity._RIGHT_;  
        textView.setBackgroundResource(R.drawable.bubble\_out);  
    }  
    textView.setLayoutParams(lp2);  
    layout.addView(textView);  
    scrollView.fullScroll(View._FOCUS\_DOWN_);  
}  

}

Also, the activity where we would see the list of users for the app.

public class Users extends AppCompatActivity {ListView usersList;TextView noUsersText;ArrayList<String> al = new ArrayList<>();int totalUsers = 0;ProgressDialog pd;

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

    usersList = (ListView)findViewById(R.id.usersList);  
    noUsersText = (TextView)findViewById(R.id.noUsersText);  

    pd = new ProgressDialog(Users.this);  
    pd.setMessage("Loading...");  
    pd.show();  

    String url = "https://chatapp-60323.firebaseio.com/users.json";  

    StringRequest request = new StringRequest(Request.Method._GET_, url, new Response.Listener<String>(){  
        @Override  
        public void onResponse(String s) {  
            doOnSuccess(s);  
        }  
    },new Response.ErrorListener(){  
        @Override  
        public void onErrorResponse(VolleyError volleyError) {  
            System._out_.println("" + volleyError);  
        }  
    });  

    RequestQueue rQueue = Volley._newRequestQueue_(Users.this);  
    rQueue.add(request);  

    usersList.setOnItemClickListener(new AdapterView.OnItemClickListener() {  
        @Override  
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {  
            UserDetails._chatWith_ \= al.get(position);  
            startActivity(new Intent(Users.this, Chat.class));  
        }  
    });  
}  

public void doOnSuccess(String s){  
    try {  
        JSONObject obj = new JSONObject(s);  

        Iterator i = obj.keys();  
        String key = "";  

        while(i.hasNext()){  
            key = i.next().toString();  

            if(!key.equals(UserDetails._username_)) {  
                al.add(key);  
            }  

            totalUsers++;  
        }  

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

    if(totalUsers <=1){  
        noUsersText.setVisibility(View._VISIBLE_);  
        usersList.setVisibility(View._GONE_);  
    }  
    else{  
        noUsersText.setVisibility(View._GONE_);  
        usersList.setVisibility(View._VISIBLE_);  
        usersList.setAdapter(new ArrayAdapter<String>(this, android.R.layout._simple\_list\_item\_1_, al));  
    }  

    pd.dismiss();  
}  

}

Now let’s make a simple Java class to help get details in a better way, that is:

public class UserDetails {static String username = "";static String password = "";static String chatWith = "";}

And yes, we’re all done with our app…

Oh wait, you’re still getting errors?

That should not be happening, I don’t know what went wrong…

Nah, just kidding 😆

We’re yet to add the UI code, and that’s what the errors might also be stating.

So let’s first add UI code for register activity.

<?xml version="1.0" encoding="utf-8"?><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/back"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.pd.chatapp.Register"android:orientation="vertical"android:gravity="center">

<TextView  
    android:layout\_width="match\_parent"  
    android:layout\_height="wrap\_content"  
    android:text="@string/register"  
    android:textSize="30sp"  
    android:gravity="center"  
    android:layout\_marginBottom="20dp"/>  

<EditText  
    android:id="@+id/username"  
    android:layout\_width="365dp"  
    android:layout\_height="49dp"  
    android:layout\_marginBottom="10dp"  
    android:background="#000000"  
    android:hint="@string/enter\_username"  
    android:inputType="text"  
    android:maxLines="1"  
    android:textColor="#ffffff" />  

<EditText  
    android:id="@+id/password"  
    android:layout\_width="363dp"  
    android:layout\_height="50dp"  
    android:layout\_marginBottom="10dp"  
    android:background="#000000"  
    android:hint="@string/enter\_password"  
    android:inputType="textPassword"  
    android:maxLines="1"  
    android:textColor="#ffffff" />  

<Button  
    android:layout\_width="match\_parent"  
    android:layout\_height="wrap\_content"  
    android:text="@string/register"  
    android:id="@+id/registerButton"  
    android:layout\_marginBottom="20dp"/>  

<TextView  
    android:layout\_width="match\_parent"  
    android:layout\_height="wrap\_content"  
    android:text="@string/click\_here\_to\_login"  
    android:textSize="20sp"  
    android:gravity="center"  
    android:id="@+id/login"/>  

</LinearLayout>

For chat activity.

<?xml version="1.0" encoding="utf-8"?><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/back"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"android:orientation="vertical"tools:context="com.pd.chatapp.Chat">

<ScrollView  
    android:layout\_width="match\_parent"  
    android:layout\_weight="20"  
    android:layout\_height="0dp"  
    android:id="@+id/scrollView">  
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        android:id="@+id/layout2"  
        android:layout\_width="match\_parent"  
        android:layout\_height="wrap\_content">  
        <LinearLayout  
            android:layout\_width="match\_parent"  
            android:layout\_height="wrap\_content"  
            android:orientation="vertical"  
            android:id="@+id/layout1">  
        </LinearLayout>  
    </RelativeLayout>  
</ScrollView>  

<include  
    layout="@layout/message\_place"  
    android:layout\_width="match\_parent"  
    android:layout\_height="wrap\_content"  
    android:gravity="bottom"  
    android:layout\_marginTop="5dp"/>  

</LinearLayout>

Also for our login activity.

<?xml version="1.0" encoding="utf-8"?><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:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:background="@drawable/back"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.pd.chatapp.Login"android:orientation="vertical"android:gravity="center">

<TextView  
    android:layout\_width="match\_parent"  
    android:layout\_height="wrap\_content"  
    android:layout\_marginBottom="20dp"  
    android:gravity="center"  
    android:text="@string/login"  
    android:textSize="30sp" />  

<EditText  
    android:id="@+id/username"  
    android:layout\_width="362dp"  
    android:layout\_height="49dp"  
    android:layout\_marginBottom="10dp"  
    android:background="#000000"  
    android:hint="@string/enter\_username"  
    android:inputType="text"  
    android:maxLines="1"  
    android:textColor="#ffffff" />  

<EditText  
    android:id="@+id/password"  
    android:layout\_width="358dp"  
    android:layout\_height="49dp"  
    android:layout\_marginBottom="10dp"  
    android:background="#000000"  
    android:hint="@string/enter\_password"  
    android:inputType="textPassword"  
    android:maxLines="1"  
    android:textColor="#ffffff" />  

<Button  
    android:id="@+id/loginButton"  
    android:layout\_width="match\_parent"  
    android:layout\_height="wrap\_content"  
    android:layout\_marginBottom="20dp"  
    android:text="@string/login" />  

<TextView  
    android:layout\_width="match\_parent"  
    android:layout\_height="wrap\_content"  
    android:text="@string/click\_here\_to\_register"  
    android:textSize="20sp"  
    android:gravity="center"  
    android:id="@+id/register"/>  

</LinearLayout>

And let’s not forget the user’s place activity.

<?xml version="1.0" encoding="utf-8"?><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:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.pd.chatapp.Users"android:orientation="vertical">

<TextView  
    android:layout\_width="match\_parent"  
    android:layout\_height="wrap\_content"  
    android:text="@string/no\_users\_found"  
    android:id="@+id/noUsersText"  
    android:visibility="gone"/>  

<ListView  
    android:layout\_width="match\_parent"  
    android:layout\_height="wrap\_content"  
    android:id="@+id/usersList"/>  

</LinearLayout>

Also for a better view, we’ve made message area in a different layout file. So make a new xml file, with name message_place.xml.

And add the following code to it.

<?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="bottom"android:orientation="horizontal">

<EditText  
    android:layout\_width="match\_parent"  
    android:layout\_height="wrap\_content"  
    android:layout\_weight="1"  
    android:textColorHint="#CFD8DC"  
    android:textColor="#CFD8DC"  
    android:hint="@string/write\_a\_message"  
    android:id="@+id/messageArea"  
    android:maxHeight="80dp"  
    />  

<ImageView  
    android:layout\_width="match\_parent"  
    android:layout\_height="wrap\_content"  
    android:layout\_weight="4"  
    android:padding="4dp"  
    android:src="@android:drawable/ic\_menu\_send"  
    android:id="@+id/sendButton"/>  

</LinearLayout>

What? You’re still getting some errors?

Don’t worry that’s because you’re missing the background and other images or resources we’ve used. You may find all of them on my GitHub account repository.

The source code of the app.

Hell, yes! Now we’re really fully done. Fully done app should greet you with a screen like this:

Android app

Made everything worth the effort, right?

Now what are you waiting for? Chat with your friends, using your own app.

Go and show off!!

Read my previous post about making a machine learning android game from scratch.


Published by HackerNoon on 2019/01/21