Ads are a crucial part of your app's overall user experience. Good ad implementations can contribute efficiently to UX and even improve user retention and engagement. For example, Rewarded Video Ads enable you to reward users with in-app currency or items for watching video ads, so that users can reach new heights where otherwise they may get stuck and would have churned.

However, an excellent ad implementation involves a lot of considerations: How often should you show these ads? Where and when should you show them? What should the award me? And so on. Unfortunately, the answer differs from app to app, and from placement to placement. There is no one-size-fits-all answer.

With Google Analytics for Firebase and AdMob, alongside several powerful yet easy-to-use tools Firebase offers, fine tuning your app in a data driven way has become much easier and more streamlined.

What are you going to be building?

In this codelab, you're going to collect analytics signals with Google Analytics for Firebase, set up Remote Config parameters and A/B testing, and send push notifications to users who are predicted to churn. We will also share with you tips about what metrics to collect and look at, and how to turn them into actions.

What you'll learn

What you'll need

Download the code

Click the following button to download all the code for this codelab:

Download source code

Unpack the downloaded zip file. This will unpack a root folder firebase-monetization-tips-master. Rename the root folder to firebase-monetization-tips.

...or clone the GitHub repository from the command line.

$ git clone https://github.com/googlecodelabs/firebase-monetization-tips

It contains two directories as follows:

Import the starter app

Launch Android Studio, choose "Import project" on the welcome screen and select the starter directory from the sample code download.

You should now have the project open in Android Studio.

Create Firebase Console Project

  1. Go to the Firebase console.
  2. Select Add Project, and name your project "Awesome Drawing Quiz".

Connect your Android app

  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.awesomedrawingquiz
  3. Set a nickname for your app: Awesome Drawing Quiz
  4. Leave the SHA-1 field blank since SHA-1 is not required for this project.
  5. Select Register App to register your app.

Add google-services.json file to your app

Next, you will be prompted a screen where you can download a configuration file that contains all the necessary Firebase metadata for your app. Click Download google-service.json and copy the 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.

First, add google-services to buildscript level dependency in the build.gradle file located at project root directory.

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ...
    
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.2'

        // Add the following line
        classpath 'com.google.gms:google-services:3.3.0'
    }
}

...

Then, apply the google-services plugin by adding a line in app/build.gradle file as follows:

apply plugin: 'com.android.application'

android {
    ...
}

dependencies {
    ...
}

// Add the following line at the bottom of your gradle file
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 File > Sync Project with Gradle Files menu to sync your project with gradle files.

The starter app already includes a dedicated Rewarded Video Ad Unit for your convenience. You are entirely free to skip this step if you don't want to create a new one under your AdMob account.

To create a new AdMob app in your account, please follow the instructions as follows:

  1. Go to the AdMob frontend.
  2. From Apps menu, click "Add App," answer "NO." when asked "Have you published your app on Google Play or the App Store," then name the app "Awesome Drawing Quiz," choose "Android" as the Platform and click "Add".

Once you created an AdMob app in your account, follow the steps as described in the below to create a new Rewarded Video Ad Unit.

  1. Click Apps menu in AdMob frontend, then select "Awesome Drawing Quiz" from the app list.
  2. Click Ad units menu, then click ADD AD UNIT to create a new Rewarded Video ad unit.
  3. Select Rewarded for the Ad format.



  4. Provide the name of the ad unit as you prefer. Then, set reward amount to 1, and reward item to "hint" (these are the reward the app current gives to users). And click CREATE AD UNIT to create a new Rewarded Video ad unit.



  5. When successfully created, you will see the instructions like the following.



  6. Go back to your Android project, and update the AdMob app id and ad unit id constants to the ones that you have created in the previous step.

AwesomeDrawingQuiz.java

// Update the value with your AdMob app id
private static final String ADMOB_APP_ID = "<YOUR_ADMOB_APP_ID>";

GameActivity.java

// Update the value with your Rewarded Video ad unit id
private static final String AD_UNIT_ID = "<YOUR_AD_UNIT_ID>";

In this section, we'll quickly walk-through Awesome Drawing Quiz game. Once you compile and run the project, you'll see the following screen when the app starts.


Once you click ‘START A GAME,' you'll see a drawing on the screen. Your mission is to guess the name of the drawing by using the hint displayed on the top, from which you can infer the first letter and the length of the drawing's name.


If you don't have any idea what the name of the drawing is, you can click ‘SKIP' to skip the level. However, instead of skipping the level, you may want a clue that helps you guess the right answer. You can get an extra clue by clicking 'HINT' button and watching a Rewarded Video ad. After you complete watching the ad, one extra letter will be disclosed in the hint as a reward.

Rewarded Video is a highly user-centric format. It's initiated by the user choosing to watch an ad, and it gives them some kind of reward that will impact user engagement in a variety of ways (in this Awesome Drawing Quiz, a hint).

