Using the Android App Messaging SDK

A tutorial for adding the Android App Messaging SDK code to your app to allow your app users to receive push notifications from your Dotdigital account

Our Android App Messaging SDK uses Firebase Cloud Messaging (FCM) to send push notifications to your Android app users. If you haven't already configured a Firebase project then please follow the instructions here first.

Our Android SDK is open source and can be found on Github here:

Basic sample apps can be found in Github here:

To embed in your native Android apps, complete the following tasks:

  1. Install the SDK
  2. Prepare the Push and Challenge handler classes
  3. Initialise the SDK
  4. Handle push message callbacks
  5. Optional: Change the icon or colour of your push notifications
  6. Starting a session
📘

Handling asynchronous requests

You can use either standard callbacks or observable streams to handle asynchronous requests. If you want to use observable streams, you need to add the RxJava library to your app. This tutorial explains how to use standard callbacks, but you can find a full sample of the RxJava code in the 'Android sample code' section.

Installing the SDK

  1. Add the App Messaging SDK to your module-level build.gradle file for Java or build.gradle.kts for Kotlin and change the minimum SDK version attribute to 21 and include the dependency for com.comapi:foundation as shown in the snippets below:
defaultConfig {
  ...
  minSdkVersion 21
  ...
}
Dependencies {
  ...
  implementation 'com.comapi:foundation:1.6.0'
}
android {
	...
  defaultConfig {
		...
  	minSdk = 21
  	...
	}
}

dependencies {
  implementation("com.comapi:foundation:1.6.0")
  ...
}
🚧

Seeing a 'Manifest merger' error?

The SDK has a minimum Android SDK version of 21, your app must support this also as a minimum level.

  1. Add the following dependencies to your top level build.gradle file:
buildscript {
    dependencies {
        classpath 'com.google.gms:google-services:4.4.1'
    }
}
buildscript {
    dependencies {
        classpath("com.google.gms:google-services:4.4.1")
    }
}
  1. Add the following plugins to your module level build.gradle file:
plugins {
    id "com.google.gms.google-services" version "4.4.1" apply false
}
plugins {
    id("com.google.gms.google-services") version "4.4.1" apply false
}
  1. Copy the google-services.json file from your Firebase project into your apps root folder; this is used to configure your app to work with Firebase.
👍

Sync Now

Remember to synchronise build.gradle files after making any changes.

Prepare the Push and Challenge handler classes

Before you can configure the Android SDK, you need the following:

  1. The value of the API space ID field in Dotdigital
  2. A class that creates a JWT token
  3. Create a placeholder push handler class which implements the PushMessageListener interface to facilitate callbacks. The PushHandler is the class we passed instance of into the SDK init method. Inside we will get push messages delivered when the app is visible.
import com.comapi.internal.push.PushMessageListener;
import com.google.firebase.messaging.RemoteMessage;

public class PushHandler implements PushMessageListener {
    @Override
    public void onMessageReceived(RemoteMessage message) {
        // TODO: Add push message handling such as displaying messages when in foreground and
        // handling deep links and custom data
    }
}
import com.comapi.internal.push.PushMessageListener
import com.google.firebase.messaging.RemoteMessage

class PushHandler : PushMessageListener {
    override fun onMessageReceived(message: RemoteMessage) {
        // TODO: Add push message handling such as displaying messages when in foreground and
        // handling deep links and custom data
    }
}

Initialising the Android SDK

🚧

Where to initialise the Android SDK in the activity lifecycle

Initialisation needs to be performed in the onCreate() of the Android Application class so that your app users' profiles don't change every time they open the app. Remember to set that class in the manifest file <application android:name=".MyApplicationClass">

  1. After you've configured the SDK, import the com.google.firebase.FirebaseApp class into your file, and initialise the Firebase SDK.
FirebaseApp.initializeApp(this);
FirebaseApp.initializeApp(this)
🚧

Firebase must be initialised before the SDK

Please ensure the FirebaseApp.initializeApp(this); is called prior to initialising the SDK as it is a dependency of the SDK.

  1. Create a new instance of the ComapiConfig class, pass your API Space Id to the apiSpaceId() method, and an instance of the class that creates a JWT to the authenticator() method using the snippet below:
    import com.comapi.ComapiConfig;
    
    ComapiConfig config = new ComapiConfig()
        // Set the id of the app space, the device belongs to
        .apiSpaceId("<API_SPACE_ID>")
        // Sets handler for authentication challenges (SDK asking for JWT token)
        .authenticator(new ChallengeHandler())
        // When we are initialising the SDK in the Application class we will need to provide pushMessageListener to listen for push notification delivered when the app is visible to the user.
        // In which case Android won't display notification in the system tray.
        .pushMessageListener(new PushHandler());
    import com.comapi.ComapiConfig
    
    val config = ComapiConfig()
        // Set the id of the app space, the device belongs to
        .apiSpaceId("<API_SPACE_ID>")
        // Sets handler for authentication challenges (SDK asking for JWT token)
        .authenticator(ChallengeHandler())
        // When we are initialising the SDK in the Application class we will need to provide pushMessageListener to listen for push notification delivered when the app is visible to the user.
        // In which case Android won't display notification in the system tray.
        .pushMessageListener(PushHandler())

    Configuring logs and proxy servers

    You can set internal file logs, console logs, and network logs to the following levels. By default all log levels are set to WARNING:
    • OFF
    • FATAL
    • ERROR
    • WARNING
    • INFO
    • DEBUG To change the log levels, add the following line to the ComapiConfig object and use the appropriate method on the LogConfig object:
    .logConfig(new LogConfig().setFileLevel(LogLevel.DEBUG).setConsoleLevel(LogLevel.INFO).setNetworkLevel(LogLevel.ERROR));
    .logConfig(
        LogConfig()
            .setFileLevel(LogLevel.DEBUG)
            .setConsoleLevel(LogLevel.INFO)
            .setNetworkLevel(LogLevel.ERROR)
    )
    You can also set a custom limit for the size of internal log files by calling the following method on the ComapiConfig object:
    .logSizeLimitKilobytes(limit);
    .logSizeLimitKilobytes(limit)
    If your app connects through a proxy server, add the following line to the ComapiConfig object:
    .apiConfiguration(new APIConfig().proxy("http://xx.xxx.xx.xxx:xxxx"));
    .apiConfiguration(APIConfig().proxy("http://xx.xxx.xx.xxx:xxxx"))

  2. Pass the ComapiConfig object to one of the following initialisation methods. These methods return a ComapiClient object that you can use to access the session and profile services, which are used to create and update the user's profile.
  • The initialiseShared() method: When you use this method, the SDK stores the ComapiClient object, and you can access it at any time by calling the Comapi.getShared() method (Java) or the RxComapi.getShared() method (RxJava)
  • The initialise() method: When you use this method, you need to store the ComapiClient object yourself. The getShared() method is not available when you use this method.
package com.example.testapp;

import android.app.Application;
import com.comapi.Callback;
import com.comapi.Comapi;
import com.comapi.ComapiClient;
import com.comapi.ComapiConfig;
import com.google.firebase.FirebaseApp;

public class MyApplication extends Application implements Callback<ComapiClient> {

    @Override
    public void onCreate() {
        super.onCreate();

        FirebaseApp.initializeApp(this);

        ComapiConfig config = new ComapiConfig()
            // Set the id of the app space, the device belongs to
            .apiSpaceId("<API_SPACE_ID>")
            // Sets handler for authentication challenges (SDK asking for JWT token)
            .authenticator(new ChallengeHandler())
            // When we are initialising the SDK in the Application class we will need to provide pushMessageListener to listen for push notification delivered when the app is visible to the user.
            // In which case Android won't display notification in the system tray.
            .pushMessageListener(new PushHandler());

        // Asynchronously initialise Foundation SDK client (retrieve it in callback).
        // Shared makes ComapiClient accessible from Comapi.getShared()
        // The last 'this' passed requires your class to implement Callback<ComapiClient> which will be invoked when the SDK has initialised
        Comapi.initialiseShared(this, config, this);
    }

    // This is a callback for successful initialisation. We need to send a message to Activities
    // that SDK is initialised and the client is available.
    @Override
    public void success(ComapiClient client) {
        // SDK initialised
    }

    @Override
    public void error(Throwable t) {
        // Error when initialising SDK
    }
}
package com.example.testapp

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.comapi.Callback
import com.comapi.Comapi
import com.comapi.ComapiClient
import com.comapi.ComapiConfig
import com.google.firebase.FirebaseApp

