Android FCM - txgz999/Mobile GitHub Wiki
- Google, Set up a Firebase Cloud Messaging client app on Android
- Google, Add Firebase to your Android project
In Firebase Console, “Add Firebase to your Android app” contains 4 steps
- Register app: find the Android package name, also called application id, from app/build.gradle
- Download config file: copy to the app folder
- Add Firebase SDK: modification done in gradle/build.gradle and app/build.gradle, then click Sync Now link
- Run your app to verify installation: build and run the app in Android emulator, then click the Verify button. Got the message “Congratulations, you’ve successfully added Firebase to your app!”
This is the first time I finished all 4 steps, normally I stopped at step 3. Since this step is applicable only to Android Studio project. Xamarin project needs to install some Nuget packages, and Nativescript needs to install some plugins.
After finishing these steps, the app is ready to receive notifications. But we need to know its registration token (a.k.a. instance ID) in order to send a message. So I add the following to the OnCreate
method of the main activity class:
FirebaseInstanceId.getInstance().instanceId.addOnCompleteListener(OnCompleteListener { task ->
if (!task.isSuccessful) {
Log.w(TAG, "getInstanceId failed", task.exception)
return@OnCompleteListener
}
val token = task.result?.token
})
To receive data message, or notification message when the app is in foreground, we need to extend the FirebaseMessagingService class:
class MyFirebaseMessagingService : FirebaseMessagingService() {
/**
* Called when message is received.
*
* @param remoteMessage Object representing the message received from Firebase Cloud Messaging.
*/
// [START receive_message]
override fun onMessageReceived(remoteMessage: RemoteMessage) {
// [START_EXCLUDE]
// There are two types of messages data messages and notification messages. Data messages are handled
// here in onMessageReceived whether the app is in the foreground or background. Data messages are the type
// traditionally used with GCM. Notification messages are only received here in onMessageReceived when the app
// is in the foreground. When the app is in the background an automatically generated notification is displayed.
// When the user taps on the notification they are returned to the app. Messages containing both notification
// and data payloads are treated as notification messages. The Firebase console always sends notification
// messages. For more see: https://firebase.google.com/docs/cloud-messaging/concept-options
// [END_EXCLUDE]
// TODO(developer): Handle FCM messages here.
// Not getting messages here? See why this may be: https://goo.gl/39bRNJ
Log.d(TAG, "From: ${remoteMessage.from}")
// Check if message contains a data payload.
remoteMessage.data.isNotEmpty().let {
Log.d(TAG, "Message data payload: " + remoteMessage.data)
}
// Check if message contains a notification payload.
remoteMessage.notification?.let {
Log.d(TAG, "Message Notification Body: ${it.body}")
}
// Also if you intend on generating your own notifications as a result of a received FCM
// message, here is where that should be initiated. See sendNotification method below.
}
// [END receive_message]
// [START on_new_token]
/**
* Called if InstanceID token is updated. This may occur if the security of
* the previous token had been compromised. Note that this is called when the InstanceID token
* is initially generated so this is where you would retrieve the token.
*/
override fun onNewToken(token: String) {
Log.d(TAG, "Refreshed token: $token")
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// Instance ID token to your app server.
sendRegistrationToServer(token)
}
// [END on_new_token]
/**
* Persist token to third-party servers.
*
* Modify this method to associate the user's FCM InstanceID token with any server-side account
* maintained by your application.
*
* @param token The new token.
*/
private fun sendRegistrationToServer(token: String?) {
// TODO: Implement this method to send token to your app server.
Log.d(TAG, "sendRegistrationTokenToServer($token)")
}
/**
* Create and show a local notification containing the received FCM message.
*
* @param messageBody FCM message body received.
*/
private fun sendNotification(messageBody: String) {
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT)
val channelId = "txgz999_test"
val notificationBuilder = NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle(getString(R.string.app_name))
.setContentText(messageBody)
.setContentIntent(pendingIntent)
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Since android Oreo notification channel is needed.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(channelId,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT)
notificationManager.createNotificationChannel(channel)
}
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build())
}
companion object {
private const val TAG = "MyFirebaseMsgService"
}
}
and register in AndroidManifest.xml:
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
I originally got a runtime error when the app receives messages: the app keeps on closing. From menu View->Tool Windows->Logcat, I realized the android:name
of the service is wrong: it is .java.MyFirebaseMessagingService
in the sample. I need to change it to either .MyFirebaseMessagingService
or com.txgz999.ask1.MyFirebaseMessagingService
. Notice that (https://stuff.mit.edu/afs/sipb/project/android/docs/guide/topics/manifest/manifest-intro.html)
(Inside this file) as a shorthand, if the first character of the string is a period, the string is appended to the application's package name (as specified by the element's package attribute).
This sample contains some good explanation and suggestions: the data message is handled by the app first, then send a local notification to inform user.