Urban Airship

Table of Contents

Android

Getting Started

Urban Airship supports push notifications for Android devices via Google’s GCM (Google Cloud Messaging for Android) service and for Amazon devices via Amazon’s ADM (Amazon Device Messaging) service.

This document explains how to get started sending notifications to your Android and Amazon apps.

Production vs. Development Apps in Urban Airship

Begin by creating both a development and production version of your app.

For Android devices, test GCM functionality using the development app. All new apps use GCM by default. Any apps using legacy transport systems require assistance from support. Please email support@urbanairship.com if you need assistance upgrading to GCM.

For Amazon devices, test ADM functionality using the development app.

Urban Airship SDK Setup

Add the Library to your Project

First, download the current client library from our resources page. This zip file contains the library, as well as sample projects that work with both Eclipse and Android Studio.

Using Android Studio

Include the urbanairship-lib-x.x.x.aar file as a dependency in the project, where x.x.x is the version of Urban Airship you are using.

  1. Open your project in Android Studio

  2. Go to File -> New Module -> Import from .JAR or .AAR Package

  3. Select the downloaded urbanairship-lib-x.x.x.aar file

  4. Add the Urban Airship library, Android Support v4 library (revision 22 or newer), Android Support v7 CardView library (revision 22 or newer, required for in-app messaging) and Google Play Service GCM and Location (version 7.0 or newer) as dependencies in the build.gradle file:

    dependencies {
        compile project (':urbanairship-lib-x.x.x')
        compile 'com.android.support:support-v4:22.0.0'
    
        // Required for Android push notifications
        compile 'com.google.android.gms:play-services-gcm:7.0.0'
    
        // Recommended for in-app messaging
        compile 'com.android.support:cardview-v7:22.0.0'
    
        // Recommended if using location services
        compile 'com.google.android.gms:play-services-location:7.0.0'
    }
    
  5. Sync the project with the gradle file.

Using Eclipse

The Urban Airship library needs to be added as a project with resources. Import the urbanairship-lib project from the zip. Then include the project, the v4 support library and the v7 CardView library as library dependencies. More detailed steps for including library projects with resources are available in the Android Developer docs.

Proguard

If using proguard, add the following rules to the project’s proguard-project.txt file:

-keepnames class * implements android.os.Parcelable {
  public static final ** CREATOR;
}

## Required even for GCM only apps
-dontwarn com.amazon.device.messaging.**

## Required for the Javascript Interface
-keepclassmembers class com.urbanairship.js.UAJavascriptInterface {
   public *;
}

## Autopilot
-keep public class * extends com.urbanairship.Autopilot

If using GCM and Google Play Services, make sure to also follow the proguard section in the Setting Up Google Play Services Guide.

Modify the AndroidManifest.xml

AndroidManifest.xml modifications are required to use the Urban Airship Library.

Note

Replace any ${applicationId} with the package name of your application. No changes are required if your application uses the new gradle builds system with the new manifest merger.

Add the following permissions under the manifest tag:

<!-- REQUIRED for Urban Airship -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<!-- Keeps the processor from sleeping when a message is received. -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />  <!-- Required for Push -->

<permission android:name="${applicationId}.permission.UA_DATA" android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.permission.UA_DATA" />
<!-- The two elements above ensure that only this application has access to the Urban Airship provider -->

<!-- OPTIONAL Urban Airship Settings -->
<!-- REQUIRED FOR LOCATION -->
<!-- Use ACCESS_COARSE_LOCATION if GPS access is not necessary -->
<!-- uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!--  OPTIONAL - This permission is only necessary if your app has multiple processes -->
<!--  <uses-permission android:name="android.permission.BROADCAST_STICKY" /> -->

Add the following services, provider, receivers, and activities under the application tag:

<!-- REQUIRED for Action.startActivityForResult -->
<activity android:name="com.urbanairship.actions.ActionActivity" />

<!-- REQUIRED for Urban Airship Push -->
<activity android:name="com.urbanairship.CoreActivity" />

<!-- REQUIRED for Urban Airship Push. The priority is important to be set lower than the
application's push intent receiver in order for the push intent receiver to handle push intents
before the core receiver. This allows the application to launch any activities before Urban
Airship performs any actions or falls back to launching the application launch intent. -->
<receiver android:name="com.urbanairship.CoreReceiver"
          android:exported="false">

    <intent-filter android:priority="-999">
        <action android:name="com.urbanairship.push.OPENED" />
        <category android:name="${applicationId}" />
    </intent-filter>
</receiver>

<!-- REQUIRED for Landing Pages
    - For more customization details, see com.urbanairship.actions.LandingPageActivity -->
<activity
    android:name="com.urbanairship.actions.LandingPageActivity"
    android:exported="false">

    <intent-filter>
        <action android:name="com.urbanairship.actions.SHOW_LANDING_PAGE_INTENT_ACTION" />

        <data android:scheme="http" />
        <data android:scheme="https" />

        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

<!-- REQUIRED for Urban Airship -->
<service android:name="com.urbanairship.push.PushService" android:label="Push Notification Service" />

<!-- Required for analytics -->
<service android:name="com.urbanairship.analytics.EventService" android:label="Event Service" />

<!-- Required for Actions -->
<service android:name="com.urbanairship.actions.ActionService" />

<!-- Required for Rich Push -->
<service android:name="com.urbanairship.richpush.RichPushUpdateService" />

<!-- OPTIONAL for Urban Airship Location (for segments support) -->
<service android:name="com.urbanairship.location.LocationService" android:label="Segments Service" />

<!-- This is required for persisting preferences related to push and location -->
<provider
    android:name="com.urbanairship.UrbanAirshipProvider"
    android:authorities="${applicationId}.urbanairship.provider"
    android:permission="${applicationId}.permission.UA_DATA"
    android:exported="true"
    android:multiprocess="true" />

Enabling User Notifications

Enabling or disabling user notifications is a preference often best left up to the user, so by default, user notifications are disabled in the library. For testing purposes, enabling notification at startup requires a few extra lines of code to your main application class.

The Urban Airship client library requires only a single integration point in the application for basic push. Here is a simple example:

Application.java

public class MyApplication extends Application {

     @Override
     public void onCreate(){
          UAirship.takeOff(this, options);
          UAirship.shared().getPushManager().setUserNotificationsEnabled(true);
     }
}

In the code block above, we’ve added a call to UAirship.shared().getPushManager().setUserNotificationsEnabled(true) which persists the preference with Urban Airship. The Channel ID is your application’s unique push identifier, and is required in order to target a specific device when sending a push notification. Don’t worry if this value initially comes back as null on your app’s first run (or after clearing the application data), as the Channel ID will be created and persisted during registration. To receive an event when the Channel ID is created, see Listening for Push Events.

The options are read from the airshipconfig.properties file, but it is also possible to specify these in code:

Application.java

// Configure your application
//
// This can be done in code as illustrated here,
// or you can add these settings to a properties file
// called airshipconfig.properties
// and place it in your "assets" folder
AirshipConfigOptions options = AirshipConfigOptions.loadDefaultOptions(this);
options.developmentAppKey = "Your development app key";
options.productionAppKey = "Your production app key";
options.inProduction = false; //determines which app key to use

// Take off initializes the services
UAirship.takeOff(this, options);

Retrieving your Channel ID

The Channel ID is a unique identifier that ties together an application/device pair on Android and Amazon devices. The Channel ID is used to target pushes to specific devices using the Urban Airship API. Once a Channel ID is created, it will persist the application until it is either reinstalled, or its internal data is cleared.