In Awesome Drawing Quiz, there are four main steps when a user watches a rewarded ad and consumes the hint.

Ad prompt

Ad impression

Ad reward

Level success

These four parts are your "rewarded funnel."

The opt-in rate lets you know how many of your users choose to watch a rewarded ad for a hint. It will help you answer: "Do users perceive a hint as a useful reward?"

The level clear rate helps you answer: "Users are watching the ads, but are they clearing the levels?"

Add Google Analytics for Firebase to your game

To understand the user journey in our game, you will add Google Analytics for Firebase to your game. Add the dependency for Google Analytics for Firebase to the dependencies block of your app/build.gradle file:

implementation 'com.google.firebase:firebase-core:16.0.0'

Once you integrate Google Analytics for Firebase SDK to your app, it will automatically capture a number of events and user properties. It also allows you to define your own custom events.

Define custom events

To understand the user journey in the game, you will define a few custom events that track user behaviors.

Event name

Triggered...

Parameters

stage_start

when a user starts a stage

none

level_start

when a user starts a new level (a new drawing quiz) within a stage.

(there are 6 levels in one stage)

level_name

level_wrong_answer

when a user submits a wrong answer

level_name

ad_prompt

when a user taps on the hint button, and is prompted to watch a Rewarded Video

ad_unit_id

level_success

when a user submits a correct answer (clears a level)

elapsed_time_sec, hint_used, level_name, number_of_attempts

level_fail

when a user skips a level

elapsed_time_sec, hint_used, level_name, number_of_attempts

stage_end

when a user finishes a stage

number_of_correct_answers

Create a helper class for logging custom events

To make logging easier, you will create a helper class to manage custom events.

Create a new Java class and name it QuizAnalytics under com.google.firebase.codelab.awesomedrawingquiz.ui.game package. Create fields that define the name of your custom events and their parameters.

QuizAnalytics.java

public final class QuizAnalytics {

    private static final String EVENT_AD_PROMPT = "ad_prompt";

    private static final String EVENT_LEVEL_FAIL = "level_fail";

    private static final String EVENT_LEVEL_SUCCESS = "level_success";

    private static final String EVENT_LEVEL_WRONG_ANSWER = "level_wrong_answer";

    private static final String EVENT_STAGE_START = "stage_start";

    private static final String EVENT_STAGE_END = "stage_end";

    private static final String PARAM_AD_UNIT_ID = "ad_unit_id";

    private static final String PARAM_ELAPSED_TIME_SEC = "elapsed_time_sec";

    private static final String PARAM_HINT_USED = "hint_used";

    private static final String PARAM_NUMBER_OF_ATTEMPTS = "number_of_attempts";

    private static final String PARAM_NUMBER_OF_CORRECT_ANSWERS = "number_of_correct_answers";
}

Next, add methods that help you log custom events in your game. Note that most of the custom events include parameters so you have more context of each event. Also note that a couple of event names and parameters (LEVEL_NAME and LEVEL_START) are already defined by Analytics, so we're going to use those.

public final class QuizAnalytics {

    ...

    public static void logStageStart(@NonNull FirebaseAnalytics instance) {
        instance.logEvent(EVENT_STAGE_START, null);
    }

    public static void logLevelStart(
            @NonNull FirebaseAnalytics instance, @NonNull String levelName) {
        Bundle param = new Bundle();
        param.putString(FirebaseAnalytics.Param.LEVEL_NAME, levelName);
        instance.logEvent(FirebaseAnalytics.Event.LEVEL_START, param);
    }

    public static void logLevelWrongAnswer(
            @NonNull FirebaseAnalytics instance, @NonNull String levelName) {
        Bundle param = new Bundle();
        param.putString(FirebaseAnalytics.Param.LEVEL_NAME, levelName);
        instance.logEvent(EVENT_LEVEL_WRONG_ANSWER, param);
    }

    public static void logAdPrompt(
            @NonNull FirebaseAnalytics instance, @NonNull String adUnitId) {
        Bundle param = new Bundle();
        param.putString(PARAM_AD_UNIT_ID, adUnitId);
        instance.logEvent(EVENT_AD_PROMPT, param);
    }

    public static void logLevelSuccess(
            @NonNull FirebaseAnalytics instance, @NonNull String levelName,
            int numberOfAttempts, int elapsedTimeSec, boolean hintUsed) {
        Bundle param = new Bundle();
        param.putString(FirebaseAnalytics.Param.LEVEL_NAME, levelName);
        param.putInt(PARAM_NUMBER_OF_ATTEMPTS, numberOfAttempts);
        param.putInt(PARAM_ELAPSED_TIME_SEC, elapsedTimeSec);
        param.putLong(PARAM_HINT_USED, hintUsed ? 1 : 0);
        instance.logEvent(EVENT_LEVEL_SUCCESS, param);
    }

