Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notifee does not support extra params from Android intent back to JS #1081

Open
aureosouza opened this issue Aug 12, 2024 · 7 comments · May be fixed by #1141
Open

Notifee does not support extra params from Android intent back to JS #1081

aureosouza opened this issue Aug 12, 2024 · 7 comments · May be fixed by #1141

Comments

@aureosouza
Copy link

@helenaford based on discussions from here and using similar solution as here. we are trying to fetch the custom data on Android overriding the handleIntent method from ReactNativeFirebaseMessagingService (react-native-firebase). As we won't be sending data only notifications to solve this currently, so just like iOS, we want to try to get from same notification:

public class ReactNativeFirebaseMessagingService extends FirebaseMessagingService {
...
@Override
  public void handleIntent(Intent intent) {
    Bundle extras = intent.getExtras();

    try {
      RemoteMessage message = new RemoteMessage(extras);
      RemoteMessage.Notification notificationData = message.getNotification();

      // Short-circuit if this is not a display notification
      if (notificationData == null) {
        super.handleIntent(intent);
        return;
      }

      // 1. Filter the notification as needed using data payload, clickAction, etc.
      if (message.getData().containsKey("mydata")) {
        // 2. Manually display notifications that match your filter
        Map<String, String> data = message.getData();

        // Create an intent that will be triggered when the user clicks the notification
        for (Map.Entry<String, String> entry : data.entrySet()) {
          intent.putExtra(entry.getKey(), entry.getValue());
        }
        super.handleIntent(intent);
        return;
      }

    } catch (Exception e) {
      super.handleIntent(intent);
      return;
    }

    // 3. Fall-through to this for everything else
    super.handleIntent(intent);
  }
  ...
}

And we are able to see our data correctly when logging in native, when adding extras to intent here intent.putExtra(entry.getKey(), entry.getValue());. The problem is that when we call on JS side:

  import notifee from '@notifee/react-native';
  ...
  const notifications = await notifee.getDisplayedNotifications();

Our notifications does not have the data params, we did some investigation on notifee code and noticed when calling getDisplayedNotifications on native module we fetch this List:

public static List b() throws Exception {
       ArrayList var0;
       var0 = new ArrayList.<init>();
       if (VERSION.SDK_INT < 23) {
           return var0;
       } else {
           StatusBarNotification[] var1;
           int var2 = (var1 = ((NotificationManager)e.a.getSystemService("notification")).getActiveNotifications()).length;

           for(int var3 = 0; var3 < var2; ++var3) {
               StatusBarNotification var4;
               Notification var5;
               Bundle var6;
               Bundle var10000 = var6 = (var5 = (var4 = var1[var3]).getNotification()).extras;
               Bundle var7;
               var7 = new Bundle.<init>();
               Bundle var8;
               var10000 = var8 = var10000.getBundle("notifee.notification");
               Bundle var9 = var6.getBundle("notifee.trigger");
               if (var10000 == null) {
                   Bundle var10001 = var8 = new Bundle;
                   var10001.<init>();
                   var10001.putString("id", "" + var4.getId());
                   Object var10;
                   if ((var10 = var6.get("android.title")) != null) {
                       var8.putString("title", var10.toString());
                   }

                   if ((var10 = var6.get("android.text")) != null) {
                       var8.putString("body", var10.toString());
                   }

                   Object var11;
                   if ((var11 = var6.get("android.subText")) != null) {
                       var8.putString("subtitle", var11.toString());
                   }

                   var6 = new Bundle.<init>();
                   if (VERSION.SDK_INT >= 26) {
                       var6.putString("channelId", var5.getChannelId());
                   }

                   var6.putString("tag", var4.getTag());
                   var6.putString("group", var5.getGroup());
                   var8.putBundle("android", var6);
                   var7.putString("id", "" + var4.getId());
               } else {
                   var7.putString("id", "" + var8.get("id"));
               }

               if (var9 != null) {
                   var7.putBundle("trigger", var9);
               }

               var7.putBundle("notification", var8);
               var7.putString("date", "" + var4.getPostTime());
               var0.add(var7);
           }

           return var0;
       }
   }

And there is only a strict specific set of variables being sent using putString and putBundle. Is there a way for notifee to pass any extra params from intent back to JS, if they exist?

@aureosouza
Copy link
Author

FYI we had to implement a patch for this, we've added an extra fetchNotificationData to getDisplayedNotifications module method, that injects the data param from firebase (if it exists):

