Android

This guide is a high level overview of the Urban Airship platform for Android and Amazon devices. We’ll first cover the basic details of app setup before moving on to push notifications, client-side customization, and making use of our growing suite of integration and engagement tools.

To see a fully-functioning Android app demonstrating many of the Urban Airship features discussed in this guide, check out the Android Sample App on GitHub.

Getting Started

There are a few important steps to get out of the way when integrating Urban Airship with your app for the first time. Follow these instructions carefully to ensure that you will have a smooth development experience.

Production vs. Development Apps in Urban Airship

Urban Airship makes a hard distinction between development and production versions of your app, each with their own distinct credentials and settings in the backend. This is so you can test in development without necessarily running the risk of sending test messages to users. Before doing any mobile integration, begin by creating development and production versions of your app in the Urban Airship dashboard.

Urban Airship SDK Setup

  1. Include the Urban Airship maven repository in the project’s build.gradle file:

    repositories {
        ...
    
        maven {
            url  "https://urbanairship.bintray.com/android"
        }
    }
    
    dependencies {
      ...
    
      // Urban Airship SDK
      compile 'com.urbanairship.android:urbanairship-sdk:7.1.+'
    
      // Recommended for in-app messaging
      compile 'com.android.support:cardview-v7:23.3.0'
    
      // Recommended for location services
      compile 'com.google.android.gms:play-services-location:8.4.0'
    }
    
  2. Verify the applicationId is set in the project’s build.gradle file:

    android {
      ...
    
      defaultConfig {
        ...
    
        applicationId "com.example.application"
      }
    }
    

Create Airship Config Options

Airship config options are a convenient way to pass custom settings to your app without needing to edit the source code. By default the Urban Airship SDK loads these settings from the airshipconfig.properties file located in your application’s assets directory. Use this file, among other things, to set the backend credentials for your app, and to toggle between development and production builds.

Create a new airshipconfig.properties file with your application’s settings:

developmentAppKey = Your Development App Key
developmentAppSecret = Your Development App Secret

productionAppKey = Your Production App Key
productionAppSecret = Your Production Secret

# Toggles between the development and production app credentials
# Before submitting your application to an app store set to true
inProduction = false

# LogLevel is "VERBOSE", "DEBUG", "INFO", "WARN", "ERROR" or "ASSERT"
developmentLogLevel = DEBUG
productionLogLevel = ERROR

# GCM Sender ID
gcmSender = Your Google API Project Number

# Notification customization
notificationIcon = ic_notification
notificationAccentColor = #ff0000

Starting Urban Airship Services

The Urban Airship SDK requires only a single entry point in the application, known as takeOff. To start, create a class that extends Application and set the name of the class for the application entry in the AndroidManifest.xml:

<application android:name=".CustomApplication" ... />

Then, override the application’s onCreate to call UAirship.takeOff:

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

    UAirship.takeOff(this);
}

This will bootstrap the SDK and look for the settings in airshipconfig.properties. These can also optionally be provided at run time by passing an AirshipConfigOptions in the call to UAirship.takeOff:

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

    AirshipConfigOptions options = new AirshipConfigOptions.Builder()
        .setDevelopmentAppKey("Your Development App Key")
        .setDevelopmentAppSecret("Your Development App Secret")
        .setProductionAppKey("Your Production App Key")
        .setProductionAppSecret("Your Production App Secret")
        .setInProduction(!BuildConfig.DEBUG)
        .setGcmSender("Your Google API Project Number")
        .setNotificationIcon(R.drawable.ic_notification)
        .setNotificationAccentColor(ContextCompat(getContext(), R.color.accent))
        .build();

    UAirship.takeOff(this, options);
}

Configure a Push Provider

Before any push notifications can be sent, you must set up some form of push transport. 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).

Because the Android and Amazon platforms are so similar, you won’t need to tailor your transport settings specifically to either host. The SDK will automatically detect the device’s operating system and use the correct transport at runtime. For more information, see the GCM setup documentation for Android, and the ADM setup documentation for Amazon.

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);

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.