class MainActivity : AppCompatActivity(), Callback<ComapiClient> {

    override fun onCreate() {
      super.onCreate()
      
			// Init Firebase, must happen before the SDK init
      FirebaseApp.initializeApp(this)
      	
      // Set the configuration for the SDK
      val config = ComapiConfig()
      	// Set the id of the app space, the device belongs to
        .apiSpaceId("<API_SPACE_ID>")
      	// Sets handler for authentication challenges (SDK asking for JWT token)
        .authenticator(ChallengeHandler())
      	// When we are initialising the SDK in the Application class we will need to provide pushMessageListener to listen for push notification delivered when the app is visible to the user.
      	// In which case Android won't display notification in the system tray.
        .pushMessageListener(PushHandler())

        // Asynchronously initialise Foundation SDK client (retrieve it in callback).
        // Shared makes ComapiClient accessible from Comapi.getShared()
        // The last 'this' passed requires your class to implement Callback<ComapiClient> which will be invoked when the SDK has initialised
        Comapi.initialiseShared(application, config, this)
    }

    // This is a callback for successful initialisation. We need to send a message to Activities
    // that SDK is initialised and the client is available.
    override fun success(client: ComapiClient) {
        // SDK initialised
    }

    override fun error(t: Throwable) {
        // Error when initialising SDK
    }
}
  1. Request permissions to receive and display push messages, see the functions below to achieve this; simply call askPermission() in your onCreate event handler, or when you would like to request the permission:
        // Starting from Android 13 (API level 33) we need a runtime permission to post notifications.
        // SDK manifest already includes POST_NOTIFICATIONS permission, but we also need to trigger a popup with permission request.
        // This is done with askPermission() method. You should handle the response from the popup as we have not shown this in this basic example.
        void askPermission() {
            if (Build.VERSION.SDK_INT >= 33) {
                if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS)
                        != PackageManager.PERMISSION_GRANTED) {
                    // Manifest.permission.POST_NOTIFICATIONS is available only if 'compileSdk 33' or greater
                    requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, 1);
                }
            }
        }
        // Starting from Android 13 (API level 33) we need a runtime permission to post notifications.
        // SDK manifest already includes POST_NOTIFICATIONS permission, but we also need to trigger a popup with permission request.
        // This is done with askPermission() method. You should handle the response from the popup as we have not shown this in this basic example.
        fun askPermission() {
            if (Build.VERSION.SDK_INT >= 33) {
                if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS)
                        != PackageManager.PERMISSION_GRANTED) {
                    // Manifest.permission.POST_NOTIFICATIONS is available only if 'compileSdk 33' or greater
                    requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS), 1)
                }
            }
        }

Handle push message callbacks

It is now time to flesh out the functionality of the push handler class which is called whenever the SDK receives a push message. Add the functionality as guided below:

Push handler callback

Within your push handler class that implements the pushMessageListener interface you'll need to decide what you want to do with the push notification and implement the logic by overriding the onMessageReceived() method. This method is called when a push notification is received while the app is in the foreground.

Functionality may include:

Displaying push notifications when the app is in the foreground

Push notifications are automatically displayed by Android only while the app is in the background. These notifications are sent to the system tray and launch your app when users tap them, but if you want to display the push notification message while the app is in the foregrounded, you will need to implement this.

You can extract the received push message body by calling message.getNotification().getBody() within your onMessageReceived handler.

Please see the official Android guidance for notifications to understand how to implement this, the example provided below is a basic implementation for illustrative purposes:

  1. Within your method you can retrieve the push message body by calling message.getNotification().getBody() you can then display or store this as desired e.g.
package com.example.testapp;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;

import androidx.core.app.NotificationCompat;
import androidx.core.app.TaskStackBuilder;

import com.comapi.ComapiClient;
import com.comapi.internal.push.PushMessageListener;
import com.google.firebase.messaging.RemoteMessage;

import org.json.JSONException;

public class PushHandler implements PushMessageListener {

    private final Context context;

    public PushHandler(Context context) {
        this.context = context;
    }