private List<Bundle> fetchNotificationData(List<Bundle> aBundleList) {
    NotificationManager notificationManager = (NotificationManager) getReactApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
    if (notificationManager != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
      StatusBarNotification[] activeNotifications = notificationManager.getActiveNotifications();

      for (StatusBarNotification sbn : activeNotifications) {
        Notification notification = sbn.getNotification();
        Bundle extras = notification.extras;

        if (extras != null) {
          Bundle data = extras.getBundle("data");
          if (data != null) {
            for (Bundle originalBundle : aBundleList) {
              Bundle originalNotificationBundle = originalBundle.getBundle("notification");
              if (originalNotificationBundle != null && originalNotificationBundle.getString("id").equals(String.valueOf(sbn.getId()))) {
                originalNotificationBundle.putBundle("data", data);
              }
            }
          }
        }
      }
    }
    return aBundleList;
  }


  @ReactMethod
  public void getDisplayedNotifications(Promise promise) {
    Notifee.getInstance()
      .getDisplayedNotifications(
        (e, aBundleList) -> NotifeeReactUtils.promiseResolver(promise, e, fetchNotificationData(aBundleList)));
  }

And firebase needs to inject the data param as well, for this we added a handleIntent override for the class that extends FirebaseMessagingService (in our case we use react-native-firebase, so that would be ReactNativeFirebaseMessagingService):

private void createNotificationChannel() {
    // Same as Firebase SDK default channel name and ids
    NotificationChannel channel = new NotificationChannel("fcm_fallback_notification_channel", "Miscellaneous", NotificationManager.IMPORTANCE_HIGH);
    NotificationManager notificationManager = getSystemService(NotificationManager.class);
    notificationManager.createNotificationChannel(channel);
  }

  private int getNotificationIcon() {
    int iconResId;
    iconResId = getResources().getIdentifier("ic_notification", "drawable", getPackageName());
    if (iconResId == 0) {
      iconResId = getApplicationInfo().icon;
    }

    return iconResId;
  }

  @Override
  public void handleIntent(Intent intent) {
    Bundle extras = intent.getExtras();

    try {
      RemoteMessage message = new RemoteMessage(extras);
      RemoteMessage.Notification notificationData = message.getNotification();

      if (notificationData == null) {
        super.handleIntent(intent);
        return;
      }

      Bundle customExtras = new Bundle();
      for (Map.Entry<String, String> entry : message.getData().entrySet()) {
        customExtras.putString(entry.getKey(), entry.getValue());
      }
      createNotificationChannel();

      NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "fcm_fallback_notification_channel")
        .setSmallIcon(getNotificationIcon())
        .setContentTitle(notificationData.getTitle())
        .setContentText(notificationData.getBody())
        .setAutoCancel(true)
        .setPriority(NotificationCompat.PRIORITY_HIGH);

      notificationBuilder.getExtras().putBundle("data", customExtras);
      NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
      // Same as tag from Firebase SDK is built
      notificationManager.notify("FCM-Notification:" + SystemClock.uptimeMillis(), 0, notificationBuilder.build());

    } catch (Exception e) {
      super.handleIntent(intent);
    }
  }

Copy link

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Oct 8, 2024
@mikehardy
Copy link
Collaborator

Saw this come through me feed as a close - @aureosouza if you proposed PRs for this (maybe you have but I haven't seen them?) this looks like something we could integrate in react-native-firebase + notifee

@aureosouza aureosouza linked a pull request Nov 8, 2024 that will close this issue
@aureosouza
Copy link
Author

@mikehardy added a PR here #1141, thanks!

@mikehardy
Copy link
Collaborator

That's fantastic @aureosouza thank you! Please have patience with me on the review (and hopefully merge+release) - got a lot of repos and new arch in rn76 + xcode release / toolchain build issues set me back a bit

@mikehardy mikehardy reopened this Nov 25, 2024
@aureosouza
Copy link
Author

Any luck with the review @mikehardy?

@mikehardy
Copy link
Collaborator

😆 after quite an odyssey upstream getting react-native 0.76 to work with headlessJS after some regressions with the switch to new architecture

quite a bit more process then I remembered (it's been a while...) setting up new firebase projects and apple developer keys and certs and stuff I'm now 🤏 to having notifee be really testable again

All the PRs here are queued up on that and about to unspool

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants