Android

Getting started

1. Install the SDK

The fastest and easiest way to use Localytics in your project is with Maven. If you cannot use Maven, use the alternative installation approach.

  1. Update your project’s build.gradle script to include the Localytics Maven repository.

    apply plugin: 'com.android.application'
    
    repositories
    {
      jcenter()
      maven
      {
        url 'http://maven.localytics.com/public'
      }
    }
    
  2. Add dependencies for Localytics, Android Support v4, and Google Play Services Ads.

    dependencies
    {
      compile 'com.android.support:support-v4:23.1.0'
      compile 'com.google.android.gms:play-services-ads:9.4.0'
      compile 'com.localytics.android:library:4.3+'
    }
    

2. Modify AndroidManifest.xml

Add the following to your AndroidManifest.xml.

  1. Your Localytics app key within the application element
    <meta-data
      android:name="LOCALYTICS_APP_KEY"
      android:value="YOUR-LOCALYTICS-APP-KEY" />
    
  2. Localytics ReferralReceiver within the application element
    <receiver
      android:name="com.localytics.android.ReferralReceiver"
      android:exported="true">
      <intent-filter>
        <action android:name="com.android.vending.INSTALL_REFERRER" />
      </intent-filter>
    </receiver>
    
  3. Test mode intent-filter within your MainActivity activity element under the existing intent-filter (i.e. the one for android.intent.action.MAIN). Replace YOUR-LOCALYTICS-APP-KEY with your Localytics app key and be sure to prepend YOUR-LOCALYTICS-APP-KEY with amp as shown below.
    <intent-filter>
      <data android:scheme="ampYOUR-LOCALYTICS-APP-KEY" />
      <action android:name="android.intent.action.VIEW" />
      <category android:name="android.intent.category.DEFAULT" />
      <category android:name="android.intent.category.BROWSABLE" />
    </intent-filter>
    
  4. Permissions above the application element
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    

3. Modify your MainActivity

Override onNewIntent in your MainActivity as follows.

@Override
protected void onNewIntent(Intent intent)
{
  super.onNewIntent(intent);

  Localytics.onNewIntent(this, intent);
}

4. Initialize the SDK

  1. If you don't have a custom Application class, create one and specify the name in your AndroidManifest.xml as follows.

    <application
      android:name=".MyApplication">
    
  2. Make the following modifications to your Application class.

    1. Import the Localytics package.

      import com.localytics.android.*;
      
    2. Integrate the Localytics SDK. If you support Android API levels less than 14 (Ice Cream Sandwich), then use alternative session management.

      @Override
      public void onCreate()
      {
        super.onCreate();
      
        Localytics.autoIntegrate(this);
      }
      

5. Next steps

Congratulations! You have successfully performed the basic Localytics integration and are now sending session data to Localytics. You can also use Localytics In-App Messaging to message users in your app, and you have everything you need to track where your most highly engaged users are coming from.

Note that it may take a few minutes for your first datapoints to show up within the Localytics Dashboard. In the meantime, we suggest reading the next few sections to learn how to:

  1. Track one user action as an event
  2. Track one user property as a profile attribute
  3. Integrate push messaging

We recommend doing these things before releasing your app for the first time with Localytics.

Session lifecycle

With just the basic setup above, the Localytics SDK automatically tracks user engagement and retention by tracking patterns of foregrounding and backgrounding of your app. Upon foregrounding, the Localytics SDK automatically creates and uploads a "start session" datapoint that captures many details about the user's device (e.g., device model, OS version, device IDs) and is used for powering charts within Localytics.

Upon backgrounding, the SDK marks the current time. When the user returns to the app later and it has been more than 15 seconds since the user had last backgrounded the app, the SDK will close the previous session by creating a "close session" datapoint, create a new "start session" datapoint, and upload both of these datapoints. If the user foregrounds the app within 15 seconds of the previous backgrounding, the previous session is resumed as if the user had not left the app at all. Due to this automatic session lifecycle tracking, Localytics is able to derive session length, session interval, session counts, session trending, and a number of other core metrics for exploration in the Localytics Dashboard.

Whenever the app transitions to the foreground or background, the Localytics SDK attempts to upload any datapoints which are cached on the device. Uploads are performed in batches to reduce network use and increase the likelihood of successful uploads. Data remains on the device until it is successfully uploaded, and only then does the SDK remove it from the device.

Tagging events

Track user actions in your app using events in Localytics. All events must have a name, but you can also track the details of the action with event attributes. Event attributes help to provide context about why and how the action occurred. Every event can have up to 50 attributes unique to that event with each attribute having a limit of 255 characters.

Standard events

Standard events make it easier to analyze user behavior and optimize your app marketing around common business goals such as driving user registrations or purchases. You can also tag custom events for other user behavior in your app that doesn't match one of the standard events.

Purchased

Localytics.tagPurchased("Shirt", "sku-123", "Apparel", 15, extraAttributes);

Added to Cart

Localytics.tagAddedToCart("Shirt", "sku-123", "Apparel", 15, extraAttributes);

Started Checkout

Localytics.tagStartedCheckout(50, 2, extraAttributes);

Completed Checkout

Localytics.tagCompletedCheckout(50, 2, extraAttributes);

Content Viewed

Localytics.tagContentViewed("Top 10", "e8z7319zbe", "Article", extraAttributes);

Searched

Localytics.tagSearched("Celtics", "Sports", 15, extraAttributes);

Shared

Localytics.tagShared("Top 10", "e8z7319zbe", "Article", "Twitter", extraAttributes);

Content Rated

Localytics.tagContentRated("Headlines", "8a4z5j9q", "Song", 5, extraAttributes);

Customer Registered

The Customer parameter is optional - you can pass in null. However, if you do choose to include an Customer object, the appropriate identifying users properties will be automatically set.

Localytics.tagCustomerRegistered(new Customer.Builder()
    .setCustomerId("3neRKTxbNWYKM4NJ")
    .setFirstName("John")
    .setLastName("Smith")
    .setFullName("Sir John Smith, III")
    .setEmailAddress("sir.john@smith.com")
    .build(),
    "Facebook",
    extraAttributes
);

Customer Logged In

The Customer parameter is optional - you can pass in null. However, if you do choose to include an Customer object, the appropriate identifying users properties will be automatically set.

Localytics.tagCustomerLoggedIn(new Customer.Builder()
    .setCustomerId("3neRKTxbNWYKM4NJ")
    .setFirstName("John")
    .setLastName("Smith")
    .setFullName("Sir John Smith, III")
    .setEmailAddress("sir.john@smith.com")
    .build(),
    "Native",
    extraAttributes
);

Customer Logged Out

Localytics.tagLoggedOut(extraAttributes);

Invited

Localytics.tagInvited("SMS", extraAttributes);

Custom event

Localytics.tagEvent("Team Favorited");

Custom event with attributes

Map<String, String> attributes = new HashMap<String, String>();
attributes.put("Team Name", "Celtics");
attributes.put("City", "Boston");
Localytics.tagEvent("Team Favorited", attributes);

Identifying users

The Localytics SDK automatically captures and uploads device IDs which the Localytics backend uses to uniquely identify users. Some apps connect to their own backend systems that use different IDs for uniquely identifying users. There is often additional identifying information, such as name and email address, connected with the external IDs. Localytics provides various setters for passing this information to Localytics when it is available in your app. Using these setters ensures that you will be able to properly connect Localytics IDs to the IDs available in other systems.

To easily identify your users during your login and/or registration flow, use our customer registered and customer logged in standard events.

Customer ID

Localytics.setCustomerId("3neRKTxbNWYKM4NJ");

Customer first name

Localytics.setCustomerFirstName("John");

Customer last name

Localytics.setCustomerLastName("Smith");

Customer full name

Localytics.setCustomerFullName("Sir John Smith, III");

Customer email address

Localytics.setCustomerEmail("sir.john@smith.com");

User profiles

Track user properties using profile attributes in Localytics. Each profile has one or more named properties that describe that user. Because they contain rich user data, profiles are excellent for creating audiences to target with personalized messaging. Each profile is identified by a unique user ID that you provide to Localytics via the SDK. If you do not set a known user ID, then the Localytics SDK automatically generates an anonymous profile ID.

Each time you set the value of a profile attribute, you can set the scope to "app-level" or "org-level". App-level profile attributes are only stored in relation to that specific Localytics app key, so they can only be used for building audiences for that one app. Org-level profile attributes are available to all apps in the org, so they can be used across multiple Localytics app keys, which might represent the same app on a different platform or multiple apps produced by your company. If you choose not to set a scope, the SDK defaults to "app-level" scope.

If you repeatedly set the same profile attribute value, the Localytics SDK and backend will take care of deduplicating the values for you so only the most recent value gets stored for that profile.

Setting a profile attribute value

Numeric value

Localytics.setProfileAttribute("Age", 45, Localytics.ProfileScope.ORGANIZATION);

Numeric values in a set

Localytics.setProfileAttribute("Lucky numbers", new long[]{8, 13}, Localytics.ProfileScope.APPLICATION);

Date value

Localytics.setProfileAttribute("Birthday", new GregorianCalendar(1962, 11, 23).getTime(), Localytics.ProfileScope.ORGANIZATION);

Date values in a set

Localytics.setProfileAttribute("Upcoming Milestone Dates", new Date[]{new GregorianCalendar(2015, 10, 1).getTime(), new GregorianCalendar(2016, 3, 17).getTime()}, Localytics.ProfileScope.APPLICATION);

String value

Localytics.setProfileAttribute("Hometown", "New York, New York", Localytics.ProfileScope.ORGANIZATION);

String values in a set

Localytics.setProfileAttribute("States visited", new String[]{"New York", "California", "South Dakota"}, Localytics.ProfileScope.APPLICATION);

Removing a profile attribute

Localytics.deleteProfileAttribute("Days until graduation", Localytics.ProfileScope.APPLICATION);

Adding to a set of profile attribute values

Adding a numeric value to a set

Localytics.addProfileAttributesToSet("Lucky numbers", new long[]{8}, Localytics.ProfileScope.APPLICATION);

Adding a date value to a set

Localytics.addProfileAttributesToSet("Upcoming Milestone Dates", new Date[]{new GregorianCalendar(2015, 4, 19).getTime(), new GregorianCalendar(2015, 12, 24).getTime()}, Localytics.ProfileScope.APPLICATION);

Adding a string value to a set

Localytics.addProfileAttributesToSet("States visited", new String[]{"North Dakota"}, Localytics.ProfileScope.APPLICATION);

Removing from a set of profile attribute values

Removing numeric values from a set

Localytics.removeProfileAttributesFromSet("Lucky numbers", new long[]{8, 9}, Localytics.ProfileScope.APPLICATION);

Removing date values from a set

Localytics.removeProfileAttributesFromSet("Upcoming Milestone Dates", new Date[]{new GregorianCalendar(2016, 3, 17).getTime()}, Localytics.ProfileScope.APPLICATION);

Removing string values from a set

Localytics.removeProfileAttributesFromSet("States visited", new String[]{"California"}, Localytics.ProfileScope.APPLICATION);

Incrementing a numeric profile attribute value

Localytics.incrementProfileAttribute("Age", 1, Localytics.ProfileScope.ORGANIZATION);

Decrementing a numeric profile attribute value

Localytics.decrementProfileAttribute("Days until graduation", 3, Localytics.ProfileScope.APPLICATION);

In-app messaging

In-app messaging allows you to engage with your users while they are inside your app using templated or custom HTML creatives that you define within the Localytics Dashboard.

Triggering an in-app message

When creating in-app campaigns in the Localytics Dashboard, you decide under which conditions the in-app creative should display. You can trigger in-app messages to display at session start. You can also trigger in-app messages to display when a particular event is tagged using the event name alone or using the event name combined with attribute conditions.

Sometimes, you just want to display an in-app message, but there is not an existing event tag of which to take advantage, and you don't want to create an additional event datapoint solely to display a message. In this situation, Localytics marketing triggers allow you to trigger in-app messages off of specific user behaviors that you do not have tagged as an event in the Localytics dashboard.

There are a variety of scenarios where these triggers might be relevant:

  • You have a lot of Summary Events tagged, and Summary Events do not allow for granular behavioral triggering of messages.
  • You do not want to pass a particular behavior as an Event to the Localytics dashboard because the behavior is so frequent that an Event tag would incur high data point usage.

Instrumenting marketing triggers

Instrumenting Marketing Triggers in the app’s code base is simple. It’s essentially the same as tagging an Event, but rather than using [Localytics.tagEvent_Name] you will use the following

Localytics.triggerInAppMessage("Item Purchased");

Marketing triggers with attributes

To create a trigger with additional attribute conditions, use

Map<String, String> values = new HashMap<String, String>();
values.put("Item Name", "Stickers");
Localytics.triggerInAppMessage("Item Purchased", values);

Because there’s no data about that trigger in the Localytics backend, you’ll need to manually send the trigger attribute mappings to Localytics via a reported Event instead, at least one time. Essentially, you must fire an actual Event that reaches Localytics server once. You can do this by:

For one session tied to your app key - for example, just in your dev build but tagged with your production key temporarily - switch that triggerInAppMessage call to a standard tagEvent call instead, and run the app through that code to actually send the event to Localytics.

This will populate the event name and attributes in the autocomplete dialog within 10 minutes. Then you can switch the tag back from tagEvent to triggerInAppMessage. From there, you will be able to target on the trigger & attributes as if it were a normal event in the dashboard.

Selecting marketing triggers in the dashboard

Once the marketing trigger has been instrumented in the app’s code base, you can use these to trigger in-app messages in the Localytics dashboard. When you get to the Scheduling page of the campaign setup process, you will chose the “Event” trigger. For these marketing triggers, you will need to type in the name of the marketing trigger in the drop down (as seen below) - it will not automatically populate as the dashboard events do.

Customizing in-app messages

Dismiss button image

By default, there is a dismiss button in the shape of an "X" on the top-left of the in-app message dialog. Use the methods below to set the dismiss button to a different image or to change the position of the button to the top-right. If you change the image, the Localytics SDK creates a copy of the referenced image based on the maximum allowed image size, so your app doesn't have to keep the image after the call.

To set the image by name, use

Bitmap bitmap = getBitmapFromAsset("your-customized-dismiss-button.png");
Localytics.setInAppMessageDismissButtonImage(getResources(), bitmap);

To set the image by image, use

Localytics.setInAppMessageDismissButtonImage(getResources(), R.drawable.your_customized_dismiss_button);

If you want to clear the image and switch back to the default, pass a null to this method as below.

Localytics.setInAppMessageDismissButtonImage(getResources(), null);

Customizing the dismiss button location

To set the dismiss button location to the left, use

Localytics.setInAppDismissButtonLocation(Localytics.InAppMessageDismissButtonLocation.LEFT);

To set the dismiss button location to the right, use