The Channel ID will be logged for all apps. You can always get the Channel ID with a couple of extra lines to your application:

String channelId = UAirship.shared().getPushManager().getChannelId();
Logger.info("My Application Channel ID: " + channelId);

Once you see the Channel ID in your application’s console log, copy it down and save it for later – we’ll be using this string to send our first test push.

GCM Setup

Google Cloud Messaging (GCM) is the transport method that Urban Airship supports for Android notifications.

Get your API Key from Google

In order to configure your Urban Airship application for GCM, you must first obtain an API key from Google.

The steps are:

  1. Go to the Google Developers Console.

  2. Create a project:

  3. Enable GCM by selecting APIs & auth and turning the Google Cloud Messaging for Android toggle to ON.

  4. Generate an API Key.

    Urban Airship takes care of API Access authorization for you, so you do not need to create an OAuth 2.0 client ID.

    1. Select APIs & auth > Credentials.
    1. Under Public API access, click “Create new key”.
    2. In the Create a new key dialog, click “Server key”.
    1. Do not specify any IP addresses in the form, and click “Create”.

    2. Copy your key for server apps.

For more detailed instructions, we recommend reading the: GCM Getting Started guide from Google.

Setting up GCM support for your app

While you will not need in-depth knowledge of Google’s GCM platform in order to use the GCM transport option in Push, we recommend you review Google’s documentation before continuing, even if you have prior experience with C2DM (the previous incarnation of this service). GCM is similar to C2DM, although several setup details, including the authorization mechanism, have changed.

Before you can use GCM:

  1. Enable GCM for your sender account and obtain an API key and project number from Google.
  2. Open up your application in your Urban Airship web application (go.urbanairship.com).
  3. Follow the instructions for Configuring Your Services.
  4. Open your airshipconfig.properties file and add the following line, changing “Your Google API Project Number” to the project number that you created in Step 1. Note that this is distinct from the API key.
  5. Make sure you set developmentAppKey, developmentAppSecret, productionAppKey, and productionAppSecret.

airshipconfig.properties

gcmSender = Your Google API Project Number (allows multiple senders separated by commas)
developmentAppKey = Your Development App Key
developmentAppSecret = Your Development App Secret
productionAppKey = Your Production App Key
productionAppSecret = Your Production App Secret
inProduction = false

Setting up Google Play Services

The Urban Airship SDK now depends on Google Play Services for GCM registration as this is now Google’s recommended integration. The library is built against Google Play Services, but does not package it for applications. Instead, any application that wants to support GCM needs to include Google Play Services as a dependency. Setup instructions can be found here.

Urban Airship provides utility classes to simplify Google Play Services error handling.

AndroidManifest.xml changes:

<!-- REQUIRED for PlayServiceUtils.handleAnyPlayServicesError to handle Google Play Services recoverable errors. -->
<activity android:name="com.urbanairship.google.PlayServicesErrorActivity" android:theme="@android:style/Theme.Translucent.NoTitleBar" />

Add this call to your main activity’s onStart method:

// Handle any Google Play Services errors
if (PlayServicesUtils.isGooglePlayStoreAvailable()) {
    PlayServicesUtils.handleAnyPlayServicesError(this);
}

This will automatically start a transparent activity that checks for Google Play service errors. If any user recoverable errors are detected, the activity will show an error dialog to the user to resolve the issue.

Modify the AndroidManifest.xml

AndroidManifest.xml modifications are required to use GCM.

Note

Replace any ${applicationId} with the package name of your application. No changes are required if your application uses the new gradle builds system with the new manifest merger.

Add the following permissions under the manifest tag:

<!-- REQUIRED PERMISSIONS for GCM -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<!-- GCM requires a Google account. -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

<!-- This app has permission to register with GCM and receive message -->
<permission android:name="${applicationId}.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
<!-- The two elements above ensure that only this application can receive the messages and registration result -->

Add the following receiver and activity under the application tag:

<!-- REQUIRED for Google Play Services (GCM)-->
<meta-data
    android:name="com.google.android.gms.version"
    android:value="@integer/google_play_services_version" />

<!-- REQUIRED for PlayServiceUtils.handleAnyPlayServicesError to handle Google Play Services recoverable errors. -->
<activity
    android:name="com.urbanairship.google.PlayServicesErrorActivity"
    android:theme="@android:style/Theme.Translucent.NoTitleBar" />

<!-- REQUIRED for GCM -->
<receiver
    android:name="com.urbanairship.push.GCMPushReceiver"
    android:permission="com.google.android.c2dm.permission.SEND">

    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
        <category android:name="${applicationId}" />
    </intent-filter>
</receiver>

Complete samples of the AndroidManifest.xml can be found in the Rich Push Sample and in the Push Sample. Please note that you cannot copy and paste the sample’s manifest verbatim.

Configure Service With Urban Airship

In the UA web application, navigate to Settings then click Services to set up your application with GCM:

Add your GCM API Key from Google and your package here:

For more detail on configuring your apps as well as using the Urban Airship web application, check out the User Guide.

ADM Setup

Amazon Device Messaging (ADM) is the transport method that Urban Airship supports for Amazon notifications.

Get your OAuth Credentials and API Key from Amazon

In order to configure your Urban Airship application for ADM, you must first obtain OAuth Credentials and an API key from Amazon.

Sign in to the Amazon Apps & Games Developer Portal.

Follow the instructions to obtain your OAuth Credentials and API Key here.

After you obtain your OAuth Credentials and API Key, go to Security Profiles » General. Copy the Client ID and Client Secret (OAuth Credentials), which will be used in the Configure Amazon section below.

Then navigate to Security Profile » Android/Kindle Settings in the Amazon Developer Console. Store your API key as the only data in a file named api_key.txt, which will be used in the next section ADM Setup.

Set up ADM Support

While you will not need in-depth knowledge of Amazon’s ADM platform in order to use ADM for push notifications, we recommend that you review Amazon’s Understanding ADM documentation before continuing.

Before you can use ADM:

  1. Obtain the API key, Client ID and Client Secret for your application from Amazon (see previous section).
  2. Store your API Key as the only data in a file named api_key.txt and place the file inside the assets folder for your project.
  3. Follow the instructions in the next section Configure Amazon Service With Urban Airship.

For a release or production version of your app, see Store Your API Key as an Asset.

Configure Amazon Service With Urban Airship

In the UA web application, navigate to Settings then click Services to set up your application with ADM:

Add your Client ID and Client Secret here:

For more detail on configuring your apps as well as using the Urban Airship web application, check out the User Guide.

Modify AndroidManifest.xml

AndroidManifest.xml modifications are required to use the Urban Airship Library.

Note

Replace any ${applicationId} with the package name of your application. No changes are required if your application uses the new gradle builds system with the new manifest merger.

Add the Amazon namespace in the manifest tag:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:amazon="http://schemas.amazon.com/apk/res/android"
          android:versionCode="1"
          android:versionName="1.0">

Add the following permissions under the manifest tag:

<!-- REQUIRED PERMISSIONS for ADM -->
<uses-permission android:name="com.amazon.device.messaging.permission.RECEIVE" />

<permission android:name="${applicationId}.permission.RECEIVE_ADM_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.permission.RECEIVE_ADM_MESSAGE" />
<!-- The two elements above ensure that only this application can receive the messages and registration result -->

Add the following under the application tag:

<!-- REQUIRED for ADM - You must explicitly enable ADM and declare whether
your app cannot work without ADM (android:required="true") or can work without
ADM (android:required="false"). If you specify android:required="false",
your app must degrade gracefully if ADM is unavailable. -->
<amazon:enable-feature
    android:name="com.amazon.device.messaging"
    android:required="false" />

<!-- REQUIRED for ADM -->
<receiver
    android:name="com.urbanairship.push.ADMPushReceiver"
    android:permission="com.amazon.device.messaging.permission.SEND">

    <intent-filter>
        <action android:name="com.amazon.device.messaging.intent.REGISTRATION" />
        <action android:name="com.amazon.device.messaging.intent.RECEIVE" />
        <category android:name="${applicationId}" />
    </intent-filter>
</receiver>

Complete samples of the AndroidManifest.xml can be found in the Rich Push Sample and in the Push Sample. Please note that you cannot copy and paste the sample’s manifest verbatim.

Listening for Push Events

Push open, push received, and registration events are broadcast by Urban Airship. To listen for these events, you need to set a broadcast receiver that listens for these intents. The BaseIntentReceiver is a base class that handles parsing the raw intents for the application and provides convenient listener style callbacks.

To listen for these intents, first create a class that extends the BaseIntentReceiver:

public class IntentReceiver extends BaseIntentReceiver {

    private static final String TAG = "IntentReceiver";

    @Override
    protected void onChannelRegistrationSucceeded(Context context, String channelId) {
        Log.i(TAG, "Channel registration updated. Channel Id:" + channelId + ".");
    }

    @Override
    protected void onChannelRegistrationFailed(Context context) {
        Log.i(TAG, "Channel registration failed.");
    }

    @Override
    protected void onPushReceived(Context context, PushMessage message, int notificationId) {
        Log.i(TAG, "Received push notification. Alert: " + message.getAlert() + ". Notification ID: " + notificationId);
    }

    @Override
    protected void onBackgroundPushReceived(Context context, PushMessage message) {
        Log.i(TAG, "Received background push message: " + message);
    }

    @Override
    protected boolean onNotificationOpened(Context context, PushMessage message, int notificationId) {
        Log.i(TAG, "User clicked notification. Alert: " + message.getAlert());

        // Return false to let UA handle launching the launch activity
        return false;
    }

    @Override
    protected boolean onNotificationActionOpened(Context context, PushMessage message, int notificationId, String buttonId, boolean isForeground) {
        Log.i(TAG, "User clicked notification button. Button ID: " + buttonId + " Alert: " + message.getAlert());

        // Return false to let UA handle launching the launch activity
        return false;
    }

    @Override
    protected void onNotificationDismissed(Context context, PushMessage message, int notificationId) {
        Log.i(TAG, "Notification dismissed. Alert: " + message.getAlert() + ". Notification ID: " + notificationId);
    }
}

Add the intent receiver to the manifest with the proper intent filter:

<!-- OPTIONAL, if you want to receive push, push opened and registration completed intents -->
<!-- Replace the receiver below with your package and class name -->
<receiver android:name="com.urbanairship.push.sample.IntentReceiver"
          android:exported="false">

    <intent-filter>
        <action android:name="com.urbanairship.push.CHANNEL_UPDATED" />
        <action android:name="com.urbanairship.push.OPENED" />
        <action android:name="com.urbanairship.push.RECEIVED" />
        <action android:name="com.urbanairship.push.DISMISSED" />

        <!-- MODIFICATION REQUIRED - Use your package name as the category -->
        <category android:name="com.urbanairship.push.sample" />
    </intent-filter>
</receiver>

Tags & Aliases

To help target specific devices or users for a notification, we have Aliases and Tags.

Tags

Tags are a feature that allow you to attribute any arbitrary metadata to a specific device. Common examples include favorites such as sports teams or news story types.

Example:

Set<String> tags = new HashSet<String>();
tags.add("Some-Tag");
UAirship.shared().getPushManager().setTags(tags);

To add tags to an Android device:

Set<String> tags = new HashSet<String>();
tags = UAirship.shared().getPushManager().getTags();
tags.add("Some-Tag");
UAirship.shared().getPushManager().setTags(tags);

Aliases

Aliases allow you to associate multiple devices to a single user or profile that may be associated with more than one device, e.g., an end-user’s Android phone and iPad. A device can have only one alias, and a single alias should not be associated with more than 10 devices. If it is likely that there will be more than a few devices per alias in your use case, you’d be better served by using a tag.

Example:

UAirship.shared().getPushManager().setAlias("AliasString");

Interactive Notifications

Built-In Interactive Notification Types

Urban Airship provides a set of standard Interactive Notification types (See: Built-In Interactive Notification Types). It is the type that determines which buttons and corresponding labels will be available when you send a push. See the next section for where to specify that in the push payload. You control what happens when you send the push separately, by tying each button ID to a specific action.

Custom Interactive Notification Types (Notification Action Button Groups)

If you want to define a custom Interactive Notification type, you must register a new notification action button group.

Note

Urban Airship reserves category IDs prefixed with “ua_”. Any custom groups with that prefix will be dropped.

Custom NotificationActionButtonGroups are supported by registering the groups with the PushManager right after takeOff() using the addNotificationActionButtonGroup method.

Example:

// Define actions for the group
NotificationActionButton hiButtonAction = new NotificationActionButton.Builder("hi")
        .setLabel(R.string.hi)
        .setPerformsInForeground(true)
        .build();

NotificationActionButton byeButtonAction = new NotificationActionButton.Builder("bye")
        .setLabel(R.string.bye)
        .setPerformsInForeground(true)
        .build();

// Define the group
NotificationActionButtonGroup buttonGroup = new NotificationActionButtonGroup.Builder()
        .addNotificationActionButton(hiButtonAction)
        .addNotificationActionButton(byeButtonAction)
        .build();

// Add the custom group
airship.getPushManager().addNotificationActionButtonGroup("custom group", buttonGroup);

In-App Messaging

In-App Messages are banner notifications that appear inside of your app, regardless of the opt-in/opt-out status of a user.

Requirements

In-app messaging requires Android API 14 + (Ice Cream Sandwich) and push to be enabled. The v7 support CardView library is recommended but not required.

Display

By default, in-app messages will only display a single banner on app foreground after a 3-second delay. You can change this behavior to display the in-app message without delay, meaning that the in-app message will be diplayed upon arrival. If a message is already being displayed upon arrival, the new in-app message will be displayed on the next activity transition.

Enabling display ASAP (no delay):

UAirship.shared()
        .getInAppMessageManager()
        .setDisplayAsapEnabled(true);

The auto display delay can be adjusted:

UAirship.shared()
        .getInAppMessageManager()
        .setAutoDisplayDelay(1000l); // 1 second

In-app messages can also be triggered manually:

UAirship.shared()
        .getInAppMessageManager()
        .showPendingMessage(activity);

Auto displaying in-app messages can be disabled, to either stop in-app messages or to manually handle when in-app messages should be displayed:

UAirship.shared()
        .getInAppMessageManager()
        .setAutoDisplayEnabled(false);

Activities can be excluded from auto-displaying an in-app message. This is useful for preventing in-app message displays on screens where it would be detrimental for the user experience, such as splash screens, settings screens, or landing pages.

To exclude an activity, modify its manifest entry to include:

<meta-data
      android:name="com.urbanairship.push.iam.EXCLUDE_FROM_AUTO_SHOW"
      android:value="true" />

Custom Style

