Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to push notification to client when Firebase has a new entry?

I wonder if it's possible to send push notifications to android mobile devices whenever Firebase gets added a new child on specific entity. For example, let's say there's an entity on Firebase called Tasks. Whenever a new task is added to that firebase collection the "child_added" event is fired and then, in some way, a push notification is sent to a mobile client. The trigger is the child_added event. However, I'm not sure if is feasible sending push notification right from Firebase events.

like image 913
blackjack Avatar asked Sep 23 '16 22:09

blackjack


2 Answers

You can make a very simple node.js server or a java servlet (based on your language preferences) then using firebase server sdk you can add childEventListener. When value changes you can use FCM to send push notifications using http protocol. I am using this in my app and it is very feasable and reliable.

Note: If you are using this flow for an android app then using java server sdk will be beneficial as it is almost similar to what you have on android.

EDIT: After getting some spotlight on this answer I thought to share some more info regarding same.

//example node.js server as seen on this official firebase blog

var firebase = require('firebase');
var request = require('request');

var API_KEY = "..."; // Your Firebase Cloud Server API key

firebase.initializeApp({
  serviceAccount: ".json",
  databaseURL: "https://.firebaseio.com/"
});
ref = firebase.database().ref();

function listenForNotificationRequests() {
  var requests = ref.child('notificationRequests');
  ref.on('child_added', function(requestSnapshot) {
    var request = requestSnapshot.val();
    sendNotificationToUser(
      request.username, 
      request.message,
      function() {
        request.ref().remove();
      }
    );
  }, function(error) {
    console.error(error);
  });
};

function sendNotificationToUser(username, message, onSuccess) {
  request({
    url: 'https://fcm.googleapis.com/fcm/send',
    method: 'POST',
    headers: {
      'Content-Type' :' application/json',
      'Authorization': 'key='+API_KEY
    },
    body: JSON.stringify({
      notification: {
        title: message
      },
      to : '/topics/user_'+username
    })
  }, function(error, response, body) {
    if (error) { console.error(error); }
    else if (response.statusCode >= 400) { 
      console.error('HTTP Error: '+response.statusCode+' - '+response.statusMessage); 
    }
    else {
      onSuccess();
    }
  });
}

// start listening
listenForNotificationRequests();

//example test java servlet which I made just to demonstrate this use case

@WebServlet("/TestServlet")
public class MainServlet extends HttpServlet {
    * @see HttpServlet#HttpServlet()
         */
        public MainServlet() {
            super();

        }

        /**
         * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
         *      response)
         */
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {

            // Get context and then relative path to saved json config file from
            // firebase
            ServletContext context = getServletContext();
            String fullPath = context.getRealPath(FILE_PATH_FOR_JSON_SERVER_AUTH);


            // Check if we actually got a file from above path
            if (fullPath != null) {

            } else {

            }

            // Setup connection to firebase database here
            FirebaseOptions options = new FirebaseOptions.Builder().setServiceAccount(new FileInputStream(fullPath))
                    .setDatabaseUrl(FIREBASE_DATABSE_URL).build();


            // Check to make sure we don't initialize firebase app each time webpage
            // is refreshed
            if (!exists) {

                // If firebase app doesn't exist then initialize it here and set
                // exists to true
                FirebaseApp.initializeApp(options);
                exists = true;



            }


        // Call this to begin listening *notify* node in firebase database for notifications
            addNotificationListener(request, response);


        }

