0

I am using pyFCM on a remote server to push event notifications to my app. The notification has a data payload as well, and this is needed for making some UI changes.

When the server app sends the app a notification, there is a notification that pops up on the phone, pressing which causes the default activity to be opened. Also, I have overridden the onMessageReceived in the service extending FirebaseMessagingService, and the data payload is then taken from the message and processed.

I have seen multiple questions on SO like these:

  1. How to open Fragment on click of push notification
  2. Open fragment from notification when the app in background
  3. How to open fragment page, when pressed a notification in android
  4. Open a fragment when press a notification in android

One thing in common with all the above SO questions is that they involve building a notification using Notification.Builder, and then setting up an Activity to open (or it's onNewIntent to be triggered depending on whether it's android:launchMode is "singleTop" in the Manifest ) . My distinct understanding is that this would work if FCM were used to send the app a data message and not a notification.

The question(s)

What I really fail to understand is how setting up a notification after the data is received from FCM helps me change the behaviour of the notification received from FCM's push. Is that even possible, or have I gotten something wrong?

The second part to this question is: How do I handle a user click on a push notification to load a specific fragment in my activity (when I am not the one creating the fragment in the app - just received via FCM)?

This is the current code that runs in my app.

The onMessageReceived code is :

@Override
public void onMessageReceived(RemoteMessage remoteMessagenew) {

    this.remoteMessage = remoteMessagenew;
    super.onMessageReceived (remoteMessage);
    database = MyDatabase.getDatabase(getApplication());
    appuser = database.AppDao().getUserDetails(); 


    Log.d(TAG, "onMessageReceived: ");
    Log.d (TAG, "onMessageReceived: #########################################################");
    Log.d (TAG, "onMessageReceived: #########################################################");
    Log.d (TAG, "onMessageReceived: Message Data is : " + remoteMessage.getData ());


    Map messageData = remoteMessage.getData ();

    // Broadcast
    args = new Bundle ();
    int stage = 0 ;
    Log.d (TAG, "onMessageReceived: ");
    if(messageData.containsKey ("otherid")) {
        incidenthandler(messageData);
    } else  if(messageData.containsKey ("itemid")) {
        itemhandler(messageData);
    }
}

The item handler to handle the data to refresh on the app screen

    private void itemhandler(Map messageData) {
    messagebody = remoteMessage.getNotification().getBody();

    if(messageData.containsKey("itemid")) {
        Log.d(TAG, "itemhandler: #######################");
        Log.d(TAG, "itemhandler: #######################");
        Log.d(TAG, "itemhandler: UPDATING");
        Log.d(TAG, "itemhandler: #######################");
        Log.d(TAG, "itemhandler: #######################");

        String item = (String ) messageData.get("itemid");
        JsonParser jsonParser = new JsonParser();
        JsonObject jsonObject = (JsonObject)jsonParser.parse(item);
        item refreshedItem = parseFromJson(jsonObject);
        database.revivDao().upsert(refreshedItem);

    } else {
        // this really shouldn't happen, but putting in a scenario where this does
    // syncing the db with the app
        Log.d(TAG, "itemhandler: #######################");
        Log.d(TAG, "itemhandler: #######################");
        Log.d(TAG, "itemhandler: REFRESHING "); 
        Log.d(TAG, "itemhandler: #######################");
        Log.d(TAG, "itemhandler: #######################");

    // this triggers a call to my intentservice to refresh the db

    Log.d(TAG, "itemhandler: refreshing items");
        Intent intent = new Intent ("refreshitems");
        Bundle clickdata = new Bundle();
        data.putString("item_sub1", item_sub1);
        data.putString("item_sub1", item_sub1);
        intent.putExtra("data", clickdata) ;  // add Bundle to Intent
        localBroadcastManager.getInstance(getApplication()).sendBroadcast(intent); // Broadcast Intent
    }

    String itemid = messageData.get("itemid").toString();
    try{
        Log.d(TAG, "itemhandler: #######################");
        Log.d(TAG, "itemhandler: #######################");
        Log.d(TAG, "itemhandler: sending localbroadcast");
        Log.d(TAG, "itemhandler: #######################");
        Log.d(TAG, "itemhandler: #######################");

    // this is where I try and switch to the screen to display some info - if the app is running

        Intent notificationIntent = new Intent("switchtofragment");
        notificationIntent.putExtra("launchitemfragment", true);
        notificationIntent.putExtra("itemid", itemid);
        notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        if(localBroadcastManager == null) localBroadcastManager = LocalBroadcastManager.getInstance(getApplication());
        localBroadcastManager.sendBroadcast(notificationIntent);

   // now override the notification to load the fragment on click

        setupNotification(itemid, messagebody);
        notificationManager.notify(1, builder.build());

    } catch (NullPointerException e){
        return;
    }
}

