Integration

Kumulos provides an SDK to ease the integration of the Kumulos Build, Push Notification and Analytics features into your Android app. This guide provides an overview of setting up the SDK for your project and sample usage. This assumes that you've deployed your API for Android and downloaded the libraries.

Project Setup

Copy both of the AAR files in to your project's libs directory and follow the steps below.

In your app's build.gradle file, you need to:

  • Add the libs directory as a repository
  • Exclude conflicting meta data files from the build
  • Set up the manifest placeholder for your GCM project
  • Add the Kumulos library dependencies

A sample build.gradle can be seen below.

// Add libs dir as a repository
repositories{
    flatDir {
        dirs 'libs'
    }
}

android {
    ...
    defaultConfig {
        ...
        // Add the sender ID to use push notifications with GCM
        // Leave blank if you don't want to configure push
        manifestPlaceholders = [
            kumulos_gcm_sender_id: ''
        ]
        ...
    }

    // Exclude conflicting files from the build
    packagingOptions {
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/ASL2.0'
        exclude 'META-INF/LICENSE'
    }
    ...
}

dependencies {
    // Kumulos dependencies
    compile 'com.squareup.okhttp3:okhttp:3.8.1'
    compile 'org.codehaus.jackson:jackson-mapper-asl:1.8.5'
    // Required for push notifications
    compile 'com.google.android.gms:play-services-gcm:+'
    // Kumulos debug & release libraries
    debugCompile(name: 'kumulos-3.0.0-debug', ext: 'aar')
    releaseCompile(name: 'kumulos-3.0.0-release', ext: 'aar')
}

The *-debug.aar file will have logging enabled, whereas the *-release.aar will have all logging disabled. Logs are written to the debug log with a tag matching com.kumulos.*. When running in debug mode, the log messages should be visible in LogCat.

Initialization

To initialize the SDK, we recommend subclassing the Application class and initializing Kumulos in its onCreate method.

Create an Application class

package com.example;

import android.app.Application;
import com.kumulos.android.Kumulos;
import com.kumulos.android.KumulosConfig;

public class ExampleApp extends Application {

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

        KumulosConfig config = new KumulosConfig.Builder("YOUR_API_KEY", "YOUR_SECRET_KEY").build();
        Kumulos.initialize(this, config);
    }
}

Your API key and secret key can be found on the app dashboard in Kumulos' control panel.

You don't need to initialise Kumulos every time you wish to make an API call. It only needs to be initialised once, after that it will remember the API key and secret key you've given it the first time.

Add your Application to the AndroidManifest

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

    <!-- Set the android:name to your custom Application class -->
    <application
        android:name=".ExampleApp"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        ...
    </application>

</manifest>

Calling API Methods

To talk to Kumulos, you need three things:

  • your method title (in lowerCamelCase)
  • a method parameters map (field names are also in lowerCamelCase)
  • some response handler code

Parameter maps

Parameters are constructed as a standard Java HashMap<String, String>.

HashMap<String, String> params = new HashMap<String, String>();
params.put("PARAM_NAME_1", "PARAM_VALUE_1");

You can use the static String.ValueOf() method to convert numbers to strings, for example:

int selectedUser=3;
HashMap<String, String> params = new HashMap<String, String>();
params.put("userID", String.valueOf(selectedUser));

Response Handler

As for the response handler, we create an anonymous extension of the com.kumulos.android.ResponseHandler class and override its didCompleteWithResult method.

Kumulos.call("YOUR_METHOD_TITLE", params, new ResponseHandler() {
    @Override
    public void didCompleteWithResult(Object result) {
        // Process the results
        // Do updates to the UI by calling back to the main UI thread with a Handler or posted Runnable
    }
});

And you're done! You can now make calls to Kumulos from your android application. Read on for a little more information about result handling.

If you are making the request from a background thread, such as an IntentService, you should use the Kumulos#callSync blocking method.

Handling Results

The Object passed in to your response handler will take different forms based on what type of action the API method returns.

Select Actions

Select actions will return an ArrayList of LinkedHashMap<String, Object> and can be cast as such to allow iteration like so:

Kumulos.call("YOUR_METHOD_TITLE", params, new ResponseHandler() {
    @Override
    public void didCompleteWithResult(Object result) {    
        ArrayList<LinkedHashMap<String, Object>> objects = (ArrayList<LinkedHashMap<String,Object>>) result;
        for (LinkedHashMap<String, Object> item : objects) {
            String field1 = (String) item.get("NAME_OF_FIELD1");
            Integer field2 = (Integer) item.get("NAME_OF_FIELD2");
            //...
        }
    }
});

The objects in the array list are LinkedHashMap<String,Object> instances where the types of the values have been converted to regular Java types such as String, Integer, and Double.

Any nested objects in the object will also be of the LinkedHashMap<String,Object> type.

We would recommend you which you create model instances (e.g. POJO) to suit your client app by casting the HashMap fields to client model fields.

All Other Actions

All other actions in Kumulos currently return number values, either integers of doubles. Therefore, the result value may be either an Integer, or Double.

Please note it is possible that the result will be null in some cases and your code should take this into account.

Handling Data Fields

Kumulos stores data fields as base64 encoded data, so if you receive data from Kumulos, you will have to base64 decode it to access the original data. Similarly, when you send data to Kumulos, you should base64 encode it before transport.

To achieve this, please refer to the Android SDK Base64 utility.

Push Notifications

The Kumulos SDK provides push notifications with GCM. To integrate Kumulos Push into your Android project, you have to complete the following steps:

  1. Set up a Google Project & enable GCM
  2. Configure Push in the Kumulos Agency Console
  3. Integrate SDK components with your Android project
  4. Register for push form the client app

Configuration & Integration

In order to enable push notifications for Android with Kumulos, you'll need to set up a GCM project and configure push for your app. These steps are shown in the following video guide.

Configuring GCM for Android with Kumulos

After configuring Kumulos & GCM, simply follow the SDK integration steps below.

Integrate the SDK

Set up the kumulos_gcm_sender_id manifest placeholder in your app's build.gradle:

android {
    ...
    defaultConfig {
        ...
        // Add the Google project number to use push notifications with GCM
        manifestPlaceholders = [
            kumulos_gcm_sender_id: ''
        ]
        ...
    }
    ...
}

Add the Kumulos PushBroadcastReceiver to your AndroidManifest.xml:

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

    <!-- Optionally add the wake lock permission to stop the CPU from sleeping when a message is received -->
    <!-- <uses-permission android:name="android.permission.WAKE_LOCK" /> -->

    <application
        android:name=".ExampleApp"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        ...

        <!-- Kumulos Push receiver -->
        <receiver android:name="com.kumulos.android.PushBroadcastReceiver" android:exported="false">
            <intent-filter>
                <action android:name="com.kumulos.push.RECEIVED" />
                <action android:name="com.kumulos.push.OPENED" />
            </intent-filter>
        </receiver>
    </application>

</manifest>

Register the client app installation to receive push notifications when you consider it appropriate:

Kumulos.pushRegister(context)

The pushRegister() method takes context as a parameter, from where it extracts the device's push token and sends this to Kumulos (so you can then send push notifications to that app on that device).

This will prompt the user whether or not they want to receive push notifications for this app. Therefore, while it is upto you to decide when it is appropriate to do this, we do not recommend doing this as soon as the app is installed (as the user has no incentive to accept at this point). Instead, it is better to wait until the user is actively using the app, which should result in a higher subscription rate.

If you want to unregister the installation, you can use Kumulos.pushUnregister().

Default Push Behavior

By default, the Kumulos PushBroadcastReceiver will show a notification on the notification area of the device when a content push is received.

Tapping this notification will open the main launcher activity of your application and track the push open conversion for you.

Your main activity will receive the push contents in its options bundle under the PushMessage.EXTRAS_KEY.

No action is taken for data pushes by default.

Changing the Push Icon

To change the icon shown in the status bar on Android, you can configure Kumulos with a drawable at initialization time:

KumulosConfig config = new KumulosConfig.Builder("API_KEY", "SECRET_KEY")
    .setPushSmallIconId(R.id.my_push_small_icon)
    .build();
Kumulos.initialize(this, config);

Make sure to comply with the status bar icon guidelines so the icon renders correctly on all devices. For help preparing assets, we suggest checking out the Android Asset Studio

Customizing Push Behavior

To customize the behavior of the SDK when a push is received or its notification is tapped, we suggest subclassing the PushBroadcastReceiver and overriding its base methods depending on what you want to customize.

Example extension class:

package com.example;

import com.kumulos.android.PushBroadcastReceiver;

public class MyPushReceiver extends PushBroadcastReceiver {

}

Make sure to change the AndroidManifest.xml receiver:

<receiver android:name="com.example.MyPushReceiver" android:exported="false">
    <intent-filter>
        <action android:name="com.kumulos.push.RECEIVED" />
        <action android:name="com.kumulos.push.OPENED" />
    </intent-filter>
