Using Firebase to create a “simple” Chat application in Android

Filip Babic
COBE
Published in
12 min readFeb 27, 2016

--

Just before you dive into reading, an updated version of Firebase demo project, and an article behind it is at the end of the article, so make sure you give it a look :)

Few days ago I’ve taken up writing a Chat application for the students here at COBE, so that we’d have a simple app for chatting and asking questions if we ever get stuck on a problem(Once when fully done). Rather than having a fully built backend to which we’d send, and from which we’d receive messages, I decided to use Firebase — an easy to use, real time database, that stores its data in JSON format. Even though Firebase provides full API and usage documentation, I’ve found it lacking detail when trying to apply it to an architecture pattern like MVP, so I’ve decided to try and explain how I understood the implementation in Android. Also I’ve extended the MVP pattern with a new layer, explicitly for Firebase — the Interactors.

Creating the basic Firebase template

I won’t go into detail about creating an account on Firebase, and the “5 minute quick-start”, I’ll just head straight into the implementation.

First we look at which directories we need in our Firebase template, for example — Firebase creates users in a separate database, and on creation the information stored is an e-mail, a password (which you cannot directly observe), and a unique UID (randomly generated key which sticks with the user throughout it’s whole lifetime), so if we wanted to store a username, we couldn’t… Thats why we need “Users” as a directory in our template, which will contain a username and maybe an avatar, so we can have some concrete information stored.

We could also have a directory named curentUsers which will contain all the users currently logged in our Chat app. We definitely need a directory Messages, to store our messages in.

So our three directories are Users, currentUsers, Messages…
The links to them look like this:
“https://<your-firebase>/currentUsers/”
“https://<your-firebase>/Users/”
“https://<your-firebase>/Messages/”

These are the directory references we use when we want to add/retrieve data, and basically everything we will need for the user and messaging system to work.

Let’s switch to actual Android talk. If you’ve imported the Firebase dependency into Gradle, you should have all Firebase client features available… Our Chat app will have 4 screens:
— Main screen for choosing the entry option (Login or Register) and showing the number of currently logged in users
— Login for the actual user authentication
— Registration screen, in which we create new Users
— Chat screen (Which can show a Chat fragment or a ListOfUsers fragment)

The Main Screen

Here we look for which entry point the user wants (Register or Log-in) and we display the number of current users in a TextView.

The MainActivityPresenter:

public class MainActivityPresenterImpl implements MainPresenter {
private final MainView mainView;
private final MainInteractor interactor;

public MainActivityPresenterImpl(MainView view) {
this.mainView = view;
interactor = new MainInteractor(this);
}


@Override
public void receiveRequest() {
interactor.receiveRequest();
}

@Override
public String getNumberOfUsers(long numberOfUsers) {
return "Online users: " + String.valueOf(numberOfUsers);
}

@Override
public void sendNumberOfChildren(long number) {
mainView.setNumberOfUsersTextView(getNumberOfUsers(number));
}
}

The MainInteractor:

public class MainInteractor implements MInteractor {
private final Firebase mainRef = new Firebase("https://<your-firebase>/currentUsers");
private final MainPresenter presenter;

public MainInteractor(MainPresenter pre) {
this.presenter = pre;
}

@Override
public void receiveRequest() {
mainRef.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
presenter.sendNumberOfChildren(dataSnapshot.getChildrenCount());
});
}
}

What happens here? In the interactor we have a Firebase reference, whose constructor parameter is a link (the currentUsers directory), and we add a listener to the reference, which sends one request to the Firebase directory currentUsers, and receives a DataSnapshot — a special Firebase feature… The snapshot in its essence is a list of all the data objects in the said directory, so if we do dataSnapshot.getChildrenCount() we just get a number of objects currently in the directory, which equals to the number of Users online! We display it in a TextView, and the User sees how many of his peers are online. Quite simple, yet powerful, as we use this principle of data requests in all aspects of communicating with our Firebase.

The Registration Screen

We’ve seen the code for the Main Screen in the previous section, but here’s what it looks like. Also by pressing register we go through a 3-step process, first we choose a Username, which if taken displays an error, otherwise we proceed to the Emoji Fragment, in which we choose our own “avatar”, next we proceed to the account details screen, in which we finish our registration, unless the e-mail is taken, in which case we receive an error as well, so here are the screens:

We have a few simple EditTexts, one for the Username, one for the E-mail, and one for the Password.. A grid of emoji to choose from(currently a single row, will add more), and a progress bar to display a spin animation during the authentication progress. The Button “Register” takes the values combined in from Fragments, and sends them to the Presenter:

public class FirebaseUserRegisterPresenterImpl implements FirebaseUserRegisterPresenter {
private final RegisterView registerView;
private final RegisterInteractor interactor;

public FirebaseUserRegisterPresenterImpl(RegisterView view) {
this.registerView = view;
this.interactor = new RegisterInteractor(this);
}


@Override
public void receiveRegisterRequest(String username, String email, String password, String emoji) {
interactor.receiveRegisterRequest(username, email, password, emoji);
registerView.spinProgressBar();
}

@Override
public void onFailure() {
registerView.onFailure();
registerView.stopProgressBar();
}

@Override
public void onSuccess() {
registerView.onSuccess();
registerView.stopProgressBar();
}
}

The Interactor:

public class RegisterInteractor implements RInteractor {
private Firebase userRef = new Firebase("https://<your-firebase>/Users/");
private final FirebaseUserRegisterPresenter presenter;

public RegisterInteractor(FirebaseUserRegisterPresenter pre) {
this.presenter = pre;
}

@Override
public void receiveRegisterRequest(final String username, String email, String password, final String emoji) {
userRef.createUser(email, password, new Firebase.ValueResultHandler<Map<String, Object>>() {
@Override
public void onSuccess(Map<String, Object> stringObjectMap) {
String uid = stringObjectMap.get("uid").toString();
userRef = new Firebase("https://<your-firebase>/Users/" + uid);
userRef.setValue(createUser(username, emoji));
presenter.onSuccess();
}

@Override
public void onError(FirebaseError firebaseError) {
presenter.onFailure();
}
});
}

@Override
public Map<String, Object> createUser(String username, String emoji) {
Map<String, Object> user = new HashMap<>();
user.put("username", username);
user.put("emoji", emoji);
return user;
}
}

Here we have a few new features :
— The .createUser() , .push() , and the .setValue() methods
— The User UID

The .createUser() — creates Users! In a separate database, so when we do create a user, we also need create its object in the /Users directory (in order to look it up). This is done by “Pushing”
The said .push() “pushes” deeper into the directory, by creating a sub-directory with a random generated key for it’s name, but before that we attach the UID to the link, so we can compare the directories to the UIDs of the Users. The UID is a key randomly generated, and by using it as the sub-directory name (and a parameter in the User object) we can later on find which username matches a specific UID, and retrieve the username after logging in, or even delete a Child of currentUsers (log the user out).
The .setValue() method adds an object(or objects) to a directory, so we can simply store whatever data we want.

The Login Screen

The login screen UI is pretty simple: two EditTexts(email and password) and a Log-in button, plus a spinner Progress bar to animate the process a bit.

What happens when the user presses Log-in?

This part is tricky, we know that our users are in a separate database, so when we log a user in, how to we know by which username he/she goes?

Thats the whole purpose of /Users directory as mentioned before. Also by naming it after the User UID, we can simply look for the directory with a matching UID (if for example we want to extrapolate certain pieces of information from a specific user). Also if we name the objects by UID, we can enter the object with the said UID, and remove it in Chat activity’s onDestroy() —an extremely simple way of logging the user out.

The Login Presenter:

public class FirebaseLoginPresenterImpl implements FirebaseLoginPresenter {
private final LoginView loginView;
private final LoginInteractor interactor;


public FirebaseLoginPresenterImpl(LoginView view) {
this.loginView = view;
interactor = new LoginInteractor(this);
}

@Override
public void receiveUserLogin(String email, String password) {
loginView.spinProgressBar();
interactor.attemptToLogIn(email, password);

}

@Override
public void onFailure() {
loginView.stopProgressBar();
loginView.onFailure();
}

@Override
public void onSuccess(String user, String uid) {
loginView.stopProgressBar();
loginView.logTheUserIn(user, uid);
}
}

It receives an email, and a password, shows a spinning progress bar until the request is complete, and calls View methods given the result:
— Successful login sends the User’s username, and the UID to an Intent, which starts the ChatActivity
— Failed login warns the user with a Toast