    public static void logLevelFail(
            @NonNull FirebaseAnalytics instance, @NonNull String levelName,
            int numberOfAttempts, int elapsedTimeSec, boolean hintUsed) {
        Bundle param = new Bundle();
        param.putString(FirebaseAnalytics.Param.LEVEL_NAME, levelName);
        param.putInt(PARAM_NUMBER_OF_ATTEMPTS, numberOfAttempts);
        param.putInt(PARAM_ELAPSED_TIME_SEC, elapsedTimeSec);
        param.putLong(PARAM_HINT_USED, hintUsed ? 1 : 0);
        instance.logEvent(EVENT_LEVEL_FAIL, param);
    }

    public static void logStageEnd(
            @NonNull FirebaseAnalytics instance, int numberOfCorrectAnswers) {
        Bundle param = new Bundle();
        param.putInt(PARAM_NUMBER_OF_CORRECT_ANSWERS, numberOfCorrectAnswers);
        instance.logEvent(EVENT_STAGE_END, param);
    }
}

Log custom events in our game

In GameActivity, create a reference to FirebaseAnalytics and initialize it as follows:

GameActivity.java

public class GameActivity extends AppCompatActivity implements RewardedVideoAdListener {
    ...
   
    // Define a field for FirebaseAnalytics
    FirebaseAnalytics firebaseAnalytics;
   
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        ...

        // Obtain a FirebaseAnalytics instance
        firebaseAnalytics = FirebaseAnalytics.getInstance(this);

        ...
    }

    ...
}

Next, in onCreate() method, call logStageStart()to indicate a stage has started.

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    ...

    if (!viewModel.isStarted()) {
        showStageStartFullScreenDialog();

        // Add the following line
        QuizAnalytics.logStageStart(firebaseAnalytics);
    }

    ...
}

Next, in setupViewModel() method, add calls to log custom events as the following.

private void setupViewModel() {
    viewModel.registerGameEventListener(ev -> {
        if (ev instanceof GameLevelEvent) {
            GameLevelEvent round = (GameLevelEvent) ev;
            Log.d(TAG, "Round loaded: " + ev);

            // Add this line
            QuizAnalytics.logLevelStart(firebaseAnalytics, round.drawing().getWord());

            ...
        } else if (ev instanceof GameLevelClueUpdateEvent) {
            ...
        } else if (ev instanceof GameWrongAnswerEvent) {
            GameWrongAnswerEvent event = (GameWrongAnswerEvent) ev;
            Log.d(TAG, "Wrong Answer received: " + event);
            
            // Add this line
            QuizAnalytics.logLevelWrongAnswer(firebaseAnalytics, event.drawing().getWord());

            ...
        } else if (ev instanceof GameLevelClearEvent) {
            GameLevelClearEvent event = (GameLevelClearEvent) ev;
            Log.d(TAG, "Round cleared: " + event);

            // Add this line
            QuizAnalytics.logLevelSuccess(firebaseAnalytics, event.drawing().getWord(),
                    event.numAttempts(), event.elapsedTimeInSeconds(), event.isHintUsed());

            ...
        } else if (ev instanceof GameLevelSkipEvent) {
            GameLevelSkipEvent event = (GameLevelSkipEvent) ev;
            Log.d(TAG, "Round skipped: " + event);

            // Add this line
            QuizAnalytics.logLevelFail(firebaseAnalytics, event.drawing().getWord(),
                    event.numAttempts(), event.elapsedTimeInSeconds(), event.isHintUsed());
        } else if (ev instanceof GameStageClearEvent) {
            GameStageClearEvent event = (GameStageClearEvent) ev;
            Log.d(TAG, "Stage cleared: " + event);

            // Add this line
            QuizAnalytics.logStageEnd(firebaseAnalytics, event.numCorrectAnswers());

            ...
        }
    });
}

Finally, add a call to logAdPrompt() in showHintConfirmDialog() method.

private void showHintConfirmDialog() {
    // Add this line
    QuizAnalytics.logAdPrompt(firebaseAnalytics, AD_UNIT_ID);

    new AlertDialog.Builder(this)
            .setTitle(R.string.need_a_hint)
            .setMessage(R.string.need_a_hint_description)
            .setPositiveButton(android.R.string.ok, (dlg, which) -> {
                showRewardedVideoAd();
            })
            .setNegativeButton(android.R.string.no, null)
            .show();
}

You can use DebugView to verify events being correctly logged. DebugView enables you to see the raw event data logged by your app on development devices in near real-time.

This is very useful for validation purposes during the instrumentation phase of development and can help you discover errors and mistakes in your analytics implementation.

Enable debug mode

Generally, events logged in your app are batched together over a period of approximately one hour and uploaded together. To validate your analytics implementation on the fly, you need to enable Debug mode on your development device to upload events with minimal delays.

First, open Terminal tool in Android Studio. It is located in the bottom toolbar.

Then execute the following command (make sure test Android device is connected to your computer or Android Emulator is running):

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

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

adb shell setprop debug.firebase.analytics.app .none.

