0

I want to achieve a custom notification behavior like WhatsApp or any other app where users can interact with the notification in-tray, type a message and send it indirectly from there.

So, far I have been successful to show a custom notification with 2 buttons when the app is in the foreground. I achieved this by simply showing a custom notification when I get control in the firebase service's onMessageReceived() function.

According to my research and work, you do not get control in the service when app is in background or killed state.

So, the question is how do you achieve this? I want to show notification with a title, body, and 2 buttons. Upon clicking the buttons, I want to do 2 different tasks i.e calling APIs by opening the app.

Any help would be appreciated.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Syed Faizan Ali
  • 361
  • 1
  • 8
  • I think this [answer](https://stackoverflow.com/questions/48298993/push-notifications-on-content-change/48299840) might help. – Alex Mamo Feb 24 '22 at 12:48

2 Answers2

1

There are two type of payloads while sending push notification.

  1. Notification & data payload : if app is in killed/background state then notification payload will be handle by system tray to show notification and on click of notification, data payload will be received inside the intent of launcher activity.(getIntent().getExtras())

  2. Data payload : if contains only "data" payload then onMessageReceived() will be called for each state foreground/background/killed state. so you can create notification and click handling will also be from here by providing pending intent.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Ankit
  • 335
  • 1
  • 10
0

Have a look at Firebase SDK implementation.

  private void dispatchMessage(Intent intent) {
    Bundle data = intent.getExtras();
    if (data == null) {
      // The intent should always have at least one extra so this shouldn't be null, but
      // this is the easiest way to handle the case where it does happen.
      data = new Bundle();
    }
    // First remove any parameters that shouldn't be passed to the app
    // * The wakelock ID set by the WakefulBroadcastReceiver
    data.remove("androidx.content.wakelockid");
    if (NotificationParams.isNotification(data)) {
      NotificationParams params = new NotificationParams(data);

      ExecutorService executor = FcmExecutors.newNetworkIOExecutor();
      DisplayNotification displayNotification = new DisplayNotification(this, params, executor);
      try {
        if (displayNotification.handleNotification()) {
          // Notification was shown or it was a fake notification, finish
          return;
        }
      } finally {
        // Ensures any executor threads are cleaned up
        executor.shutdown();
      }

      // App is in the foreground, log and pass through to onMessageReceived below
      if (MessagingAnalytics.shouldUploadScionMetrics(intent)) {
        MessagingAnalytics.logNotificationForeground(intent);
      }
    }
    onMessageReceived(new RemoteMessage(data));
  }

And

/**
   * Handle a notification message by displaying it if appropriate, and returning whether the
   * message is consumed.
   * <li>If this is a no UI notification just used for analytics, doesn't show a notification and
   *     returns true.
   * <li>If the app is in the foreground, doesn't show a notification, and returns false.
   * <li>If the app is in the background, shows a notification and returns true.
   */
  boolean handleNotification() {
    if (params.getBoolean(MessageNotificationKeys.NO_UI)) {
      return true; // Fake notification, nothing else to do
    }

    if (isAppForeground()) {
      return false; // Needs to be passed to onMessageReceived
    }
    ImageDownload imageDownload = startImageDownloadInBackground();
    CommonNotificationBuilder.DisplayNotificationInfo notificationInfo =
        CommonNotificationBuilder.createNotificationInfo(context, params);
    waitForAndApplyImageDownload(notificationInfo.notificationBuilder, imageDownload);
    showNotification(notificationInfo);
    return true;
  }

  public static boolean isNotification(Bundle data) {
    return "1".equals(data.getString(MessageNotificationKeys.ENABLE_NOTIFICATION))
        || "1"
            .equals(data.getString(keyWithOldPrefix(MessageNotificationKeys.ENABLE_NOTIFICATION)));
  }

From the implementation, you can see the app only have control if:

  • The message is not push notification (which is data message)
  • The app is running in foreground.

Otherwise, Firebase SDK will take over and manipulate the notification. That's a weird design from Firebase because it could lead to inconsistency notification when the app in background and foreground. Firebase also mentioned it here but not so obvious.

Then, the solutions:

  • Ask backend side to change message to data message instead of notification message.
  • Or, add a hack to by past the condition from firebase sdk which makes NotificationParams.isNotification(data) return false
ThaiPD
  • 3,503
  • 3
  • 30
  • 48