Sending in-app message color overrides is supported through the Message Composer and push API. Advanced styling is also supported by providing a custom banner view style in the applications theme with the attribute inAppMessageBannerStyle.

The banner view can be customized with the following attributes:

  • bannerFontPath - A path to custom font in the assets directory.
  • bannerPrimaryColor - The default banner primary color
  • bannerSecondaryColor - The default banner secondary color
  • bannerDismissButtonDrawable - Custom drawable for the banner’s dismiss button
  • bannerNoDismissButton - Removes the banner dismiss button (still dismissible by swiping)
  • bannerTextAppearance - Text appearance style for the message’s text
  • bannerActionButtonTextAppearance - Text appearance style for the message’s text

If the CardView v7 support library is included, the banner view will be an extension of a CardView, so any of the card attributes also apply.

Example:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
   <!--Set the custom banner style-->
   <item name="inAppMessageBannerStyle">@style/AppBannerStyle</item>
</style>

<!-- Custom in-app message style -->
<style name="AppBannerStyle" parent="InAppMessage.Banner">
   <item name="cardElevation">20dp</item>
   <item name="bannerFontPath">"fonts/MyFont.ttf"</item>
   <item name="bannerPrimaryColor">#ff990f</item>
   <item name="bannerSecondaryColor">#334455</item>
   <item name="bannerNoDismissButton">false</item>
   <item name="bannerDismissButtonDrawable">@drawable/ic_close_white_18dp</item>
   <item name="bannerTextAppearance">@style/AppBannerTextAppearance</item>
   <item name="bannerActionButtonTextAppearance">@style/AppBannerActionButtonTextAppearance</item>
</style>

<!-- Custom in-app message text style -->
<style name="AppBannerTextAppearance" parent="InAppMessage.Banner.TextAppearance">
   <item name="android:textSize">30sp</item>
   <item name="android:textStyle">italic</item>
</style>

<!-- Custom in-app message action button text style -->
<style name="AppBannerActionButtonTextAppearance" parent="InAppMessage.Banner.TextAppearance">
   <item name="android:textSize">18sp</item>
</style>

Actions

Urban Airship Actions provides a convenient way to automatically perform tasks by name in response to push notification, Rich App Page or Landing Page interactions and JavaScript.

An action is an abstraction over a unary function, which takes an argument and performs a defined task, producing an optional result. Actions may restrict or vary the work they perform depending on the arguments they receive, which may include type introspection and runtime context.

The Urban Airship library comes with pre-made actions for common tasks such as setting/modifying tags, showing a landing page, enabling deep linking and opening URLs out of the box. Actions can also be extended to enable custom application behaviors and engagement experiences.

Actions are sent with a push by supplying a JSON payload as a push extra with the key com.urbanairship.actions. The JSON payload is a map of the action names to action values. The values can be any valid JSON value.

Actions are triggered with additional context in the form of a Situation, which corresponds to the way the action was triggered. The different situations allow actions to determine whether they should run, or potentially perform different behaviors depending on this context.

Available Situations
  • MANUAL_INVOCATION: action is invoked manually.
  • PUSH_RECEIVED: action is triggered when a push is received.
  • PUSH_OPENED: action is triggered when a notification is opened.
  • WEB_VIEW_INVOCATION: action is triggered from a web view.
  • FOREGROUND_NOTIFICATION_ACTION_BUTTON: action is triggered from a foreground notification action button.
  • BACKGROUND_NOTIFICATION_ACTION_BUTTON: action is triggered from a background notification action button.

Action Registry

The action registry is the central place to register actions by name. Each entry in the registry contains an action, the names that the action is registered under, a predicate that allows filtering when an action can run, and allows specifying alternative actions for different situations.

Registering an action:

Action customAction = new CustomAction();
UAirship.shared()
         .getActionRegistry()
         .registerAction(customAction, "my_action_name");

Looking up an entry:

ActionRegistry.Entry entry = UAirship.shared()
                                      .getActionRegistry()
                                      .getEntry("my_action_name");

Setting a predicate:

// Predicate that will reject PUSH_RECEIVED causing the action to never run during that situation.
Predicate<ActionArguments> rejectPushReceivedPredicate = new Predicate<ActionArguments>() {
    @Override
    public boolean apply(ActionArguments arguments) {
        return !(Situation.PUSH_RECEIVED.equals(arguments.getSituation()));
    }
};

entry.setPredicate(rejectPushReceivedPredicate);

Triggering Actions

Actions can be triggered automatically through push via our REST API or Message Composer platform. They can also be triggered automatically from a web view displaying rich message content. This interaction between web content and the Actions Framework is described in more detail in Custom HTML Templates.

It is possible to trigger Actions manually as well. To guarantee proper execution of Actions, all Actions should run through the ActionRunRequest. The ActionRunRequest class provides a fluent API for running actions. The action run request takes in a value, situation, and metadata and constructs the ActionArguments and passes it to the action to run. ActionArguments value can only contain an ActionValue instance. ActionValue is a class that limits the type of values to those that can be JSON serializable and contains getter methods that automatically does type checking to parse value back into its original form.Each action is run on its own thread when triggered asynchronously. Synchronous runs will block and should never be called on the UI thread.

Example:

// Running an action directly through the ActionRunRequest
ActionRunRequest.createRequest("actionName")
                .setSituation(Situation.MANUAL_INVOCATION)
                .setValue("actionValue")
                .run();

// Running an action by registered name
ActionRunRequest.createRequest("my_action_name")
                .setValue("actionValue")
                .run();

// An optional callback when finished
ActionRunRequest.createRequest("my_action_name")
                .setValue("actionValue")
                .run(new ActionCompletionCallback() {
                    public void onFinish(ActionArguments arguments, ActionResult result) {
                        Logger.info("Action finished!  Result: " + result);
                    }
                });

// Block until the action finishes
ActionResult result = ActionRunRequest.createRequest("my_action_name")
                                      .runSync();

Landing Page Action

The landing page action allows showing a rich content page when a user clicks a notification. By default, it will be shown as a full screen activity using the application theme. An action bar will be shown if the landing page is shown on a device running HONEYCOMB or higher (3.0). A guide to fully customize the landing page can be found here: Android Landing Page Customization.

Accepted Arguments
  • URL defined as a String

  • A Map containing:
    • url: URL defined as a String
    • cache_on_receive: flag to enable or disable caching when a PUSH_RECEIVED. Defaults to false.
Default Action Names
  • landing_page_action
  • ^p
Accepted Situations
  • Situation.PUSH_OPENED
  • Situation.PUSH_RECEIVED
  • Situation.WEB_VIEW_INVOCATION
  • Situation.MANUAL_INVOCATION
  • Situation.FOREGROUND_NOTIFICATION_ACTION_BUTTON

When the landing page is invoked with PUSH_RECEIVED, it will only cache the landing page content in the background instead of displaying it.

Default Registry Predicate

Rejects Situation.PUSH_RECEIVED if the application has not been opened in the last week.

Example:

ActionRunRequest.createRequest("landing_page_action")
                 .setSituation(Situation.MANUAL_INVOCATION)
                 .setValue("http://www.urbanairship.com")
                 .run();

Open External URL Action

The open external URL action allows launching any URL. The action will construct an intent with a given Uri and try to start an activity with it for viewing.

Accepted Arguments
  • URL as a String
Default Action Names
  • open_external_url_action
  • ^u
Accepted Situations
  • Situation.PUSH_OPENED
  • Situation.WEB_VIEW_INVOCATION
  • Situation.MANUAL_INVOCATION
  • Situation.FOREGROUND_NOTIFICATION_ACTION_BUTTON