    @Override
    public void onMessageReceived(RemoteMessage message) {
        try {
            ComapiClient.parsePushMessage(message);
        } catch (JSONException e) {
            e.printStackTrace();
        }

        // Create an Intent for the activity you want to start
        Intent resultIntent = new Intent(context, MainActivity.class);
        // Create the TaskStackBuilder and add the intent, which inflates the back stack
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
        stackBuilder.addNextIntentWithParentStack(resultIntent);
        // Get the PendingIntent containing the entire back stack
        PendingIntent resultPendingIntent =
                stackBuilder.getPendingIntent(0,
                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

        // Check if message contains a notification payload.
        if (message.getNotification() != null) {
            // Do something with the message to notify the user
            // Example
            createNotificationChannel(context);
            Notification.Builder b = new Notification.Builder(context);
            b.setAutoCancel(true)
                    .setDefaults(NotificationCompat.DEFAULT_ALL)
                    .setWhen(System.currentTimeMillis())
                    .setSmallIcon(R.drawable.ic_launcher_background)
                    .setContentTitle("foreground message")
                    .setContentText(message.getNotification().getBody())
                    .setContentInfo("INFO")
                    .setContentIntent(resultPendingIntent);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                b.setChannelId("test_channel_id1");
            }
            NotificationManager nm =
                    (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            nm.notify(1, b.build());
        }
    }

    private void createNotificationChannel(Context context) {
        // Create the NotificationChannel, but only on API 26+ because
        // the NotificationChannel class is new and not supported in older libraries
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            CharSequence name = "test channel";
            String description = "some description";
            int importance = NotificationManager.IMPORTANCE_DEFAULT;
            NotificationChannel channel =
                    new NotificationChannel("test_channel_id1", name, importance);
            channel.setDescription(description);
            // Register the channel with the system; you can't change the importance
            // or other notification behaviors after this
            NotificationManager notificationManager =
                    context.getSystemService(NotificationManager.class);
            notificationManager.createNotificationChannel(channel);
        }
    }
}
package com.example.testapp

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.TaskStackBuilder
import android.content.Intent
import com.comapi.internal.push.PushMessageListener
import com.google.firebase.messaging.RemoteMessage
import com.comapi.ComapiClient
import org.json.JSONException

class PushHandler(private val context: Context) : PushMessageListener {

    override fun onMessageReceived(message: RemoteMessage) {
        try {
            val result = ComapiClient.parsePushMessage(message)
        } catch (e: JSONException) {
            e.printStackTrace()
        }

        // Create an Intent for the activity you want to start
        val resultIntent = Intent(context, MainActivity::class.java)
        // Create the TaskStackBuilder and add the intent, which inflates the back stack
        val stackBuilder = TaskStackBuilder.create(context)
        stackBuilder.addNextIntentWithParentStack(resultIntent)
        // Get the PendingIntent containing the entire back stack
        val resultPendingIntent = stackBuilder.getPendingIntent(
            0,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )

        // Check if message contains a notification payload.
        if (message.notification != null) {
            // Do something with the message to notify the user
            // Example
            createNotificationChannel(context)
            val b = Notification.Builder(context).apply {
                setAutoCancel(true)
                setDefaults(NotificationCompat.DEFAULT_ALL)
                setWhen(System.currentTimeMillis())
                setSmallIcon(R.drawable.ic_launcher_background)
                setContentTitle("foreground message")
                setContentText(message.notification?.body)
                setContentInfo("INFO")
                setContentIntent(resultPendingIntent)
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    setChannelId("test_channel_id1")
                }
            }
            val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            nm.notify(1, b.build())
        }
    }

    private fun createNotificationChannel(context: Context) {
        // Create the NotificationChannel, but only on devices that have the Google API 26+ because
        // the NotificationChannel class is new and not supported in older libraries
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val name: CharSequence = "test channel"
            val description = "some description"
            val importance = NotificationManager.IMPORTANCE_DEFAULT
            val channel = NotificationChannel("test_channel_id1", name, importance).apply {
                setDescription(description)
            }
            // Register the channel with the system; you can't change the importance
            // or other notification behaviors after this
            val notificationManager = context.getSystemService(NotificationManager::class.java)
            notificationManager.createNotificationChannel(channel)
        }
    }
}

Handling deep links

The SDK is capable of handling deep links for you and tracking their usage, which is our recommendation.
If you want to trigger deep links within your own app to drive functionality then you will need to implement these in your app prior to sending them in a push, however you can always use a URL or invoke another apps deep links without needing to implement your own deep link schema.

