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.
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.
Click the following button to download all the code for this codelab:
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:
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.
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.
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'
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:
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.
// Update the value with your AdMob app id
private static final String ADMOB_APP_ID = "<YOUR_ADMOB_APP_ID>";
// 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?"
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.
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 |
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.
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);
}
}
In GameActivity
, create a reference to FirebaseAnalytics
and initialize it as follows:
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.
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.
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 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".
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.
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:
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:
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:
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.
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.
<?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:
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.
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.
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);
}
...
}
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.
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.
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.
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 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.
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.
public class AwesomeDrawingQuizInstanceIdService extends FirebaseInstanceIdService {
// No additional code required
}
Finally, register these services in the app manifest as follows:
<?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>
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:
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..
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.
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.
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.
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.
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.
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.
<?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)
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:
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.
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