diff --git a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingPlugin.java b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingPlugin.java index 14282cfc5c63..f15d70899e58 100644 --- a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingPlugin.java +++ b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingPlugin.java @@ -71,6 +71,12 @@ public class FlutterFirebaseMessagingPlugin private Observer remoteMessageObserver; private final LiveData liveDataToken = FlutterFirebaseTokenLiveData.getInstance(); private Observer tokenObserver; + private final LiveData liveDataRegistered = + FlutterFirebaseRegisteredLiveData.getInstance(); + private Observer registeredObserver; + private final LiveData liveDataUnregistered = + FlutterFirebaseUnregisteredLiveData.getInstance(); + private Observer unregisteredObserver; private RemoteMessage initialMessage; // We store the initial notification in a separate variable @@ -93,10 +99,14 @@ private void initInstance(BinaryMessenger messenger) { channel.invokeMethod("Messaging#onMessage", content); }; tokenObserver = token -> channel.invokeMethod("Messaging#onTokenRefresh", token); + registeredObserver = fid -> channel.invokeMethod("Messaging#onRegistered", fid); + unregisteredObserver = fid -> channel.invokeMethod("Messaging#onUnregistered", fid); // We remove these observers in the onDetachedFromEngine method. Using "observeForever()" // allows us to use without a LifecycleOwner. liveDataRemoteMessage.observeForever(remoteMessageObserver); liveDataToken.observeForever(tokenObserver); + liveDataRegistered.observeForever(registeredObserver); + liveDataUnregistered.observeForever(unregisteredObserver); registerPlugin(channelName, this); } @@ -111,6 +121,8 @@ public void onAttachedToEngine(FlutterPluginBinding binding) { public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { liveDataToken.removeObserver(tokenObserver); liveDataRemoteMessage.removeObserver(remoteMessageObserver); + liveDataRegistered.removeObserver(registeredObserver); + liveDataUnregistered.removeObserver(unregisteredObserver); } @Override @@ -184,6 +196,38 @@ private Task> getToken() { return taskCompletionSource.getTask(); } + private Task register() { + TaskCompletionSource taskCompletionSource = new TaskCompletionSource<>(); + + cachedThreadPool.execute( + () -> { + try { + Tasks.await(FirebaseMessaging.getInstance().register()); + taskCompletionSource.setResult(null); + } catch (Exception e) { + taskCompletionSource.setException(e); + } + }); + + return taskCompletionSource.getTask(); + } + + private Task unregister() { + TaskCompletionSource taskCompletionSource = new TaskCompletionSource<>(); + + cachedThreadPool.execute( + () -> { + try { + Tasks.await(FirebaseMessaging.getInstance().unregister()); + taskCompletionSource.setResult(null); + } catch (Exception e) { + taskCompletionSource.setException(e); + } + }); + + return taskCompletionSource.getTask(); + } + private Task subscribeToTopic(Map arguments) { TaskCompletionSource taskCompletionSource = new TaskCompletionSource<>(); @@ -567,6 +611,12 @@ public void onMethodCall(final MethodCall call, @NonNull final Result result) { case "Messaging#getToken": methodCallTask = getToken(); break; + case "Messaging#register": + methodCallTask = register(); + break; + case "Messaging#unregister": + methodCallTask = unregister(); + break; case "Messaging#subscribeToTopic": methodCallTask = subscribeToTopic(call.arguments()); break; diff --git a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingService.java b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingService.java index 85d557453646..c9a5d434813d 100644 --- a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingService.java +++ b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseMessagingService.java @@ -14,6 +14,16 @@ public void onNewToken(@NonNull String token) { FlutterFirebaseTokenLiveData.getInstance().postToken(token); } + @Override + public void onRegistered(@NonNull String installationId) { + FlutterFirebaseRegisteredLiveData.getInstance().postFid(installationId); + } + + @Override + public void onUnregistered(@NonNull String installationId) { + FlutterFirebaseUnregisteredLiveData.getInstance().postFid(installationId); + } + @Override public void onMessageReceived(@NonNull RemoteMessage remoteMessage) { // Added for commenting purposes; diff --git a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseRegisteredLiveData.java b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseRegisteredLiveData.java new file mode 100644 index 000000000000..16d8228ba1c7 --- /dev/null +++ b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseRegisteredLiveData.java @@ -0,0 +1,22 @@ +// Copyright 2026 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.firebase.messaging; + +import androidx.lifecycle.LiveData; + +public class FlutterFirebaseRegisteredLiveData extends LiveData { + private static FlutterFirebaseRegisteredLiveData instance; + + public static FlutterFirebaseRegisteredLiveData getInstance() { + if (instance == null) { + instance = new FlutterFirebaseRegisteredLiveData(); + } + return instance; + } + + public void postFid(String fid) { + postValue(fid); + } +} diff --git a/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseUnregisteredLiveData.java b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseUnregisteredLiveData.java new file mode 100644 index 000000000000..a9168f29e1bf --- /dev/null +++ b/packages/firebase_messaging/firebase_messaging/android/src/main/java/io/flutter/plugins/firebase/messaging/FlutterFirebaseUnregisteredLiveData.java @@ -0,0 +1,22 @@ +// Copyright 2026 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.firebase.messaging; + +import androidx.lifecycle.LiveData; + +public class FlutterFirebaseUnregisteredLiveData extends LiveData { + private static FlutterFirebaseUnregisteredLiveData instance; + + public static FlutterFirebaseUnregisteredLiveData getInstance() { + if (instance == null) { + instance = new FlutterFirebaseUnregisteredLiveData(); + } + return instance; + } + + public void postFid(String fid) { + postValue(fid); + } +} diff --git a/packages/firebase_messaging/firebase_messaging/example/android/app/src/main/AndroidManifest.xml b/packages/firebase_messaging/firebase_messaging/example/android/app/src/main/AndroidManifest.xml index 74a78b939e5e..1f7a734b088f 100644 --- a/packages/firebase_messaging/firebase_messaging/example/android/app/src/main/AndroidManifest.xml +++ b/packages/firebase_messaging/firebase_messaging/example/android/app/src/main/AndroidManifest.xml @@ -30,6 +30,9 @@ +