We are trying to implement a device to device notification system in firebase, but we experience a strange phenomena where we sometimes receive notifications and sometimes we do not. unfortunately, we could not identify what's the difference between the occasion.
We do however saw in the firebase documentation that we need to implement a service listener for the device token change(and add it to the manifest), and we think that maybe the problem is that we do not inform the server with that change.
The firebase functions code:
/*
* Functions SDK : is required to work with firebase functions.
* Admin SDK : is required to send Notification using functions.
*/
'use strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
/*
* 'OnWrite' works as 'addValueEventListener' for android. It will fire the function
* everytime there is some item added, removed or changed from the provided 'database.ref'
* 'sendNotification' is the name of the function, which can be changed according to
* your requirement
*/
exports.sendNotification = functions.database.ref('/notifications/{user_id}/{notification_id}').onWrite(event => {
/*
* You can store values as variables from the 'database.ref'
* Just like here, I've done for 'user_id' and 'notification'
*/
const user_id = event.params.user_id;
const notification_id = event.params.notification_id;
console.log('We have a notification from : ', user_id);
/*
* Stops proceeding to the rest of the function if the entry is deleted from database.
* If you want to work with what should happen when an entry is deleted, you can replace the
* line from "return console.log.... "
*/
if(!event.data.val()){
return console.log('A Notification has been deleted from the database : ', notification_id);
}
/*
* 'fromUser' query retreives the ID of the user who sent the notification
*/
const fromUser = admin.database().ref(`/notifications/${user_id}/${notification_id}`).once('value');
return fromUser.then(fromUserResult => {
const from_user_id = fromUserResult.val().from;
console.log('You have new notification from : ', from_user_id);
/*
* The we run two queries at a time using Firebase 'Promise'.
* One to get the name of the user who sent the notification
* another one to get the devicetoken to the device we want to send notification to
*/
const userQuery = admin.database().ref(`Users/${from_user_id}/name`).once('value');
const deviceToken = admin.database().ref(`/Users/${user_id}/device_token`).once('value');
return Promise.all([userQuery, deviceToken]).then(result => {
const userName = result[0].val();
const token_id = result[1].val();
/*
* We are creating a 'payload' to create a notification to be sent.
*/
const payload = {
notification: {
title : "New Friend Request",
body: `${userName} has sent you request`,
icon: "default",
click_action : "in.tvac.akshaye.lapitchat_TARGET_NOTIFICATION"
},
data : {
from_user_id : from_user_id
}
};
/*
* Then using admin.messaging() we are sending the payload notification to the token_id of
* the device we retreived.
*/
return admin.messaging().sendToDevice(token_id, payload).then(response => {
console.log('This was the notification Feature');
});
});
});
});
The send notification function
private void sendNotificationToCostumer() {
final HashMap<String, String> notificationData = new HashMap<>();
notificationData.put("from", currentUser.getUid());
notificationData.put("type", "bid request");
FirebaseDatabase.getInstance().getReference().child("notifications").child(curJobInflated.second.getPersonUid())
.push().setValue(notificationData).
addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Toast.makeText(MapActivity.this, "sending notification to the costumer",
Toast.LENGTH_SHORT).show();
}
});
}
The firebase instance service (It is a copied code, and probably essential code here is missing
package com.rubz.dvir.rubz;
import android.util.Log;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
import com.google.firebase.messaging.FirebaseMessaging;
public class MyFirebaseInstanceIdService extends FirebaseInstanceIdService {
private static final String TAG = "MyFirebaseIIDService";
private static final String FRIENDLY_ENGAGE_TOPIC = "friendly_engage";
/**
* The Application's current Instance ID token is no longer valid and thus a new one must be requested.
*/
@Override
public void onTokenRefresh() {
// If you need to handle the generation of a token, initially or after a refresh this is
// where you should do that.
String token = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "FCM Token: " + token);
}
}
Firebase messaging service implementation
package com.rubz.dvir.rubz;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.widget.Toast;
import com.google.firebase.messaging.RemoteMessage;
public class FirebaseMessagingService extends com.google.firebase.messaging.FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
String messageTitle = remoteMessage.getNotification().getTitle();
String messageBody = remoteMessage.getNotification().getBody();
//get the click action
String click_action = remoteMessage.getNotification().getClickAction();
String dataMessage = remoteMessage.getData().get("message");
//String dataFrom = remoteMessage.getData().get("from_id");
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, getString(R.string.default_notification_channel_id))
.setSmallIcon(R.drawable.menu_icon)
.setContentTitle("new notification")
.setContentText("Hi your bid has accepted")
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
int mNotificationId = (int)System.currentTimeMillis();
NotificationManager mNotifyMgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
Intent resultIntent = new Intent(click_action);
resultIntent.putExtra("message", dataMessage);
//resultIntent.putExtra("dataFrom", dataFrom);
PendingIntent resultPendingIntent = PendingIntent.getActivity(this,0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
mNotifyMgr.notify(mNotificationId, mBuilder.build());
}
}
The relevant service declarations appear in the manifest.
update: when testing in debug mode we receive the notification in the "received" device, but somehow the notification doesn't inflate.
update2: the problem wasn't related to firebase at all, but to the notification usage in SDK>26
see Notification Not showing