1. Overview
Welcome to the 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
- Allow users to sign in.
- Sync data using the Firebase Realtime Database.
- Receive background messages with Firebase Notifications.
- Configure an application with Firebase Remote Config.
- Track application usage flows with Google Analytics for Firebase.
- Allow users to send invitations to install with Firebase Invites.
- Display ads with AdMob.
- Report crashes with Firebase Crash Reporting.
- Test your app with Firebase Test Lab.
What you need
- Android Studio version 2.1+.
- Sample code.
- A test device with Android 2.3+ and Google Play services 9.8 or later, or an Emulator with Google Play services 9.8 or later
- Google app version 6.6+ (only needed for testing Firebase App Indexing in Step 9)
- If using a device, a connection cable.
2. Get the sample code
Clone the GitHub repository from the command line:
$ git clone https://github.com/firebase/friendlychat-android
8. Send Messages
Implement text message sending
In this section, you will add the ability for app users to send text messages. The code snippet below listens for click events on the send button, creates a new FriendlyMessage
object with the contents of the message field, and pushes the message to the database. The push() method adds an automatically generated ID to the pushed object’s path. These IDs are sequential which ensures that the new messages will be added to the end of the list.
Update the onClick
method of mSendButton
in the onCreate
method in the MainActivity
class. This code is at the bottom of the onCreate
method already, update the onClick
body to match the code below:
MainActivity.java
mSendButton = (Button) findViewById(R.id.sendButton);
mSendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
FriendlyMessage friendlyMessage = new
FriendlyMessage(mMessageEditText.getText().toString(),
mUsername,
mPhotoUrl,
null /* no image */);
mFirebaseDatabaseReference.child(MESSAGES_CHILD)
.push().setValue(friendlyMessage);
mMessageEditText.setText("");
}
});
Implement image message sending
In this section, you will add the ability for app users to send image messages. Creating an image message is done with these steps:
- Select image
- Handle image selection
- Write temporary image message to the RTDB
- Begin to upload selected image
- Update image message URL to that of the uploaded image, once upload is complete
Select Image
With the following code snippet you will allow the user to select an image from the device’s local storage. Update the onClick method of mAddMessageImageView
in the onCreate method in the MainActivity class. This code is at the bottom of the onCreate
method already, update the onClick
body to match the code below.
MainActivity.java
mAddMessageImageView = (ImageView) findViewById(R.id.addMessageImageView);
mAddMessageImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, REQUEST_IMAGE);
}
});
Handle image selection and write temp message
Once the user has selected an image a call to the MainActivity’s onActivityResult will be fired. This is where you handle the user’s image selection. Using the code snippet below, add the onActivityResult method to MainActivity. In this function you will write a message with a temporary image url to the database indicating the image is being uploaded.
MainActivity.java
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.d(TAG, "onActivityResult: requestCode=" + requestCode + ", resultCode=" + resultCode);
if (requestCode == REQUEST_IMAGE) {
if (resultCode == RESULT_OK) {
if (data != null) {
final Uri uri = data.getData();
Log.d(TAG, "Uri: " + uri.toString());
FriendlyMessage tempMessage = new FriendlyMessage(null, mUsername, mPhotoUrl,
LOADING_IMAGE_URL);
mFirebaseDatabaseReference.child(MESSAGES_CHILD).push()
.setValue(tempMessage, new DatabaseReference.CompletionListener() {
@Override
public void onComplete(DatabaseError databaseError,
DatabaseReference databaseReference) {
if (databaseError == null) {
String key = databaseReference.getKey();
StorageReference storageReference =
FirebaseStorage.getInstance()
.getReference(mFirebaseUser.getUid())
.child(key)
.child(uri.getLastPathSegment());
putImageInStorage(storageReference, uri, key);
} else {
Log.w(TAG, "Unable to write message to database.",
databaseError.toException());
}
}
});
}
}
}
}
Upload image and update message
Add the method putImageInStorage to MainActivity. It is called in onActivityResult to initiate the upload of the selected image. Once the upload is complete you will update the message to use the appropriate image.
MainActivity.java
private void putImageInStorage(StorageReference storageReference, Uri uri, final String key) {
storageReference.putFile(uri).addOnCompleteListener(MainActivity.this,
new OnCompleteListener<UploadTask.TaskSnapshot>() {
@Override
public void onComplete(@NonNull Task<UploadTask.TaskSnapshot> task) {
if (task.isSuccessful()) {
FriendlyMessage friendlyMessage =
new FriendlyMessage(null, mUsername, mPhotoUrl,
task.getResult().getMetadata().getDownloadUrl()
.toString());
mFirebaseDatabaseReference.child(MESSAGES_CHILD).child(key)
.setValue(friendlyMessage);
} else {
Log.w(TAG, "Image upload task was not successful.",
task.getException());
}
}
});
}
Test Sending Messages
- Click the Run button.
- Enter a message and hit the send button, the new message should be visible in the app UI and in the Firebase console.
- Tap the “+” image to select an image. The new message should be visible first with a placeholder image then once the image upload is complete the selected image. The new message should also be visible in the Firebase console, as an object in the Database and as a blob in Storage.
9. Add Messages to the On-device Index
You can use Firebase App Indexing to index personal content to the user’s device. This allows your users to search the Google app and find messages they added in the FriendlyChat application. If your app is installed and enabled with App Indexing, a search query related to personal content takes a user directly into your app, which helps drive re-engagement with your app’s content.
In this section, we configure the application to write messages to the index so that they are discoverable in the Google app.
Add the app indexing dependency
The firebase-appindexing dependency provides the ability to write messages to the on-device index and to log user actions. Add this dependency in your app/build.gradle
file.
app/build.gradle
compile 'com.google.firebase:firebase-appindexing:11.8.0'
Add an intent filter
Add an intent filter into your AndroidManifest.xml
file to handle incoming links of the form http://friendlychat.firebase.google.com/message/*.
AndroidManifest.xml
<activity android:name="com.google.firebase.codelab.friendlychat.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:host="friendlychat.firebase.google.com"
android:scheme="http"
android:pathPrefix="/message"
/>
</intent-filter>
</activity>
Add personal content to index
Any time a user sends a message from their device, you can add that message to the on-device index for future discoverability through the Google app.
First, add the following code to create a message to write to the on-device index:
MainActivity.java
private Indexable getMessageIndexable(FriendlyMessage friendlyMessage) {
PersonBuilder sender = Indexables.personBuilder()
.setIsSelf(mUsername.equals(friendlyMessage.getName()))
.setName(friendlyMessage.getName())
.setUrl(MESSAGE_URL.concat(friendlyMessage.getId() + "/sender"));
PersonBuilder recipient = Indexables.personBuilder()
.setName(mUsername)
.setUrl(MESSAGE_URL.concat(friendlyMessage.getId() + "/recipient"));
Indexable messageToIndex = Indexables.messageBuilder()
.setName(friendlyMessage.getText())
.setUrl(MESSAGE_URL.concat(friendlyMessage.getId()))
.setSender(sender)
.setRecipient(recipient)
.build();
return messageToIndex;
}
Then, call indexMessage()
whenever there is a new message. You can index the FriendlyMessage
when the viewHolder
for it is populated in the same FirebaseRecyclerAdapter
:
MainActivity.java
@Override
protected void onBindViewHolder(final MessageViewHolder viewHolder, FriendlyMessage friendlyMessage, int position) {
...
if (friendlyMessage.getText() != null) {
// write this message to the on-device index
FirebaseAppIndex.getInstance()
.update(getMessageIndexable(friendlyMessage));
}
}
Note: It helps to add an IntentService
that establishes a base index of all messages for you initially. See details in the App Indexing documentation.
Log user actions
Logging user actions in your app helps improve your users’ experience when they search for your app content in the Google app.
Logging user actions on personal content, such as viewing messages, needs to be marked with the upload attribute set to false
in the Metadata
object of the Action
. This ensures user action activity on personal content remains on the device, and will not be uploaded to Google servers.
To log the VIEW_ACTION
user action, add the following function to MainActivity.java
MainActivity.java
private Action getMessageViewAction(FriendlyMessage friendlyMessage) {
return new Action.Builder(Action.Builder.VIEW_ACTION)
.setObject(friendlyMessage.getName(), MESSAGE_URL.concat(friendlyMessage.getId()))
.setMetadata(new Action.Metadata.Builder().setUpload(false))
.build();
}
Next, add the FirebaseUserActions.getInstance().end(...)
method to indicate the end of logging a user action. You can do this right after you’ve added the new message to the index. Pass getMessageViewAction()
from above to this method:
MainActivity.java
...
@Override
protected void onBindViewHolder(final MessageViewHolder viewHolder, FriendlyMessage friendlyMessage, int position) {
...
if (friendlyMessage.getText() != null) {
// write this message to the on-device index
FirebaseAppIndex.getInstance()
.update(getMessageIndexable(friendlyMessage));
}
// log a view action on it
FirebaseUserActions.getInstance().end(getMessageViewAction(friendlyMessage));
}
Test your implementation
- Start up the FriendlyChat app and send a new message with the text Hi world!
- Go to the Google app, switch to In Apps and search for Hi world.
- Confirm that the message can be found in the Google app.
- Tap on the result and confirm the FriendlyChat app opens.
Congratulations! You’ve successfully written your app’s content to the on-device index for discoverability in the Google app.
10. Receive Reengagement Notifications
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
compile 'com.google.firebase:firebase-messaging:11.8.0'
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";
@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());
}
}
The class MyFirebaseInstanceIdService
will be a service used to handle FCM logic. This service is used to alert the application when a new InstanceID token is generated, and to retrieve the generated token.
Modify it to extend FirebaseInstanceIdService
and override the onTokenRefresh
method to subscribe to a topic. Use the following code to update the onTokenRefresh
method in MyFirebaseInstanceIdService
to look like this:
MyFirebaseInstanceIdService.java
public class MyFirebaseInstanceIdService extends FirebaseInstanceIdService {
private static final String TAG = "MyFirebaseIIDService";
private static final String FRIENDLY_ENGAGE_TOPIC = "friendly_engage";
/**
* The Application's current Instance ID token is no longer valid
* and thus a new one must be requested.
*/
@Override
public void onTokenRefresh() {
// If you need to handle the generation of a token, initially or
// after a refresh this is where you should do that.
String token = FirebaseInstanceId.getInstance().getToken();
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
and the MyFirebaseInstanceIdService
. Add these declarations as children 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>
<service
android:name=".MyFirebaseInstanceIdService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
That’s it! FCM is all ready to receive messages.
Test Background Notifications
- Run the updated application.
- Hit the device’s home button (or otherwise send the app to the background).
- Use the Composer in the Firebase console to send notifications.
- In Firebase console select Notifications from 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
- Confirm that 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.
11. Remotely Configure Friendly Message Length
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 Parameter. Set the parameter key to friendly_msg_length
and the parameter value to 10. Make sure to click Publish 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
compile 'com.google.firebase:firebase-config:11.8.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();
}
});
}
/**
* 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
- Click the Run button.
- Check that the Friendly Message character limit has been set to 10. Update the Remote Config value from 10 to 15 in 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.
12. Send Install Invites
Firebase App Invites provide a simple way for your users to share your application with their friends through Email or SMS.
Add AppInvite dependency
Confirm that the firebase-appinvites
dependency is in your app/build.gradle
file:
app/build.gradle
compile 'com.google.android.gms:play-services-appinvite:11.8.0'
Setup GoogleApiClient
Make MainActivity implement the GoogleApiClient.OnConnectionFailedListener
interface. This should have already been done so just confirm it has been implemented:
MainActivity.java
public class MainActivity extends AppCompatActivity implements
GoogleApiClient.OnConnectionFailedListener {
Implement the required onConnectionFailed method. This should have already been done so just confirm it has been implemented:
MainActivity.java
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d(TAG, "onConnectionFailed:" + connectionResult);
}
AppInvites is initiated by calling startActivityForResult
, this allows the AppInvites UI to handle the invitation generation then return its completion status to the calling activity via the onActivityResult
method. Update the GoogleApiClient
initialization to onCreate
method in the MainActivity
using this:
MainActivity.java
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this, this)
.addApi(Auth.GOOGLE_SIGN_IN_API)
.build();
Send invitations
Add the sendInvitation
method to MainActivity
such that it creates and starts the intent which provides the user the ability to send invitations.
MainActivity.java
private void sendInvitation() {
Intent intent = new AppInviteInvitation.IntentBuilder(getString(R.string.invitation_title))
.setMessage(getString(R.string.invitation_message))
.setCallToActionText(getString(R.string.invitation_cta))
.build();
startActivityForResult(intent, REQUEST_INVITE);
}
Handle the Activity result invite callback, which will indicate whether or not the sending of the invites occurred successfully. Update the onActivityResult method in MainActivity so it looks like the snippet below
MainActivity.java
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.d(TAG, "onActivityResult: requestCode=" + requestCode + ", resultCode=" + resultCode);
if (requestCode == REQUEST_IMAGE) {
if (resultCode == RESULT_OK) {
if (data != null) {
final Uri uri = data.getData();
Log.d(TAG, "Uri: " + uri.toString());
FriendlyMessage tempMessage = new FriendlyMessage(null, mUsername, mPhotoUrl,
LOADING_IMAGE_URL);
mFirebaseDatabaseReference.child(MESSAGES_CHILD).push()
.setValue(tempMessage, new DatabaseReference.CompletionListener() {
@Override
public void onComplete(DatabaseError databaseError,
DatabaseReference databaseReference) {
if (databaseError == null) {
String key = databaseReference.getKey();
StorageReference storageReference =
FirebaseStorage.getInstance()
.getReference(mFirebaseUser.getUid())
.child(key)
.child(uri.getLastPathSegment());
putImageInStorage(storageReference, uri, key);
} else {
Log.w(TAG, "Unable to write message to database.",
databaseError.toException());
}
}
});
}
}
} else if (requestCode == REQUEST_INVITE) {
if (resultCode == RESULT_OK) {
// Check how many invitations were sent and log.
String[] ids = AppInviteInvitation.getInvitationIds(resultCode, data);
Log.d(TAG, "Invitations sent: " + ids.length);
} else {
// Sending failed or it was canceled, show failure message to the user
Log.d(TAG, "Failed to send invitation.");
}
}
}
Add a call to sendInvitation
to 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.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);
}
}
Test App Invite
- Click the Run button.
- Tap the menu overflow at the top right of the screen.
- Select the Invite option.
- You should see the App Invites interface which will allow you to select Email and SMS contacts and send a custom invitation. You must have control of the receiving account to view the invitation once sent.
- Tap send and verify that the invitation is sent to the selected contact.
- Once your app is in the Play Store the selected contact is taken to app install screen from invite.
You now know how to enable invites. Congrats!
13. Track User Flows
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
compile 'com.google.firebase:firebase-analytics:11.8.0'
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 inviting events in the onActivityResult
callback. Your onActivityResult
callback should handle the REQUEST_INVITE requestCode like the code below. You can see that in each case we send a SHARE
event but with different custom parameters for success and failure.
MainActivity.java
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.d(TAG, "onActivityResult: requestCode=" + requestCode + ", resultCode=" + resultCode);
If (requestCode == REQUEST_IMAGE) {
// ...
} else if (requestCode == REQUEST_INVITE) {
if (resultCode == RESULT_OK) {
Bundle payload = new Bundle();
payload.putString(FirebaseAnalytics.Param.VALUE, "sent");
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.SHARE,
payload);
// Check how many invitations were sent and log.
String[] ids = AppInviteInvitation.getInvitationIds(resultCode,
data);
Log.d(TAG, "Invitations sent: " + ids.length);
} else {
Bundle payload = new Bundle();
payload.putString(FirebaseAnalytics.Param.VALUE, "not sent");
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.SHARE,
payload);
// Sending failed or it was canceled, show failure message to
// the user
Log.d(TAG, "Failed to send invitation.");
}
}
}
Any events you log to Google Analytics for Firebase will be aggregated, anonymized, and reported in the Firebase console within 24 hours.
14. Monetize with Ads
AdMob gives you a way to easily monetize your application, you simply add the AdView
placeholder and Google handles the ad delivery for you.
Add AdMob dependency
Confirm the play-services-ads
dependency exists in your app/build.gradle
file:
app/build.gradle
compile 'com.google.android.gms:play-services-ads:11.8.0'
Add ads namespace
Verify that the ads namespace is in the root RelativeLayout
tag in the activity_main.xml file.
activity_main.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:ads="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.google.firebase.codelab.friendlychat.MainActivity">
Add AdView to main layout
Include the view that will contain the ad. In your activity_main.xml
file inside the root RelativeLayout
add the following AdView
tag.
activity_main.xml
<com.google.android.gms.ads.AdView
android:id="@+id/adView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_alignParentTop="true"
ads:adSize="BANNER"
ads:adUnitId="@string/banner_ad_unit_id">
</com.google.android.gms.ads.AdView>
Update RecyclerView
element to be laid out below the AdView
, RecyclerView
should now look like this:
activity_main.xml
<android.support.v7.widget.RecyclerView
android:id="@+id/messageRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/adView"
android:layout_above="@+id/linearLayout"/>
Add AdView variable
In the MainActivity
add an instance variable that represents the AdView
:
MainActivity.java
private AdView mAdView;
Request Ad
In MainActivity
in the onCreate
method request the ad to be placed in the AdView
:
MainActivity.java
mAdView = (AdView) findViewById(R.id.adView);
AdRequest adRequest = new AdRequest.Builder().build();
mAdView.loadAd(adRequest);
Handle lifecycle events
In MainActivity
add Activity
lifecycle event handling, pausing, resuming and destroying where necessary.
MainActivity.java
@Override
public void onPause() {
if (mAdView != null) {
mAdView.pause();
}
mFirebaseAdapter.stopListening();
super.onPause();
}
/** Called when returning to the activity */
@Override
public void onResume() {
super.onResume();
mFirebaseAdapter.startListening();
if (mAdView != null) {
mAdView.resume();
}
}
/** Called before the activity is destroyed */
@Override
public void onDestroy() {
if (mAdView != null) {
mAdView.destroy();
}
super.onDestroy();
}
Test AdMob
- Click the Run button.
- Verify that Test Ad shows as in the screenshot in step 1.
15. Report Crashes
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 {
// ...
compile '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() {
throw new NullPointerException("Fake null pointer exception");
}
Test Firebase Crashlytics
- Click the Run button.
- Verify that the Cause Crash menu item is available from the overflow menu.
- Verify that when Cause Crash is selected the application crashes.
- From the logcat verify that the crash report was successfully uploaded.
- If you do not see logs indicating that the crash report was sent, make sure that the Android Studio logcat filter is set to No Filters.
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!
16. Test Your App (in the cloud!)
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 Tests to create a new test configuration.
Set up the run configuration to run tests on Firebase Test Lab
In the Configurations window, set up the new run configuration as follows:
- Name: FriendlyChat Test
- Module: app
- Test: Class
- Class: com.google.firebase.codelab.friendlychat.MainActivityEspressoTest
- In Deployment Target Options, on the Target menu, choose Cloud Test Lab Device Matrix. If you are not logged in, click Connect to Google Cloud Platform to connect to Firebase Test Lab.
Under Cloud project:, click the icon and select your Google Cloud Platform project from the list.
Configure your test matrix to select test devices
Android Studio includes a Sample configuration that automatically configures a test matrix consisting of several widely-used devices and platform versions. To use the Sample configuration, choose it from the Matrix configuration list and then proceed to step 2 below. To create your own test matrix and select specific devices, platform versions, and locales, complete the optional first step below.
- (Optional) If you want to select a custom matrix of test device configurations, perform the following steps:
- Click the ellipsis next to Matrix Configuration to open the Matrix Configurations dialog (below).
- Click the Add New Configuration icon to add a new Matrix, then configure the new Matrix as follows:
- Enter a name for your new configuration in the Name field.
- Select the devices, Android versions, locales and screen orientations that you want to test your app with. For example, choose any device, a locale, and the latest 2-3 API levels.
- Click OK to save your configuration – It should be automatically selected in the “Choose Device” dialog.
- Click OK on the Run/Debug Configurations dialog to exit.
Run the new run configuration and verify results
- 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.
- 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 Android Studio
Figure: Firebase Test Lab results in the Firebase Console
Note
: In the
Run
window, ensure that both the
Show passed
and
Show ignored
icons are selected to see both successful test results and device configurations that were not run due to device/platform incompatibility.
17. Congratulations!
You have used Firebase to easily build a real-time chat application.
What we’ve covered
- Firebase Authentication
- Firebase Realtime Database
- Firebase Cloud Messaging
- Firebase Remote Config
- Google Analytics for Firebase
- Firebase App Invites
- Monetizing with AdMob
- Firebase Crash Reporting
- Firebase Test Lab for Android
Next Steps
- Use Firebase in your own Android app.
Leave a Reply