Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(firebase_messaging): Support for multiple senderId (Android) #3936

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/firebase_messaging/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
- Support for multiple senderId

## 7.0.3

- Update a dependency to the latest release.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.android.gms.tasks.Continuation;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.TaskCompletionSource;
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.InstanceIdResult;
import com.google.firebase.messaging.FirebaseMessaging;
Expand All @@ -37,6 +40,7 @@
public class FirebaseMessagingPlugin extends BroadcastReceiver
implements MethodCallHandler, NewIntentListener, FlutterPlugin, ActivityAware {

private static final String FCM_VALUE = "FCM";
private static final String CLICK_ACTION_VALUE = "FLUTTER_NOTIFICATION_CLICK";
private static final String TAG = "FirebaseMessagingPlugin";

Expand Down Expand Up @@ -128,6 +132,7 @@ public void onReceive(Context context, Intent intent) {
@NonNull
private Map<String, Object> parseRemoteMessage(RemoteMessage message) {
Map<String, Object> content = new HashMap<>();
content.put("senderId", message.getFrom());
content.put("data", message.getData());

RemoteMessage.Notification notification = message.getNotification();
Expand Down Expand Up @@ -179,17 +184,17 @@ public void onMethodCall(final MethodCall call, final Result result) {
FlutterFirebaseMessagingService.onInitialized();
result.success(true);
} else if ("configure".equals(call.method)) {
FirebaseInstanceId.getInstance()
.getInstanceId()
String senderId = (String) call.arguments;
getToken(senderId)
.addOnCompleteListener(
new OnCompleteListener<InstanceIdResult>() {
new OnCompleteListener<String>() {
@Override
public void onComplete(@NonNull Task<InstanceIdResult> task) {
public void onComplete(@NonNull Task<String> task) {
if (!task.isSuccessful()) {
Log.w(TAG, "getToken, error fetching instanceID: ", task.getException());
Log.w(TAG, "configure, error fetching instanceID: ", task.getException());
return;
}
channel.invokeMethod("onToken", task.getResult().getToken());
channel.invokeMethod("onToken", task.getResult());
}
});
if (mainActivity != null) {
Expand Down Expand Up @@ -231,28 +236,34 @@ public void onComplete(@NonNull Task<Void> task) {
}
});
} else if ("getToken".equals(call.method)) {
FirebaseInstanceId.getInstance()
.getInstanceId()
String senderId = (String) call.arguments;
getToken(senderId)
.addOnCompleteListener(
new OnCompleteListener<InstanceIdResult>() {
new OnCompleteListener<String>() {
@Override
public void onComplete(@NonNull Task<InstanceIdResult> task) {
public void onComplete(@NonNull Task<String> task) {
if (!task.isSuccessful()) {
Log.w(TAG, "getToken, error fetching instanceID: ", task.getException());
result.success(null);
return;
}

result.success(task.getResult().getToken());
result.success(task.getResult());
}
});
} else if ("deleteInstanceID".equals(call.method)) {
final String senderId = (String) call.arguments;
new Thread(
new Runnable() {
@Override
public void run() {
try {
FirebaseInstanceId.getInstance().deleteInstanceId();
if (senderId != null) {
FirebaseInstanceId.getInstance().deleteToken(senderId, FCM_VALUE);
} else {
FirebaseInstanceId.getInstance().deleteInstanceId();
}

if (mainActivity != null) {
mainActivity.runOnUiThread(
new Runnable() {
Expand Down Expand Up @@ -288,6 +299,39 @@ public void run() {
}
}

private Task<String> getToken(final String senderId) {
if (senderId == null) {
return FirebaseInstanceId.getInstance()
.getInstanceId()
.continueWithTask(
new Continuation<InstanceIdResult, Task<String>>() {
@Override
public Task<String> then(@NonNull Task<InstanceIdResult> task) throws Exception {
if (!task.isSuccessful()) {
return Tasks.forException(task.getException());
}
InstanceIdResult result = task.getResult();
return Tasks.forResult(task.getResult().getToken());
}
});
} else {
final TaskCompletionSource<String> task = new TaskCompletionSource<String>();
new Thread(
new Runnable() {
@Override
public void run() {
try {
task.setResult(FirebaseInstanceId.getInstance().getToken(senderId, FCM_VALUE));
} catch (IOException e) {
task.setException(e);
}
}
})
.start();
return task.getTask();
}
}

@Override
public boolean onNewIntent(Intent intent) {
boolean res = sendMessageFromIntent("onResume", intent);
Expand Down Expand Up @@ -318,6 +362,9 @@ private boolean sendMessageFromIntent(String method, Intent intent) {
}
}

if (dataMap.containsKey("from")) {
message.put("senderId", dataMap.get("from"));
}
message.put("notification", notificationMap);
message.put("data", dataMap);

Expand Down
82 changes: 56 additions & 26 deletions packages/firebase_messaging/lib/firebase_messaging.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ void _fcmSetupBackgroundChannel(
backgroundChannel.invokeMethod<void>('FcmDartService#initialized');
}

class FirebaseMessagingHandlers {
MessageHandler _onMessage;
MessageHandler _onBackgroundMessage;
MessageHandler _onLaunch;
MessageHandler _onResume;
}

/// Implementation of the Firebase Cloud Messaging API for Flutter.
///
/// Your app should call [requestNotificationPermissions] first and then
Expand All @@ -64,11 +71,9 @@ class FirebaseMessaging {
const MethodChannel('plugins.flutter.io/firebase_messaging'));

final MethodChannel _channel;
bool _initialized = false;

MessageHandler _onMessage;
MessageHandler _onBackgroundMessage;
MessageHandler _onLaunch;
MessageHandler _onResume;
Map<String, FirebaseMessagingHandlers> _handlers = {};

/// On iOS, prompts the user for notification permissions the first time
/// it is called.
Expand Down Expand Up @@ -97,23 +102,33 @@ class FirebaseMessaging {
}

/// Sets up [MessageHandler] for incoming messages.
/// Use [senderId] to receive messages from another firebase app.
/// See [Firebase official documentation](https://firebase.google.com/docs/cloud-messaging/concept-options#receiving-messages-from-multiple-senders)
void configure({
String senderId,
MessageHandler onMessage,
MessageHandler onBackgroundMessage,
MessageHandler onLaunch,
MessageHandler onResume,
}) {
_onMessage = onMessage;
_onLaunch = onLaunch;
_onResume = onResume;
_channel.setMethodCallHandler(_handleMethod);
_channel.invokeMethod<void>('configure');
if (!_initialized) {
_channel.setMethodCallHandler(_handleMethod);
_initialized = true;
}

FirebaseMessagingHandlers handlers = FirebaseMessagingHandlers();
_handlers[senderId] = handlers;
handlers._onMessage = onMessage;
handlers._onLaunch = onLaunch;
handlers._onResume = onResume;

_channel.invokeMethod<void>('configure', senderId);
if (onBackgroundMessage != null) {
_onBackgroundMessage = onBackgroundMessage;
handlers._onBackgroundMessage = onBackgroundMessage;
final CallbackHandle backgroundSetupHandle =
PluginUtilities.getCallbackHandle(_fcmSetupBackgroundChannel);
final CallbackHandle backgroundMessageHandle =
PluginUtilities.getCallbackHandle(_onBackgroundMessage);
PluginUtilities.getCallbackHandle(handlers._onBackgroundMessage);

if (backgroundMessageHandle == null) {
throw ArgumentError(
Expand All @@ -123,13 +138,15 @@ class FirebaseMessaging {
);
}

_channel.invokeMethod<bool>(
'FcmDartService#start',
<String, dynamic>{
'setupHandle': backgroundSetupHandle.toRawHandle(),
'backgroundHandle': backgroundMessageHandle.toRawHandle()
},
);
if (!_initialized) {
_channel.invokeMethod<bool>(
'FcmDartService#start',
<String, dynamic>{
'setupHandle': backgroundSetupHandle.toRawHandle(),
'backgroundHandle': backgroundMessageHandle.toRawHandle()
},
);
}
}
}

Expand All @@ -142,8 +159,10 @@ class FirebaseMessaging {
}

/// Returns the FCM token.
Future<String> getToken() async {
return await _channel.invokeMethod<String>('getToken');
/// Use [senderId] to get the token from another sender.
/// See [Firebase official documentation](https://firebase.google.com/docs/cloud-messaging/concept-options#receiving-messages-from-multiple-senders)
Future<String> getToken({String senderId}) async {
return await _channel.invokeMethod<String>('getToken', senderId);
}

/// Subscribe to topic in background.
Expand All @@ -163,9 +182,11 @@ class FirebaseMessaging {
///
/// A new Instance ID is generated asynchronously if Firebase Cloud Messaging auto-init is enabled.
///
/// returns true if the operations executed successfully and false if an error ocurred
Future<bool> deleteInstanceID() async {
return await _channel.invokeMethod<bool>('deleteInstanceID');
/// returns true if the operations executed successfully and false if an error occurred
/// Use [senderId] remove a secondary sender
Future<bool> deleteInstanceID({String senderId}) async {
_handlers.remove(senderId);
return await _channel.invokeMethod<bool>('deleteInstanceID', senderId);
}

/// Determine whether FCM auto-initialization is enabled or disabled.
Expand All @@ -189,11 +210,20 @@ class FirebaseMessaging {
call.arguments.cast<String, bool>()));
return null;
case "onMessage":
return _onMessage(call.arguments.cast<String, dynamic>());
final args = call.arguments.cast<String, dynamic>();
String senderId = args['senderId'];
final handlers = _handlers[senderId];
return handlers._onMessage(args);
case "onLaunch":
return _onLaunch(call.arguments.cast<String, dynamic>());
final args = call.arguments.cast<String, dynamic>();
String senderId = args['senderId'];
final handlers = _handlers[senderId];
return handlers._onLaunch(call.arguments.cast<String, dynamic>());
case "onResume":
return _onResume(call.arguments.cast<String, dynamic>());
final args = call.arguments.cast<String, dynamic>();
String senderId = args['senderId'];
_handlers[senderId]._onResume(args);
return;
default:
throw UnsupportedError("Unrecognized JSON message");
}
Expand Down
Loading