Debug Analytics events with DebugView

Once you have enabled Debug mode on your test device, go to the Firebase console and select DebugView from the menu. Then, on your test device, play your game to see events being logged and shown on the DebugView report.

You can access detailed information about each event by clicking the event name. As an example, the following screenshot shows parameter details associated with the level_start event.

Please refer to DebugView help center article for more details.

Audiences in Analytics

Audiences let you segment your users in ways that are meaningful to your app and your business. You can segment users by event or by user property, or combine events, parameters, and properties to include practically any subset of users.

In this step, you will create an audience that contains users who are highly engaged and perform very well in your game. You will include users who cleared more than 50 stages as "Expert users".

Create an Audience

Go to Firebase console then select Audiences menu under the ANALYTICS section. Then click the NEW AUDIENCE button to create a new audience.

Next, enter a name and a description of your new Audience as follows:

Choose "stage_end" in the conditions field. Since this event is triggered when a user finishes the stage, you should pick this event to define Expert Users audience.

Then, click Count > 0 condition to change the value. Modify the value to 50 as shown below.

Next, click ADD A PARAMETER button to add a parameter as an additional condition.

Remember in each stage there are 6 levels, you want to count stage_end events with all 6 quizzes answered correctly. To do so, choose "number_of_correct_answers" parameter, and set the condition to "equal to," "6," as follows:

You will see the following screen after you finish audience configuration for Expert users. Click CREATE to complete audience creation.

View Audience details

You can see a detailed report for an audience by clicking the audience name from the list as shown in the following screenshot. Remember, your graph will probably look much emptier than this example.

Metrics available from Audience report are as follows:

Enable custom parameter reporting

Google Analytics for Firebase will collect total number of event counts by default, but reporting for custom parameters needs to be turned on explicitly for each event parameter you're interested in. Once this is enabled, Google Analytics for Firebase will display additional cards to show the stats for custom parameters.

To register custom parameters for an event:

  1. Go to the Firebase console and select the Awesome Drawing Quiz project you created earlier. Then click Events from the navigation menu.
  2. In the row for the event you want to modify, click > Edit Parameter reporting.

  3. In the Enter parameter name field, enter the name of the parameter you'd like to register. If no match is found, just enter the parameter name then click ADD.
  4. Refer to the table below, and set the Type field to Text or Number accordingly. For numeric parameters, make sure you set the Unit of Measurement field as well.
  5. Click SAVE button to finish the setup.

Enable parameter reporting on each event listed below.

Event name

Parameter name

Parameter type

Unit of Measurement

level_start

level_name

Text

N/A

level_wrong_answer

level_name

Text

N/A

ad_prompt

ad_unit_id

Text

N/A

level_success

elapsed_time_sec

Number

Seconds

hint_used

Number

Standard

level_name

Text

N/A

number_of_attempts

Number

Standard

level_fail

elapsed_time_sec

Number

Seconds

hint_used

Number

Standard

level_name

Text

N/A

number_of_attempts

Number

Standard

stage_end

number_of_correct_answers

Number

Standard

The following screenshot shows custom parameter reporting setup for level_success event:

Find insights on Rewarded Video from Event reports

Rewarded Video is a highly user-centric format. It's initiated by a user choosing to watch an ad, and it gives the user a reward that impact a user engagement in a variety of ways, (in your Awesome Drawing Quiz game, a hint.)

Recall there are four parts to a Rewarded Ad in Awesome Drawing Quiz.

Ad prompt

Ad impression

Ad reward

Level success

Go to the Firebase console and Select the Awesome Drawing Quiz project you created earlier. Click Events menu under ANALYTICS, to see our list of custom events.

Suppose that for level_success, we want to find out how many times a player used a hint to clear the levelOn the event list screen, click on level_success.

In the level_success detailed event report, find the hint_used card, and click SUM to see the total number of hints that were used. Note that earlier in your code, when a level is cleared using a hint, the event parameter hint_used was set to 1.

Suppose you're curious if the showing one additional letter is a valuable enough hint for your users, and you're interested in experimenting with the number of letters your hint gives away.

To achieve this, you will change your project code so that the app retrieves the reward amount from the cloud using Firebase Remote Config. You will then create a value on the Firebase console to adjust this value without having to re-publish your app.

Add Firebase Remote Config to our game

First, add the dependency for Firebase Remote Config to app/build.gradle file:

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

Next, create remote_config_defaults.xml file under src/main/res/xml directory. Then define a default value for reward_amount parameter as shown in the following.

remote_config_defaults.xml

<?xml version="1.0" encoding="utf-8"?>
<defaultsMap>
    <entry>
        <key>reward_amount</key>
        <value>1</value>
    </entry>
</defaultsMap>

Now let's change your game's code a little bit to make room for Remote Config. in AwesomeDrawingQuiz.java, add provideRemoteConfig() method as follows:

AwesomeDrawingQuiz.java

