Skip to content
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
6 changes: 6 additions & 0 deletions packages/firebase_messaging/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
12 changes: 9 additions & 3 deletions packages/firebase_messaging/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, dynamic> 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. |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@ public void onComplete(@NonNull Task<InstanceIdResult> task) {
sendMessageFromIntent("onLaunch", mainActivity.getIntent());
}
result.success(null);
Comment thread
jherencia marked this conversation as resolved.
} else if ("getLaunchMessage".equals(call.method)) {
if (mainActivity != null) {
Map<String, Object> message = this.getMessageFromIntent(mainActivity.getIntent());
result.success(message);
return;
}
result.success(null);
} else if ("subscribeToTopic".equals(call.method)) {
String topic = call.arguments();
FirebaseMessaging.getInstance()
Expand Down Expand Up @@ -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<String, Object> getMessageFromIntent(Intent intent) {
if (CLICK_ACTION_VALUE.equals(intent.getAction())
|| CLICK_ACTION_VALUE.equals(intent.getStringExtra("click_action"))) {
Map<String, Object> message = new HashMap<>();
Bundle extras = intent.getExtras();

if (extras == null) {
return false;
return null;
}

Map<String, Object> notificationMap = new HashMap<>();
Expand All @@ -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<String, Object> message = this.getMessageFromIntent(intent);

if (message == null) {
return false;
}
return false;

channel.invokeMethod(method, message);
return true;
}
}
16 changes: 12 additions & 4 deletions packages/firebase_messaging/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,15 +138,23 @@ class _PushMessagingExampleState extends State<PushMessagingExample> {
@override
void initState() {
super.initState();
initPushNotifications();
}

void initPushNotifications() async {
final Map<String, dynamic> message =
await _firebaseMessaging.getLaunchMessage();

if (message != null) {
print("getLaunchMessage $message");
_navigateToItemDetail(message);
}

_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
_showItemDialog(message);
},
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
_navigateToItemDetail(message);
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
_navigateToItemDetail(message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
10 changes: 9 additions & 1 deletion packages/firebase_messaging/lib/firebase_messaging.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -137,6 +138,13 @@ class FirebaseMessaging {
}
}

/// Retrieves the FCM message that launched the application (if any,
/// otherwise returns null).
Future<Map<dynamic, dynamic>> getLaunchMessage() async {
Comment thread
jherencia marked this conversation as resolved.
return await _channel
.invokeMethod<Map<dynamic, dynamic>>('getLaunchMessage');
}

final StreamController<String> _tokenStreamController =
StreamController<String>.broadcast();

Expand Down
2 changes: 1 addition & 1 deletion packages/firebase_messaging/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
38 changes: 38 additions & 0 deletions packages/firebase_messaging/test/firebase_messaging_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 <dynamic, dynamic>{
'notification': <dynamic, dynamic>{
'title': 'Title',
'body': 'Body',
'click_action': 'FLUTTER_NOTIFICATION_CLICK',
},
'data': <dynamic, dynamic>{
'variable1': 'value1',
'variable2': 'value2',
},
};
default:
return null;
}
});

final Map<dynamic, dynamic> 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<dynamic> onMessage = Completer<dynamic>();
final Completer<dynamic> onLaunch = Completer<dynamic>();
Expand Down