Example:

ActionRunRequest.createRequest("open_external_url_action")
                 .setSituation(Situation.MANUAL_INVOCATION)
                 .setValue("http://www.urbanairship.com")
                 .run();

Add Tags Action

The add tags action allows adding one or more tags to the device.

Accepted Arguments
  • String for a single tag
  • JSON array for multiple tags
Default Action Names
  • add_tags_action
  • ^+t
Accepted Situations
  • Situation.PUSH_OPENED
  • Situation.WEB_VIEW_INVOCATION
  • Situation.MANUAL_INVOCATION
  • Situation.PUSH_RECEIVED
  • Situation.FOREGROUND_NOTIFICATION_ACTION_BUTTON
  • Situation.BACKGROUND_NOTIFICATION_ACTION_BUTTON

Default Registry Predicate

Rejects Situation.PUSH_RECEIVED.

Example:

ActionRunRequest.createRequest("add_tags_action")
                 .setSituation(Situation.MANUAL_INVOCATION)
                 .setValue("tagOne")
                 .run();

Remove Tags Action

The remove tags action allows removing one or more tags from the device.

Accepted Arguments
  • String for a single tag
  • JSON array for multiple tags
Default Action Names
  • remove_tags_action
  • ^-t
Accepted Situations
  • Situation.PUSH_OPENED
  • Situation.WEB_VIEW_INVOCATION
  • Situation.MANUAL_INVOCATION
  • Situation.PUSH_RECEIVED
  • Situation.FOREGROUND_NOTIFICATION_ACTION_BUTTON
  • Situation.BACKGROUND_NOTIFICATION_ACTION_BUTTON

Default Registry Predicate

Rejects Situation.PUSH_RECEIVED.

Example:

ActionRunRequest.createRequest("remove_tags_action")
                 .setSituation(Situation.MANUAL_INVOCATION)
                 .setValue("tagOne")
                 .run();

Share Action

The share action constructs an intent to share the text to the application. The Android chooser activity will be started to pick the application used to share the text.

Accepted Arguments
  • String
Default Action Names
  • share_action
  • ^s
Accepted Situations
  • Situation.PUSH_OPENED
  • Situation.WEB_VIEW_INVOCATION
  • Situation.MANUAL_INVOCATION
  • Situation.FOREGROUND_NOTIFICATION_ACTION_BUTTON

Example:

ActionRunRequest.createRequest("share_action")
                 .setSituation(Situation.MANUAL_INVOCATION)
                 .setValue("Oh, Hi Marc!")
                 .run();

Open Rich Push Inbox Action

The open rich push inbox action will try to start an activity to view either the Rich Push Inbox or a Rich Push Message if a message ID is supplied as the arguments value and the message is present in the inbox.

In order to view the inbox, the action will attempt to start an activity with intent action com.urbanairship.VIEW_RICH_PUSH_INBOX. To view messages, the intent will use the action com.urbanairship.VIEW_RICH_PUSH_MESSAGE with the message ID supplied as the data in the form of message:<MESSAGE_ID>. It is up to the application to assign the proper intent filters to activities that can handle either of those requests.

Accepted Arguments
  • An optional message ID as a String.
Default Action Names
  • open_mc_action
  • ^mc
Accepted Situations
  • Situation.PUSH_OPENED
  • Situation.WEB_VIEW_INVOCATION
  • Situation.MANUAL_INVOCATION
  • Situation.FOREGROUND_NOTIFICATION_ACTION_BUTTON

Example:

// Open the Rich Push Inbox
ActionRunRequest.createRequest("open_mc_action")
               .setSituation(Situation.MANUAL_INVOCATION)
               .run();

// Open the Rich Push Message
ActionRunRequest.createRequest("open_mc_action")
             .setSituation(Situation.MANUAL_INVOCATION)
             .setValue(messageId)
             .run();

Add Custom Event Action

The custom event action creates and adds a custom events. See Custom Events documentation for more details on Custom Events.

Accepted Arguments

  • A map with the following keys
    • “event_name”: The name of the event (Required).
    • “event_value”: The value of the event.
    • “interaction_id”: The interaction ID of the event.
    • “interaction_type”: The interaction type of the event.
Default Action Names
  • add_custom_event_action
Accepted Situations
  • Situation.PUSH_OPENED
  • Situation.PUSH_RECEIVED
  • Situation.WEB_VIEW_INVOCATION
  • Situation.MANUAL_INVOCATION
  • Situation.FOREGROUND_NOTIFICATION_ACTION_BUTTON
  • Situation.BACKGROUND_NOTIFICATION_ACTION_BUTTON

Default Registry Predicate Only accepts Situation.WEB_VIEW_INVOCATION and Situation.MANUAL_INVOCATION.

Example custom event map:

Map<String, Object> customEventData = new HashMap<String, Object>();
customEventData.put(CustomEvent.EVENT_NAME, "event name");
customEventData.put(CustomEvent.EVENT_VALUE, 123.45);

Running the action:

ActionRunRequest.createRequest("add_custom_event_action")
                 .setSituation(Situation.MANUAL_INVOCATION)
                 .setValue(customEventData)
                 .run();

Custom Actions

The action framework supports any custom actions. Create an action by extending the base action class, then register the action to the registry after takeoff. The action can then be triggered the same way as the built-in actions.

Note

Actions that are either long lived or are unable to be interrupted by the device going to sleep should request a wake lock before performing. This is especially important for actions that are performing in Situation.PUSH_RECEIVED, when a push is delivered when the device is not active.

Example:

public class CustomAction extends Action {

    @Override
    public boolean acceptsArguments(ActionArguments arguments) {
        if (!super.acceptsArguments(arguments)) {
            return false;
        }

        // Do any argument inspections. The action will stop
        // execution if this method returns false.

        return true;
    }

    @Override
    public void onStart(ActionArguments arguments) {
        Logger.info("Action started!");

        // Called before the perform
    }

    @Override
    public ActionResult perform(ActionArguments arguments) {
        Logger.info("Action is performing!");

        return ActionResult.newEmptyResult();
    }

    @Override
    public void onFinish(ActionArguments arguments, ActionResult result) {
        Logger.info("Action finished!");

        // Called after the action performed
    }
}

Register the action after takeoff:

UAirship.takeOff(this, new UAirship.OnReadyCallback() {
    @Override
    public void onAirshipReady(UAirship airship) {
          airship.getActionRegistry()
                 .registerAction(new CustomAction(), "my_custom_action");
    }
});

Analytics Reporting

This document is a quick guide to integrating Reports support with your Android application using the Urban Airship client library. We’ll start with a discussion of the minimum code necessary to take advantage of these features, and briefly cover the event upload model the client library implements.

Setting up Analytics: Minor Assembly Required

Our Android client library ships with analytics support nearly ready to go. However, due to the way Android Activities work, there are a few changes you will need to make to your Activities in order for the library to be able to determine when your app passes into the foreground or background, which are crucial to reporting all metrics.

Instrumenting Your Activities

Note

Starting with Android API 14, the client library can now detect when an activity is started or stopped without any manual instrumentation. If your app’s minSDKVersion >= 14 (Ice Cream Sandwich), you no longer need to modify any of your activities. Make sure to set the minSDKVersion in the airshipconfig.properties in order to prevent any missing instrumented analytic warnings.