public class AwesomeDrawingQuiz extends Application {

    ...

    // Add provideRemoteConfig() method
    private FirebaseRemoteConfig provideRemoteConfig() {
        FirebaseRemoteConfig remoteConfig = FirebaseRemoteConfig.getInstance();
        FirebaseRemoteConfigSettings settings = new FirebaseRemoteConfigSettings.Builder()
                .setDeveloperModeEnabled(BuildConfig.DEBUG)
                .build();
        remoteConfig.setConfigSettings(settings);
        remoteConfig.setDefaults(R.xml.remote_config_defaults);

        Task<Void> fetchTask;
        if (BuildConfig.DEBUG) {
            fetchTask = remoteConfig.fetch(0L);
        } else {
            fetchTask = remoteConfig.fetch();
        }

        fetchTask.addOnCompleteListener(task -> {
            if (task.isSuccessful()) {
                Log.d("AwesomeDrawingQuiz", "Remote config value fetched");
                remoteConfig.activateFetched();
            }
        });
        return remoteConfig;
    }
}

Please note that you have enabled developer mode on debug build by calling setDeveloperModeEnabled().

In onCreate() method, pass FirebaseRemoteConfig object generated by provideRemoteConfig() method as a parameter of AwesomeDrawingQuizViewModelFactory's constructor.

public class AwesomeDrawingQuiz extends Application {

    ...

    @Override
    public void onCreate() {
        ...

        // Pass FirebaseRemoteConfig as a parameter
        viewModelFactory = new AwesomeDrawingQuizViewModelFactory(this, provideRemoteConfig());
    }

    private FirebaseRemoteConfig provideRemoteConfig() {
        ...
    }
}

Next, modify AwesomeDrawingQuizViewFactory's implementation to include FirebaseRemoteConfig object as shown below.

AwesomeDrawingQuizViewFactory.java

public class AwesomeDrawingQuizViewModelFactory implements ViewModelProvider.Factory {

    ...

    // Add below
    private FirebaseRemoteConfig remoteConfig;

    // Add a parameter for FirebaseRemoteConfig object
    public AwesomeDrawingQuizViewModelFactory(
            @NonNull Context context, @NonNull FirebaseRemoteConfig remoteConfig) {
        ...

        // Add below
        this.remoteConfig = remoteConfig;
    }

    @NonNull
    @Override
    @SuppressWarnings("unchecked")
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        if (modelClass.isAssignableFrom(GameViewModel.class)) {

            // Pass FirebaseRemoteConfig object as a parameter
            return (T) new GameViewModel(drawingDao, remoteConfig);
        } else if (modelClass.isAssignableFrom(SplashViewModel.class)) {
            ...
        }
        ...
    }
}

Now you will wire up Remote Config parameter to our game. Add a field to retain a FirebaseRemoteConfig instance in GameViewModel class.

GameViewModel.java

public final class GameViewModel extends ViewModel {
    ...

    // Add below
    private FirebaseRemoteConfig remoteConfig;

    ...

    // Add a parameter for FirebaseRemoteConfig object
    public GameViewModel(DrawingDao drawingDao, FirebaseRemoteConfig remoteConfig) {
        this.drawingDao = drawingDao;
        this.remoteConfig = remoteConfig;
    }

    ...
}

Next, implement fetchRewardAmount() method in GameViewModel and add a call to fetchRewardAmount() in startLevel() method.

public final class GameViewModel extends ViewModel {
    ...

    // Define a key for Remote Config Parameter
    private static final String KEY_REWARD_AMOUNT = "reward_amount";

    void startLevel() {
        numCorrectAnswers = 0;
        gameDifficulty = GameSettings.DIFFICULTY_NORMAL;

        // Add a call to fetchRewardAmount()
        fetchRewardAmount();
        seenWords.clear();

        startLevel(1);
    }

    ...

    // Add fetchRewardAmount() method
    private void fetchRewardAmount() {
        this.disclosedLettersOnReward = (int) remoteConfig.getLong(KEY_REWARD_AMOUNT);
        Log.d("GameViewModel", "Reward amount set to " + disclosedLettersOnReward);
    }

    ...
}

Create a Remote Config parameter

Go to the Firebase console and select the Awesome Drawing Quiz project you created earlier. Then click Remote Config -> ADD YOUR FIRST PARAMETER button.

Name the parameter as reward_amount and set its default value to 2. Then click ADD PARAMETER button.

Click PUBLISH CHANGES button to make this configuration live to the users.

Run your app again, and ask for a hint. Notice that you now get 2 letters as your reward!

Now instead of having to use a hard-coded reward amount, your game can fetch the number of letters to show from Remote Config, which means you can change the reward amount at any time without requiring your users to download an app update.

When you are updating your app and using Firebase Remote Config to push it to an application with an active user base, you want to make sure you get it right. You might be uncertain about the following:

The best way to implement a feature to optimize the user experience.

