Related
I am using the fcm console to send messages to all of devices that have my app installed, the notifications don't have any extra payload only the notification message.
I want to know if there is a simple way to know if an Activity was opened from an FCM notification click.
There is a solution for that by extending the FirebaseMessagingService service and creating a notification by yourself.
I want to know if there is another solution without extending the service or passing extras to the notification.
Is there any extra that is being passed with the Intent that is used for opening the activity from the notification click?
I hope you know that FCM based messaging created two types of notification
Firstly, the notification that we create from on onMessageReceived() method, the point to note here is that onMessageReceived() will be triggered if the app is in foreground.
Secondly, FCM creates its own default notification when app is in background, in this case onMessageReceived() is not intercepted and hence we cannot set a pending intent on our own.
Note : the above mentioned types comes to play when you are sending a "notification" push message to your app in the case of a me "data" message onMessageReceived() is triggered irrespective of the app being in foreground or background (the notifications sent from FCM console are "notifications" type push messages)
Coming back to your question, it is not clear if you're sending a push message from FCM console or making a FCM server request, so let's make cases here.
Message being sent by FCM Console :
send data payload from the advance section in notification panel on FCM which looks like this
when app is in foreground onMessageReceived() will be intercepted
use the code below to receive the data payload
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMsgService";
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
//Displaying data in log
//It is optional
//Calling method to generate notification
sendNotification(remoteMessage.getData());
}
//This method is only generating push notification
//It is same as we did in earlier posts
private void sendNotification(Map<String, String> messageBody) {
Intent intent = new Intent(this, SplashScreen.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(
this,
0,
setNotificationRoute(messageBody),
PendingIntent.FLAG_UPDATE_CURRENT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setContentTitle(messageBody.get("title"))
.setContentText(messageBody.get("text"))
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
}
private Intent setNotificationRoute(Map<String, String> messageBody) {
Intent intent = null;
String type = messageBody.get("type");
if (type != null) {
switch (type) {
case "android": //intercept your payload here to create swit
intent = new Intent(this, MainActivity.class);
break;
default:
break;
}
}
return intent;
}
}
and if app is in background, then on notification click app will be opened on a "default" activity, you can set any activity as a default one by adding the following lines in the app manifest in the activity's intent filter:
<category android:name="android.intent.category.DEFAULT" />
inside this activity you can call to get intent extras and then get the data payload to decide on which activity you need to land on. The code is below
.
.
.
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
setNotificationRoute(getIntent().getExtras());
.
.
}
private void setNotificationRoute(Bundle extras) {
String type = extras.getString("type");
Intent intent = null;
if (type != null) {
switch (type) {
case "message":
String room = extras.getString("room");
intent = new Intent(this, MainActivity.class);
startActivity(intent);
break;
default:
break;
}
}
}
Message sent from FCM Server: the message sent from FCM consolse above is same as sending the below json body as post request to FCM server:
{
"notification": {
"title": "Hi Tester",
"text": "News for testing",
"sound": "default",
"badge": 0
},
"data":{
"type": "credits"
},
"priority": "high",
"to": "{{device_token}}"
}
the process of intercepting the notifications will be same for this case.
if you want to Start any Activity without extending FireBaseMessaging class then you can set this flags with your FCM data payload.
So whenever the user clicked on FCM Notification on System Tray.That defined Activity should open.And if you want to get extra bundle data.you can get it using getIntent().getExtras(); method.
"message":{
"token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"notification":{
"title":"Match update",
"body":"Arsenal goal in added time, score is now 3-0"
},
"android":{
"ttl":"86400s",
"notification"{
"click_action":"MainActivity.class" // define your activity here
}
},
"payload": {
"aps": {
"category": "NEW_MESSAGE_CATEGORY"
}
}
}
Please refer to below link:-
"sets the appropriate keys to define the result of a user tap on the notification on Android and iOS — click_action, and category, respectively."
No need pass any extra with intent. Just set the flags like
Intent lIntent = new Intent(getApplicationContext(),YourActivity.class);
lIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
lIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
lIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(lIntent);
I'm working with Firebase and testing sending notifications to my app from my server while the app is in the background. The notification is sent successfully, it even appears on the notification centre of the device, but when the notification appears or even if I click on it, the onMessageReceived method inside my FCMessagingService is never called.
When I tested this while my app was in the foreground, the onMessageReceived method was called and everything worked fine. The problem occurs when the app is running in the background.
Is this intended behaviour, or is there a way I can fix this?
Here is my FBMessagingService:
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class FBMessagingService extends FirebaseMessagingService {
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.i("PVL", "MESSAGE RECEIVED!!");
if (remoteMessage.getNotification().getBody() != null) {
Log.i("PVL", "RECEIVED MESSAGE: " + remoteMessage.getNotification().getBody());
} else {
Log.i("PVL", "RECEIVED MESSAGE: " + remoteMessage.getData().get("message"));
}
}
}
This is working as intended, notification messages are delivered to your onMessageReceived callback only when your app is in the foreground. If your app is in the background or closed then a notification message is shown in the notification center, and any data from that message is passed to the intent that is launched as a result of the user tapping on the notification.
You can specify a click_action in your JSON to indicate the intent that should be launched when the notification is tapped by the user. The main activity is used if no click_action is specified.
When the intent is launched you can use the
getIntent().getExtras();
to retrieve a Set that would include any data sent along with the notification message.
For more on notification message see docs.
Remove notification field completely from your server request. Send only data and handle it in onMessageReceived() otherwise your onMessageReceived() will not be triggered when app is in background or killed.
Don't forget to include "priority": "high" field in your notification request. According to the documentation: data messages are sent with a normal priority, thus they will not arrive instantly; it could also be the problem.
Here is what I am sending from server
{
"data":{
"id": 1,
"missedRequests": 5
"addAnyDataHere": 123
},
"to": "fhiT7evmZk8:APA91bFJq7Tkly4BtLRXdYvqHno2vHCRkzpJT8QZy0TlIGs......",
"priority": "high"
}
So you can receive your data in onMessageReceived(RemoteMessage message) like this....let say I have to get id
Object obj = message.getData().get("id");
if (obj != null) {
int id = Integer.valueOf(obj.toString());
}
this method handleIntent() has been depreciated, so handling a notification can be done as below:
Foreground State: The click of the notification will go to the pending Intent's activity which you are providing while creating a notification pro-grammatically as it generally created with data payload of the notification.
Background/Killed State - Here, the system itself creates a notification based on notification payload and clicking on that notification will take you to the launcher activity of the application where you can easily fetch Intent data in any of your life-cycle methods.
Here is more clear concepts about firebase message. I found it from their support team.
Firebase has three message types:
Notification messages : Notification message works on background or foreground. When app is in background, Notification messages are delivered to the system tray. If the app is in the foreground, messages are handled by onMessageReceived() or didReceiveRemoteNotification callbacks. These are essentially what is referred to as Display messages.
Data messages: On Android platform, data message can work on background and foreground. The data message will be handled by onMessageReceived(). A platform specific note here would be: On Android, the data payload can be retrieved in the Intent used to launch your activity. To elaborate, if you have "click_action":"launch_Activity_1", you can retrieve this intent through getIntent() from only Activity_1.
Messages with both notification and data payloads: When in the background, apps receive the notification payload in the notification tray, and only handle the data payload when the user taps on the notification. When in the foreground, your app receives a message object with both payloads available. Secondly, the click_action parameter is often used in notification payload and not in data payload. If used inside data payload, this parameter would be treated as custom key-value pair and therefore you would need to implement custom logic for it to work as intended.
Also, I recommend you to use onMessageReceived method (see Data message) to extract the data bundle. From your logic, I checked the bundle object and haven't found expected data content. Here is a reference to a similar case which might provide more clarity.
From server side, firebase notification should bellow format:
Server side should send "notification" object. Lacks of "notification" object in my TargetActivity didn't getting message using getIntent().
Correct message format is given bellow:
{
"data": {
"body": "here is body",
"title": "Title"
},
"notification": {
"body": "here is body",
"title": "Title",
"click_action": "YOUR_ACTION"
},
"to": "ffEseX6vwcM:APA91bF8m7wOF MY FCM ID 07j1aPUb"
}
Here is more clear concepts about firebase message. I found it from their support team.
For more info visit my this thread and this thread
I had the same problem. It is easier to use the 'data message' instead of the 'notification'. The data message always load the class onMessageReceived.
In that class you can make your own notification with the notificationbuilder.
Example:
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
sendNotification(remoteMessage.getData().get("title"),remoteMessage.getData().get("body"));
}
private void sendNotification(String messageTitle,String messageBody) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0 /* request code */, intent,PendingIntent.FLAG_UPDATE_CURRENT);
long[] pattern = {500,500,500,500,500};
Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = (NotificationCompat.Builder) new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentTitle(messageTitle)
.setContentText(messageBody)
.setAutoCancel(true)
.setVibrate(pattern)
.setLights(Color.BLUE,1,1)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
As per Firebase Cloud Messaging documentation-If Activity is in foreground then onMessageReceived will get called. If Activity is in background or closed then notification message is shown in the notification center for app launcher activity.
You can call your customized activity on click of notification if your app is in background by calling rest service api for firebase messaging as:
URL-https://fcm.googleapis.com/fcm/send
Method Type- POST
Header- Content-Type:application/json
Authorization:key=your api key
Body/Payload:
{ "notification": {
"title": "Your Title",
"text": "Your Text",
"click_action": "OPEN_ACTIVITY_1" // should match to your intent filter
},
"data": {
"keyname": "any value " //you can get this data as extras in your activity and this data is optional
},
"to" : "to_id(firebase refreshedToken)"
}
And with this in your app you can add below code in your activity to be called:
<intent-filter>
<action android:name="OPEN_ACTIVITY_1" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
onMessageReceived(RemoteMessage remoteMessage) method called based on the following cases.
FCM Response With notification and data block:
{
"to": "device token list",
"notification": {
"body": "Body of Your Notification",
"title": "Title of Your Notification"
},
"data": {
"body": "Body of Your Notification in Data",
"title": "Title of Your Notification in Title",
"key_1": "Value for key_1",
"image_url": "www.abc.com/xyz.jpeg",
"key_2": "Value for key_2"
}
}
App in Foreground:
onMessageReceived(RemoteMessage remoteMessage) called, shows LargeIcon and BigPicture in the notification bar. We can read the content from both notification and data block
App in Background:
onMessageReceived(RemoteMessage remoteMessage) not called, system tray will receive the message and read body and title from notification block and shows default message and title in the notification bar.
FCM Response With only data block:
In this case, removing notification blocks from json
{
"to": "device token list",
"data": {
"body": "Body of Your Notification in Data",
"title": "Title of Your Notification in Title",
"key_1": "Value for key_1",
"image_url": "www.abc.com/xyz.jpeg",
"key_2": "Value for key_2"
}
}
Solution for calling onMessageReceived()
App in Foreground:
onMessageReceived(RemoteMessage remoteMessage) called, shows LargeIcon and BigPicture in the notification bar. We can read the content from both notification and data block
App in Background:
onMessageReceived(RemoteMessage remoteMessage) called, system tray will not receive the message because of notification key is not in the response. Shows LargeIcon and BigPicture in the notification bar
Code
private void sendNotification(Bitmap bitmap, String title, String
message, PendingIntent resultPendingIntent) {
NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle();
style.bigPicture(bitmap);
Uri defaultSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
String NOTIFICATION_CHANNEL_ID = mContext.getString(R.string.default_notification_channel_id);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "channel_name", NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(notificationChannel);
}
Bitmap iconLarge = BitmapFactory.decodeResource(mContext.getResources(),
R.drawable.mdmlogo);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.mdmlogo)
.setContentTitle(title)
.setAutoCancel(true)
.setSound(defaultSound)
.setContentText(message)
.setContentIntent(resultPendingIntent)
.setStyle(style)
.setLargeIcon(iconLarge)
.setWhen(System.currentTimeMillis())
.setPriority(Notification.PRIORITY_MAX)
.setChannelId(NOTIFICATION_CHANNEL_ID);
notificationManager.notify(1, notificationBuilder.build());
}
Reference Link:
https://firebase.google.com/docs/cloud-messaging/android/receive
If app is in the background mode or inactive(killed), and you click on Notification, you should check for the payload in LaunchScreen(in my case launch screen is MainActivity.java).
So in MainActivity.java on onCreate check for Extras:
if (getIntent().getExtras() != null) {
for (String key : getIntent().getExtras().keySet()) {
Object value = getIntent().getExtras().get(key);
Log.d("MainActivity: ", "Key: " + key + " Value: " + value);
}
}
I got the same issue. If the app is foreground - it triggers my background service where I can update my database based on the notification type.
But, the app goes to the background - the default notification service will be taken care to show the notification to the user.
Here is my solution to identify app in background and trigger your background service,
public class FirebaseBackgroundService extends WakefulBroadcastReceiver {
private static final String TAG = "FirebaseService";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "I'm in!!!");
if (intent.getExtras() != null) {
for (String key : intent.getExtras().keySet()) {
Object value = intent.getExtras().get(key);
Log.e("FirebaseDataReceiver", "Key: " + key + " Value: " + value);
if(key.equalsIgnoreCase("gcm.notification.body") && value != null) {
Bundle bundle = new Bundle();
Intent backgroundIntent = new Intent(context, BackgroundSyncJobService.class);
bundle.putString("push_message", value + "");
backgroundIntent.putExtras(bundle);
context.startService(backgroundIntent);
}
}
}
}
}
In the manifest.xml
<receiver android:exported="true" android:name=".FirebaseBackgroundService" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</receiver>
Tested this solution in latest android 8.0 version. Thanks
Override the handleIntent Method of the FirebaseMessageService works for me.
here the code in C# (Xamarin)
public override void HandleIntent(Intent intent)
{
try
{
if (intent.Extras != null)
{
var builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
foreach (string key in intent.Extras.KeySet())
{
builder.AddData(key, intent.Extras.Get(key).ToString());
}
this.OnMessageReceived(builder.Build());
}
else
{
base.HandleIntent(intent);
}
}
catch (Exception)
{
base.HandleIntent(intent);
}
}
and thats the Code in Java
public void handleIntent(Intent intent)
{
try
{
if (intent.getExtras() != null)
{
RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
for (String key : intent.getExtras().keySet())
{
builder.addData(key, intent.getExtras().get(key).toString());
}
onMessageReceived(builder.build());
}
else
{
super.handleIntent(intent);
}
}
catch (Exception e)
{
super.handleIntent(intent);
}
}
By default the Launcher Activity in you app will be launched when your app is in background and you click the notification, if you have any data part with your notifcation you can handle it in the same activity as follows,
if(getIntent().getExtras()! = null){
//do your stuff
}else{
//do that you normally do
}
I might be very late here to answers but the official documentation is a bit confusing.
Also it's clearly stated that there are two types of notification
Notification message: Automatically handled by FCM
Data message: Handled by the client app.
No doubt if the server sends a Data message then onMessageReceived() methods definitely get invoked but in the case of Notification message onMessageReceived() method will get invoked only if the app is in the foreground and when the app is in the background the data we are sending is just null.
Example:
Lets assume server is sending notification message type:
A. In case of Foreground:
remoteMessage.data["key"] will work
B. In case of background:
-remoteMessage.data["key"] will return null
but here if you find the same intent data in defaul activity with getIntent().getExtras().getString("key") will work
C. In case of kill:
-remoteMessage.data["key"] will return null
but here if you find the same intent data in defaul activity with getIntent().getExtras().getString("key") will work
Now, let's assume server is sending data message type:
D. In case of Foreground:
remoteMessage.data["key"] will work
E. In case of background:
remoteMessage.data["key"] will work
F. In case of kill:
remoteMessage.data["key"] will work
No doubt data message will always invokes onMessageReceived() method but in case of notification message and app is in background/kill state you can use the solution of B. Thanks
I hope it will save everyone's time.
according to the solution from t3h Exi i would like to post the clean code here. Just put it into MyFirebaseMessagingService and everything works fine if the app is in background mode. You need at least to compile com.google.firebase:firebase-messaging:10.2.1
#Override
public void handleIntent(Intent intent)
{
try
{
if (intent.getExtras() != null)
{
RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
for (String key : intent.getExtras().keySet())
{
builder.addData(key, intent.getExtras().get(key).toString());
}
onMessageReceived(builder.build());
}
else
{
super.handleIntent(intent);
}
}
catch (Exception e)
{
super.handleIntent(intent);
}
}
If app is in background Fire-base by default handling notification But if we want to our custom notification than we have to change our server side, which is responsible for to send our custom data(data payload)
Remove notification payload completely from your server request. Send only Data and handle it in onMessageReceived() otherwise your onMessageReceived will not be triggered when app is in background or killed.
now,your server side code format look like,
{
"collapse_key": "CHAT_MESSAGE_CONTACT",
"data": {
"loc_key": "CHAT_MESSAGE_CONTACT",
"loc_args": ["John Doe", "Contact Exchange"],
"text": "John Doe shared a contact in the group Contact Exchange",
"custom": {
"chat_id": 241233,
"msg_id": 123
},
"badge": 1,
"sound": "sound1.mp3",
"mute": true
}
}
NOTE: see this line in above code
"text": "John Doe shared a contact in the group Contact Exchange"
in Data payload you should use "text" parameter instead of "body" or "message" parameters for message description or whatever you want to use text.
onMessageReceived()
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.e(TAG, "From: " + remoteMessage.getData().toString());
if (remoteMessage == null)
return;
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
/* Log.e(TAG, "Data Payload: " + remoteMessage.getData().toString());*/
Log.e(TAG, "Data Payload: " + remoteMessage);
try {
Map<String, String> params = remoteMessage.getData();
JSONObject json = new JSONObject(params);
Log.e("JSON_OBJECT", json.toString());
Log.e(TAG, "onMessageReceived: " + json.toString());
handleDataMessage(json);
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.getMessage());
}
}
}
I had similar issue. Based on the answers and references mentioned in this page, here are my two cents on how I resolved my problem with the below approach:
The message format I had earlier was as below:
{
"notification": {
"title": "AppName",
"sound": null,
"body": "Hey!YouhaveaMessage"
},
"data": {
"param1": null,
"param2": [
238
],
"id": 1
},
"to": "--the device push token here--"
}
I modified the message format to the below:
{
"data": {
"title": "AppName",
"body": "Hey! You have a message",
"param1": null,
"param2": [
238
],
"id": 1
},
"priority": "high",
"to": " — device push token here — "
}
Then I retrieved the title, body and all the parameters from the "data" payload itself. This solved the problem and I could then get the OnMessageReceived callback even though the app is in the background.
I wrote a blog post explaining the same issue, you can find it here.
{
"notification": {
"title": "Notification Title",
"body": "Notification Body",
"click_action": "ActivityToOpen"
},
"data": {
"key": "value "
},
"to": "id"
}
If the FCM payload has notification{} block like the above and the app is in the background, the system builds the notification for you with the title and body given in notification{}. When the user clicks on it, the activity mentioned in click_action opens, if nothing is given the default launcher activity opens and data inside the data{} block can be accessed from
intent.extras // of the launcher activity
If the app is in the foreground the function onMessageReceived() in FirebaseMessagingService() class gets triggered. We will have to build the notification by ourselves and we can access the data as following:
val value = message.data.getOrDefault("key", "")
If the FCM payload is without notification block{} like following;
{
"data": {
"title": "Notification Title",
"body": "Notification Body",
"key": "value "
},
"to" : "id"
}
The function onMessageReceived() in FirebaseMessagingService() class gets triggered regardless of app being in background or foreground and we will have to build notification by ourselves.
We can access the data as following:
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
val title = message.data.getOrDefault("title", "")
val body = message.data.getOrDefault("body", "")
}
Just call this in your MainActivity's onCreate Method :
if (getIntent().getExtras() != null) {
// Call your NotificationActivity here..
Intent intent = new Intent(MainActivity.this, NotificationActivity.class);
startActivity(intent);
}
Try this:
public void handleIntent(Intent intent) {
try {
if (intent.getExtras() != null) {
RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
for (String key : intent.getExtras().keySet()) {
builder.addData(key, intent.getExtras().get(key).toString());
}
onMessageReceived(builder.build());
} else {
super.handleIntent(intent);
}
} catch (Exception e) {
super.handleIntent(intent);
}
}
I had this issue(app doesn't want to open on notification click if app is in background or closed), and the problem was an invalid click_action in notification body, try removing or changing it to something valid.
The point which deserves highlighting is that you have to use data message - data key only - to get onMessageReceived handler called even when the app is in background. You shouldn't have any other notification message key in your payload, otherwise the handler won't get triggered if the app is in background.
It is mentioned (but not so emphasized in FCM documentation) here:
https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages
Use your app server and FCM server API: Set the data key only. Can be
either collapsible or non-collapsible.
The backend I'm working with is using Notification messages and not Data messages. So after reading all the answers I tried to retrieve the extras from the bundle of the intent that comes to the launched activity.
But no matter which keys I tried to retrieve from getIntent().getExtras();, the value was always null.
However, I finally found a way to send data using Notification messages and retrieve it from the intent.
The key here is to add the data payload to the Notification message.
Example:
{
"data": {
"message": "message_body",
"title": "message_title"
},
"notification": {
"body": "test body",
"title": "test title"
},
"to": "E4An.."
}
After you do this, you will be able to get your info in this way:
intent.getExtras().getString("title")
will be message_title
and
intent.getExtras().getString("message")
will be message_body
Reference
If your problem is related to showing Big Image i.e. if you are sending push notification with an image from firebase console and it displays the image only if the app in the foreground. The solution for this problem is to send a push message with only data field. Something like this:
{ "data": { "image": "https://static.pexels.com/photos/4825/red-love-romantic-flowers.jpg", "message": "Firebase Push Message Using API" "AnotherActivity": "True" }, "to" : "device id Or Device token" }
When message is received and your app is in background the notification is sent to the extras intent of the main activity.
You can check the extra value in the oncreate() or onresume() function of the main activity.
You can check for the fields like data, table etc ( the one specified in the notification)
for example I sent using data as the key
public void onResume(){
super.onResume();
if (getIntent().getStringExtra("data")!=null){
fromnotification=true;
Intent i = new Intent(MainActivity.this, Activity2.class);
i.putExtra("notification","notification");
startActivity(i);
}
}
I think the answers to tell you change message type to data are clear to you.
But sometimes, if you cannot decide your the message type you received and you have to handle it. I post my method here. You just implemented FirebaseMessagingService and handle your message in handlIntent() method. From there you could customize your own notification. You can implement your own method sendYourNotificatoin()
class FCMPushService : FirebaseMessagingService() {
companion object {
private val TAG = "FCMPush"
}
override fun handleIntent(intent: Intent?) {
Logger.t(TAG).i("handleIntent:${intent.toString()}")
val data = intent?.extras as Bundle
val remoteMessage = RemoteMessage(data)
if (remoteMessage.data.isNotEmpty()) {
val groupId: String = remoteMessage.data[MESSAGE_KEY_GROUP_ID] ?: ""
val title = remoteMessage.notification?.title ?: ""
val body = remoteMessage.notification?.body ?: ""
if (title.isNotEmpty() && body.isNotEmpty())
sendYourNotificatoin(this, title, body, groupId)
}
}
}
I was having the same issue and did some more digging on this. When the app is in the background, a notification message is sent to the system tray, BUT a data message is sent to onMessageReceived()
See https://firebase.google.com/docs/cloud-messaging/downstream#monitor-token-generation_3
and https://github.com/firebase/quickstart-android/blob/master/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/MyFirebaseMessagingService.java
To ensure that the message you are sending, the docs say, "Use your app server and FCM server API: Set the data key only. Can be either collapsible or non-collapsible."
See https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages
There are two types of messages: notification messages and data messages.
If you only send data message, that is without notification object in your message string. It would be invoked when your app in background.
Check the answer of #Mahesh Kavathiya. For my case, in server code has only like this:
{
"notification": {
"body": "here is body",
"title": "Title",
},
"to": "sdfjsdfonsdofoiewj9230idsjkfmnkdsfm"
}
You need to change to:
{
"data": {
"body": "here is body",
"title": "Title",
"click_action": "YOUR_ACTION"
},
"notification": {
"body": "here is body",
"title": "Title"
},
"to": "sdfjsdfonsdofoiewj9230idsjkfmnkdsfm"
}
Then, in case app in Background, the default activity intent extra will get "data"
Good luck!
That is the intended behavior, you need to set click_action in firebase notification data set to be able to receive data from background.
See updated answer here:
https://stackoverflow.com/a/73724040/7904082
There are 2 types of Firebase push-notifications:
1- Notification message(Display message) ->
-- 1.1 If you choose this variant, the OS will create it self a notification if app is in Background and will pass the data in the intent. Then it's up to the client to handle this data.
-- 1.2 If the app is in Foreground then the notification it will be received via callback-function in the FirebaseMessagingService and it's up to the client to handle it.
2- Data messages(up to 4k data) -> These messages are used to send only data to the client(silently) and it's up to the client to handle it for both cases background/foreground via callback-function in FirebaseMessagingService
This is according official docs:https://firebase.google.com/docs/cloud-messaging/concept-options
Just override the OnCreate method of FirebaseMessagingService. It is called when your app is in background:
public override void OnCreate()
{
// your code
base.OnCreate();
}
I am using FCM in my app. I followed the Firebase documentation and all seems to be working as expected.
I am able to receive message notifications when the app is killed, in background and in foreground.
Now I want to give full control the user by adding some app preferences.
The user can disable all notification from the app settings
The user can change notification ringtone from the app settings
I have an idea on how to do it when the app is in killed or in the foreground. I am thinking of accessing the sharedprefs in my FirebaseMessagingService to see if the app should show the notification and check what ringtone to use. Unless there is a generic way to set those parameters, like "unsubscribing" from all notification or changing the app default notification ringtone which I am not aware of?
However I have no idea how i can do it for the notifications received when the app in is the background since the notifications are delivered to the device's system tray.
Anyone has any idea or can point me to some documentation... I am not finding any info on this?
By default, notifications affect every user. If you'd like to allow users to opt-out of all notifications (or certain subsets of notifications), you should use topic messaging: by default, you'd call subscribeToTopic() for all users and if they opt-out of notifications, you'd call unsubscribeToTopic().
By sending a notification to a topic, only the subscribed users will receive it.
The notification payload support documentation explains how to include a sound parameter to override the ringtone - on Android, that can be any sound bundled in the app's res/raw directory.
It should be noted that neither of these features are supported in the Console, but require that you create your own Firebase Cloud Messaging Server
As firebase notification service is having 2 object 1st is "data" and 2nd is "notification", when you are sending push from firebase console it sending data in "notification" object. When u handle the notification in FirebaseMessagingService.class you are creating a custom notification with NotificationBuilder. When App is in background you wont be able to create notification from "notification" object. so, your custom made notification wont be appear in notification tray, You need to push a notification from your backend and send notification contents in "data" object. You will be able to customise your notification every time.
please refer this for more : https://firebase.google.com/docs/cloud-messaging/android/send-multiple
The user can disable all notification from the app settings.
You can use shared preferences as you stated yourself. As far as a generic method is concerned you should look into #ianhanniballake 's answer.
The user can change notification ringtone from the app settings
For default ringtones refer to this link. The 3rd answer in this link also explains how to bring the sound selector from settings activity of your app. If you want custom sounds refer this.
Of course, you'll have to store user preferences so that you don't ask the user each time to select a sound.
And one more thing since you are using a service so you need not access shared preferences each and every time to find which sound to play, you can store the choice in any variable. Whenever there is a change of notif-sound by the user, you can either set a listener or {stop service -> update preferences -> restart service}. Make sure every time the service starts it reads the preferences.
In this AndroidHive tutorials you can find how we change Ringtone for particular app and how to deal with notifications when app is in foreground and app is in background.
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
......
// Check if message contains a notification payload.
if (remoteMessage.getNotification() != null) {
Log.e(TAG, "Notification Body: " + remoteMessage.getNotification().getBody());
handleNotification(remoteMessage.getNotification().getBody());
}
}
When notification type message is sent, firebase automatically shows the notification when the app is in background. If the app is in foreground, handleNotification() method handles the notification message.
private void handleNotification(String message) {
if (!NotificationUtils.isAppIsInBackground(getApplicationContext())) {
// app is in foreground, broadcast the push message
Intent pushNotification = new Intent(Config.PUSH_NOTIFICATION);
pushNotification.putExtra("message", message);
LocalBroadcastManager.getInstance(this).sendBroadcast(pushNotification);
// play notification sound
NotificationUtils notificationUtils = new NotificationUtils(getApplicationContext());
notificationUtils.playNotificationSound();
}else{
// If the app is in background, firebase itself handles the notification
}
}
Here you handle custom Notification Sound-
// Playing notification sound
public void playNotificationSound() {
try {
Uri alarmSound = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE
+ "://" + mContext.getPackageName() + "/raw/notification");
Ringtone r = RingtoneManager.getRingtone(mContext, alarmSound);
r.play();
} catch (Exception e) {
e.printStackTrace();
}
}
Firebase will not call your onMessageReceived when your app is in background or killed, and you can't customise your notification. System generated notification will show.
to make firebase library to call your onMessageReived in every cases
a) Foreground
b) Background
c) Killed
you must not put json key "notification" in your request to firebase api but instead use "data", see below.
For example following message will not call onMessagereceived()
{
"to": "/topics/test",
"notification": {
"title" : "title",
"text": "data!",
"icon": "ic_notification"
}
}
but this will work
{
"to": "/topics/test",
"data": {
"text":"text",
"title":"",
"line1":"testline",
"line2":"test"
}
}
see this link:https://firebase.google.com/docs/cloud-messaging/concept-options
it has a detailed description of firebase message type
For example:
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, "From: " + remoteMessage.getFrom());
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data payload: " + remoteMessage.getData());
}
if(remoteMessage.getData().get("state").toString().equals("Request")){
sendNotification(remoteMessage.getData().get("body").toString(), remoteMessage.getData().get("message").toString(), remoteMessage.getData().get("state").toString(),Activity_name.class);
}
}
private void sendNotification(String messageBody, String data, String state,Class<?> activityCompat) {
int requestID = (int) System.currentTimeMillis();
Intent intent = new Intent(this, activityCompat);
Bundle bundle = new Bundle();
bundle.putString("message", data);
bundle.putString("state", state);
intent.putExtras(bundle);
PendingIntent pendingIntent = PendingIntent.getActivity(this, requestID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.small_logo)
.setContentTitle("Title")
.setContentText(messageBody).setContentIntent(pendingIntent)
.setAutoCancel(true)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(messageBody))
.setTicker(messageBody);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationBuilder.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
Notification notification = notificationBuilder.build();
notificationManager.notify((int) Calendar.getInstance().getTimeInMillis(), notification);
}
I'm working with Firebase and testing sending notifications to my app from my server while the app is in the background. The notification is sent successfully, it even appears on the notification centre of the device, but when the notification appears or even if I click on it, the onMessageReceived method inside my FCMessagingService is never called.
When I tested this while my app was in the foreground, the onMessageReceived method was called and everything worked fine. The problem occurs when the app is running in the background.
Is this intended behaviour, or is there a way I can fix this?
Here is my FBMessagingService:
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class FBMessagingService extends FirebaseMessagingService {
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.i("PVL", "MESSAGE RECEIVED!!");
if (remoteMessage.getNotification().getBody() != null) {
Log.i("PVL", "RECEIVED MESSAGE: " + remoteMessage.getNotification().getBody());
} else {
Log.i("PVL", "RECEIVED MESSAGE: " + remoteMessage.getData().get("message"));
}
}
}
This is working as intended, notification messages are delivered to your onMessageReceived callback only when your app is in the foreground. If your app is in the background or closed then a notification message is shown in the notification center, and any data from that message is passed to the intent that is launched as a result of the user tapping on the notification.
You can specify a click_action in your JSON to indicate the intent that should be launched when the notification is tapped by the user. The main activity is used if no click_action is specified.
When the intent is launched you can use the
getIntent().getExtras();
to retrieve a Set that would include any data sent along with the notification message.
For more on notification message see docs.
Remove notification field completely from your server request. Send only data and handle it in onMessageReceived() otherwise your onMessageReceived() will not be triggered when app is in background or killed.
Don't forget to include "priority": "high" field in your notification request. According to the documentation: data messages are sent with a normal priority, thus they will not arrive instantly; it could also be the problem.
Here is what I am sending from server
{
"data":{
"id": 1,
"missedRequests": 5
"addAnyDataHere": 123
},
"to": "fhiT7evmZk8:APA91bFJq7Tkly4BtLRXdYvqHno2vHCRkzpJT8QZy0TlIGs......",
"priority": "high"
}
So you can receive your data in onMessageReceived(RemoteMessage message) like this....let say I have to get id
Object obj = message.getData().get("id");
if (obj != null) {
int id = Integer.valueOf(obj.toString());
}
this method handleIntent() has been depreciated, so handling a notification can be done as below:
Foreground State: The click of the notification will go to the pending Intent's activity which you are providing while creating a notification pro-grammatically as it generally created with data payload of the notification.
Background/Killed State - Here, the system itself creates a notification based on notification payload and clicking on that notification will take you to the launcher activity of the application where you can easily fetch Intent data in any of your life-cycle methods.
Here is more clear concepts about firebase message. I found it from their support team.
Firebase has three message types:
Notification messages : Notification message works on background or foreground. When app is in background, Notification messages are delivered to the system tray. If the app is in the foreground, messages are handled by onMessageReceived() or didReceiveRemoteNotification callbacks. These are essentially what is referred to as Display messages.
Data messages: On Android platform, data message can work on background and foreground. The data message will be handled by onMessageReceived(). A platform specific note here would be: On Android, the data payload can be retrieved in the Intent used to launch your activity. To elaborate, if you have "click_action":"launch_Activity_1", you can retrieve this intent through getIntent() from only Activity_1.
Messages with both notification and data payloads: When in the background, apps receive the notification payload in the notification tray, and only handle the data payload when the user taps on the notification. When in the foreground, your app receives a message object with both payloads available. Secondly, the click_action parameter is often used in notification payload and not in data payload. If used inside data payload, this parameter would be treated as custom key-value pair and therefore you would need to implement custom logic for it to work as intended.
Also, I recommend you to use onMessageReceived method (see Data message) to extract the data bundle. From your logic, I checked the bundle object and haven't found expected data content. Here is a reference to a similar case which might provide more clarity.
From server side, firebase notification should bellow format:
Server side should send "notification" object. Lacks of "notification" object in my TargetActivity didn't getting message using getIntent().
Correct message format is given bellow:
{
"data": {
"body": "here is body",
"title": "Title"
},
"notification": {
"body": "here is body",
"title": "Title",
"click_action": "YOUR_ACTION"
},
"to": "ffEseX6vwcM:APA91bF8m7wOF MY FCM ID 07j1aPUb"
}
Here is more clear concepts about firebase message. I found it from their support team.
For more info visit my this thread and this thread
I had the same problem. It is easier to use the 'data message' instead of the 'notification'. The data message always load the class onMessageReceived.
In that class you can make your own notification with the notificationbuilder.
Example:
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
sendNotification(remoteMessage.getData().get("title"),remoteMessage.getData().get("body"));
}
private void sendNotification(String messageTitle,String messageBody) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0 /* request code */, intent,PendingIntent.FLAG_UPDATE_CURRENT);
long[] pattern = {500,500,500,500,500};
Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = (NotificationCompat.Builder) new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentTitle(messageTitle)
.setContentText(messageBody)
.setAutoCancel(true)
.setVibrate(pattern)
.setLights(Color.BLUE,1,1)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
As per Firebase Cloud Messaging documentation-If Activity is in foreground then onMessageReceived will get called. If Activity is in background or closed then notification message is shown in the notification center for app launcher activity.
You can call your customized activity on click of notification if your app is in background by calling rest service api for firebase messaging as:
URL-https://fcm.googleapis.com/fcm/send
Method Type- POST
Header- Content-Type:application/json
Authorization:key=your api key
Body/Payload:
{ "notification": {
"title": "Your Title",
"text": "Your Text",
"click_action": "OPEN_ACTIVITY_1" // should match to your intent filter
},
"data": {
"keyname": "any value " //you can get this data as extras in your activity and this data is optional
},
"to" : "to_id(firebase refreshedToken)"
}
And with this in your app you can add below code in your activity to be called:
<intent-filter>
<action android:name="OPEN_ACTIVITY_1" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
onMessageReceived(RemoteMessage remoteMessage) method called based on the following cases.
FCM Response With notification and data block:
{
"to": "device token list",
"notification": {
"body": "Body of Your Notification",
"title": "Title of Your Notification"
},
"data": {
"body": "Body of Your Notification in Data",
"title": "Title of Your Notification in Title",
"key_1": "Value for key_1",
"image_url": "www.abc.com/xyz.jpeg",
"key_2": "Value for key_2"
}
}
App in Foreground:
onMessageReceived(RemoteMessage remoteMessage) called, shows LargeIcon and BigPicture in the notification bar. We can read the content from both notification and data block
App in Background:
onMessageReceived(RemoteMessage remoteMessage) not called, system tray will receive the message and read body and title from notification block and shows default message and title in the notification bar.
FCM Response With only data block:
In this case, removing notification blocks from json
{
"to": "device token list",
"data": {
"body": "Body of Your Notification in Data",
"title": "Title of Your Notification in Title",
"key_1": "Value for key_1",
"image_url": "www.abc.com/xyz.jpeg",
"key_2": "Value for key_2"
}
}
Solution for calling onMessageReceived()
App in Foreground:
onMessageReceived(RemoteMessage remoteMessage) called, shows LargeIcon and BigPicture in the notification bar. We can read the content from both notification and data block
App in Background:
onMessageReceived(RemoteMessage remoteMessage) called, system tray will not receive the message because of notification key is not in the response. Shows LargeIcon and BigPicture in the notification bar
Code
private void sendNotification(Bitmap bitmap, String title, String
message, PendingIntent resultPendingIntent) {
NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle();
style.bigPicture(bitmap);
Uri defaultSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
String NOTIFICATION_CHANNEL_ID = mContext.getString(R.string.default_notification_channel_id);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "channel_name", NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(notificationChannel);
}
Bitmap iconLarge = BitmapFactory.decodeResource(mContext.getResources(),
R.drawable.mdmlogo);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.mdmlogo)
.setContentTitle(title)
.setAutoCancel(true)
.setSound(defaultSound)
.setContentText(message)
.setContentIntent(resultPendingIntent)
.setStyle(style)
.setLargeIcon(iconLarge)
.setWhen(System.currentTimeMillis())
.setPriority(Notification.PRIORITY_MAX)
.setChannelId(NOTIFICATION_CHANNEL_ID);
notificationManager.notify(1, notificationBuilder.build());
}
Reference Link:
https://firebase.google.com/docs/cloud-messaging/android/receive
If app is in the background mode or inactive(killed), and you click on Notification, you should check for the payload in LaunchScreen(in my case launch screen is MainActivity.java).
So in MainActivity.java on onCreate check for Extras:
if (getIntent().getExtras() != null) {
for (String key : getIntent().getExtras().keySet()) {
Object value = getIntent().getExtras().get(key);
Log.d("MainActivity: ", "Key: " + key + " Value: " + value);
}
}
I got the same issue. If the app is foreground - it triggers my background service where I can update my database based on the notification type.
But, the app goes to the background - the default notification service will be taken care to show the notification to the user.
Here is my solution to identify app in background and trigger your background service,
public class FirebaseBackgroundService extends WakefulBroadcastReceiver {
private static final String TAG = "FirebaseService";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "I'm in!!!");
if (intent.getExtras() != null) {
for (String key : intent.getExtras().keySet()) {
Object value = intent.getExtras().get(key);
Log.e("FirebaseDataReceiver", "Key: " + key + " Value: " + value);
if(key.equalsIgnoreCase("gcm.notification.body") && value != null) {
Bundle bundle = new Bundle();
Intent backgroundIntent = new Intent(context, BackgroundSyncJobService.class);
bundle.putString("push_message", value + "");
backgroundIntent.putExtras(bundle);
context.startService(backgroundIntent);
}
}
}
}
}
In the manifest.xml
<receiver android:exported="true" android:name=".FirebaseBackgroundService" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</receiver>
Tested this solution in latest android 8.0 version. Thanks
Override the handleIntent Method of the FirebaseMessageService works for me.
here the code in C# (Xamarin)
public override void HandleIntent(Intent intent)
{
try
{
if (intent.Extras != null)
{
var builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
foreach (string key in intent.Extras.KeySet())
{
builder.AddData(key, intent.Extras.Get(key).ToString());
}
this.OnMessageReceived(builder.Build());
}
else
{
base.HandleIntent(intent);
}
}
catch (Exception)
{
base.HandleIntent(intent);
}
}
and thats the Code in Java
public void handleIntent(Intent intent)
{
try
{
if (intent.getExtras() != null)
{
RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
for (String key : intent.getExtras().keySet())
{
builder.addData(key, intent.getExtras().get(key).toString());
}
onMessageReceived(builder.build());
}
else
{
super.handleIntent(intent);
}
}
catch (Exception e)
{
super.handleIntent(intent);
}
}
By default the Launcher Activity in you app will be launched when your app is in background and you click the notification, if you have any data part with your notifcation you can handle it in the same activity as follows,
if(getIntent().getExtras()! = null){
//do your stuff
}else{
//do that you normally do
}
I might be very late here to answers but the official documentation is a bit confusing.
Also it's clearly stated that there are two types of notification
Notification message: Automatically handled by FCM
Data message: Handled by the client app.
No doubt if the server sends a Data message then onMessageReceived() methods definitely get invoked but in the case of Notification message onMessageReceived() method will get invoked only if the app is in the foreground and when the app is in the background the data we are sending is just null.
Example:
Lets assume server is sending notification message type:
A. In case of Foreground:
remoteMessage.data["key"] will work
B. In case of background:
-remoteMessage.data["key"] will return null
but here if you find the same intent data in defaul activity with getIntent().getExtras().getString("key") will work
C. In case of kill:
-remoteMessage.data["key"] will return null
but here if you find the same intent data in defaul activity with getIntent().getExtras().getString("key") will work
Now, let's assume server is sending data message type:
D. In case of Foreground:
remoteMessage.data["key"] will work
E. In case of background:
remoteMessage.data["key"] will work
F. In case of kill:
remoteMessage.data["key"] will work
No doubt data message will always invokes onMessageReceived() method but in case of notification message and app is in background/kill state you can use the solution of B. Thanks
I hope it will save everyone's time.
according to the solution from t3h Exi i would like to post the clean code here. Just put it into MyFirebaseMessagingService and everything works fine if the app is in background mode. You need at least to compile com.google.firebase:firebase-messaging:10.2.1
#Override
public void handleIntent(Intent intent)
{
try
{
if (intent.getExtras() != null)
{
RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
for (String key : intent.getExtras().keySet())
{
builder.addData(key, intent.getExtras().get(key).toString());
}
onMessageReceived(builder.build());
}
else
{
super.handleIntent(intent);
}
}
catch (Exception e)
{
super.handleIntent(intent);
}
}
If app is in background Fire-base by default handling notification But if we want to our custom notification than we have to change our server side, which is responsible for to send our custom data(data payload)
Remove notification payload completely from your server request. Send only Data and handle it in onMessageReceived() otherwise your onMessageReceived will not be triggered when app is in background or killed.
now,your server side code format look like,
{
"collapse_key": "CHAT_MESSAGE_CONTACT",
"data": {
"loc_key": "CHAT_MESSAGE_CONTACT",
"loc_args": ["John Doe", "Contact Exchange"],
"text": "John Doe shared a contact in the group Contact Exchange",
"custom": {
"chat_id": 241233,
"msg_id": 123
},
"badge": 1,
"sound": "sound1.mp3",
"mute": true
}
}
NOTE: see this line in above code
"text": "John Doe shared a contact in the group Contact Exchange"
in Data payload you should use "text" parameter instead of "body" or "message" parameters for message description or whatever you want to use text.
onMessageReceived()
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.e(TAG, "From: " + remoteMessage.getData().toString());
if (remoteMessage == null)
return;
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
/* Log.e(TAG, "Data Payload: " + remoteMessage.getData().toString());*/
Log.e(TAG, "Data Payload: " + remoteMessage);
try {
Map<String, String> params = remoteMessage.getData();
JSONObject json = new JSONObject(params);
Log.e("JSON_OBJECT", json.toString());
Log.e(TAG, "onMessageReceived: " + json.toString());
handleDataMessage(json);
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.getMessage());
}
}
}
I had similar issue. Based on the answers and references mentioned in this page, here are my two cents on how I resolved my problem with the below approach:
The message format I had earlier was as below:
{
"notification": {
"title": "AppName",
"sound": null,
"body": "Hey!YouhaveaMessage"
},
"data": {
"param1": null,
"param2": [
238
],
"id": 1
},
"to": "--the device push token here--"
}
I modified the message format to the below:
{
"data": {
"title": "AppName",
"body": "Hey! You have a message",
"param1": null,
"param2": [
238
],
"id": 1
},
"priority": "high",
"to": " — device push token here — "
}
Then I retrieved the title, body and all the parameters from the "data" payload itself. This solved the problem and I could then get the OnMessageReceived callback even though the app is in the background.
I wrote a blog post explaining the same issue, you can find it here.
{
"notification": {
"title": "Notification Title",
"body": "Notification Body",
"click_action": "ActivityToOpen"
},
"data": {
"key": "value "
},
"to": "id"
}
If the FCM payload has notification{} block like the above and the app is in the background, the system builds the notification for you with the title and body given in notification{}. When the user clicks on it, the activity mentioned in click_action opens, if nothing is given the default launcher activity opens and data inside the data{} block can be accessed from
intent.extras // of the launcher activity
If the app is in the foreground the function onMessageReceived() in FirebaseMessagingService() class gets triggered. We will have to build the notification by ourselves and we can access the data as following:
val value = message.data.getOrDefault("key", "")
If the FCM payload is without notification block{} like following;
{
"data": {
"title": "Notification Title",
"body": "Notification Body",
"key": "value "
},
"to" : "id"
}
The function onMessageReceived() in FirebaseMessagingService() class gets triggered regardless of app being in background or foreground and we will have to build notification by ourselves.
We can access the data as following:
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
val title = message.data.getOrDefault("title", "")
val body = message.data.getOrDefault("body", "")
}
Just call this in your MainActivity's onCreate Method :
if (getIntent().getExtras() != null) {
// Call your NotificationActivity here..
Intent intent = new Intent(MainActivity.this, NotificationActivity.class);
startActivity(intent);
}
Try this:
public void handleIntent(Intent intent) {
try {
if (intent.getExtras() != null) {
RemoteMessage.Builder builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
for (String key : intent.getExtras().keySet()) {
builder.addData(key, intent.getExtras().get(key).toString());
}
onMessageReceived(builder.build());
} else {
super.handleIntent(intent);
}
} catch (Exception e) {
super.handleIntent(intent);
}
}
I had this issue(app doesn't want to open on notification click if app is in background or closed), and the problem was an invalid click_action in notification body, try removing or changing it to something valid.
The point which deserves highlighting is that you have to use data message - data key only - to get onMessageReceived handler called even when the app is in background. You shouldn't have any other notification message key in your payload, otherwise the handler won't get triggered if the app is in background.
It is mentioned (but not so emphasized in FCM documentation) here:
https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages
Use your app server and FCM server API: Set the data key only. Can be
either collapsible or non-collapsible.
The backend I'm working with is using Notification messages and not Data messages. So after reading all the answers I tried to retrieve the extras from the bundle of the intent that comes to the launched activity.
But no matter which keys I tried to retrieve from getIntent().getExtras();, the value was always null.
However, I finally found a way to send data using Notification messages and retrieve it from the intent.
The key here is to add the data payload to the Notification message.
Example:
{
"data": {
"message": "message_body",
"title": "message_title"
},
"notification": {
"body": "test body",
"title": "test title"
},
"to": "E4An.."
}
After you do this, you will be able to get your info in this way:
intent.getExtras().getString("title")
will be message_title
and
intent.getExtras().getString("message")
will be message_body
Reference
If your problem is related to showing Big Image i.e. if you are sending push notification with an image from firebase console and it displays the image only if the app in the foreground. The solution for this problem is to send a push message with only data field. Something like this:
{ "data": { "image": "https://static.pexels.com/photos/4825/red-love-romantic-flowers.jpg", "message": "Firebase Push Message Using API" "AnotherActivity": "True" }, "to" : "device id Or Device token" }
When message is received and your app is in background the notification is sent to the extras intent of the main activity.
You can check the extra value in the oncreate() or onresume() function of the main activity.
You can check for the fields like data, table etc ( the one specified in the notification)
for example I sent using data as the key
public void onResume(){
super.onResume();
if (getIntent().getStringExtra("data")!=null){
fromnotification=true;
Intent i = new Intent(MainActivity.this, Activity2.class);
i.putExtra("notification","notification");
startActivity(i);
}
}
I think the answers to tell you change message type to data are clear to you.
But sometimes, if you cannot decide your the message type you received and you have to handle it. I post my method here. You just implemented FirebaseMessagingService and handle your message in handlIntent() method. From there you could customize your own notification. You can implement your own method sendYourNotificatoin()
class FCMPushService : FirebaseMessagingService() {
companion object {
private val TAG = "FCMPush"
}
override fun handleIntent(intent: Intent?) {
Logger.t(TAG).i("handleIntent:${intent.toString()}")
val data = intent?.extras as Bundle
val remoteMessage = RemoteMessage(data)
if (remoteMessage.data.isNotEmpty()) {
val groupId: String = remoteMessage.data[MESSAGE_KEY_GROUP_ID] ?: ""
val title = remoteMessage.notification?.title ?: ""
val body = remoteMessage.notification?.body ?: ""
if (title.isNotEmpty() && body.isNotEmpty())
sendYourNotificatoin(this, title, body, groupId)
}
}
}
I was having the same issue and did some more digging on this. When the app is in the background, a notification message is sent to the system tray, BUT a data message is sent to onMessageReceived()
See https://firebase.google.com/docs/cloud-messaging/downstream#monitor-token-generation_3
and https://github.com/firebase/quickstart-android/blob/master/messaging/app/src/main/java/com/google/firebase/quickstart/fcm/MyFirebaseMessagingService.java
To ensure that the message you are sending, the docs say, "Use your app server and FCM server API: Set the data key only. Can be either collapsible or non-collapsible."
See https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages
There are two types of messages: notification messages and data messages.
If you only send data message, that is without notification object in your message string. It would be invoked when your app in background.
Check the answer of #Mahesh Kavathiya. For my case, in server code has only like this:
{
"notification": {
"body": "here is body",
"title": "Title",
},
"to": "sdfjsdfonsdofoiewj9230idsjkfmnkdsfm"
}
You need to change to:
{
"data": {
"body": "here is body",
"title": "Title",
"click_action": "YOUR_ACTION"
},
"notification": {
"body": "here is body",
"title": "Title"
},
"to": "sdfjsdfonsdofoiewj9230idsjkfmnkdsfm"
}
Then, in case app in Background, the default activity intent extra will get "data"
Good luck!
That is the intended behavior, you need to set click_action in firebase notification data set to be able to receive data from background.
See updated answer here:
https://stackoverflow.com/a/73724040/7904082
There are 2 types of Firebase push-notifications:
1- Notification message(Display message) ->
-- 1.1 If you choose this variant, the OS will create it self a notification if app is in Background and will pass the data in the intent. Then it's up to the client to handle this data.
-- 1.2 If the app is in Foreground then the notification it will be received via callback-function in the FirebaseMessagingService and it's up to the client to handle it.
2- Data messages(up to 4k data) -> These messages are used to send only data to the client(silently) and it's up to the client to handle it for both cases background/foreground via callback-function in FirebaseMessagingService
This is according official docs:https://firebase.google.com/docs/cloud-messaging/concept-options
Just override the OnCreate method of FirebaseMessagingService. It is called when your app is in background:
public override void OnCreate()
{
// your code
base.OnCreate();
}
I have an Android app developed with Ionic Framework. I'm using the ngCordova plugin for push notifications and sending them using parse.com
The notifications are received when the app is running, but the notifications are not shown on the notification tray when the app is on background. I receive something like this:
notification = {
payload: {
data: {
alert: "message",
}
}
}
But, when I send them directly through CGM, the notification do appear on the notification tray. And the object I receive is like:
notification = {
message: "this appear on notification tray",
payload: {
message: "this appear on notification tray"
}
}
Is there something wrong with Parse? Or is something I'm missing about Parse?
This is kind of an old post, but I ran across this issue using Xamarin and Parse Push notifications, but my work around may work for you (and others that may see this in the future).
I wound up broadcasting a local Push Notification after the Parse notification is received.
First assign a receiver to the Parse notification event:
ParsePush.ParsePushNotificationReceived += PushNotificationReceived;
Then in the method:
void PushNotificationReceived (object sender, ParsePushNotificationEventArgs e)
{
var payload = JObject.Parse (e.StringPayload); // Parse the JSON payload
Notification.Builder builder = new Notification.Builder (this);
builder.SetContentTitle (payload ["alert"].ToString ());
builder.SetContentText (payload ["androidDetail"].ToString ()); // Note: this is another field I added to the Parse Notification
builder.SetDefaults (NotificationDefaults.Sound | NotificationDefaults.Vibrate);
builder.SetSound (RingtoneManager.GetDefaultUri (RingtoneType.Notification));
builder.SetSmallIcon (Resource.Drawable.small_notification_icon);
var largeIcon = BitmapFactory.DecodeResource (Resources, Resource.Drawable.large_notification_icon);
builder.SetLargeIcon (largeIcon);
var notification = builder.Build ();
notification.Defaults |= NotificationDefaults.Vibrate;
NotificationManager notManager = (NotificationManager)GetSystemService (Context.NotificationService);
notManager.Notify (0, notification);
}
Hope this helps you and anyone else who comes across this!