diff --git a/packages/firebase_messaging/CHANGELOG.md b/packages/firebase_messaging/CHANGELOG.md index 76fe5f3c10c0..cfe19fde74aa 100644 --- a/packages/firebase_messaging/CHANGELOG.md +++ b/packages/firebase_messaging/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.2.0 + +* Android: Receiving Data messages if app terminated + ## 5.1.3 * Update google-services Android gradle plugin to 4.3.0 in documentation and examples. diff --git a/packages/firebase_messaging/README.md b/packages/firebase_messaging/README.md index fced940d7b7f..a0a64331df0a 100644 --- a/packages/firebase_messaging/README.md +++ b/packages/firebase_messaging/README.md @@ -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)" | | **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 _handleNotification (Map message, bool dialog) as } ```` +## Receiving Data messages(Android) + +To receiving data messages, follow these steps: +1. Include the following code within the `` tag of your `android/app/src/main/AndroidManifest.xml`: +```xml + + + + + + + ``` +2. implement onReceive method. onReceive called if app terminated or app in background. +```java +public class MyReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + RemoteMessage remoteMessage = intent.getParcelableExtra(EXTRA_REMOTE_MESSAGE); + + Map 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. 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 4ab56e01d299..84bcc32c5dfa 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 @@ -53,7 +53,7 @@ private FirebaseMessagingPlugin(Registrar registrar, MethodChannel channel) { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(FlutterFirebaseMessagingService.ACTION_TOKEN); - intentFilter.addAction(FlutterFirebaseMessagingService.ACTION_REMOTE_MESSAGE); + intentFilter.addAction(FlutterFirebaseMessagingService.ACTION_FOREGROUND_REMOTE_MESSAGE); LocalBroadcastManager manager = LocalBroadcastManager.getInstance(registrar.context()); manager.registerReceiver(this, intentFilter); } @@ -70,7 +70,7 @@ public void onReceive(Context context, Intent intent) { if (action.equals(FlutterFirebaseMessagingService.ACTION_TOKEN)) { String token = intent.getStringExtra(FlutterFirebaseMessagingService.EXTRA_TOKEN); channel.invokeMethod("onToken", token); - } else if (action.equals(FlutterFirebaseMessagingService.ACTION_REMOTE_MESSAGE)) { + } else if (action.equals(FlutterFirebaseMessagingService.ACTION_FOREGROUND_REMOTE_MESSAGE)) { RemoteMessage message = intent.getParcelableExtra(FlutterFirebaseMessagingService.EXTRA_REMOTE_MESSAGE); Map content = parseRemoteMessage(message); diff --git a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingService.java b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingService.java index 345c0161e192..9f4e3417051a 100644 --- a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingService.java +++ b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingService.java @@ -11,13 +11,27 @@ 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; + + @Override + public void onCreate() { + super.onCreate(); + isDataMessages = FlutterFirebaseMessagingUtils.isDataMessages(this); + } + /** * Called when message is received. * @@ -25,11 +39,32 @@ public class FlutterFirebaseMessagingService extends FirebaseMessagingService { */ @Override public void onMessageReceived(RemoteMessage remoteMessage) { - Intent intent = new Intent(ACTION_REMOTE_MESSAGE); + if (!isDataMessages) { + sendForegroundBroadcast(remoteMessage); + } + + 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. * diff --git a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingUtils.java b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingUtils.java new file mode 100644 index 000000000000..aa07d3c1601f --- /dev/null +++ b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingUtils.java @@ -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) { + 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 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; + } +} diff --git a/packages/firebase_messaging/example/android/app/src/main/AndroidManifest.xml b/packages/firebase_messaging/example/android/app/src/main/AndroidManifest.xml index adaa7b419adb..39d9908adcec 100644 --- a/packages/firebase_messaging/example/android/app/src/main/AndroidManifest.xml +++ b/packages/firebase_messaging/example/android/app/src/main/AndroidManifest.xml @@ -19,5 +19,14 @@ + + + + + + + diff --git a/packages/firebase_messaging/example/android/app/src/main/java/io/flutter/plugins/firebasemessagingexample/FlutterBackgroundMessagesReceiver.java b/packages/firebase_messaging/example/android/app/src/main/java/io/flutter/plugins/firebasemessagingexample/FlutterBackgroundMessagesReceiver.java new file mode 100644 index 000000000000..baf23c1855f4 --- /dev/null +++ b/packages/firebase_messaging/example/android/app/src/main/java/io/flutter/plugins/firebasemessagingexample/FlutterBackgroundMessagesReceiver.java @@ -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 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 entry : data.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + messageDataIntent.putExtra(key, value); + } + + PendingIntent pendingIntent = PendingIntent.getActivity(context, 123, messageDataIntent, 0); + + 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); + } + } +} diff --git a/packages/firebase_messaging/pubspec.yaml b/packages/firebase_messaging/pubspec.yaml index cdfa9b11d659..80712912c39c 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.1.3 +version: 5.2.0 flutter: plugin: