-
Notifications
You must be signed in to change notification settings - Fork 9.8k
[firebase_messaging] Android: Receiving Data messages if app terminated #1898
Changes from all commits
d5c1495
526169d
58723cf
bc5538b
195ae31
7a7f432
8177bcf
1c3f7c0
44180c3
5b59684
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -88,7 +88,7 @@ Messages are sent to your Flutter app via the `onMessage`, `onLaunch`, and `onRe | |||||
| --------------------------: | ----------------- | ----------------- | -------------- | | ||||||
| **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. | | ||||||
| **Data Message on Android** | `onMessage` | `onMessage` while app stays in the background. | *not supported by plugin, message is lost* | | ||||||
| **Data Message on Android** | `onMessage` | `onMessage` while app stays in the background. | see "Receiving Data messages(Android)" | | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| **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. | | ||||||
|
||||||
Additional reading: Firebase's [About FCM Messages](https://firebase.google.com/docs/cloud-messaging/concept-options). | ||||||
|
@@ -108,6 +108,35 @@ Future<void> _handleNotification (Map<dynamic, dynamic> message, bool dialog) as | |||||
} | ||||||
```` | ||||||
|
||||||
## Receiving Data messages(Android) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
To receiving data messages, follow these steps: | ||||||
1. Include the following code within the `<activity>` tag of your `android/app/src/main/AndroidManifest.xml`: | ||||||
```xml | ||||||
<receiver android:name=".MyReceiver"> | ||||||
<intent-filter> | ||||||
<action android:name="io.flutter.plugins.firebasemessaging.BACKGROUND_NOTIFICATION" /> | ||||||
</intent-filter> | ||||||
</receiver> | ||||||
<meta-data | ||||||
android:name="flutter.firebase.data.message" | ||||||
android:value="true" /> | ||||||
``` | ||||||
2. implement onReceive method. onReceive called if app terminated or app in background. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
```java | ||||||
public class MyReceiver extends BroadcastReceiver { | ||||||
@Override | ||||||
public void onReceive(Context context, Intent intent) { | ||||||
RemoteMessage remoteMessage = intent.getParcelableExtra(EXTRA_REMOTE_MESSAGE); | ||||||
|
||||||
Map<String, String> data = remoteMessage.getData(); | ||||||
|
||||||
String title = data.get("title"); | ||||||
String body = data.get("body"); | ||||||
} | ||||||
} | ||||||
``` | ||||||
|
||||||
## Sending Messages | ||||||
Refer to the [Firebase documentation](https://firebase.google.com/docs/cloud-messaging/) about FCM for all the details about sending messages to your app. When sending a notification message to an Android device, you need to make sure to set the `click_action` property of the message to `FLUTTER_NOTIFICATION_CLICK`. Otherwise the plugin will be unable to deliver the notification to your app when the users clicks on it in the system tray. | ||||||
|
||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,25 +11,60 @@ | |
|
||
public class FlutterFirebaseMessagingService extends FirebaseMessagingService { | ||
|
||
public static final String ACTION_REMOTE_MESSAGE = | ||
"io.flutter.plugins.firebasemessaging.NOTIFICATION"; | ||
public static final String ACTION_FOREGROUND_REMOTE_MESSAGE = | ||
"io.flutter.plugins.firebasemessaging.FOREGROUND_NOTIFICATION"; | ||
public static final String ACTION_BACKGROUND_REMOTE_MESSAGE = | ||
"io.flutter.plugins.firebasemessaging.BACKGROUND_NOTIFICATION"; | ||
public static final String EXTRA_REMOTE_MESSAGE = "notification"; | ||
|
||
public static final String ACTION_TOKEN = "io.flutter.plugins.firebasemessaging.TOKEN"; | ||
public static final String EXTRA_TOKEN = "token"; | ||
|
||
/** | ||
* true, if application receive messages with type "Data messages" About FCM messages | ||
* {@https://firebase.google.com/docs/cloud-messaging/concept-options} | ||
*/ | ||
private boolean isDataMessages; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is private it probably doesn't need a doc comment. |
||
|
||
@Override | ||
public void onCreate() { | ||
super.onCreate(); | ||
isDataMessages = FlutterFirebaseMessagingUtils.isDataMessages(this); | ||
} | ||
|
||
/** | ||
* Called when message is received. | ||
* | ||
* @param remoteMessage Object representing the message received from Firebase Cloud Messaging. | ||
*/ | ||
@Override | ||
public void onMessageReceived(RemoteMessage remoteMessage) { | ||
Intent intent = new Intent(ACTION_REMOTE_MESSAGE); | ||
if (!isDataMessages) { | ||
sendForegroundBroadcast(remoteMessage); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like you're broadcasting twice here if |
||
} | ||
|
||
boolean applicationForeground = | ||
FlutterFirebaseMessagingUtils.isApplicationForeground(getApplicationContext()); | ||
|
||
if (applicationForeground) { | ||
sendForegroundBroadcast(remoteMessage); | ||
} else { | ||
sendBackgroundBroadcast(remoteMessage); | ||
} | ||
} | ||
|
||
private void sendForegroundBroadcast(RemoteMessage remoteMessage) { | ||
Intent intent = new Intent(ACTION_FOREGROUND_REMOTE_MESSAGE); | ||
intent.putExtra(EXTRA_REMOTE_MESSAGE, remoteMessage); | ||
LocalBroadcastManager.getInstance(this).sendBroadcast(intent); | ||
} | ||
|
||
private void sendBackgroundBroadcast(RemoteMessage remoteMessage) { | ||
Intent intent = new Intent(ACTION_BACKGROUND_REMOTE_MESSAGE); | ||
intent.putExtra(EXTRA_REMOTE_MESSAGE, remoteMessage); | ||
intent.setPackage(getPackageName()); | ||
sendBroadcast(intent); | ||
} | ||
/** | ||
* Called when a new token for the default Firebase project is generated. | ||
* | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// Copyright 2019 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.firebasemessaging; | ||
|
||
import android.app.ActivityManager; | ||
import android.app.KeyguardManager; | ||
import android.content.Context; | ||
import android.content.pm.ApplicationInfo; | ||
import android.content.pm.PackageManager; | ||
import android.os.Bundle; | ||
import android.os.Process; | ||
import java.util.List; | ||
|
||
public class FlutterFirebaseMessagingUtils { | ||
|
||
private static final String DATA_MESSAGES_KEY = "flutter.firebase.data.message"; | ||
|
||
static boolean isDataMessages(Context context) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method name came across as a bit confusing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I found this method name kind of confusing. I think it might make sense to call it |
||
try { | ||
ApplicationInfo info = | ||
context | ||
.getPackageManager() | ||
.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); | ||
Bundle bundle = info.metaData; | ||
return bundle.getBoolean(DATA_MESSAGES_KEY); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
} | ||
return false; | ||
} | ||
|
||
public static boolean isApplicationForeground(Context context) { | ||
KeyguardManager keyguardManager = | ||
(KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); | ||
|
||
if (keyguardManager.inKeyguardRestrictedInputMode()) { | ||
return false; | ||
} | ||
int myPid = Process.myPid(); | ||
|
||
ActivityManager activityManager = | ||
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); | ||
|
||
List<ActivityManager.RunningAppProcessInfo> list; | ||
|
||
if ((list = activityManager.getRunningAppProcesses()) != null) { | ||
for (ActivityManager.RunningAppProcessInfo aList : list) { | ||
ActivityManager.RunningAppProcessInfo info; | ||
if ((info = aList).pid == myPid) { | ||
return info.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,99 @@ | ||||||
// Copyright 2019 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.firebasemessagingexample; | ||||||
|
||||||
import android.app.Notification; | ||||||
import android.app.NotificationChannel; | ||||||
import android.app.NotificationManager; | ||||||
import android.app.PendingIntent; | ||||||
import android.content.BroadcastReceiver; | ||||||
import android.content.Context; | ||||||
import android.content.Intent; | ||||||
import android.content.res.Resources; | ||||||
import android.graphics.BitmapFactory; | ||||||
import android.media.RingtoneManager; | ||||||
import android.net.Uri; | ||||||
import android.text.TextUtils; | ||||||
import androidx.core.app.NotificationCompat; | ||||||
import androidx.core.content.ContextCompat; | ||||||
import com.google.firebase.messaging.RemoteMessage; | ||||||
import java.util.Map; | ||||||
|
||||||
public class FlutterBackgroundMessagesReceiver extends BroadcastReceiver { | ||||||
|
||||||
private static final String DEFAULT_CHANNEL_ID = "default"; | ||||||
public static final String EXTRA_REMOTE_MESSAGE = "notification"; | ||||||
|
||||||
@Override | ||||||
public void onReceive(Context context, Intent intent) { | ||||||
showNotification(context, intent); | ||||||
} | ||||||
|
||||||
private void showNotification(Context context, Intent intent) { | ||||||
RemoteMessage remoteMessage = intent.getParcelableExtra(EXTRA_REMOTE_MESSAGE); | ||||||
|
||||||
Map<String, String> data = remoteMessage.getData(); | ||||||
|
||||||
String title = data.get("title"); | ||||||
String body = data.get("body"); | ||||||
|
||||||
NotificationManager notificationManager = | ||||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); | ||||||
|
||||||
setupNotificationChannel(notificationManager); | ||||||
|
||||||
Intent messageDataIntent = new Intent("FLUTTER_NOTIFICATION_CLICK"); | ||||||
messageDataIntent.setPackage(context.getPackageName()); | ||||||
for (Map.Entry<String, String> entry : data.entrySet()) { | ||||||
String key = entry.getKey(); | ||||||
String value = entry.getValue(); | ||||||
messageDataIntent.putExtra(key, value); | ||||||
} | ||||||
|
||||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 123, messageDataIntent, 0); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
The |
||||||
|
||||||
Resources resources = context.getResources(); | ||||||
|
||||||
int iconId = resources.getIdentifier("ic_launcher", "mipmap", context.getPackageName()); | ||||||
NotificationCompat.Builder builder = | ||||||
new NotificationCompat.Builder(context, DEFAULT_CHANNEL_ID) | ||||||
.setColor(ContextCompat.getColor(context, android.R.color.black)) | ||||||
.setSmallIcon(iconId) | ||||||
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), iconId)) | ||||||
.setAutoCancel(true) | ||||||
.setContentIntent(pendingIntent); | ||||||
|
||||||
if (TextUtils.isEmpty(title)) { | ||||||
CharSequence appLabel = context.getApplicationInfo().loadLabel(context.getPackageManager()); | ||||||
builder.setContentTitle(appLabel); | ||||||
} else { | ||||||
builder.setContentTitle(title); | ||||||
} | ||||||
|
||||||
if (!TextUtils.isEmpty(body)) { | ||||||
builder.setStyle((new NotificationCompat.BigTextStyle()).bigText(body)).setContentText(body); | ||||||
} | ||||||
|
||||||
Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); | ||||||
builder.setSound(uri); | ||||||
notificationManager.notify(System.currentTimeMillis() + "", 0, builder.build()); | ||||||
} | ||||||
|
||||||
private void setupNotificationChannel(NotificationManager notificationManager) { | ||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { | ||||||
if (notificationManager.getNotificationChannel(DEFAULT_CHANNEL_ID) != null) { | ||||||
return; | ||||||
} | ||||||
|
||||||
NotificationChannel channel = | ||||||
new NotificationChannel( | ||||||
DEFAULT_CHANNEL_ID, "Primary Channel", NotificationManager.IMPORTANCE_HIGH); | ||||||
channel.enableLights(true); | ||||||
channel.enableVibration(true); | ||||||
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); | ||||||
notificationManager.createNotificationChannel(channel); | ||||||
} | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 <[email protected]> | ||
homepage: https://github.com/flutter/plugins/tree/master/packages/firebase_messaging | ||
version: 5.1.3 | ||
version: 5.2.0 | ||
|
||
flutter: | ||
plugin: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.