Building a chat application for Android

Summary In this codelab, you'll learn how to build a chat application for Android using Firebase.
URL https://github.com/rohanraarora/Firebase-Codelab
Category Firebase
Feedback Link You can provide feedback on the content bundle and codelab here.

Content

Overview

In this codelab you’ll build a chat application for Android using Firebase and Android Studio.
We’ll allow users to log in with an email/password combination.

What you’ll learn

What you’ll need

Create a Firebase application

Duration: 0:10

The first step is to create a Firebase application. This will be the server-side component that our Android application talks to. 1. Go to the Firebase web site
2. Login or sign up
3. Manage the app that was automatically created for you
This app is on Firebase's free hacker plan. This plan is great for when you're developing your app on Firebase.
4. Any data that our Android application writes, will be visible in the Data tab
5. In the Login & Auth tab, enable Email & Password authentication

Create a project in Android Studio

Duration: 0:10

In this step we’ll create a project in Android Studio.
1. Start Android Studio and Start a new Android Studio project
2. You can name the project anything you want. But in the code below, we’ve named it Nanochat 
3. Set the minimum SDK to 15 (ICS) or higher. We've left it on 19 (KitKat) here.
4. Start with a Blank Activity
5. We’ll leave all the defaults for this activity
6. If the project outline is not visible on the left, click the 1:Project label
7. Open up the main activity, which can be found in app/res/layout/activity_main.xml. In this file the root element will be a RelativeLayout and in there will be a TextView. We won’t be using the TextView, so delete it (or leave it and put a welcome message in it).

We now have a blank project in Android Studio. Let’s wire our app up to Firebase!

Connect the Android project to Firebase

Duration: 0:15

Before we can start writing code that interacts with our Firebase database, we’ll need to make Android Studio aware that we’ll be using Firebase. We need to do this in two places: in the gradle build script and in our AndroidManifest.xml.

    compile 'com.firebase:firebase-client-android:2.3.0+'
    packagingOptions {  
        exclude 'META-INF/LICENSE'  
        exclude 'META-INF/LICENSE-FIREBASE.txt'  
        exclude 'META-INF/NOTICE'  
    }  
    <uses-permission android:name="android.permission.INTERNET" />  
    Firebase.setAndroidContext(this);

This code allows the Firebase client to keep its context.

    import com.firebase.client.Firebase;
    private Firebase mFirebaseRef;

that we initialize in onCreate:

    mFirebaseRef = new Firebase("https://<your-app>.firebaseio.com");

Be sure to replace <your-app> with the name of the Firebase app you created in the first section.

That’s all the setup that is required. Next up we’ll allow the user to enter a message in our app and send the message to Firebase.

Allow the user to send a message

Duration: 0:20

Now we can start sending data to Firebase! In this step we’ll allow the user to enter a message in a text box. When they then click the Send button, we will send the message to Firebase.

    <LinearLayout
        android:id="@+id/footer"
        android:layout_alignParentBottom="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <EditText
            android:id="@+id/message_text"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:inputType="textShortMessage" />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Send" />
    </LinearLayout>

Now we have an EditText, where the user can enter their chat message, and a Button that they can click to send the message.

    EditText mEditText;

and initialize it at the end of the onCreate method:

    mMessageEditText = (EditText) this.findViewById(R.id.message_text);
    public void onSendButtonClick(View v) {
        String message = mMessageEdit.getText().toString();
        Map<String,Object> values = new HashMap<>();
        values.put("name", "puf");
        values.put("message", message);
        mFirebaseRef.push().setValue(values);
        mMessageEdit.setText("");
    }

You will have to import the packages for some of these classes. Android Studio will tell you where to import them from.

    android:onClick="onSendButtonClick"

Now that we can send messages to Firebase, it is time for the next step: making the messages show up in our Android app in realtime.

Show the (existing and new) messages

Duration: 0:20

A chat app that doesn’t show existing messages is not very useful. So in this step we’ll add a list of the existing messages to our Android app. At the end of this section we’ll have a fully functional chat app.

Let's take this in chunks: first you'll build a new layout that we use to display each message, then you'll create a Java class to represent each message and finally we'll get the message from Firebase and put them into a ListView.

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="horizontal" android:layout_width="match_parent"
        android:layout_height="match_parent">
    <TextView
        android:id="@+id/username_text_view"
        android:text="Username"
        android:textStyle="bold"
        android:gravity="end"
        android:layout_width="0dp"
        android:layout_weight="3"
        android:layout_height="wrap_content"
        android:padding="10dp"/>

    <TextView
        android:id="@+id/message_text_view"
        android:text="Message"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="10"
        android:paddingLeft="0dp"
        android:paddingRight="10dp"
        android:paddingTop="10dp"
        android:paddingBottom="10dp"/>

Most of these attributes are just there to make the messages look reasonable. So feel free to use your own padding and layout attributes. Just be sure to use the same R.id fields in your code that you specify in the android:id attributes in the XML.

      public class ChatMessage {
          private String name;
          private String message;

          public ChatMessage() {
            // necessary for Firebase's deserializer
          }
          public ChatMessage(String name, String message) {
              this.name = name;
              this.message = message;
          }

          public String getName() {
              return name;
          }

          public String getMessage() {
              return message;
          }
      }

As you can see, this is plain-old Java object. But it’s a POJO with some special traits. First ChatMessage follows a JavaBean pattern for its property names. The getName method is a getter for a name property, while getMessage is a getter for a message property. And second, those property names correspond to the ones we’ve been using when we sent messages to Firebase in our onSendButtonClick() function.

If you end up making this ChatMessage an inner class of another class, you must make it static: public static class ChatMessage.

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/footer"/>

This is the container that all messages will be added to: one message_layout for each ChatMessage.
The id value is very important here, since Android uses it to find the ListView. So make sure to enter it exactly as specified: @android:id/list.

    public class MainActivity extends ListActivity {
    mListAdapter = new FirebaseListAdapter<ChatMessage>(mFirebaseRef, ChatMessage.class,
                                                        R.layout.message_layout, this) {
        @Override
        protected void populateView(View v, ChatMessage model) {
            ((TextView)v.findViewById(R.id.username_text_view)).setText(model.getName());
            ((TextView)v.findViewById(R.id.message_text_view)).setText(model.getMessage());
        }
    };
    setListAdapter(mListAdapter);

The FirebaseListAdapter maps the data from your Firebase database into the ListView that you added to the layout. It creates a new instance of your message_layout for each ChatMessage and calls the populateView method. You override this method and put the name and message in the correct subviews.

    public void onSendButtonClick(View v) {
        String message = mMessageEdit.getText().toString();
        mFirebaseRef.push().setValue(new ChatMessage("puf", message));
        mMessageEdit.setText("");
    }

In this section we made our app show the chat messages. It was a lot of work, but in the end you can see that the Java code is not that big, Thanks to firebase.

Enable login

Duration: 0:15 As a final step, we're going to allow the users of our app to log in using email and password.

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Login"
        android:id="@+id/login"
        android:layout_alignParentTop="true"
        android:layout_alignParentEnd="true"
        android:onClick="onLoginButtonClick" />

Note that the button refers to an onLoginButtonClick method, which you'll create in a few minutes.

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
      <EditText
          android:id="@+id/email"
          android:inputType="textEmailAddress"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_marginTop="16dp"
          android:layout_marginLeft="4dp"
          android:layout_marginRight="4dp"
          android:layout_marginBottom="4dp"
          android:hint="Email" />
      <EditText
          android:id="@+id/password"
          android:inputType="textPassword"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_marginTop="4dp"
          android:layout_marginLeft="4dp"
          android:layout_marginRight="4dp"
          android:layout_marginBottom="16dp"
          android:hint="Password"/>
    </LinearLayout>

So we have two EditText controls under each other. The rest of the popup will be handled by a stock Android dialog. Since your app will display the sign-in dialog as a popup, add the handling to MainActivity.java:

    public void onLoginButtonClick(View v) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        builder.setMessage("Enter your email address and password")
               .setTitle("Log in");

        LayoutInflater inflater = this.getLayoutInflater();
        builder.setView(inflater.inflate(R.layout.dialog_signin, null));

        builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                AlertDialog dlg = (AlertDialog) dialog;
                final String email = ((TextView)dlg.findViewById(R.id.email)).getText().toString();
                final String password =((TextView)dlg.findViewById(R.id.password)).getText().toString();

                // TODO: sign in to Firebase

            }
        });
        builder.setNegativeButton("Cancel", null);

        AlertDialog dialog = builder.create();
        dialog.show();
    }

This method builds and show the dialog, with our two text boxes as the main body. When the user clicks OK, it extracts the email address and password from the text controls.

    mFirebaseRef.createUser(email, password, new Firebase.ResultHandler() {
        @Override
        public void onSuccess() {
            mFirebaseRef.authWithPassword(email, password, null);
        }
        @Override
        public void onError(FirebaseError firebaseError) {
            mFirebaseRef.authWithPassword(email, password, null);
        }
    });

In this code, we always try to register the user. If the user already registered that will result in onError, otherwise it will result on onSuccess.
Either way, we next call authWithPassword to authenticate the (pre-existing or just-created) user.

    private String mUsername;
    mFirebaseRef.addAuthStateListener(new Firebase.AuthStateListener() {
        @Override
        public void onAuthStateChanged(AuthData authData) {
            if(authData != null) {
                mUsername = ((String)authData.getProviderData().get("email"));
                findViewById(R.id.login).setVisibility(View.INVISIBLE);
            }
            else {
                mUsername = null;
                findViewById(R.id.login).setVisibility(View.VISIBLE);
            }
        }
    });

Firebase calls our listener whenever the authentication state changes, so whenever the user logs in or out. When the user logs in, we store their email address in our field and hide the login button.

Firebase Authentication supports multiple authentication providers and each of them exposes a different set of data. For example, if we'd allow our users to authenticate with their existing Twitter account, we could identify them by their twitter handle.

    mFirebaseRef.push().setValue(new ChatMessage(mUsername, message));

We could definitely improve the layout of things. But this step has been long enough as it is. So let's wrap up with a few notes.

    mFirebaseRef.unauth();

This will trigger the AuthStateListener we created before, which will clear the username field and re-enable the login button.

This is also where you can configure the password reset emails that you can send to your users, in case they forgot their password.

Wrap-up

Congratulations! You've just built a fully functional multi-user chat application that uses Firebase to store the data and authentication users.

As a reward for finishing the codelab you’ve earned a promo code! When you’re ready to put your Firebase app in production, you can use the promo code androidcodelab49 for $49 off your first month of a paid Firebase plan. Just enter the code when you upgrade your Firebase.

What we've covered

✓ Interacting with a Firebase Database from an Android application.
✓ Using Firebase Authentication in an Android application to authenticate users.

Next Steps