Localytics.setInAppDismissButtonLocation(Localytics.InAppMessageDismissButtonLocation.RIGHT);

Customize In-Apps with a Callback

Using a messaging callback, as described in the advanced section, can allow for customization of individual in-apps right before they are shown to the user. The list of configurable properties are available on an object passed through the callback, the InAppConfiguration. Modification of these values will result in different displays for an individual creative. The list is as follows:

  • Aspect Ratio: The value passed in should be a float value representing a ratio of height to width. This property is only relevant for center in-app creatives.
  • Offset: The value passed in should always be a positive density independent pixel offset from the top or bottom of the screen. This property is only relevant for top and bottom banner in-app creatives.
  • Background Alpha: The value passed in should be a float value between 0.0 (transparent) and 1.0 (opaque).
  • Dismiss Button Image: The values passed in should be a reference to the app's Resource and either a Bitmap or a resource ID. This value will override any values set globally with Localytics.setInAppMessageDismissButtonImage().
  • Dismiss Button Location: The value passed in should be either InAppMessageDismissButtonLocation.LEFT for in-app creatives with a dismiss button on the left, or InAppMessageDismissButtonLocation.RIGHT for in-app creatives with a dismiss button on the right. This value will override any values set globally with Localytics.setInAppMessageDismissButtonLocation().
  • Dismiss Button Visibility: The value passed in should be an int corresponding to the values of View.GONE, View.INVISIBLE, or View.VISIBLE. If View.GONE or View.INVISIBLE is passed in then the dismiss button will be hidden for this in app creative.

A simple example of using the callbacks to modify an in-app creative may look as follows:

@NonNull
@Override
public InAppConfiguration localyticsWillDisplayInAppMessage(@NonNull final InAppCampaign campaign, @NonNull final InAppConfiguration configuration) {
    if (configuration.isCenterInAppCampaign()) {
        configuration.setAspectRatio(1.2f);
    } else if (configuration.isTopBannerInAppCampaign() || configuration .isBottomBannerInAppCampaign()) {
        configuration.setBannerOffsetDps(50);
    }
    configuration.setBackgroundAlpha(0f);

    configuration.setDismissButtonImage(getResources(), R.id.dismiss_button_image);
    configuration.setDismissButtonLocation(Localytics.InAppMessageDismissButtonLocation.RIGHT);
    configuration.setsetDismissButtonVisibility(View.VISIBLE);

    return configuration;
}

Custom Creative Javascript API

The Localytics SDK adds a localytics Javascript function to the in-app HTML that provides access to several native SDK methods and properties and handles URL navigation and in-app dismissal.

The sections below cover the details of the in-app Javascript API. It is crucial to understand these APIs when designing and uploading your own custom creatives. Note: When using the in-app message builder, calls to these APIs will be handled automatically.

Properties Accessors

  • localytics.campaign (Only available on SDK 4.3 and later): Returns a Javascript object containing information about the campaign that triggered this In-App message. The javascript object contains the campaign name as it would appear on the dashboard (name), the campaign ID (campaignId), the name of the event that triggered the In-App (eventName), and the attribute keys and values tagged on the event or trigger that launched the in-app message (eventAttributes).

    var campaign = localytics.campaign; // {"name": "App Upgrade Campaign", "campaignId": "449859", "eventName": "App Launch", "eventAttributes": {"isFirstSession": "NO"}};
    
  • localytics.identifiers: Returns a Javascript object containing a user's identifying data: customer_id, first_name, last_name, full_name, and email. Note: These values will only be populated if you have set them for the user directly via the SDK.

    var identifiers = localytics.identifiers; // {"customer_id": "3neRKTxbNWYKM4NJ", "first_name": "John", "last_name": "Smith", "full_name": "Sir John Smith, III", "email": "sir.john@smith.com"};
    
  • localytics.customDimensions: Returns a Javascript object containing the current session's custom dimensions. The keys of the object will be "c0", "c1", etc. for dimensions that have been set.

    var dimensions = localytics.customDimensions; // {"c0": "Paid", "c1": "Logged In"};
    
  • localytics.attributes: Returns a Javascript object containing the attribute keys and values tagged on the event or trigger that launched the in-app message.

    var eventAttributes = localytics.attributes; // {"Team Name": "Celtics", "City": "Boston"};
    
  • localytics.libraryVersion: Returns the Localytics SDK version as a string.

    var sdkVersion = localytics.libraryVersion; // "iOSa_4.3.0"
    

Methods

  • localytics.tagClickEvent(action) (Only available on SDK 4.3 and later): Tags an in-app clickthrough (also kown as a conversion) with an optional action attribute. If the action attribute is omitted, the default of click will be used. This method can only be called once per in-app. The first time this method is called an event will be recorded, and any subsequent calls will be ignored.

    localytics.tagClickEvent("Share");
    
  • localytics.tagEvent(event, attributes, customerValueIncrease): Tags an event with optional attributes and an optional custom value increase.

    function submitNPS(ratingValue)
    {
      var attributes = {"Raw Rating": ratingValue};
      if (ratingValue >= 9)
      {
        attributes["Rating"] = "Promoter";
      }
      else if (ratingValue <= 6)
      {
        attributes["Rating"] = "Detractor";
      }
      else
      {
        attributes["Rating"] = "Neutral";
      }
      localytics.tagEvent("In-App Rating Result", attributes);
    }
    
  • localytics.setCustomDimension(index, value): Sets a custom dimension value for a particular index.

    localytics.setCustomDimension(0, "Trial");
    
  • localytics.close(): Closes the in-app. If an in-app message viewed event hasn't been tagged (i.e. ampView), an event with ampAction equal to "X" will be tagged.

    function formSubmit()
    {
      localytics.tagEvent("Form Submit", {"Email": "john@smith.com"});
      localytics.close();
    }
    
  • localytics.setProfileAttribute(name, value, scope) (Only available on SDK 4.3 and later): Sets a profile attribute with an optional scope ("app" or "org").

    localytics.setProfileAttribute("Favorite Team", "Red Sox", "app");
    
  • localytics.deleteProfileAttribute(name, scope) (Only available on SDK 4.3 and later): Delete a profile attribute with an optional scope ("app" or "org").

    localytics.deleteProfileAttribute("Favorite Team", "app");
    
  • localytics.addProfileAttributesToSet(name, values, scope) (Only available on SDK 4.3 and later): Add profile attributes with an optional scope ("app" or "org").

    localytics.addProfileAttributesToSet("Favorite Team", ["Red Sox", "Celtics"], "org");
    
  • localytics.removeProfileAttributesFromSet(name, values, scope) (Only available on SDK 4.3 and later): Remove profile attributes with an optional scope ("app" or "org").

    localytics.removeProfileAttributesFromSet("Favorite Team", ["Red Sox", "Celtics"], "org");
    
  • localytics.incrementProfileAttribute(name, value, scope) (Only available on SDK 4.3 and later): Increment a profile attribute with an optional scope ("app" or "org").

    localytics.incrementProfileAttribute("Age", 1, "app");
    
  • localytics.decrementProfileAttribute(name, value, scope) (Only available on SDK 4.3 and later): Decrement a profile attribute with an optional scope ("app" or "org").

    localytics.decrementProfileAttribute("Days Until Graduation", 3, "org");
    
  • localytics.setCustomerFirstName(name, value, scope) (Only available on SDK 4.3 and later): Set the user's first name.

    localytics.setCustomerFirstName("John");
    
  • localytics.setCustomerLastName(name, value, scope) (Only available on SDK 4.3 and later): Set the user's last name.

    localytics.setCustomerLastName("Smith");
        
    
  • localytics.setCustomerFullName(name, value, scope) (Only available on SDK 4.3 and later): Set the user's full name.

    localytics.setCustomerFullName("Sir John Smith, III");
        
    
  • localytics.setCustomerEmail(name, value, scope) (Only available on SDK 4.3 and later): Set the user's email.

    localytics.setCustomerEmail("sir.john@smith.com");
        
    

URL and Deep Link Navigation

Tagging Click Events

All displayed in-app messages tag a single viewed event (i.e. ampView) with an action attribute (i.e. ampAction). Each viewed event is described as an "impression" on the campaign performance page in the Localytics Dashboard. To record a campaign clickthrough (also known as a conversion) when a call-to-action (CTA) is pressed, append the ampAction=click key-value query string to the end of your web or deep link URL. Note: This query paramater is added automatically when entering a URL or deep link into the CTA field in the campaign editor in the Localytics Dashboard.

// Opening a web URL
function openHomePage()
{
  window.open("http://www.localytics.com?ampAction=click&ampExternalOpen=true");
  localytics.close();
}

// Opening a deep link URL
function openFavorites()
{
  window.open("myApp://favorites?ampAction=click");
  localytics.close();
}

If you have multiple CTAs within an in-app creative, you can use different ampAction values to distinguish each clickthrough. Any ampAction not equal to "X" will be considered a clickthrough. By using different ampAction values, you will be able to see which CTA the user clicks on the campaign performance page in the Localytics Dashboard (instead of all CTAs being grouped under a single "click").

// Opening a deep link to the favorites page
function openFavorites()
{
  window.open("myApp://favorites?ampAction=favorites");
  localytics.close();
}

// Opening a deep link to the share page
function openShare()
{
  window.open("myApp://share?ampAction=share");
  localytics.close();
}

By default, when an in-app is dismissed (by pressing the "X" button or calling localytics.close()) and a viewed event has not yet been tagged, an ampView event is automatically tagged with an ampAction equal to "X".

Opening URLs

Use the Javascript window.open(url) function when opening HTTP and HTTPS URLs and other HTML files contained within the ZIP. URLs can be opened either in the phone's default browser (e.g. Chrome or Safari) or directly within the in-app message view. To control this behavior, append the ampExternalOpen parameter to the URL.

// Opening a web URL in the phone's default browser
function openHomepage()
{
  window.open("http://www.localytics.com?ampAction=click&ampExternalOpen=true");
  localytics.close();
}

// Opening a web URL within the in-app message view
function openBlogInsideInApp()
{
  window.open("http://info.localytics.com/blog?ampAction=click&ampExternalOpen=false");
}

// Opening another HTML file contained in the ZIP within the in-app message view
function goToFeedback()
{
  window.open("feedback.html?ampAction=feedback&ampExternalOpen=false");
}

Push messaging

Push messaging allows you to keep your users up-to-date and reengage them with your app after periods of inactivity.

Before continuing, please be sure that you have completed all of the steps in Getting Started. If you are already using Google Cloud Messaging follow our custom push configuration instructions.

If you are using the v3 version of the Localytics SDK, follow our guide for Push messaging in v3 in the Legacy SDKs section.

Localytics push messaging can be integrated with either Google Cloud Messaging (GCM) or Firebase Cloud Messaging (FCM). FCM is the latest push platform for Android, and although the Localytics SDK was developed against the GCM platform, FCM is fully supported. To use FCM, follow our Firebase Cloud Messaging Integration guide. Otherwise, follow our Google Cloud Messaging Integration guide.

Google Cloud Messaging Integration

A sample project for using Localytics with Google Cloud Messaging is available in our Android samples Github repository.

1. Create a Google API project and enable GCM

  1. Visit the Google services page.
  2. Create or choose your app and package name and click Choose and configure services.
  3. Click the Cloud Messaging button.
  4. Note your Server API Key and Sender ID.

2. Add the Google Cloud Messaging dependency

Add the dependency for Google Cloud Messaging (GCM).

dependencies
{
  compile 'com.android.support:support-v4:23.1.0'
  compile 'com.google.android.gms:play-services-ads:9.4.0'
  compile 'com.google.android.gms:play-services-gcm:9.4.0'
  compile 'com.localytics.android:library:4.3+'
}

3. Modify AndroidManifest.xml

Add the following to your AndroidManifest.xml. Replace YOUR-PACKAGE-NAME with your application's package wherever you see it.

  1. Push permissions above the application element.

    <uses-permission
        android:name="com.google.android.c2dm.permission.RECEIVE" />
    <permission
        android:name="YOUR-PACKAGE-NAME.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
    <uses-permission
        android:name="YOUR-PACKAGE-NAME.permission.C2D_MESSAGE" />
    
  2. The Google Play Services GcmReceiver, Localytics GcmListenerService, and Localytics InstanceIDListenerService within the application element. If you are already using GCM or another push provider, follow our custom push configuration instructions.

    <receiver
        android:name="com.google.android.gms.gcm.GcmReceiver"
        android:exported="true"
        android:permission="com.google.android.c2dm.permission.SEND" >
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <category android:name="YOUR-PACKAGE-NAME" />
        </intent-filter>
    </receiver>
    
    <service
        android:name="com.localytics.android.GcmListenerService"
        android:exported="false" >
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        </intent-filter>
    </service>
    
    <service
        android:name="com.localytics.android.InstanceIDListenerService"
        android:exported="false" >
        <intent-filter>
            <action android:name="com.google.android.gms.iid.InstanceID" />
        </intent-filter>
    </service>
    
  3. The Localytics PushTrackingActivity within the application element.

    <activity android:name="com.localytics.android.PushTrackingActivity"/>
    

4. Register for push notifications in your app

Register for push using your GCM Sender ID within onCreate() of your app's MainActivity.

Localytics.registerPush("YOUR-SENDER-ID");

Also, make sure that the user's device has the latest version of Google Play Services installed by following our guide for updating Play Services.

5. Add your server API Key to the Localytics Dashboard

  1. Log in to the Localytics Dashboard, navigate to Settings > Apps, and input your server API key within Add Certs as shown in the steps in the image below.

    screenshot of server API key in Localytics Dashboard

6. Test push integration

If you have integrated SDK version 4.1 or later, use the Rapid Push Verification page to confirm that you have integrated push correctly. Be sure to select the correct app in the dropdown at the top of the page.

screenshot of Rapid Push Verification page

7. Next steps

Since the release of Lollipop (API 21), the material design style dictates that all non-alpha channels in notification icons be ignored. Therefore, depending on the shape of your app icon, your notification icon may not appear as desired on devices running Lollipop or later. To change the notification icon and accent color, use MessagingListener#localyticsWillShowPushNotification to modify the NotificationCompat.Builder to meet your UI needs by following the steps in Modifying push notifications.

Firebase Cloud Messaging Integration

A sample project for using Localytics with Firebase Cloud Messaging is available in our Android samples Github repository.

1. Add Firebase to your Android project

Follow the instructions for Adding Firebase to your Android project. Ensure that you have followed both steps for Adding Firebase to your app and Adding the SDK.

2. Add the Firebase Cloud Messaging dependency

Add the dependency for Firebase Cloud Messaging (FCM) as follows.

dependencies
{
  compile 'com.android.support:support-v4:23.1.0'
  compile 'com.google.android.gms:play-services-ads:9.4.0'
  compile 'com.google.firebase:firebase-messaging:9.4.0'
  compile 'com.localytics.android:library:4.3+'
}