</receiver>

Changing the Launched Activity

To change which activity will be launched when the user taps a notification, you can override the PushBroadcastReceiver#getPushOpenActivityIntent(Context, PushMessage).

package com.example;

import android.content.Context;
import android.content.Intent;

import com.kumulos.android.PushBroadcastReceiver;
import com.kumulos.android.PushMessage;

public class MyPushReceiver extends PushBroadcastReceiver {

    @Override
    protected Intent getPushOpenActivityIntent(Context context, PushMessage pushMessage) {
        // TODO implement your own logic here
        return super.getPushOpenActivityIntent(context, pushMessage);
    }
}

The PushMessage model will not be added to the Intent by default, it is up to you to add it as an extra if desired:

Intent launchIntent = new Intent(context, MyActivity.class);
launchIntent.putExtra(PushMessage.EXTRAS_KEY, pushMessage);

You can return null to track the push conversion and do nothing when the notification is tapped.

If the Intent returned does not describe an Activity, it will be ignored

Customizing the Notification

To customize the notification shown to the user for content pushes, you can override PushBroadcastReceiver#buildNotification(Context, PushMessage).

package com.example;

import android.app.Notification;
import android.content.Context;

import com.kumulos.android.PushBroadcastReceiver;
import com.kumulos.android.PushMessage;

public class MyPushReceiver extends PushBroadcastReceiver {

    @Override
    protected Notification buildNotification(Context context, PushMessage pushMessage) {
        // TODO customize the notification
        return super.buildNotification(context, pushMessage);
    }
}

If you want to handle the open with the broadcast receiver, be sure to set up the content intent of the notification as follows:

Intent openIntent = new Intent(PushBroadcastReceiver.ACTION_PUSH_OPENED);
        openIntent.putExtra(PushMessage.EXTRAS_KEY, pushMessage);
        openIntent.setPackage(context.getPackageName());

PendingIntent pendingOpenIntent = PendingIntent.getBroadcast(
        context,
        (int) pushMessage.getTimeSent(),
        openIntent,
        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT);

...

notificationBuilder.setContentIntent(pendingOpenIntent);

This will ensure that the notification conversion is tracked in Kumulos.

If you want to do something else, you can manually track push open conversion using Kumulos#pushTrackOpen(String).

Kumulos.pushTrackOpen(pushMessage.getId());

Launching a Service for Background Data Pushes

To launch a service when a background data push is received, you can override PushBroadcastReceiver#getBackgroundPushServiceIntent.

package com.example;

import android.content.Context;
import android.content.Intent;

import com.kumulos.android.PushBroadcastReceiver;
import com.kumulos.android.PushMessage;

public class MyPushReceiver extends PushBroadcastReceiver {

    @Override
    protected Intent getBackgroundPushServiceIntent(Context context, PushMessage pushMessage) {
        // TODO implement your own logic here
        return super.getBackgroundPushServiceIntent(context, pushMessage);
    }
}

This would easily allow you to handle data processing in the background by launching an IntentService for example.

The PushMessage model will not be added to the Intent by default, it is up to you to add it as an extra if desired:

Intent serviceIntent = new Intent(context, MyIntentService.class);
serviceIntent.putExtra(PushMessage.EXTRAS_KEY, pushMessage);

Return null if you want to do nothing with the data push.

If the Intent returned does not describe a Service, it will be ignored

URL Pushes

Push notifications sent to open a URL will, by default, track the push open if the user taps the notification, and then open the default web browser.

Overriding All Behaviors

If you want to completely replace the logic for handling pushes, you can override PushBroadcastReceiver#onPushReceived(Context, PushMessage).

Bear in mind you will be responsible for all aspects of the push process such as showing a notification to the user, tracking an open conversion using Kumulos#pushTrackOpen(String), or launching any activities or services.

Push Channels

You can create and manage subscriptions to push channels from the SDK. Channels allow you to send push notifications to certain interest groups.

All channel management is performed with an instance of the PushSubscriptionManager class.

You can create an instance using the default constructor:

PushSubscriptionManager manager = new PushSubscriptionManager();

This constructor requires the Kumulos SDK to be initialized as described in the Initialization section of this guide

The push manager exposes several methods to manage subscriptions:

public interface PushSubscriptionManagerInterface {
    void subscribe(Context c, String[] uuids, Kumulos.Callback callback);