Finally, the function to handle the notification:

private void setupNotification(String housecallid, String message) {
        //Log.d(TAG, "setting up notification");
        String idChannel = ANDROID_CHANNEL_ID;
        Context context = getApplicationContext();

        Intent notificationIntent = new Intent(getApplicationContext(), Reviv.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        notificationIntent.putExtra("housecallid", housecallid);
        notificationIntent.putExtra("launchhousecallfragment", true);
        Bundle extras = new Bundle();
        extras.putBoolean("launchhousecallfragment", true);
        extras.putString("housecallid", housecallid);
        notificationIntent.putExtras(extras);
        notificationIntent.putExtra("data", extras);


        PendingIntent pendingIntent = PendingIntent.getActivity(this,0, notificationIntent,
                PendingIntent.FLAG_UPDATE_CURRENT);

        if(notificationManager == null )
            notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);


        builder = new NotificationCompat.Builder(context, null);
        builder. setSmallIcon(R.drawable.heart)
                .setLargeIcon(BitmapFactory.decodeResource(context.getResources(),
                        R.drawable.heart))
                .setContentIntent(pendingIntent)
                .setContentTitle("Reviv")
                .setContentText(message);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if(notificationChannel == null) {
                notificationChannel = new NotificationChannel(idChannel, context.getString(R.string.app_name), importance);
                // Configure the notification channel.
                notificationChannel.setDescription("Reviv Housecall Notification");
                notificationChannel.enableLights(true);
                notificationChannel.setLightColor(Color.RED);
                notificationChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
                notificationManager.createNotificationChannel(notificationChannel);
                builder.setChannelId(idChannel);
                builder.setAutoCancel(true);
            }
        } else {
            builder.setContentTitle(context.getString(R.string.app_name))
                    .setPriority(NotificationCompat.PRIORITY_HIGH)
                    .setColor(ContextCompat.getColor(context, R.color.transparent))
                    .setVibrate(new long[]{100, 250})
                    .setLights(Color.RED, 500, 5000)
                    .setAutoCancel(true);
        }
        builder.setContentIntent(pendingIntent);
    }
kilokahn
  • 1,136
  • 2
  • 18
  • 38

1 Answers1

0
  1. You could rather send data messages only instead of sending push messages + payload and receive all notifications in onMessageReceived whether the app is in foreground or background. You can then setup the notification using the setupNotification function you have written and check for extras in the start of the Reviv activity and replace fragments accordingly.
  2. I'm not sure but about this approach using push notifications but here's a suggestion: Try to see if you receive the data payload (check for getIntent().getExtras()) which you are sending with the push notification in the activity which is opened by default on clicking the push notification.
Aditya
  • 33
  • 6
  • I have tried this option as well. However, data messages sent via FCM do not get processed if the app is closed - this means that app users will have to either periodically keep checking their apps, or have it open all the time, which is not a practical real world scenario, and thus necessitates the need for a push notification. – kilokahn Jan 06 '19 at 11:45
  • @kilokahn They do ! I have implemented it myself in my college's app. It doesn't matter if the app is closed. Check if you are indeed sending the _data_ messages. – Aditya Jan 06 '19 at 12:50
  • I'm sorry to say that my experience suggests otherwise. As you can see in the documentation, data messages trigger onMessageReceived in _backgrounded_ apps, not killed/dead/closed ones. Can you point me to any resource that tells me about handling of data messages in this scenario? https://firebase.google.com/docs/cloud-messaging/android/receive – kilokahn Jan 06 '19 at 16:06
  • It can be seen in the summary table of the link you provided that for both foregrounded and backgrounded apps, data messages are received in _onMessageReceived_. Try it out !! – Aditya Jan 06 '19 at 20:29
  • I already have, and it doesn't seem to be working. A data message sent to my app when its open has all the intended results - whether it is in FG or BG. However, when the app is force-closed or exits (via finish/finishAffinity), I get no notifications at all. – kilokahn Jan 06 '19 at 21:05
  • In either case, I am unable to load a specific fragment because for some strange reason, unless the app is open and in foreground, my onMessageReceived does not seem to be triggered, and any code in it does not run. So the question of click handling isn't addressed. – kilokahn Jan 06 '19 at 21:07
  • Is the repo open for us to see ? – Aditya Jan 07 '19 at 21:05
  • I'm sorry - its a private repo, but the code I have shared is more or less it, albeit a bit anonymized. Let me know if you need any specific details and I'll share them with you. – kilokahn Jan 08 '19 at 10:01