To enable deep link handling you must do the following:

  1. For the app to understand which Activity it should open when myappscheme://mycustomhost link is invoked the activity declaration in AndroidManifest should have an intent-filter:
<activity
    android:name=".MyActivity"
    android:exported="true">
    <intent-filter android:label="Open Activity in DotDigital Sample App">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- Accepts URIs that begin with "myappscheme://mycustomhost" -->
        <data
            android:host="mycustomhost"
            android:scheme="myappscheme" />
    </intent-filter>
</activity>
  1. Your apps MainActivity needs to be adapted to ensure that any intents passed (onCreate, onNewIntent) to the app are checked to see if they are push messages so that the SDK can automatically track the usage in Dotdigital's analytics and invoke any deep links that are associated with the push action. In addition your app will need to request permission to invoke deep links in later versions of Android, so this needs to be done in the onCreate. The code required is shown below:
public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        askPermission();
        ComapiClient client = Comapi.getShared();
        handlePush(client, getIntent());
    }

    // If you use android:launchMode="singleTop" this will be the place where new intent for an existing activity will be delivered
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        ComapiClient client = Comapi.getShared();
        handlePush(client, intent);
    }

    // Starting from Android 13 (API level 33) we need a runtime permission to post notifications.
    // SDK manifest already includes POST_NOTIFICATIONS permission, but we also need to trigger a popup with permission request.
    // This is done with askPermission() method. You should handle the response from the popup as we have not shown this in this basic example.
    void askPermission() {
        if (Build.VERSION.SDK_INT >= 33) {
            if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS)
                    != PackageManager.PERMISSION_GRANTED) {
                // Manifest.permission.POST_NOTIFICATIONS is available only if 'compileSdk 33' or greater
                requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, 1);
            }
        }
    }

    // Handle push notification intents
    void handlePush(ComapiClient client, Intent intent) {
        if (client != null) {
            // If we call client.handlePushMessage method providing the Activity context, intent that started the Activity,
            // a boolean flag telling the SDK if it should open a deep link if it finds it and a callback,
            // then the SDK will track the notification click interaction in Dotdigital analytics, optionally try to
            // start an activity to open the deep link, and as a result will provide an object with:
            //  getUrl - Dotdigital deep link URL
            //  getData - Dotdigital custom data
            //  isTrackingSuccessful - was the click recorded in analytics
            //  isStartActivitySuccessful - was the activity associated with the deep link started
            client.handlePushNotification(this, intent, true, new Callback<PushHandleResult>() {
                @Override
                public void success(PushHandleResult result) {

                }

                @Override
                public void error(Throwable t) {

                }
            });
        }
    }
}
class MainActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        askPermission()
        val client = Comapi.getShared()
        handlePush(client, intent)
    }

    // If you use android:launchMode="singleTop" this will be the place where new intent for an existing activity will be delivered
    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        val client = Comapi.getShared()
        handlePush(client, intent)
    }

    // Starting from Android 13 (API level 33) we need a runtime permission to post notifications.
    // SDK manifest already includes POST_NOTIFICATIONS permission, but we also need to trigger a popup with permission request.
    // This is done with askPermission() method. You should handle the response from the popup as we have not shown this in this basic example.
    fun askPermission() {
        if (Build.VERSION.SDK_INT >= 33) {
            if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS)
                    != PackageManager.PERMISSION_GRANTED) {
                // Manifest.permission.POST_NOTIFICATIONS is available only if 'compileSdk 33' or greater
                requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS), 1)
            }
        }
    }

    // Handle push notification intents
    fun handlePush(client: ComapiClient?, intent: Intent) {
        // If we call client.handlePushNotification method providing the Activity context, intent that started the Activity,
        // a boolean flag telling the SDK if it should open a deep link if it finds it and a callback,
        // then the SDK will track the notification click interaction in Dotdigital analytics, optionally try to
        // start an activity to open the deep link, and as a result will provide an object with:
        //  url - Dotdigital deep link URL
        //  data - Dotdigital custom data
        //  isTrackingSuccessful - was the click recorded in analytics
        //  isStartActivitySuccessful - was the activity associated with the deep link started
        client?.handlePushNotification(this, intent, true, object : Callback<PushHandleResult> {
            override fun success(result: PushHandleResult) {

            }

            override fun error(t: Throwable) {

            }
        })
    }
}