Enabling User Notifications

The Urban Airship SDK makes a distinction between “user notifications”, which can be seen by the user, and other forms of push that allow you to send data to your app silently, or in the background. Enabling or disabling user notifications is a preference often best left up to the user, so by default, user notifications are disabled. For testing purposes, enabling notification at startup requires a few extra lines of code to your main application class.

After UAirship.takeOff, call:

UAirship.shared().getPushManager().setUserNotificationsEnabled(true);

Note

All Urban Airship settings are persistent unless otherwise noted.

Send a Test Notification

Now would be a great time to send a test notification. See our user guide on Send Your First Notification to get started using our dashboard interface, or our API reference for more details.

Reference, Migration Guides, and Troubleshooting

All the public classes and methods provided by the Urban Airship SDK have corresponding Javadoc entries in our Reference documentation.

We also publish an update to the SDK migration guide SDK migration guide every time the SDK is updated, to help make the update process as painless as possible.

Lastly, if you find yourself stuck, check our Troubleshooting guide before contacting support.

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 AirshipReceiver 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 AirshipReceiver:

public class SampleAirshipReceiver extends AirshipReceiver {

    private static final String TAG = "SampleAirshipReceiver";

    @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(@NonNull Context context, @NonNull PushMessage message, boolean notificationPosted) {
        Log.i(TAG, "Received push message. Alert: " + message.getAlert() + ". posted notification: " + notificationPosted);
    }

    @Override
    protected void onNotificationPosted(@NonNull Context context, @NonNull NotificationInfo notificationInfo) {
        Log.i(TAG, "Notification posted. Alert: " + notificationInfo.getMessage().getAlert() + ". NotificationId: " + notificationInfo.getNotificationId());
    }

    @Override
    protected boolean onNotificationOpened(@NonNull Context context, @NonNull NotificationInfo notificationInfo) {
        Log.i(TAG, "Notification opened. Alert: " + notificationInfo.getMessage().getAlert() + ". NotificationId: " + notificationInfo.getNotificationId());

        // Return false here to allow Urban Airship to auto launch the launcher activity
        return false;
    }

    @Override
    protected boolean onNotificationOpened(@NonNull Context context, @NonNull NotificationInfo notificationInfo, @NonNull ActionButtonInfo actionButtonInfo) {
        Log.i(TAG, "Notification action button opened. Button ID: " + actionButtonInfo.getButtonId() + ". NotificationId: " + notificationInfo.getNotificationId());

        // Return false here to allow Urban Airship to auto launch the launcher
        // activity for foreground notification action buttons
        return false;
    }

    @Override
    protected void onNotificationDismissed(@NonNull Context context, @NonNull NotificationInfo notificationInfo) {
        Log.i(TAG, "Notification dismissed. Alert: " + notificationInfo.getMessage().getAlert() + ". Notification ID: " + notificationInfo.getNotificationId());
    }
}

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=".SampleAirshipReceiver"
          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" />

        <category android:name="${applicationId}" />
    </intent-filter>
</receiver>

Addressing Devices

To help target specific devices or users for a notification, we have Tags, Named Users and Tag Groups.

Tags

Tags allow you to attribute arbitrary metadata to a specific device. Common examples include favorites such as sports teams or news story categories.

Add a tag:

UAirship.shared().getPushManager().editTags()
    .addTag("some_tag")
    .removeTag("some_other_tag")
    .apply();

Named Users

Named Users 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 tablet. A device can have only one Named User, and a single Named User should not be associated with more than 20 devices. Named Users provide the ability to directly set tags on them (see Tag Groups).

By default, Named Users cannot be associated from the device. In this case if the devices attempts a Named User association, the association will fail and the SDK will log an error. In order to change this setting, see the Settings documentation.

Examples:

Once the application has a channel ID, you can associate the channel to a NamedUser:

// Associate the channel to a Named User ID.
UAirship.shared().getPushManager().getNamedUser().setId("NamedUserID");

To disassociate a channel from a Named User ID, set its ID to null:

// Disassociate the channel from a Named User ID.
UAirship.shared().getPushManager().getNamedUser().setId(null);

Note

Associating the channel with a Named User ID, will implicitly disassociate the channel from the previously associated Named User ID, if it existed.

Tag Groups

Tag groups are configurable namespaces for organizing tags for the channel and Named User. Please view the Tag Groups documentation for more details. The Tag Group Walkthrough will guide you through the creation of your first Tag Group.

By default, Tag Groups cannot be modified from the device. In this case if a device attempts to modify Tag Groups, the modification will fail and the SDK will log an error. In order to change this setting, see the Settings documentation.

Channel Tag Groups

Once you have created a Tag Group, you can add or remove tags for the Tag Group on the channel. In this example, we have created a Tag Group loyalty.

Examples:

Add a tag to a Tag Group on the channel:

// Add a tag to a Tag Group and apply the change.

UAirship.shared().getPushManager().editTagGroups()
        .addTag("loyalty", "bronze-member")
        .apply();

Add a set of tags to a Tag Group on the channel:

Set<String> tagsToAdd = new HashSet<>();
tagsToAdd.add("silver-member");
tagsToAdd.add("gold-member");

// Add a set of tags to a Tag Group and apply the change.

UAirship.shared().getPushManager().editTagGroups()
        .addTags("loyalty", tagsToAdd)
        .apply();

Remove a tag from a Tag Group on the channel:

// Remove a tag from a Tag Group and apply the change.

UAirship.shared().getPushManager().editTagGroups()
        .removeTag("loyalty", "bronze-member")
        .apply();

Remove tags from a Tag Group on the channel:

Set<String> tagsToRemove = new HashSet<>();
tagsToRemove.add("silver-member");
tagsToRemove.add("gold-member");

// Remove tags from a Tag Group and apply the change.

UAirship.shared().getPushManager().editTagGroups()
        .removeTags("loyalty", tagsToRemove)
        .apply();

Add and remove a tag for a Tag Group on the channel:

// Add and remove a tag and apply the change.
UAirship.shared().getPushManager().editTagGroups()
       .addTag("loyalty", "silver-member")
       .removeTag("loyalty", "bronze-member")
       .apply();

Note

If setting channel tags is enabled during registration, the predefined device Tag Group cannot be modified from the device and will log an error message. Disable channel tags by setting PushManager.setChannelTagRegistrationEnabled to false if you want to modify this “default” Tag Group.

Named User Tag Groups

Named users provide the ability to directly set tags on them. Once you have created a Tag Group and associated a channel with the NamedUser, you can add or remove tags for the Tag Group on that Named User. In this example, we have created a Tag Group loyalty.

Examples:

Add a tag to a Tag Group on a Named User:

// Add a tag to a Tag Group and apply the change.

UAirship.shared().getPushManager().getNamedUser().editTagGroups()
        .addTag("loyalty", "bronze-member")
        .apply();

Add a set of tags to a Tag Group on a Named User:

Set<String> tagsToAdd = new HashSet<>();
tagsToAdd.add("silver-member");
tagsToAdd.add("gold-member");

// Add a set of tags to a Tag Group and apply the change.

UAirship.shared().getPushManager().getNamedUser().editTagGroups()
        .addTags("loyalty", tagsToAdd)
        .apply();

Remove a tag from a Tag Group on a Named User:

// Remove a tag from a Tag Group and apply the change.

UAirship.shared().getPushManager().getNamedUser().editTagGroups()
        .removeTag("loyalty", "bronze-member")
        .apply();

Remove tags from a Tag Group on a Named User:

Set<String> tagsToRemove = new HashSet<>();
tagsToRemove.add("silver-member");
tagsToRemove.add("gold-member");

// Remove tags from a Tag Group and apply the change.

UAirship.shared().getPushManager().getNamedUser().editTagGroups()
        .removeTags("loyalty", tagsToRemove)
        .apply();

Add and remove a tag for a Tag Group on a Named User:

// Add and remove a tag and apply the change.
UAirship.shared().getPushManager().getNamedUser().editTagGroups()
       .addTag("loyalty", "silver-member")
       .removeTag("loyalty", "bronze-member")
       .apply();

Custom Identifiers

In addition to Named Users, tags and Tag Groups, you can assign up to 20 custom identifiers to users. Identifiers that define additional ways of looking at a user— for example, Android Advertising ID, Google Analytics CID, or any other identifier you deem significant – can be added to your users.

Custom identifiers will be visible in the Connect data stream. We recommend adding any IDs that you may want to be visible in Connect, even if Connect does not yet support the relevant integration. Unlike the other identifiers (e.g., tags), you cannot use custom identifiers to target your users.

Add a custom identifier:

UAirship.shared().getAnalytics()
           .editAssociatedIdentifiers()
           .addIdentifier("customIdentifierKey", "customIdentifierValue")
           .apply();

Automatically track the Android Advertising ID:

UAirship.shared().getAnalytics().setAutoTrackAdvertisingIdEnabled(true);

Manually track the Android Advertising ID:

// Get the Android Advertising ID info - This call is blocking and should be done in a
// background thread.
AdvertisingIdClient.Info adInfo = AdvertisingIdClient.getAdvertisingIdInfo(getContext());

UAirship.shared().getAnalytics()
           .editAssociatedIdentifiers()
           .setAdvertisingId(adInfo.getId(), adInfo.isLimitAdTrackingEnabled())
           .apply();

Push

Quiet Time

Quiet time disables push notification sounds and prevents the phone from vibrating during the set time frame.

// Set the quiet time from 7:30pm - 7:30am
SimpleDateFormat formatter = new SimpleDateFormat("hh:mm", Locale.US);
Date startDate = formatter.parse("19:30");
Date endDate = formatter.parse("07:30");

UAirship.shared().getPushManager().setQuietTimeInterval(startDate,endDate);

// Enable quiet time
UAirship.shared().getPushManager().setQuietTimeEnabled(true);

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 UAirship.takeOff using the PushManager.addNotificationActionButtonGroup method.

Example:

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

NotificationActionButton byeButtonAction = new NotificationActionButton.Builder("bye")
        .setLabel(R.string.bye)
        .setIcon(R.drawable.your_icon_file)
        .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 by adjusting the auto display delay.

Adjusting the auto display delay:

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

Display ASAP Mode

Enabling display ASAP will attempt to display the in-app message on arrival. If the app is currently in the background, the in-app message will be displayed on the next foreground after the auto display delay. If a message is already displaying when a new in-app message is received, the new message will be displayed after the auto display delay when the currently displayed message dismisses.

Enabling display ASAP:

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

Note

Exercise caution when using display ASAP mode. The default auto display behavior for in-app messages is designed to offer a balance between immediacy and user experience, by minimizing the chance of accidentally disrupting the user’s activities in your app. Push automation may also occasionally see latencies that result in associated in-app messages being displayed in unexpected contexts.

Manual Display

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.

Disabling auto display:

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
  • urbanAirshipFontPath - Text appearance attribute that defines a path to custom font in the assets directory

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>
   <item name="urbanAirshipFontPath">"fonts/CustomBannerTextFont.ttf"</item>
</style>

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

Message Center

Displaying the Message Center

The Message Center can be displayed by calling RichPushInbox.startInboxActivity:

UAirship.shared().getInbox().startInboxActivity();

RichPushInbox.startInboxActivity will either launch the MessageCenterActivity or it can be customized by the application by adding the following intent filter to a different activity in the AndroidManifest.xml:

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

A specific message can be displayed by calling RichPushInbox.startMessageActivity:

UAirship.shared().getInbox().startMessageActivity(messageId);

To launch a different activity then the default MessageCenterActivity, add the following intent filter to a different activity in the AndroidManifest.xml:

<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 with the following code:

RichPushMessage message = null;

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

Styling the Message Center

The Message Center can be styled by providing a custom style in the application’s theme with the attribute messageCenterStyle.

The Message Center can be customized with the following attributes:

  • messageCenterItemBackground - A drawable for the message item’s background.
  • messageCenterItemTitleTextAppearance - Text appearance style for the message’s title
  • messageCenterItemDateTextAppearance - Text appearance style for the message’s date
  • messageCenterItemIconEnabled - Flag to enable message list icons instead of a checkbox
  • messageCenterItemIconPlaceholder - The default placeholder when using message list icons
  • messageNotSelectedTextAppearance - Text appearance style for the no message selected text when displaying the Message Center in a tablet view.
  • messageNotSelectedText - The string to use for the no message selected text when displaying the Message Center in a tablet view.
  • messageCenterEmptyMessageTextAppearance - Text appearance style for the empty message list text.
  • messageCenterEmptyMessageText - The string to use for the empty message list text.
  • messageCenterDividerColor - The message list divider color.
  • urbanAirshipFontPath - Text appearance attribute that defines a path to custom font in the assets directory

Example:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
   <!-- Set the custom Message Center style -->
   <item name="messageCenterStyle">@style/AppTheme.MessageCenter</item>
</style>

 <style name="AppTheme.MessageCenter" parent="MessageCenter">
     <item name="messageCenterDividerColor">#44000000</item>
     <item name="messageCenterItemIconEnabled">true</item>
     <item name="messageCenterItemTitleTextAppearance">@style/AppTheme.MessageCenter.TitleTextAppearance</item>
     <item name="messageCenterItemDateTextAppearance">@style/AppTheme.MessageCenter.DateTextAppearance</item>
 </style>

 <!-- Custom message title text style -->
 <style name="AppTheme.MessageCenter.TitleTextAppearance" parent="MessageCenter.Item.Title.TextAppearance">
     <item name="android:textSize">30sp</item>
     <item name="android:textStyle">italic</item>
     <item name="android:textColor">@android:color/black</item>
     <item name="urbanAirshipFontPath">"fonts/CustomFont.ttf"</item>
 </style>

 <!-- Custom message date text style -->
 <style name="AppTheme.MessageCenter.DateTextAppearance" parent="MessageCenter.Item.Date.TextAppearance">
     <item name="android:textSize">16sp</item>
     <item name="urbanAirshipFontPath">"fonts/CustomFont.ttf"</item>
 </style>

Message Center Filtering

Sometimes it can be useful to filter the contents of the message center according to some predetermined pattern. To facilitate this, use the shared MessageCenter instance to set a predicate. Once set, only messages that match the predicate will be displayed. As a simple example, the predicate shown below keeps only messages whose titles contain the string “Cool”.

RichPushInbox.Predicate pred = new RichPushInbox.Predicate() {
    @Override
    public boolean apply(RichPushMessage message) {
        return message.getTitle().contains("Cool");
    }
};

UAirship.shared().getMessageCenter().setPredicate(pred);

Embedding the Message Center

The Message Center can be embedded in any FragmentActivity using MessageCenterFragment. The fragment can be defined directly in an activity layout file:

<fragment
    android:name="com.urbanairship.messagecenter.MessageCenterFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

It can also be embedded using an fragment transaction:

MessageCenterFragment fragment = new MessageCenterFragment();
getSupportFragmentManager().beginTransaction()
                                   .replace(R.id.content_frame, fragment, "message_center")
                                   .commit();

Actions

Urban Airship Actions provides a convenient way to automatically perform tasks by name in response to an interaction with the Message Center, a Landing Page, or a push notification.

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

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();

