Image: Working Friendly Chat app.

Welcome to the Grow Friendly Chat codelab. In this codelab, you'll learn how to use the Firebase platform to create Android applications. You will implement a chat client and monitor its performance using Firebase.

What you learn to do

What you need

Clone the GitHub repository from the command line:

$ git clone https://github.com/firebase/friendlychat-android

From Android Studio, select the grow-android-start directory () from the sample code download (File > Open > .../friendlychat-android/grow-android-start).

You should now have the grow-android-start project open in Android Studio.

  1. Go to the Firebase console.
  2. Select Create New Project, and name your project "FriendlyChat."
  1. From the overview screen of your new project,
    click Add Firebase to your Android app.
  2. Enter the codelab's package name: com.google.firebase.codelab.friendlychat.
  3. Enter the SHA1 of your signing keystore. If you are using the standard debug keystore, use the command below to find the SHA1 hash:
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v -storepass android

Add google-services.json file to your app

After adding the package name and SHA1 and selecting Continue, your browser automatically downloads a configuration file that contains all the necessary Firebase metadata for your app. Copy the google-services.json file into the app directory in your project.

Add google-services plugin to your app

The google-services plugin uses the google-services.json file to configure your application to use Firebase. The following line should already be added to the end of the build.gradle file in the app directory of your project (check to confirm):

apply plugin: 'com.google.gms.google-services'

Sync your project with gradle files

To be sure that all dependencies are available to your app, you should sync your project with gradle files at this point. Select Sync Project with Gradle Files () from the Android Studio toolbar.

Firebase Realtime Database Rules

Access to your Firebase Database is configured by a set of rules written in a JSON configuration language.

Go to your project in the Firebase console and select Database. Select Get Started in the Realtime Database option. When prompted for security rules, choose to start in either test mode or locked mode, choose test mode. Once the default rules are established, select the Rules tab and update the rules configuration with the following:

{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}

For more information on how this works (including documentation on the "auth" variable) see the Firebase security documentation.

Configure Authentication APIs

Before your application can access the Firebase Authentication APIs on behalf of your users, you will have to enable it

  1. Navigate to the Firebase console and select your project
  2. Select Authentication
  3. Select the Sign In Method tab
  4. Toggle the Google switch to enabled (blue)
  5. Press Save on the resulting dialog

If you get errors later in this codelab with the message "CONFIGURATION_NOT_FOUND", come back to this step and double check your work.

Test the working starter app:

  1. Click Run ()in the Android Studio toolbar.
  2. Sign in and add a few messages to confirm that the app works as expected.

Firebase In-App Messaging helps you engage your app's active users by sending them targeted, contextual messages that encourage them to use key app features.

Add InAppMessaging dependency

Confirm the existence of this dependency to your app/build.gradle file.

app/build.gradle

implementation 'com.google.firebase:firebase-inappmessaging-display:17.0.2'

Get your app's Instance ID

Click Run ()in the Android Studio toolbar. Then search for the line with "Starting InAppMessaging" in the logcat. Make note of the Instance ID.

Create in app message

From the Firebase console, select `In-App Messaging` from the navigation panel. Select `Start your first campaign`. Enter "Welcome back" for the title and "Good to see you again" for the body. Then click Test on your Device. Enter the Instance ID in the available field and click the + sign to add it. Then click Test.

Test In-App Messaging

  1. Close your app if opened.
  2. Open your app.
  3. Confirm that a modal dialog displays a message with the title and body we entered in the console.

You can use Firebase Cloud Messaging (FCM) to send notifications to users of your app. In this section we will configure the application to receive reengagement notifications which you can send from Firebase console.

Add FCM dependency

The firebase-messaging dependency provides the ability to send and receive FCM messages. Confirm the existence of this dependency to your app/build.gradle file.

app/build.gradle

implementation 'com.google.firebase:firebase-messaging:17.3.4'

Add FCM Services

The RegistrationIntentService class is a background service which is used to request the InstanceID token which identifies the application to the FCM server. It also subscribes to the topic that will be used to send re-engagement notifications (via topic messaging).

The class MyFirebaseMessagingService will be the background service that handles incoming FCM messages.

Update it to extend FirebaseMessagingService which is provided by the firebase-fcm library added earlier. It automatically handles notification messages, which are messages that the server specifies should produce a notification. To handle data messages (which are passed silently to the app rather than automatically creating a notification) you can override the onMessageReceived method from the FirebaseMessagingService base class:

MyFirebaseMessagingService.java

public class MyFirebaseMessagingService extends FirebaseMessagingService {

   private static final String TAG = "MyFMService";
   private static final String FRIENDLY_ENGAGE_TOPIC = "friendly_engage";