3. Extend Firebase Service classes

  1. Create a class that extends FirebaseInstanceIdService and pass the token to Localytics as follows.

    public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService
    {
      @Override
      public void onTokenRefresh()
      {
        String refreshedToken = FirebaseInstanceId.getInstance().getToken();
        Localytics.setPushRegistrationId(refreshedToken);
      }
    }
    
  2. Create a class that extends FirebaseMessagingService, and if the received message is from Localytics, call Localytics.displayPushNotification(Bundle data) with the received message. Othwerwise, handle the message yourself as follows.

    public class MyFirebaseMessagingService extends FirebaseMessagingService
    {
      /**
       * All pushes received from Localytics will contain an 'll' string extra which can be parsed into
       * a JSON object. This JSON object contains performance tracking information, such as a campaign
       * ID. Any push received containing this 'll' string extra, should be handled by the Localytics
       * SDK. Any other push can be handled as you see fit.
       */
      @Override
      public void onMessageReceived(RemoteMessage remoteMessage)
      {
        Map<String, String> data = remoteMessage.getData();
        if (data != null)
        {
          if (data.containsKey("ll"))
          {
            Localytics.displayPushNotification(convertMap(data));
          }
          else
          {
            // The notification is not from Localytics or the Firebase Dashboard
            showNotification(data.get("message"));
          }
        }
      }
    
      private Bundle convertMap(Map<String, String> map)
      {
        Bundle bundle = new Bundle(map != null ? map.size() : 0);
        if (map != null)
        {
          for (Map.Entry<String, String> entry : map.entrySet())
          {
            bundle.putString(entry.getKey(), entry.getValue());
          }
        }
    
        return bundle;
      }
    
      private void showNotification(String message)
      {
        if (!TextUtils.isEmpty(message))
        {
          Intent intent = new Intent(this, MainActivity.class);
          intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
          PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
                 PendingIntent.FLAG_ONE_SHOT);
    
          NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                 .setSmallIcon(R.mipmap.ic_launcher)
                 .setContentTitle("FCM Message")
                 .setContentText(message)
                 .setAutoCancel(true)
                 .setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE)
                 .setContentIntent(pendingIntent);
    
          NotificationManager notificationManager =
                 (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    
          notificationManager.notify(0, notificationBuilder.build());
        }
      }
    }
    

4. Modify AndroidManifest.xml

Add declarations for your FirebaseInstanceIdService and FirebaseMessagingService and the Localytics PushTrackingActivity to the application element of your AndroidManifest.xml.

<service
  android:name=".MyFirebaseInstanceIDService">
  <intent-filter>
    <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
  </intent-filter>
</service>

<service
  android:name=".MyFirebaseMessagingService">
  <intent-filter>
    <action android:name="com.google.firebase.MESSAGING_EVENT"/>
  </intent-filter>
</service>

<activity android:name="com.localytics.android.PushTrackingActivity"/>

5. Add your server API key to the Localytics Dashboard

  1. Retrieve your server API key from your Firebase project's settings as shown in the steps in the image below.

    screenshot of server API key in Firebase console
  2. Log in to the Localytics Dashboard, navigate to Settings > Apps, and input your server API key within Add Certs as shown in the steps in the image below.

    screenshot of server API key in Localytics Dashboard

6. Test push integration

If you have integrated SDK version 4.1 or later, use the Rapid Push Verification page to confirm that you have integrated push correctly. Be sure to select the correct app in the dropdown at the top of the page.

screenshot of Rapid Push Verification page

7. Next steps

Since the release of Lollipop (API 21), the material design style dictates that all non-alpha channels in notification icons be ignored. Therefore, depending on the shape of your app icon, your notification icon may not appear as desired on devices running Lollipop or later. To change the notification icon and accent color, use MessagingListener#localyticsWillShowPushNotification to modify the NotificationCompat.Builder to meet your UI needs by following the steps in Modifying push notifications.

App Inbox

App Inbox allows you to deliver personalized content to users through a dedicated inbox inside your app. Create Inbox Campaigns using templated or custom HTML creatives from within the Localytics Dashboard. Inbox messages will display in your users' inbox for a scheduled amount of time.

Before continuing, please be sure that you have completed all of the steps in Getting Started.

To add App Inbox to your app you need to include a list of inbox messages within your app's user interface and then handle displaying the inbox message detail view. The instructions below will walk your through the process.

1. Include a list of inbox messages

You have 2 options for adding a list of inbox messages to your app:

  1. Using InboxListAdapter, which is the recommended and simplest approach.
  2. Getting inbox campaigns from the Localytics class static methods and displaying them in your own View.

Using InboxListAdapter (recommended)

  1. Add a ListView and a TextView to be used as the empty view to an Activity or Fragment layout as follows.

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <ListView
            android:id="@+id/lv_inbox"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/white"
            android:divider="?android:attr/listDivider"/>
    
        <TextView
            android:id="@+id/tv_empty_inbox"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="@string/no_messages"/>
    
    </FrameLayout>
    
  2. Set the TextView as the empty view of your ListView and then create an InboxListAdapter and set it as the Adapter of the ListView in your Activity or Fragment as follows.

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_inbox);
    
        ListView listView = (ListView) findViewById(R.id.lv_inbox);
        listView.setEmptyView(findViewById(R.id.tv_empty_inbox));
        InboxListAdapter inboxListAdapter = new InboxListAdapter(this);
        listView.setAdapter(inboxListAdapter);
    
        // ...
    }
    
  3. Tell the InboxListAdapter to retrieve campaign data as follows. You can optionally include a callback interface to be notified when the refresh has completed.

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
    
        // ...
    
        listView.setAdapter(inboxListAdapter);
        inboxListAdapter.getData(new InboxListAdapter.Listener()
        {
            @Override
            public void didFinishLoadingCampaigns()
            {
                // optionally hide a ProgressBar
            }
        });
    
        // ...
    }
    

Using your own View

If you prefer to not use InboxListAdapter, you can get inbox data directly via the Localytics class static methods. These methods are useful if you would rather use a RecyclerView or another interface for displaying a list of inbox messages.

To retrieve cached inbox campaign data, call Localytics.getInboxCampaigns() from a background thread as follows.

new AsyncTask<Void, Void, List<InboxCampaign>>()
{
    @Override
    protected List<InboxCampaign> doInBackground(Void... params)
    {
        return Localytics.getInboxCampaigns();
    }

    @Override
    protected void onPostExecute(List<InboxCampaign> campaigns)
    {
        // create UI using campaigns
    }
}.execute();

To refresh inbox campaign data from the network, call Localytics.refreshInboxCampaigns(InboxRefreshListener listener) as follows.

Localytics.refreshInboxCampaigns(new InboxRefreshListener()
{
    @Override
    public void localyticsRefreshedInboxCampaigns(List<InboxCampaign> campaigns)
    {
        // refresh UI using campaigns
    }
});

2. Display an inbox message detail view

If you are using the v3 version of the Localytics SDK, follow our guide for Displaying an inbox message detail view in v3 in the Legacy SDKs section.

You must use InboxDetailFragment or InboxDetailSupportFragment to display an inbox message detail view. These classes extend android.app.Fragment and android.support.v4.app.Fragment respectively. If your Activity extends FragmentActivity, ActionBarActivity, or AppCompatActivity use InboxDetailSupportFragment and the Android Support v4 FragmentManager. Otherwise, use InboxDetailFragment.

Create a new InboxDetailFragment using an InboxCampaign object as follows. If you need to use the Support Library, replace InboxDetailFragment with InboxDetailSupportFragment and getFragmentManager() with getSupportFragmentManager().

InboxCampaign campaign = /* campaign from InboxListAdapter or Localytics.getInboxCampaigns */;
InboxDetailFragment fragment = InboxDetailFragment.newInstance(campaign);

If you are using InboxListAdapter, you should add an AdapterView.OnItemClickListener to your ListView that handles marking the campaign as read, refreshing the adapter, and showing the detail view in a separate Activity or Fragment as follows. The InboxCampaign class implements the Parcelable interface so you can add it to any Intent.

public class MyInboxActivity extends Activity implements AdapterView.OnItemClickListener
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
		{
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_inbox);

        ListView listView = (ListView) findViewById(R.id.lv_inbox);
        listView.setOnItemClickListener(this);
        InboxListAdapter inboxListAdapter = new InboxListAdapter(this);
        listView.setAdapter(inboxListAdapter);
        inboxListAdapter.getData(null);
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id)
    {
        InboxListAdapter inboxListAdapter = (InboxListAdapter) parent.getAdapter();
        InboxCampaign campaign = inboxListAdapter.getItem(position);
        campaign.setRead(true);

        inboxListAdapter.notifyDataSetChanged();

        if (campaign.hasCreative())
        {
            Intent intent = new Intent(this, MyInboxDetailActivity.class);
            intent.putExtra("campaign", campaign);
            startActivity(intent);
        }
    }
}
public class MyInboxDetailActivity extends Activity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_inbox_detail);

        if (savedInstanceState == null)
				{
            InboxCampaign campaign = getIntent().getParcelableExtra("campaign");
            InboxDetailFragment fragment = InboxDetailFragment.newInstance(campaign);
            getFragmentManager().beginTransaction()
                    .add(R.id.container, fragment)
                    .commit();
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

3. Customize the appearance and behavior

To match the look and feel of your app, there are several customization options available for App Inbox.

Changing fonts and color

To change the InboxListItem unread indicator color and text size, color, and font, subclass InboxListAdapter and override getView(int position, View convertView, ViewGroup parent) as follows.

public class MyInboxListAdapter extends InboxListAdapter
{
    public MyInboxListAdapter(Context context)
    {
        super(context);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {
        InboxListItem item;
        if (convertView == null)
        {
            item = new InboxListItem(getContext());

            // customize TextViews and UnreadIndicatorView
            item.getTitleTextView().setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
            item.getSummaryTextView().setTextColor(ContextCompat.getColor(getContext(), R.color.text_color));
            item.getUnreadIndicatorView().setColor(ContextCompat.getColor(getContext(), R.color.unread_color));
        }
        else
        {
            item = (InboxListItem) convertView;
        }

        item.populateViews(getItem(position), true);

        return item;
    }
}

Manually managing thumbnails

To manually manage InboxListItem thumbnail downloading and caching, subclass InboxListAdapter and disable thumbnail downloading before you add it to the ListView. Next, override getView(int position, View convertView, ViewGroup parent) to handle thumbnail images as follows.

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_inbox);

    ListView listView = (ListView) findViewById(R.id.lv_inbox);
    MyInboxListAdapter inboxListAdapter = new MyInboxListAdapter(this);
    inboxListAdapter.setDownloadsThumbnails(false);
    listView.setAdapter(inboxListAdapter);

    // ...
}
public class MyInboxListAdapter extends InboxListAdapter
{
    public MyInboxListAdapter(Context context)
    {
        super(context);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {
        if (convertView == null)
        {
            convertView = new InboxListItem(getContext());
        }

        InboxListItem item = (InboxListItem) convertView;
        InboxCampaign campaign = getItem(position);
        item.populateViews(campaign, false);

        ImageView thumbnailImageView = item.getThumbnailImageView();
        if (campaign.hasThumbnail())
        {
            thumbnailImageView.setVisibility(View.VISIBLE);
            Uri thumbnailUri = campaign.getThumbnailUri();
            // load thumbnail URI into ImageView
        }
        else
        {
            thumbnailImageView.setImageURI(null);
            thumbnailImageView.setVisibility(View.GONE);
        }

        return item;
    }
}

Using a custom list item view

To use your own inflated view within InboxListAdapter, subclass InboxListAdapter and disable thumbnail downloading before you add it to the ListView. Next, override getView(int position, View convertView, ViewGroup parent) to inflate and populate your own view as follows.

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_inbox);

    ListView listView = (ListView) findViewById(R.id.lv_inbox);
    MyInboxListAdapter inboxListAdapter = new MyInboxListAdapter(this);
    inboxListAdapter.setDownloadsThumbnails(false);
    listView.setAdapter(inboxListAdapter);

    // ...
}
public class MyInboxListAdapter extends InboxListAdapter
{
    public MyInboxListAdapter(Context context)
    {
        super(context);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {
        if (convertView == null)
        {
            LayoutInflater inflater = (LayoutInflater) getContext()
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.item_inbox, parent, false);
            convertView.setTag(new ViewHolder(convertView));
        }

        ViewHolder holder = (ViewHolder) convertView.getTag();
        InboxCampaign campaign = getItem(position);

        holder.title.setText(campaign.getTitle());
        holder.summary.setText(campaign.getSummary());

        return convertView;
    }

    static class ViewHolder
    {
        TextView title;
        TextView summary;

        public ViewHolder(View view)
        {
            title = (TextView) view.findViewById(R.id.tv_title);
            summary = (TextView) view.findViewById(R.id.tv_summary);
        }
    }
}

Handling message detail errors

If you are using the v3 version of the Localytics SDK, follow our guide for Handling detail message errors in v3 in the Legacy SDKs section.

In rare cases the inbox message detail view may fail to load. By default, InboxDetailFragment will display a gray "X" in the center of the view when this occurs. To provide your own custom error view, implement the InboxDetailCallback interface in the attached Activity as follows.

public class MyInboxActivity extends Activity implements InboxDetailCallback
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_inbox_campaign);

        if (savedInstanceState == null)
        {
            InboxCampaign campaign = getIntent().getParcelableExtra("campaign");
            InboxDetailFragment fragment = InboxDetailFragment.newInstance(campaign);
            getFragmentManager().beginTransaction()
                    .add(R.id.container, fragment)
                    .commit();
        }
    }

    @Override
    public void onCreativeLoadError()
    {
        findViewById(R.id.error_view).setVisibility(View.VISIBLE);
    }
}

Places

One of the unique aspects of mobile is that users always have their devices with them. Places lets you take advantage of this by sending users content that's personalized to their current location. With Places, you can send notifications to users the instant they enter or exit a specific location. Additionally, you can analyze data about visits to physical locations, giving you access to insights that have never before been available. Read more about setting up Places geofences.

Before continuing, please be sure that you have completed all of the steps in Getting Started. If you want to manually monitor geofences, follow our custom places integration instructions.

1. Add the Google Play Services Location dependency

Update your project's build.gradle to include the Google Play Services location dependency as follows.

dependencies
{
  compile 'com.android.support:support-v4:23.1.0'
  compile 'com.google.android.gms:play-services-ads:9.4.0'
  compile 'com.google.android.gms:play-services-location:9.4.0'
  compile 'com.localytics.android:library:4.3+'
}

2. Modify AndroidManifest.xml