Available Actions

  • LandingPageAction - The landing page action allows showing a rich content page in an overlay.
  • OpenExternalUrlAction - 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.
  • DeepLinkAction - The deep link action works exactly as the Open External URL Action. It constructs an intent with a given URI and tries to launch an activity. The deep linking strategy assumes the application is set up to handle URIs. See Android Deep Linking guide for more details on providing deep linking.
  • ShareAction - 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.
  • AddTagsAction - The add tags action allows adding one or more tags to the device. Note: this action is a no-op if channelTagRegistrationEnabled to false on PushManager.
  • RemoveTagsAction - The remove tags action allows removing one or more tags from the device.
  • AddCustomEventAction - The custom event action creates and adds a custom event. See Custom Events documentation for more details on Custom Events.
  • ClipboardAction - The clipboard action copies text to the system clipboard.
  • OpenRichPushInboxAction - 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.
  • OverlayRichPushMessageAction - The overlay rich push action will display a Rich Push Message in the landing page activity.

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 UAirship.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 UAirship.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 will 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.

Gingerbread Support

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.

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());

Screen Tracking

The Urban Airship SDK gives you the ability to track what screen a user views within the application, as well as how long the user stayed on that screen and the user’s previous screen. These events then come through Urban Airship Connect and allow you to see the path that a user takes through an application, or to trigger actions based on a user visiting a particular area of the application.

Track a screen:

UAirship.shared().getAnalytics().trackScreen("MainActivity");

When a new screen is tracked or when the application backgrounds, a screen tracking event will be generated. Normally, Analytics.trackScreen should be called in either Activity.onStart or Fragment.onStart, depending on how the application is built.

For applications where you only want to track activities, you can automate screen tracking with an activity life cycle listener. Add the following in Application.onCreate:

// Only available in ICS or newer
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
   this.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {

       @Override
       public void onActivityStarted(Activity activity) {
           UAirship.shared().getAnalytics().trackScreen(activity.getClass().getSimpleName());
       }

       ...

   });
}

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 settings screen. The SDK provides the following preferences:

Customizing Push Notifications

All incoming push notifications are built using a class that extends the NotificationFactory. By default, the Urban Airship SDK uses the DefaultNotificationFactory. With this factory, the standard Android Notification layout will use the application’s title and icon. A default big text style will be applied for all notifications.

DefaultNotificationFactory is the recommended factory as it provides full support for all of the Android push features. Common notifications customizations are supported, such as the title, sound, accent color, and icons:

UAirship.takeOff(this, new UAirship.OnReadyCallback() {
    @Override
    public void onAirshipReady(UAirship airship) {
        // Create a customized default notification factory
        DefaultNotificationFactory notificationFactory;
        notificationFactory = new DefaultNotificationFactory(getApplicationContext());

        // Custom notification icon
        notificationFactory.setSmallIconId(R.drawable.ic_notification);

        // The accent color for Android Lollipop+
        notificationFactory.setColor(NotificationCompat.COLOR_DEFAULT);

        // Set the factory on the PushManager
        airship.getPushManager().setNotificationFactory(notificationFactory);
    }
});

A SystemNotificationFactory class is also available that supports the same features as the DefaultNotificationFactory, but does not use the Big Text Style by default.

Custom Notification Factories

Custom notification factories are supported, but may cause some Android Push features to no longer work. Only the features that the factory implements will be available.

Example:

public class CustomNotificationFactory extends NotificationFactory {

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

    @Override
    public Notification createNotification(PushMessage message, int notificationId) {
        // do not display a notification if there is not an alert
        if (UAStringUtil.isEmpty(message.getAlert())) {
            return null;
        }

        // 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();
    }
}

Custom Notification Layouts

The CustomLayoutNotificationFactory 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:layout_height="fill_parent"
    android:layout_alignParentTop="true"
    android:orientation="vertical"
    android:background="@color/color_primary_dark"
    android:padding="8dp">

    <!-- 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.  -->
    <TextView
        android:id="@+id/message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="0dip"
        android:maxLines="2"
        android:text="Custom Layout"
        android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title"
        android:textSize="11dip"
        android:layout_toLeftOf="@+id/icon"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/subject" />

    <ImageView
        android:id="@+id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:src="@drawable/ic_notification"/>

</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 UAirship.takeOff:

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

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.

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

Clearing Notifications

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

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.

