{"_id":"5b4dc6af666e630003bca0f8","project":"55dd9f2e0efd5821000d54d9","version":{"_id":"55dd9f4dab0e4d210045aae9","__v":45,"project":"55dd9f2e0efd5821000d54d9","createdAt":"2015-08-26T11:13:17.024Z","releaseDate":"2015-08-26T11:13:17.024Z","categories":["55dd9f4dab0e4d210045aaea","55ddb5fa9067202b00ddff6f","55e0472c6bad670d0081f213","55e04764a44fae0d00214671","55e047a9a44fae0d00214672","55e047b258c5460d0076a9a7","55e95e337fc27b2d00d32cf2","55e979bda7ca823900ad549a","55edb8c18dcb210d0056900b","55f0365c8563861700a33765","55f03677d58f9b1900acf996","55f036938eeefc23001ea5de","55f036a38563861700a33767","55f036c08563861700a33769","55f036d02911b72100482cd7","55f036e92911b72100482cd9","55f036fa8563861700a3376b","55f0370ee507711900e58c69","55f0371df6101b1900c70700","55f0374f2911b72100482cdb","55f0375e2911b72100482cdc","560eb0f659cb8d0d0015cd52","560eb25239fad419002ae1e0","561fb64d4d67490d00804b2a","562b9f775a39cd0d009aff22","562ba0505a39cd0d009aff23","562ba149d56bc30d00f0cb18","562ba595f68a5f0d007b1f3b","562ba78fd56bc30d00f0cb1b","562ba8b95a39cd0d009aff27","562baadf6562140d001501d2","562bab37f68a5f0d007b1f3d","562bc1bf9ebc950d000f7523","562bc99ced4bea0d00c11dfa","562bd29c1b98640d00714520","562bd5875a39cd0d009aff60","562bdfabff2da50d002c0aaf","562be0bd5a39cd0d009aff75","57a0b476d8313e1900454439","5b19051beece890003020163","5b34ded01cb20f000391ad6d","5b3a325acffe770003fd29e5","5b3c737a7f7b890003365501","5b3c929b367036000391b11e","5b7c1e210dc2e20003871521"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"2.0.0","version":"2"},"category":{"_id":"5b34ded01cb20f000391ad6d","project":"55dd9f2e0efd5821000d54d9","version":"55dd9f4dab0e4d210045aae9","__v":0,"sync":{"url":"","isSync":false},"reference":false,"createdAt":"2018-06-28T13:12:48.898Z","from_sync":false,"order":2,"slug":"push-notifications","title":"Push notifications"},"user":"5a251846c297dc0012e531cd","githubsync":"","__v":0,"parentDoc":null,"updates":[],"next":{"pages":[],"description":""},"createdAt":"2018-07-17T10:36:31.702Z","link_external":false,"link_url":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"settings":"","auth":"required","params":[],"url":""},"isReference":false,"order":3,"body":"[block:html]\n{\n  \"html\": \"<div id=\\\"userMap\\\">\\n<div class=\\\"content\\\"><a href=\\\"https://developer.dotmailer.com/v2/docs/creating-a-push-notification-profile\\\"><div class=\\\"box box1\\\">Create a push notification profile in dotmailer</div></a></div>\\n<div class=\\\"arrow\\\">→</div>\\n<div class=\\\"content\\\"><a href=\\\"https://developer.dotmailer.com/v2/docs/creating-a-json-web-token\\\"><div class=\\\"box box2\\\">Create a class or function that generates a JSON Web Token</div></a></div>\\n<div class=\\\"arrow\\\">→</div>\\n<div class=\\\"content\\\"><a href=\\\"https://developer.dotmailer.com/v2/docs/setting-up-push-notifications#section-mobile-sdk-options\\\"><div class=\\\"box box3 active\\\">Use one of our mobile SDKs in your app</div></a></div>\\n<div class=\\\"clearfix\\\"></div></div>\\n\\n<style>\\n  .box {\\n    padding: 10px;\\n    border: 2px solid #000;\\n    width: 120px;\\n    height: 120px;\\n    background-color: #EAEAEA;\\n    hyphens: auto;\\n    float: left;\\n    font-size: 12px;\\n}\\n\\n.box:hover {\\n    background-color: #82bc42;\\n}\\n\\n.box.active {\\n    background-color: #82bc42;\\n}\\n\\n#userMap {\\n    overflow-x: auto;\\n    overflow-y: auto;\\n    padding: 20px;\\n    min-width: 770px;\\n}\\n\\n#userMap a:hover {\\n    text-decoration: none;\\n  }\\n\\ndiv.arrow {\\n    max-width: 50px;\\n    margin-left: 15px;\\n    margin-right: 15px;\\n    font-size: 50px;\\n}\\n\\n\\n#userMap div.arrow, #userMap div.content {\\n    float: left;\\n}\\n\\n.clearfix {\\n    clear: both;\\n}\\n\\n\\n#userMap div.arrow {\\n    position: relative;\\n    top: 45px;\\n}\\n\\n.box1 {\\n    margin-left:0px;\\n}\\n\\ndiv.box.box1 {\\n    margin-left: -20px;\\n}\\n\\n</style>\"\n}\n[/block]\nOur Android SDK uses [Firebase Cloud Messaging (FCM)](https://firebase.google.com/docs/cloud-messaging/) to send push notifications to your Android app users.\n\nTo set up push notifications for native Android apps, complete the following tasks:\n\n1. [Install the Android SDK](#section-installing-the-android-sdk)\n2. [Configure the Android SDK](#section-configuring-the-android-sdk)\n3. [Initialise the Android SDK](#section-initialising-the-android-sdk)\n4. [Add an email address to the Android app user's profile](#section-adding-an-email-address-to-the-android-app-user-s-profile)\n5. Optional: [Change the icon or colour of your push notifications](#section-changing-the-icon-or-colour-of-push-notification)\n[block:callout]\n{\n  \"type\": \"info\",\n  \"body\": \"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](#section-android-sample-code).\",\n  \"title\": \"Handling asynchronous requests\"\n}\n[/block]\n# Installing the Android SDK\n\n1. Add the Android SDK to your module-level `build.gradle` file and change the `minSDKVersion` attribute to 16\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"defaultConfig {\\n...\\nminSdkVersion 16\\n...\\n}\\nDependencies {\\n...\\nimplementation 'com.comapi:foundation:1.2.0'\\n}\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\n\n[block:callout]\n{\n  \"type\": \"warning\",\n  \"body\": \"The Android SDK has a `minSDKVersion` attribute of 16, your attribute must have a value of at least 16.\",\n  \"title\": \"Seeing a 'Manifest merger' error?\"\n}\n[/block]\n2. Add the Google services plugin to your module-level `build.gradle` file\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"apply plugin: 'com.google.gms.google-services'\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\n3. Add the following dependencies to your module-level `build.gradle` file\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"implementation ('com.google.android.gms:play-services-base:15.0.1') {force = true}\\nimplementation ('com.google.firebase:firebase-messaging:17.0.0') {force = true}\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\n\n[block:callout]\n{\n  \"type\": \"success\",\n  \"body\": \"Remember to synchronise `build.gradle` files after making any changes.\",\n  \"title\": \"Sync Now\"\n}\n[/block]\n# Configuring the Android SDK\n\nBefore you can configure the Android SDK, you need the following:\n\n* The value of the [API space ID field in dotmailer](doc:creating-a-push-notification-profile#section-finding-your-api-space-id)\n* A class that [creates a JWT token](https://developer.dotmailer.com/v2/docs/creating-a-json-web-token#section-android-jwt-code-sample)\n\n1. Create a new instance of the `ComapiConfig` class\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"ComapiConfig config = new ComapiConfig()\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\n2. Pass your API Space ID to the `apiSpaceId()` method\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"ComapiConfig config = new ComapiConfig() .apiSpaceId(\\\"<API_SPACE_ID>\\\")\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\n3. Pass an instance of the class that creates a JWT to the `authenticator()` method\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"ComapiConfig config = new ComapiConfig() .apiSpaceId(\\\"<API_SPACE_ID>\\\") .authenticator(new ChallengeHandler());\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\n## Displaying push notifications when the app is in the foreground\n\nPush notifications are automatically displayed only while the app is in the background. These notifications are sent to the system tray and launch your app when users tap them.\n\nIf you want to display the push notification message while the app is in the foreground, do the following:\n\n1. Create a class that implements the `pushMessageListener` class (here, we've called the example class `PushHandler`). You'll need to decide what you want to do with the push notification and implement the logic by overriding the `onMessageReceived()` [method](https://firebase.google.com/docs/cloud-messaging/android/receive). This method is called when a push notification is received while the app is in the foreground.\n[block:callout]\n{\n  \"type\": \"info\",\n  \"body\": \"If a push notification is delivered while the app is in the foreground, you can build your own in the 'onMessageReceived()` method that [creates a link to a particular activity in your app](https://developer.android.com/training/notify-user/build-notification#click).\",\n  \"title\": \"Deep links\"\n}\n[/block]\n\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"public class PushHandler implements PushMessageListener {\\n\\n    :::at:::Override\\n    public void onMessageReceived(RemoteMessage message) {\\n      // Check if message contains a notification payload.\\n        if (message.getNotification() != null) {\\n        Log.i(\\\"TAG\\\", \\\"Push notification: \\\" + message.getNotification().getBody());\\n      // Do something with the message to notify the user\\n          // Example\\n          createNotificationChannel(context);\\n          Notification.Builder b = new Notification.Builder(context);\\n          b.setAutoCancel(true)\\n            .setDefaults(NotificationCompat.DEFAULT_ALL)\\n            .setWhen(System.currentTimeMillis())\\n            .setSmallIcon(R.drawable.common_google_signin_btn_icon_dark)\\n            .setContentTitle(\\\"foreground message\\\")\\n            .setContentText(body)\\n            .setContentInfo(\\\"INFO\\\");\\n          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\\n            b.setChannelId(\\\"test_channel_id1\\\");\\n          }\\n          NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);\\n          nm.notify(1, b.build());\\n        }\\n    }\\n}\\n\\nprivate void createNotificationChannel(Context context) {\\n        // Create the NotificationChannel, but only on devices that have the Google API 26+ because the NotificationChannel class is new and not in supported in older libraries\\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\\n            CharSequence name = \\\"test channel\\\";\\n            String description = \\\"some description\\\";\\n            int importance = NotificationManager.IMPORTANCE_DEFAULT;\\n            NotificationChannel channel = new NotificationChannel(\\\"test_channel_id1\\\", name, importance);\\n            channel.setDescription(description);\\n            // Register the channel with the system; you can't change the importance\\n            // or other notification behaviors after this\\n            NotificationManager notificationManager = context.getSystemService(NotificationManager.class);\\n            notificationManager.createNotificationChannel(channel);\\n        }\\n}\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\n2. Pass your class to the `pushMessageListener()` on the `ComapiConfig` object \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"config.pushMessageListener(new PushHandler());\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\n## Configuring logs and proxy servers\n\nYou can set internal file logs, console logs, and network logs to the following levels. By default all log levels are set to `WARNING`:\n\n* `OFF`\n* `FATAL`\n* `ERROR`\n* `WARNING`\n* `INFO`\n* `DEBUG`\n\nTo change the log levels, add the following line to the `ComapiConfig` object and use the appropriate method on the `LogConfig` object:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \".logConfig(new LogConfig().setFileLevel(LogLevel.DEBUG).setConsoleLevel(LogLevel.INFO).setNetworkLevel(LogLevel.ERROR));\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\nYou can also set a custom limit for the size of internal log files by calling the following method on the `ComapiConfig` object:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \".logSizeLimitKilobytes(limit);\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\nIf your app connects through a proxy server, add the following line to the `ComapiConfig` object:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \".apiConfiguration(new APIConfig().proxy(\\\"http://xx.xxx.xx.xxx:xxxx\\\"));\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\n# Initialising the Android SDK\n[block:callout]\n{\n  \"type\": \"warning\",\n  \"body\": \"Initialisation needs to be performed in the `onCreate()` [method](https://developer.android.com/reference/android/app/Application#onCreate() of the Android `Application` class so that your app users' profiles don't change every time they open the app.\",\n  \"title\": \"Where to initialise the Android SDK in the activity lifecycle\"\n}\n[/block]\n1. After you've configured the SDK, import the `com.google.firebase.FirebaseApp` class into your java file, and initialise the Firebase SDK.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"FirebaseApp.initializeApp(this);\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\n2 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.\n\n* 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)\n* 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.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"public class MyApplication extends Application {\\n\\n    @Override\\n    public void onCreate()\\n    {\\n        super.onCreate();\\n      \\n      \\tFirebaseApp.initializeApp(this);\\n\\n        ComapiConfig config = new ComapiConfig();\\n        //<API space ID> string must be the same as the value of the 'API space ID' field in your push notification profile in dotmailer. \\n        config.apiSpaceId(\\\"<API space ID>\\\");\\n        //Set handler for authentication challenges (SDK asks for a JWT token)\\n        config.authenticator(new ChallengeHandler());\\n\\n        Comapi.initialiseShared(this, config, new Callback<ComapiClient>() {\\n            @Override\\n            public void success(final ComapiClient client) {\\n              //Use ComapiClient object to communicate with services\\n            }\\n            @Override\\n            public void error(Throwable t) {\\n\\t\\t\\t\\t\\t\\t\\t//Error\\n            }\\n        }); \",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\n# Adding an email address to an Android app user's profile\n\nPush notifications can be sent from dotmailer only to users who have a profile that contains an email address.\n\nTo create a profile for the user, a session must be started.\n\nWhen a session is started, the JWT token is used to create the user's profile ID ('profileId' string). This profile ID remains the same until a session is stopped.\n\nWhen a session is stopped and started again, a new JWT token is created and used to create a new profile ID.\n\nA session is stopped in any of the following circumstances:\n\n* The user uninstalls the app, and then reinstalls it\n* The user clears all of the app's data\n\nYou can also stop a session by calling the `endSession()` method:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"client.service().session().endSession(\\n   new Callback<ComapiResult<Void>>(){/* implement */});\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\n1. After you've initialised the Android SDK, you need to check whether the session was started\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"if(client.getSession() != null && client.getSession().isSuccessfullyCreated()) {/*Implement*/}\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\nIf the session hasn't been started, start it by calling the `startSession()` method then continue to step 2. Otherwise, continue to step 2.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"client.service().session().startSession(new Callback<Session>() { /*Implement */ });\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\n2. Use the `getProfileId()` method on the `Session` object, and pass the returned ID to the `getProfile()` method.\n\nThe `getProfile()` method returns a `ComapiResult<T>` object with the following methods:\n\n* `result.isSuccessful()`: True if the HTTP status returned from the `result.getCode()` method is in the range 200..300\n\n* `result.getResult()`: Response data (profile data in a `Map<String, Object>` map)\n\n* `result.getMessage()`: HTTP status message\n\n* `result.getErrorBody()` Error details\n\n* `result.getCode()`: HTTP status code\n\n*  `result.getETag()`: Version of the data\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"if(client.getSession() != null && client.getSession().isSuccessfullyCreated()) {\\n  client.service().profile().getProfile(client.getSession().getProfileId(), new Callback<ComapiResult<Map<String, Object>>>() {\\n    @Override\\n    public void success(ComapiResult<Map<String, Object>> result) {\\t\\n      @Override\\n      public void error(Throwable t) {\\n        //Error\\n      }\\n    }\\n  }\\n}\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\n3. When you have the user's profile data in the `ComapiResult` map, add an email address to it and update the profile.\n[block:callout]\n{\n  \"type\": \"warning\",\n  \"title\": \"Ask users to enter their email address\",\n  \"body\": \"You should ask users if they'd like to receive push notifications from you and prompt them to enter their email address.\"\n}\n[/block]\n\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"public void success(ComapiResult<Map<String, Object>> result) {\\n  Map <String, Object> additionalMap = new HashMap<>();\\n  //Add the user's email address to the profile\\n  additionalMap.put(\\\"email\\\", \\\"exampleappuser1@gmail.com\\\");\\n  client.service().profile().patchMyProfile(additionalMap, result.getETag(), new Callback<ComapiResult<Map<String, Object>>>() { }\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\n\n[block:callout]\n{\n  \"type\": \"info\",\n  \"body\": \"An eTag string contains data about the version of a resource and is returned from every service response.\\n\\nWhen updating a profile, the eTag string is used to check that the profile hasn't already been updated before you update it.\\n\\nWhen you use the `patchMyProfile()` method, you need to pass it the `eTag`, which is returned from the result of the `getProfile()` method.\",\n  \"title\": \"eTags\"\n}\n[/block]\nNow, when a user launches your app, the user's profile data is sent to dotmailer and (if that profile contains an email address that belongs to one of your contacts) that contact can now receive push notifications from dotmailer :tada: .\n\n# Changing the icon or colour of push notification\n\nOur Android SDK uses [Firebase Cloud Messaging (FCM)](https://firebase.google.com/docs/cloud-messaging/) to send push notifications to your Android app users. As such, you can use the Android `<meta-data>` [tag](https://developer.android.com/guide/topics/manifest/meta-data-element) in your `AndroidManifest.xml` file to change the icon and colour of your push notifications.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<!-- Set icon used with incoming notification messages. This is used when no icon is set for the incoming notification message. -->\\n<meta-data\\nandroid:name=\\\"com.google.firebase.messaging.default_notification_icon\\\"\\nandroid:resource=\\\"@mipmap/ic_launcher_round\\\" />\\n<!-- Set color used with incoming notification messages. This is used when no color is set for the incoming notification message. -->\\n<meta-data\\nandroid:name=\\\"com.google.firebase.messaging.default_notification_color\\\"\\nandroid:resource=\\\"@color/colorAccent\\\" />\",\n      \"language\": \"java\"\n    }\n  ]\n}\n[/block]\n\n[block:callout]\n{\n  \"type\": \"success\",\n  \"body\": \"[Send a push notification to your app](https://support.dotmailer.com/hc/en-gb/articles/360001827870) from the program builder in dotmailer.\",\n  \"title\": \"Next steps\"\n}\n[/block]\n# Android sample code\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"package com.example.testapp;\\n\\nimport com.google.firebase.FirebaseApp;\\n\\nimport com.comapi.Comapi;\\nimport com.comapi.ComapiClient;\\nimport android.app.Application;\\n\\nimport com.comapi.Callback;\\nimport com.comapi.ComapiConfig;\\nimport com.comapi.Session;\\nimport com.comapi.internal.network.ComapiResult;\\n\\nimport java.util.HashMap;\\nimport java.util.Map;\\n\\npublic class MyApplication extends Application {\\n\\n  @Override\\n  public void onCreate()\\n  {\\n    super.onCreate();\\n    \\n    FirebaseApp.initializeApp(this);\\n\\n    ComapiConfig config = new ComapiConfig();\\n    //<API space ID> string must be the same as the value of the 'API space ID' field in your push notification profile in dotmailer.\\n    config.apiSpaceId(\\\"<API space ID>\\\");\\n    //Set the handler for authentication challenges (SDK asking for JWT token)\\n    config.authenticator(new AuthChallengeHandler());\\n\\n    Comapi.initialiseShared(this, config, new Callback<ComapiClient>() {\\n      @Override\\n      public void success(final ComapiClient client) {\\n        if(client.getSession() != null && client.getSession().isSuccessfullyCreated()) {\\n          client.service().profile().getProfile(client.getSession().getProfileId(), new Callback<ComapiResult<Map<String, Object>>>() {\\n            @Override\\n            public void success(ComapiResult<Map<String, Object>> result) {\\n              Map <String, Object> additionalMap = new HashMap<>();\\n              //Add the user's email address to the profile\\n              additionalMap.put(\\\"email\\\", \\\"exampleappuser1@gmail.com\\\");\\n              client.service().profile().patchMyProfile(additionalMap, result.getETag(), new Callback<ComapiResult<Map<String, Object>>>() {\\n                @Override\\n                public void success(ComapiResult<Map<String, Object>> result) {\\n                }\\n                @Override\\n                public void error(Throwable t) {\\n                }\\n              });\\n            }\\n            @Override\\n            public void error(Throwable t) {\\n            }\\n          }\\n                                               );\\n        } else {\\n          client.service().session().startSession(new Callback<Session>() {\\n            @Override\\n            public void success(Session result) {\\n\\n            }\\n\\n            @Override\\n            public void error(Throwable t) {\\n\\n            }\\n          });\\n        }\\n\\n      }\\n\\n      @Override\\n      public void error(Throwable t) {\\n        //Error\\n      }\\n    });\\n  }\\n}\",\n      \"language\": \"java\",\n      \"name\": \"Java sample application\"\n    }\n  ]\n}\n[/block]\n\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"package com.example.testapp;\\n\\nimport android.app.Application;\\n\\nimport com.google.firebase.FirebaseApp;\\n\\nimport com.comapi.ComapiConfig;\\nimport com.comapi.RxComapi;\\nimport com.comapi.RxComapiClient;\\nimport com.comapi.internal.network.ComapiResult;\\n\\nimport java.util.HashMap;\\nimport java.util.Map;\\n\\nimport rx.Observable;\\nimport rx.Subscriber;\\nimport rx.functions.Func1;\\n\\npublic class MyApplication extends Application {\\n\\n  @Override\\n  public void onCreate()\\n  {\\n    super.onCreate();\\n    \\n    FirebaseApp.initializeApp(this);\\n\\n    RxComapi.initialise(\\n      this, new ComapiConfig()\\n      //<API space ID> string must be the same as the value of the 'API space ID' field in your push notification profile in dotmailer.\\n      .apiSpaceId(\\\"<API space ID>\\\")\\n      //Set the handler for authentication challenges (SDK asking for JWT token)\\n      .authenticator(new ChallengeHandler(getSharedPreferences(Const.PREFS_NAME, MODE_PRIVATE)))\\n    ).flatMap(new Func1<RxComapiClient, Observable<RxComapiClient>>() {\\n      @Override\\n      public Observable<RxComapiClient> call(final RxComapiClient client) {\\n        if (client.getSession() != null && client.getSession().isSuccessfullyCreated()) {  \\n            return Observable.fromCallable(new Callable<RxComapiClient>() {\\n              @Override\\n              public RxComapiClient call() throws Exception {\\n                return client;\\n              }\\n            });\\n          } else {\\n            return client.service().session().startSession().map(new Func1<Session, RxComapiClient>() {\\n              @Override\\n              public RxComapiClient call(Session session) {\\n                return client;\\n              }\\n            });\\n        }\\n      }\\n    }).flatMap(new Func1<RxComapiClient, Observable<ComapiResult<Map<String, Object>>>>() {\\n      @Override\\n      public Observable<ComapiResult<Map<String, Object>>> call(final RxComapiClient client) {\\n        if (!client.getSession().isSuccessfullyCreated()) {\\n          return Observable.error(new Exception(\\\"Failed to update profile\\\"));\\n        }\\n        return client.service()\\n          .profile()\\n          .getProfile(client.getSession().getProfileId())\\n          .flatMap(new Func1<ComapiResult<Map<String, Object>>, Observable<ComapiResult<Map<String, Object>>>>() {\\n            @Override\\n            public Observable<ComapiResult<Map<String, Object>>> call(ComapiResult<Map<String, Object>> result) {\\n              Map<String, Object> additionalMap = new HashMap<>();\\n              //Add the user's email address to the profile\\n              additionalMap.put(\\\"email\\\", \\\"exampleappuser1@gmail.com\\\");\\n              return client.service().profile().patchMyProfile(additionalMap, result.getETag());\\n            }\\n          }\\n                  );\\n      }\\n    }).subscribe(new Subscriber<ComapiResult<Map<String, Object>>>() {\\n      @Override\\n      public void onCompleted() {\\n      }\\n      @Override\\n      public void onError(Throwable e) {\\n      }\\n      @Override\\n      public void onNext(ComapiResult<Map<String, Object>> result) {\\n      }\\n    });\\n  }\\n}\\n\",\n      \"language\": \"java\",\n      \"name\": \"RxJava sample application\"\n    }\n  ]\n}\n[/block]","excerpt":"A tutorial for adding the Android SDK code to your app to allow your app users to receive push notifications from your dotmailer account.","slug":"android-sdk","type":"basic","title":"Using the Android SDK"}

Using the Android SDK

A tutorial for adding the Android SDK code to your app to allow your app users to receive push notifications from your dotmailer account.

[block:html] { "html": "<div id=\"userMap\">\n<div class=\"content\"><a href=\"https://developer.dotmailer.com/v2/docs/creating-a-push-notification-profile\"><div class=\"box box1\">Create a push notification profile in dotmailer</div></a></div>\n<div class=\"arrow\">→</div>\n<div class=\"content\"><a href=\"https://developer.dotmailer.com/v2/docs/creating-a-json-web-token\"><div class=\"box box2\">Create a class or function that generates a JSON Web Token</div></a></div>\n<div class=\"arrow\">→</div>\n<div class=\"content\"><a href=\"https://developer.dotmailer.com/v2/docs/setting-up-push-notifications#section-mobile-sdk-options\"><div class=\"box box3 active\">Use one of our mobile SDKs in your app</div></a></div>\n<div class=\"clearfix\"></div></div>\n\n<style>\n .box {\n padding: 10px;\n border: 2px solid #000;\n width: 120px;\n height: 120px;\n background-color: #EAEAEA;\n hyphens: auto;\n float: left;\n font-size: 12px;\n}\n\n.box:hover {\n background-color: #82bc42;\n}\n\n.box.active {\n background-color: #82bc42;\n}\n\n#userMap {\n overflow-x: auto;\n overflow-y: auto;\n padding: 20px;\n min-width: 770px;\n}\n\n#userMap a:hover {\n text-decoration: none;\n }\n\ndiv.arrow {\n max-width: 50px;\n margin-left: 15px;\n margin-right: 15px;\n font-size: 50px;\n}\n\n\n#userMap div.arrow, #userMap div.content {\n float: left;\n}\n\n.clearfix {\n clear: both;\n}\n\n\n#userMap div.arrow {\n position: relative;\n top: 45px;\n}\n\n.box1 {\n margin-left:0px;\n}\n\ndiv.box.box1 {\n margin-left: -20px;\n}\n\n</style>" } [/block] Our Android SDK uses [Firebase Cloud Messaging (FCM)](https://firebase.google.com/docs/cloud-messaging/) to send push notifications to your Android app users. To set up push notifications for native Android apps, complete the following tasks: 1. [Install the Android SDK](#section-installing-the-android-sdk) 2. [Configure the Android SDK](#section-configuring-the-android-sdk) 3. [Initialise the Android SDK](#section-initialising-the-android-sdk) 4. [Add an email address to the Android app user's profile](#section-adding-an-email-address-to-the-android-app-user-s-profile) 5. Optional: [Change the icon or colour of your push notifications](#section-changing-the-icon-or-colour-of-push-notification) [block:callout] { "type": "info", "body": "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](#section-android-sample-code).", "title": "Handling asynchronous requests" } [/block] # Installing the Android SDK 1. Add the Android SDK to your module-level `build.gradle` file and change the `minSDKVersion` attribute to 16 [block:code] { "codes": [ { "code": "defaultConfig {\n...\nminSdkVersion 16\n...\n}\nDependencies {\n...\nimplementation 'com.comapi:foundation:1.2.0'\n}", "language": "java" } ] } [/block] [block:callout] { "type": "warning", "body": "The Android SDK has a `minSDKVersion` attribute of 16, your attribute must have a value of at least 16.", "title": "Seeing a 'Manifest merger' error?" } [/block] 2. Add the Google services plugin to your module-level `build.gradle` file [block:code] { "codes": [ { "code": "apply plugin: 'com.google.gms.google-services'", "language": "java" } ] } [/block] 3. Add the following dependencies to your module-level `build.gradle` file [block:code] { "codes": [ { "code": "implementation ('com.google.android.gms:play-services-base:15.0.1') {force = true}\nimplementation ('com.google.firebase:firebase-messaging:17.0.0') {force = true}", "language": "java" } ] } [/block] [block:callout] { "type": "success", "body": "Remember to synchronise `build.gradle` files after making any changes.", "title": "Sync Now" } [/block] # Configuring the Android SDK Before you can configure the Android SDK, you need the following: * The value of the [API space ID field in dotmailer](doc:creating-a-push-notification-profile#section-finding-your-api-space-id) * A class that [creates a JWT token](https://developer.dotmailer.com/v2/docs/creating-a-json-web-token#section-android-jwt-code-sample) 1. Create a new instance of the `ComapiConfig` class [block:code] { "codes": [ { "code": "ComapiConfig config = new ComapiConfig()", "language": "java" } ] } [/block] 2. Pass your API Space ID to the `apiSpaceId()` method [block:code] { "codes": [ { "code": "ComapiConfig config = new ComapiConfig() .apiSpaceId(\"<API_SPACE_ID>\")", "language": "java" } ] } [/block] 3. Pass an instance of the class that creates a JWT to the `authenticator()` method [block:code] { "codes": [ { "code": "ComapiConfig config = new ComapiConfig() .apiSpaceId(\"<API_SPACE_ID>\") .authenticator(new ChallengeHandler());", "language": "java" } ] } [/block] ## Displaying push notifications when the app is in the foreground Push notifications are automatically displayed only while the app is in the background. These notifications are sent to the system tray and launch your app when users tap them. If you want to display the push notification message while the app is in the foreground, do the following: 1. Create a class that implements the `pushMessageListener` class (here, we've called the example class `PushHandler`). You'll need to decide what you want to do with the push notification and implement the logic by overriding the `onMessageReceived()` [method](https://firebase.google.com/docs/cloud-messaging/android/receive). This method is called when a push notification is received while the app is in the foreground. [block:callout] { "type": "info", "body": "If a push notification is delivered while the app is in the foreground, you can build your own in the 'onMessageReceived()` method that [creates a link to a particular activity in your app](https://developer.android.com/training/notify-user/build-notification#click).", "title": "Deep links" } [/block] [block:code] { "codes": [ { "code": "public class PushHandler implements PushMessageListener {\n\n @Override\n public void onMessageReceived(RemoteMessage message) {\n // Check if message contains a notification payload.\n if (message.getNotification() != null) {\n Log.i(\"TAG\", \"Push notification: \" + message.getNotification().getBody());\n // Do something with the message to notify the user\n // Example\n createNotificationChannel(context);\n Notification.Builder b = new Notification.Builder(context);\n b.setAutoCancel(true)\n .setDefaults(NotificationCompat.DEFAULT_ALL)\n .setWhen(System.currentTimeMillis())\n .setSmallIcon(R.drawable.common_google_signin_btn_icon_dark)\n .setContentTitle(\"foreground message\")\n .setContentText(body)\n .setContentInfo(\"INFO\");\n if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n b.setChannelId(\"test_channel_id1\");\n }\n NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);\n nm.notify(1, b.build());\n }\n }\n}\n\nprivate void createNotificationChannel(Context context) {\n // Create the NotificationChannel, but only on devices that have the Google API 26+ because the NotificationChannel class is new and not in supported in older libraries\n if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n CharSequence name = \"test channel\";\n String description = \"some description\";\n int importance = NotificationManager.IMPORTANCE_DEFAULT;\n NotificationChannel channel = new NotificationChannel(\"test_channel_id1\", name, importance);\n channel.setDescription(description);\n // Register the channel with the system; you can't change the importance\n // or other notification behaviors after this\n NotificationManager notificationManager = context.getSystemService(NotificationManager.class);\n notificationManager.createNotificationChannel(channel);\n }\n}", "language": "java" } ] } [/block] 2. Pass your class to the `pushMessageListener()` on the `ComapiConfig` object [block:code] { "codes": [ { "code": "config.pushMessageListener(new PushHandler());", "language": "java" } ] } [/block] ## 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: [block:code] { "codes": [ { "code": ".logConfig(new LogConfig().setFileLevel(LogLevel.DEBUG).setConsoleLevel(LogLevel.INFO).setNetworkLevel(LogLevel.ERROR));", "language": "java" } ] } [/block] You can also set a custom limit for the size of internal log files by calling the following method on the `ComapiConfig` object: [block:code] { "codes": [ { "code": ".logSizeLimitKilobytes(limit);", "language": "java" } ] } [/block] If your app connects through a proxy server, add the following line to the `ComapiConfig` object: [block:code] { "codes": [ { "code": ".apiConfiguration(new APIConfig().proxy(\"http://xx.xxx.xx.xxx:xxxx\"));", "language": "java" } ] } [/block] # Initialising the Android SDK [block:callout] { "type": "warning", "body": "Initialisation needs to be performed in the `onCreate()` [method](https://developer.android.com/reference/android/app/Application#onCreate() of the Android `Application` class so that your app users' profiles don't change every time they open the app.", "title": "Where to initialise the Android SDK in the activity lifecycle" } [/block] 1. After you've configured the SDK, import the `com.google.firebase.FirebaseApp` class into your java file, and initialise the Firebase SDK. [block:code] { "codes": [ { "code": "FirebaseApp.initializeApp(this);", "language": "java" } ] } [/block] 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. [block:code] { "codes": [ { "code": "public class MyApplication extends Application {\n\n @Override\n public void onCreate()\n {\n super.onCreate();\n \n \tFirebaseApp.initializeApp(this);\n\n ComapiConfig config = new ComapiConfig();\n //<API space ID> string must be the same as the value of the 'API space ID' field in your push notification profile in dotmailer. \n config.apiSpaceId(\"<API space ID>\");\n //Set handler for authentication challenges (SDK asks for a JWT token)\n config.authenticator(new ChallengeHandler());\n\n Comapi.initialiseShared(this, config, new Callback<ComapiClient>() {\n @Override\n public void success(final ComapiClient client) {\n //Use ComapiClient object to communicate with services\n }\n @Override\n public void error(Throwable t) {\n\t\t\t\t\t\t\t//Error\n }\n }); ", "language": "java" } ] } [/block] # Adding an email address to an Android app user's profile Push notifications can be sent from dotmailer only to users who have a profile that contains an email address. To create a profile for the user, a session must be started. When a session is started, the JWT token is used to create the user's profile ID ('profileId' string). This profile ID remains the same until a session is stopped. When a session is stopped and started again, a new JWT token is created and used to create a new profile ID. A session is stopped in any of the following circumstances: * The user uninstalls the app, and then reinstalls it * The user clears all of the app's data You can also stop a session by calling the `endSession()` method: [block:code] { "codes": [ { "code": "client.service().session().endSession(\n new Callback<ComapiResult<Void>>(){/* implement */});", "language": "java" } ] } [/block] 1. After you've initialised the Android SDK, you need to check whether the session was started [block:code] { "codes": [ { "code": "if(client.getSession() != null && client.getSession().isSuccessfullyCreated()) {/*Implement*/}", "language": "java" } ] } [/block] If the session hasn't been started, start it by calling the `startSession()` method then continue to step 2. Otherwise, continue to step 2. [block:code] { "codes": [ { "code": "client.service().session().startSession(new Callback<Session>() { /*Implement */ });", "language": "java" } ] } [/block] 2. Use the `getProfileId()` method on the `Session` object, and pass the returned ID to the `getProfile()` method. The `getProfile()` method returns a `ComapiResult<T>` object with the following methods: * `result.isSuccessful()`: True if the HTTP status returned from the `result.getCode()` method is in the range 200..300 * `result.getResult()`: Response data (profile data in a `Map<String, Object>` map) * `result.getMessage()`: HTTP status message * `result.getErrorBody()` Error details * `result.getCode()`: HTTP status code * `result.getETag()`: Version of the data [block:code] { "codes": [ { "code": "if(client.getSession() != null && client.getSession().isSuccessfullyCreated()) {\n client.service().profile().getProfile(client.getSession().getProfileId(), new Callback<ComapiResult<Map<String, Object>>>() {\n @Override\n public void success(ComapiResult<Map<String, Object>> result) {\t\n @Override\n public void error(Throwable t) {\n //Error\n }\n }\n }\n}", "language": "java" } ] } [/block] 3. When you have the user's profile data in the `ComapiResult` map, add an email address to it and update the profile. [block:callout] { "type": "warning", "title": "Ask users to enter their email address", "body": "You should ask users if they'd like to receive push notifications from you and prompt them to enter their email address." } [/block] [block:code] { "codes": [ { "code": "public void success(ComapiResult<Map<String, Object>> result) {\n Map <String, Object> additionalMap = new HashMap<>();\n //Add the user's email address to the profile\n additionalMap.put(\"email\", \"exampleappuser1@gmail.com\");\n client.service().profile().patchMyProfile(additionalMap, result.getETag(), new Callback<ComapiResult<Map<String, Object>>>() { }", "language": "java" } ] } [/block] [block:callout] { "type": "info", "body": "An eTag string contains data about the version of a resource and is returned from every service response.\n\nWhen updating a profile, the eTag string is used to check that the profile hasn't already been updated before you update it.\n\nWhen you use the `patchMyProfile()` method, you need to pass it the `eTag`, which is returned from the result of the `getProfile()` method.", "title": "eTags" } [/block] Now, when a user launches your app, the user's profile data is sent to dotmailer and (if that profile contains an email address that belongs to one of your contacts) that contact can now receive push notifications from dotmailer :tada: . # Changing the icon or colour of push notification Our Android SDK uses [Firebase Cloud Messaging (FCM)](https://firebase.google.com/docs/cloud-messaging/) to send push notifications to your Android app users. As such, you can use the Android `<meta-data>` [tag](https://developer.android.com/guide/topics/manifest/meta-data-element) in your `AndroidManifest.xml` file to change the icon and colour of your push notifications. [block:code] { "codes": [ { "code": "<!-- Set icon used with incoming notification messages. This is used when no icon is set for the incoming notification message. -->\n<meta-data\nandroid:name=\"com.google.firebase.messaging.default_notification_icon\"\nandroid:resource=\"@mipmap/ic_launcher_round\" />\n<!-- Set color used with incoming notification messages. This is used when no color is set for the incoming notification message. -->\n<meta-data\nandroid:name=\"com.google.firebase.messaging.default_notification_color\"\nandroid:resource=\"@color/colorAccent\" />", "language": "java" } ] } [/block] [block:callout] { "type": "success", "body": "[Send a push notification to your app](https://support.dotmailer.com/hc/en-gb/articles/360001827870) from the program builder in dotmailer.", "title": "Next steps" } [/block] # Android sample code [block:code] { "codes": [ { "code": "package com.example.testapp;\n\nimport com.google.firebase.FirebaseApp;\n\nimport com.comapi.Comapi;\nimport com.comapi.ComapiClient;\nimport android.app.Application;\n\nimport com.comapi.Callback;\nimport com.comapi.ComapiConfig;\nimport com.comapi.Session;\nimport com.comapi.internal.network.ComapiResult;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class MyApplication extends Application {\n\n @Override\n public void onCreate()\n {\n super.onCreate();\n \n FirebaseApp.initializeApp(this);\n\n ComapiConfig config = new ComapiConfig();\n //<API space ID> string must be the same as the value of the 'API space ID' field in your push notification profile in dotmailer.\n config.apiSpaceId(\"<API space ID>\");\n //Set the handler for authentication challenges (SDK asking for JWT token)\n config.authenticator(new AuthChallengeHandler());\n\n Comapi.initialiseShared(this, config, new Callback<ComapiClient>() {\n @Override\n public void success(final ComapiClient client) {\n if(client.getSession() != null && client.getSession().isSuccessfullyCreated()) {\n client.service().profile().getProfile(client.getSession().getProfileId(), new Callback<ComapiResult<Map<String, Object>>>() {\n @Override\n public void success(ComapiResult<Map<String, Object>> result) {\n Map <String, Object> additionalMap = new HashMap<>();\n //Add the user's email address to the profile\n additionalMap.put(\"email\", \"exampleappuser1@gmail.com\");\n client.service().profile().patchMyProfile(additionalMap, result.getETag(), new Callback<ComapiResult<Map<String, Object>>>() {\n @Override\n public void success(ComapiResult<Map<String, Object>> result) {\n }\n @Override\n public void error(Throwable t) {\n }\n });\n }\n @Override\n public void error(Throwable t) {\n }\n }\n );\n } else {\n client.service().session().startSession(new Callback<Session>() {\n @Override\n public void success(Session result) {\n\n }\n\n @Override\n public void error(Throwable t) {\n\n }\n });\n }\n\n }\n\n @Override\n public void error(Throwable t) {\n //Error\n }\n });\n }\n}", "language": "java", "name": "Java sample application" } ] } [/block] [block:code] { "codes": [ { "code": "package com.example.testapp;\n\nimport android.app.Application;\n\nimport com.google.firebase.FirebaseApp;\n\nimport com.comapi.ComapiConfig;\nimport com.comapi.RxComapi;\nimport com.comapi.RxComapiClient;\nimport com.comapi.internal.network.ComapiResult;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport rx.Observable;\nimport rx.Subscriber;\nimport rx.functions.Func1;\n\npublic class MyApplication extends Application {\n\n @Override\n public void onCreate()\n {\n super.onCreate();\n \n FirebaseApp.initializeApp(this);\n\n RxComapi.initialise(\n this, new ComapiConfig()\n //<API space ID> string must be the same as the value of the 'API space ID' field in your push notification profile in dotmailer.\n .apiSpaceId(\"<API space ID>\")\n //Set the handler for authentication challenges (SDK asking for JWT token)\n .authenticator(new ChallengeHandler(getSharedPreferences(Const.PREFS_NAME, MODE_PRIVATE)))\n ).flatMap(new Func1<RxComapiClient, Observable<RxComapiClient>>() {\n @Override\n public Observable<RxComapiClient> call(final RxComapiClient client) {\n if (client.getSession() != null && client.getSession().isSuccessfullyCreated()) { \n return Observable.fromCallable(new Callable<RxComapiClient>() {\n @Override\n public RxComapiClient call() throws Exception {\n return client;\n }\n });\n } else {\n return client.service().session().startSession().map(new Func1<Session, RxComapiClient>() {\n @Override\n public RxComapiClient call(Session session) {\n return client;\n }\n });\n }\n }\n }).flatMap(new Func1<RxComapiClient, Observable<ComapiResult<Map<String, Object>>>>() {\n @Override\n public Observable<ComapiResult<Map<String, Object>>> call(final RxComapiClient client) {\n if (!client.getSession().isSuccessfullyCreated()) {\n return Observable.error(new Exception(\"Failed to update profile\"));\n }\n return client.service()\n .profile()\n .getProfile(client.getSession().getProfileId())\n .flatMap(new Func1<ComapiResult<Map<String, Object>>, Observable<ComapiResult<Map<String, Object>>>>() {\n @Override\n public Observable<ComapiResult<Map<String, Object>>> call(ComapiResult<Map<String, Object>> result) {\n Map<String, Object> additionalMap = new HashMap<>();\n //Add the user's email address to the profile\n additionalMap.put(\"email\", \"exampleappuser1@gmail.com\");\n return client.service().profile().patchMyProfile(additionalMap, result.getETag());\n }\n }\n );\n }\n }).subscribe(new Subscriber<ComapiResult<Map<String, Object>>>() {\n @Override\n public void onCompleted() {\n }\n @Override\n public void onError(Throwable e) {\n }\n @Override\n public void onNext(ComapiResult<Map<String, Object>> result) {\n }\n });\n }\n}\n", "language": "java", "name": "RxJava sample application" } ] } [/block]