I have an android app that successfully receives notifications from the Firebase console. I now intend to build a nodejs server where we can send these notifications to save logging into the firebase console, however, it appears that the node.js library 'firebase-admin' only supports sending to individual device ids or topics rather than all devices as per the console.
So I've made a nodejs service to send to topic 'all', and tried to alter android to receive these notifications, however I get no notifications on my device from this nodejs server.
Here is my server code:
var admin = require("firebase-admin");
var serviceAccount = require("./firebase-privatekey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://myapp-android-xxx.firebaseio.com"
});
var payload = {
notification: {
title: "Account Deposit",
body: "A deposit to your savings account has just cleared."
},
data: {
account: "Savings",
balance: "$3020.25"
},
topic: "all",
};
admin.messaging().send(payload)
.then(function(response) {
console.log("Successfully sent message:", response);
})
.catch(function(error) {
console.log("Error sending message:", error);
});
This is the android code that worked with the console notifications:
public class MyNotificationService extends FirebaseMessagingService {
public MyNotificationService() {
}
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d("Firebase", "From: " + remoteMessage.getFrom());
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
Log.d("Firebase", "Message data payload: " + remoteMessage.getData());
handleNow(remoteMessage.getData(), remoteMessage.getNotification().getBody());
}
// Check if message contains a notification payload.
if (remoteMessage.getNotification() != null) {
Log.d("Firebase", "Message Notification Body: " + remoteMessage.getNotification().getBody());
}
}
public void handleNow(Map<String, String> data, String title) {
NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
int notificationId = 1;
String channelId = "channel-01";
String channelName = "Channel Name";
int importance = NotificationManager.IMPORTANCE_HIGH;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel mChannel = new NotificationChannel(
channelId, channelName, importance);
notificationManager.createNotificationChannel(mChannel);
}
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext(), channelId)
.setSmallIcon(R.drawable.myapp_notification_icon)
.setBadgeIconType(R.drawable.myapp_notification_icon)
.setContentTitle(title)
.setContentText(data.get("information"));
notificationManager.notify(notificationId, mBuilder.build());
}
}
and this is the new (Additional not replaced) code, with the intention of receiving topic messages:
#Override
protected void onCreate(Bundle savedInstanceState) {
//other code...
FirebaseMessaging.getInstance().subscribeToTopic("all")
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
System.out.println("win");
} else {
System.out.println("fail");
}
}
});
}
The nodejs server tells me it was a successful send of a message, yet a breakpoint on either the win or fail message never gets hit on android
I had quite the same problem. The solution I found was adding:
<service
android:name=".java.MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
to AndroidManifest.xml and:
#Override
public void onDeletedMessages() {
super.onDeletedMessages();
}
to MyFirebaseMessagingService.
According to the docs.
Override onDeletedMessages
In some situations, FCM may not deliver a message. This occurs when
there are too many messages (>100) pending for your app on a
particular device at the time it connects or if the device hasn't
connected to FCM in more than one month. In these cases, you may
receive a callback to FirebaseMessagingService.onDeletedMessages()
When the app instance receives this callback, it should perform a full
sync with your app server. If you haven't sent a message to the app on
that device within the last 4 weeks, FCM won't call
onDeletedMessages().
Related
My notification work if the app is in the background but it doesn't work in the foreground
this is my MyFirebaseMessagingService class:
public class MyFirebaseMessagingService extends FirebaseMessagingService {
public static final String TAG = "MessagingService";
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
String title = remoteMessage.getNotification().getTitle();
String message = remoteMessage.getNotification().getBody();
String uid = remoteMessage.getData().get("uid");
String click_action = remoteMessage.getNotification().getClickAction();
Log.d(TAG, "onMessageReceived: "+title + message + uid+" "+click_action);
Intent intent = new Intent(click_action);
if (click_action.equals("com.example.android.ProfileFriends")){
intent.putExtra("uid",uid);
}else if (click_action.equals("com.example.android.ChatActivity")){
}
// intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
notificationBuilder.setContentTitle(title);
notificationBuilder.setContentText(message);
notificationBuilder.setSmallIcon(R.mipmap.ic_launcher_event);
notificationBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
notificationBuilder.setCategory(NotificationCompat.CATEGORY_MESSAGE);
notificationBuilder.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0,notificationBuilder.build());
}
}
Please, how can i resolve this problem?
FCM has different behaviours for app status (foreground and background / killed).
You should handle this by the payload you sent from server, according to your use case.
The msg sent from server has to be sent in either "notification" or "data" format, from dashboard or server side api.
Note: From firebase dashobard you can only send "notification" body and not data. In such cases, FCM will directly display the notif without giving a callback to your app.
Server side
Below are sample formats :
Notification Type Format
Note : Android System will by default display the notification in the notification tray and you don't need to display it.
{
"to": "your_token_id",
"notification" : {
"title" : "FCM Notification title!",
"body" : "FCM Notification subtext!",
"content_available" : true,
"priority" : "high"
}
}
Data Format (For receiving callback in app, in foreground and background)
Note : You have to handle callback and display notif on your own.
{
"to": "your_token_id",
"data" : {
"title" : "FCM Notification Title ",
"subtext" : "FCM Notification Sub Title",
"type" : "999",
"priority" : "high"
}
}
Android Client
To handle the payload received in your Android receiver, checl the official guide here
/* The class extends FirebaseMessagingService() */
override fun onMessageReceived(remoteMessage: RemoteMessage) {
Log.d(TAG, "From: ${remoteMessage.from}")
// Check if message contains a data payload.
remoteMessage.data.isNotEmpty().let {
Log.d(TAG, "Message data payload: " + remoteMessage.data)
if (/* Check if data needs to be processed by long running job */ true) {
// For long-running tasks (10 seconds or more) use WorkManager.
scheduleJob()
} else {
// Handle message within 10 seconds
handleNow()
}
}
// Check if message contains a notification payload.
remoteMessage.notification?.let {
Log.d(TAG, "Message Notification Body: ${it.body}")
}
// Also if you intend on generating your own notifications as a result of a received FCM
// message, here is where that should be initiated. See sendNotification method below.
}
Check the documentation here
I am integrating firebase FCM push notifications on an android app and all things are working fine except when app goes background/killed, the MyFirebaseMessagingService is not called. It works fine when app is in foreground. The onMessageReceived() is not at all called when app is in background.
I want to send push notifications from Firebase console UI only to all subscribers at once, not trying to do 1-1 messaging or sending through web server.
Not that the service is not running when app is closed from Recent Apps tray, the app is not at all working when simply the app goes to background.
Below are the respective codes:
MyFirebaseMessagingService
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMsgService";
/**
* Called when message is received.
*
* #param remoteMessage Object representing the message received from Firebase Cloud Messaging.
*/
// [START receive_message]
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// TODO(developer): Handle FCM messages here.
// Not getting messages here? See why this may be:
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 (/* Check if data needs to be processed by long running job */ true) {
// For long-running tasks (10 seconds or more) use Firebase Job Dispatcher.
scheduleJob();
} else {
// Handle message within 10 seconds
handleNow();
}
}
// Check if message contains a notification payload.
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
}
// Also if you intend on generating your own notifications as a result of a received FCM
// message, here is where that should be initiated. See sendNotification method below.
}
Service Declaration in Manifest
<service android:name=".MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
Application Class Declaration
FirebaseMessaging.getInstance().subscribeToTopic("weather")
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
String msg = "Subscribed";
if (!task.isSuccessful()) {
msg = "Message";
}
Log.d("Token", msg);
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
});
I want to integrate fcm for having push notification functionality in my app. But the problem is my fcm token is not getting generated at all. I used the same code I used in this project into a new project and it worked without any issue.
But with this project I have tried everything to no result at all.
I tried with both the deprecated onTokenRefresh() method as well as the new onNewToken() method but none of them is working for me.
Below is the code for the same.
public class CustomFirebaseMessagingService extends FirebaseMessagingService {
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
// TODO(developer): Handle FCM messages here.
Log.d("Firebase", "From: " + remoteMessage.getFrom());
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
Log.d("Firebase", "Message data payload: " + remoteMessage.getData());
}
// Check if message contains a notification payload.
if (remoteMessage.getNotification() != null) {
Log.d("Firebase", "Message Notification Body: " + remoteMessage.getNotification().getBody());
}
// Also if you intend on generating your own notifications as a result of a received FCM
// message, here is where that should be initiated. See sendNotification method below.
}
#Override
public void onNewToken(String s) {
super.onNewToken(s);
Log.d("token",s);
}
}
And this is the code with onTokenRefresh() method
public class MyFirebaseInstanceIdService extends FirebaseInstanceIdService {
#Override
public void onTokenRefresh() {
super.onTokenRefresh();
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d("Firebase", "Refreshed token: " + refreshedToken);
}
}
I have declared both services in the manifest too.
<service android:name=".notification.CustomFirebaseMessagingService"
android:stopWithTask="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service
android:name=".notification.MyFirebaseInstanceIdService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
What I have tried
Deleted the project on console and creating again
Copying whole project to a new folder and open again in Android Studio
Tried with both of the above service
Tried calling FirebaseInstanceId.getToken() but this gives null pointer exception.
Aside from the implementation errors, have a look at how instance ID works: https://developers.google.com/instance-id/, check the chapter Instance ID lifecycle at the bottom.
I can see 2 points where it might go wrong:
You don't have a connection with the Google servers. Check if you have a working internet connection by opening the device and opening a webpage. Also take proxies and firewalls into account, those might block your traffic (for example, if you are in China, the Great Firewall might block your connection with the Instance ID servers).
Make sure you don't already have a token. This is quite a common error. You implement the ID token service, and run the app. It works fine, now you want to send the token to the server, and write the code for it. When you run the app again, you will see no connection to the server, and think there's an issue with your implementation.
What actually happened, is that you already got your token on the first run, and it was cached by the app. The second time it already has a token, and the onNewToken() will not be called.
If you uninstall the app, and install it again, it will ask for a new token on launch.
Finally got the solution to my problem. I got this hint when I was trying to integrate OneSignal notification sdk. The problem was that I had the below code in the application tag of manifest.
tools:node="replace"
This was written in OneSignal docs.
Make sure you are not replacing the tag in your AndroidManifest.xml with tools:node="replace"
As OneSignal was also internally using FireBase I thought to give it a try with the firebase directly and it worked after I removed it.
Hope this will help someone else too
As said by #Nilesh Rathod FirebaseInstanceIdService is depreciated. So no you need only one service in the manifest.
Try this way.
1. First Create a Service
public class YourService extends FirebaseMessagingService {
public static int NOTIFICATION_ID = 1;
#Override
public void onNewToken(String s) {
super.onNewToken(s);
}
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder mNotifyBuilder = new NotificationCompat.Builder(this, "2")
.setSmallIcon(R.drawable.your_icon)
.setContentTitle(remoteMessage.getNotification().getTitle())
.setContentText(remoteMessage.getNotification().getBody())
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (NOTIFICATION_ID > 1073741824) {
NOTIFICATION_ID = 0;
}
Objects.requireNonNull(notificationManager).notify(NOTIFICATION_ID++, mNotifyBuilder.build());
}
}
Now add this to Manifest
<service
android:name=".YourService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
the generate of token for firebase is in first install of app or when you delete the cache for app ,this example of code work for me
public class MyFirebaseInstanceIdService extends FirebaseInstanceIdService {
SharedPreferences sharedPreferences1;
SharedPreferences.Editor editor1;
private static final String PREF_NAME1 = "prefs_token";
private static final String KEY_FCM = "devices_token";
//this method will be called
//when the token is generated
#Override
public void onTokenRefresh() {
sharedPreferences1 = getSharedPreferences(PREF_NAME1, Context.MODE_PRIVATE);
editor1 = sharedPreferences1.edit();
super.onTokenRefresh();
//now we will have the token
String token = FirebaseInstanceId.getInstance().getToken();
editor1.putString(KEY_FCM,token.toString());
editor1.apply();
//for now we are displaying the token in the log
//copy it as this method is called only when the new token is generated
//and usually new token is only generated when the app is reinstalled or the data is cleared
Log.d("MyRefreshedToken", token);
String device_token = sharedPreferences1.getString(KEY_FCM, "");
}
}
The onTokenRefresh/onNewToken methods are only called when a token is generated. Most of the time a token just exists, and is not modified. During that time, onTokenRefresh/onNewToken won't be called.
So most likely, your token was generated when you first add the app, before you had the onTokenRefresh/onNewToken. So to now get the token, you can do two things:
Uninstall and reinstall the app. Deleting the app will delete the existing token, then it will generate a new token on the reinstall, and call your onTokenRefresh/onNewToken.
Also request the token in e.g. your MainActivity.onCreate.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
...
String iidToken = FirebaseInstanceId.getInstance().getToken();
Log.d("Firebase", "Got token: " + iidToken);
Create two java classes and one interface in APP
Interface:
public interface FCMTokenInterface {
void onTokenReceived(String token);
void onFailure();
}
Token Listener Class:
public class MyInstanceIDListenerService extends FirebaseInstanceIdService {
private static FCMTokenInterface fcmTokenCallback;
private static Handler handlerOs = new Handler();
private static int delay=20000;
private static final String TAG = "MyFirebaseIIDService";
private static final String FRIENDLY_ENGAGE_TOPIC = "friendly_engage";
#Override
public void onTokenRefresh() {
String token = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "FCM Token: 3" + token);
//putting token in preference
Prefs.putString(Constant.FCM_TOKEN,token);
FirebaseMessaging.getInstance()
.subscribeToTopic(FRIENDLY_ENGAGE_TOPIC);
}
public static void setCallback(FCMTokenInterface callback) {
try {
String token = FirebaseInstanceId.getInstance().getToken();
if (token != null && !token.isEmpty()) {
//putting token in preference
Prefs.putString(Constant.FCM_TOKEN,token);
callback.onTokenReceived(token);
return;
}
} catch (Exception e) {
retry(callback);
Log.v("SetCallback EXP= ", e.toString());
}
fcmTokenCallback = callback;
// startHandler();
}
public static void retry(FCMTokenInterface callback)
{
setCallback(callback);
}
private static void startHandler() {
handlerOs.postDelayed(new Runnable() {
#Override
public void run() {
retry(fcmTokenCallback);
// fcmTokenCallback.onFailure();
fcmTokenCallback = null;
}
}, delay);
}
private static void clearHandler() {
handlerOs.removeCallbacksAndMessages(null);
}
}
Message Receiving Class:
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "MyFMService";
String CHANNEL_ID = "com.app.app";
NotificationChannel mChannel;
private NotificationManager mManager;
private String title, msg, actionCode;
private int badge = 0;
#RequiresApi(api = Build.VERSION_CODES.O)
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// Handle data payload of FCM messages.
Log.d(TAG, "FCM Message Id: " + remoteMessage.getMessageId());
Log.d(TAG, "FCM Notification Message: " + remoteMessage.getData() + "...." +
remoteMessage.getFrom());
if (remoteMessage.getData() != null) {
Map<String, String> params = remoteMessage.getData();
JSONObject object = new JSONObject(params);
//Log.e("JSON_OBJECT", object.toString());
title = object.optString("title","");
actionCode = object.optString("action_code", "");
msg = object.optString("body", "");
if (remoteMessage.getData().containsKey("badge")) {
badge = Integer.parseInt(remoteMessage.getData().get("badge"));
//Log.d("notificationNUmber", ":" + badge);
setBadge(getApplicationContext(), badge);
Prefs.putBoolean(Constant.HAS_BADGE,true);
}
if (!(title.equals("") && msg.equals("") && actionCode.equals(""))) {
createNotification(actionCode, msg, title);
}
else {
//Log.e("Notification", "Invalid Data");
}
}
}
public void createNotification(String action_code, String msg, String title) {
Intent intent = null;
intent = new Intent(this, HomeActivity.class);
intent.putExtra(Constant.ACTION_CODE, action_code);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel androidChannel = new NotificationChannel(CHANNEL_ID,
title, NotificationManager.IMPORTANCE_DEFAULT);
// Sets whether notifications posted to this channel should display notification lights
androidChannel.enableLights(true);
// Sets whether notification posted to this channel should vibrate.
androidChannel.enableVibration(true);
// Sets the notification light color for notifications posted to this channel
androidChannel.setLightColor(Color.GREEN);
// Sets whether notifications posted to this channel appear on the lockscreen or not
androidChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
getManager().createNotificationChannel(androidChannel);
Notification.Builder nb = new Notification.Builder(getApplicationContext(), CHANNEL_ID)
.setContentTitle(title)
.setContentText(msg)
.setTicker(title)
.setShowWhen(true)
.setSmallIcon(R.mipmap.ic_small_notification)
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(),
R.mipmap.ic_launcher_round))
.setAutoCancel(true)
.setContentIntent(contentIntent);
getManager().notify(101, nb.build());
} else {
try {
#SuppressLint({"NewApi", "LocalSuppress"}) android.support.v4.app.NotificationCompat.Builder notificationBuilder = new android.support.v4.app.NotificationCompat.Builder(this).setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setSmallIcon(R.mipmap.ic_small_notification)
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(),
R.mipmap.ic_launcher_round))
.setContentTitle(title)
.setTicker(title)
.setContentText(msg)
.setShowWhen(true)
.setContentIntent(contentIntent)
.setLights(0xFF760193, 300, 1000)
.setAutoCancel(true).setVibrate(new long[]{200, 400});
/*.setSound(Uri.parse("android.resource://"
+ getApplicationContext().getPackageName() + "/" + R.raw.tone));*/
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify((int) System.currentTimeMillis() /* ID of notification */, notificationBuilder.build());
} catch (SecurityException se) {
se.printStackTrace();
}
}
}
private NotificationManager getManager() {
if (mManager == null) {
mManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
return mManager;
}
}
Implement Interface on your Splash:
class SplashActivity : AppCompatActivity(), FCMTokenInterface {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash)
MyInstanceIDListenerService.setCallback(this)
}
override fun onFailure() {
Log.d("Token", "Unable to get token.")
}
override fun onTokenReceived(token: String?) {
Log.d("Token", token)
Prefs.putString(Constant.FCM_TOKEN, token)
}
}
Manifest under Application TAG
<service android:name="fcm.MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service android:name="fcm.MyInstanceIDListenerService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="#mipmap/ic_launcher_round" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="#color/colorAccent" />
I am trying to use the Firebase messaging in Oreo device, but even though the channel is being created I am unable to receive the data in the proper channel. (Note: this is not working in the emulator, but only on device)
This is my application class:
public class FCMPlayApp extends Application {
#Override
public void onCreate() {
super.onCreate();
FirebaseMessaging.getInstance().subscribeToTopic(MessagingConstants.TOPIC_ALL);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel();
}
}
#RequiresApi(Build.VERSION_CODES.O)
private void createNotificationChannel() {
NotificationChannel allChannel = new NotificationChannel(
"channel_all", "All Channel", NotificationManager.IMPORTANCE_HIGH);
((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
.createNotificationChannel(allChannel);
}
}
Here is my messaging service class:
public class MessagingService extends FirebaseMessagingService {
private static final String TAG = "MessagingService";
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Log.d(TAG, "Message received for topic: " + remoteMessage.getFrom());
}
}
Here is what I have defined in the Android manifest for the default channel:
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="channel_all"/>
Here is what I am sending using Postman tool for the push message:
{
"to": "/topics/all",
"data": {
"msg_bg_color" : "#ABCDEF"
},
"notification": {
"body": "Hello",
"title": "This is test message."
},
"android_channel_id" : "channel_all"
}
Can you please let me know as to what I am missing.
As Diego has mentioned:
android_channel_id should go inside the notification payload
This is the answer. Once Diego answer, I will mark it as an answer. This is just a reference for others.
I have an Android application, where I'm using some method to show notification number on app icon. Now I want to set that number when notification is received.
I thought that I should set the number when notification received so I set it inside onMessageReceived method. But, my problem is when my app is in background, onMessageReceived method not called, so the notification number isn't set.
Following is my code. I set the number inside onMessageReceived. I already tested setBadge method and can verify that it is working. The problem is onMessageReceived is not called so setBadge is also not called, which doesn't set the number.
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// TODO(developer): Handle FCM messages here.
Log.d(TAG, "From: " + remoteMessage.getFrom());
Conts.notificationCounter ++;
//I am setting in here.
setBadge(getApplicationContext(),Conts.notificationCounter );
Log.e("notificationNUmber",":"+ Conts.notificationCounter);
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data payload: " + remoteMessage.getData());
}
// Check if message contains a notification payload.
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
}
// Also if you intend on generating your own notifications as a result of a received FCM
// message, here is where that should be initiated. See sendNotification method below.
}
// [END receive_message]
public static void setBadge(Context context, int count) {
String launcherClassName = getLauncherClassName(context);
if (launcherClassName == null) {
Log.e("classname","null");
return;
}
Intent intent = new Intent("android.intent.action.BADGE_COUNT_UPDATE");
intent.putExtra("badge_count", count);
intent.putExtra("badge_count_package_name", context.getPackageName());
intent.putExtra("badge_count_class_name", launcherClassName);
context.sendBroadcast(intent);
}
public static String getLauncherClassName(Context context) {
PackageManager pm = context.getPackageManager();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
for (ResolveInfo resolveInfo : resolveInfos) {
String pkgName = resolveInfo.activityInfo.applicationInfo.packageName;
if (pkgName.equalsIgnoreCase(context.getPackageName())) {
String className = resolveInfo.activityInfo.name;
return className;
}
}
return null;
}
When I searched this issue, I found that if the coming message is display message then onMessageReceived is called only if app is foreground. But if coming message is data message then onMessageReceived is called even if the app is background.
But my friend told me who is sending the notification(server side), the message already goes as both display and data message. He said that data object is filled.
Following is the JSON for coming message, it has data object.
{
"to":"my_device_id",
"priority":"high",
"notification":{
"body":"Notification Body",
"title":"Notification Title",
"icon":"myicon",
"sound":"default"
},
"data":{
"Nick":"DataNick",
"Room":"DataRoom"
}
}
If I only use data object, onMessageReceived is called as they said but that time notification does not appear at the top.
Now why onMessageReceived is not called if the message is also data message. Should I do something different to handle data message? Is it working same with display messaging in client side.
Any help would be appreciated. Thanks in advance.
No way to call onMessageReceived unless the coming json includes ONLY data payload as I learned from Firebase support.
So I have to use data payload but if you use data payload it does not show notification at the top so you should create your custom notification using data payload information.
So I sent notification to myself when I get the data payload in onMessageReceived. And I set the badge in onMessageReceived right after sending notification to myself.
Following code is the final version.
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
//for data payload
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data payload: " + remoteMessage.getData());
title = remoteMessage.getData().get("title");
sendNotification(remoteMessage.getData().get("body"), title);
badge = Integer.parseInt(remoteMessage.getData().get("badge"));
Log.e("notificationNUmber",":"+badge);
setBadge(getApplicationContext(), badge);
}
//for notification payload so I did not use here
// Check if message contains a notification payload.
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
}
// Also if you intend on generating your own notifications as a result of a received FCM
// message, here is where that should be initiated. See sendNotification method below.
}
// [END receive_message]
private void sendNotification(String messageBody, String title) {
Intent intent = new Intent(this, MainMenuActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, notify_no /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
if (notify_no < 9) {
notify_no = notify_no + 1;
} else {
notify_no = 0;
}
Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher_3_web)
.setContentTitle(title)
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(notify_no + 2 /* ID of notification */, notificationBuilder.build());
}
Thanks for all.
To do stuff when receiving Notification-payloads in the background, just override zzE in FirebaseMessagingService. The method name might change between versions, just type #Override into the class and see what it suggests for you. In my case, I found zzD and zzE, and by trying them out I noticed that zzE has the data I wanted. zzD had some wakelock-stuff in its extras. The return value of zzE basically indicates whether the notification was handled, so if you return true, the OS won't show the notification. I prefer to return super.zzE(intent) after I've done my things, to let the OS handle the notification.
Updating the badge will only work on launchers supported by ShortcutBadger though. You can do pretty much whatever you want instead, though.
Here's my code (my notifications contain "badge" in the data payload):
public class PushNotificationService extends FirebaseMessagingService
{
#Override
public void onMessageReceived(RemoteMessage remoteMessage)
{
// This is called when the app is in the foreground
// Show a custom notification or send a broadcast to update the UI here
}
#Override
public boolean zzE(Intent intent)
{
if(intent.hasExtra("badge"))
{
try
{
ShortcutBadger.applyCount(getApplicationContext(), Integer.parseInt(intent.getStringExtra("badge"));
}
catch (Exception e)
{
Log.e("failedToParse", "Badge!?");
}
}
// pass the intent through to the non-overriden zzE
// to show the default notification.
return super.zzE(intent);
// You could also show a custom notification here
// and return true instead of this if you
// don't want the default notifications.
}
}