Add the following to your AndroidManifest.xml as follows.

  1. Permissions for accessing fine location and receiving boot completed intents above the application element.

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    
  2. The Google Play Services version within the application element.

    <meta-data
      android:name="com.google.android.gms.version"
      android:value="@integer/google_play_services_version" />
    
  3. The Localytics GeofenceTransitionsService within the application element.

    <service android:name="com.localytics.android.GeofenceTransitionsService"/>
    
  4. The Localytics BootReceiver within the application element.

    <receiver android:name="com.localytics.android.BootReceiver" >
      <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
      </intent-filter>
    </receiver>
    
  5. The Localytics PushTrackingActivity within the application element for tracking Places campaign performance.

    <activity android:name="com.localytics.android.PushTrackingActivity"/>
    

3. Enable location monitoring

Enable location monitoring in your Application class after integrating Localytics as follows.

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

  Localytics.autoIntegrate(this);
  Localytics.setLocationMonitoringEnabled(true);
}

4. Make sure user has latest Google Play Services

Follow the guide on updating Google Play Services.

5. Request location permissions for Android Marshmallow

If you app is targeting Marshmallow (targetSdkVersion of 23 or higher), you need to request location permissions at runtime. If the reason that your app needs location permissions is not obvious, we recommend showing the user an explanation before presenting the permission prompt.

Request the location permission at runtime as follows.

  1. Create a unique request code for your Activity as follows.

    private static final int REQUEST_LOCATION_PERMISSION = 101;
    
  2. In your Activity check for the location permission and request it if it is not granted as follows.

    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED)
    {
      ActivityCompat.requestPermissions(
          this,
          new String[] {Manifest.permission.ACCESS_FINE_LOCATION},
          REQUEST_LOCATION_PERMISSION
      );
    }
    
  3. In your Activity handle the permissions request result as follows.

    @Override
    public void onRequestPermissionsResult(
        int requestCode,
        @NonNull String[] permissions,
        @NonNull int[] grantResults)
    {
      super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    
      switch (requestCode)
      {
          case REQUEST_LOCATION_PERMISSION:
            if (permissions.length > 0 && permissions[0].equals(Manifest.permission.ACCESS_FINE_LOCATION)
                  && grantResults[0] == PackageManager.PERMISSION_GRANTED)
            {
              Localytics.setLocationMonitoringEnabled(true);
            }
            else
            {
              // Show an explanation toast or dialog
              Toast.makeText(this, "The app needs location permissions to continue.", Toast.LENGTH_LONG).show();
            }
            break;
      }
    }
    

Uninstall Tracking

Localytics Uninstall Tracking reports an analytics event for users that uninstall your app at high accuracy within 24 hours. Those uninstalls can be used in charts, funnels, campaign performance reports, and remarketing so you can find and address the root causes of user loss.

Before continuing, please be sure that you have completed all of the steps in Getting Started and Push Messaging. You'll need a subscription to Uninstall Tracking to use this feature.

1. Register for a push token

Localytics Uninstall Tracking requires Localytics push integrated in your app. To properly track uninstalls for all users - regardless of notification permissions - you need to register for a push token as early as possible.

Basic Integration

Follow our standard guide for integrating push, ensuring you call Localytics.registerPush in your main Activity’s onCreate method.

Custom Integration

If your app is registering for push directly with GCM/FCM rather than using Localytics.registerPush, follow the Localytics custom push configuration instructions. Ensure your app registers with GCM/FCM and calls Localytics.setPushRegistrationId in your main Activity's onCreate.

2. Ensuring Your App Ignores Uninstall Tracking Pushes (Custom Integrations Only)

If you are performing any custom push notification handling or are integrating with other push providers, you should ensure that the Localytics silent uninstall tracking pushes do not trigger any local notification display in your code.

The Localytics uninstall tracking push includes a key/value of localyticsUninstallTrackingPush: "true" which you can use to detect when an app launch is coming from a background uninstall tracking push. Your application's onCreate will also be called if a push is received while the app is closed; if you are performing any server checks assuming that the app has been launched in the foreground by the user, you should prevent that if the app launch was triggered from an incoming push notification.

3. Provisioning Uninstalls

Once your app's integration has been set up, contact your account manager or our support team to enable Uninstall Tracking for your app. You'll need an active subscription to Uninstalls. Let us know which Localytics apps are configured for uninstalls so only apps that have completed integration are provisioned.

Tracking user flow

Track screens or views within your app so you can visualize user flow within the Localytics Dashboard. We recommend exhaustively tagging every visible view in your app.

The Localytics SDK will perform duplicate suppression on two identical tagged screens that occur in a row within a single session. For example, in the set of screens {"Screen 1", "Screen 1"}, the second screen would be suppressed. However, in the set {"Screen 1", "Screen 2", "Screen 1"}, no duplicate suppression would occur.

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

    Localytics.tagScreen("Item List");
}

Tracking revenue

There are two ways to think about Lifetime Value (LTV) in Localytics: monetary and non-monetary. If your app allows real currency transactions, our Dashboard can show LTV in USD. If your app uses virtual currencies like coins or tokens, you can configure your app to report LTV as the raw data you pass in.

You can configure each Mobile App in Localytics to have either a monetary value or raw data for Lifetime Value:

Tracking Monetary LTV

If you'd like to track LTV as monetary-based revenue, you should increment the value upon the completion of a purchase by the purchase amount. Make sure to configure your app in Localytics to show LTV as "Tracked as Money (US cents)".

LTV must be an integer amount, and the Localytics system requires you pass the number of USD cents as the LTV value in order to track money. For example, if the purchase amount is "USD $2.99", you should pass the integer "299" as the LTV. If the cents don't matter to you, feel free to round up to whole dollar values, but continue to pass the value as cents. If you want to track the rounded value of "USD $3.00", you should pass "300" as the value.

Currently, Localyics only allows LTV tracking in USD. If you want to track other currencies, you could convert the monetary amount to USD on the device before sending to Localytics.

Tracking Non-Monetary LTV

Another way to measure LTV is to track a non-monetary value important to your app. Examples include the number seconds spent engaged with content, or the amount of virtual currency earned. To track these values, send the corresponding integer value to Localytics. Make sure to configure your app in Localytics to show LTV as "Raw Value". Otherwise, Localytics will automatically divide your values by 100 and display a "$" in front of the values in the Dashboard.

LTV Examples

Increment user lifetime value (LTV) on any event using the optional LTV incrementer parameter as seen below.

You can increment LTV with our standard purchased and completed checkout events as follows. The item price and total price values will be used respectively.

Localytics.tagPurchased("Shirt", "sku-123", "Apparel", 15, extraAttributes);
Localytics.tagCompletedCheckout(50, 2, extraAttributes);

You can also increment LTV using a custom event by including a customer value increase value.

Map<String, String> values = new HashMap<String, String>();
values.put("Item name", "Stickers");
values.put("Aisle", "Knick-Knacks");
Localytics.tagEvent("Item Purchased", values, 499);

Setting custom dimensions

Custom dimensions are special fields that are used for splitting and filtering data within the Localytics Dashboard and are useful for storing user-level information. Custom dimensions are like sticky event attributes in the sense that once their value is set, it remains set until the value is changed again. Unlike event attributes, custom dimension values are attached to all three types of datapoints (session start, session close, and event) the same way that standard dimensions are which makes their values available within almost every report in the Localytics Dashboard.

Custom dimensions start at an index of 0 and the number of custom dimensions available to you depends on the level of your subscription. Name all of your custom dimensions in the Localytics Dashboard > Settings > Apps > (find app) > Gear icon > Custom Dimensions.

Whenever a datapoint is created, all custom dimension values are attached to the datapoint. Therefore, it is ideal to set custom dimensions as soon as their value is known in all code paths in your app. It is not uncommon to use an analytics callback to set custom dimension values before the start of a session (and the creation of a "session start" datapoint).

Setting a value

Localytics.setCustomDimension(0, "Trial");

Initializing dimensions before session start

Set the value of all custom dimensions as early as possible in your app's code path even if you only set the value to "Not applicable" or "Not available". Having at least some value for each custom dimension instead of nothing will prevent the display of "[Unspecified]" values in the Dashboard and help to make it much more obvious to distinguish intentionally missing values from unintentionally missing values.

Use the AnalyticsListener to initialize your custom dimension values on the user's first session. The isFirst value will be true in the localyticsSessionWillOpen callback on the user's first session.

In your Application class implement the AnalyticsListener interface and set your default values in localyticsSessionWillOpen as follows.

public class MyApplication extends Application implements AnalyticsListener
{

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

    Localytics.autoIntegrate(this);
    Localytics.setAnalyticsListener(this);
  }

  @Override
  public void localyticsSessionWillOpen(boolean isFirst, boolean isUpgrade, boolean isResume)
  {
    if (isFirst)
    {
      Localytics.setCustomDimension(0, "Logged Out");
      Localytics.setCustomDimension(1, "0");
    }
  }

  @Override
  public void localyticsSessionDidOpen(boolean isFirst, boolean isUpgrade, boolean isResume)
  {
  }

  @Override
  public void localyticsSessionWillClose()
  {
  }

  @Override
  public void localyticsDidTagEvent(String eventName, Map<String, String> attributes, long customerValueIncrease)
  {
  }
}

Alternatively, you can use the AnalyticsListenerAdapter class to implement only localyticsSessionWillOpen as follows.

public class MyApplication extends Application
{

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

    Localytics.autoIntegrate(this);
    Localytics.setAnalyticsListener(new AnalyticsListenerAdapter()
    {

      @Override
      public void localyticsSessionWillOpen(boolean isFirst, boolean isUpgrade, boolean isResume)
      {
        if (isFirst)
        {
          Localytics.setCustomDimension(0, "Logged Out");
          Localytics.setCustomDimension(1, "0");
        }
      }

    });
  }
}

Clearing a value

Though it may be more appropriate to set a custom dimension value back to an initial state, sometimes you may need to completely clear the value. Setting an an empty string will NOT clear the value. To clear a value use the following code:

Localytics.setCustomDimension(0, null);

Tracking user acquisition source

Use Localytics to track the origin of your new users and attribute them to a specific acquisition source. Localytics employs a variety of attribution methods, but at the core, all methods work by matching a shared identifier from both before and after app installation.

In order for your app to be able to access the INSTALL_REFERRER Intent that is required for most Android acquisition tracking to work, be sure that you have made the proper modifications to your AndroidManifest.xml as discussed at the beginning of this guide. You've likely already made this modification, but it's worth checking just to be sure.

If your app or another SDK in your app are already listening for the INSTALL_REFERRER Intent that Localytics uses, in part, to track user acquistion source on Android, follow our instructions for using Localytics in addition to another attribution provider.

Testing attribution

Test your Android Attribution integration quickly and easily using test mode and the Android Debug Bridge (ADB) utility.

You can also test Android attribution manually. When you use test mode, Localytics creates a fake Device ID, and then creates a fake Google Advertising ID for that Device ID, so that you can run tests without needing physical data.

  1. Uninstall any previous installations of your test app on your test device. Then, install your test app.
  2. Open the test app on your test device.
  3. In your terminal, confirm that ADB is installed and in your environment path.
  4. Run the following command
    adb shell am broadcast -a com.android.vending.INSTALL_REFERRER -n YOUR-PACKAGE-NAME/com.localytics
    .android.ReferralReceiver --es "referrer" "utm_source%3Dother_src31%26utm_medium%3Dm31%26utm_term%3Dt31%26utm_content
    %3Dc31%26utm_campaign%3DC31%26localytics_test_mode%3Dtrue"
    
  5. Check the Attribution Dashboard to see if your data appears correctly.

Advanced

Installing without Maven

If you are unable to install the SDK using Maven, the following alternative approach will also install the SDK and its dependencies.

  1. Download the latest version of the SDK here.

  2. Unzip the archive and drag the JAR into your project's libs directory.

  3. Update your project's build.gradle to add dependencies for the Localytics JAR, Android Support v4, and Google Play Services as follows.

    dependencies
    {
      compile files('libs/localytics.jar')
      compile 'com.android.support:support-v4:23.2.0'
      compile 'com.google.android.gms:play-services-gcm:9.4.0'
      compile 'com.google.android.gms:play-services-ads:9.4.0'
    }
    
  4. If you are building your app using Proguard, update your project's proguard-rules.pro to include Localytics exceptions as follows.

    -keep class com.localytics.android.** { *; }
    -keepattributes JavascriptInterface
    -dontwarn com.localytics.android.**
    
  5. You are now ready to continue with the setup process here.

Alternative session management

The standard Localytics session management approach relies on the Localytics SDK to automatically listen for indications of state change (e.g. foreground / background transitions) in your app and then to track the state change accordingly. Most apps are able to use the standard approach. If you are unable to use the standard session management approach, the instructions below will help you to initialize the SDK to achieve the same result as the standard approach.

  1. If you don't have a custom Application class, create one and specify the name in your AndroidManifest.xml as follows

    <application
      android:name=".MyApplication">
    
  2. In your Application class

    1. Import the Localytics package as follows.

      import com.localytics.android.*;
      
    2. Initialize Localytics as follows.

      @Override
      public void onCreate()
      {
        super.onCreate();
      
        Localytics.integrate(this);
      }
      
  3. In every Activity in your app, override onResume() and onPause() to manage the session and uploading. The simplest approach for accomplishing this task is to include this code in a common base Activity that each Activity extends.

    1. In onResume, notify the Localytics SDK that the activity has resumed as follows.

      @Override
      protected void onResume()
      {
        super.onResume();
      
        Localytics.onActivityResume(this);
      }
      
    2. In onPause, notify the Localytics SDK that the activity has paused as follows.

      @Override
      protected void onPause()
      {
        super.onPause();
      
        Localytics.onActivityPause(this);
      }
      
    3. In onNewIntent, notify the Localytics SDK of the new Intent as follows.

      @Override
      protected void onNewIntent(Intent intent) {
          super.onNewIntent(intent);
      
          Localytics.onNewIntent(this, intent);
      }
      
  4. You are now ready to continue with the next steps.

Callbacks

Analytics callbacks

These analytics callbacks are useful for setting the value of a custom dimension or profile attribute before a session opens, firing an event at the beginning or end of a session, or taking action in your app based on an auto-tagged campaign performance event in the Localytics SDK. All Localytics analytics callbacks are called on an internal background thread.

In your Application class implement the AnalyticsListener interface as follows.

public class MyApplication extends Application implements AnalyticsListener
{

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

    Localytics.autoIntegrate(this);
    Localytics.setAnalyticsListener(this);
  }

  @Override
  public void localyticsSessionWillOpen(boolean isFirst, boolean isUpgrade, boolean isResume)
  {
    // ... do something ...
  }

  @Override
  public void localyticsSessionDidOpen(boolean isFirst, boolean isUpgrade, boolean isResume)
  {
    // ... do something ...
  }

  @Override
  public void localyticsSessionWillClose()
  {
    // ... do something ...
  }

  @Override
  public void localyticsDidTagEvent(String eventName, Map<String, String> attributes, long customerValueIncrease)
  {
    // ... do something ...
  }
}

Alternatively, you can use the AnalyticsListenerAdapter class to listen for specific callbacks.

public class MyApplication extends Application
{

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

    Localytics.autoIntegrate(this);
    Localytics.setAnalyticsListener(new AnalyticsListenerAdapter()
    {

      @Override
      public void localyticsSessionWillOpen(boolean isFirst, boolean isUpgrade, boolean isResume)
      {
        // ... do something ...
      }

    });
  }
}

Messaging callbacks

Messaging callbacks are useful for understanding when Localytics in-app messages are about to appear to prevent conflicts with other views in your app. All Localytics messaging callbacks are called on the main thread.

In your Application class implement the MessagingListener interface as follows.

public class MyApplication extends Application implements MessagingListener
{

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

    Localytics.autoIntegrate(this);
    Localytics.setMessagingListener(this);
  }

  @Override
  public boolean localyticsShouldShowInAppMessage(@NonNull final InAppCampaign campaign) {
      return true; //return false to suppress the in-app
  }

  @Override
  public boolean localyticsShouldDelaySessionStartInAppMessages() {
      return false; // return true to delay the triggering of messages set to display on session start.  
      // To later trigger these messages call Localytics.triggerSessionStartInAppMessages().
  }

  @NonNull
  @Override
  public InAppConfiguration localyticsWillDisplayInAppMessage(@NonNull final InAppCampaign campaign, @NonNull final InAppConfiguration configuration) {
       // ... optionally modify the aspect ratio for center in-app campaigns, offset for banner in-app campaigns and background alpha ...
      return configuration;
  }

  @Override
  public void localyticsWillDisplayInAppMessage()
  {
    // ... do something ...
  }

  @Override
  public void localyticsDidDisplayInAppMessage()
  {
    // ... do something ...
  }

  @Override
  public void localyticsWillDismissInAppMessage()
  {
    // ... do something ...
  }

  @Override
  public void localyticsDidDismissInAppMessage()
  {
    // ... do something ...
  }

  @Override
  public boolean localyticsShouldShowPushNotification(@NonNull PushCampaign campaign)
  {
    return true; // return false to suppress the notification
  }

  @Override
  public boolean localyticsShouldShowPlacesPushNotification(@NonNull PlacesCampaign campaign)
  {
    return true; // return false to suppress the notification
  }

  @NonNull
  @Override
  public NotificationCompat.Builder localyticsWillShowPlacesPushNotification(@NonNull NotificationCompat.Builder builder, @NonNull PlacesCampaign campaign)
  {
    // ... optionally modify the icon, sound, or defaults ...
    return builder;
  }

  @NonNull
  @Override
  public NotificationCompat.Builder localyticsWillShowPushNotification(@NonNull NotificationCompat.Builder builder, @NonNull PushCampaign campaign)
  {
    // ... optionally modify the icon, sound, or defaults ...
    return builder;
  }
}

Alternatively, you can use the MessagingListenerAdapter class to listen for specific callbacks.

public class MyApplication extends Application
{

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

    Localytics.autoIntegrate(this);
    Localytics.setMessagingListener(new MessagingListenerAdapter()
    {

      @Override
      public void localyticsWillDisplayInAppMessage()
      {
        // ... do something ...
      }

    });
  }
}

Location callbacks

Location callbacks are useful for understanding when Localytics responds location changes. localyticsDidUpdateLocation is called on an internal background thread; the others are called on the main thread.

In your Application class implement the LocationListener interface as follows.

public class MyApplication extends Application implements LocationListener
{

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

    Localytics.autoIntegrate(this);
    Localytics.setLocationListener(this);
  }

  @Override
  public void localyticsDidUpdateLocation(@Nullable Location location)
  {
    // ... do something ...
  }

  @Override
  public void localyticsDidTriggerRegions(@NonNull List<Region> regions, @NonNull Region.Event event)
  {
    // ... do something ...
  }

  @Override
  public void localyticsDidUpdateMonitoredGeofences(@NonNull List<CircularRegion> added, @NonNull List<CircularRegion> removed)
  {
    // ... do something ...
  }
}

Tracking location

Most apps don't track user geolocation so Localytics uses an IP-to-location lookup service to determine which country they are located in. If your app does track user geolocation, you can use the following code to set latitude and longitude in Localytics much more precisely. The exact latitude and longitude are not available within the Localytics Dashboard, but are available in the export logs. Localytics will always attach the most recently provided location to each datapoint, similar to custom dimensions, so it is best to set the location anytime there is a significant location change and before each session start.

You can set the user’s current location in the class that implements the LocationListener callback as follows.

@Override
public void onLocationChanged(Location location)
{
  Localytics.setLocation(location);
}

Custom Places integration

With the standard Places integration the Localytics SDK will automatically monitor geofences and update the device's location using the lowest power setting. However, if you prefer to get location updates yourself and monitor geofences, you can use the APIs described below to notify the Localytics SDK when geofences are entered and exited.

1. Getting geofences to monitor

On each location update get the closest 20 geofences to monitor for the given location as follows. The CircularRegion object contains a unique ID, name, radius, latitude, longitude, and attributes.

List<CircularRegion> geofences = Localytics.getGeofencesToMonitor(location.getLatitude(), location.getLongitude());

2. Notifying the SDK of geofence enter and exit triggers

For Places analytics and messaging functionality, notify the SDK when a geofence is entered or exited as follows.

CircularRegion geofence = new CircularRegion.Builder()
    .setUniqueId(uniqueId)
    .build();
Localytics.triggerRegion(geofence, Region.Event.ENTER);

3. Notifying the SDK of location updates

Get more accurate location data for your events and sessions by setting the current location of the device by following the steps in Tracking location.

4. Resume location updates and region monitoring on device boot

The Android OS won't automatically resume location updates and geofence monitoring after a device is powered off and back on. Therefore, you must create a BroadcastReceiver for the BOOT_COMPLETED Intent to resume location services.

  1. Add the RECEIVE_BOOT_COMPLETED permission above the application element of your AndroidManifest.xml.

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    
  2. Add a declaration for a BroadcastReceiver that receives the BOOT_COMPLETED Intent within the application element of your AndroidManifest.xml

    <receiver android:name=".MyBootReceiver" >
      <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
      </intent-filter>
    </receiver>
    
  3. Implement MyBootReceiver to resume location updates and geofence monitoring. When the first location update is returned, get the closest geofences from the Localytics SDK and begin monitoring them as described in getting geofences to monitor.

    public class MyBootReceiver extends BroadcastReceiver
    {
      @Override
      public void onReceive(final Context context, Intent intent)
      {
        // TODO: Create a GoogleApiClient and a LocationRequest
        // to resume location updates with a balanced power/accuracy priority.
        // When the first location update is returned, get the geofences
        // to monitor from the Localytics SDK and add them using a GeofencingRequest
      }
    }
    

Setting a custom identifier

Once a custom identifier is set, it is attached to each subsequent datapoint, similar to the behavior of custom dimensions. Unlike custom dimensions, however, custom identifiers only end up in export data. They are useful for attaching multiple unique identifiers to a given datapoint for use in joining your Localytics export data with other sources in an enterprise data warehouse. Custom identifiers are not used by the Localytics backend or Dashboard for anything and are just passed through to the export data.

Localytics.setIdentifier("Hair Color", "Black");

Using Localytics with other attribution providers

Android only supports one BroadcastReceiver for the INSTALL_REFERRER IntentFilter. Therefore, if you need to use Localytics alongside other attribution providers, you need to follow the instructions below to delegate the referrer Intent to the other relevant BroadcastReceivers.

  1. Create a BroadcastReceiver class and delegate the referrer Intent to the Localytics ReferralReceiver and any other relevant BroadcastReceivers as follows.

    public class MyReferralReceiver extends BroadcastReceiver
    {
    
      @Override
      public void onReceive(Context context, Intent intent)
      {
        ReferralReceiver localyticsReferralReceiver = new ReferralReceiver();
        localyticsReferralReceiver.onReceive(context, intent);
    
        OtherReceiver1 otherReceiver1 = new OtherReceiver1();
        otherReceiver1.onReceive(context, intent);
    
        OtherReceiver2 otherReceiver2 = new OtherReceiver2();
        otherReceiver2.onReceive(context, intent);
      }
    }
    
  2. In your AndroidManifest.xml, include your custom BroadcastReceiver within the application tag as follows.

    <receiver android:name=".MyReferralReceiver" android:exported="true">
      <intent-filter>
        <action android:name="com.android.vending.INSTALL_REFERRER" />
      </intent-filter>
    </receiver>
    

Modifying push notifications

If you are using the v3 version of the Localytics SDK, follow our guide for Push notification options in v3 in the Legacy SDKs section.

When using the standad Localytics push integration you can modify several notification properties using MessagingListener, such as the title, LED color, sound, accent color, and icon. Due to the new material design style , we recommend that you at least set the accent color and icon for supporting devices running Lollipop (API 21) and later.

If you haven't set a MessagingListener on the Localytics SDK, set one after initializing the SDK and implement the localyticsWillShowPushNotification method to modify the NotificationCompat.Builder. You can also use the MessagingListenerAdapter to override just the methods that you need. Note: We do not recommend modifying the content intent. However, if you must modify the content intent, follow our guide for Building notifications and tagging push events.

public class MyApplication extends Application
{
  @Override
  public void onCreate()
  {
    super.onCreate();

    Localytics.autoIntegrate(this);
    Localytics.setMessagingListener(new MessagingListenerAdapter()
    {
      @NonNull
      @Override
      public NotificationCompat.Builder localyticsWillShowPushNotification(@NonNull NotificationCompat.Builder builder,
                                                                           @NonNull PushCampaign campaign)
      {
        return builder
              .setSmallIcon(getSmallIcon())
              .setColor(getColor(R.color.accent_color))
              .setContentTitle(getString(R.string.notification_title))
              .setSound(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.notification_sound));
      }
    });
  }

  private int getSmallIcon()
  {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
    {
      return R.drawable.ic_launcher_alpha_only;
    }
    else
    {
      return R.drawable.ic_launcher;
    }
  }
}

Custom push configuration

If you are using the v3 version of the Localytics SDK, follow our guide for Custom push configuration in v3 in the Legacy SDKs section.

Whether you are using another push provider or sending pushes with your own system, getting Localytics push messaging working alongside another implementation may take a few additional steps. The sections below will help you setup the correct configuration for your app.

Sending a registration ID to Localytics

If you are already registering with GCM you need to send that registration ID to Localytics.

  1. Send the registration ID to Localyics as follows. If this is the first version of your app to include the Localytics SDK, call this method on the first app launch for this version and not just on a token refresh to ensure Localytics gets the current token for users updating from the previous app version.

    When using GoogleCloudMessaging:

    GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
    String registrationId = gcm.register(SENDER_ID);
    Localytics.setPushRegistrationId(registrationId);
    

    When using InstanceID:

    InstanceID instanceID = InstanceID.getInstance(context);
    String token = instanceID.getToken(SENDER_ID, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
    Localytics.setPushRegistrationId(token);
    

    When using Firebase:

    String token = FirebaseInstanceId.getInstance().getToken();
    Localytics.setPushRegistrationId(token);
    
  2. After you have successfully set the push registration ID, follow the steps for using Localytics with GcmReceiver and a GcmListenerService or using Localytics with a standard BroadcastReceiver. By following one of these sections, notification display and push received and push opened tracking will automatically be handled. If you would prefer to build and display notifications yourself and manually track push received and push opened, follow the steps for building notifications and tagging push events.

Using Localytics with GcmReceiver and a GcmListenerService

Problems may arise when declaring multiple push BroadcastReceivers or Services because other receivers or services may not properly ignore pushes sent by Localytics. This can cause issues with how notifications are displayed (duplicates) and/or how push opens are tracked.

If you are using com.google.android.gms.gcm.GcmReceiver and a GcmListenerService that extends com.google.android.gms.gcm.GcmListenerService, change the parent class of your GcmListenerService to be com.localytics.android.GcmListenerService and add PushTrackingActivity to your AndroidManifest.xml.

A sample project for using Localytics with a custom GcmListenerService is available in our Android samples Github repository.

  1. Extend com.localytics.android.GcmListenerService in your GcmListenerService and pass the message to the super class only if the received message is from Localytics. Otherwise, handle the message yourself as follows.

    public class MyGcmIntentService extends com.localytics.android.GcmListenerService
    {
      /**
       * All pushes received from Localytics will contain an 'll' string extra which can be parsed into
       * a JSON object. This JSON object contains performance tracking information, such as a campaign
       * ID. Any push received containing this 'll' string extra, should be handled by the Localytics
       * super class. Any other push can be handled as you see fit.
       */
      @Override
      public void onMessageReceived(String from, Bundle data)
      {
        if (data.containsKey("ll"))
        {
          super.onMessageReceived(from, data);
        }
        else
        {
          String message = data.getString("message");
          if (!TextUtils.isEmpty(message))
          {
            Intent mainIntent = new Intent(this, MainActivity.class);
            PendingIntent launchIntent = PendingIntent.getActivity(this, 1, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                 .setSmallIcon(R.mipmap.ic_launcher)
                 .setContentTitle(getString(R.string.app_name))
                 .setContentText(message)
                 .setStyle(new NotificationCompat.BigTextStyle().bigText(message))
                 .setContentIntent(launchIntent)
                 .setDefaults(Notification.DEFAULT_ALL)
                 .setAutoCancel(true);
    
            NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            notificationManager.notify(0, builder.build());
          }
        }
      }
    }
    
  2. Add declarations for the Google Play Services GcmReceiver, your GcmIntentService, and the Localytics PushTrackingActivity to the application element of your AndroidManifest.xml.

    <receiver
      android:name="com.google.android.gms.gcm.GcmReceiver"
      android:exported="true"
      android:permission="com.google.android.c2dm.permission.SEND" >
      <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <category android:name="YOUR-PACKAGE-NAME" />
      </intent-filter>
    </receiver>
    
    <service
      android:name=".MyGcmListenerService"
      android:exported="false" >
      <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
      </intent-filter>
    </service>
    
    <activity android:name="com.localytics.android.PushTrackingActivity"/>
    
  3. You are now ready to continue with adding your server API key to the Localytics Dashboard.

Using Localytics with a standard BroadcastReceiver

Problems may arise when declaring multiple push BroadcastReceivers or Services because other receivers or services may not properly ignore pushes sent by Localytics. This can cause issues with how notifications are displayed (duplicates) and/or how push opens are tracked.

If you are using a standard BroadcastReceiver, forward the received Intent's Bundle to the Localytics SDK and add PushTrackingActivity to your AndroidManifest.xml.

A sample project for using Localytics with a standard BroadcastReceiver is available in our Android samples Github repository.

  1. If the received message is from Localytics, call Localytics.displayPushNotification(Bundle data) with the received message's Bundle. Othwerwise, handle the intent yourself as follows.

    public class MyBroadcastReceiver extends BroadcastReceiver
    {
      /**
       * All pushes received from Localytics will contain an 'll' string extra which can be parsed into
       * a JSON object. This JSON object contains performance tracking information, such as a campaign
       * ID. Any push received containing this 'll' string extra, should be handled by the Localytics
       * SDK. Any other push can be handled as you see fit.
       */
      @Override
      public void onReceive(Context context, Intent intent)
      {
        if (intent.hasExtra("ll"))
        {
          Localytics.displayPushNotification(intent.getExtras());
        }
        else
        {
          String message = intent.getStringExtra("message");
          if (!TextUtils.isEmpty(message))
          {
            Intent mainIntent = new Intent(context, MainActivity.class);
            PendingIntent launchIntent = PendingIntent.getActivity(context, 1, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
            NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
                   .setSmallIcon(R.mipmap.ic_launcher)
                   .setContentTitle(context.getString(R.string.app_name))
                   .setContentText(message)
                   .setStyle(new NotificationCompat.BigTextStyle().bigText(message))
                   .setContentIntent(launchIntent)
                   .setDefaults(Notification.DEFAULT_ALL)
                   .setAutoCancel(true);
    
            NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            notificationManager.notify(0, builder.build());
          }
        }
      }
    }
    
  2. Add your BroadcastReceiver and the Localytics PushTrackingActivity to the application element of your AndroidManifest.xml.

    <receiver
      android:name=".MyBroadcastReceiver"
      android:permission="com.google.android.c2dm.permission.SEND" >
      <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <category android:name="YOUR-PACKAGE-NAME" />
      </intent-filter>
    </receiver>
    
    <activity android:name="com.localytics.android.PushTrackingActivity"/>
    
  3. You are now ready to continue with adding your server API key to the Localytics Dashboard.

Building notifications and tagging push events

If you would prefer to not use the Localytics GcmListenerService, you will need to handle building notifications and tagging the push received and push opened events.

  1. Declare the Google Play Services GcmReceiver, the Localytics PushTrackingActivity, and your GcmListenerService in your AndroidManifest.xml as follows.

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.localytics.android.example" >
    
        <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
        <permission
            android:name="com.localytics.android.example.permission.C2D_MESSAGE"
            android:protectionLevel="signature" />
    
        <uses-permission android:name="com.localytics.android.example.permission.C2D_MESSAGE" />
    
        <application>
    
            <activity android:name="com.localytics.android.PushTrackingActivity"/>
    
            <receiver
                android:name="com.google.android.gms.gcm.GcmReceiver"
                android:exported="true"
                android:permission="com.google.android.c2dm.permission.SEND" >
                <intent-filter>
                    <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                    <category android:name="com.localytics.android.example" />
                </intent-filter>
            </receiver>
    
            <service
                android:name=".MyGcmListenerService"
                android:exported="false" >
                <intent-filter>
                    <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                </intent-filter>
            </service>
    
            <service
                android:name="com.localytics.android.InstanceIDListenerService"
                android:exported="false" >
                <intent-filter>
                    <action android:name="com.google.android.gms.iid.InstanceID" />
                </intent-filter>
            </service>
    
        </application>
    </manifest>
    
  2. When the Bundle is delivered to your Service, tag the push received event and build and show the notification using PushTrackingActivity as the PendingIntent.

      public class MyGcmListenerService extends com.google.android.gms.gcm.GcmListenerService
      {
        @Override
        public void onMessageReceived(String from, Bundle data)
        {
          Localytics.tagPushReceivedEvent(data);
    
          String message = data.getString("message");
          if (!TextUtils.isEmpty(message))
          {
            Intent trackingIntent = new Intent(context, PushTrackingActivity.class);
            trackingIntent.putExtras(data); // add all extras from received bundle
    
            int requestCode = getRequestCode(data);
            PendingIntent contentIntent = PendingIntent.getActivity(context, requestCode, trackingIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
            NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
                   .setSmallIcon(R.mipmap.ic_launcher)
                   .setContentTitle(context.getString(R.string.app_name))
                   .setContentText(message)
                   .setStyle(new NotificationCompat.BigTextStyle().bigText(message))
                   .setContentIntent(contentIntent)
                   .setDefaults(Notification.DEFAULT_ALL)
                   .setAutoCancel(true);
    
            NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            notificationManager.notify(requestCode, builder.build());
        }
      }
    
      /**
       * Get a unique requestCode so we don't override other unopened pushes. The Localytics SDK
       * uses the campaign ID (ca) within the 'll' JSON string extra. Use that value if it exists.
       */
      private int getRequestCode(Bundle extras)
      {
        int requestCode = 1;
        if (extras != null && extras.containsKey("ll"))
        {
          try
          {
            JSONObject llObject = new JSONObject(extras.getString("ll"));
            requestCode = llObject.getInt("ca");
          }
          catch (JSONException e)
          {
          }
        }
        return requestCode;
      }
    }
    

If you cannot use PushTrackingActivity in the PendingIntent, then you must handle tagging the push opened event when your Activity resumes.

  1. When building the notification include the ll key as an extra in the PendingIntent Intent by adding all the extras from the received Intent as follows.

    public class MyGcmListenerService extends com.google.android.gms.gcm.GcmListenerService
    {
      @Override
      public void onMessageReceived(String from, Bundle data)
      {
        Localytics.tagPushReceivedEvent(data);
    
        String message = data.getString("message");
        if (!TextUtils.isEmpty(message))
        {
          Intent launchIntent = new Intent(context, MainActivity.class);
          launchIntent.putExtras(data); // add all extras from received bundle
    
            int requestCode = getRequestCode(data);
            PendingIntent contentIntent = PendingIntent.getActivity(context, requestCode, trackingIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
            NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
                   .setSmallIcon(R.mipmap.ic_launcher)
                   .setContentTitle(context.getString(R.string.app_name))
                   .setContentText(message)
                   .setStyle(new NotificationCompat.BigTextStyle().bigText(message))
                   .setContentIntent(contentIntent)
                   .setDefaults(Notification.DEFAULT_ALL)
                   .setAutoCancel(true);
    
            NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            notificationManager.notify(requestCode, builder.build());
        }
      }
    
      /**
       * Get a unique requestCode so we don't override other unopened pushes. The Localytics SDK
       * uses the campaign ID (ca) within the 'll' JSON string extra. Use that value if it exists.
       */
      private int getRequestCode(Bundle extras)
      {
          int requestCode = 1;
          if (extras != null && extras.containsKey("ll"))
          {
              try
              {
                  JSONObject llObject = new JSONObject(extras.getString("ll"));
                  requestCode = llObject.getInt("ca");
              }
              catch (JSONException e)
              {
              }
          }
          return requestCode;
      }
    }
    
  2. When your Activity resumes or receives the new Intent, handle the push opened as follows. We recommend using a base Activity that all Activities in our app extend so any Activity can be used in the PendingIntent.

    public class BaseActivity extends FragmentActivity
    {
        @Override
        protected void onResume()
        {
            super.onResume();
    
            Localytics.handlePushNotificationOpened(getIntent());
        }
    
        @Override
        protected void onNewIntent(Intent intent)
        {
            super.onNewIntent(intent);
    
            Localytics.handlePushNotificationOpened(intent);
        }
    }
    
  3. You are now ready to continue with adding your server API key to the Localytics Dashboard.

Update Google Play Services

The latest Google Play Services update must be installed on the user's device for functionality to work properly. Ensure that the user has the latest update as follows.

  1. In your MainActivity create a unique request code as follows.

    private static final int REQUEST_GOOGLE_PLAY_SERVICES = 100;
    
  2. In onResume() of your MainActivity, check for Google API availability and prompt the user to update if neccessary as follows.

    @Override
    protected void onResume()
    {
      super.onResume();
    
      GoogleApiAvailability api = GoogleApiAvailability.getInstance();
      int code = api.isGooglePlayServicesAvailable(this);
      if (code == ConnectionResult.SUCCESS)
      {
        // The device's Google Play Services version is up to date
    
        // If you are integrating Places, add the following line:
        Localytics.setLocationMonitoringEnabled(true);
    
        // If you are integating Push, add the following line:
        Localytics.registerPush("YOUR-SENDER-ID");
      }
      else if (api.isUserResolvableError(code))
      {
        if (!api.showErrorDialogFragment(this, code, REQUEST_GOOGLE_PLAY_SERVICES))
        {
          Toast.makeText(this, api.getErrorString(code), Toast.LENGTH_LONG).show();
        }
      }
      else
      {
        Toast.makeText(this, api.getErrorString(code), Toast.LENGTH_LONG).show();
      }
    }
    
  3. Handle the Google Play Services request code as follows.

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
      super.onActivityResult(requestCode, resultCode, data);
    
      switch(requestCode)
      {
        case REQUEST_GOOGLE_PLAY_SERVICES:
          if (resultCode == Activity.RESULT_OK)
          {
            // The device's Google Play Services version is up to date
    
            // If you are integrating Places, add the following line:
            Localytics.setLocationMonitoringEnabled(true);
    
            // If you are integating Push, add the following line:
            Localytics.registerPush("YOUR-SENDER-ID");
          }
          break;
      }
    }
    
  4. You can now continue integration for Push or Places.

Sending a test mode push fron the Dashboard

After completing push integration you can create a test campaign to send yourself a push message to be sure that everything is properly configured.

  1. Login to the Localytics Dashboard, navigate to Messaging, select your app key from the dropdown at the top of the screen, and click the + button at the top right of the screen.
  2. Name your campaign, e.g. "Push Test", and click Continue to Creatives.
  3. Under Message Body, enter a sample push alert message, e.g. "Push test example message".
  4. Select One Time and Immediately then click Continue and Confirm.
  5. Click Test on Device.
  6. Click Enable Test Mode.
  7. Use one of the options mentioned on the screen to open the test mode URL on your connected device. If the URL doesn't cause your app to open at this point, please ensure that you have completed all steps of Getting Started, especially the test mode step.
  8. Your test push message will appear on your connected device. If the push does not immediately appear, check that your server API key is correct, then wait a few minutes and retry.

Delaying Session Start In-Apps

There are certain scenarios (like when presenting a splash screen) in which In-apps set to trigger on Session Start should be delayed. In order to delay those messages you must implement the messaging callback. An example of how to delay those in-apps would look as follows:

@Override
public boolean localyticsShouldDelaySessionStartInAppMessages()
{
  if (this.isSplashScreenShowing())
  {
    return true;
  }
  return false;
}

Then in the future when in-apps that are triggered by Session Start are valid, call:

Localytics.triggerInAppMessagesForSessionStart();
    

Troubleshooting

To gain more insight into whether your Localytics tagging is occuring when expected, you can enable logging in your app using the code below. When looking at logs, a 202 response code indicates that an upload was successful. Be sure to disable logging in production builds. Localytics log output is required when contacting support.

Localytics.setLoggingEnabled(true);

Updating the SDK

This section contains guides to help developers using older Localytics SDK versions to update their Localytics integration since a breaking change occurred.

Most Localytics SDK updates do not change existing interfaces and dependencies, though sometimes might add new interfaces. Because there are not breaking changes, you can effortlessly update to the latest Localytics SDK version before each of your app releases.

Localytics will infrequently introduce breaking changes as part of the introduction of new functionality or to lay a better foundation for future feature development.

Updating from v3 to v4

v4 of the Localytics Android SDK includes several public API changes, support for standard events, and improvements to its interface with the Google Play Services APIs. v4 also includes the new Places product and drops support for In-App Messaging on devices running Gingerbread (API level 9 and 10).

Follow the steps in each section below to make the appropriate adjustments for updating to v4.

Integration

In your MainActivity or your app's base Activity class that all extend, instead of setting the new Intent in onNewIntent, call the Localytics.onNewIntent(Activity activity, Intent intent); method.

Update the following

@Override
protected void onNewIntent(Intent intent)
{
  super.onNewIntent(intent);

  setIntent(intent);
}

to

@Override
protected void onNewIntent(Intent intent)
{
  super.onNewIntent(intent);

  Localytics.onNewIntent(this, intent);
}

Callbacks

v4 only supports a single callback for the AnalyticsListener interface and the MessagingListener interface. Therefore, the API has changed from add to set. If you are using one of these listeners, confirm that you are only setting this listener in one place in your app.

Replace the following method calls:

Localytics.addAnalyticsListener(this);
Localytics.addMessagingListener(this);

with the set versions as follows.

Localytics.setAnalyticsListener(this);
Localytics.setMessagingListener(this);

Push messaging

Within v4 push messaging has been updated to use InstanceID for Google Cloud Messaging registration and GcmReceiver from Play Services for handling receiving messages.

To migrate to these new APIs, you need to remove the previous Localytics PushReceiver and add the Play Services GcmReceiver, the Localytics GcmListenerService, and the Localytics InstanceIDListenerService to your AndroidManifest.xml. Replace YOUR-PACKAGE-NAME with your application's package wherever you see it.

  1. Remove the Localytics PushReceiver from the application element.

    <!-- Delete this receiver -->
    <receiver
      android:name="com.localytics.android.PushReceiver"
      android:permission="com.google.android.c2dm.permission.SEND" >
      <intent-filter>
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <category android:name="YOUR-PACKAGE-NAME" />
      </intent-filter>
    </receiver>
    
  2. Add the Google Play Services GcmReceiver, Localytics GcmListenerService, and Localytics InstanceIDListenerService within the application element. If you are already using GCM or another push provider, follow our custom push configuration instructions.

    <receiver
      android:name="com.google.android.gms.gcm.GcmReceiver"
      android:exported="true"
      android:permission="com.google.android.c2dm.permission.SEND" >
      <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <category android:name="YOUR-PACKAGE-NAME" />
      </intent-filter>
    </receiver>
    
    <service
      android:name="com.localytics.android.GcmListenerService"
      android:exported="false" >
      <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
      </intent-filter>
    </service>
    
    <service
      android:name="com.localytics.android.InstanceIDListenerService"
      android:exported="false" >
      <intent-filter>
        <action android:name="com.google.android.gms.iid.InstanceID" />
      </intent-filter>
    </service>
    

App inbox

v4 includes 2 Fragment classes for displaying inbox message detail views: InboxDetailFragment that extends android.app.Fragment and InboxDetailSupportFragment that extends android.support.v4.app.Fragment.

  1. Update your Fragment class to use InboxDetailSupportFragment instead of InboxDetailFragment as follows. For more information on which Fragment class to use, see displaying an inbox messsage detail view.

    Replace the following instance creation:

    InboxDetailFragment fragment = InboxDetailFragment.newInstance(campaign);
    

    with InboxDetailSupportFragment as follows.

    InboxDetailSupportFragment fragment = InboxDetailSupportFragment.newInstance(campaign);
    
  2. If you were using the onCreativeLoadError() callback method to handle message detail errors, the interface has changed from InboxDetailFragment.Callback to InboxDetailCallback.

    Update the following

    public class MyInboxActivity extends Activity implements InboxDetailFragment.Callback
    

    to

    public class MyInboxActivity extends Activity implements InboxDetailCallback
    

Options

If you were previously changing the default session timeout interval, use the new setOptions method.

Update the following

Localytics.setSessionTimeoutInterval(30);

to

HashMap<String, Object> options = new HashMap<>;
options.put("session_timeout", 30);
Localytics.setOptions(options);

Updating from v3.0 to v3.1+

Use this guide if you have integrated the Localytics library 3.0 into your app and are ready to upgrade to 3.1.

To ensure proper initialization and push message tracking, we made several important changes in SDK 3.1. These changes require a few adjustments to how the SDK is integrated into your app. If you are migrating from 3.0, please follow the steps below to make the appropriate adjustments.

  1. If your integration is automatic, make the following changes.
    1. In your main Activity, remove the call to registerActivityLifecycleCallbacks() in the Activity's onCreate() method. This method call should be removed in all other Activity classes as well.
      public void onCreate(Bundle savedInstanceState)
      {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
      
        // Remove the next 2 lines
        getApplication().registerActivityLifecycleCallbacks(
            new LocalyticsActivityLifecycleCallbacks(this));
      }
      
    2. If you don't have a custom Application class, create one, and specify the name in your AndroidManifest.xml.
      <application
        android:name=".MyApplication"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name">
      
    3. In your Application's onCreate() method, register LocalyticsActivityLifecycleCallbacks.
      public class MyApplication extends Application
      {
        @Override public void onCreate()
        {
          super.onCreate();
      
          // Register LocalyticsActivityLifecycleCallbacks
          registerActivityLifecycleCallbacks(
              new LocalyticsActivityLifecycleCallbacks(this));
        }
      }
      
  2. If your integration is manual, make the following changes.
    1. In your main Activity, remove the call to Localytics.integrate() in the Activity's onCreate() method. This method call should be removed in all other Activity classes as well.
      public void onCreate(Bundle savedInstanceState)
      {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
      
        // Remove this line
        Localytics.integrate(this);
      }
      
    2. If you don't have a custom Application class, create one, and specify the name in your AndroidManifest.xml.
      <application
        android:name=".MyApplication"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name">
      
    3. In your Application's onCreate() method, integrate Localytics.
      public class MyApplication extends Application
      {
        @Override public void onCreate()
        {
          super.onCreate();
      
          // Integrate Localytics
          Localytics.integrate(this);
        }
      }
      
  3. Add Google Play Services in your project for GCM support. More information on Play Services can be found here.
  4. Add the PushTrackingActivity to your AndroidManifest.xml. This Activity is used to track open rates for push notifications.
    <activity android:name="com.localytics.android.PushTrackingActivity" />
    

Updating from v2 to v3

v3 of the Localytics Android SDK represents a substantial architectural change as well as a simplification of the API. To ensure proper initialization and push message tracking, we now require the use of a custom Application class for integration. Please follow the steps below to make the appropriate adjustments.

Most notably, the API forgoes the singleton pattern utilized in v2 and instead makes use of class-level methods. In addition, all methods which were previously part of LocalyticsSession and LocalyticsAmpSession are now available combined under Localytics.

v3 of the Localytics Android SDK also lays the foundation for upcoming features. Architecture updates allow us to quickly introduce new features without requiring backwards-incompatible changes. The v3 SDK supports

  • background event tagging, enabling you to track events that occur outside a session
  • new Profile APIs, enabling you to increment, decrement, add to set, and remove from set

Changes to the method signatures make the iOS and Android Localytics SDKs almost identical. We've aligned the method signatures on iOS and Android, making it easier to keep your integrations up to date. The new method signatures also simplify development in new languages and alternative platforms.

Profiles data now defaults to an app-level scope. Previously all Profiles data defaulted to an org-level scope.

  1. Add Google Play Services in your project for GCM support. More information on Google Play Services can be found here.
  2. Add the PushTrackingActivity to your AndroidManifest.xml. This Activity is used to track open rates for push notifications.
    <activity android:name="com.localytics.android.PushTrackingActivity" />
    
  3. In your main Activity, remove the creation of the LocalyticsSession or LocalyticsAmpSession member object and remove the call to registerActivityLifecycleCallbacks() in the Activity's onCreate() method. These should be removed in all other Activity classes as well.

    public void onCreate(Bundle savedInstanceState)
    {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
    
      // Remove the following line
      this.localyticsSession = new LocalyticsAmpSession(this.getApplicationContext());
    
      // Remove the next 2 lines
      getApplication().registerActivityLifecycleCallbacks(
          new LocalyticsActivityLifecycleCallbacks(this.localyticsSession));
    
      // Activity Creation Code
    
    }
    
  4. If you don't have a custom Application class, create one, and specify the name in your AndroidManifest.xml.
    <application
      android:name=".MyApplication"
      android:icon="@drawable/ic_launcher"
      android:label="@string/app_name">
    
  5. In your Application's onCreate() method, register ALocalyticsActivityLifecycleCallbacks.
    public class MyApplication extends Application
    {
      @Override public void onCreate()
      {
        super.onCreate();
    
        // Integrate Localytics
        registerActivityLifecycleCallbacks(
            new LocalyticsActivityLifecycleCallbacks(this));
      }
    }
    

Legacy SDKs

This section contains a reference for some older Localytics SDK version integrations, APIs, and code snippets.

v3 SDK

Getting started

1. Install the SDK

The fastest and easiest way to use Localytics in your project is with Maven. If you cannot use Maven, use the alternative installation approach.

  1. Update your project’s build.gradle script to include the Localytics Maven repository.

    apply plugin: 'com.android.application'
    
    repositories
    {
      jcenter()
      maven
      {
        url 'http://maven.localytics.com/public'
      }
    }
    
  2. Add dependencies for Localytics, Android Support v4, and Google Play Services.

    dependencies
    {
      compile 'com.android.support:support-v4:23.1.0'
      compile 'com.google.android.gms:play-services-gcm:8.4.0'
      compile 'com.google.android.gms:play-services-ads:8.4.0'
      compile 'com.localytics.android:library:3.8+'
    }
    
2. Modify AndroidManifest.xml

Add the following to your AndroidManifest.xml.

  1. Your Localytics app key within the application tag
    <meta-data
      android:name="LOCALYTICS_APP_KEY"
      android:value="YOUR-LOCALYTICS-APP-KEY" />
    
  2. Localytics ReferralReceiver within the application tag
    <receiver
      android:name="com.localytics.android.ReferralReceiver"
      android:exported="true">
      <intent-filter>
        <action android:name="com.android.vending.INSTALL_REFERRER" />
      </intent-filter>
    </receiver>
    
  3. Test mode intent-filter within your MainActivity activity tag under the existing intent-filter (i.e. the one for android.intent.action.MAIN). Replace YOUR-LOCALYTICS-APP-KEY with your Localytics app key.
    <intent-filter>
      <data android:scheme="ampYOUR-LOCALYTICS-APP-KEY" />
      <action android:name="android.intent.action.VIEW" />
      <category android:name="android.intent.category.DEFAULT" />
      <category android:name="android.intent.category.BROWSABLE" />
    </intent-filter>
    
  4. Permissions above the application tag
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    
3. Modify your MainActivity

Override onNewIntent in your MainActivity as follows.

@Override
protected void onNewIntent(Intent intent)
{
  super.onNewIntent(intent);

  setIntent(intent);
}
4. Subclass FragmentActivity

Subclass FragmentActivity in every Activity as follows.

public class Activity extends FragmentActivity
5. Initialize the SDK
  1. If you don't have a custom Application class, create one and specify the name in your AndroidManifest.xml as follows.

    <application
      android:name=".MyApplication">
    
  2. Make the following modifications to your Application class.

    1. Import the Localytics package.

      import com.localytics.android.*;
      
    2. Register LocalyticsActivityLifecycleCallbacks. If you support Android API levels less than 14 (Ice Cream Sandwich), then use alternative session management.

      @Override
      public void onCreate()
      {
        super.onCreate();
      
        registerActivityLifecycleCallbacks(
                new LocalyticsActivityLifecycleCallbacks(this));
      }
      
6. Next steps

Congratulations! You have successfully performed the basic Localytics integration and are now sending session data to Localytics. You can also use Localytics In-App Messaging to message users in your app, and you have everything you need to track where your most highly engaged users are coming from.

Note that it may take a few minutes for your first datapoints to show up within the Localytics Dashboard. In the meantime, we suggest reading the next few sections to learn how to:

  1. Track one user action as an event
  2. Track one user property as a profile attribute
  3. Integrate push messaging

We recommend doing these things before releasing your app for the first time with Localytics.

In-app messaging

Dependencies

To use Localytics In-App Messaging within your Android app, you need to extend android.support.v4.app.FragmentActivity in every Activity in which you plan on showing in-app messages. This requirement is necessary because the Localytics SDK uses the android.support.v4.app.DialogFragment class to display campaigns within your app. Using a DialogFragment is the best way to present UI on top of your app; we don't want to try to inject a View directly into your the layout of your Activity. If you're not already using FragmentActivity or any of it's subclasses in your app (i.e. ActionBarActivity or AppCompatActivity), all you need to do to enable in-app messaging is to follow our Getting Started which includes instructions to extend FragmentActivity instead of Activity in all of your app's Activities.

Push messaging

Before continuing, please be sure that you have completed all of the steps in Getting Started. If you are already using GCM for push in your app, follow our custom push configuration instructions.

1. Create a Google API project and enable GCM
  1. Visit the Google services page.
  2. Create or choose your app and package name and click Choose and configure services.
  3. Click the Cloud Messaging button.
  4. Note your Server API Key and Sender ID.
2. Modify AndroidManifest.xml

Add the following to your AndroidManifest.xml. Replace YOUR-PACKAGE-NAME with your application's package wherever you see it.

  1. Push permissions above the application element.

    <uses-permission
        android:name="com.google.android.c2dm.permission.RECEIVE" />
    <permission
        android:name="YOUR-PACKAGE-NAME.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
    <uses-permission
        android:name="YOUR-PACKAGE-NAME.permission.C2D_MESSAGE" />
    
  2. The Localytics PushReceiver within the application element. If you are already using GCM or another push provider, follow our custom push configuration instructions.

    <receiver
        android:name="com.localytics.android.PushReceiver"
        android:permission="com.google.android.c2dm.permission.SEND" >
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <category android:name="YOUR-PACKAGE-NAME" />
        </intent-filter>
    </receiver>
    
  3. The Localytics PushTrackingActivity within the application element.

    <activity android:name="com.localytics.android.PushTrackingActivity"/>
    
3. Register for push notifications in your app

Register for push using your GCM Sender ID within onCreate() of your app's MainActivity.

Localytics.registerPush("YOUR-SENDER-ID");
4. Add your Server API Key to the Localytics Dashboard
  1. Log in to the Localytics Dashboard, navigate to Settings > Apps, and input your server API key within Add Certs as shown in the steps in the image below.

    screenshot of server API key in Localytics Dashboard
5. Send yourself a test push message

After completing push integration you can create a test campaign to send yourself a push message to be sure that everything is properly configured.

  1. Login to the Localytics Dashboard, navigate to Messaging, select your app key from the dropdown at the top of the screen, and click the + button at the top right of the screen.
  2. Name your campaign, e.g. "Push Test", and click Continue to Creatives.
  3. Under Message Body, enter a sample push alert message, e.g. "Push test example message".
  4. Select One Time and Immediately then click Continue and Confirm.
  5. Click Test on Device.
  6. Click Enable Test Mode.
  7. Use one of the options mentioned on the screen to open the test mode URL on your connected device. If the URL doesn't cause your app to open at this point, please ensure that you have completed all steps of Getting Started, especially the test mode step.
  8. Your test push message will appear on your connected device. If the push does not immediately appear, check that your server API key is correct, then wait a few minutes and retry.
6. Next steps

After you have setup push notifications, configure your notification options. We recommend that you set at least set the accent color and icon for supporting Lollipop (API 21) and above.

App Inbox

Display an inbox message detail view

You must use InboxDetailFragment to display an inbox message detail view. Create a new InboxDetailFragment using an InboxCampaign object as follows.

InboxCampaign campaign = /* campaign from InboxListAdapter or Localytics.getInboxCampaigns */;
InboxDetailFragment fragment = InboxDetailFragment.newInstance(campaign);

If you are using InboxListAdapter, you should add an AdapterView.OnItemClickListener to your ListView that handles marking the campaign as read, refreshing the adapter, and showing the detail view in a separate Activity or Fragment as follows. The InboxCampaign class implements the Parcelable interface so you can add it to any Intent.

public class MyInboxActivity extends FragmentActivity implements AdapterView.OnItemClickListener
{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_inbox);

        ListView listView = (ListView) findViewById(R.id.lv_inbox);
        listView.setOnItemClickListener(this);
        InboxListAdapter inboxListAdapter = new InboxListAdapter(this);
        listView.setAdapter(inboxListAdapter);
        inboxListAdapter.getData(null);
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id)
    {
        InboxListAdapter inboxListAdapter = (InboxListAdapter) parent.getAdapter();
        InboxCampaign campaign = inboxListAdapter.getItem(position);
        campaign.setRead(true);

        inboxListAdapter.notifyDataSetChanged();

        if (campaign.hasCreative())
        {
            Intent intent = new Intent(this, MyInboxDetailActivity.class);
            intent.putExtra("campaign", campaign);
            startActivity(intent);
        }
    }
}
public class MyInboxDetailActivity extends FragmentActivity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_inbox_detail);

        if (savedInstanceState == null) {
            InboxCampaign campaign = getIntent().getParcelableExtra("campaign");
            InboxDetailFragment fragment = InboxDetailFragment.newInstance(campaign);
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.container, fragment)
                    .commit();
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
Handling detail message errors

In rare cases the inbox message detail view may fail to load. By default, InboxDetailFragment will display a gray "X" in the center of the view when this occurs. To provide your own custom error view, implement the InboxDetailFragment.Callback interface in the attached Activity as follows.

public class MyInboxActivity extends FragmentActivity implements InboxDetailFragment.Callback
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_inbox_campaign);

        if (savedInstanceState == null)
        {
            InboxCampaign campaign = getIntent().getParcelableExtra("campaign");
            InboxDetailFragment fragment = InboxDetailFragment.newInstance(campaign);
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.container, fragment)
                    .commit();
        }
    }

    @Override
    public void onCreativeLoadError()
    {
        findViewById(R.id.error_view).setVisibility(View.VISIBLE);
    }
}