Apps targeting Android Marshmallow (API 23) and later will prompt users to grant permissions at runtime, instead of install time. Since users can revoke permissions at any time from the app Settings screen, your app needs to check that it has the permissions it needs every time it runs. See Permissions for more details.

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 UALocationManager.setLocationUpdatesEnabled. Continuing updates in the background is controlled by UALocationManager.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 PendingResult is returned. The PendingResult can be used to cancel the request or set a callback when the result is ready.

Example:

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

Cancelable locationRequest = UAirship.shared()
                                     .getLocationManager()
                                     .requestSingleLocation(locationCallback);

// Optionally you can cancel the request and the callback will not be called
locationRequest.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();


Cancelable locationRequest = UAirship.shared()
                                     .getLocationManager()
                                     .requestSingleLocation(locationCallback, 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.

Implementing your own GCM

Urban Airship is able to coexist with another GCM implementation, but involves a few slight modifications to Implementing GCM Client on Android

  1. Create a GcmListenerService to listen for incoming GCM messages. In the onMessageReceived method, filter out any messages that are intended for Urban Airship:

    public class CustomGcmListenerService extends GcmListenerService {
    
        @Override
        public void onMessageReceived(String from, Bundle data) {
            if (from.equals(UAirship.shared().getAirshipConfigOptions().gcmSender)) {
              // Handled by Urban Airship SDK
              return;
            }
    
            ...
        }
    }
    
  2. Register the CustomGcmListenerService in the application’s manifest:

    <service
        android:name=".CustomGcmListenerService"
        android:exported="false" >
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        </intent-filter>
    </service>
    
  3. To listen for when security tokens are refreshed, create an instance ID listener service that extends UAInstanceIDListenerService instead of InstanceIDListenerService:

    public class CustomInstanceIDListenerService extends UAInstanceIDListenerService {
    
       @Override
       public void onTokenRefresh() {
            // Make sure to call through to super
            super.onTokenRefresh();
    
            // handle token refresh
        }
    }
    
  4. Replace the Urban Airship UAInstanceIDListenerService with your CustomInstanceIDListenerService in the application’s manifest:

    <!-- Replace the Urban Airship UAInstanceIDListenerService with CustomInstanceIDListenerService -->
    <service android:name="com.urbanairship.push.UAInstanceIDListenerService" tools:node="remove"/>
    
    <service android:name=".CustomInstanceIDListenerService" android:exported="false">
          <intent-filter>
             <action android:name="com.google.android.gms.iid.InstanceID"/>
          </intent-filter>
    </service>
    
  5. Register any additional sender IDs:

    InstanceID instanceID = InstanceID.getInstance(this);
    String token = instanceID.getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
    

SDK Localization

The Urban Airship SDK provides localization for interactive notification buttons and the Message Center for the following languages: English, Chinese (Traditional), Chinese (Simplified), German, Hungarian, Slovak, Russian, Portuguese (Portugal), Portuguese (Brazil), French, Danish, Turkish, Norwegian, Romanian, Polish, Finnish, Dutch, Hindi, Spanish (Latin American), Spanish (Spain), Hebrew, Czech, Italian, Vietnamese, Thai, Japanese, Arabic, Korean, Malay, Indonesian, and Swedish.

To add translations for another language, follow these steps:

  1. Create a res/values-<language-code> folder, where <language-code> is the ISO 639-1 code for the language you are adding. For example, if you were trying to add Spanish, you would create a values-es folder.

  2. Copy the following code and place it in res/values-<language-code>/ua_strings.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
       <!--Notification action buttons text. The action buttons are displayed under a variety of alert messages -->
       <!-- Examples: Would you like to fill out a survey? [Yes] [No], 20% off Mens shoes [Shop now] -->
       <string name="ua_notification_button_yes">Yes</string>
       <string name="ua_notification_button_no">No</string>
       <string name="ua_notification_button_shop_now">Shop Now</string>
       <string name="ua_notification_button_buy_now">Buy Now</string>
       <string name="ua_notification_button_follow">Follow</string>
       <string name="ua_notification_button_unfollow">Unfollow</string>
       <string name="ua_notification_button_opt_in">Opt-in</string>
       <string name="ua_notification_button_opt_out">Opt-out</string>
       <string name="ua_notification_button_remind">Remind Me Later</string>
       <string name="ua_notification_button_share">Share</string>
       <string name="ua_notification_button_accept">Accept</string>
       <string name="ua_notification_button_decline">Decline</string>
       <string name="ua_notification_button_download">Download</string>
       <string name="ua_notification_button_more_like">More Like This</string>
       <string name="ua_notification_button_less_like">Less Like This</string>
       <string name="ua_notification_button_like">Like</string>
       <string name="ua_notification_button_dislike">Dislike</string>
       <string name="ua_notification_button_copy">Copy</string>
       <string name="ua_notification_button_save">Save</string>
       <string name="ua_notification_button_add_to_calendar">Add To Calendar</string>
       <string name="ua_notification_button_add">Add</string>
       <string name="ua_notification_button_tell_me_more">Tell Me More</string>
       <string name="ua_notification_button_send_info">Send Info</string>
       <string name="ua_notification_button_rate_now">Rate Now</string>
       <string name="ua_notification_button_search">Search</string>
       <string name="ua_notification_button_book_now">Book Now</string>
    
       <!--Share dialog title. The share dialog is used to share a message to a variety of social media and/or messaging apps.-->
       <string name="ua_share_dialog_title">Share</string>
    
       <!-- Channel Capture -->
       <string name="ua_channel_notification_ticker">Urban Airship Channel</string>
       <string name="ua_channel_copy_toast">Channel copied to clipboard!</string>
    
       <!-- Message Center -->
       <string name="ua_message_center_title">Message Center</string>
       <string name="ua_empty_message_list">No Messages</string>
       <string name="ua_message_not_selected">No Message Selected</string>
       <string name="ua_mc_failed_to_load">Unable to load message. Please try again later.</string>
       <string name="ua_retry_button">Retry</string>
       <string name="ua_ok">Ok</string>
       <string name="ua_cancel">Cancel</string>
       <string name="ua_select_none">Select None</string>
       <string name="ua_connection_error">Connection Error</string>
       <string name="ua_mark_read">Mark Read</string>
       <string name="ua_delete">Delete</string>
       <string name="ua_select_all">Select All</string>
    
       <!-- Label describing the number of selected items -->
       <plurals name="ua_selected_count">
         <item quantity="one"><xliff:g id="count" example="1">%1$d</xliff:g> selected</item>
         <item quantity="other"><xliff:g id="count" example="3">%1$d</xliff:g> selected</item>
       </plurals>
    
    </resources>
    
  3. Translate the copy of ua-strings.xml. For example, before translation, the start of the file should look like this:

    <?xml version="1.0" encoding="UTF-8"?><resources>
        <!--Notification action buttons text. The action buttons are displayed under a variety of alert messages -->
        <!-- Examples: Would you like to fill out a survey? [Yes] [No], 20% off Mens shoes [Shop now] -->
        <string name="ua_notification_button_yes">Yes</string>
        <string name="ua_notification_button_no">No</string>
        <string name="ua_notification_button_shop_now">Shop Now</string>
        ...
    </resources>
    

    Replace the strings with the equivalent string in the desired language. If you were creating a Spanish localization, the start of your res/values-es/ua-strings.xml file would look like this:

    <?xml version="1.0" encoding="UTF-8"?><resources>
        <!--Notification action buttons text. The action buttons are displayed under a variety of alert messages -->
        <!-- Examples: Would you like to fill out a survey? [Yes] [No], 20% off Mens shoes [Shop now] -->
        <string name="ua_notification_button_yes"></string>
        <string name="ua_notification_button_no">No</string>
        <string name="ua_notification_button_shop_now">Obtener ahora</string>
        ...
    </resources>
    

When you build your app, the new language resource will be merged automatically. For more information, please see the Android documentation on resource merging and localization.