diff --git a/packages/firebase_messaging/CHANGELOG.md b/packages/firebase_messaging/CHANGELOG.md index 8d5fb83365e3..696f6036a860 100644 --- a/packages/firebase_messaging/CHANGELOG.md +++ b/packages/firebase_messaging/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.0.0 +* **Breaking Change** Separated onLaunch to an specific method to be able to retrieve launch + message in a synchronous way. + ## 5.0.4 * Automatically use version from pubspec.yaml when reporting usage to Firebase. diff --git a/packages/firebase_messaging/README.md b/packages/firebase_messaging/README.md index e692ef3346ba..1ff2585d6298 100644 --- a/packages/firebase_messaging/README.md +++ b/packages/firebase_messaging/README.md @@ -57,12 +57,18 @@ Next, you should probably request permissions for receiving Push Notifications. ## Receiving Messages -Messages are sent to your Flutter app via the `onMessage`, `onLaunch`, and `onResume` callbacks that you configured with the plugin during setup. Here is how different message types are delivered on the supported platforms: +Launch messages (when application is closed) are retrieved on application init so you can act based on the notification data: + +```dart + Map message = await _firebaseMessaging.getLaunchMessage(); +``` + +Messages when the application is not in foreground are sent to your Flutter app via the `onMessage` and `onResume` callbacks that you configured with the plugin during setup. Here is how different message types are delivered on the supported platforms: | | App in Foreground | App in Background | App Terminated | | --------------------------: | ----------------- | ----------------- | -------------- | -| **Notification on Android** | `onMessage` | Notification is delivered to system tray. When the user clicks on it to open app `onResume` fires if `click_action: FLUTTER_NOTIFICATION_CLICK` is set (see below). | Notification is delivered to system tray. When the user clicks on it to open app `onLaunch` fires if `click_action: FLUTTER_NOTIFICATION_CLICK` is set (see below). | -| **Notification on iOS** | `onMessage` | Notification is delivered to system tray. When the user clicks on it to open app `onResume` fires. | Notification is delivered to system tray. When the user clicks on it to open app `onLaunch` fires. | +| **Notification on Android** | `onMessage` | Notification is delivered to system tray. When the user clicks on it to open app `onResume` fires if `click_action: FLUTTER_NOTIFICATION_CLICK` is set (see below). | Notification is delivered to system tray. When the user clicks on it to open app `geLaunchMessage` fires if `click_action: FLUTTER_NOTIFICATION_CLICK` is set (see below). | +| **Notification on iOS** | `onMessage` | Notification is delivered to system tray. When the user clicks on it to open app `onResume` fires. | Notification is delivered to system tray. When the user clicks on it to open app `getLaunchMessage` fires. | | **Data Message on Android** | `onMessage` | `onMessage` while app stays in the background. | *not supported by plugin, message is lost* | | **Data Message on iOS** | `onMessage` | Message is stored by FCM and delivered to app via `onMessage` when the app is brought back to foreground. | Message is stored by FCM and delivered to app via `onMessage` when the app is brought back to foreground. | diff --git a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FirebaseMessagingPlugin.java b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FirebaseMessagingPlugin.java index 7c01c516c843..316ef11abd49 100644 --- a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FirebaseMessagingPlugin.java +++ b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FirebaseMessagingPlugin.java @@ -113,8 +113,12 @@ public void onComplete(@NonNull Task task) { channel.invokeMethod("onToken", task.getResult().getToken()); } }); + result.success(null); + } else if ("getLaunchMessage".equals(call.method)) { if (registrar.activity() != null) { - sendMessageFromIntent("onLaunch", registrar.activity().getIntent()); + Map message = this.getMessageFromIntent(registrar.activity().getIntent()); + result.success(message); + return; } result.success(null); } else if ("subscribeToTopic".equals(call.method)) { @@ -176,15 +180,14 @@ public boolean onNewIntent(Intent intent) { return res; } - /** @return true if intent contained a message to send. */ - private boolean sendMessageFromIntent(String method, Intent intent) { + private Map getMessageFromIntent(Intent intent) { if (CLICK_ACTION_VALUE.equals(intent.getAction()) || CLICK_ACTION_VALUE.equals(intent.getStringExtra("click_action"))) { Map message = new HashMap<>(); Bundle extras = intent.getExtras(); if (extras == null) { - return false; + return null; } Map notificationMap = new HashMap<>(); @@ -199,10 +202,20 @@ private boolean sendMessageFromIntent(String method, Intent intent) { message.put("notification", notificationMap); message.put("data", dataMap); + return message; + } + return null; + } - channel.invokeMethod(method, message); - return true; + /** @return true if intent contained a message to send. */ + private boolean sendMessageFromIntent(String method, Intent intent) { + Map message = this.getMessageFromIntent(intent); + + if (message == null) { + return false; } - return false; + + channel.invokeMethod(method, message); + return true; } } diff --git a/packages/firebase_messaging/example/lib/main.dart b/packages/firebase_messaging/example/lib/main.dart index cf8e64347bed..900c8c65247c 100644 --- a/packages/firebase_messaging/example/lib/main.dart +++ b/packages/firebase_messaging/example/lib/main.dart @@ -137,15 +137,22 @@ class _PushMessagingExampleState extends State { @override void initState() { super.initState(); + } + + void initPushNotifications() async { + final Map message = + await _firebaseMessaging.getLaunchMessage(); + + if (message != null) { + print("getLaunchMessage $message"); + _navigateToItemDetail(message); + } + _firebaseMessaging.configure( onMessage: (Map message) async { print("onMessage: $message"); _showItemDialog(message); }, - onLaunch: (Map message) async { - print("onLaunch: $message"); - _navigateToItemDetail(message); - }, onResume: (Map message) async { print("onResume: $message"); _navigateToItemDetail(message); diff --git a/packages/firebase_messaging/ios/Classes/FirebaseMessagingPlugin.m b/packages/firebase_messaging/ios/Classes/FirebaseMessagingPlugin.m index 625409274191..fd24241f28b0 100644 --- a/packages/firebase_messaging/ios/Classes/FirebaseMessagingPlugin.m +++ b/packages/firebase_messaging/ios/Classes/FirebaseMessagingPlugin.m @@ -68,12 +68,11 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; result(nil); + } else if ([@"getLaunchMessage" isEqualToString:method]) { + result(_launchNotification); } else if ([@"configure" isEqualToString:method]) { [FIRMessaging messaging].shouldEstablishDirectChannel = true; [[UIApplication sharedApplication] registerForRemoteNotifications]; - if (_launchNotification != nil) { - [_channel invokeMethod:@"onLaunch" arguments:_launchNotification]; - } result(nil); } else if ([@"subscribeToTopic" isEqualToString:method]) { NSString *topic = call.arguments; diff --git a/packages/firebase_messaging/lib/firebase_messaging.dart b/packages/firebase_messaging/lib/firebase_messaging.dart index d048f14d7196..d323ff0584fd 100644 --- a/packages/firebase_messaging/lib/firebase_messaging.dart +++ b/packages/firebase_messaging/lib/firebase_messaging.dart @@ -30,7 +30,6 @@ class FirebaseMessaging { final Platform _platform; MessageHandler _onMessage; - MessageHandler _onLaunch; MessageHandler _onResume; /// On iOS, prompts the user for notification permissions the first time @@ -59,16 +58,19 @@ class FirebaseMessaging { /// Sets up [MessageHandler] for incoming messages. void configure({ MessageHandler onMessage, - MessageHandler onLaunch, MessageHandler onResume, }) { _onMessage = onMessage; - _onLaunch = onLaunch; _onResume = onResume; _channel.setMethodCallHandler(_handleMethod); _channel.invokeMethod('configure'); } + Future> getLaunchMessage() async { + return await _channel + .invokeMethod>('getLaunchMessage'); + } + final StreamController _tokenStreamController = StreamController.broadcast(); @@ -126,8 +128,6 @@ class FirebaseMessaging { return null; case "onMessage": return _onMessage(call.arguments.cast()); - case "onLaunch": - return _onLaunch(call.arguments.cast()); case "onResume": return _onResume(call.arguments.cast()); default: diff --git a/packages/firebase_messaging/pubspec.yaml b/packages/firebase_messaging/pubspec.yaml index 10d2f8e2eae5..4aec21f86b9f 100644 --- a/packages/firebase_messaging/pubspec.yaml +++ b/packages/firebase_messaging/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Firebase Cloud Messaging, a cross-platform messaging solution that lets you reliably deliver messages on Android and iOS. author: Flutter Team homepage: https://github.com/flutter/plugins/tree/master/packages/firebase_messaging -version: 5.0.4 +version: 6.0.0 flutter: plugin: diff --git a/packages/firebase_messaging/test/firebase_messaging_test.dart b/packages/firebase_messaging/test/firebase_messaging_test.dart index aefea5981bda..ee2c110d7854 100644 --- a/packages/firebase_messaging/test/firebase_messaging_test.dart +++ b/packages/firebase_messaging/test/firebase_messaging_test.dart @@ -89,15 +89,48 @@ void main() { expect((await iosSettingsFromStream).toMap(), iosSettings.toMap()); }); + test('getLaunchMessage', () async { + final MethodChannel channel = + const MethodChannel('plugins.flutter.io/firebase_messaging'); + firebaseMessaging = + FirebaseMessaging.private(channel, const LocalPlatform()); + + channel.setMockMethodCallHandler((MethodCall methodCall) async { + switch (methodCall.method) { + case 'getLaunchMessage': + return { + 'notification': { + 'title': 'Title', + 'body': 'Body', + 'click_action': 'FLUTTER_NOTIFICATION_CLICK', + }, + 'data': { + 'variable1': 'value1', + 'variable2': 'value2', + }, + }; + default: + return null; + } + }); + + final Map message = + await firebaseMessaging.getLaunchMessage(); + + expect(message['notification']['title'], 'Title'); + expect(message['notification']['body'], 'Body'); + expect( + message['notification']['click_action'], 'FLUTTER_NOTIFICATION_CLICK'); + expect(message['data']['variable1'], 'value1'); + expect(message['data']['variable2'], 'value2'); + }); + test('incoming messages', () async { final Completer onMessage = Completer(); - final Completer onLaunch = Completer(); final Completer onResume = Completer(); firebaseMessaging.configure(onMessage: (dynamic m) async { onMessage.complete(m); - }, onLaunch: (dynamic m) async { - onLaunch.complete(m); }, onResume: (dynamic m) async { onResume.complete(m); }); @@ -105,16 +138,10 @@ void main() { verify(mockChannel.setMethodCallHandler(captureAny)).captured.single; final Map onMessageMessage = {}; - final Map onLaunchMessage = {}; final Map onResumeMessage = {}; await handler(MethodCall('onMessage', onMessageMessage)); expect(await onMessage.future, onMessageMessage); - expect(onLaunch.isCompleted, isFalse); - expect(onResume.isCompleted, isFalse); - - await handler(MethodCall('onLaunch', onLaunchMessage)); - expect(await onLaunch.future, onLaunchMessage); expect(onResume.isCompleted, isFalse); await handler(MethodCall('onResume', onResumeMessage));