   @Override
   public void onMessageReceived(RemoteMessage remoteMessage) {
       // Handle data payload of FCM messages.
       Log.d(TAG, "FCM Message Id: " + remoteMessage.getMessageId());
       Log.d(TAG, "FCM Notification Message: " +
               remoteMessage.getNotification());
       Log.d(TAG, "FCM Data Message: " + remoteMessage.getData());
   }

   @Override
   public void onNewToken(String token) {
      Log.d(TAG, "FCM Token: " + token);
      // Once a token is generated, we subscribe to topic.
      FirebaseMessaging.getInstance()
              .subscribeToTopic(FRIENDLY_ENGAGE_TOPIC);
   }

}

Add service declarations for the MyFirebaseMessagingService as a child of the application element.

AndroidManifest.xml

<service
   android:name=".MyFirebaseMessagingService"
   android:exported="false">
   <intent-filter>
       <action android:name="com.google.firebase.MESSAGING_EVENT" />
   </intent-filter>
</service>

That's it! FCM is all ready to receive messages.

Test Background Notifications

  1. Run the updated application.
  2. Hit the device's home button (or otherwise send the app to the background).
  3. In Firebase console select Cloud Messaging from the Grow section of the left navigation bar. Select Send Your First Message. Set Message Text to "Friendly Chat?". Select the app we connected earlier as the App target. Click Send Message.
  4. Confirm that the message is received and notification is displayed on the device. The user should receive a notification that takes them back to the application when tapped.

Hooray! You can re-engage your users easily with FCM. See the documentation for more on FCM.

Google Analytics for Firebase provides a way for you to understand the way users move through your application, where they succeed and where they get stuck. It can also be used to understand the most used parts of your application.

Add Analytics dependency

Confirm the existence of the firebase-analytics dependency your app/build.gradle file:

app/build.gradle

implementation 'com.google.firebase:firebase-analytics:16.0.4'

Initialize Analytics

Add a FirebaseAnalytics instance variable to MainActivity:

MainActivity.java

private FirebaseAnalytics mFirebaseAnalytics;

In MainActivity initialize mFirebaseAnalytics by adding the following line to the onCreate method. By initializing Google Analytics for Firebase you will automatically track the lifecycle of your application throughout user sessions without writing any more code.

MainActivity.java

mFirebaseAnalytics = FirebaseAnalytics.getInstance(this);

Send Custom Events

Initializing Google Analytics for Firebase provides some default metrics like app installs and session lifecycle, however you may want to add custom events that help you understand how users interact with your app in specific ways. To send a custom event, call mFirebaseAnalytics.logEvent() with the information about the custom event.

In MainActivity, log message events when a message is sent. Update the logMessageSent method to look like the following.

MainActivity.java

private void logMessageSent() {
    // Log message has been sent.
    FirebaseAnalytics.getInstance(this).logEvent("message", null);
}

Any events you log to Google Analytics for Firebase will be aggregated, anonymized, and reported in the Firebase console within 24 hours.

Debug analytics

Generally, events logged by your app are batched together over the period of approximately one hour and uploaded together. This approach conserves the battery on end users' devices and reduces network data usage. However, for the purposes of validating your analytics implementation (and, in order to view your analytics in the DebugView report), you can enable Debug mode on your development device to upload events with a minimal delay.

To enable Analytics Debug mode on an Android device, execute the following commands:

adb shell setprop debug.firebase.analytics.app com.google.firebase.codelab.friendlychat

View the log events in near real time via the debug view in the Firebase console.

This behavior persists until you explicitly disable Debug mode by executing the following command:

adb shell setprop debug.firebase.analytics.app none

Firebase Crashlytics allows your application to report when crashes occur and log the events leading up to the crash.

Add Firebase Crashlytics dependency

Confirm the crashlytics dependency exists in your app/build.gradle file and that the Fabric maven repository dependency is in your project/build.gradle.

app/build.gradle

apply plugin: 'com.android.application'
apply plugin: 'io.fabric'

dependencies {
    // ...
  implementation 'com.crashlytics.sdk.android:crashlytics:2.7.1'
}

project/build.gradle

buildscript {
    repositories {
        // ...
        maven {
           url 'https://maven.fabric.io/public'
        }
    }
    dependencies {
        // ...
        classpath 'io.fabric.tools:gradle:1.24.4'
    }
}

Initiate crash

Add a handler for clicks on the Cause Crash menu item. Update the onOptionsItemSelected method in MainActivity so that it now looks like the code below:

MainActivity.java

@Override
public boolean onOptionsItemSelected(MenuItem item) {
   switch (item.getItemId()) {
       case R.id.crash_menu:
           Log.w("Crashlytics", "Crash button clicked");
           causeCrash();
           return true;
       case R.id.invite_menu:
           sendInvitation();
           return true;
       case R.id.fresh_config_menu:
           fetchConfig();
           return true;
       case R.id.sign_out_menu:
           mFirebaseAuth.signOut();
           mUsername = ANONYMOUS;
           startActivity(new Intent(this, SignInActivity.class));
           return true;
              default:
           return super.onOptionsItemSelected(item);
   }
}

Add causeCrash method

In the MainActivity, add the causeCrash method below.

MainActivity.java

private void causeCrash() {
   Crashlytics.getInstance().crash();
}

Test Firebase Crashlytics

  1. To activate the app with Firebase Crashlytics, go to the Firebase Dashboard and click ‘Crashlytics', select ‘Yes, this app is new to Crashlytics.'
  2. Back in Android Studio, click the Run button.
  3. Verify that the Cause Crash menu item is available from the overflow menu.
  4. Verify that when Cause Crash is selected the application crashes.
  5. From the logcat verify that the crash report was successfully uploaded.

The crash should now appear in your dashboard. Now you know how to automatically report crashes in your application and the crash will appear in the Crashlytics dashboard within 5 minutes. Well done!

Firebase Remote Config allows you to remotely configure your application without having to deploy any new code. In this codelab "Friendly Messages" are restricted to a maximum length. By defining this maximum length with Firebase Remote Config rather than hardcoding it in the client, we can update the value over the air through the Firebase console.

Add Config Rules in Firebase console

In the Remote Config section of Firebase console click Add Your First Parameter. Set the parameter key to friendly_msg_length and the parameter value to 10. Click Add Parameter. Make sure to click Publish Changes when you are done.

Add Firebase Remote Config dependency

The firebase-config dependency provides the ability to remotely configure applications. Confirm that the following dependency is added to your app/build.gradle file:

app/build.gradle

implementation 'com.google.firebase:firebase-config:15.0.0'

Add a Firebase Remote Config instance variable in the MainActivity class:

MainActivity.java (instance variable)

// Firebase instance variables
private FirebaseRemoteConfig mFirebaseRemoteConfig;

Request and use config

In the MainActivity onCreate method, add the following snippet to initialize the FirebaseRemoteConfig and then invoke the fetchConfig method. Add it just above the initialization of the Firebase Realtime Database:

MainActivity.java

// Initialize Firebase Remote Config.
mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();

// Define Firebase Remote Config Settings.
FirebaseRemoteConfigSettings firebaseRemoteConfigSettings =
       new FirebaseRemoteConfigSettings.Builder()
       .setDeveloperModeEnabled(true)
       .build();

// Define default config values. Defaults are used when fetched config values are not
// available. Eg: if an error occurred fetching values from the server.
Map<String, Object> defaultConfigMap = new HashMap<>();
defaultConfigMap.put("friendly_msg_length", 10L);

// Apply config settings and default values.
mFirebaseRemoteConfig.setConfigSettings(firebaseRemoteConfigSettings);
mFirebaseRemoteConfig.setDefaults(defaultConfigMap);

// Fetch remote config.
fetchConfig();

Add fetchConfig and applyRetrievedLengthLimit methods to MainActivity. These methods fetch and activate the retrieved configuration from the Remote Config API. The retrieved configuration determines the max number of characters that a Friendly Message can contain. The default max is 10, set in the FirebaseRemoteConfigSettings object.

MainActivity.java

// Fetch the config to determine the allowed length of messages.
public void fetchConfig() {
   long cacheExpiration = 3600; // 1 hour in seconds
   // If developer mode is enabled reduce cacheExpiration to 0 so that 
   // each fetch goes to the server. This should not be used in release
   // builds.
   if (mFirebaseRemoteConfig.getInfo().getConfigSettings()
           .isDeveloperModeEnabled()) {
       cacheExpiration = 0;
   }
   mFirebaseRemoteConfig.fetch(cacheExpiration)
           .addOnSuccessListener(new OnSuccessListener<Void>() {
               @Override
               public void onSuccess(Void aVoid) {
                   // Make the fetched config available via
                   // FirebaseRemoteConfig get<type> calls.
                   mFirebaseRemoteConfig.activateFetched();
                   applyRetrievedLengthLimit();
               }
           })
           .addOnFailureListener(new OnFailureListener() {
               @Override
               public void onFailure(@NonNull Exception e) {
                   // There has been an error fetching the config
                   Log.w(TAG, "Error fetching config: " + 
                           e.getMessage());
                   applyRetrievedLengthLimit();
               }
           });
    // print app's Instance ID token. FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
            @Override
            public void onComplete(@NonNull Task<InstanceIdResult> task) {
                if (task.isSuccessful()) {
                    Log.d(TAG, "Remote instance ID token: " + task.getResult().getToken());
                }
            }
        });

}


/**
 * Apply retrieved length limit to edit text field. 
 * This result may be fresh from the server or it may be from cached
 * values.
 */
private void applyRetrievedLengthLimit() {
   Long friendly_msg_length =
           mFirebaseRemoteConfig.getLong("friendly_msg_length");
   mMessageEditText.setFilters(new InputFilter[]{new 
           InputFilter.LengthFilter(friendly_msg_length.intValue())});
   Log.d(TAG, "FML is: " + friendly_msg_length);
}

Add a call to fetchConfig in the onOptionsItemSelected method in MainActivity. The onOptionsItemSelected method should now look like:

MainActivity.java

@Override
public boolean onOptionsItemSelected(MenuItem item) {
   switch (item.getItemId()) {
       case R.id.fresh_config_menu:
           fetchConfig();
           return true;
       case R.id.sign_out_menu:
           mFirebaseAuth.signOut();
           mUsername = ANONYMOUS;
           startActivity(new Intent(this, SignInActivity.class));
           return true;
       default:
           return super.onOptionsItemSelected(item);
   }
}

Test Remote Config

  1. Click the Run button.
  2. Check that the Friendly Message character limit has been set to 10. Update the Remote Config value from 10 to 15 in the Firebase console, then publish. From the overflow menu of the app select Fresh Config and confirm that the Friendly Message character limit is now 15.

Congratulations! You now know how to make vital updates to your app without re-releasing it.

Firebase A/B Testing helps you optimize your app experience by making it easy to run, analyze, and scale product and marketing experiments. Here you will use Firebase A/B testing to try different message lengths.

Get your app's Instance ID

Click the Run button.. Then search for the line with "Remote instance ID token" in the Xcode console. Make note of the token.

Create experiment

  1. From the Firebase console, select `A/B Testing` from the navigation panel. Select `create experiment`.
  2. Choose Remote Config as the type of experiment to create.
  3. Name your experiment "max msg length" and set the description to "What is the best max msg length". Select `Next`.
  4. Select the Friendly Chat app as the target. Select `Next`.
  5. Select primary metric to track. Choose the `message` event if available or `first_open` otherwise. It may take a few hours before the `message` event is available here so use `first_open` for now. Select `Next`.
  6. For the control group select `friendly_msg_length` parameter. For Variant B set the value to 10. Select `Add another variant` and set its value to 3. Select `Save`.
  7. Expand your draft experiment and select `Manage test devices`.
  8. Paste in your Instance ID token and select Variant A for testing. Select `Add` and `Save`.
  9. Now hit `Fetch Config` in your app's UI and verify that your text field is limited to the Variant A value.

Firebase Test Lab lets you test your app on various types of Android devices across multiple API levels and locales. The best part is that all this testing happens automatically in the cloud without you needing to maintain a collection of test devices.

Add an Espresso instrumentation test case

First we need to add an instrumentation test that we will run on Firebase Test Lab. In the MainActivityEspressoTest.java file, add the following test case method to the class.

MainActivityEspressoTest.java

@Test
public void verifySignUpButtonDisplayed() {
    onView(ViewMatchers.withId(R.id.sign_in_button)).check(matches(isDisplayed()));
}

Create a new run configuration for testing

Click app > Edit Configurations...

In the Configurations window, click the plus (+) button, choose Android Instrumented Tests to create a new test configuration.

Setup the run configuration to run tests on Firebase Test Lab

In the Configurations window, set up the new run configuration as follows:

  1. Name: FriendlyChat Test
  2. Module: app
  3. Test: Class
  4. Class: com.google.firebase.codelab.friendlychat.MainActivityEspressoTest

Setup deployment target

In Deployment Target Options, on the Target menu, choose Firebase Test Lab Device Matrix. If you are not logged in, click Connect to Google Cloud Platform to connect to Firebase Test Lab. Select your Cloud project from the drop down list.

Configure your test matrix to select test devices

Select Sample Spark Configuration for the Matrix Configuration, this automatically selects a matrix that can be run on a spark level project.

Run the new run configuration and verify results

  1. Click on the Run button to test your app with the selected configuration. The project will build and automatically run the test on each device in the test matrix in the cloud.
  2. After a few minutes, the test results will be displayed in Android Studio. The results of all test runs can also be viewed on the web in the Test Lab section of your project's Firebase Console, as shown in the figure below.

    Figure: Firebase Test Lab results in the Firebase Console

You have used Firebase to take a real-time chat application to the next level.

What we've covered

Next Steps

Learn More