The Interactor:

public class LoginInteractor implements LInteractor {
private Firebase userRef = new Firebase("https://<your-firebase>/Users/");
private final FirebaseLoginPresenter presenter;

public LoginInteractor(FirebaseLoginPresenter pre) {
this.presenter = pre;
}

@Override
public void attemptToLogIn(String email, String password) {
userRef.authWithPassword(email, password, new Firebase.AuthResultHandler() {
@Override
public void onAuthenticated(final AuthData authData) {
userRef = new Firebase("https://<your-firebase>/Users/" + authData.getUid()); //retrieve the user data
userRef.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
Firebase loggedUser = new Firebase("https://<your-firebase>/currentUsers/" + authData.getUid()); //helps us log the user out later on
loggedUser.setValue(createUser(user.getUsername(), user.getEmoji()));
presenter.onSuccess(user.getUsername(), authData.getUid(), user.getEmoji());
}

@Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}

@Override
public void onAuthenticationError(FirebaseError firebaseError) {
presenter.onFailure();
}
});
}

@Override
public Map<String, Object> createUser(String user, String emoji) {
Map<String, Object> userToCreate = new HashMap<>();
userToCreate.put("username", user);
userToCreate.put("emoji", emoji);
return userToCreate;
}
}

If the user authentication is successful, we get the username for the said user, and send it to a chat screen, but before that we add the user to the /currentUsers directory so we can simply see who logged in. The AuthData here is received by default, and serves to show some Firebase specific data about the user(like the UID, a special Key generated by the authentication..)

The Chat Screen

The ChatActivity utilises 2 Fragments, one for the messaging service, and one for displaying a list of active Users. By clicking on the List Menu icon once, we replace the Messaging Fragment with the List Fragment, and by clicking it once more, we pop the BackStack(and go back!).

The issue here is that we receive all our data from Firebase, meaning we can’t implement Firebase in our Views, but the ListView/RecyclerView adapters are Android View components too, so how do we proceed here?

The answer once again is MVP (+Interactors)! A good architecture reflects itself in the components it’s implemented in, meaning we can write our Adapters in MVP too, them being the View component, which have a Presenter that sends new values into the ListView items (and requests the said values from an Interactor). Since the values are generated by an Interactor, which has a reference to Firebase — we’re able to decouple the Android — from Java — from Backend.

The Adapter:

public class CustomMessageRecyclerAdapter extends RecyclerView.Adapter<CustomMessageRecyclerAdapter.ViewHolder> implements MessageAdapterView {
private final ArrayList<Message> mMessageList = new ArrayList<>();
private final String user;
private final MessagePresenterImpl presenter;

public CustomMessageRecyclerAdapter(String username) {
this.user = username;
presenter = new MessagePresenterImpl(this);
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_message, parent, false);
return new ViewHolder(v);
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Message current = mMessageList.get(position);
if (current.getAuthor().equals(user)) {
holder.mAuthorTextView.setText("You");
} else {
holder.mAuthorTextView.setText(current.getAuthor());
}
holder.mMessageTextView.setText(current.getMessage());
holder.mEmojiTextView.setText(current.getEmoji());
}

@Override
public int getItemCount() {
return mMessageList.size();
}

@Override
public void addItem(Message message) {
mMessageList.add(message);
notifyDataSetChanged();
}

@Override
public void request() {
presenter.requestMessages();
}

public class ViewHolder extends RecyclerView.ViewHolder {
private TextView mAuthorTextView;
private TextView mMessageTextView;
private TextView mEmojiTextView;

public ViewHolder(View itemView) {
super(itemView);
mAuthorTextView = (TextView) itemView.findViewById(R.id.message_author);
mMessageTextView = (TextView) itemView.findViewById(R.id.message_value);
mEmojiTextView = (TextView) itemView.findViewById(R.id.message_emoji);
}
}
}

This is very basic, we have a method which inflates our ViewHolder, one that fills the said holder, a method for requesting the messages from Firebase, and one that adds a message to the ArrayList if there is a new message to display.

The Presenter:

