Notification that brings to activity - android

I'm doing stuff by clicking the notification appeared which can allow to straightly access to the activity that I needed(in this case I want to access to RealtimeData.java activity).
Btw, is that possible to display the notification instead of opening the activity for showing up the notification (the notification seems not to pop-out if I didn't enter to the specific activity. Notification pop-out if and only if I access to the RealtimeData activity) Thank a lot.
public class RealtimeData extends AppCompatActivity {
private DatabaseReference mDatebase;
private TextView mTempView; #Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_realtime_data);
Toolbar toolbar = (Toolbar) findViewById(R.id.app_bar);
setSupportActionBar(toolbar);
mAuth = FirebaseAuth.getInstance();
buttonDelete = (Button) findViewById(R.id.deletebutton)
mDatebase = FirebaseDatabase.getInstance().getReference().child("Region 1").child("Parameter Reading");
mTempView = (TextView) findViewById(R.id.tempvalue);
mDatebase.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
#Override
public void onDataChange(com.google.firebase.database.DataSnapshot dataSnapshot) {
for (com.google.firebase.database.DataSnapshot datasnap: dataSnapshot.getChildren()){
String temp = datasnap.child("airtemperature").getValue(String.class);
if (Float.valueOf(temp) >= 24 && Float.valueOf(temp) <= 30) {
mTempView.setText("Air Temperature: " + temp + " *C" + " Normal");
}
else if (Float.valueOf(temp) < 24)
{
mTempView.setText("Air Temperature: " + temp + " *C" + " Abnormal Low ");
NotificationCompat.Builder wbuilder = new NotificationCompat.Builder(RealtimeData.this);
wbuilder.setSmallIcon(R.drawable.impressive);
wbuilder.setContentTitle("Notification Alert");
wbuilder.setContentText("Air Temperature lowly abnormal!!");
wbuilder.setWhen(System.currentTimeMillis());
wbuilder.setVibrate(new long[]{1000,1000,1000,1000,1000});
wbuilder.setLights(Color.CYAN,3000,3000);
wbuilder.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, wbuilder.build());
}
else if (Float.valueOf(temp) > 30)
{
mTempView.setText("Air Temperature: " + temp + " *C" + " Abnormal High ");
NotificationCompat.Builder wbuilder = new NotificationCompat.Builder(RealtimeData.this);
wbuilder.setSmallIcon(R.drawable.impressive);
wbuilder.setContentTitle("Notification Alert");
wbuilder.setContentText("Air temperature highly abnormal!!");
wbuilder.setWhen(System.currentTimeMillis());
wbuilder.setVibrate(new long[]{1000,1000,1000,1000,1000});
wbuilder.setLights(Color.CYAN,3000,3000);
wbuilder.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, wbuilder.build());
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
mTempView.setText("Air Temperature: Error");
}
});;

You are launching the notification from inside the activity so obviously it will only show when using the RealtimeData activity.
If you want a notification to show without any activity open, you will have to send one yourself.
Notifications can be sent using a number of services like
Parse (you have to manage your own server now)
Firebase Notifications ( Send notifications both downstream and upstream)
OneSignal
PubNub
These are the ones that i know, might be others if you google search.
Using any of these, you will have to post a notification to be delivered to the device you want from your own server or via a management console these services provide.
I am currently using Firebase Services to send notifications to devices. All of the services above have a pretty good documentation regarding how to send notifications to apps :)

Related

Firebase and notification issues

