Firebase (FCM): open activity and pass data on notification click. android

AndroidFirebaseNotificationsFirebase Realtime-DatabaseFirebase Cloud-Messaging

Android Problem Overview


There should be clear implementation of how to work with Firebase notification and data. I read many answers but can't seem to make it work. here are my steps:

1.) I am passing notification and data to android in PHP and it seems to be fine:

$msg = array
	(
		 "body" => $body,
		 "title" => $title,
		 "sound" => "mySound"
	);
	
	$data = array
	(
		 
		 "user_id" => $res_id,
		 "date" => $date,
		 "hal_id" => $hal_id,
		 "M_view" => $M_view
	);
	
	$fields = array
	(
		'registration_ids' => $registrationIds,
		'notification' => $msg,
		'data' => $data
	);
	
	
	 
	$headers = array
	(
		'Authorization: key='.API_ACCESS_KEY,
		'Content-Type: application/json'
	);
	 
	$ch = curl_init();
	curl_setopt( $ch,CURLOPT_URL, 'https://android.googleapis.com/gcm/send' );
	curl_setopt( $ch,CURLOPT_POST, true );
	curl_setopt( $ch,CURLOPT_HTTPHEADER, $headers );
	curl_setopt( $ch,CURLOPT_RETURNTRANSFER, true );
	curl_setopt( $ch,CURLOPT_SSL_VERIFYPEER, false );
	curl_setopt( $ch,CURLOPT_POSTFIELDS, json_encode( $fields ) );
	$result = curl_exec($ch );
	curl_close( $ch );

2.) when notification and data is received in Android it shows notification. When I click on this notification it opens app. But I can not figure out the way to handle the data when the app is opened. There are couple differences when app is in foreground and backround. The code that I have now is the following:

public class MyFirebaseMessagingService extends FirebaseMessagingService {

private static final String TAG = "MyFirebaseMsgService";

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
   
    String user_id = "0";
    String date = "0";
    String cal_id = "0";
    String M_view = "0";

    if (remoteMessage.getData().size() > 0) {
        Log.d(TAG, "Message data payload: " + remoteMessage.getData());
        user_id = remoteMessage.getData().get("user_id");
        date = remoteMessage.getData().get("date");
        hal_id = remoteMessage.getData().get("hal_id");
        M_view = remoteMessage.getData().get("M_view");
    }

    //Calling method to generate notification
    sendNotification(remoteMessage.getNotification().getBody(), user_id, date, hal_id, M_view);
}

private void sendNotification(String messageBody, String user_id, String date, String hal_id, String M_view) {
    Intent intent = new Intent(this, MainActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
  
    intent.putExtra("fcm_notification", "Y");
    intent.putExtra("user_id", user_id);
    intent.putExtra("date", date);
    intent.putExtra("hal_id", hal_id);
    intent.putExtra("M_view", M_view);
    int uniqueInt = (int) (System.currentTimeMillis() & 0xff);
    PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), uniqueInt, intent,
            PendingIntent.FLAG_UPDATE_CURRENT);

    Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
    notificationBuilder.setSmallIcon(R.drawable.ic_launcher)
            .setContentText(messageBody)
            .setAutoCancel(true)
            .setSound(defaultSoundUri)
            .setContentIntent(pendingIntent);

    NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(0, notificationBuilder.build());
}}

3.) When I use the code above and when I click on notification all it does it opens the app if in background. If app in foreground then on notification click it simply dismisses notification. However, I want to receive data and open specific Activity in both scenarios (background and foreground). I have in MainActivity the following code, but I am not able to get data. fcm_notification, date, hal_id returns null.

public class MainActivity extends Activity {
 UserFunctions userFunctions;
	
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    setIntent(intent);
    Intent intent_o = getIntent();
}

@Override
protected void onResume() {
    super.onResume();
    userFunctions = new UserFunctions();
    if(userFunctions.isUserLoggedIn(getApplicationContext())){
        Intent intent_o = getIntent();
        String fcm_notification = intent_o.getStringExtra("fcm_notification") ;
        String user_id = intent_o.getStringExtra("user_id");
        String date = intent_o.getStringExtra("date");
        String hal_id = intent_o.getStringExtra("hal_id");
        String M_view = intent_o.getStringExtra("M_view");
        Intent intent = new Intent(this, JobList.class);

        // THIS RETURNS NULL, user_id = null
        System.out.print("FCM" + user_id);
        startActivity(intent);
        finish();
    }else{
        // user is not logged in show login screen
        Intent login = new Intent(this, LoginActivity.class);
        startActivity(login);
        // Closing dashboard screen
        finish();
    }
}}

