diff --git a/packages/firebase_messaging/CHANGELOG.md b/packages/firebase_messaging/CHANGELOG.md index 4aa824b2b8d7..591355f1d5ef 100644 --- a/packages/firebase_messaging/CHANGELOG.md +++ b/packages/firebase_messaging/CHANGELOG.md @@ -1,3 +1,9 @@ +## 6.0.9 + +* Separated onLaunch to an specific method to be able to retrieve launch + message in a synchronous way. +* Deprecated the `onLaunch` function from `FirebaseMessaging.configure`. + ## 6.0.8 * Support for provisional notifications for iOS version >= 12. diff --git a/packages/firebase_messaging/README.md b/packages/firebase_messaging/README.md index 1ae547afd539..32914b5fa413 100644 --- a/packages/firebase_messaging/README.md +++ b/packages/firebase_messaging/README.md @@ -176,12 +176,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 `getLaunchMessage` 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 7176daa75796..d58995d85936 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 @@ -199,6 +199,13 @@ public void onComplete(@NonNull Task task) { sendMessageFromIntent("onLaunch", mainActivity.getIntent()); } result.success(null); + } else if ("getLaunchMessage".equals(call.method)) { + if (mainActivity != null) { + Map message = this.getMessageFromIntent(mainActivity.getIntent()); + result.success(message); + return; + } + result.success(null); } else if ("subscribeToTopic".equals(call.method)) { String topic = call.arguments(); FirebaseMessaging.getInstance() @@ -300,15 +307,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<>(); @@ -323,10 +329,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 d248b9c5cbc6..227c8e9fb064 100644 --- a/packages/firebase_messaging/example/lib/main.dart +++ b/packages/firebase_messaging/example/lib/main.dart @@ -138,15 +138,23 @@ class _PushMessagingExampleState extends State { @override void initState() { super.initState(); + initPushNotifications(); + } + + 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/FLTFirebaseMessagingPlugin.m b/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m index c763f293c700..85aa8ec7e293 100644 --- a/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m +++ b/packages/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m @@ -136,6 +136,8 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result [[UIApplication sharedApplication] registerForRemoteNotifications]; result([NSNumber numberWithBool:YES]); } + } else if ([@"getLaunchMessage" isEqualToString:method]) { + result(_launchNotification); } else if ([@"configure" isEqualToString:method]) { [FIRMessaging messaging].shouldEstablishDirectChannel = true; [[UIApplication sharedApplication] registerForRemoteNotifications]; diff --git a/packages/firebase_messaging/lib/firebase_messaging.dart b/packages/firebase_messaging/lib/firebase_messaging.dart index e17206f18c52..fe44c287ddb4 100644 --- a/packages/firebase_messaging/lib/firebase_messaging.dart +++ b/packages/firebase_messaging/lib/firebase_messaging.dart @@ -104,7 +104,8 @@ class FirebaseMessaging { void configure({ MessageHandler onMessage, MessageHandler onBackgroundMessage, - MessageHandler onLaunch, + @Deprecated('Use `FirebaseMessaging.getLaunchMessage` instead.') + MessageHandler onLaunch, MessageHandler onResume, }) { _onMessage = onMessage; @@ -137,6 +138,13 @@ class FirebaseMessaging { } } + /// Retrieves the FCM message that launched the application (if any, + /// otherwise returns null). + Future> getLaunchMessage() async { + return await _channel + .invokeMethod>('getLaunchMessage'); + } + final StreamController _tokenStreamController = StreamController.broadcast(); diff --git a/packages/firebase_messaging/pubspec.yaml b/packages/firebase_messaging/pubspec.yaml index 4ef24f56c7e2..30100d6d5401 100644 --- a/packages/firebase_messaging/pubspec.yaml +++ b/packages/firebase_messaging/pubspec.yaml @@ -2,7 +2,7 @@ name: firebase_messaging description: Flutter plugin for Firebase Cloud Messaging, a cross-platform messaging solution that lets you reliably deliver messages on Android and iOS. homepage: https://github.com/FirebaseExtended/flutterfire/tree/master/packages/firebase_messaging -version: 6.0.8 +version: 6.0.9 flutter: plugin: diff --git a/packages/firebase_messaging/test/firebase_messaging_test.dart b/packages/firebase_messaging/test/firebase_messaging_test.dart index ef7d489cfb06..b18030ad7d64 100644 --- a/packages/firebase_messaging/test/firebase_messaging_test.dart +++ b/packages/firebase_messaging/test/firebase_messaging_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: deprecated_member_use_from_same_package + import 'dart:async'; import 'package:flutter/services.dart'; @@ -102,6 +104,42 @@ 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();