public class MessagePresenterImpl implements MessagePresenter {
private final MessageAdapterView adapterView;
private final MessageInteractor interactor;

public MessagePresenterImpl(MessageAdapterView view) {
this.adapterView = view;
this.interactor = new MessageInteractor(this);
}

@Override
public void sendMessageToAdapter(Message message) {
adapterView.addItem(message);
}

@Override
public void requestMessages() {
interactor.request();
}
}

The Interactor:

public class MessageInteractor {
private final MessagePresenter presenter;
private final Firebase mMessagesRef = new Firebase("https://<your-firebase>/messages");
private final Query mMessageQuery;


public MessageInteractor(MessagePresenter pre) {
this.presenter = pre;
this.mMessageQuery = mMessagesRef.orderByValue().limitToLast(100);
}

public void request() {
mMessageQuery.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
presenter.sendMessageToAdapter(dataSnapshot.getValue(Message.class));
}
//some more auto-generated methods

The Adapter needs a new message, it tells the Presenter to request messages, but it’s not the Presenter’s job, so he tells the Interactor to request them from Firebase, by doing this we have a clean structure and flow of Data, fully independent, so by changing the View, we don’t have to change everything, we just tweak the Data POJOs, the Presenters and Interactors don’t have to change what they do, the requests stay the same! So if we switch the amount of data, just simply add more fields to a POJO, if we want to display it differently, just simply change the View (by adding more widgets).
The Query just means a request, .orderByValue() means we get the Objects(values) there, .limitToLast(100) means we always get the last 100 messages. Though if the chat is active for a while, all messages(even after 100) will be displayed, until the message fragment is destroyed/restarted.

Also in our ChatActivity’s onDestroy we send the UID to an interactor(through a presenter) to delete the user from currentUsers (log him out).

public class ChatLoginInteractor implements CLoginInteractor {
@Override
public void logTheUserOut(String uid) {
Firebase userRef = new Firebase("https://<your-firebase>/currentUsers/" + uid);
userRef.removeValue(); //removes the Child from Firebase
}
}

How it works, step by step.

The Firebase library for Android is very well built, the documentation is a bit tricky to understand but the basic principles are easy to get hold of if you dig around and try to combine stuff.

— Firebase reference is just a link to the directory in which you want to make changes, requests, or simply add new Data
— The Listeners gives us “Rx-like” features, they constantly observe on new children being added(each object in a directory is a child) and we can work with their Data
— DataSnapshot is a list of current Values in a single Directory
— AuthData is like a Bundle of all the Data for a certain user/request, the UID, the unique Key…
— Firebase uses Jackson parsing, so your POJOs need empty constructors, and getters/setters generated
— You don’t really need special REST clients, as the DataSnapshot feature can do all the Data parsing for you by using .getValue(POJO.class)
— Real time… All the requests and pushes to Firebase are extremely fast since all the data is formatted as JSON objects
— Before using any references to your Firebase, you have to call Firebase.setAndroidContext(this) in each Activity’s onCreate() method

Conclusion

Firebase is an extremely powerful tool for simple Backend databases, it’s very fast and simple to use on small projects, but can be used even for a bit more complex Apps like this Chat app.
It’s cross platform, so you can build Firebase apps for Android, iOS, and JS with full support (JS supports Angular, React and Node I believe) and use a single Firebase template across all 3 main platforms.

My project isn’t as detailed as it could be, the design could receive some love, there could be more queries and features, but the business logic is working, so you can have a fully working small private chat app! Thanks for reading, and stay tuned in for more Articles in which I explain using the MVP approach on different projects and Android features. :)

Update

Check out my new article regarding Firebase :

It contains the project featured in this article as a standalone fully updated code, as well as a new project with all new Firebase features implemented(with implemented MVP architecture, tests, and Dagger 2)!

If not, atleast check out this featured project on github:

Filip Babić is an Android developer at COBE and a Computer Science student at FERIT, Osijek. He is a huge Kotlin fan, and occasionally holds mini work-shops and Kotlin meet-ups in Osijek. He likes to learn new stuff, play DnD and write about the things he loves the most. When he’s not coding, writing about coding, learning to code, or learning others how to code, he nurtures his inner nerdiness by gaming and watching fantasy shows.

--

--

Android developer. Praise Kotlin :] Keen and enthusiastic learner and mentor, passionate about teaching and helping others. GDE @ Android, Osijek, Croatia.