IF anyone can direct or advice how can I retrieve data in MainActivity.java from Firebase in either scenario (foreground or background) that would be fantastic.

Android Solutions


Solution 1 - Android

So first off, I'll put in the detail mentioned in the Handling Messages docs.

In the summary under the Both row, it shows that when the app is on foreground, the payload will be handled in your onMessageReceived().

In order to open the activity from onMessageReceived(), you should check if the data you need is in the payload, if it does, call your specific activity then pass all the other details you need via intent.

Now if the app is in background, it is mentioned in the docs that the notification is received by the Android system tray and that the data payload can be retrieved from the extras of the intent.

Just adding in the details from my answer here which pretty much just gives the docs statement and a link to a sample:

>Handle notification messages in a backgrounded app > >When your app is in the background, Android directs notification messages to the system tray. A user tap on the notification opens the app launcher by default. > >This includes messages that contain both notification and data payload (and all messages sent from the Notifications console). In these cases, the notification is delivered to the device's system tray, and the data payload is delivered in the extras of the intent of your launcher Activity.

I think this answer by @ArthurThompson explains it very well:

>When you send a notification message with a data payload (notification and data) and the app is in the background you can retrieve the data from the extras of the intent that is launched as a result of the user tapping on the notification. > >From the FCM sample which launches the MainActivity when the notification is tapped:

if (getIntent().getExtras() != null) {
    for (String key : getIntent().getExtras().keySet()) {
        String value = getIntent().getExtras().getString(key);
        Log.d(TAG, "Key: " + key + " Value: " + value);
    }
}

Solution 2 - Android

After trying all the answers and blogs came up with solution. if anyone needs please use this video as reference

https://www.youtube.com/watch?v=hi8IPLNq59o

IN ADDITION to the video to add intents do in MyFirebaseMessagingService:

public class MyFirebaseMessagingService extends FirebaseMessagingService {

private static final String TAG = "MyFirebaseMsgService";

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
   
    String user_id = "0";
    String date = "0";
    String hal_id = "0";
    String M_view = "0";

    if (remoteMessage.getData().size() > 0) {
        Log.d(TAG, "Message data payload: " + remoteMessage.getData());
        user_id = remoteMessage.getData().get("user_id");
        date = remoteMessage.getData().get("date");
        cal_id = remoteMessage.getData().get("hal_id");
        M_view = remoteMessage.getData().get("M_view");
    }

    String click_action = remoteMessage.getNotification().getClickAction();

    //Calling method to generate notification
    sendNotification(remoteMessage.getNotification().getBody(), remoteMessage.getNotification().getTitle(), user_id, date, hal_id, M_view, click_action);
}

private void sendNotification(String messageBody, String messageTitle, String user_id, String date, String hal_id, String M_view, String click_action) {
    Intent intent = new Intent(click_action);
    intent.putExtra("user_id", user_id);
    intent.putExtra("date", date);
    intent.putExtra("hal_id", hal_id);
    intent.putExtra("M_view", M_view);

    PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent,
            PendingIntent.FLAG_ONE_SHOT);

    Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
    notificationBuilder.setSmallIcon(R.drawable.ic_launcher)
            .setContentTitle(messageTitle)
            .setContentText(messageBody)
            .setAutoCancel(true)
            .setSound(defaultSoundUri)
            .setContentIntent(pendingIntent);

    NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(0, notificationBuilder.build());
}}

and in new NotificationReceive activity in onCreate or onResume add this

    notification_Y_N = (TextView) findViewById(R.id.notification_Y_N);
    user_id_text = (TextView) findViewById(R.id.user_id_text);

    Intent intent_o = getIntent();
    String user_id = intent_o.getStringExtra("user_id");
    String date = intent_o.getStringExtra("date");
    String hal_id = intent_o.getStringExtra("hal_id");
    String M_view = intent_o.getStringExtra("M_view");

    notification_Y_N.setText(date);
    user_id_text.setText(hal_id);

