Push notifications

The Layer Push Notification Service can be used to keep your app’s data updated.

Setting up push with Layer

The Layer Android SDK receives pushes through both the Layer push protocol and Firebase Cloud Messaging (FCM), depending on usage and other conditions. In order to enable your application to receive FCM push notifications, some setup must be performed in both the Firebase Developer Console and the Layer developer portal.

Note

Existing GCM projects are still supported. For setup instructions, see this section. This is possible because FCM uses GCM internals. Because all new features will be added to FCM, it is recommended to migrate projects to Firebase. See here for more information.

Setup FCM on the web

Go to the Firebase Developer Console and click the Create New Project button. You may also import an existing google project by clicking the or import a Google project link.

Name your project and click the Create Project button.

Click the Add Firebase to your Android app button.

Enter your app’s package name and click the Add App button.

Follow the instructions to move the downloaded google-services.json file into your app. Then click the Continue button.

Follow the instructions to modify the build.gradle files. Then click the Finish button.

Now retrieve the Server API key and the Sender ID. You can do this by clicking the overflow button on your project and clicking the Manage item.

Select the Cloud Messaging tab and note the Server key and Sender ID fields. These will be entered into the Layer Dashboard in the following section.

Setup GCM on the web

If you have an existing GCM project and do not wish to convert it to a Firebase project, you can create the google-services.json using the following steps. Go to the Add Google Services page and click the Pick a platform button.

Click the Enable services for my Android App button.

Choose your app from the existing apps in the App name dropdown. If you are creating an app you should follow the Firebase setup steps in the previous section. Also choose the correct package name in the Android package name dropdown. Click the Choose and configure services button.

Click the Cloud Messaging icon. Then click the Enable Google Cloud Messaging button.

Note the Server API Key and Sender ID fields. These will be entered into the Layer Dashboard in the following section. Click the Generate configuration files button.

Click the Download google-services.json button. Move the downloaded google-services.json file into your app/ directory.

Add the google-services dependency to your project-level build.gradle file:

classpath 'com.google.gms:google-services:3.0.0'

Apply the google-services plugin in your app-level build.gradle file:

apply plugin: 'com.google.gms.google-services'

Setup FCM in the Layer Developer Dashboard

Navigate to the Layer Developer Portal and login with your credentials. Select the application for which you would like to upload certificates from the Application drop-down menu. Click on the Push section of the left hand navigation.

Click the Configure for Android button.

If you haven’t already, create your project in the Firebase Developers Console and configure your credentials for FCM. Make a note of the Sender ID and Server API key. Otherwise, skip to the next step (“Add Credentials”).

  • Project Number: the Sender ID from your Firebase Developers Console project.
  • API Key: the Server key from your Firebase Developers Console project.

When your app is in the background, the LayerClient alerts you to pushes via a broadcast Intent with the com.layer.sdk.PUSH action. Your BroadcastReceiver can then create and post the actual UI notification, or take another action.

Note

Layer supports up to 5 different FCM credentials per project. This makes it easy to use different Google projects for development, testing, and production purposes.

Registering your app to receive notifications

Add the FCM dependency to your build.gradle file:

compile 'com.google.firebase:firebase-messaging:10.0.1'

Add the LayerFcmService and LayerFcmInstanceIdService to your AndroidManifest.xml:

<service
    android:name="com.layer.sdk.services.LayerFcmService">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>
<service
    android:name="com.layer.sdk.services.LayerFcmInstanceIdService">
    <intent-filter>
        <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
    </intent-filter>
</service>

Ensure the generated google-services.json file is project’s module folder (typically app/). Set the SDK to use FCM as follows:

LayerClient.Options options = new LayerClient.Options();
options.useFirebaseCloudMessaging(true);
LayerClient layerClient = LayerClient.newInstance(context, "[your APP ID]", options);

Note

Even when using GCM instead of FCM, still call options.useFirebaseCloudMessaging(true) to ensure push notitifications are enabled in the SDK.

Multiple FCM senders in the same app

Google does support multiple FCM senders in a single app. If the desired sender for Layer messages is defined in the google-services.json file then no additional work is needed. However, if a sender other than the one defined in google-services.json should be used for Layer messages, simply set the desired Sender ID on LayerClient.Options as follows:

ayerClient.Options options = new LayerClient.Options();
options.useFirebaseCloudMessaging(true);
options.alternateFcmSenderId("alternate_sender_ID");
LayerClient layerClient = LayerClient.newInstance(context, "[your APP ID]", options);