Too often, app developers don't learn that their users dislike a new feature or an updated user experience until their app's rating in the app store declines. A/B testing can help measure whether your users like new variants of features, or whether they prefer the app as it currently exists. Plus, keeping most of your users in a control group ensures that most of your user base can continue to use your app without experiencing any changes to its behavior or appearance until the experiment has concluded.

The best way to optimize the user experience for a business goal.

Sometimes you're implementing product changes to maximize a metric like revenue or retention. With A/B testing, you set your business objectives, and Firebase does the statistical analysis to determine if a variant is outperforming the control group for your selected objective.

To A/B test feature variants with a control group, do the following:

Recall that you just changed your game to give out two letters as a hint. However, you are not sure whether this new amount is good enough to help your users to clear a level and improve retention compared to the old value.

With Firebase A/B Testing, you can test out whether your hypothesis is true or not.

Create an Experiment

Go to the Firebase console and select Awesome Drawing Quiz project. Then, select A/B Testing under GROW section.

Click the CREATE EXPERIMENT button. From the following dialog, select Remote Config to create a Remote Config Experiment.

First, start with Experiment basics. Enter ‘Reward amount Test' as the name. Then select your game (com.google.firebase.codelab.awesomedrawingquiz) as the target app. Since you don't want to run an experiment aggressively, you will set this experiment to target 10% of all users.

Click NEXT button to configure experiment for a control group and variant group. Enter the name of the variant group as ‘Two letters as a reward.'

Next, click the ADD PARAMETER button and select reward_amount from the parameter list. Then enter 2 as a value for ‘Two letters as a reward' group, and change the value to 1 for the control group.

Click NEXT button to set a goal of the experiment. Since you want to confirm whether giving more hints (reveals 2x letters than the control group) helps users to beat a level or not, select level_success event as a goal metric.

Review an Experiment before it runs

Once you Click REVIEW button, a new experiment will be created. You can review your experiment settings at a glance.

If an experiment is in draft stage, you can test the experiment on your device before it rolled out to users. Once you click Details section, the section will expand, and you'll see Test devices menu on the screen. Click MANAGE TEST DEVICES button.

In the dialog, you can assign your test device to either a control group or a variant group by using Instance ID token. The following screenshot shows that a test device is assigned to the variant group(Two letters as a reward) group.

Run an Experiment

You can run an experiment by clicking START EXPERIMENT button. After you start an experiment, you can't modify experiment settings anymore.

Once an experiment has been running for a while, you can check in on its progress and see what your results look like for the users who have participated in your experiment so far.

It's totally up to you how many days that experiment will run, which variant to choose, etc.. However, it is highly encouraged to read the following sections before you make a decision.

To find more details on running experiments, refer to the following articles.

Firebase Cloud Messaging (FCM) is a cross-platform messaging solution that lets you reliably deliver messages at no cost. By using FCM, you can send push notifications to your users to drive user engagement and retention.

Add Firebase Cloud Messaging to our game

Add the dependency for Firebase Cloud Messaging to app/build.gradle file.

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

Next, create AwesomeDrawingQuizMessagingService class that extends FirebaseMessagingService under com.google.firebase.codelab.awesomedrawingquiz.service package. This is required to do any message handling, for example, receiving notification on apps in the background.

AwesomeDrawingQuizMessagingService.java

public class AwesomeDrawingQuizMessagingService extends FirebaseMessagingService {
    // No additional code required
}

Next, create AwesomeDrawingQuizInstanceIdService class that extends FirebaseInstanceIdService under com.google.firebase.codelab.awesomedrawingquiz.service package. It handles the creation, rotation, and updating of registration tokens, which is required for sending to specific devices or for creating device groups.

AwesomeDrawingQuizInstanceIdService.java

public class AwesomeDrawingQuizInstanceIdService extends FirebaseInstanceIdService {
    // No additional code required
}

Finally, register these services in the app manifest as follows:

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    ...
    <application>
        ...

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

        <!-- Register AwesomeDrawingQuizInstanceIdService -->
        <service
            android:name=".service.AwesomeDrawingQuizInstanceIdService"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
            </intent-filter>
        </service>
    </application>

</manifest>

Send a notification message from Notifications composer

You can send push notifications to users by using Notifications composer without having to implement your own server for sending push messages.

First, go to Firebase console then select Cloud Messaging menu under GROW section. Then click SEND YOUR FIRST MESSAGE button to compose a new message.

In the Compose message screen, you can enter Message text (a message content that users will see) and Message label (Used to identify the message on Firebase console; not shown to users) as you want.

You can specify a delivery date of the message. If you choose Send Now, the message will be sent immediately, once you click the SEND MESSAGE button.

If you want to schedule a message, you can select Send Later option instead. You can choose the date, time, and time zone of a scheduled message.

Next, target users that you want to send a message. You can choose targeted users by User segment, Topic, and Single device.