        /**
         * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
         *      response)
         */
        protected void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {

            // Build apache httpclient POST request
            HttpClient client = HttpClientBuilder.create().build();
            HttpPost post = new HttpPost(ENDPOINT_URL);


            //Get the required id stored in lastMsgId tree map here
            if (!(chatLogs.getLastMsgIdTreeMap().isEmpty())) {


                sendToID = chatLogs.getLastMsgIdTreeMap().firstKey();
                lstmsg = chatLogs.getLastMsgIdTreeMap().get(sendToID);
            }

            //Set up a unique id with concatenating sendToID and lstmsg
            uniqueID = sendToID + lstmsg;
            //Set up a previous id to check with unique id. To avoid instant duplicate notifications
            previousID = fcmHelper.getPreviousid();

            // Check uniqueId and PreviousID beforeSending
            if (!(uniqueID.equals(previousID))) {
                fcmHelper.setPreviousid(uniqueID);

                //Check if device token and user Id hashmap is not null
                if (!(userLogs.getUserIdAndFcmTokenHashMap().isEmpty())) {

                    //Get the device token of sendTo Id here
                    deviceToken = userLogs.getUserIdAndFcmTokenHashMap().get(sendToID);

                    // Create JSON object for downstream data/notification
                    JSONObject mainNotificationJsonObj = new JSONObject();
                    JSONObject outerBaseJsonObj = new JSONObject();
                    try {

                        // Notification payload has 'title' and 'body' key
                        mainNotificationJsonObj.put(TITLE, NEW_MESSAGE_RECEIVED);
                        mainNotificationJsonObj.put(BODY, lstmsg);
                        mainNotificationJsonObj.put(NOTIFICATION_SOUND, NOTIFICATION_SOUND_TYPE_DEFAULT);
                        //mainNotificationJsonObj.put(TAG, fcmHelper.getFcmTagId());
                        System.out.println("This is sentBy id =" + fcmHelper.getFcmTagId());

                        // This will be used in case of both 'notification' or 'data' payload
                        outerBaseJsonObj.put(TO, deviceToken);


                        // Set priority of notification. For instant chat setting
                        // high will
                        // wake device from idle state - HIGH BATTERY DRAIN
                        outerBaseJsonObj.put(PRIORITY_KEY, PRIORITY_HIGH);

                        // Specify required payload key here either 'data' or
                        // 'notification'. We can even use both payloads in single
                        // message
                        outerBaseJsonObj.put(NOTIFICATION, mainNotificationJsonObj);
                    } catch (JSONException e) {

                        e.printStackTrace();
                    }


                    // Setup http entity with json data and 'Content-Type' header
                    StringEntity requestEntity = new StringEntity(outerBaseJsonObj.toString(),
                            ContentType.APPLICATION_JSON);

                    // Setup required Authorization header
                    post.setHeader(AUTHORIZATION, FIREBASE_SERVER_KEY);

                    // Pass setup entity to post request here
                    post.setEntity(requestEntity);

                    // Execute apache http client post response
                    HttpResponse fcmResponse = client.execute(post);


                    // Get status code from FCM server to debug error and success
                    System.out.println(RESPONSE_CODE + fcmResponse.getStatusLine().getStatusCode());

                    // Get response entity from FCM server and read throw lines
                    BufferedReader rd = new BufferedReader(new InputStreamReader(fcmResponse.getEntity().getContent()));

                    StringBuffer result = new StringBuffer();
                    String line = "";
                    while ((line = rd.readLine()) != null) {
                        result.append(line);
                    }

                    if (response != null) {

                        // Print out the response to webpage
                        PrintWriter out;
                        out = response.getWriter();
                        out.println(result);
                        System.out.println("This is Result - " + result);
                    }
                } else {
                    //Abort this process if conditions not met
                    post.abort();
                    System.out.println(THIS_MSG_ALREADY_SENT);
                }

            }

        }





        /*
         * This is the main method to be called to setup notifications listener on server startup
         */

        private void addNotificationListener(HttpServletRequest request, HttpServletResponse response) {


            //Initialize Value event listener 
            lastMsgListener = new ValueEventListener() {

                @Override
                public void onDataChange(DataSnapshot arg0) {

                    // Clear idLastMessagerecivedhash map if not null
                    if (lastMsgIdTreeMap != null) {
                        lastMsgIdTreeMap.clear();
                    }



                    //Get lastmsg to be sent as notification here
                    lstmsg = (String) arg0.child(LAST_MESSAGE).getValue();

                    //Get sendToID here
                    String sendToID = (String) arg0.child(SEND_TO).getValue();

                    //Get Sent by ID here
                    sentBy = (String) arg0.child(SENT_BY).getValue();

                    //Set fcmTag ID here
                    fcmHelper.setFcmTagId(sentBy);

                    //Check if lstmsg is not null
                    if (lstmsg != null) {

                        // Create lastmsgTimestampHashMap here
                        lastMsgIdTreeMap.put(sendToID, lstmsg);



                    }
                    //Check for null again
                    if (lastMsgIdTreeMap != null) {

                        chatLogs.setLastMsgIdTreeMap(lastMsgIdTreeMap);

                    }

                    try {
                        doPost(request, response);
                    } catch (ServletException e) {

                        e.printStackTrace();
                    } catch (IOException e) {

                        e.printStackTrace();
                    }


                }

                @Override
                public void onCancelled(DatabaseError arg0) {

                }
            };

            //Set up database reference to notify node here
            messageRef = FirebaseDatabase.getInstance().getReference().child(NOTIFY);

            //Add value listener to database reference here
            messageRef.addValueEventListener(lastMsgListener);



        }

"Java servlet is just a personal test. Some parts have been edited or removed to only give an idea about it's setup this is in no way production ready servlet please don't just copy - paste. I encourage you to understand and build your own."

like image 133
Nishant Dubey Avatar answered Nov 18 '22 15:11

Nishant Dubey


Edit: you should take a look at Firebase Cloud Functions, which let you do that without having to create a Node.js server

like image 32
quiro Avatar answered Nov 18 '22 17:11

quiro