The Layer SDK fetches the registration token for this Sender ID and handles sending it to Layer servers. Ensure this Sender ID and associated Server API Key have been saved on Layer’s developer console. You can still fetch the token for the primary Sender ID (defined in google-services.json) by calling FirebaseInstanceId.getInstance().getToken().

GCM support

While recommended, it is not required to update your app to Firebase. Follow the same steps as registering for FCM, using the google-services.json file generated from Setup GCM on the web section.

Sending push notifications

The sending client can now generate push notifications by specifying special options when sending the message:

private void sendTextMessage(String text) {

    //Put the text into a message part, which has a MIME type of "text/plain" by default
    MessagePart messagePart = layerClient.newMessagePart(text);

    //Formats the push notification text
    MessageOptions options = new MessageOptions();
    PushNotificationPayload payload = new PushNotificationPayload.Builder()
        .text(MainActivity.getUserID() + ": " + text)
        .build();
    options.defaultPushNotificationPayload(payload);

    //Creates and returns a new message containing the message parts
    Message message = layerClient.newMessage(options, Arrays.asList(messagePart));

    //Sends the message
    mConversation.send(message);
}

Receiving push notifications

Receiving clients access push notifications via broadcast Intents. It is up to your app to implement a BroadcastReceiver for generating and posting notifications from these Intents, which contain the following extras:

  • layer-conversation-id: A parceled URI representing the conversation ID associated with the pushed message.
  • layer-message-id: A parceled URI representing the message ID associated with the pushed message.
  • Extra strings defined by keys in PushNotificationPayload. These can be queried directly or by using the helper method PushNotificationPayload.fromLayerPushExtras().
// Example BroadcastReceiver
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;

import com.layer.sdk.messaging.Message;
import com.layer.sdk.messaging.PushNotificationPayload;

public class LayerPushReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

        //Don't show a notification on boot
        if(intent.getAction() == Intent.ACTION_BOOT_COMPLETED)
            return;

        // Get notification content
        Bundle extras = intent.getExtras();
        PushNotificationPayload payload = PushNotificationPayload.fromLayerPushExtras(extras);
        Uri conversationId = null;
        if (extras.containsKey("layer-conversation-id")) {
            conversationId = extras.getParcelable("layer-conversation-id");
        }

        // Build the notification
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
                .setSmallIcon(R.drawable.layer_launcher)
                .setContentTitle(context.getResources().getString(R.string.app_name))
                .setContentText(payload.getText())
                .setAutoCancel(true)
                .setLights(context.getResources().getColor(R.color.blue), 100, 1900)
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setDefaults(NotificationCompat.DEFAULT_SOUND | NotificationCompat.DEFAULT_VIBRATE);

        // Set the action to take when a user taps the notification
        Intent resultIntent = new Intent(context, ConversationsActivity.class);
        resultIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        resultIntent.putExtra("layer-conversation-id", conversationId);
        PendingIntent resultPendingIntent = PendingIntent.getActivity(context, 0, resultIntent, PendingIntent.FLAG_CANCEL_CURRENT);
        mBuilder.setContentIntent(resultPendingIntent);

        // Show the notification
        NotificationManager mNotifyMgr = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        mNotifyMgr.notify(1, mBuilder.build());
    }
}

Note that this BroadcastReceiver must filter for the com.layer.sdk.PUSH action. One way to do so is through your app’s AndroidManifest.xml (replace com.myapp.package with your own package name):

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

    <!-- Standard permissions -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.INTERNET"/>

    <!-- Signature-only permissions -->
    <permission android:name="com.myapp.package.permission.LAYER_PUSH"
       android:protectionLevel="signature"/>
    <uses-permission android:name="com.myapp.package.permission.LAYER_PUSH"/>

    <!-- LayerClient.sendLogs() permissions -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_LOGS"/>

    <application>
       <!-- Your custom "com.layer.sdk.PUSH" notification Receiver -->
       <receiver android:name=".LayerPushReceiver">
          <intent-filter>
             <action android:name="com.layer.sdk.PUSH"/>
             <category android:name="com.myapp.package"/>
          </intent-filter>
       </receiver>

       <!-- Layer's FCM Services -->
       <service
           android:name="com.layer.sdk.services.LayerFcmService">
           <intent-filter>
               <action android:name="com.google.firebase.MESSAGING_EVENT" />
           </intent-filter>
       </service>
       <service
           android:name="com.layer.sdk.services.LayerFcmInstanceIdService">
           <intent-filter>
               <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
           </intent-filter>
       </service>
    </application>