Alternative Session Management

The standard Localytics session management approach relies on the Localytics SDK to automatically listen for indications of state change (e.g. foreground / background transitions) in your app and then to track the state change accordingly. Most apps are able to use the standard approach. If you are unable to use the standard session management approach, the instructions below will help you to initialize the SDK to achieve the same result as the standard approach.

  1. If you don't have a custom Application class, create one and specify the name in your AndroidManifest.xml as follows

    <application
      android:name=".MyApplication">
    
  2. In your Application class

    1. Import the Localytics package

      import com.localytics.android.*;
      
    2. Initialize Localytics.

      @Override
      public void onCreate()
      {
        super.onCreate();
      
        Localytics.integrate(this);
      }
      
  3. In every Activity in your app, override onResume() and onPause() to manage the session and uploading. The simplest approach for accomplishing this task is to include this code in a common base Activity that each Activity extends.

    1. In onResume, open a session, start an upload, and register the activity for messaging as follows.

      @Override
      protected void onResume()
      {
        super.onResume();
      
        Localytics.openSession();
        Localytics.upload();
      
        if (activity instanceof FragmentActivity)
        {
          Localytics.setInAppMessageDisplayActivity((FragmentActivity) activity);
        }
      
        Localytics.handleTestMode(activity.getIntent());
      }
      
    2. In onPause, close the session, upload data, and unregister the activity from messaging as follows.

      @Override
      protected void onPause()
      {
        super.onPause();
      
        if (activity instanceof FragmentActivity)
        {
          Localytics.dismissCurrentInAppMessage();
          Localytics.clearInAppMessageDisplayActivity();
        }
      
        Localytics.closeSession();
        Localytics.upload();
      }
      
    3. Set the new Intent in onNewIntent() as follows.

      @Override
      protected void onNewIntent(Intent intent) {
          super.onNewIntent(intent);
      
          setIntent(intent);
      }
      
  4. You are now ready to continue with the next steps.

