How To Create Context Aware Coupons

Using Plot Projects to Create Context Aware Coupons, Offers and Promotions

More and more research is pointing out that relevance is the key metric to increase offer redemption in advertisement. Plot Projects is successfully enabling our clients to reach out to their customers in the right place at the right time. Further customer segmentation however is an exercise left for the implementor. Many mobile apps have some form of customer profile, either through social media logins or customer loyalty programs.

Context Aware Coupons

In this article I will demonstrate how Plot Projects can be used to target users based on their location and profile through an Android example application. Fork the project on GitHub. The example project can be found in the coupons/android directory.

The application consists of two tabs on the main activity. The first one allows the client to pick categories of interest. This represents the profile. The second tab allows an admin to enter a new coupon. The coupon will be placed on your last known GPS locations so be sure to open Google Maps or something similar if you wish this to be accurate.


First create a Plot account if you haven’t already done so. Edit src/com/plotprojects/coupons/ and enter your public and private key.

final static String PLOT_ACCOUNT_ID = "<my account id>";
final static String PLOT_PUBLIC_KEY = "<my public key>";
final static String PLOT_PRIVATE_KEY = "<my private key>";

Please note that it is a bad idea to put your private key into the app in production. It is better to have a backend system you control communicate with our system using this key. But for the purpose of this example this isn’t necessary. The project can be built by opening a command terminal in coupons/android and running the ant build command. This will generate an apk file that can be installed on your phone in out/production/android.

Basic Setup

The basic setup happens in MainActivity. Here the Plot plugin is initialized with the public key.

Plot.init(getApplicationContext(), plotConfiguration);

The following permissions are needed:

  • The plugin will need to access the internet to be able to retrieve notifications.
  • The plugin will need access to the phones location services in order to check the vicinity to a geofence.
  • The plugin will need to be able to re-enable itself after the phone has rebooted
  • The plugin would like to vibrate the phone when a notification is shown
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.VIBRATE"/>

Additionally the following receivers and services need to be registered:

<receiver android:name="">
        <action android:name="android.intent.action.BOOT_COMPLETED" />
        <action android:name="android.intent.action.QUICKBOOT_POWERON" />
        <category android:name="android.intent.category.HOME" />
<service android:name=""
    <meta-data android:name="debug" android:value="true" />
<service android:name="" />
<receiver android:exported="false"
        <action android:name="" />

The state of the buttons is persisted by the Settings class. By default all categories are off. The CouponService contains an example of how the Plot API can be used to create new notifications.

Filtering Notifications

The Plot plugin allows the filtering of notifications. In this example we use this to filter out notifications that belong to categories the user hasn’t subscribed to. This is done by registering our filter in AndroidManifest.xml as

    public List<FilterableNotification> filterNotifications(List<FilterableNotification> filterableNotifications) {
        // Get the users preferences
        Settings settings = new Settings(getApplicationContext());
        List<FilterableNotification> filteredNotifications = new ArrayList<FilterableNotification>(filterableNotifications.size());
        for (FilterableNotification notification : filterableNotifications) {
            try {
                String category = new JSONObject(notification.getData()).getString("category").toLowerCase();
                if (settings.getBoolean(category, false)) {
                    Log.d(LOG_TAG, "Category active: " + category);
                } else {
                    Log.d(LOG_TAG, "Category not active: " + category);
            } catch (Exception e) {
                Log.e(LOG_TAG, "Malformed notification: " + notification.getData(), e);
        return filteredNotifications;

As you can see this class implements filterNotifications and filters out any notifications that are not enabled in settings.

Handling Notification Actions

When the user touches the notification body it should lead to a place where this message can be acted upon right away. In this case this means we take the user to a coupon view: Please note that the full action name should be your base package name as defined in your manifest root appended with plot.OpenNotification, so in this case that would be The onReceive method needs to be overridden and passes the notification on to the ViewCouponActivity. This activity takes the notification data and displays it. Normally this would be the place to display details on the coupon along with a picture. One could do this by adding a coupon id to the notification data.

    public void onReceive(Context context, Intent intent) {
        FilterableNotification notification = intent.getParcelableExtra("notification");
        Log.d(LOG_TAG, "Opening notification: " + notification.toString());
        Intent openIntent = new Intent(context, ViewCouponActivity.class);
        openIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);


This setup shows what is the minimum of what needs to be done to create context aware notifications. The code on GitHub is there to be freely modified and adapted to your needs and can be used commercially.

A few things that would improve the example are:

  • Offer an opt-in to the user on first start. Users can be sensitive about location data and will appreciate you asking them nicely.
  • When looking up the user’s location, try to get a fresh location if the last known location is older than 2 minutes.
  • Have your own backend create the notifications instead of the phone to prevent having to put your private key inside the app.
  • Create a database of coupons inside the app and associate notifications with their ID in order to display more information and keep statistics up to date.
  • Apply nice graphics to the app.
  • More loosely coupled and testable components for instance using Roboguice.

Perhaps we will revisit this example in a later blog post and improve on one of these. Until then I am curious what you can come up with yourself.