</manifest>

You can use your Layer Dashboard log to help debug push notifications from the Layer server’s perspective.

Receiving non-Layer push notifications

No additional work needs to be done if your app only supports Layer push notifications. However, if push notifications can come from your server or a third-party service, please follow the steps below.

Ensure the declared FirebaseMessagingService extends from LayerFcmService and add the following code to onMessageReceived():

public class MyFirebaseMessagingService extends LayerFcmService {

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        if (isLayerMessage(remoteMessage)) {
            // Handle in Layer SDK
            super.onMessageReceived(remoteMessage);
            return;
        }
        // Handle non-Layer message
    }
}

If extension is not possible or desirable, use the following code to ensure Layer messages are handled:

public class MyFirebaseMessagingService extends FirebaseMessagingService {

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        if (LayerFcmService.handleMessageReceived(remoteMessage, this)) {
            // Layer SDK handled the push
            return;
        }
        // Handle non-Layer message
    }
}

Similarly, ensure the declared FirebaseInstanceIdService extends from LayerFcmInstanceIdService and call super.onTokenRefresh() in onTokenRefresh(). If extension is not possible or desirable, use the following code the ensure Layer servers are updated with the new registration ID:

public class MyFirebaseInstanceIdService extends FirebaseInstanceIdService {

    @Override
    public void onTokenRefresh() {
        LayerFcmInstanceIdService.handleTokenRefresh(this);
        // Update your servers with new token
    }
}

Supporting existing GCM implementation

Existing GCM implementations are supported both on their own and alongside an FCM implementation. The Layer SDK only uses the FCM library so some care must be taken with this scenario.

If converting to a FirebaseMessagingService is not possible, keep the GcmReceiver and your custom GcmListenerService declarations in the manifest. This will allow your existing GcmListenerService to keep receiving intents alongside the FirebaseMessagingService. The behavior for the existing InstanceIDListenerService is a bit different. Token refreshes will only be sent to the FirebaseInstanceIdService so you should still handle all token refreshes in that class.

This behavior is subject to change with newer FCM library versions so these implementations should be thoroughly tested to ensure functionality remains as desired. It is recommended to migrate all GCM projects to FCM projects for this reason.

Please use at least version 10.0.1 of the Firebase library as it has fixed some conflicts with token generation when used alongside the GCM library.

Troubleshooting and Integration Checklist

Work through the following checklist to verify that all integration steps have been completed:

  • Correct Server key and Sender ID are entered in the Layer developer dashboard
  • Project’s app/ folder contains the google-services.json file
  • Root-level build.gradle file contains the google-services dependency
  • Module-level build.gradle file (typically app/build.gradle) contains the firebase-messaging dependency and applies the google-services plugin
  • LayerFcmService (or custom class with calls to LayerFcmService) declared in the app’s manifest with a com.google.firebase.MESSAGING_EVENT action intent filter
  • LayerFcmInstanceIdService (or custom class with calls to LayerFcmInstanceIdService) declared in the app’s manifest with a com.google.firebase.INSTANCE_ID_EVENT action intent filter
  • Appropriate permissions for com.myapp.package.permission.LAYER_PUSH declared in the manifest
  • A BroadcastReceiver declared in the manifest with a com.layer.sdk.PUSH action intent filter
  • Set broadcastPushInForeground to true if you want to broadcast PUSH intents even while the app is in the foreground

If problems still arise, perform the following steps to ensure the push notification is sent by Layer servers:

  • Ensure logging is enabled by calling LayerClient.setLoggingEnabled(context, true)
  • Successfully registered FCM registrationId with Layer Server is logged after authentication
  • Failed to register FCM registrationId with Layer Server: <cause> will be logged if FCM registration failed
  • Verify a PushNotificationPayload with at least a text value is set on the MessageOptions object
  • Monitor the logs in the Layer developer dashboard while sending a message
    • Following the Message: Created message with identifier log entry there should be a Push: Sent gcm push notification with text '<payload_text>' from <sender_user_id> to <recipient_user_id> log entry
  • On the receiving device, verify that Processing Layer FCM push... is logged after it is known that the server sent the GCM/FCM push
    • If that entry is not logged then check for other logs from LayerFcmService as it may have logged why the push notification was not processed

We also recommend reviewing the FCM documentation as well as the GCM documentation if supporting a legacy implementation.

Synchronization Deleting