I am using the notification payload below and use Postman to send a push notifications to Android devices:
{
"to" : "/topics/xxxx" ,
"data" : {
"url" : "https://res.cloudinary.com/demo/image/upload/w_200/lady.jpg",
"dl" : ""
},
"notification" : {
"title" : "This is a sample notification from general2",
"body" : "Rich notification testing (body)",
"image": "https://res.cloudinary.com/demo/image/upload/w_200/lady.jpg"
}
}
I have used the image key value pair to include an image in the push notification. My expected output is:
But this is what it is showing in the phone:
As you can see, the image is not appearing. What might be the problem?
I found out the problem is in aggressive battery savers. For example, Xiaomi and Samsung (Android 11 especially) phones will restrict FCM to do background networking (to fetch the image), thus, the image will be missing. This will only happen when phone is in deep sleep. If it's not (and your app is closed or in background), the image will be showing. If the app is in foreground, onMessageReceived() will be called and you need to fetch the image manually anyways and it will not be restricted by phone.
Sadly, I didn't find any workaround around this, except users manually disabling battery saving for your app :/
In-short
Uploading Image to a popular image hosting website and putting that link for notification image url worked for me. Also check image size i used (1000 × 500 px)
Expaned
I was also facing the same problem but creating MyFirebaseMessagingService did not help.
I was trying to load image from own hosting like (https://www.example.com/image/img.png).
Although it was https and firebase console was showing the image but on actual device it was not displayed.
For me uploading image to imggur.com or putting it in firbase storage and using that link for notification worked without any extra messaging service code.
This code might help
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMessagingServ";
Target target = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
sendNotification(bitmap);
}
#Override
public void onBitmapFailed(Exception e, Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
if(remoteMessage.getData()!=null)
getImage(remoteMessage);
}
private void sendNotification(Bitmap bitmap){
NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle();
style.bigPicture(bitmap);
Uri defaultSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent,0);
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
String NOTIFICATION_CHANNEL_ID = "101";
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
#SuppressLint("WrongConstant") NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "Notification", NotificationManager.IMPORTANCE_MAX);
//Configure Notification Channel
notificationChannel.setDescription("Game Notifications");
notificationChannel.enableLights(true);
notificationChannel.setVibrationPattern(new long[]{0, 1000, 500, 1000});
notificationChannel.enableVibration(true);
notificationManager.createNotificationChannel(notificationChannel);
}
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentTitle(Config.title)
.setAutoCancel(true)
.setSound(defaultSound)
.setContentText(Config.content)
.setContentIntent(pendingIntent)
.setStyle(style)
.setLargeIcon(bitmap)
.setWhen(System.currentTimeMillis())
.setPriority(Notification.PRIORITY_MAX);
notificationManager.notify(1, notificationBuilder.build());
}
private void getImage(final RemoteMessage remoteMessage) {
Map<String, String> data = remoteMessage.getData();
Config.title = data.get("title");
Config.content = data.get("content");
Config.imageUrl = data.get("imageUrl");
Config.gameUrl = data.get("gameUrl");
//Create thread to fetch image from notification
if(remoteMessage.getData()!=null){
Handler uiHandler = new Handler(Looper.getMainLooper());
uiHandler.post(new Runnable() {
#Override
public void run() {
// Get image from data Notification
Picasso.get()
.load(Config.imageUrl)
.into(target);
}
}) ;
}
}
}
you have to handle the reception of the image on your own,like said in the last line here of the official documentation:
With notification set as shown, this send request enables the receiving client to handle the image delivered in the payload.
Firesebase official documentation
2021 Working Solution:
override fun onMessageReceived(remoteMessage: RemoteMessage)
remoteMessage.notification?.let {
sendNotification(it.title!!, it.body!!, it.imageUrl) //image url in uri type
}
}
private fun sendNotification(title: String, message: String, imageUri: Uri?) {
...
val imageBitmap = imageUri?.let {
Glide.with(requireContext()).asBitmap().load(it).submit().get()
//you pick one, Glide or Picasso
Picasso.get().load(it).get()
}
...
}
Check your image size, mostly if you prefer to use an image less than or equal to 1 mb, mostly your issue will get resolved, I tried it and works in Xiaomi device.
Related
I am using Azure Hubs to send notifications. I am able to receive the notification and it displays on the device when I pull down the notifications window. However, I do not see the notification display at the top of the screen as expected. I even "locked" the screen and it didn't display. I got the notification sound and my logs show I received it.
Screen showing received notification
My FirebaseMessageService:
using System;
using System.Linq;
using WindowsAzure.Messaging;
using Android.App;
using Android.Content;
using Android.Support.V4.App;
using Android.Util;
using Firebase.Messaging;
using IDEQ.AQI.Pages;
namespace IDEQ.AQI.Droid
{
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class FirebaseService : FirebaseMessagingService
{
const string Tag = "FirebaseMsgService";
public override void OnNewToken(string token)
{
// NOTE: save token instance locally, or log if desired
SendRegistrationToServer(token);
}
private void SendRegistrationToServer(string token)
{
try
{
var hub = new NotificationHub(Constants.NotificationHubName, Constants.ListenConnectionString, this);
// register device with Azure Notification Hub using the token from FCM
var registration = hub.Register(token, Constants.SubscriptionTags);
// subscribe to the SubscriptionTags list with a simple template.
var pnsHandle = registration.PNSHandle;
var templateReg = hub.RegisterTemplate(pnsHandle, "defaultTemplate", Constants.FCMTemplateBody, Constants.SubscriptionTags);
}
catch (Exception e)
{
Log.Error(Constants.DebugTag, $"Error registering device: {e.Message}");
}
}
public override void OnMessageReceived(RemoteMessage message)
{
base.OnMessageReceived(message);
string messageBody;
Log.Info(Tag, "From: " + message.From);
if (message.GetNotification() != null)
{
Log.Info(Tag, "Notification Message Body: " + message.GetNotification().Body);
messageBody = message.GetNotification().Body;
}
// NOTE: test messages sent via the Azure portal will be received here
else
{
messageBody = message.Data.Values.First();
}
// convert the incoming message to a local notification
SendLocalNotification(messageBody);
// send the incoming message directly to the MainPage
SendMessageToMainPage(messageBody);
}
private void SendLocalNotification(string body)
{
try
{
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
intent.PutExtra("message", body);
var requestCode = new Random().Next();
var pendingIntent = PendingIntent.GetActivity(this, requestCode, intent, PendingIntentFlags.OneShot);
var notificationBuilder = new NotificationCompat.Builder(this, Constants.NotificationChannelId)
.SetContentTitle("IDEQ Alert")
.SetSmallIcon(Resource.Drawable.ic_launcher)
.SetContentText(body)
.SetAutoCancel(true)
.SetShowWhen(false)
.SetContentIntent(pendingIntent);
var notificationManager = NotificationManagerCompat.From(this);
notificationManager.Notify(0, notificationBuilder.Build());
}
catch (Exception e)
{
Log.Error(Tag, e.ToString());
}
}
private void SendMessageToMainPage(string body)
{
(Xamarin.Forms.Application.Current.MainPage as MainPage)?.AddMessage(body);
}
}
}
//My main activity where I create the channel:
private void CreateNotificationChannel()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
{
// Notification channels are new in API 26 (and not a part of the
// support library). There is no need to create a notification
// channel on older versions of Android.
return;
}
var channel = new NotificationChannel(Constants.NotificationChannelId, Constants.NotificationChannelName, NotificationImportance.Default)
{
Description = string.Empty
};
var notificationManager = (NotificationManager) GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(channel);
}
Notification in your system tray will only be displayed if application is in background or turned off. If your application is running, your OnMessageRecieved method will get hit, however Android will not display notification in the system try. This is how life cycle of push notification works in Android.
The only way u can display notification in system tray when application in in foreground is when you force Local Notification like you did in SendLocalNotification method.
I used firebase to build My project.
It will also use the FCM (firebase cloud message).
But there is a problem. I can't handle the FCM (create my custom notificaion) when app is in background.
The official site tutorial said that
case 1: App foreground -> override the "onMessageReceived()" to create your custom notification.
case 2: App background -> System will create the notification directly. We needn't and can't do anything. Because it doesn't trigger the "onMessageReceived()" in this case.
However if I can do nothing when app is background, I can't create my custom notification. (e.g. After Users click the notification and it will pop up a window to show detail information.)
So how do I handle notifications with FCM when app is in background?
There is a bad news. Google change the Firebase source code in version 'com.google.firebase:firebase-messaging:11.6.0'.
handelIntent is "public final void method" now. which means we can't override it .
If you want to use the solution, change the version to be "com.google.firebase:firebase-messaging:11.4.2"
Try my way. It can perfectly work on the project build version is Android 6.0 above(api level 23) and I have tried it already.
There is better way than official site tutorial
The official site said that the notification will be created by system when app is in background. So you can't handle it by overriding the "onMessageReceived()". Because the "onMessageReceived()" is only triggered when app is in foreground.
But the truth is not. Actually the notificaions (when app is in background) are created by Firebase Library.
After I traced the firebase library code. I find a better way.
Step 1. Override the "handleIntent()" instead of "onMessageReceived()" in FirebaseMessagingService
why:
Because the method will be trigger either app is in foreground or the background. So we can handle FCM message and create our custom notifications in both cases.
#Override
public void handleIntent(Intent intent) {
Log.d( "FCM", "handleIntent ");
}
Step 2. Parse the message from FCM
how:
If you don't know the format of the message you set. Print it and try to parse it.
Here is the basic illustration
Bundle bundle = intent.getExtras();
if (bundle != null) {
for (String key : bundle.keySet()) {
Object value = bundle.get(key);
Log.d("FCM", "Key: " + key + " Value: " + value);
}
}
Step 2. Remove the notifications created by Firebase library when the app is in background
why:
We can create our custom notification. But the notification created by Firebase Library will still be there (Actually it created by ""super.handleIntent(intent)"". There is detail explaination below.). Then we'll have two notifcations. That is rather weird. So we have to remove the notificaion created by Firebase Library
how (project build level is Android 6.0 above):
Recognize the notifications which we want to remove and get the informaion. And use the "notificationManager.cancel()" to remove them.
private void removeFirebaseOrigianlNotificaitons() {
//check notificationManager is available
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager == null )
return;
//check api level for getActiveNotifications()
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
//if your Build version is less than android 6.0
//we can remove all notifications instead.
//notificationManager.cancelAll();
return;
}
//check there are notifications
StatusBarNotification[] activeNotifications =
notificationManager.getActiveNotifications();
if (activeNotifications == null)
return;
//remove all notification created by library(super.handleIntent(intent))
for (StatusBarNotification tmp : activeNotifications) {
Log.d("FCM StatusBarNotification",
"StatusBarNotification tag/id: " + tmp.getTag() + " / " + tmp.getId());
String tag = tmp.getTag();
int id = tmp.getId();
//trace the library source code, follow the rule to remove it.
if (tag != null && tag.contains("FCM-Notification"))
notificationManager.cancel(tag, id);
}
}
The my whole sample code:
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static int notificationCount=0;
#Override
public void handleIntent(Intent intent) {
//add a log, and you'll see the method will be triggered all the time (both foreground and background).
Log.d( "FCM", "handleIntent");
//if you don't know the format of your FCM message,
//just print it out, and you'll know how to parse it
Bundle bundle = intent.getExtras();
if (bundle != null) {
for (String key : bundle.keySet()) {
Object value = bundle.get(key);
Log.d("FCM", "Key: " + key + " Value: " + value);
}
}
//the background notification is created by super method
//but you can't remove the super method.
//the super method do other things, not just creating the notification
super.handleIntent(intent);
//remove the Notificaitons
removeFirebaseOrigianlNotificaitons();
if (bundle ==null)
return;
//pares the message
CloudMsg cloudMsg = parseCloudMsg(bundle);
//if you want take the data to Activity, set it
Bundle myBundle = new Bundle();
myBundle.putSerializable(TYPE_FCM_PLATFORM, cloudMsg);
Intent myIntent = new Intent(this, NotificationActivity.class);
myIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
myIntent.putExtras(myBundle);
PendingIntent pendingIntent = PendingIntent.getActivity(this, notificationCount, myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//set the Notification
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.icon)
.setContentTitle(cloudMsg.getTitle())
.setContentText(cloudMsg.getMessage())
.setAutoCancel(true)
.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(notificationCount++, notificationBuilder.build());
}
/**
* parse the message which is from FCM
* #param bundle
*/
private CloudMsg parseCloudMsg(Bundle bundle) {
String title = null, msg=null;
//if the message is sent from Firebase platform, the key will be that
msg = (String) bundle.get("gcm.notification.body");
if(bundle.containsKey("gcm.notification.title"))
title = (String) bundle.get("gcm.notification.title");
//parse your custom message
String testValue=null;
testValue = (String) bundle.get("testKey");
//package them into a object(CloudMsg is your own structure), it is easy to send to Activity.
CloudMsg cloudMsg = new CloudMsg(title, msg, testValue);
return cloudMsg;
}
/**
* remove the notification created by "super.handleIntent(intent)"
*/
private void removeFirebaseOrigianlNotificaitons() {
//check notificationManager is available
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager == null )
return;
//check api level for getActiveNotifications()
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
//if your Build version is less than android 6.0
//we can remove all notifications instead.
//notificationManager.cancelAll();
return;
}
//check there are notifications
StatusBarNotification[] activeNotifications =
notificationManager.getActiveNotifications();
if (activeNotifications == null)
return;
//remove all notification created by library(super.handleIntent(intent))
for (StatusBarNotification tmp : activeNotifications) {
Log.d("FCM StatusBarNotification",
"tag/id: " + tmp.getTag() + " / " + tmp.getId());
String tag = tmp.getTag();
int id = tmp.getId();
//trace the library source code, follow the rule to remove it.
if (tag != null && tag.contains("FCM-Notification"))
notificationManager.cancel(tag, id);
}
}
}
However if I can do nothing when app is background, I can't create my custom notification. (e.g. After Users click the notification and it will pop up a window to show detail information.)
So how do I handle notifications with FCM when app is in background?
First, you need to create correct message payload that you send to fcm server. Example:
{
"to": "topic_name",
"priority": "high",
"data": {
"field1": "field1 value"
"field2": "field2 value"
}
"notification" : {
"body" : "Lorem ipsum",
"title" : "sampke title"
"click_action": "SHOW_DETAILS"
}
}
data payload is actual data you want to show as message details after user clicks on notification, notification payload represents how generated notification should look (there are much more attributes possible to set), you don't need to build notification by yourself, you only need to set it properties here.
To show your activity after user taps on notication, you need to set intent filter corresponding to click_action:
<intent-filter>
<action android:name="SHOW_DETAILS"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
so activity that have above intent filter will be launched automatically when user taps to notification.
Last step is to retrieve data when activity is launched after notification tap. It's pretty easy. Custom data is passed to activity via bundle. Inside onCreate method for your activity do something like that:
Bundle bundle = getIntent().getExtras();
if(bundle.getString("action").equals("SHOW_DETAILS")) /*This indicates activity is launched from notification, not directly*/
{
//Data retrieved from notification payload send
String filed1 = bundle.getString("field1");
String filed2 = bundle.getString("field2");
}
All of above is valid if app is not running or it's in background. If your app is foreground, no notification will be created. Instead, you will receive onMessageReceived() event so you can handle the same data there (I guess you know how).
Reference:
https://firebase.google.com/docs/cloud-messaging/http-server-ref
https://github.com/firebase/quickstart-android/tree/master/messaging
You need to use FCM data messages in order to create custom notification in a android app.Even your app is in background, onMessageReceived will be called, so you can process the data and show the custom notification.
https://firebase.google.com/docs/cloud-messaging/android/receive
Data message format which has to be sent from server:
{"message":{
"token":"Your Device Token",
"data":{
"Nick" : "Mario",
"body" : "great match!",
"Room" : "PortugalVSDenmark"
}
}
}
FCM Won't send a background notification if your app is killed any more, and as you described in your answer about the handleIntent() solution It may work for some devices and for some old version of the FCM, also if you #override method that doesn't described in the official doc's of firebase you may struggle some problems here, and you use it on your own risk!.
What is the solution?
You need to use your own push-notification-service beside FCM like Telegram.
OR using SyncAdapter beside GCM like Gmail.
So if you need it to work successfully like those apps, you have to use your own hack.
public class FirebaseMessageReceiver extends FirebaseMessagingService{
private static final String TAG = "main";
String s12;
String channel_id = "general";
Intent intent;
#Override
public void onNewToken(#NonNull String token)
{
Log.d(TAG, "Refreshed token: " + token);
}
#Override
public void
onMessageReceived(RemoteMessage remoteMessage) {
s12=remoteMessage.getNotification().getClickAction();
Log.d("tttt",(remoteMessage.getData().toString()));
Log.d("ttttttt",(remoteMessage.getNotification().toString()));
if (remoteMessage.getNotification() != null) {
showNotification(remoteMessage.getNotification().getTitle(), remoteMessage.getNotification().getBody());
}
//
}
public void handleIntent(Intent intent)
{
try
{
if (intent.getExtras() != null)
{
RemoteMessage.Builder builder = new RemoteMessage.Builder("FirebaseMessageReceiver");
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);
}
}
private RemoteViews getCustomDesign(String title, String message) {
RemoteViews remoteViews = new RemoteViews(getApplicationContext().getPackageName(), R.layout.notification);
remoteViews.setTextViewText(R.id.title111, title);
remoteViews.setTextViewText(R.id.message111, message);
remoteViews.setImageViewResource(R.id.icon111, R.drawable.favicon);
return remoteViews;
}
// Method to display the notifications
public void showNotification(String title, String message) {
intent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(s12));
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent notifyIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
Log.d("notifyy",notifyIntent.toString());
NotificationCompat.Builder builder
= new NotificationCompat
.Builder(getApplicationContext(),
channel_id)
.setSmallIcon(R.drawable.favicon)
.setAutoCancel(true)
.setVibrate(new long[]{1000, 1000, 1000, 1000, 1000})
.setOnlyAlertOnce(true)
.setContentIntent(notifyIntent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
builder = builder.setContent(getCustomDesign(title, message));
}
else {
builder = builder.setContentTitle(title)
.setContentText(message)
.setSmallIcon(R.drawable.favicon);
}
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Check if the Android Version is greater than Oreo
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(channel_id, "web_app",
NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(
notificationChannel);
}
notificationManager.notify(0, builder.build());
}
}
I'm having a problem using the FCM Push Notification Messaging Service, as I've overridden the handleIntent() method to receive the notification when the app is in the foreground. I am also using the onMessageReceived() method.
But when the app is in the background, I will receive 2 notifications, which one of them only opens up the app and runs the MainActivity while the other is opening up the app how I want it to.
FYI: The notification I receive when I am in the foreground is exactly how I want it to open.
This is the code I've written below :
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private final String NOTIFICATION_TYPE = "type";
private final String NOTIFICATION_ID = "id";
private final String NOTIFICATION_TYPE_PRODUCT_DETAIL = "productdetail";
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
String title = remoteMessage.getNotification().getTitle();R
String body = remoteMessage.getNotification().getBody();
String token = remoteMessage.getFrom();
Log.d("FireBase TAG: ", token);
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
Log.d("FireBaseMessageService","FireBase Data payload : " + remoteMessage.getData());
}
// Check if message contains a notification payload.
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
}
}
#Override
public void handleIntent(Intent intent) {
super.handleIntent(intent);
String type = intent.getExtras().getString(NOTIFICATION_TYPE, "");
int id = 0;
try {
id = Integer.valueOf(intent.getExtras().getString(NOTIFICATION_ID, ""));
} catch (NumberFormatException e) {
e.printStackTrace();
}
//Intents
Intent mainIntent = MainActivity.newIntent(this);
Intent editProfileIntent = EditProfileActivity.newIntent(this);
Intent settingsIntent = SettingsActivity.newIntent(this);
Intent productIntent = ProductActivity.newNotificationIntent(this, id, false, true);
if (UserManager.getSingleton().isUserLoggedIn(getApplicationContext())) {
PendingIntent pendingIntent;
if (type.equalsIgnoreCase(NOTIFICATION_TYPE_PRODUCT_DETAIL)) {
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(mainIntent);
stackBuilder.addNextIntent(productIntent);
editProfileIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_ONE_SHOT);
}
else {
pendingIntent = PendingIntent.getActivity(this, 0, productIntent,
PendingIntent.FLAG_ONE_SHOT);
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(intent.getExtras().getString("gcm.notification.title"))
.setContentText(intent.getExtras().getString("gcm.notification.body"))
.setDefaults(NotificationCompat.DEFAULT_VIBRATE)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setContentIntent(pendingIntent);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.notify(0, builder.build());
}
}
}
I have deleted the NotificationCompat.Builder from the onMessageReceived() method.
But I am still receiving two notifications in the background.
App Gradle :
compile 'com.google.firebase:firebase-core:11.4.2' //Firebase
compile 'com.google.firebase:firebase-messaging:11.4.2' //Firebase Cloud Messaging
I've tried searching for a solution online but unluckily there isn't a solution pointing to Android.
You are handling your Notification stuff into handleIntent(Intent intent). You should probably remove super.handleIntent(intent); to prevent the Firebase system to handle notification while the app is in background.
Solution: remove super.handleIntent(intent);
Just make a sendnotification() method and set whatever parameters you want to pass like body i.e sendnotification(String body). Use pending intent to start you activity and when you click on notification your app parse the data to the launcher activity which is defined in manifest, so once you have data in your launcher activity you can send data to other activity using intent.
I think the .setContentText("") is getting called more than 1 times and are you getting same notification two times?
The notification which works perfectly is generated by your code but when your application is not in foreground android system will generate the notification for you. In this case when you don't have the control to send data in your intent that you were sending to open your desired Activity.
In this case, you have to do some modification on your servers payload. You have to add click_action in your payload, this is how android system will identify the destination activity.
Payload Example:
{ "notification": {
"title":"Notification title",
"body":"Notification body",
"click_action":"<Your_destination_activity>",
}, "data":{
"param1":"value1",
"param2":"value2"
},
"priority":"high",
}
Reference:
https://firebase.google.com/docs/cloud-messaging/http-server-ref
yes,
When you app in background you will receive the push at system tray so system tray will create push with notification title and message.
and when you click on the push your initial launcher activity (which mentioned as launcher in manifest) will open.
you can get your notification data at you launcher activity (bundle).
private void handlePush() {
Intent intent = null;
if (bundle.getString("push_type") != null && bundle.getString("push_type").length() > 0) {
switch (bundle.getString("push_type")) {
case PUSH_TYPE_FOLLOW_USER: {
intent = new Intent(this, ProfileExternalActivity.class);
intent.putExtra(Constants.USER_ID, Integer.parseInt(bundle.getString("id")));
intent.putExtra(Constants.FROM_PUSH_NOTIFICATION_SPLASH, true);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
break;
}
}
if (intent != null)
startActivity(intent);
finish();
}
}
and you need to check activty have data or not
if (bundle != null)
handlePush();
else //your next activity
FYI : https://firebase.google.com/docs/cloud-messaging/android/receive
or
you can get payload object instead of data object inside notification , if you have payload object in your notification object, push all time received at your onMessageReceived().
for people still having this issue:
Here is a hack to prevent this behavior. I've looked all over and there seems to be minimal info about this, but if you save the actual message being sent in shared preferences and then do a check against that value in onRecieve, you can easily prevent this. The only downside is that your user can't send the exact same message two times in a row in the form of a notification (but that would be annoying anyway). example:
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
SharedPreferences Settings = getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = Settings.edit();
message = remoteMessage.getNotification().getBody();
from = remoteMessage.getFrom(); //this comes through as the topic, oddly...
if(from.equals("/topics/"+userID+deviceID+"all")) {
if(!message.equals(Settings.getString("messageall",null))) {//this filters any dupe messages
utils.postNotification(title, message, context, extra, "messages");//create notification
editor.putString("messageall", message);//always update to the last message
editor.commit();
}
}
}
my question is for I can't see the background in my Android Wear emulator, I declare .setBackgroung with a validated image but when I run the App and send me a notification the background does not show.
Any help?
public class NotificationService extends FirebaseMessagingService {
public static final String TAG = "FIREBASE";
public static final int NOTIFICATION_ID = 001;
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
//super.onMessageReceived(remoteMessage);
Log.d(TAG, "From: " + remoteMessage.getFrom());
Log.d(TAG, "Notification Message Body: " + remoteMessage.getNotification().getBody());
enviarNotificacion(remoteMessage);
}
public void enviarNotificacion(RemoteMessage remoteMessage){
Intent i = new Intent();
i.setAction("TOQUE_ANIMAL");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
Uri sonido = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Action action =
new NotificationCompat.Action.Builder(R.drawable.ic_full_poke,
getString(R.string.texto_accion_toque), pendingIntent)
.build();
NotificationCompat.WearableExtender wearableExtender =
new NotificationCompat.WearableExtender()
.setHintHideIcon(true)
.setBackground(BitmapFactory.decodeResource(getResources(),
R.drawable.bk_androidwear_notification))
.setGravity(Gravity.CENTER_VERTICAL);
NotificationCompat.Builder notificacion = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notificacion)
.setContentTitle("Notificacion")
.setContentText(remoteMessage.getNotification().getBody())
.setAutoCancel(true)
.setSound(sonido)
.setContentIntent(pendingIntent)
.extend(wearableExtender.addAction(action))
//.addAction(R.drawable.ic_full_poke, getString(R.string.texto_accion_toque), pendingIntent)
;
NotificationManagerCompat notificationManager =
NotificationManagerCompat.from(this);
notificationManager.notify(NOTIFICATION_ID, notificacion.build());
}
}
You may refer with this SO post. Try to use setBackground(Bitmap) method from WearableExtender instead of setLargeIcon(Bitmap). You may also check this tutorial on how to create a watchface for Android Wear.
Additional reference to set background image for Android Wear notification: Background image for android wear notification
The resolution for square watch faces as of right now is 280x280, whereas the circular watch face has a resolution of 320x320 (although some of this will be cut off, obviously).
However, Android Wear implements a sort of parallax scrolling by default for larger images, so it can handle larger images with great ease, and it is even recommended that you do so (see this previous answer). So you really shouldn't worry about the size of the image, unless you were trying to create a static, full-screen image with specific areas of interaction as a substitute for custom UI elements (which you can do, I might add, although Google recommends you have only one area of interaction per screen on the watch to keep things simple).
If you want to see a good example of multiple images being used for backgrounds with parallax scrolling, I recommend you check out the GridViewPager sample project, which can be found in the SDK samples in the wearable subfolder for API level 20.
Hope this helps!
I am trying to use FCM to send UpStream Message, so I followed the tutorial on google and it works.
As shown in the code below in MainActivity, I send Upstream message when the button is clicked, then in MyAndroidFirebaseMsgService I should see a Log message as shown
below in MyAndroidFirebaseMsgService.
But what happen is, the Log messages in MyAndroidFirebaseMsgService in onMessageSent in do not get displayed even I kept pressing the button several times.
the Log message in MyAndroidFirebaseMsgService in onMessageSent can be displayed only if sent a downstream messagefrom FCM to the App, in this case, both the Logs in
in MyAndroidFirebaseMsgService will be displayed.
Please let me know why the Log message in onMessageSent is not getting displayed once there is an UpStream message sent?and how to fix it.
Mainactivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnSendUpstreamMsg = (Button) findViewById(R.id.btn_send_upstream_message);
mBtnSendUpstreamMsg.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FirebaseMessaging fm = FirebaseMessaging.getInstance();
fm.send(new RemoteMessage.Builder("673xxxxx" + "#gcm.googleapis.com")
.setMessageId("2")
.addData("my_message", "Hello World")
.addData("my_action","SAY_HELLO")
.build());
}
});
}
MyAndroidFirebaseMsgService:
public class MyAndroidFirebaseMsgService extends FirebaseMessagingService {
private final static String TAG = MyAndroidFirebaseMsgService.class.getSimpleName();
#Override
public void onMessageSent(String s) {
super.onMessageSent(s);
Log.d(TAG, "onMessageSent: upstream message");
}
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, "onMessageReceived: downstream message");
//Log data to Log Cat
Log.d(TAG, "onMessageReceived->From: " + remoteMessage.getFrom());
Log.d(TAG, "onMessageReceived->Notification Message Body: " + remoteMessage.getNotification().getBody());
//create notification
createNotification(remoteMessage.getNotification().getBody());
}
private void createNotification( String messageBody) {
Intent intent = new Intent( this , ResultActivity.class );
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent resultIntent = PendingIntent.getActivity( this , 0, intent,
PendingIntent.FLAG_ONE_SHOT);
Uri notificationSoundURI = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder mNotificationBuilder = new NotificationCompat.Builder( this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Android Tutorial Point FCM Tutorial")
.setContentText(messageBody)
.setAutoCancel( true )
.setSound(notificationSoundURI)
.setContentIntent(resultIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, mNotificationBuilder.build());
}
}
Yes, is possible to send a Firebase messaging push notification and receive it in all app life cycles using onMessageReceived.
But is necessary to change the default Firebase behaviour, intercepting the intent request before everything else.
** IMPORTANT NOTE **
This was a pretty stupid idea from Firebase by remove the developers processment capability when the FCM message arives with the notification message format, but not for data message.
This created a bunch of "workarounds" in many solutions, which made the analythics and everything else being messed up.
If I had designed this solution, I would always call the onMessageReceived method with a completion handle. Let the developer decide what to do (free tip for you, Firebase).
Use onMessageReceived is the correct way to do. This method is the only one who brings RemoteMessage object, that have every information what you need. It was designed for it. You are on correct path.
** HOW TO DO **
In your Firebase Class MyAndroidFirebaseMsgService, which extends FirebaseMessagingService, override the public method handleIntent to intercep the intent request before Firebase catch it.
#Override
public void handleIntent(Intent intent){
if(intent.hasExtra("google.message_id")){
intent = handleFirebaseIntent(intent);
}
super.handleIntent(intent);
}
After, transform the notification message package into an data message, removing all "gcm.notification.%" and "gcm.n.%" extras from intent, and translating "gcm.notification.title", "gcm.notification.body" and "gcm.notification.image" elements into what you need:
// Thank you Google, for that brilliant idea to treat notification message and notification data
// differently on Android, depending of what app life cycle is. Because of that, all the developers
// are doing "workarounds", using data to send push notifications, and that's not what you planned for.
// Let the developers decide what to do on their apps and ALWAYS deliver the notification
// to "onMessageReceived" method. Its simple, is freedom and its what the creative ones need.
private Intent handleFirebaseIntent(Intent intent){
//printIntentExtras(intent);
String FCM_TITLE_KEY = "gcm.notification.title";
String FCM_BODY_KEY = "gcm.notification.body";
String FCM_IMAGE_KEY = "gcm.notification.image";
String title = intent.getStringExtra(FCM_TITLE_KEY);
String body = intent.getStringExtra(FCM_BODY_KEY);
String image = intent.getStringExtra(FCM_IMAGE_KEY);
// Remove the key extras that identifies an Notification type message
Bundle bundle = intent.getExtras();
if (bundle != null) {
for (String key : bundle.keySet()) {
if (key.startsWith("gcm.notification.") || key.startsWith("gcm.n."))
{
intent.removeExtra(key);
}
}
}
Boolean isTitleEmpty = StringUtils.isNullOrEmpty(title);
Boolean isBodyEmpty = StringUtils.isNullOrEmpty(body);
Boolean isImageEmpty = StringUtils.isNullOrEmpty(image);
// Notification title and body has prevalence over Data title and body
if(
!isTitleEmpty || !isBodyEmpty || !isImageEmpty
){
// This is my personalized translation method, designed for my solution.
// Probably you gonna need to do it by your own
String contentData = intent.getStringExtra(Definitions.PUSH_NOTIFICATION_CONTENT);
Map<String, Object> content;
if(StringUtils.isNullOrEmpty(contentData)){
content = new HashMap<String, Object>();
content.put(Definitions.NOTIFICATION_ID, new Random().nextInt(65536) - 32768);
content.put(Definitions.NOTIFICATION_CHANNEL_KEY, "basic_channel" );
} else {
content = JsonUtils.fromJson(new TypeToken<Map<String, Object>>(){}.getType(),contentData);
}
if(!isTitleEmpty) content.put(Definitions.NOTIFICATION_TITLE, title);
if(!isBodyEmpty) content.put(Definitions.NOTIFICATION_BODY, body);
if(!isImageEmpty){
content.put(Definitions.NOTIFICATION_BIG_PICTURE, image);
content.put(Definitions.NOTIFICATION_LAYOUT, NotificationLayout.BigPicture.toString());
}
contentData = JsonUtils.toJson(content);
intent.putExtra(Definitions.PUSH_NOTIFICATION_CONTENT, contentData);
}
//printIntentExtras(intent);
return intent;
}
private void printIntentExtras(Intent intent){
Bundle bundle;
if ((bundle = intent.getExtras()) != null) {
for (String key : bundle.keySet()) {
System.out.println(key + " : " + (bundle.get(key) != null ? bundle.get(key) : "NULL"));
}
}
}
You can check my entire solution here.