    void unsubscribe(Context c, String[] uuids, Kumulos.Callback callback);

    void setSubscriptions(Context c, String[] uuids, Kumulos.Callback callback);

    void clearSubscriptions(Context c, Kumulos.Callback callback);

    void listChannels(Context c, Kumulos.ResultCallback<List<PushChannel>> callback);

    void createChannel(Context c, String uuid, boolean subscribe, Kumulos.ResultCallback<PushChannel> callback);

    void createChannel(Context c, String uuid, boolean subscribe, @Nullable String name, boolean showInPortal, @Nullable JSONObject meta, Kumulos.ResultCallback<PushChannel> callback);
}

Results are handled with instances of the Kumulos.Callback or Kumulos.ResultCallback<ResultType> classes. For example:

PushSubscriptionManager manager = new PushSubscriptionManager();
manager.listChannels(this, new Kumulos.ResultCallback<List<PushChannel>>() {
    @Override
    public void onSuccess(List<PushChannel> result) {
        // Handle the result
    }

    // Optional error override
    @Override
    public void onFailure(Exception e) {
        // Handle the error
    }
});

When creating channels, the visiblity and meta data can be controlled. Please make sure you understand these concepts when creating channels from the SDK.

Installation ID

When initialized for the first time, the Kumulos SDK will create a unique identifier for the app installation that initialized the SDK.

This identifier can be used to target push notifications to a specific device through KScript or the Push Notifications API.

In order to retrieve this installation ID, simply access the class variable:

String id = com.kumulos.android.Installation.id(context);

Once you have the installation ID, you can send it to your app's backend to be used later for push targeting. For more information about push targeting, please see the KScript Documentation or push notification documentation as appropriate.

Changelog

3.0.0

  • Use OkHttp instead of Android AsyncHttpClient
  • Add config builder for initialization
  • Add default push notification small icon
  • Allow configuration of push small icon
  • Raise minimum API level to 14

Breaking Changes

  • Removed Kumulos.initWithAPIKeyAndSecretKey, use Kumulos.initialize instead
  • Removed ResponseHandler.onStart and ResponseHandler.onFinish
  • Callbacks are no longer called on the main thread

Migration Guide

In order to initialize Kumulos, you should change your initWithAPIKeyAndSecretKey call to the following:

KumulosConfig config = new KumulosConfig.Builder("API_KEY", "SECRET_KEY").build();
Kumulos.initialize(this, config);

Note this should be done in your application class's onCreate method as specified in the integration guide

To migrate any calls that update the UI from a callback, you should post the UI update back to the main thread as follows:

Kumulos.call("getThings", new ResponseHandler() {
    @Override
    public void didCompleteWithResult(@Nullable Object result) {
        // Process the result
        String newText = ...;

        // Post UI updates back to the UI thread
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // Example update to UI component
                mEditText.setText(result);
            }
        });
    }
});

For more information, see the Android developer guide

2.4.3

  • Prevent IllegalStateException when running on Android O

2.4.2

  • Guard against null actions in stats service

2.4.1

  • Guard against null actions in GCM registration

2.4.0

  • Add push notification URL open handling
  • Add background data push notification handling

2.3.0

  • Add PushSubscriptionManager to allow management of push channels & subscriptions
  • Make constructor of PushMessage package-visible instead of public

2.2.0

  • Allow overriding of ResponseHandler#onSuccess to access raw JSON response from API calls
  • Fix occasional authentication header issues from networking library

2.1.0

  • Fix usage of Kumulos from an arbitrary Thread in a Service where no Looper is bound
  • Add Kumulos#callSync method to perform blocking networking requests, suitable for use in background threads such as an IntentService
  • Fix NullPointerExceptions in some IntentServices
  • Ignore GCM payloads from other push services if used instead of Kumulos

2.0.1

  • Fix java.lang.NullPointerException in response handler if the JSON response fails to parse. In this case, ResponseHandler#didFailWithError will be called.
  • Remove logging of full response body in response parser to prevent cluttering LogCat when data fields are used

2.0.0

  • Move all classes to the com.kumulos.android package
  • Change debug log tags to match com.kumulos.*
  • Add support for push notifications with GCM
  • Bring minimum SDK to API level 11

1.0.0

  • Add session support to Android
  • Update to android-async-http 1.4.9

Thanks

We would like to thank Aamir Latif, Alex Eskandari, and David Montiel for their contributions to the Android libraries.

Additionally we'd like to thank the Jackson JSON Parser and OkHttp projects for their excellent work on which we build our library.