Android has no explicit events an app developer can listen for that indicate whether an app is running in the foreground or the background, since by default, apps continue running regardless of which Activity is currently displayed. The fluid transitions that are possible between the Activities of different apps further complicate this conceptual model. However, for our purposes it is safe to assume that if none of your app’s Activities are currently being shown, the app is “in the background”, and should not count as time that the user spends interacting with it.

The client library measures this by keeping track of when your application’s Activities start and stop, which correspond with their display. Because the library cannot detect this directly on older Android APIs, there are two ways to facilitate this, both of which require a small amount of manual intervention.

The easiest way to do this is to subclass InstrumentedActivity and InstrumentedListActivity, both of which are provided with the library. Using subclasses of these Activities ensures that whenever your Activity is presented or hidden from the user, that this information is passed along to the library.

If you would like to manually instrument your class, update your Activity’s onStart and onStop methods with the following:

@Override
public void onStart() {
    super.onStart();
    Analytics.activityStarted(this);
}

@Override
public void onStop() {
    super.onStop();
    Analytics.activityStopped(this);
}

Analytics Events and Uploading

Our client library stores events in a local database and uploads them periodically in a background thread. We’ve taken great care to make sure that the database won’t grow beyond a small fixed size, so extended periods of lost connectivity are nothing to worry about. The event upload thread is woken up when new events are triggered and goes to sleep when there are no more events to process, so the impact on battery life is negligible.

Custom Events

Custom events let you track user activities and key conversions in your application, and tie them back to corresponding push messaging campaigns. Custom events requires analytics to be enabled. If disabled, any event that is added to analytics will be ignored. For a more detailed explanation on custom events and possible use cases, see the Custom Events topic guide.

Recording custom events:

CustomEvent.Builder builder = new CustomEvent.Builder("event_name")
      .setEventValue(123.12);

// Record the event
UAirship.shared().getAnalytics().addEvent(builder.create());

Disabling Analytics

If you do not wish to include analytics and reporting in your application, simply add the following line to your airshipconfig.properties file:

analyticsEnabled = false

You can disable analytics at runtime, which will delete any locally stored events.

UAirship.shared().getAnalytics().setEnabled(false);

Warning

Features that depend on analytics being enabled may not work properly if it’s disabled (reports, location segmentation, region triggers, push to local time).

Preferences

Push Preferences (Enable, Disable, Sound, Vibrate)

Push is enabled by default, but user notifications are disabled. This allows the application to still be messaged, even if the user disables notifications. To enable user notifications, call setUserNotificationsEnabled(boolean enabled) on the PushManager instance.

Enabling and Disabling user notifications

// Handling user push preferences
if (userRequestedPush) {
    UAirship.shared().getPushManager().setUserNotificationsEnabled(true);
} else {
    UAirship.shared().getPushManager().setUserNotificationsEnabled(false);
}

We do not recommend triggering these calls directly from a checkbox or other UI widget. These calls immediately start the registration process, so it is best to call from a location where they can’t be toggled on and off quickly.

Other options are also available via PushManager.

PushManager pushManager = UAirship.shared().getPushManager();
pushManager.setSoundEnabled(boolean); // enable/disable sound when a push is received
pushManager.setVibrateEnabled(boolean); // enable/disable vibrate on receive
pushManager.setPushEnabled(boolean); // enable/disable all push messages.

Preferences Screen

You can easily add Urban Airship preferences to the Android preference screens. The library provides several ready to use preferences that can be mixed in with non UA preferences to allow the user to enable/disable push, sound, vibration, location, set quiet times, set aliases, and add tags.

To add Urban Airship preferences, first define an xml preference file:

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

    <com.urbanairship.preference.PushEnablePreference
        android:key="push_preference"
        android:persistent="false"
        android:title="@string/push_preference_title" />

    <com.urbanairship.preference.SoundEnablePreference
        android:dependency="push_preference"
        android:key="sound_preference"
        android:persistent="false"
        android:title="@string/sound_preference_title" />

</PreferenceScreen>

Modify your PushPreferencesActivity or PreferenceFragment:

public class PushPreferencesActivity extends SherlockPreferenceActivity {

    private UAPreferenceAdapter preferenceAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Add the preferences defined in xml above
        this.addPreferencesFromResource(R.xml.push_preferences);

        // Creates the UAPreferenceAdapter with the entire preference screen
        preferenceAdapter = new UAPreferenceAdapter(getPreferenceScreen());
    }

    @Override
    protected void onStop() {
        super.onStop();

        // Apply any changed UA preferences from the preference screen
        preferenceAdapter.applyUrbanAirshipPreferences();
    }
}

If you want to customize any of the preferences views or behavior, just extend the preference or create a new preference that implements the UAPreference interface.

For example, to create a new PushEnablePreference:

public class PushEnablePreference extends CheckBoxPreference implements UAPreference  {

    public PushEnablePreference(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public PreferenceType getPreferenceType() {
        return PreferenceType.PUSH_ENABLE;
    }

    @Override
    public void setValue(Object value) {
        this.setChecked((Boolean) value);
    }
}

The setValue(Object value) will be called on creating the UAPreferenceAdapter. The preferences will then be saved once the preference adapter’s applyUrbanAirshipPreferences method is called.

Customizing Push Notifications

NotificationFactory

All incoming push notifications are built using a class that extends the NotificationFactory class.

Example:

public class CustomNotificationFactory extends NotificationFactory {

    public CustomNotificationFactory(Context context) {
        super(context);
    }

    @Override
    public Notification createNotification(PushMessage message, int notificationId) {
        // Build the notification
        NotificationCompat.Builder builder = new NotificationCompat.Builder(getContext())
                .setContentTitle("Notification title")
                .setContentText(message.getAlert())
                .setAutoCancel(true)
                .setSmallIcon(R.drawable.notification_icon);


        // To support interactive notification buttons extend the NotificationCompat.Builder
        builder.extend(createNotificationActionsExtender(message, notificationId));

        return builder.build();
    }

    @Override
    public int getNextId(PushMessage pushMessage) {
        return NotificationIDGenerator.nextID();
    }
}

Notification factories can be set on the PushManager instance after UAirship.takeOff:

CustomNotificationFactory notificationFactory = new CustomNotificationFactory(this);
UAirship.shared().getPushManager().setNotificationFactory(notificationFactory);

DefaultNotificationFactory

The DefaultNotificationFactory is the default handler for incoming push notifications. Notifications created by this factory use the standard Android Notification layout with the Big Text style. The icon is set to the application’s icon by default and the notification subject is set to the application’s name.

SystemNotificationFactory

The SystemNotificationFactory is the similar to the DefaultNotificationFactory but does not apply any default styles.

CustomLayoutNotificationFactory

The CustomLayoutNotificationFactory is a factory that uses a custom layout to display the notification. The factory needs to be customized before being set.

Example Layout:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:orientation="vertical"
    android:paddingTop="0dip"
    android:layout_alignParentTop="true"
    android:layout_height="fill_parent">