If you choose User segment, you have to select an App before you define additional conditions(User audiences, User property, App version, Languages, and Predictions). For example, the below screenshot shows a configuration that targets all users of Awesome Drawing Quiz app.

Similarly, you can also target Expert users, a user audience that you have created before, as follows.

If you're interested in sending a message by Topic, please refer to this documentation for more details.

Additionally, you can define Conversion events of the message and configure Advanced options(Notification title, Android Notification channel, etc.). These two steps are optional.

Once all configurations are done, you can draft a message by clicking SAVE AS A DRAFT, or send/schedule a message by clicking the SEND MESSAGE button.

Please note that if you want to see the message on your device, you make sure that Awesome Drawing Quiz is not in the foreground. Otherwise, it will silently consume your notification.

When you are reaching out to your users or starting a new marketing campaign, you want to make sure that you get it right. A/B testing can help you find the optimal wording and presentation by testing message variants on selected portions of your user base. Whether your goal is better retention or conversion on an offer, A/B testing can perform statistical analysis to determine if a message variant is outperforming the control group for your selected objective.

To A/B test feature variants with a control group, do the following:

Create an Experiment

By using Notification composer in Firebase console, you can evaluate multiple variants on a single notification message.

Go to the Firebase console and select Awesome Drawing Quiz project. Then, select A/B Testing menu under GROW section.

Click CREATE EXPERIMENT button at the top-right corner of the screen. From the following dialog, select Notifications to create a Notification Experiment.

First, start with Experiment basics. Enter ‘Re-engage users' as a name. Then select your game(com.google.firebase.codelab.awesomedrawingquiz) as a target app. Since you don't want to run an experiment aggressively, you set this experiment to target 10% of all users.

Click NEXT button to configure experiment for a control group and variant group. You will test which push message scenario will help to re-engage our users. The scenarios are as follows:

Start by adding a new variant by clicking the ADD VARIANT button. Then enter the content for each variant as follows:

Please note that you left the control group's message field blank. If you leave the field blank, Firebase will not send a notification to this group.

Click NEXT to set a goal for the experiment. Since you want to make users play a few more levels by receiving a message, set the stage_start event as a goal metric.

Finally, you can specify message options including delivery date, title, custom data, etc. For the purpose of this experiment, you can leave everything set to their default values..

Review an Experiment before it runs

Once you Click REVIEW button, a new experiment will be created. You can review your experiment settings at a glance.

If an experiment is in draft stage, you can test the experiment on your device before it rolled out to users. Once you click the Details section, the section will expand, and you will see a Test devices menu on the screen. Click the MANAGE TEST DEVICES button.

In the dialog, you can assign your test device to either a control group or a variant group by using Instance ID token. The following screenshot shows that a test device is being sent the "5000 drawings" notification.

Run an Experiment

You can run an experiment by clicking START EXPERIMENT button. Once you start an experiment, you can't modify the experiment settings anymore.

Once an experiment has been running for a while, you can check in on its progress and see what your results look like for the users who have participated in your experiment so far.

It's totally up to you how many days that experiment will run, which variant to choose, etc.. However, it is highly encouraged to read the following sections before you make a decision.

Firebase Predictions applies machine learning to your analytics data to create dynamic user groups based on your users' predicted behavior.

Enable Analytics data sharing and Predictions

Firebase Predictions uses the data you log with Google Analytics for Firebase to make its predictions. Since you've already added Analytics into your game, the only step left is to share this data with Firebase Predictions.

Go to the Firebase console and select Predictions panel. You will see the following screen.

Click TURN PREDICTIONS ON button to enable Analytics data sharing for Predictions.

Predefined Predictions

By default, Predictions provides four types of predictions: churn and not_churn, which predicts which users will disengage (or won't) from your app over the next 7 days (that is, they will not open the app or app-related notification messages), and spend and not_spend, which predicts which users will (or won't) spend money in your app over the next seven days.

Once you turn on Predictions in your project, you'll see the following screen that shows you a list of predictions provided by Firebase by default.

Predictions needs sufficient event data volume to make meaningful predictions. If Predictions does not have sufficient data to make a prediction, it will display "Preparing a prediction" message as you can see from the above screenshot.

Once prediction data is ready for use, its status will be updated, and the number of users that can be targeted along with its risk tolerance will also be shown on the prediction card. The following screenshot shows an example of prediction cards:

For more information about predefined predictions, refer to developer documentation.

Custom Predictions

In addition to Predefined Predictions, Firebase Predictions allows us to create predictions based on any conversion event in your analytics data.

In this Codelab, we'll make two Custom Predictions based on the level_success custom event. We need to mark level_success event as a conversion event to make predictions based on it.

Select Events under ANALYTICS, then mark level_success as a conversion event as shown in the below screenshot.

Next, select the Predictions menu under GROW. Then click Create a prediction button to create a custom prediction.

We'll create two predictions based on the level_success event. First one is will_win_level, which predicts users who are likely to successfully complete a level. Next one is will_not_win_level, which predicts users who are not likely to beat a level.