Extracting a deep link

If you wish to extract a deep link URL so that you can choose how to handle it, you can use the parsePushMessage function to parse Dotdigital data for the URL or Firebase RemoteMessage (a helper method).

public class PushHandler implements PushMessageListener {
    @Override
    public void onMessageReceived(RemoteMessage message) {
        try {
            // Let the SDK parse the push payload
            PushDetails result = ComapiClient.parsePushMessage(message);
            // Get the deep link URL if available
            Log.i("TEST", "url = " + result.getUrl());
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
}
class PushHandler : PushMessageListener {
    override fun onMessageReceived(message: RemoteMessage) {
        try {
            // Let the SDK parse the push payload
            val result = ComapiClient.parsePushMessage(message)
            // Get the deep link URL if available
            Log.i("TEST", "url = ${result.url}")
        } catch (e: JSONException) {
            e.printStackTrace()
        }
    }
}

Handling custom data payloads

The SDK can deliver custom data payloads via the push messages which your app can interpret to drive functionality. To access this custom data you will need to call the SDKs parsePushMessage() method with the contents of the push message. Any custom data can be extracted from the returned object by calling getData(). Add this code to your onMessageReceived() implementation in your push handler class. e.g.

public class PushHandler implements PushMessageListener {
    @Override
    public void onMessageReceived(RemoteMessage message) {
        try {
            // Let the SDK parse the push payload
            PushDetails result = ComapiClient.parsePushMessage(message);
            // Get the custom data using getData()
            Log.i("TEST", "data = " + (result.getData() != null ? result.getData().toString() : "null"));
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
}
class PushHandler : PushMessageListener {
    override fun onMessageReceived(message: RemoteMessage) {
        try {
            // Let the SDK parse the push payload
            val result = ComapiClient.parsePushMessage(message)
            // Get the custom data using getData()
            Log.i("TEST", "data = ${result.data?.toString() ?: "null"}")
        } catch (e: JSONException) {
            e.printStackTrace()
        }
    }
}

Changing the icon or colour of push notification

Our Android SDK uses Firebase Cloud Messaging (FCM) to send push notifications to your Android app users. As such, you can use the Android <meta-data> tag in your AndroidManifest.xml file to change the icon and colour of your push notifications.

<!-- Set icon used with incoming notification messages. This is used when no icon is set for the incoming notification message. -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@mipmap/ic_launcher_round" />
<!-- Set color used with incoming notification messages. This is used when no color is set for the incoming notification message. -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/colorAccent" />

Sessions

Starting a session

To receive push messages and register push tokens the SDK requires an active session.

To create a session you must have successfully initialized and retrieved a client object, you need to be able to identify the app user so that the sub claim in the JWT can be populated for the user; when the SDK requests the JWT in order to start the session. To create a session call:

client.service().session().startSession(new Callback<Session>(){/* implement */});
client.service().session().startSession(object : Callback<Session> {
    override fun success(session: Session) {
        // implement
    }

    override fun error(t: Throwable) {
        // implement
    }
})
rxClient.service().session().startSession()
   .subscribe(new Observer<Session>(){/* implement */});
rxClient.service().session().startSession()
    .subscribe(object : Observer<Session> {
        /* implement */
    })

Ending a session

You only have to end a session if you want to stop the app receiving push notifications, or you want to change users on the app.

To end the current session, call:

client.service().session().endSession(
   new Callback<ComapiResult<Void>>(){/* implement */});
client.service().session().endSession(object : Callback<ComapiResult<Void>> {
    /* implement */
})
rxClient.service().session().endSession()
   .subscribe(new Observer<ComapiResult<Void>>(){/* implement */});
rxClient.service().session().endSession()
    .subscribe(object : Observer<ComapiResult<Void>> {
        /* implement */
    })

🚧

Only users that have both an email and push token will be created in Dotdigital

Please read the Registering your app users for push page to understand the requirements for syncing your app users with Dotdigital. Only users who have both an email address and a push token will be synced and eligible to receive push messages.

👍

Next steps

Now ensure your app passes an email address to the SDK for the app user to ensure they get a contact created in Dotdigital by following these instructions

📘

Want to know more about the SDK?

To find out more about the SDK and its features and functions please go here