diff --git a/messaging/app/src/main/java/com/google/firebase/example/messaging/MainActivity.java b/messaging/app/src/main/java/com/google/firebase/example/messaging/MainActivity.java index 63032304e..3573aa7ea 100644 --- a/messaging/app/src/main/java/com/google/firebase/example/messaging/MainActivity.java +++ b/messaging/app/src/main/java/com/google/firebase/example/messaging/MainActivity.java @@ -141,4 +141,20 @@ private void askNotificationPermission() { } } // [END ask_post_notifications] + + private void registerFid() { + // [START register_fid] + // Trigger manual registration if auto-initialization is turned off. + // Consider calling this every time the app starts to guarantee sync status. + FirebaseMessaging.getInstance().register() + .addOnCompleteListener(this, task -> { + if (!task.isSuccessful()) { + // Registration failed. Consider retrying the registration with exponential backoff. + Log.w(TAG, "Failed to register with Firebase Cloud Messaging", task.getException()); + } + // Success! The Firebase Installation ID can be used to target messages to this app + // instance and will be delivered asynchronously to your onRegistered() callback. + }); + // [END register_fid] + } } diff --git a/messaging/app/src/main/java/com/google/firebase/example/messaging/MyFirebaseMessagingService.java b/messaging/app/src/main/java/com/google/firebase/example/messaging/MyFirebaseMessagingService.java index 516efd6bb..d871207d3 100644 --- a/messaging/app/src/main/java/com/google/firebase/example/messaging/MyFirebaseMessagingService.java +++ b/messaging/app/src/main/java/com/google/firebase/example/messaging/MyFirebaseMessagingService.java @@ -136,4 +136,24 @@ public Result doWork() { return Result.success(); } } + + // [START on_fid_registered] + /** + * There are three scenarios when `onRegistered` is called: + * 1) Every time a manual `register()` call finishes successfully + * 2) Whenever the FID is changed and the app is re-registered with FCM via the new FID + * 3) Automatically on the first app startup or routine sync when auto-initialization is enabled. + * Under #2, there are three scenarios when the existing FID is changed: + * A) App is restored to a new device + * B) User uninstalls/reinstalls the app + * C) User clears app data + */ + @Override + public void onRegistered(@NonNull String installationId) { + Log.d(TAG, "Registered installation ID: " + installationId); + + // Send the Firebase Installation ID to your app server. + sendRegistrationToServer(installationId); + } + // [END on_fid_registered] } diff --git a/messaging/app/src/main/java/com/google/firebase/example/messaging/kotlin/MainActivity.kt b/messaging/app/src/main/java/com/google/firebase/example/messaging/kotlin/MainActivity.kt index 4c42becaf..589dcb786 100644 --- a/messaging/app/src/main/java/com/google/firebase/example/messaging/kotlin/MainActivity.kt +++ b/messaging/app/src/main/java/com/google/firebase/example/messaging/kotlin/MainActivity.kt @@ -9,9 +9,9 @@ import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat +import com.google.firebase.Firebase import com.google.firebase.firestore.FieldValue import com.google.firebase.firestore.firestore -import com.google.firebase.Firebase import com.google.firebase.messaging.messaging import com.google.firebase.messaging.remoteMessage import kotlinx.coroutines.launch @@ -20,135 +20,150 @@ import java.util.concurrent.atomic.AtomicInteger class MainActivity : AppCompatActivity() { - companion object { - private const val TAG = "MainActivity" - private const val NOTIFICATION_REQUEST_CODE = 1234 - } + companion object { + private const val TAG = "MainActivity" + private const val NOTIFICATION_REQUEST_CODE = 1234 + } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - // [START handle_data_extras] - intent.extras?.let { - for (key in it.keySet()) { - val value = intent.extras?.get(key) - Log.d(TAG, "Key: $key Value: $value") - } - } - // [END handle_data_extras] + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // [START handle_data_extras] + intent.extras?.let { + for (key in it.keySet()) { + val value = intent.extras?.get(key) + Log.d(TAG, "Key: $key Value: $value") + } } + // [END handle_data_extras] + } - fun runtimeEnableAutoInit() { - // [START fcm_runtime_enable_auto_init] - Firebase.messaging.isAutoInitEnabled = true - // [END fcm_runtime_enable_auto_init] - } + fun runtimeEnableAutoInit() { + // [START fcm_runtime_enable_auto_init] + Firebase.messaging.isAutoInitEnabled = true + // [END fcm_runtime_enable_auto_init] + } - fun deviceGroupUpstream() { - // [START fcm_device_group_upstream] - val to = "a_unique_key" // the notification key - val msgId = AtomicInteger() - Firebase.messaging.send( - remoteMessage(to) { - setMessageId(msgId.get().toString()) - addData("hello", "world") - }, - ) - // [END fcm_device_group_upstream] - } + fun deviceGroupUpstream() { + // [START fcm_device_group_upstream] + val to = "a_unique_key" // the notification key + val msgId = AtomicInteger() + Firebase.messaging.send( + remoteMessage(to) { + setMessageId(msgId.get().toString()) + addData("hello", "world") + }, + ) + // [END fcm_device_group_upstream] + } - fun sendUpstream() { - val SENDER_ID = "YOUR_SENDER_ID" - val messageId = 0 // Increment for each - // [START fcm_send_upstream] - val fm = Firebase.messaging - fm.send( - remoteMessage("$SENDER_ID@fcm.googleapis.com") { - setMessageId(messageId.toString()) - addData("my_message", "Hello World") - addData("my_action", "SAY_HELLO") - }, - ) - // [END fcm_send_upstream] - } + fun sendUpstream() { + val SENDER_ID = "YOUR_SENDER_ID" + val messageId = 0 // Increment for each + // [START fcm_send_upstream] + val fm = Firebase.messaging + fm.send( + remoteMessage("$SENDER_ID@fcm.googleapis.com") { + setMessageId(messageId.toString()) + addData("my_message", "Hello World") + addData("my_action", "SAY_HELLO") + }, + ) + // [END fcm_send_upstream] + } - fun subscribeTopics() { - // [START subscribe_topics] - Firebase.messaging.subscribeToTopic("weather") - .addOnCompleteListener { task -> - var msg = "Subscribed" - if (!task.isSuccessful) { - msg = "Subscribe failed" - } - Log.d(TAG, msg) - Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() - } - // [END subscribe_topics] - } + fun subscribeTopics() { + // [START subscribe_topics] + Firebase.messaging.subscribeToTopic("weather") + .addOnCompleteListener { task -> + var msg = "Subscribed" + if (!task.isSuccessful) { + msg = "Subscribe failed" + } + Log.d(TAG, msg) + Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() + } + // [END subscribe_topics] + } - fun logRegToken() { - // [START log_reg_token] - Firebase.messaging.getToken().addOnCompleteListener { task -> - if (!task.isSuccessful) { - Log.w(TAG, "Fetching FCM registration token failed", task.exception) - return@addOnCompleteListener - } + fun logRegToken() { + // [START log_reg_token] + Firebase.messaging.getToken().addOnCompleteListener { task -> + if (!task.isSuccessful) { + Log.w(TAG, "Fetching FCM registration token failed", task.exception) + return@addOnCompleteListener + } - // Get new FCM registration token - val token = task.result + // Get new FCM registration token + val token = task.result - // Log and toast - val msg = "FCM Registration token: $token" - Log.d(TAG, msg) - Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() - } - // [END log_reg_token] + // Log and toast + val msg = "FCM Registration token: $token" + Log.d(TAG, msg) + Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() } + // [END log_reg_token] + } - // [START ask_post_notifications] - // Declare the launcher at the top of your Activity/Fragment: - private val requestPermissionLauncher = registerForActivityResult( - ActivityResultContracts.RequestPermission(), - ) { isGranted: Boolean -> - if (isGranted) { - // FCM SDK (and your app) can post notifications. - } else { - // TODO: Inform user that that your app will not show notifications. - } + // [START ask_post_notifications] + // Declare the launcher at the top of your Activity/Fragment: + private val requestPermissionLauncher = registerForActivityResult( + ActivityResultContracts.RequestPermission(), + ) { isGranted: Boolean -> + if (isGranted) { + // FCM SDK (and your app) can post notifications. + } else { + // TODO: Inform user that that your app will not show notifications. } + } - private fun askNotificationPermission() { - // This is only necessary for API level >= 33 (TIRAMISU) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == - PackageManager.PERMISSION_GRANTED - ) { - // FCM SDK (and your app) can post notifications. - } else if (shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) { - // TODO: display an educational UI explaining to the user the features that will be enabled - // by them granting the POST_NOTIFICATION permission. This UI should provide the user - // "OK" and "No thanks" buttons. If the user selects "OK," directly request the permission. - // If the user selects "No thanks," allow the user to continue without notifications. - } else { - // Directly ask for the permission - requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) - } - } + private fun askNotificationPermission() { + // This is only necessary for API level >= 33 (TIRAMISU) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == + PackageManager.PERMISSION_GRANTED + ) { + // FCM SDK (and your app) can post notifications. + } else if (shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) { + // TODO: display an educational UI explaining to the user the features that will be enabled + // by them granting the POST_NOTIFICATION permission. This UI should provide the user + // "OK" and "No thanks" buttons. If the user selects "OK," directly request the permission. + // If the user selects "No thanks," allow the user to continue without notifications. + } else { + // Directly ask for the permission + requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) + } } - // [END ask_post_notifications] - - // [START get_store_token] - private suspend fun getAndStoreRegToken(): String { - val token = Firebase.messaging.token.await() - // Add token and timestamp to Firestore for this user - val deviceToken = hashMapOf( - "token" to token, - "timestamp" to FieldValue.serverTimestamp(), - ) - - // Get user ID from Firebase Auth or your own server - Firebase.firestore.collection("fcmTokens").document("myuserid") - .set(deviceToken).await() - return token + } + // [END ask_post_notifications] + + // [START get_store_token] + private suspend fun getAndStoreRegToken(): String { + val token = Firebase.messaging.token.await() + // Add token and timestamp to Firestore for this user + val deviceToken = hashMapOf( + "token" to token, + "timestamp" to FieldValue.serverTimestamp(), + ) + + // Get user ID from Firebase Auth or your own server + Firebase.firestore.collection("fcmTokens").document("myuserid") + .set(deviceToken).await() + return token + } + // [END get_store_token] + + private fun registerFid() { + // [START register_fid] + // Trigger manual registration if auto-initialization is turned off. + // Consider calling this every time the app starts to guarantee sync status. + FirebaseMessaging.getInstance().register() + .addOnCompleteListener(this) { task -> + if (!task.isSuccessful()) { + Log.w(TAG, "Failed to register with Firebase Cloud Messaging", task.exception) + } + // Success! The Firebase Installation ID can be used to target messages to this app + // instance and will be delivered asynchronously to your onRegistered() callback. + } + // [END register_fid] } - // [END get_store_token] } diff --git a/messaging/app/src/main/java/com/google/firebase/example/messaging/kotlin/MyFirebaseMessagingService.kt b/messaging/app/src/main/java/com/google/firebase/example/messaging/kotlin/MyFirebaseMessagingService.kt index 719a6e960..279ed4b09 100644 --- a/messaging/app/src/main/java/com/google/firebase/example/messaging/kotlin/MyFirebaseMessagingService.kt +++ b/messaging/app/src/main/java/com/google/firebase/example/messaging/kotlin/MyFirebaseMessagingService.kt @@ -133,4 +133,23 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { return Result.success() } } + + // [START on_fid_registered] + /** + * There are three scenarios when `onRegistered` is called: + * 1) Every time a manual `register()` call finishes successfully + * 2) Whenever the FID is changed and the app is re-registered with FCM via the new FID. + * 3) Automatically on the first app startup or routine sync when auto-initialization is enabled. + * Under #2, there are three scenarios when the existing FID is changed: + * A) App is restored to a new device + * B) User uninstalls/reinstalls the app + * C) User clears app data + */ + override fun onRegistered(installationId: String) { + Log.d(TAG, "Registered installation ID: $installationId") + + // Send the Firebase Installation ID to your app server. + sendRegistrationToServer(installationId) + } + // [END on_fid_registered] }