Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a local notification in response to a push notification (from firebase) in cordova/ionic

I'm building an application using Ionic Framework that implements a chat function similar to good-old facebook messenger, in that i want to notify users of a chat message, but if they view it elsewhere, i want to remove the notification from their home screen.

I'm using firebase as a back-end for push notifications (though that could be changed i suppose).

I know that you can't expire a remote notification, but i've been told you can expire + remove a local notification, so my question is - can i reliably receive a remote notification, create a local one, and display that, and then in response to a notification with a scope of 'expire' or 'remove', delete a local notification so that my users don't see a duplication of information?

Most plugins tend to detect the status of the app and add a remote notification to the homescreen with the info you've pushed by default, is there a way to avoid this?

Thanks guys.

EDIT: - Local notifications: http://ionicframework.com/docs/native/local-notifications/ - Firebase cloud messaging: https://github.com/fechanique/cordova-plugin-fcm

like image 874
Dan Matthews Avatar asked Jul 31 '17 01:07

Dan Matthews


People also ask

What is the difference between local notification and push notification?

The essential difference between local notifications and push notifications is simple: Local notifications are scheduled by an app locally and are delivered by the same device. Push notifications are sent by a remote server (its provider) which sends these notifications to devices on which the app is installed.


2 Answers

As far as I can tell there're no plugins which accomplish all what you need. However..

can i reliably receive a remote notification, create a local one, and display that, and then in response to a notification with a scope of 'expire' or 'remove', delete a local notification so that my users don't see a duplication of information?

Most plugins tend to detect the status of the app and add a remote notification to the homescreen with the info you've pushed by default, is there a way to avoid this?

Yes, by using silent notifications and building the local notification by yourself.

For a project I'm working in, I modified the plugin cordova-plugin-fcm to add support for (local on demand) notifications dismiss/display, send multiple notifications to the cordova app, and some PRs that are not included yet. Also I build the notification by myself, to have full control of what is displayed. You can take a look at the code to get some ideas.

In brief it works like this:

Firstly, I send a "silent" push to the app, which is not displayed by Android:

{
    "content_available": true, // IMPORTANT: For Apple -> content-available: 1, for firebase -> content_available: true
    "priority": "high",
    "to": "/topics/all", // or to a fcm token
    "data"{
        "title": "My title", // this implies that you display the notification by yourself
        "body": "My body", // this implies that you display the notification by yourself
        "type": "NEW_USER_MESSAGE", // only relevant to this project
        "userId": "1", // only relevant to this project
        "timestamp", "150000000"
    }
}

Note: If the payload have the "notification": {} item, Android will display it on the system tray (if the app is in background). https://firebase.google.com/docs/cloud-messaging/concept-options#notifications_and_data_messages

Secondly, when the push arrives to the app (in onMessageReceived()), I build the local notification, assigning it a TAG and an ID. This is the way you can use to dismiss it later. For example, you could create a local notification with the TAG "NEW_USER_MESSAGE" and ID 1 (a constant indicating a state of the message, or the user ID for example). Also, Android will replace notifications with the same TAG and ID, so this is another way to automatically replace notifications (for example if you send a generic message, like "New update available").

    public static String TYPE_NEW_USER_MESSAGE = "NEW_USER_MESSAGE";
    public static String TYPE_USER_LEFT_ROOM = "USER_LEFT_ROOM";

    NotificationManager notificationManager =
            (NotificationManager) _ctx.getSystemService(Context.NOTIFICATION_SERVICE);

    // based in the type of the message you've received, you can stylize the notification
    if (type.equals( TYPE_USER_LEFT_ROOM )){
        notificationBuilder.setColor(Color.RED);
        notificationBuilder.setLights(Color.RED, 1000, 500);
    }
    else if (type.equals( TYPE_NEW_USER_MESSAGE )){
        notificationBuilder.setColor(Color.BLUE);
        notificationBuilder.setLights(Color.BLUE, 1000, 1000);
    }

    Notification n = notificationBuilder.build();
    notificationManager.notify(type, userId, n);

One advantage of doing it in this way, is that you have full control of the notification to be displayed, so you can stylize it like you want.

If you want to discard expired messages, you can check out the elapsed time between the sent timestamp and the current timestamp:

java.util.Date now = new java.util.Date();
java.util.Date sent_timestamp = new java.util.Date( Long.valueOf(timestamp.toString()) );
            final Long elapsed_time = ((now.getTime() - sent_timestamp.getTime()) / 1000);
Log.d(TAG, "New message. sent " + elapsed_time + "s ago");

Thirdly, when the user clicks on a notification Android will launch your app, and the plugin will send the payload of the push message to the cordova view (onNotificationReceived()).

Once your app is opened and you have received the push message, you can dismiss it adding a new action to the plugin:

onNotificationReceived(data){
    if (data.wasTapped === true){
        if (data.type === 'NEW_USER_MESSAGE'){
            FCMPlugin.dismissNotification(NEW_USER_MESSAGE, 1);
        }
    }
}

The Android action:

else if (action.equals( ACTION_DISMISS_NOTIFICATION )) {
    cordova.getThreadPool().execute(new Runnable() {
        public void run() {
            try{
                Log.d(TAG, "FCMPlugin dismissNotificaton: " + args.getString(0)); //tag
                NotificationManager nManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
                        nManager.cancel(args.getString(0)/*NEW_USER_MESSAGE*/, args.getInt(1) /*1*/);
                Log.d(TAG, "FCMPlugin dismissNotificaton() to remove: " + id); //tag
                callbackContext.success();
            }catch(Exception e){
                callbackContext.error(e.getMessage());
            }
       }
 });

https://github.com/TrustedCircles/cordova-plugin-fcm/blob/master/src/android/FCMPlugin.java#L286

And the method exposed to the cordova app:

// dismisses a notification by tag+id
FCMPlugin.prototype.dismissNotification = function( tag, userId, success, error ){
    exec(success, error, "FCMPlugin", 'dismissNotification', [tag, userId]);
}

https://github.com/TrustedCircles/cordova-plugin-fcm/blob/master/www/FCMPlugin.js#L65

like image 90
Gustavo Iñiguez Goia Avatar answered Oct 07 '22 00:10

Gustavo Iñiguez Goia


The only tricky bit with notifications in cordova/ionic is the JS part receiving the notification and triggering the Android code.

I used https://github.com/phonegap/phonegap-plugin-push library and its pretty straight forward.

There is a callback when notifications are received in JS(Cordova/Ionic), use this to render you notifications locally in Android.

P.S: Basel's answer tells you how to clear your notifications, so I decided to leave that bit out.

like image 24
Rahul Ravindran Avatar answered Oct 06 '22 22:10

Rahul Ravindran