20

I implemented Firebase and testing the Firebase notifications. When the app is in the foreground I don't have problems, I implemented a service that extends FirebaseMessagingService and handle the message and data in onMessageReceived

I have problems when the app is in background, I would like to send a notification that opens a specific activity and does what I schedule to do, not just opening the App.

I did as described on the Firebase guide, but I'm not able to start the specific activity.

Here the manifest:

<activity android:name=".BasicNotificationActivity">
            <intent-filter>
                <action android:name="OPEN_ACTIVITY_1" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

And here the Firebase Console. What do I have to write in those fields to open my "BasicNotificationActivity"?

Firebase console

Micha F.
  • 634
  • 4
  • 17
Alex
  • 1,447
  • 7
  • 23
  • 48
  • 2
    The behaviour you describe is odd. According to the documentation (https://firebase.google.com/docs/notifications/android/console-device#receive_and_handle_messages) onMessageReceived should *only* be called if the app is in *foreground* (as you have notification and data payload). What you describe is that it is the other way round. Is that correct? – Micha F. Jun 29 '16 at 23:06
  • I'm sorry, I exchanged foreground and background. I edited the question. – Alex Jul 01 '16 at 13:56
  • Possible duplicate of [Firebase FCM notifications click\_action payload](http://stackoverflow.com/questions/37407366/firebase-fcm-notifications-click-action-payload) – Micha F. Jul 01 '16 at 15:33

4 Answers4

22

This is a duplicate of this question: Firebase FCM notifications click_action payload

But the answer that was accepted by the author of this question just states that it is not possible with Firebase Console, but it is - with an easy workaround. This answer by diidu to the same question explains the workaround I would use.

UPDATE:
To elaborate on his answer:

Add a helper class (or implement startActivity() method somehow):

public class ClickActionHelper {
    public static void startActivity(String className, Bundle extras, Context context){
        Class cls;
        try {
            cls = Class.forName(className);
        }catch(ClassNotFoundException e){
            //means you made a wrong input in firebase console
        }
        Intent i = new Intent(context, cls);
        i.putExtras(extras);
        context.startActivity(i);
    }
}

In the launcher-activity of your app, call a mehtod to check any new intents in onCreate() and onNewIntent() (onNewIntent() is only called instead of onCreate() if Activity is launched with single-top flag):

@Override
protected void onCreate(Bundle bundle) {
    [...]
    checkIntent(getIntent());
 }

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    [...]
    checkIntent(intent);
}

public void checkIntent(Intent intent) {
       if (intent.hasExtra("click_action")) {
        ClickActionHelper.startActivity(intent.getStringExtra("click_action"), intent.getExtras(), this);
       }
}

And in onMessageReceived():

 public void onMessageReceived(RemoteMessage remoteMessage) {
     Map<String, String> data = remoteMessage.getData();        
     if (data.containsKey("click_action")) {
         ClickActionHelper.startActivity(data.get("click_action"), null, this);
     }
 }

To send a notification with firebase console, put a key-value-pair as custom data like this:

Key: click_action
Value: <fully qualified classname of your activity>

Now when a notification is received and clicked on, it will open your activity. If your app is in foreground, it will also immediately change to the activity - it would probably be good to ask the user if he wants to go to this activity or not (by showing a dialog in onMessageReceived()).

Community
  • 1
  • 1
Micha F.
  • 634
  • 4
  • 17
  • In that answer I don't understand where to put the code that should handle the "custom data". – Alex Jul 01 '16 at 19:29
  • I updated my answer. That would be how the custom data was handled. When in background, the custom data is in the extras of the intent that launches the launcher activity. – Micha F. Jul 01 '16 at 20:14
  • onMessageReceived() you mentioned, in which class is supposed to be placed? In the Service I implemented for Firebase? Or in the new class I want to open with the notification? – Alex Jul 04 '16 at 16:57
  • The onMessageReceived() you already implemented and mentioned in your question. How this works is that if the app is in background, the data will be in the intent that launches the launcheractivity and your desired activity will be started from onNewIntent (where this intent is "intercepted"). If in foreground onMessageReceived() will be called and you can start the activity from there – Micha F. Jul 04 '16 at 17:05
  • I managed to obtain the behavior I want when the App is not launched. When it is in foreground obviously I don't have problems, but when it is in BACKGROUND I still have doubts because onCreate() is not called. How should I handle the payload when the App is in the background? – Alex Jul 05 '16 at 14:20
  • Well, onCreate() isnt called if the app is only paused when the message arrives, but onNewIntent() will always be called when a new Intent comes in. – Micha F. Jul 05 '16 at 14:26
  • You say "I still have doubts": So you didnt try it? Why dont you just go on and try what I suggested? If something doesnt work then, you can comment here again to ask :) If you had done so, you wouldnt have wondered about onCreate() - I never mentioned it in my answer ;) – Micha F. Jul 05 '16 at 16:53
  • Thank you:) I tried every time before posting, and I got an undesired behavior when the app is in background (that's why I said "i still have doubts"). I'm now keep testing, I'll let you know if it works :) – Alex Jul 05 '16 at 17:14
  • This worked for me thank you so much! The missing part was for me. After I wrote whole name it worked – Hilal Sep 07 '16 at 11:37
  • onNewIntent() does not always worked . I tried onResume() and it worked: – Saeed Sharman Jan 27 '17 at 16:06
  • 1
    @SaeedSharman Yes, I'm sorry, you are right, I relied on what diidu suggested without checking when onNewIntent() is called (https://developer.android.com/reference/android/app/Activity.html#onNewIntent(android.content.Intent)). Updated my answer: Check the Intent in onCreate() also. But just checking in onResume() as you do is also working fine :) – Micha F. Jan 30 '17 at 12:36
  • remove `i.putExtras(extras);` (you can remove `Bundle extras` method input, too ) and add `i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);` before `context.startActivity(i);`. Be careful about **click_action value** in Firebase console. for example type SecondActivity.class not SecondActivity!!! – Alireza Noorali Jul 11 '17 at 09:06
  • @AlirezaNoorali You need the Intent-extras if you want to specify more custom key-value-pairs in the console and use them in the started Activity - if thats not the case for you you can modify the code to what suits you, same with Intent-flags. Anyway, this code is not supposed to be copy-pasted (see the last paragraph of my answer). And you are right, as I posted, the value for "click_action" must be the *fully qualified* classname. – Micha F. Jul 11 '17 at 10:33
  • @MichaF: oh Micha I didn't write to you. I wrote to other users who couldn't use your code. Excuse me I'm not good in english.Your post is the the closest solution to my purpose. by null value in extra input of **ClickActionHelper.startActivity** in **onMessageReceived** I got this exception: _java.lang.NullPointerException: Attempt to invoke virtual method 'void android.os.Bundle.unparcel()' on a null object reference_ . I said _Be careful about click_action value in Firebase console_ to emphasis your answer which says write _fully qualified classname of your activity_ . anyway I apologize – Alireza Noorali Jul 11 '17 at 11:29
  • Tried to implement this solution however the Helper class gives me errors on errors on compilation – Mederic Nov 27 '17 at 07:22
  • Awesome solution! Just to add, I preferred using `click_action_console` as the key instead of just `click_action` to prevent it from interfering with the notification functions deployed in Firebase Functions. – Maverick7 May 29 '18 at 20:53
7

This question is 2 years old. But is still relevant. This is how you could do it with the firebase console (without `click_action).

When your app is in background onMessageReceived will not be called. But when you get a notification while your app is in background, you will get an Intent along with the custom data you specify in the firebase console.

So when the user taps the notification the intent will then be executed to open your launcher activity. You can check for the data by getIntent().hasExtra("key") in your launcher activity. Where the "key" is whatever key you specify in the console.

Check if you have that "key" then, you can make another Intent and call startActivity

Here is an implementation of mine,

On my SplashActivity > OnCreate (This method Looks best if you have a splash screen as the launcher activity):

if (getIntent().hasExtra("key")){

      Intent intent = new Intent(this, TargetActivity.class);
      startActivity(intent);
      finish();

} else {
      startActivity(new Intent(this, MainActivity.class));
      finish();
}

This will just start the TargetActivity. You can add any functionality to this as per your wish :)

Ali
  • 166
  • 1
  • 8
1

I also have the trouble like u but i don't get any right answer that is the facts i know,

  1. onNewIntent

this override method is not working when the app is background coz of working default action is android.intent.action.MAIN

  1. Firebase Console

firebase console is not enough power to handle click_action that means click action can't not reach to application when app in background.

  1. CURL

Curl is working in app background but not foreground may be i can't find.

curl --header "Authorization: key=<Colud_Messaging_Server_Key_here>" --header Content-Type:"application/json" https://fcm.googleapis.com/fcm/send  -d "{\"to\":\"dwdfosu6SDs:APA91bFWxZZB2f8AOgP9MG504cy5AhFECHGbYFfIzVixoAfy-ErT0NYQrREg150CbKYBtk3Ywpu7WQuWsQhk-VmoOe-0iDK3ZgaFZNvYxDuixZB8zQp0zydoXhyrMosi5C4eyBb7Ai1z\",\"notification\": {\"title\": \"Click Action Message\",\"text\": \"Sample message\",\"click_action\":\"noti\",\"ticket_download\":\"noti\"}}"

Add Minifest

<activity
   android:name=".activity.TicketDownload"
   android:exported="true">
   <intent-filter>
      <action android:name="noti" />
      <category android:name="android.intent.category.DEFAULT" />
   </intent-filter>
</activity>

note : cloud_messaging_server_key is locate setting>cloudmessaging

if anything new facts pls let's me know.

1

I have used handleIntent(Intent intent) method to handle intent and move to that particular screen. Below is my code which is working perfectly even when the app is in background :

FCMMessagingService.java which extends FCMMessagingService

@Override
    public void handleIntent(Intent intent) {
        super.handleIntent(intent);

     Intent i = null;
     String value_action = "";

      if (intent.getExtras() != null) {
          if (key.equals("click_action")) {
                value_action = intent.getExtras().getString(key);
            }

          i = new Intent(FCMMessagingService.this, MainActivity.class);
          i.putExtra("notificationFlag", value_action);
          i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

      }
     PendingIntent pendingIntent = PendingIntent.getActivity(this, notifCount, i, PendingIntent.FLAG_ONE_SHOT);
        final int icon = R.mipmap.logo;
    Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);

    Notification notification;
    notification = notificationBuilder.setSmallIcon(icon).setTicker(title)
            .setAutoCancel(true)
            .setContentTitle(title)
            .setContentText(body)
            .setSound(defaultSoundUri)
            .setContentIntent(pendingIntent)
            .setSmallIcon(R.mipmap.notification_icon)
            .setLargeIcon(BitmapFactory.decodeResource(getResources(), icon))
            .build();


    notificationBuilder.setContentTitle(title);
    notificationBuilder.setContentText(body);
    notificationBuilder.setAutoCancel(true);
    notificationBuilder.setSound(defaultSoundUri);
    notificationBuilder.setContentIntent(pendingIntent);
    notificationBuilder.setLargeIcon(BitmapFactory.decodeResource(getResources(), icon));
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        notificationBuilder.setSmallIcon(R.mipmap.logo);
    } else {
        notificationBuilder.setSmallIcon(R.mipmap.logo);
    }

    NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(notifCount, notification);

}
R G
  • 461
  • 3
  • 15