  <ImageView android:id="@+id/icon"
      android:src="@drawable/icon"
      android:layout_width="65dip"
      android:layout_height="65dip"
      android:layout_margin="0dip"/>

<!-- The custom notification requires a subject field.
To maximize space in this layout this
field is hidden. Visibility is set to gone. -->
<TextView android:id="@+id/subject"
    android:text="Subject"
    android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title"
    android:layout_alignTop="@+id/icon"
    android:layout_toRightOf="@+id/icon"
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:maxLines="1" android:visibility="gone"/>

<!-- The message block. Standard text size is 14dip
but is increased here to maximize impact. -->
<TextView android:id="@+id/message"
    android:textSize="48dip"
    android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title"
    android:text="Message"
    android:maxLines="4"
    android:layout_marginTop="0dip"
    android:layout_marginRight="2dip"
    android:layout_marginLeft="10dip"
    android:layout_height="wrap_content"
    android:layout_toRightOf="@+id/icon"
    android:layout_width="wrap_content" />

</RelativeLayout>

The icon, subject, and message elements are required. If you do not wish to use one of the fields, set its visibility to gone.

Example factory:

CustomLayoutNotificationFactory nf = new CustomLayoutNotificationFactory(this);

nf.statusBarIconDrawableId = R.drawable.icon;

nf.layout = R.layout.notification_layout; // The layout resource to use
nf.layoutIconDrawableId = R.drawable.notification_icon; // The icon you want to display
nf.layoutIconId = R.id.icon; // The icon's layout 'id'
nf.layoutSubjectId = R.id.subject; // The id for the 'subject' field
nf.layoutMessageId = R.id.message; // The id for the 'message' field

// set this ID to a value > 0 if you want a new notification to replace the previous one
// nf.constantNotificationId = 100;

// Custom sounds can be played when a notification is received using this
// notification factory implementation. For the purposes of this example,
// we'll use a cat.mp3 located at project\_root/res/raw/cat.mp3.
nf.soundUri = Uri.parse("android.resource://" + this.getPackageName() + "/" + R.raw.cat);

Then, set the factory after takeOff():

UAirship.shared().getPushManager().setNotificationFactory(nf);

Clearing Notifications

Notifications can be cleared manually by using the Android NotificationManager.cancelAll method.

3.0+ Compatibility for Custom Notification Layouts

The new Android client library allows you to extensively customize the way notifications are displayed, by incorporating custom layouts. However, with this flexibility also comes the responsibility of making sure that your notification layout displays correctly on different devices and Android OS version, in particular, Android’s recent Honeycomb release for tablets, and the 4.x Ice Cream Sandwich and Jellybean releases.

Here are a few potential pitfalls you can avoid when incorporating custom notification layouts into an app that targets 3.0 (Honeycomb)+.

Invisible Message Text

In the official Android Creating Status Bar Notifications guide, the example layout file defines a TextView with a black textColor attribute for the notification message.

This was sound advice when the default background for the expanded status bar window was white or gray, but in Honeycomb, this window has been replaced with new redesigned notification UI with a black background. This means that if you create a layout similar to the one shown in the guide linked above, your notification messages will appear to be blank.

The solution is to use a pair of text styles introduced in API level 9 that define the default colors for status bar events, which will allow your layout to adapt to changing UI themes:

@android:style/TextAppearance.StatusBar.EventContent.Title

and

@android:style/TextAppearance.StatusBar.EventContent

On Honeycomb, these result in white and light gray text, respectively, and match the style of system notifications that tablet users will already be accustomed to.

Because these styles were added in API level 9, you must build your application targeting at least this version in order to take advantage of them. If you want your app to run on devices with an earlier Android release, you can set an earlier API level as your minimum SDK version. This will generate a compiler warning, but as long as you refrain from making incompatible API calls (or only do so after performing reflection in order to determine if it is safe to do so), your app will run fine on earlier devices.

In this case you will need to create two layouts– one using the “traditional” text color attribute, and another using the Honeycomb-compatible styles referenced above. Place the new layout in its own directory, named “layout-v9”, and Android will know to look for it there when the device is running API level 9 or above.

Persistence of Ticker Text

The Android Notification class defines a flag Notification.FLAG_ONLY_ALERT_ONCE, which the documentation says “should be set if you want the sound and/or vibration play each time the notification is sent, even if it has not been canceled before that.” As this interpretation is the exact opposite of the name of the flag, it is likely that this is a mistake in the documentation. Furthermore, in prior Android releases, setting this flag does not appear to have any noticeable effect.

In Honeycomb or later, however, if you do not set this flag and your notification is ongoing or involves UI elements that will be periodically updated, such as a progress bar, every update to the existing notification will cause the ticker text to redisplay, which will make the notification itself inaccessible. Therefore, if you are creating your own implementation of a NotificationFactory and intend on updating the notification UI in any way after it has been displayed, make sure to explicitly set this flag on the notification returned.

Other Resources

Backwards compatibility and cross-device support are complex topics that require careful planning and carry their own set of best practices. This guide is meant to help you prevent some known issues from arising with custom notifications, but is in no way an exhaustive resource. For more information on how to design your app to maximize compatibility, see the following links:

Creating Backward Compatible UIs

Status Bar Icons

Android Developers Forum Google Groups

Location Targeting

The location component of the Urban Airship library is designed to be extremely flexible. You can use it to do all of your location tracking or if you want you can just use it when you’re in need of a single location update and if neither one of those fits your needs, you can do your own location tracking and let us upload them to your dashboard for you. So, let’s get started integrating location into your application so you can start making extremely relevant, targeted pushes to your users.

Setup

In order to start using location in your app, you’ll need to add a location permission to your AndroidManifest.xml.

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

If approximate locations aren’t good enough, change COARSE in the above permission to FINE then your application will gain access to the GPS.

Also in your AndroidManifest.xml, you’ll want to add the LocationService as such:

<service android:name="com.urbanairship.location.LocationService" />

Using Urban Airship Location

Urban Airship Location exposes a very simple, high-level API for requesting location.

The Urban Airship SDK is compatible with Google Play Services’ Fused Location Provider. Fused Location will automatically be used if Google Play Services is available on the device, is up to date, and the application was built with the Google Play Services dependency. Otherwise the standard Android location will be used, such as for Amazon devices. Google Play Services setup instructions can be found here.

Enabling Location Updates

Location updates are enabled or disabled by setLocationUpdatesEnabled(). Updates continuing in the background is controlled by setBackgroundLocationAllowed(). Listening for background locations now requires both location updates enabled and background location allowed.

// Enable location updates
UAirship.shared().getLocationManager().setLocationUpdatesEnabled(true);

// Allow location updates to continue in the background
UAirship.shared().getLocationManager().setBackgroundLocationAllowed(true);

Location Request Options

Location request options are modeled after the Fused Location Provider by specifying high level needs instead of low level criteria. The location request options will automatically be converted to either criteria when using the standard Android Location APIs or to a LocationRequest when using Fused Location Provider.

LocationRequestOptions options = new LocationRequestOptions.Builder()
        .setPriority(LocationRequestOptions.PRIORITY_BALANCED_POWER_ACCURACY)
        .setMinDistance(800)
        .setMinTime(5, TimeUnit.MINUTES)
        .create();

// Set the default continuous location request options
UAirship.shared().getLocationManager().setLocationRequestOptions(options);

The location request options for location updates will automatically default to LocationRequestOptions.PRIORITY_BALANCED_POWER_ACCURACY for request priority, 800 meters for minimum distance update interval and 5 minutes for minimum time update interval.

Continuous Location Updates

The easiest way to gather location information about the user is to enable continuous location updates.

UAirship.shared().getLocationManager().setLocationUpdatesEnabled(true);

This will automatically start recording location when the application is in the foreground.

Background updates can be enabled, but should be used sparingly as they could have a larger impact on the device’s battery.

UAirship.shared().getLocationManager().setBackgroundLocationAllowed(true);

Location updates can be listened for by instantiating or extending the LocationListener class:

// Create a listener
LocationListener listener = new LocationListener() {
    @Override
    public void onLocationChanged(Location location) {
        Log.i("Location", "New user location " + location);
    }
});

