Clicking on notification doesn't open mentioned activity

rick picture rick · Nov 4, 2016 · Viewed 11.4k times · Source

I am trying to open an Activity when the notification is clicked and below is my code.

Intent intent = new Intent(this.getApplicationContext(), NotificationActivity.class);
intent.putExtra("msgBody",messageBody);
intent.putExtra(Constants.NOTIF_INTENT_TYPE,Constants.NOTIF_INTENT_TYPE);

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                |Intent.FLAG_ACTIVITY_SINGLE_TOP
                |Intent.FLAG_ACTIVITY_CLEAR_TOP); //Tried with many options here

PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 , intent,
                                         PendingIntent.FLAG_CANCEL_CURRENT);

Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);

NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.otp_icon)
                .setContentTitle("Push MSG")
                .setContentText(messageBody)
                .setAutoCancel(true)
                .setSound(defaultSoundUri)
                .setContentIntent(pendingIntent);

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

notificationManager.notify(0, notificationBuilder.build());

Android Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.com.pushapp">

    <uses-sdk
        android:minSdkVersion="17"
        android:targetSdkVersion="21" />

    <supports-screens
        android:anyDensity="true"
        android:largeScreens="true"
        android:normalScreens="true"
        android:smallScreens="true" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_LOGS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:name=".AndroidPushApp"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher">
        <activity
            android:name=".PushSplashScreen"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".MainApplicationScreen"
            android:screenOrientation="portrait"
            android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
            </intent-filter>
        </activity>
        <activity
            android:name=".StartActivity"
            android:launchMode="singleTask"
            android:screenOrientation="portrait"
            android:uiOptions="splitActionBarWhenNarrow"
            android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
            </intent-filter>
        </activity>

        <service android:name=".MyFirebaseMessagingService">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>

        <service android:name=".MyFirebaseInstanceIDService">
            <intent-filter>
                <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
            </intent-filter>
        </service>

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

        <activity
            android:name=".NotificationActivity"
            android:exported="true"
            android:label="@string/title_activity">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Whenever I get notification from FCM I am calling this notification. The NotificationActivity is not opening whenever I click on notification, rather the app is opening(splash screen->starting activity of my usual app flow). Whenever I get notification while the app is already open, the NotificationActivity is getting opened, but not when app is not already opened. Could someone please help me on resolving this?

Note: Please I am reiterating that NotificationActivity.class is not getting opened when clicked on notification when app is not already opened state.

Answer

Mukesh Rana picture Mukesh Rana · Nov 7, 2016

According to FCM Documentation, for receiving and handling messages,

If you want to receive notifications when your app is in the foreground, you need to add some message handling logic.

To receive messages, use a service that extends FirebaseMessagingService. Your service should override the onMessageReceived callback, which is provided for most message types, with the following exceptions:

1). Notifications delivered when your app is in the background. In this case, the notification is delivered to the device’s system tray. A user tap on a notification opens the app launcher by default.

2). Messages with both notification and data payload, both background and foreground. In this case, 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.

enter image description here

So Basically, we have two types of Payloads

1). Notification Payload

2). Data Payload

3). Both (an additional type we can consider).

Now let's discuss one by one these payloads. Before that you need to understand how can you send these Payloads to your app. All you have to do is to make use of any tool that can perform HTTP POST Request. In my case, I am using the Postman tool, a Google Chrome Plugin.

Before making a HTTP Post Request for FCM, you have to consider three things:

1). HTTP Post Request URL : https://fcm.googleapis.com/fcm/send

2). Request Headers :

i). Content-Type : application/json

ii). Authorization : key = YOUR_SERVER_KEY

Below is the screenshot for the same to show how it looks.

enter image description here

3). Body : In this we are going to have JSON for Notification and Data Payloads.

  • So starting with Notification Payload, the simplest of all. In this case, onMessageReceived() is called only when the app is in Foreground, For all other cases, it's a System Tray Notification, which opens the Launcher Activity when clicked. This is helpful when you don't want to control Notifications by your own and not much data to deal with when Notification comes. You can even control the sound, icon and click_action(only when the app is in Foreground) without writing any code in your onMessageReceived(). One example of a body of such HTTP POST Request is attached in the screenshot below.

enter image description here

For opening desired Activity when sending click_action parameter, you have to use the below code in your onMessageReceived().

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    if (null != remoteMessage.getNotification().getClickAction()) {
            startActivity(remoteMessage.getNotification().getClickAction(), null, this);
    }
}

and below is your startActivity() method :

public void startActivity(String className, Bundle extras, Context context) {
    Class cls = null;
    try {
        cls = Class.forName(className);
    } catch (ClassNotFoundException e) {
        //means you made a wrong input in firebase console
    }
    Intent intent = new Intent(context, cls);
    if (null != extras) {
        intent.putExtras(extras);
    }
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
            | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    context.startActivity(intent);
}

NOTE : This click_action key will work only when the app is in Foreground, for all other case when the app is in Background and closed, it doesn't work. It doesn't even open the Launcher Activity, in case of Background and Closed, if this parameter is specified.

  • Now comes the Data Payload. This is similar to the one we have in GCM. This is very important if we want to handle all the Notification stuff by ourselve same as we all were doing in case of GCM. Example of a body of such HTTP POST Request is shown below.

enter image description here

So in this case, onMessageReceived() is called everytime and this will work in the same way as that of GCM, so helpful to all of us. You have to Override onMessageReceived() as shown below.

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    Map<String, String> data = remoteMessage.getData();
    if (null != data && 0 < data.size()) {
        if (data.containsKey("custom_key_1")) {
            sendNotification(data.get("custom_key_1"));
        }
    }
}

private void sendNotification(String messageBody) {
        Intent intent = new Intent(this, DesiredActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
                PendingIntent.FLAG_ONE_SHOT);

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

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

        notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
  • Last but not the least, we can send both Notification and Data Payloads as well. In this case, onMessageReceived() is called when the app is in Foreground. For background and closed state, Notification comes in the system tray similar to Notification Payload but the only difference is we can have data extras as well that we can use to redirect user to a desired Activity, when clicked on a Notification. Below is the example of a body of such HTTP POST Request.Example of a body of such HTTP POST Request is shown below.

enter image description here

When clicking on a Notification on System Tray, it will open the Launcher Activity and You need to Override onCreate() of your Launcher Activity to get the data extras and redirect user to the desired Activity. Below is the code, you have to write in onCreate() of your Activity to redirect user to the desired Activity.

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if(getIntent().hasExtra("custom_key_1") && getIntent().getStringExtra("custom_key_1")
            .equals("custom_value_1")){
        startActivity(new Intent(this, DesiredActivity.class));
        finish();
        return;
    }

    // other operations
}

Another case to this type is, when your Launcher Activity is defined as launchMode="true" in the manifest and when the Notification Arrives, your Launcher Activity is in the Foreground. So when you click on the Notification, you have to Override the onNewIntent() method in your Launcher Activity to open the desired Activity. Below is the sample code for the same.

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    if (getIntent().hasExtra("custom_key_1") && getIntent().getStringExtra("custom_key_1")
                .equals("custom_value_1")) {
            startActivity(new Intent(this, DesiredActivity.class));
            finish();
    }
}

So in short, I would say it's good to go with the Data Payload type as it provides more flexibility and control over the Notification and more importantly as we all are used to GCM, so this type is what we all would like to prefer.

Note : Some devices are having issue receiving Notifications in Background as I found some queries over same here. Moreover at the time, I was investigating these cases, my ASUS phone was not receiving notifications in Background for any of the types mentioned above. So not sure what's the issue with these devices.