Callbacks

Analytics callbacks

These analytics callbacks are useful for setting the value of a custom dimension or profile attribute before a session opens, firing an event at the beginning or end of a session, or taking action in your app based on an auto-tagged campaign performance event in the Localytics SDK. All Localytics analytics callbacks are called on an internal background thread.

In your Application class implement the AnalyticsListener interface as follows.

public class MyApplication extends Application implements AnalyticsListener
{

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

    registerActivityLifecycleCallbacks(
            new LocalyticsActivityLifecycleCallbacks(this));
    Localytics.addAnalyticsListener(this);
  }

  @Override
  public void localyticsSessionWillOpen(boolean isFirst, boolean isUpgrade, boolean isResume)
  {
    // ... do something ...
  }

  @Override
  public void localyticsSessionDidOpen(boolean isFirst, boolean isUpgrade, boolean isResume)
  {
    // ... do something ...
  }

  @Override
  public void localyticsSessionWillClose()
  {
    // ... do something ...
  }

  @Override
  public void localyticsDidTagEvent(String eventName, Map<String, String> attributes, long customerValueIncrease)
  {
    // ... do something ...
  }
}

Alternatively, you can use the AnalyticsListenerAdapter class to listen for specific callbacks.

public class MyApplication extends Application
{

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

    registerActivityLifecycleCallbacks(
          new LocalyticsActivityLifecycleCallbacks(this));
    Localytics.addAnalyticsListener(new AnalyticsListenerAdapter()
    {

      @Override
      public void localyticsSessionWillOpen(boolean isFirst, boolean isUpgrade, boolean isResume)
      {
        // ... do something ...
      }

    });
  }
}
Messaging callbacks

Messaging callbacks are useful for understanding when Localytics in-app messages are about to appear to prevent conflicts with other views in your app. All Localytics messaging callbacks are called on the main thread.

In your Application class implement the MessagingListener interface as follows.

public class MyApplication extends Application implements MessagingListener
{

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

    registerActivityLifecycleCallbacks(
            new LocalyticsActivityLifecycleCallbacks(this));
    Localytics.addMessagingListener(this);
  }

  @Override
  public void localyticsWillDisplayInAppMessage()
  {
    // ... do something ...
  }

  @Override
  public void localyticsDidDisplayInAppMessage()
  {
    // ... do something ...
  }

  @Override
  public void localyticsWillDismissInAppMessage()
  {
    // ... do something ...
  }

  @Override
  public void localyticsDidDismissInAppMessage()
  {
    // ... do something ...
  }
}

Alternatively, you can use the MessagingListenerAdapter class to listen for specific callbacks.

public class MyApplication extends Application
{

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

    registerActivityLifecycleCallbacks(
            new LocalyticsActivityLifecycleCallbacks(this));
    Localytics.addMessagingListener(new MessagingListenerAdapter()
    {

      @Override
      public void localyticsWillDisplayInAppMessage()
      {
        // ... do something
      }

    });
  }
}

Push notification options

When using the Localytics PushReceiver you can configure several notification options, such as the title, LED color, sound, accent color, and icon. We recommend that you at least set the accent color and icon for supporting Lollipop (API 21) and above.

To configure these options build a PushNotificationOptions object and set it on the Localytics SDK just after initializing as follows. Note: These options will apply to all notifications.

public class MyApplication extends Application
{
    @Override
    public void onCreate()
    {
        super.onCreate();

        Localytics.autoIntegrate(this);
        Localytics.setPushNotificationOptions(new PushNotificationOptions.Builder()
                .setSmallIcon(getSmallIcon())
                .setAccentColor(getColor(R.color.accent_color))
                .setTitle(getString(R.string.notification_title))
                .setSound(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.notification_sound);)
                .build());
    }

    private int getSmallIcon()
    {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
        {
            return R.drawable.lollipop_icon;
        }
        else
        {
            return R.drawable.ic_launcher;
        }
    }
}

Custom push configuration

Whether you are using another push provider or sending pushes with your own system, getting Localytics push messaging working alongside another implementation may take a few additional steps. The sections below will help you setup the correct configuration for your app.

Sending a registration ID to Localytics

If you are already registering with GCM using the GoogleCloudMessaging or InstanceID APIs, you need to send that registration ID to Localytics.

  1. Send the registration ID to Localyics as follows.

    When using GoogleCloudMessaging:

    GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
    String registrationId = gcm.register(SENDER_ID);
    Localytics.setPushRegistrationId(registrationId);
    

    When using InstanceID:

    InstanceID instanceID = InstanceID.getInstance(context);
    String token = instanceID.getToken(SENDER_ID, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
    Localytics.setPushRegistrationId(token);
    
  2. After you have successfully set the push registration ID, follow the steps for using multiple push broadcast receivers. By following the multiple push receivers steps, notification display and push received and push opened tracking will automatically be handled. If you would prefer to build and display notifications yourself and manually track push received and push opened, follow the steps for building notifications and tagging push events.

Using multiple push broadcast receivers

Problems may arise when declaring multiple push BroadcastReceivers because other receivers may not properly ignore pushes sent by Localytics. This can cause issues with how notifications are displayed (duplicates) and/or how push opens are tracked. For these reasons we recommend that you declare only one BroadcastReceiver in your AndroidManifest.xml for the com.google.android.c2dm.intent.RECEIVE IntentFilter. This receiver should then send the Intent to the appropriate BroadcastReceiver in code.

  1. Declare PushTrackingActivity and your custom BroadcastReceiver in your AndroidManifest.xml as follows.

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.localytics.android.example" >
    
        <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
        <permission
            android:name="com.localytics.android.example.permission.C2D_MESSAGE"
            android:protectionLevel="signature" />
    
        <uses-permission android:name="com.localytics.android.example.permission.C2D_MESSAGE" />
    
        <application>
    
            <activity android:name="com.localytics.android.PushTrackingActivity"/>
    
            <receiver
                android:name=".MyPushReceiver"
                android:permission="com.google.android.c2dm.permission.SEND" >
                <intent-filter>
                    <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
                    <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                    <category android:name="com.localytics.android.example" />
                </intent-filter>
            </receiver>
    
        </application>
    </manifest>
    
  2. Implement your custom BroadcastReceiver to send the Intent to the Localytics receiver and other appropriate BroadcastReceivers as follows.

    public class MyPushReceiver extends BroadcastReceiver
    {
        /**
         * All pushes received from Localytics will contain an 'll' string extra which can be parsed into
         * a JSON object. This JSON object contains performance tracking information, such as a campaign
         * ID. Any push received containing this 'll' string extra, should be passed on to the Localytics
         * PushReceiver. Any other push can be handled as you see fit.
         */
        @Override
        public void onReceive(Context context, Intent intent)
        {
            if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION"))
            {
                String registrationId = intent.getStringExtra("registration_id");
                if (!TextUtils.isEmpty(registrationId))
                {
                    Localytics.setPushRegistrationId(registrationId);
                }
            }
            else if (intent.getExtras().containsKey("ll"))
            {
                new PushReceiver().onReceive(context, intent);
            }
            else if (/* check for other provider */)
            {
                new OtherPushReceiver().onReceive(context, intent);
            }
            else
            {
                String message = intent.getStringExtra("message");
                if (!TextUtils.isEmpty(message))
                {
                    Intent mainIntent = new Intent(context, MainActivity.class);
                    PendingIntent launchIntent = PendingIntent.getActivity(context, 1, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
                    NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
                            .setSmallIcon(R.mipmap.ic_launcher)
                            .setContentTitle(context.getString(R.string.app_name))
                            .setContentText(message)
                            .setStyle(new NotificationCompat.BigTextStyle().bigText(message))
                            .setContentIntent(launchIntent)
                            .setDefaults(Notification.DEFAULT_ALL)
                            .setAutoCancel(true);
    
                    NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
                    notificationManager.notify(0, builder.build());
                }
            }
        }
    }
    
  3. You are now ready to continue with adding your server API key to the Localytics Dashboard.

Building notifications and tagging push events

If you would prefer to not use the Localytics PushReceiver, you will need to handle building notifications and tagging the push received and push opened events.

  1. Declare PushTrackingActivity and your BroadcastReceiver in your AndroidManifest.xml as follows.

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.localytics.android.example" >
    
        <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
        <permission
            android:name="com.localytics.android.example.permission.C2D_MESSAGE"
            android:protectionLevel="signature" />
    
        <uses-permission android:name="com.localytics.android.example.permission.C2D_MESSAGE" />
    
        <application>
    
            <activity android:name="com.localytics.android.PushTrackingActivity"/>
    
            <receiver
                android:name=".MyPushReceiver"
                android:permission="com.google.android.c2dm.permission.SEND" >
                <intent-filter>
                    <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
                    <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                    <category android:name="com.localytics.android.example" />
                </intent-filter>
            </receiver>
    
        </application>
    </manifest>
    
  2. When the Intent is delivered to your BroadcastReceiver, tag the push received event and build and show the notification using PushTrackingActivity as the PendingIntent.

    public class MyPushReceiver extends BroadcastReceiver
    {
        @Override
        public void onReceive(Context context, Intent intent)
        {
            Localytics.handlePushNotificationReceived(intent); // tag push received event
    
            String message = intent.getStringExtra("message");
            if (!TextUtils.isEmpty(message))
            {
                Intent trackingIntent = new Intent(context, PushTrackingActivity.class);
                trackingIntent.putExtras(intent); // add all extras from received intent
    
                int requestCode = getRequestCode(intent.getExtras());
                PendingIntent contentIntent = PendingIntent.getActivity(context, requestCode, trackingIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
                NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
                       .setSmallIcon(R.mipmap.ic_launcher)
                       .setContentTitle(context.getString(R.string.app_name))
                       .setContentText(message)
                       .setStyle(new NotificationCompat.BigTextStyle().bigText(message))
                       .setContentIntent(contentIntent)
                       .setDefaults(Notification.DEFAULT_ALL)
                       .setAutoCancel(true);
    
                NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
                notificationManager.notify(requestCode, builder.build());
            }
        }
    
        /**
         * Get a unique requestCode so we don't override other unopened pushes. The Localytics SDK
         * uses the campaign ID (ca) within the 'll' JSON string extra. Use that value if it exists.
         */
        private int getRequestCode(Bundle extras)
        {
            int requestCode = 1;
            if (extras != null && extras.containsKey("ll"))
            {
                try
                {
                    JSONObject llObject = new JSONObject(extras.getString("ll"));
                    requestCode = llObject.getInt("ca");
                }
                catch (JSONException e)
                {
                }
            }
            return requestCode;
        }
    }
    

If you cannot use PushTrackingActivity in the PendingIntent, then you must handle tagging the push opened event when your Activity resumes.

  1. When building the notification include the ll key as an extra in the PendingIntent Intent by adding all the extras from the received Intent as follows.

    public class MyPushReceiver extends BroadcastReceiver
    {
        @Override
        public void onReceive(Context context, Intent intent)
        {
            Localytics.handlePushNotificationReceived(intent); // tag push received event
    
            String message = intent.getStringExtra("message");
            if (!TextUtils.isEmpty(message))
            {
                Intent launchIntent = new Intent(context, MainActivity.class);
                launchIntent.putExtras(intent); // add all extras from received intent
    
                int requestCode = getRequestCode(intent.getExtras());
                PendingIntent contentIntent = PendingIntent.getActivity(context, requestCode, launchIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
                NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
                       .setSmallIcon(R.mipmap.ic_launcher)
                       .setContentTitle(context.getString(R.string.app_name))
                       .setContentText(message)
                       .setStyle(new NotificationCompat.BigTextStyle().bigText(message))
                       .setContentIntent(contentIntent)
                       .setDefaults(Notification.DEFAULT_ALL)
                       .setAutoCancel(true);
    
                NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
                notificationManager.notify(requestCode, builder.build());
            }
        }
    
        /**
         * Get a unique requestCode so we don't override other unopened pushes. The Localytics SDK
         * uses the campaign ID (ca) within the 'll' JSON string extra. Use that value if it exists.
         */
        private int getRequestCode(Bundle extras)
        {
            int requestCode = 1;
            if (extras != null && extras.containsKey("ll"))
            {
                try
                {
                    JSONObject llObject = new JSONObject(extras.getString("ll"));
                    requestCode = llObject.getInt("ca");
                }
                catch (JSONException e)
                {
                }
            }
            return requestCode;
        }
    }
    
  2. When your Activity resumes or receives the new Intent, handle the push opened as follows. We recommend using a base Activity that all Activities in our app extend so any Activity can be used in the PendingIntent.

    public class BaseActivity extends FragmentActivity
    {
        @Override
        protected void onResume()
        {
            super.onResume();
    
            Localytics.handlePushNotificationOpened(getIntent());
        }
    
        @Override
        protected void onNewIntent(Intent intent)
        {
            super.onNewIntent(intent);
    
            Localytics.handlePushNotificationOpened(intent);
        }
    }
    
  3. You are now ready to continue with adding your server API key to the Localytics Dashboard.