// Add the listener to UALocationManager
UAirship.shared().getLocationManager().addLocationListener(listener);

// Remove the listener when finished
UAirship.shared().getLocationManager().removeLocationListener(listener);

Single Location Requests

Single location requests can be made in addition to continuous location updates or by themselves to have more control over when location requests are made.

When a single location request is made, a Pending Result is returned. The Pending Result can be used to cancel the request or set a callback when the result is ready.

Example:

PendingResult<Location> pendingResult = UAirship.shared().getLocationManager().requestSingleLocation();

pendingResult.onResult(new PendingResult.ResultCallback<Location>() {
    @Override
    public void onResult(Location location) {
        if (location != null) {
            Log.i("Location", "Single request failed!");
        } else {
            Log.i("Location", "Single location request: " + location);
        }
    }
});

// Optionally you can cancel the request and the callback will not be called
pendingResult.cancel();

Single location requests will default to the continuous location request options, but can also be defined just for the request:

// Create request options for a high accuracy location
LocationRequestOptions options = new LocationRequestOptions.Builder()
            .setPriority(LocationRequestOptions.PRIORITY_HIGH_ACCURACY)
            .create();

PendingResult<Location> pendingResult = UAirship.shared().getLocationManager().requestSingleLocation(options);

Recording Location

Note

Any location update from UALocationManager will automatically be uploaded.

If you’re using your own location tracking, you can still upload those events to Urban Airship so you can view them on your dashboard.

When a new location becomes available, record the location:

UAirship.shared().getAnalytics().recordLocation(location);

The location will be automatically batched and uploaded so you can view it on your dashboard.

Rich Push

Sending Rich Push Messages through the REST API

Note

As of the release of the Urban Airship Push API v3, the operation of sending a rich message to an inbox within the application is no longer handled via the Rich Push endpoints at /api/airmail/.

Rich content which is optionally associated with a push notification is now included in the message component of the Push API at /api/push/. See the Rich App Pages documentation for more detail.

Apart from the android dictionary required to send an Android push backing the rich message, this is done the same way as you would find on iOS. Sending a JSON payload with this structure as a body to the following URL:

https://go.urbanairship.com/api/push/

will deliver the message.

Library Components

Below are a few basic definitions of the classes used for interacting with the Rich Push API.

For more detail, please see the Urban Airship Android Library Reference

For more information on getting started with Rich Push on Android, see the Android Rich Push Tutorial.

RichPushManager

The Rich Push manager is the primary interface for Rich Push. It gives you access to the RichPushUser, RichPushInbox and provides ways to refresh messages.

RichPushUser

The Rich Push user is the basic building block of Rich Push and one is automatically created for you when your application is installed on a device. The Rich Push user has access to the ID and user token.

RichPushInbox

The Rich Push inbox is the access point for the user’s Rich Push messages. The inbox provides methods to delete messages, mark messages as read and unread, and is automatically updated with Urban Airship.

RichPushMessage

Each Rich Push message contains information about a user’s Rich Application page such as sent date, title, read/unread status, extras, and the url of the message’s content.

RichPushMessageWebView

An extended WebView that is configured to display the content of a RichPushMessage. Only available in API 5 (Eclair) and higher.

UAWebViewClient

A web view client that allows running Urban Airship Actions through Javascript and handles any user authorization for Rich Application Pages. Extend this class when setting a custom web view client for a RichPushMessageWebView.

UAJavascriptInterface

The Urban Airship Javascript interface. Provides Javascript calls to trigger actions and to retrieve information about the message such as the title and send date.

Message Operations

Note

Please note that in order to reduce network traffic and help battery life, modifications to RichPushMessages aren’t immediately synced with the server. The RichPushUpdateService will make sure your changes are synced to the server in a timely fashion or you can manually sync which you’ll learn about in the next section.

Marking Messages Read

A RichPushMessage really only has one mutable state, whether it’s read or not. As of this writing, there’s no API endpoint for marking a message unread, but we do support marking it unread locally if you want to allow your users to do so. You can change message read state one of two ways.

Individually:

// mark it read
message.markRead();

// mark it unread
message.markUnread();

or in bulk:

// mark messages read
UAirship.shared().getRichPushManager().getRichPushInbox().markMessagesRead(someSetOfIds);

// mark messages unread
UAirship.shared().getRichPushManager().getRichPushInbox().markMessagesUnread(someSetOfIds);

Deleting a Message

Just like updating the read status of a RichPushMessage, we can delete in one of two ways.

Individually:

// delete it
message.delete();

or in bulk:

// delete a bunch of messages using the inbox and a set of message ids
UAirship.shared().getRichPushManager().getRichPushInbox().deleteMessages(someSetOfIds);

Note

Be sure you want to do this, as the message is unrecoverable.

Inbox Intents

The Open Rich Push Inbox Action will try to start an activity to view either the Rich Push Inbox or a Rich Push Message.

To handle opening the Rich Push inbox, add the intent filter to an activity that displays the inbox:

<intent-filter>
    <action android:name="com.urbanairship.VIEW_RICH_PUSH_INBOX" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

To handle opening the Rich Push message, add the intent filter to the AndroidManifest.xml file

<intent-filter>
    <action android:name="com.urbanairship.VIEW_RICH_PUSH_MESSAGE" />
    <data android:scheme="message"/>
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

The message can be parsed from the intent:

RichPushInbox inbox = UAirship.shared().getRichPushManager().getRichPushInbox();

if (RichPushInbox.VIEW_MESSAGE_INTENT_ACTION.equals(intent.getAction()) {
    String messageId = getIntent().getData().getSchemeSpecificPart();
    RichPushMessage message = inbox.getMessage(messageId);
}

Listening for updates to RichPushInbox and RichPushUser

In certain circumstances, you’re going to want to listen for updates to the RichPushInbox and maybe even the RichPushUser (although this is more rare). In order to do this, you’ll need to implement the RichPushManager.Listener interface:

public class RichPushListener implements RichPushManager.Listener {

        @Override
        public void onUpdateMessages(boolean success) {
                messagesRefreshingAnimation.stop();
        }

        @Override
        public void onUpdateUser(boolean success) {
                userUpdatingAnimation.stop();
        }
}

Once you have your class created, you need to tell the RichPushManager where to find it.

For example, in an Activity:

// onCreate
RichPushListener richPushListener = new RichPushListener();
// onResume
UAirship.shared().getRichPushManager().addListener(richPushListener);

and in order to avoid leaking your listener:

// onPause
UAirship.shared().getRichPushManager().removeListener(richPushListener);

Finally, request a refresh of your RichPushInbox or an update of your RichPushUser:

UAirship.shared().getRichPushManager().refreshMessages();
UAirship.shared().getRichPushManager().updateUser();

The RichPushInbox.Listener has a single onUpdateInbox method that notifies whenever the inbox changes. This is used in the RichPushSample to update both the InboxFragment as well as the MessageFragmentAdapter from the InboxActivity:

@Override
public void onUpdateInbox() {
    updateRichPushMessages();
}

/**
 * Grabs the latest messages from the Rich Push inbox, and syncs them
 * with the inbox fragment and message view pager if available
 */
private void updateRichPushMessages() {
    messages = UAirship.shared().getRichPushManager().getRichPushInbox().getMessages();
    this.inbox.setMessages(messages);
    if (messagePager != null) {
        ((MessageFragmentAdapter) messagePager.getAdapter()).setRichPushMessages(messages);
    }
}