Solution 3 - Android

To invoke the onMessageReceived() method you will need to use another method to send notifications (like creating a Web API to send notifications). Then using it,

> remove the notification payload from your FCM messages in order to have the data payload delivered to the onMessageReceived() method.

When your app is in the background, data payload is delivered to the onMessageReceived method only if there is no notification payload.

> In case both payloads exist then system automatically handles the > notification part (system tray) and your app gets the data payload in > the extras of the intent of launcher Activity (after the user tap on > the notification).

For more info please refer to the following links:

Solution 4 - Android

You don't need to implement sendNotification and onMessageReceived yourself.

When sending:

$data = array
(
    "user_id" => $res_id
    //whatever fields you want to include
);

$msg = array
(
     "body" => $body,
     "title" => $title,
     "data" => $data
     // more fields
);

android side (on your MainACtivity:

private void handleIntent(Intent intent) {
    String user_id= intent.getStringExtra("user_id");
    if(user_id!= null)
        Log.d(TAG, user_id);
}

and of course:

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    handleIntent(intent);
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    handleIntent(getIntent());
}

whatever fields you put in data will be sent to your intent extra.

Solution 5 - Android

firstly, if you have data object and notification object in response . then ask the backend developer to remove notification object. i hope my own class help .

public class MyFirebaseService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseService";

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    super.onMessageReceived(remoteMessage);
   

    // Check if message contains a data payload.
    if (remoteMessage.getData().size() > 0) {
        Log.d(TAG, "Message data payload: " + remoteMessage.getData());
        Log.d(TAG, "Message data payload:id " + remoteMessage.getData().get("mode_id"));

      
        sendNotification(remoteMessage.getData().get("body"),
                remoteMessage.getData().get("mode_id"), remoteMessage.getData().get("click_action"));
        
    }
}

private void sendNotification(String messageBody, String id, String clickAction) {
    Intent intent = new Intent(clickAction);
    TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
    stackBuilder.addNextIntentWithParentStack(intent);
    intent.putExtra("id", id);
    intent.putExtra("body", messageBody);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
            | Intent.FLAG_ACTIVITY_CLEAR_TASK);

    PendingIntent pendingIntent =
            stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "111")
            .setSmallIcon(R.drawable.venus_logo)
            .setContentText(messageBody)
            .setAutoCancel(true)
            .setVibrate(new long[]{1000, 1000, 1000, 1000, 1000})
            .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
            .setContentIntent(pendingIntent)
            .setLights(Color.GREEN, 3000, 3000);

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
        NotificationManager notificationManager =
                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        int importance = NotificationManager.IMPORTANCE_HIGH;
        NotificationChannel notificationChannel = new NotificationChannel("111", "NOTIFICATION_CHANNEL_NAME", importance);
        notificationChannel.enableLights(true);
        notificationChannel.setLightColor(Color.RED);
        notificationChannel.enableVibration(true);
        notificationChannel.setShowBadge(false);
        notificationChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
        assert notificationManager != null;
        notificationBuilder.setChannelId("111");
        notificationManager.createNotificationChannel(notificationChannel);
        notificationManager.notify(0, notificationBuilder.build());
    } else {
        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
        notificationManager.notify(0, notificationBuilder.build());
    }
  }
}

then add this to your manifest file .

    <service
        android:name=".data.services.MyFirebaseService"
        android:exported="false">
        <intent-filter>
            <action android:name="com.google.firebase.MESSAGING_EVENT" />
        </intent-filter>
    </service>
    <activity
        android:name=".ui.notifications.NotificationsDetailsActivity"
        android:excludeFromRecents="true"
        android:launchMode="singleTask"
        android:parentActivityName=".ui.home.HomeActivity"
        android:taskAffinity="">
        <intent-filter>
            <action android:name="co.example.yourApp.ui.notifications_TARGET_NOTIFICATION" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVaidasView Question on Stackoverflow
Solution 1 - AndroidAL.View Answer on Stackoverflow
Solution 2 - AndroidVaidasView Answer on Stackoverflow
Solution 3 - AndroidAshuView Answer on Stackoverflow
Solution 4 - AndroidOhad CohenView Answer on Stackoverflow
Solution 5 - Androiddev saadView Answer on Stackoverflow