My problem with the database in Firebase, I developed a chat app and it worked, now I tried to add the notification feature.
My code says when the user press the send button, inside the on click check the status of the other user (there is a table of the users status in database), if it is offline, add in database a notification table with receiver id a notification.
On create method I checked if there is inside of the notification table an id with same current user id, show the notification.
The problem is the first run the app is great and it shows the notification after I press send and the other user is offline without obstacles, but after that i remove the notification from database to test again, even if I press the back button to get back to previous activity the notification is added to the database, even if I moved to another app, it will add the notification to the database. I could not find what the problem is.
inside on create method the checking:
onlineOfflineUserStatus = "online";
databaseStatus = FirebaseDatabase.getInstance().getReferenceFromUrl("https://chatim-2ed18.firebaseio.com/status");
databaseStatus.child(subString).setValue(onlineOfflineUserStatus);
databaseNotification = FirebaseDatabase.getInstance().getReferenceFromUrl("https://chatim-2ed18.firebaseio.com/Notification");
databaseNotification.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if ( dataSnapshot.child(subString).exists()){
Intent intent = new Intent(getApplicationContext() , messageActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext() , 1 , intent , 0 );
Notification notification = new Notification.Builder(getApplicationContext())
.setContentTitle("New Message")
.setContentText("please press here")
.setContentIntent(pendingIntent)
.addAction(android.R.drawable.sym_action_chat, "Chat", pendingIntent)
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.build();
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, notification);
}else {
Log.i("the notification", " is not here ");
System.out.println("the notification is not here ...ooooo");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
inside on click the fab button:
databaseStatus.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.child(subStringGetName).getValue().equals("offline")) {
addNotification();
} else {
Log.i("the status ", " is.. online");
System.out.println("the status is ... online");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
});
add notification method:
public void addNotification(){
databaseNotification.child(subStringGetName).child("From").setValue(subString);
databaseNotification.child(subStringGetName).child("To").setValue(subStringGetName);
databaseNotification.child(subStringGetName).child("Body").setValue("Hellooo");
}

How to handle notifications with FCM when app is in either foreground or background

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());
}
}

onMessageSent of FirebaseMessagingService is not called accordingly

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.

FCM - Setting badge in onMessageReceived

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.
}
}

Failed to send upstream message using XMPP protocols in fcm

Sir I am trying to send upstream message from my android phone but failed to do so.Don't know where i am wrong.
Here is my code:
public class MainActivity extends AppCompatActivity {
Button button;
private AtomicInteger msgId;
FirebaseMessaging fm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FirebaseMessaging.getInstance().subscribeToTopic("hello");
FirebaseInstanceId.getInstance().getToken();
msgId = new AtomicInteger();
button = (Button) findViewById(R.id.click);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(),""+msgId,Toast.LENGTH_SHORT).show();
fm = FirebaseMessaging.getInstance();
RemoteMessage message=new RemoteMessage.Builder("<my sender id>#gcm.googleapis.com")
.setMessageId(Integer.toString(msgId.incrementAndGet()))
.addData("my_message", "Hello World")
.addData("my_action", "SAY_HELLO")
.build();
fm.send(message);
}
});
}
}
I had implemented onMessageSent() and onSendError() as according to docs but these methods were never called.Here is my messaging service class:
public class MyFirebaseMessagingService extends FirebaseMessagingService {
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Log.d("msg", "onMessageReceived: " + remoteMessage.getData().get("message"));
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("test")
.setContentText(remoteMessage.getData().get("message"));
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.notify(0, builder.build());
}
#Override
public void onMessageSent(String s) {
super.onMessageSent(s);
Log.d("fcm", "onMessageSent: message sent");
Toast.makeText(getApplicationContext(), "message:" + s, Toast.LENGTH_SHORT).show();
}
#Override
public void onSendError(String s, Exception e) {
super.onSendError(s, e);
Log.d("fcm", "onSendError: erroe");
Toast.makeText(getApplicationContext(), "error:" + s, Toast.LENGTH_SHORT).show();
}
}
i am trying this from 1 week still don't know where I am wrong.Also there is nothing in logcat.please help.
You have to set TTL at your message, For Example
RemoteMessage message=new RemoteMessage.Builder("<my sender id>#gcm.googleapis.com")
.setMessageId(Integer.toString(msgId.incrementAndGet()))
.addData("my_message", "Hello World")
.addData("my_action", "SAY_HELLO")
.setTtl(86400)
.build();
setTtl(86400) this line is important ,hope it will helps.
In an effort to optimize device resources especially battery the callbacks to onMessageSent and onSendError are batched so you may not receive a callback till you send around 10 or so upstream messages.
See the docs for more.
You need to have an app server running some xmpp server to talk to the Firebase Messaging server.
Be sure to set Time-to-Live on your message. For example, new RemoteMessage.Builder(...).setTtl(<time in sec>). I have not seen this behavior documented in the FirebaseMessagingService and/or Cloud Connection Server (CCS) references.

Categories

Resources