0

Android 7.0 introduced the ability for users to enter text directly into a notification in order to respond to it, without opening the app. I am using the react-native-firebase project in order to receive push notifications in my React Native app.

Based on the documentation it seems like this functionality is supported -- specifically, AndroidNotification.addAction and AndroidAction.addRemoteInput would seem to indicate that this is possible.

However, I can't find any examples on how to correctly implement this feature. Is this feature supported in a React Native project using react-native-firebase?

Donut
  • 110,061
  • 20
  • 134
  • 146

2 Answers2

4

Yes, this is possible:

  1. Update your AndroidManifest.xml file to include the following:

    <receiver android:name="io.invertase.firebase.notifications.RNFirebaseBackgroundNotificationActionReceiver" android:exported="true">
        <intent-filter>
            <action android:name="io.invertase.firebase.notifications.BackgroundAction"/>
        </intent-filter>
    </receiver>
    <service android:name="io.invertase.firebase.notifications.RNFirebaseBackgroundNotificationActionsService"/>
    
  2. Add a .js file (e.g., backgroundMessaging.js) containing the following:

    import firebase from 'react-native-firebase'
    export const backgroundMessageListener = async (message) => {
        const notification = new firebase.notifications.Notification()
    
        // TODO: Configure your notification here...
    
        // https://rnfirebase.io/docs/v4.3.x/notifications/reference/AndroidAction
        const action = new firebase.notifications.Android.Action('reply', 'ic_launcher', 'Reply')
    
        action.setShowUserInterface(false)
    
        // https://rnfirebase.io/docs/v4.0.x/notifications/reference/AndroidRemoteInput
        const remoteInput = new firebase.notifications.Android.RemoteInput("input")
        remoteInput.setLabel('Reply')
        action.addRemoteInput(remoteInput)
    
        notification.android.addAction(action)
    
        firebase.notifications().displayNotification(notification)
    
        return Promise.resolve()
    }
    
    export const backgroundActionHandler = async (notificationOpen) => {
        if (notificationOpen.action === 'reply') {
            // TODO: Handle the input entered by the user here...
            console.log(notificationOpen);
        }
    
        return Promise.resolve();
    };
    
  3. Update index.js as follows:

    import { backgroundMessageListener, backgroundActionHandler } from './backgroundMessaging'
    
    AppRegistry.registerHeadlessTask('RNFirebaseBackgroundMessage', () => backgroundMessageListener)
    AppRegistry.registerHeadlessTask('RNFirebaseBackgroundNotificationAction', () => backgroundActionHandler);
    

Notes:
This example assumes that you've already configured react-native-firebase and followed the setup guide here. The backgroundMessageListener function will get called when your app is not in the foreground and a "data" notification is received. The Receiving Notifications has examples on how to perform additional setup such as requesting permission to receive notifications.

Donut
  • 110,061
  • 20
  • 134
  • 146
  • Not showing actions in android when app is in background or app is closed .Can you please tell me exactly how can i achieve same from react-native firebase . Thanks in advance – Kartik Shah Apr 23 '19 at 09:37
  • I've done this and it works perfectly, except when I submit the input in the notification it loads forever, even though I call `Promise.resolve()` – Pal Kerecsenyi Aug 24 '19 at 09:49
  • @PalKerecsenyi see my answer. – Rohit Hazra Feb 26 '20 at 21:06
2

To add to what @Donut has answered, things have changed a bit now.

AndroidManifest.xml (remains unchanged as per @Donut)

<receiver android:name="io.invertase.firebase.notifications.RNFirebaseBackgroundNotificationActionReceiver" android:exported="true">
    <intent-filter>
        <action android:name="io.invertase.firebase.notifications.BackgroundAction"/>
    </intent-filter>
</receiver>
<service android:name="io.invertase.firebase.notifications.RNFirebaseBackgroundNotificationActionsService"/>

It is recommended that you add a channel-id here (i.e. app-local-notifications)

<meta-data
 android:name="com.google.firebase.messaging.default_notification_channel_id"
            android:value="@string/default_notification_channel_id"/>

Your backgroundMessaging.js should now be

import firebase from 'react-native-firebase'

const channelId = 'app-local-notifications'

export const backgroundMessageListener = async (message) => {
    const channel = new firebase.notifications.Android.Channel(
        channelId,
        'Local Interactive Notifications',
        firebase.notifications.Android.Importance.Max
    ).setDescription('Local Interactive Notifications');
    firebase.notifications().android.createChannel(channel);

    const localNotification = new firebase.notifications.Notification({
        sound: 'default' //important
    })
        .setNotificationId(message.messageId)
        .setTitle(message.data.title)
        .setBody(message.data.body)
        .android.setChannelId(channelId) //important
        .android.setSmallIcon('ic_launcher')
        .android.setPriority(firebase.notifications.Android.Priority.High); //important

    const action = new firebase.notifications.Android.Action('reply', 'ic_launcher', 'Reply')
    action.setShowUserInterface(false)

    const remoteInput = new firebase.notifications.Android.RemoteInput("inputText")
    remoteInput.setLabel('Message')
    action.addRemoteInput(remoteInput)

    localNotification.android.addAction(action)
    firebase.notifications().displayNotification(localNotification).catch(err => console.log(err)); //important
}

export const backgroundActionHandler = async (notificationOpen) => {
    if (notificationOpen && notificationOpen.notification) {
        const action = notificationOpen.action;
        const notificationId = notificationOpen.notification.notificationId;
        if (action === "reply") {
            console.log(notificationOpen)
        } else {
            console.log("unsupported action", action);
        }
        // hide the notification instead of Promise.resolve()
        firebase.notifications().removeDeliveredNotification(notificationId); //important
    }
};

Things to note: 1. Notification sent via FCM should be data-only notifications. 2. Notification priority should be "high". 3. Notification action must not take longer than 60s. 4. ChannelID is required. 5. Notification sound should be default.

Rohit Hazra
  • 657
  • 9
  • 27