Create each prediction as shown below.

Once you finish creating two custom predictions, you'll see those predictions in the predictions list as follows.

Similar to Predefined Predictions, Firebase also requires sufficient event volume to make predictions for Custom Predictions.

Once prediction data is ready for use, its status will be updated, and the number of users that can be targeted along with its risk tolerance will also be shown on the prediction card.

Firebase Predictions makes it easy to provide your app's users a different experience depending on your users' predicted behavior.

Adjust Difficulty to users who are likely to churn

You can use Firebase Predictions to identify users who are likely to disengage from your game, and apply a user retention strategy to those users. In this section, we'll lower the difficulty of our game for users that are predicted to churn out.

First, we need to make game difficulty remotely configurable. To do so, let's declare game difficulty as a Remote Config parameter.

Open remote_config_defaults.xml file, then add an entry for new parameter ‘difficulty' with its default value.

remote_config_defaults.xml

<?xml version="1.0" encoding="utf-8"?>
<defaultsMap>
    <!-- Add below -->
    <entry>
        <key>difficulty</key>
        <value>1</value>
    </entry>
    ...
</defaultsMap>

Difficulty value of 1 means ‘Normal' difficulty in your game. You can refer to GameSettings.java to see predefined difficulties. (Easy, Normal, Hard)

GameSettings.java

public final class GameSettings {

    public static final int MAX_LEVEL = 6;

    public static final int DIFFICULTY_EASY = 0;

    public static final int DIFFICULTY_NORMAL = 1;

    public static final int DIFFICULTY_HARD = 2;

}

Next, open GameViewModel.java file. Then implement fetchDifficulty() method and add a call to fetchDifficulty() in startLevel() method as follows:

GameViewModel.java

public final class GameViewModel extends ViewModel {

    // Define a key for Remote Config Parameter
    private static final String KEY_DIFFICULTY = "difficulty";

    ...

    void startLevel() {
        numCorrectAnswers = 0;

        // Replaces gameDifficulty = GameSettings.DIFFICULTY_NORMAL;
        fetchDifficulty();
        
        fetchRewardAmount();
        seenWords.clear();

        startLevel(1);
    }

    ...

    // Add a method to fetch game difficulty from Remote Config
    private void fetchDifficulty() {
        int difficulty = (int) remoteConfig.getLong(KEY_DIFFICULTY);
        Log.d("GameViewModel", "Game difficulty set to " + difficulty);
        this.gameDifficulty = difficulty;
    }
    ...
}

Now your game will fetch the game difficulty by Remote Config, which means you can change the game difficulty dynamically. Since you want to lower the difficulty to users who are likely to disengage, you need to define a condition that is used to segment your users.

To do so, select Remote Config menu under GROW section in Firebase console. Then click ADD PARAMETER button to add a new Remote Config parameter. Once a dialog pops up, enter difficulty as a parameter key and set 1 as a default value.

In this Codelab, we'll target user groups who are likely to churn, which have low to medium risk tolerance level. Before you can target users in Remote Config, you need to define a condition for each of them.

Click Add value for condition > Define a new condition. Then you'll see the following dialog.

Let's start with defining a condition for a predicted user group who are predicted to churn in low risk tolerance. Name it Likely to churn, then select com.google.firebase.codelab.awesomedrawingquiz as an App.

Click AND button to add a new constraint. Select Prediction > Predict: churn from the list then select Low risk tolerance. This will target a smaller group of users, but it will minimize the chance of including false positives.

Once you click CREATE CONDITION button, a new condition will be added. Set a value for this condition to 0. (DIFFICULTY_EASY)

Click the UPDATE button. You'll see new conditions are added in the parameter.

Finally, click PUBLISH CHANGES button to make this configuration live to the users.

Now Remote Config will adjust the game difficulty by predicted user behavior. Users who are likely to churn will get easier game experience than the others.

Send push notifications to users who are not likely to clear a level

You may want to encourage users who are predicted to not able to clear a level to increase retention rate. In this case, you can target that kind of users by using the ‘will_not_win_level' custom prediction that we created in the previous section.

First, select Cloud Messaging menu under GROW section in Firebase console. Then click NEW MESSAGE button to compose a new message.

After composing a message text, move on to Target section to specify users who will receive a message. Select com.google.firebase.codelab.awesomedrawingquiz as a target app, then click AND.

Next, you will add a condition to target users who are not likely to fully complete a level. To do so, select Predict: will_not_win_level as a condition, and select Low risk tolerance level. (Note: You can choose any risk tolerance level that you want)

Now all set! Once you click the SEND MESSAGE button, a push message will be delivered only to users who are not likely to clear a level, without disturbing other users.

You've reached the end of the codelab, you now can make go/no-go decisions on a new ad implementation based on data, change app settings on the fly, learn more about your users, and create customized app experiences based on your users' predicted behavior.

Learn More