Android SDK Integration Guide

 

New: Video Guide

Watch this video for a detailed view of the integration process. We recommend using both the video and the written guide below.

Before You Begin: SDK Prerequisites

Follow the steps in Integrating a Singular SDK: Planning and Prerequisites.

These steps are prerequisites for any Singular SDK integration.

1. Adding the SDK to Your Project

1.1 Adding the SDK Using Gradle

Note: Starting with Gradle 7, Android suggests using centralized repository declarations in settings.gradle over project or module level build.gradle declarations.

  1. Add the Singular SDK repository to the settings.gradle file:

    dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
    google()
    mavenCentral()
    maven{ url 'https://maven.singular.net/' }
    }
    }

    OR in older Gradle versions, Add the Singular SDK repository to the project/build.gradle file:

    repositories {
            mavenCentral()
    maven { url 'https://maven.singular.net/' } }
  2. Add the Singular library to the dependencies list in app/build.gradle:

    dependencies {
    ... implementation 'com.google.android.gms:play-services:6.5.87' implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.singular.sdk:singular_sdk:12.5.4' ... }

    Additionally add the following to support Samsung Galaxy Store's install referrer if your app is distributed through the Samsung Galaxy Store:

    dependencies {
          ...
          implementation 'store.galaxy.samsung.installreferrer:samsung_galaxystore_install_referrer:4.0.0'
          ...
          }
  3. If you have disabled transitive dependencies for the Singular SDK, add the following to your app/build.gradle:

    dependencies {
    ... implementation 'com.android.installreferrer:installreferrer:2.2' implementation 'com.google.android.gms:play-services-appset:16.0.0' ... }
     
  4. If your app doesn't implement Google Play Services API 17.0.0 or higher, add the following dependency to the app/build.gradle file:

    dependencies {
    ...
    implementation 'com.google.android.gms:play-services-ads-identifier:17.0.0+'
    ...
    }

Note: Gradle 1.x-2.x users should use "compile" instead of "implementation" to add dependencies.

1.2. Adding the SDK Without Gradle

Downloading the SDK Manually
  1. Download the SDK from the link at the top of the page.
  2. Unzip the SDK package and add Singular.aar into your Android project's libs directory. If it doesn't exist, create a directory called libs in your project folder (usually at <project>/app/libs).
Adding the SDK Using Maven

Add our maven repository to your project's pom.xml:

<project ...>
<repositories>
    <repository>
      <id>singular.net</id>
      <url>http://maven.singular.net/</url>
    </repository>
 </repositories>
</project>

Add the dependency:

<dependency>
    <groupId>com.singular.sdk</groupId>
    <artifactId>singular_sdk</artifactId>
    <version>12.0.2</version>
</dependency>
Adding the SDK Using Eclipse

You can use the Eclipse AAR plugin: gradle-eclipse-aar-plugin

If you don't want to use the plugin, follow these steps:

  1. Unzip singular_sdk-12.5.4.aar.
  2. Rename classes.jar to singular_sdk-12.5.4.jar (this is the main SDK jar).
  3. Add the 'com.android.installreferrer:installreferrer:2.2' library to your project in any way you prefer.
  4. Copy the BIND_GET_INSTALL_REFERRER_SERVICE permission from the AndroidManifest.xml contained in the AAR to your AndroidManifest.xml
  5. copy the CHECK_LICENSE permissions from the AndroidManifest.xml contained in the AAR to your AndroidManifest.xml
Adding the SDK Using Proguard

Add the following lines of code to your proguard.config file:

-keep class com.singular.sdk.** { *; }
-keep public class com.android.installreferrer.** { *; }
# Uncomment this line in case your are calling the 'revenue' function using the Google billing library
#-keep public class com.android.billingclient.** { *; }

1.3. Adding Required Permissions

Add these permissions under the <manifest> tag in your AndroidManifest.xml file:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="BIND_GET_INSTALL_REFERRER_SERVICE" />
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />

Note: If your app build is targeting Android 12/API level 31 or higher, add permissions to access the Google Advertising ID:

<uses-permission android:name="com.google.android.gms.permission.AD_ID" />
Do not add this permission if you're integrating the Kids SDK.

Additionally add the following to support Samsung Galaxy Store's install referrer if your app is distributed through the Samsung Galaxy Store and targets Android 11 or higher:

<queries>
<package android:name="com.sec.android.app.samsungapps" />
</queries>
Caution: If your app has the android.permission.GET_TASKS permission, the app may be initialized before the user actually opens it. This may initialize the Singular SDK and cause discrepancies in install time. To prevent the problem, remove the permission if it's not needed, or move the Singular SDK initialization call somewhere else in the code, ensuring it is called only after the user opens the app for the first time.

2. Setting Up a Basic SDK Integration

Note: Remember to remain compliant with the various privacy laws enacted in regions where you are doing business, including GDPR, CCPA, and COPPA, when implementing the Singular SDKs. For more information, see SDK Opt-In and Opt-Out Practices.

2.1. Importing the Singular Library

To import the Singular library, add the following import to your MainActivity file:

Java (MainActivity.java) Kotlin (MainActivity.kt)
import com.singular.sdk.*;

2.2. Constructing a Configuration Object

Before initializing the SDK, you have to create a SingularConfig object. This object will contain the following:

  1. Your SDK Key and SDK Secret (to retrieve them, log into your Singular account and go to DEVELOPER TOOLS > SDK Integration > SDK Keys).
  2. Optionally, any SDK preferences you may want to set.
  3. META Install Referrer Attribution Support

    Required SDK configuration to enable "Meta Install Referrer" attribution:

    1. Provide your Facebook App Id in the Singular Configuration Object.
      // To enable META Install Referrer
      config.withFacebookAppId("INSERT YOUR FACEBOOK APP ID HERE");
    Where can I find an app's Facebook App ID?

The following code example creates a configuration object and sets the common configuration options before initializing the Singular SDK.

The following sections give more details about these options and how you can customize them.

Java (MainActivity.java) Kotlin (MainActivity.kt)
@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
// Create a configuration object SingularConfig config = new SingularConfig("SDK KEY", "SDK SECRET");

// Set up a deep links handler config.withSingularLink( getIntent(), new SingularLinkHandler() { @Override public void onResolved(SingularLinkParams params) { String deeplink = params.getDeeplink(); String passthrough = params.getPassthrough(); boolean isDeferred = params.isDeferred(); // Add deep link handling code here } } );

Singular.init(context, config); ... }

 

SingularConfig Method Reference: See all available ".with" options

The table below lists all the available ".with" methods for the SingularConfig object to add options and features to your app.

You will find details about each feature in the sections below or under Advanced Options.

Method

Description

.withFacebookAppId(String facebookAppID)

Configure the Facebook App ID. Required for "Meta Install Referrer" attribution.

See "Where can I find an app's Facebook App ID?"

.withCustomUserId(String customId)

Send the user ID to Singular.

.withSingularLink(getIntent(), SingularLinkHandler handler)

Enable deep linking with Singular Links.

.withDDLTimeoutInSec (long timeout)

Set the length of time Singular searches for a deferred deep link when the app is first opened.

.withDDLHandler (DeferredDeepLinkHandler handler)

Enable deep linking with legacy tracking links (instead of the newer Singular Links).

.withOpenURI (URI openURI)

Fetch the URI from the intent (to process deep links if the app is opened through a link that doesn't originate from Singular).

.withGlobalProperty(String key, String value, boolean overrideExisting)

Set a global property to a given value. The key and value will be sent to Singular with any event/session sent from the app.

.withSessionTimeoutInSec (long timeout)

Set the session timeout.

.withFCMDeviceToken(String token)

Sets the FCM token to be sent on the first session.

.withLoggingEnabled ()

Enable logging.

.withLogLevel (int level)

Configure the logging level (default is Log.ERROR).

2.3. Adding Deep Linking Support

Deep links are links that lead to specific content inside an app. When a user clicks a deep link on a device with the app installed, the app opens and shows a specific product or experience. Singular tracking links can include deep linking as well as deferred deep linking (see our Deep Linking FAQ and the Singular Links FAQ for more information).

Enabling Deep Links Using Singular Links

To enable deep links for your app, see Singular Links Prerequisites.

Adding a Deep Linking Intent Filter

Add an intent filter like the following to your AndroidManifest.xml to enable deep link support in an activity. If you have more than one activity that should be opened through a deep link, do this for each activity.

Note: The Intent should NOT be configured with a 'android:host' value. If a 'android:host' value must be used: be sure to include the host value in the Singular Apps page configuration for the App scheme, and use the same 'scheme://host' value for all Singular deeplinks.

<activity> 
    <intent-filter>
        <data android:scheme="singular-example" />
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
    </intent-filter>
</activity>

Handling Deep Links

The Singular SDK provides a handler mechanism to read the details of the tracking link that led to the app being opened. A sample handler is included above in the sample code for Constructing a Configuration Object.

Note: The SingularLinkHandler is called only if the app was opened through a Singular Link (see the Singular Links FAQ). Other types of app links will only trigger if the domain is included in supportedDomains when you create the Singular Config object. See "Handling Non-Singular Deep Links" for more information

Modifying the Deferred Deep Link Timeout

By default, when an app sends the first session to Singular from a certain device, the Singular server looks in its records to see if there is a deferred deep link that matches that device (see What are deferred deep links?). If a deferred deep link is found, it is sent back to the app to process. But if no deferred deep link is found within 60 seconds, the server stops searching.

You can modify the timeout value by calling withDDLTimeoutInSec when you create the SingularConfig object. The example below changes the timeout to 30 seconds:

SingularConfig config = new SingularConfig("SDK KEY", "SDK SECRET");
...
config.withDDLTimeoutInSec(30);
...
Singular.init(context, config);

Handling Non-Singular Deep Links

The Singular SDK supports non-Singular-served deep links. This is required to measure attribution for partners such as Google Ads.

Starting with Android SDK version 12.1.1, non-Singular Universal Links are supported by default. No action is needed on your part to support third-party links if you have the new version of the SDK.

2.4. Initializing Singular

The Singular SDK should be initialized every time your app is opened. This is a prerequisite to all Singular attribution functionality, and it also sends a new user session to Singular (sessions are used to calculate user retention).

To initialize Singular, use the following code:

Singular.init(context, config);
Parameter Description
Context context From the application class, you can pass this or getApplicationContext() as the context. To get the application context from inside an activity, call currentActivity.getApplicationContext().
SingularConfig config The SingularConfig object you created in previous steps.

The init method can be called at every point in the app but must get called before any event is reported. We recommend calling init in the onCreate of the main activity as well as any activity that will be opened directly by a deep link.

Note: You have to initialize Singular inside any activity that will be opened by a deep link (see Implementing deep links). Therefore, we don't recommend initializing Singular in the application's onCreate. If Singular is initialized on the application level and then again inside an activity, it will cause duplicate sessions in the Singular database.

2.5. Sending the User ID to Singular (Optional)

You may send your internal User ID to Singular using a Singular SDK method.

Note: If you use Singular's Cross-Device solution, you must collect the User ID across all platforms.

  • The User ID can be any identifier and should not expose PII (Personally Identifiable Information). For example, you should not use a User's email address, username, or phone number. Singular recommends using a hashed value unique only to your first-party data.
  • The User ID value passed to Singular should also be the same internal User ID you capture across all platforms (Web/Mobile/PC/Console/Offline).
  • Singular will include the User ID in user-level exports, ETL, and Internal BI postbacks (if configured). The User ID is first-party data, and Singular does not share it with other parties.
  • The User ID value, when set with the Singular SDK Method, will persist until it is unset using the unsetCustomUserId method or until the app is uninstalled. Closing or restarting the app does not unset the User ID.

To set the User ID, use the setCustomUserId method. To unset it (for example, if the User "logs out" of the account), call unsetCustomUserId.

Note: If multiple Users use a single device, we recommend implementing a logout flow to set and unset the User ID for each login and logout.

If you already know the user ID when the app opens, call setCustomUserId before initializing the Singular SDK. This way, Singular can have the User ID from the first Session. However, the User ID is typically unavailable until the User registers or performs a login. In that case, call setCustomUserId after the registration flow is complete.

Singular.setCustomUserID Method
Description Send the user ID to Singular.
Signature public void setCustomUserId(string customUserId)
Usage Example
Singular.setCustomUserId("custom_user_id");
Singular.unsetCustomUserID Method
Description Unset the user ID that has been sent to Singular.
Signature public void unsetCustomUserId()
Usage Example
Singular.unsetCustomUserId();
Optional: Custom User ID Device Mapping

Important: This advanced Enterprise feature is only available in exceptional cases. Please consult with one of Singular’s Solution Engineers before implementing it.

Singular can receive additional mobile event tracking data via a server-to-server integration. To utilize this feature, you must map the User ID to Singular’s Mobile Device tracking identifier.

Note: Call this method as soon as possible after initializing the Singular SDK or once you have the User ID.

Singular.setDeviceCustomUserId Method
Description Sets the Custom User Id the same as login and maps it to Singular’s tracking identifier.
Signature public void setDeviceCustomUserId(string customUserId)
Usage Example
Singular.setDeviceCustomUserId("custom_user_id");

2.6. Implementing Global Properties (Optional)

The Singular SDK lets you define additional custom properties to send to the Singular servers with every session and event sent from the app. These properties can represent any information you want about the user, the app mode or status, or anything else. Once you set these properties, they are available as dimensions in your reports and you can use them to break down your data.

For example, if you have a gaming app, you can define a property called "Level" and set it initially to "0". Any session and event sent from the app will be sent with "Level": "0". Once the user levels up you reset the property to "1" and so on. You can then get your reports, including sessions, event counts, and revenue data, broken down by user level.

  • You can define up to 5 global properties.
  • They persist between app runs (with the latest value you gave them) until you unset them or the user uninstalls the app.
  • Each property name and value can be up to 200 characters long. If you pass a longer property name or value, it will be truncated to 200 characters.
  • Global properties are accessible and available in user-level exports and postbacks. In the future, aggregate reporting support will be added. Let your Singular customer success manager know if you have any questions, or are interested in updates to global properties support!

Setting Global Properties through SingularConfig

You can use the withGlobalProperty method to set global properties through SingularConfig before initializing the SDK.

Note that since global properties and their values persist between app runs, the property that you are setting may already be set to a different value. Use the overrideExisting parameter to tell the SDK whether to override an existing property with the new value or not.

withGlobalProperty Method 
Description Set a global property.
Signature withGlobalProperty(String key, String value, boolean overrideExisting)
Usage Example
// Set two global properties and override any existing values
SingularConfig config = new SingularConfig("SDK KEY", "SDK SECRET")
  .withGlobalProperty(“MyProperty”, “MyValue”, true)
  .withGlobalProperty(“AnotherProperty”, “AnotherValue”, true);

Setting Global Properties After Initialization

Use the following methods to set, unset, and retrieve global properties at any time in the app's run.

Singular.setGlobalProperty Method
Description

Set a global property to a given value.

Notes:

  • If the property does not exist yet, and there are already 5 other global properties, the property will not be added.
  • If the property has already been set, the overrideExisting parameter determines whether the existing value will be overridden.
  • The method returns true if the property was set successfully or false otherwise.
Signature public static bool setGlobalProperty(String key, String value, boolean overrideExisting)
Usage Example
boolean result = Singular.setGlobalProperty(“MyProperty”, “MyValue”, true);
Singular.getGlobalProperties Method
Description Retrieve all the global properties and their current values as a Map.
Signature public static Map<String, String> getGlobalProperties()
Usage Example
Map<String, String> map = Singular.getGlobalProperties();
Singular.unsetGlobalProperty Method
Description Remove a global property.
Signature public static void unsetGlobalProperty(String key)
Usage Example
Singular.unsetGlobalProperty(“MyProperty”);
Singular.clearGlobalProperties Method
Description Remove all global properties.
Signature public static void clearGlobalProperties()
Usage Example
Singular.clearGlobalProperties();

3. Tracking Events and Revenue

3.1. Tracking Events

Singular can collect data about in-app events to help analyze the performance of your campaigns and measure KPIs. For example, your organization may want to collect data about user logins, registrations, tutorial completions, or leveling up in a gaming app.

What are Standard Events and Attributes?

Singular supports a variety of standard events. These commonly used events are often supported by ad networks for reporting and optimization. Another advantage is that when you use standard event names, Singular recognizes them automatically and adds them to the Events list without you having to define them manually. We recommend using standard events whenever possible.

The list of events sent to Singular (with the accompanying attributes) should be compiled by the UA/marketing/business team based on your organization's marketing KPIs. The business team can follow the guide at How to Track In-App Events: Guide For Singular Attribution Customers.

With each event you track, you can pass various attributes. See the recommended standard attributes per event.

Sending Events

In your code, send events to Singular using the eventJSON or event methods (we recommend eventJSON for readability).

Singular.eventJSON Method
Description Report a user event to Singular with additional information in JSONObject format.
Signature

Singular.eventJSON(String name, JSONObject args)

Note: 'args' is a JSONObject containing one or more key-value pairs. The key is a string and the value can be any type that's allowed as a JSONObject value.
Usage Example
Java (MainActivity.java) Kotlin (MainActivity.kt)
// Example 1:
// Send the standard event sng_tutorial_complete with the // recommended standard attributes JSONObject att = new JSONObject(); try { att.put(Attributes.sngAttrContent.toString(), "Telugu"); att.put(Attributes.sngAttrContentId.toString(), 32); att.put(Attributes.sngAttrContentType.toString(), "video"); att.put(Attributes.sngAttrSuccess.toString(), 92); } catch (JSONException e) { e.printStackTrace(); // Or log the exception } Singular.eventJSON(Events.sngTutorialComplete.toString(), att);
// Example 2: // Send a custom event named "bonus_points_earned" with custom attributes JSONObject att = new JSONObject(); try { att.put("Points", 500);
att.put("score", 650); } catch (JSONException e) { e.printStackTrace(); // Or log the exception } Singular.eventJSON("Bonus Points Earned", att);
Singular.event Method
Description Report a user event to Singular with or without additional information.
Signature

Singular.event(String eventName)
Singular.event(String eventName, Object... args)

Note: 'args' is one or more key-value pairs (see the example below). The key is a string and the value can be any type that's allowed as a JSONObject value (i.e., JSONObject, JSONArray, String, Boolean, Integer, Long, Double or NULL).

The 'args' list must contain an even number of elements or the event will be rejected by Singular.

Usage Example
Java (MainActivity.java) Kotlin (MainActivity.kt)
// Example 1:
// Send the standard event "Subscribe" (sng_subscribe) without any attributes
Singular.event(Events.sngSubscribe.toString());

// Example 2:
// Send the standard event "sng_tutorial_complete" with the 
//recommended standard attributes Singular.event(Events.sngTutorialComplete.toString(), Attributes.sngAttrContent.toString(), "Spanish", Attributes.sngAttrContentId.toString(), 52, Attributes.sngAttrContentType.toString(), "video", Attributes.sngAttrSuccess.toString(), 46 ); // Example 3: // Send a custom event named "SignUp" without any custom attributes Singular.event("SignUp"); // Example 4: // Send a custom event named "bonus_points_earned" with a custom attribute Singular.event("Bonus Points Earned", "Points", 500);

Notes:

  • We highly recommend passing event names and attributes in English to guarantee compatibility with third-party partners and analytics solutions if you plan to use them.
  • Event names are limited to 32 ASCII characters. Strings in non-ASCII characters have to be under 32 bytes once converted to UTF-8.
  • Attributes and values are limited to 500 ASCII characters.

3.2. Tracking Revenue

Singular can collect data about revenue gained through the app to help analyze the performance and ROI of your campaigns. Singular will make the data available to you in reports, log export, and postbacks.

Tracking Revenue Using the Purchase Object

When reporting revenue events to Singular, we recommend passing the purchase object received from the billing library. This has two advantages:

  1. Singular gets all the details of the transaction, which enriches your Singular reports.
  2. Singular gets the transaction receipt from Google which can be used to validate the transaction in the context of fighting in-app fraud.

REMINDER:

  • Starting on August 2, 2023, all new apps must use Billing Library version 5 or newer. By November 1, 2023, all updates to existing apps must use Billing Library version 5 or newer. Learn more.
  • If your app is targeting Android 14 or higher, you must update to PBL 5.2.1 or PBL 6.0.1 or higher.

Use the revenue and customRevenue methods to report events. CustomRevenue allows you to pass a custom event name, so that you'll be able to view revenue in Singular reports broken down by the different types of revenue events.

Note: Any revenue reported in a different currency will be auto-converted to your organization's preferred currency, as set in your Singular account.

Singular.revenue Method
Description Send a revenue event to Singular with optional additional information.
Signature

Singular.revenue(String currency, double amount, Object purchase)

Note:

  • When passing an object, it has to be of type Purchase or Singular will not contain the receipt information.
  • Pass currency as a three-letter ISO 4217 currency code, such as “USD”, “EUR”, or “INR".
Usage Example

Send a Revenue event to Singular with the purchase object received from the Google Billing Library

Java (MainActivity.java) Kotlin (MainActivity.kt)
Singular.revenue("USD", 5.50, purchase);
Singular.customRevenue Method
Description Send a revenue event to Singular with an event name and optional additional information.
Signature

Singular.customRevenue(String eventName, String currency, double amount, Object purchase)

Note:

  • When passing an object, it has to be of type Purchase or Singular will not contain the receipt information.
  • Pass currency as a three-letter ISO 4217 currency code, such as “USD”, “EUR”, or “INR".
Usage Example

Send a Custom Revenue event to Singular with a custom Event Name and the purchase object received from the Google Billing Library

Java (MainActivity.java) Kotlin (MainActivity.kt)
Singular.customRevenue("MyCustomRevenue", "USD", 5.50, purchase);

Reporting Revenue without the Purchase Object

While we strongly recommend reporting revenue events the way described above, you can also use revenue and customRevenue without passing the purchase object. Instead, you pass the currency and amount of the transaction, and optional product details.

Note that when reporting revenue events this way, Singular does not get the purchase receipt and cannot validate the transaction.

Read more...
Singular.revenue Method (Without Purchase Object)

Description

Send a revenue event to Singular with optional additional information.

Signature

Singular.revenue(String currency, double amount, String productSKU, String productName, String productCategory, int productQuantity, double productPrice)

Singular.revenue(String currency, double amount)

Note: Pass currency as a three-letter ISO 4217 currency code, such as “USD”, “EUR”, or “INR".

Usage Example

Send a Revenue event to Singular without Purchase Object and with product details.

Java (MainActivity.java) Kotlin (MainActivity.kt)

With Product Details

Singular.revenue("EUR", 5.00, "SKU1928375", "Reservation Fee", "Fee" , 1, 5.00);

Without Product Details

Singular.revenue("USD", 5.50);
Singular.customRevenue Method (Without Purchase Object)
Description Send a revenue event to Singular with an event name and optional additional information.
Signature

Singular.customRevenue(String eventName, String currency, double amount, String productSKU, String productName, String productCategory, int productQuantity, double productPrice)
Singular.customRevenue(String currency, double amount)

Note: Pass currency as a three-letter ISO 4217 currency code, such as “USD”, “EUR”, or “INR".

Usage Example

Send a Custom Revenue event to Singular without Purchase Object and with product details.

Java (MainActivity.java) Kotlin (MainActivity.kt)

With Custom Event Name and Product Details

Singular.customRevenue("MyCustomRevenue", "EUR", 5.00, "SKU1928375", "Reservation Fee", "Fee" , 1, 5.00);

With Custom Event Name and Without Product Details

Singular.customRevenue("MyCustomRevenue", "USD", 5.50);

3.3. Hybrid Event Tracking (Advanced)

Singular recommends sending all events and revenue through the Singular SDK integrated into your app. However, Singular can collect events and revenue from other sources.

Any event NOT sent from the Singular SDK must comply with Singular's Server-to-Server Event documentation requirements and provide the matching device identifier to correctly attribute an event.

Important:

Discrepancies will occur if device identifiers used on Server-to-Server event requests do not have a matching device identifier in Singular. Be aware of the following possibilities:

  • If an event request is received "before" the Singular SDK has recorded the device identifier, from an App Session, then the event request will be considered the "first session" for the unknown device, and Singular will attribute the device as an organic attribution.
  • If the Singular SDK did record a device identifier, but the Singular SDK identifier differs from the device identifier specified in the Server-to-Server Event request then the event will be attributed incorrectly.

Hybrid Event Tracking Guides

Sending Events from an Internal Server

Singular can collect data about revenue from your Server to help analyze the performance and ROI of your campaigns.

Requirements:

  • From an in-app Registration or Login Event, capture and pass the device identifiers and store this data with the User ID on your server. Because device identifiers may change for a user, be sure to update the identifiers when a user generates an app session. This guarantees the server-side event will be attributed to the correct device.
  • Server-side events are platform specific and should only be sent with the device identifier matching the device platform (e.g., IDFA or IDFV for iOS devices).
  • You can use the Singular Internal BI postback mechanism to push an event in real time to your internal endpoint so that you can update the data set on the server side. See the Internal BI Postback FAQ.
  • Review the "Tracking Revenue" section in the Server-to-Server Integration guide for details.
Sending Events from a Revenue Provider
Third-party providers like RevenueCat or adapty can provide Purchase and Subscription Revenue to Singular.

Follow the links below for details on how to enable these partners.

Sending Events from Segment

To enable Segment to send events to Singular, in parallel with the Singular SDK, you must add a "Cloud-Mode" Destination in Segment. Follow our guide HERE.

4. Advanced Options

4.1. Creating Short Referrer Links

Note: This functionality is available in SDK version 12.1.1+. Once created, short links remain active for 30 days.

Use short links to transform long, parameter-filled Singular Links into shorter and more secure links that are convenient for sharing.

Typically, you will want to create short links dynamically so that your app's users can share them with friends to invite them to use the app.

To create a short link, you need:

  • A Singular Link that leads to your app download (see the Singular Links FAQ).
  • Any parameters you want to add to the link dynamically (see Tracking Link Parameters for the list of options).
  • The name and ID of the referring user, if you want to be able to track new app installs back to the user who shared the link.

Use the createReferrerShortLink method to create a short link as in the example below.

Java (MainActivity.java) Kotlin (MainActivity.kt)
// Create a JSON object to add parameters to the Singular Link (if they don't exist in the link URL yet)
JSONObject params = new JSONObject();       
try {
      params.put("channel","sms");
      params.put("another parameter","parameter value");
} catch (JSONException e) {
      e.printStackTrace();
}

Singular.createReferrerShortLink (
  "https://sample.sng.link/D52wc/cuvk?pcn=test", // The original Singular Link URL
  "Referrer Name",
  "Referrer ID",
  params,
  new ShortLinkHandler() { 
    @Override
      public void onSuccess(final String shortLinkURL) {
        view.post(new Runnable() {
          @Override
          public void run() {
            // Add your share logic here
          }   
        });
      }

@Override public void onError(final String error) { view.post(new Runnable() { @Override public void run() { // Logic to retry/abort/modify the params passed to
// the function, based on the cause of the error } }); } });

Adding_Ad_Revenue_Attribution_Support

4.2. Adding Ad Revenue Attribution Support

Note: Starting in version 11.0.0, Singular added the option to set up ad revenue attribution through the Singular SDK. You still have the option of setting up ad revenue attribution the old way (using API calls), which doesn't require you to update the Singular SDK in your apps.

To add ad revenue attribution support in the Singular SDK:

  1. Update to the latest version of the Singular SDK.
  2. Add the appropriate code snippet to your Singular SDK integration, depending on the mediation platform you use for ad revenue data.

Note: Pass currency as a three-letter ISO 4217 currency code, e.g., "USD," "EUR", "INR".

AdMob
  1. Note: This feature needs to be enabled in your Admob account.

    See https://support.google.com/admob/answer/11322405#getstarted

KotlinJava
var mRewardedAd: RewardedAd? = null

override fun onAdLoaded(rewardedAd: RewardedAd) {
   mRewardedAd = rewardedAd
   mRewardedAd?.setOnPaidEventListener(object : OnPaidEventListener {
      override fun onPaidEvent(adValue: AdValue) {
         val impressionData: AdValue = adValue
         val data = SingularAdData(
            "AdMob",
            impressionData.currencyCode,
            impressionData.valueMicros / 1000000.0)
         Singular.adRevenue(data)
      }
  })
}
AppLovinMax
  1. Retrieve the object received from AppLovin MAX's event, onMessageReceived
KotlinJava
val message: AppLovinCommunicatorMessage? = null 
val adData: Bundle? = message?.messageData

adData?.let { bundle -
    val data = SingularAdData(
        "AppLovin",
        "USD",
        bundle.getDouble("revenue", 0.0)
    )
    Singular.adRevenue(data)
}
IronSource
  1. Retrieve the object received from IronSource's event, OnImpressionSuccess
  2. Ensure that the ARM SDK Postbacks Flag in IronSource is turned on
  3. See https://developers.is.com/ironsource-mobile/general/ad-revenue-measurement-postbacks/#step-1
KotlinJava
val impressionData: ImpressionData? = null

impressionData?.let { data -
    val singularAdData = SingularAdData(
        "IronSource",
        "USD",
        data.revenue)
    Singular.adRevenue(singularAdData)
}
TradPlus
  1. Set the impressionDelegate
  2. Add Singular to the TradPlusAdImpression Callback
KotlinJava
TradPlusSdk.setGlobalImpressionListener(
object : GlobalImpressionManager.GlobalImpressionListener { override fun onImpressionSuccess(tpAdInfo: TPAdInfo?) { if (tpAdInfo == null) return val revenue = tpAdInfo.ecpm.toDouble() / 1000 val data = SingularAdData(
"TradPlus",
"USD",
revenue) Singular.adRevenue(data) } })
Other (Generic)
  1. Initialize the SingularAdData object with the relevant data
  2. Report the data to Singular
KotlinJava
val data = SingularAdData(
"YOUR_AD_PLATFORM",
"CURRENCY_CODE",
9.90)
Singular.adRevenue(data)

4.3. Uninstall Tracking

To enable uninstall tracking for your Android app, first configure the app in the Singular platform as detailed in Setting Up Uninstall Tracking. Then follow the instructions below.

Note: Google deprecated the GCM APIs in April 2018. Use Firebase Cloud Messaging (FCM) for uninstall tracking, as described below.

I. Integrate with FCM:

To track uninstalls, you can use the services of the Firebase Cloud Messaging (FCM) platform. If you are not already using FCM follow Google's instructions on how to Set up a Firebase Cloud Messaging client app on Android.

FCM Requirements ( source )

FCM clients require devices running Android 4.1 or higher that also have the Google Play Store app installed, or an emulator running Android 4.1 with Google APIs. Note that you are not limited to deploying your Android apps through the Google Play Store.

Users/devices who are not running on supported versions of Android will not be available for Singular uninstall tracking.

II. Update the AndroidManifest.xml File:

Update your AndroidManifest.xml file to add the necessary intent filter for your app (replace MyFirebaseMessagingService with your class that implements the Firebase Service):

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

III. Register and Send the FCM Device Token:

Finally, set the FCM device token after your SingularConfig is initialized in OnCreate(), as follows:

Singular.setFCMDeviceToken(String fcmDeviceToken);

4.4. Collecting the Install Referrer on Older Devices

Note: Google is deprecating the install_referrer intent broadcast. See: Still Using InstallBroadcast? Switch to the Play Referrer API by March 1, 2020

The install referrer is Singular's most accurate tool to determine attribution, in addition to helping Singular detect and analyze fraud attempts. It is an identifier provided by the Google Play Store that points to the ad that the user clicked on before installing the app.

On devices that have the latest version of the Google Play Store, the Singular SDK collects the install referrer value automatically (since Singular is integrated with the latest Google Play Referrer API).

To collect the install referrer on older devices:

If you have an existing install referrer receiver:

Chances are your app already has a BroadcastReceiver that receives the INSTALL_REFERRER from Android.

If so, just go into the BroadcastReceiver's onReceive method and add the following line:

new SingularInstallReceiver().onReceive(context, intent);

For example, if your existing receiver is called MyCustomInstallReceiver, it should look as follows:

public class MyCustomInstallReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // Pass install referrer information on to Singular
        new SingularInstallReceiver().onReceive(context, intent);
        // ...
    }
}

If there is no other install referrer receiver:

If you don't have any install referrer receiver in your app, you can let the Singular SDK register the only receiver simply by adding the following to your <application> tag in your manifest file:

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

4.5. Managing Sessions

For Android API 14 (Ice Cream Sandwich) and above, the Singular SDK can handle session management automatically. If your app's minSdkVersion is 14 or higher, no additional configuration is required for session management.

Modifying the Session Timeout

By default, if the app runs in the background for 60 seconds or more before returning to the foreground, the SDK registers a new session.

To change the timeout value, use withSessionTimeoutInSec(<timeout in seconds>) in SingularConfig before initializing the SDK.

For example:

// Set the session timeout to 120 seconds
SingularConfig config = new SingularConfig("SDK KEY", "SDK SECRET")
.withSessionTimeoutInSec(120); 

Manual Session Management

If your app's minSdkVersion is below 14, you need to manage sessions manually by calling the Singular's SDK two session-handling methods, onActivityPaused and onActivityResumed, from each of your activities.

Note: If you have a custom common base activity class from which all other activities are derived, you can place these calls to onActivityResumed and onActivityPaused in the common activity's "onResume and onPause methods.

Singular.onActivityResumed Method
Description Call this method inside the activity's onResume method to manage the Singular session.
Signature public static void onActivityResumed()
Usage Example
@Override
protected void onResume() {
    super.onResume();
    Singular.onActivityResumed();
    .... //other code if any
}
Singular.onActivityPaused Method
Description Call this method inside the activity's onPause method to manage the Singular session.
Signature public static void onActivityPaused()
Usage Example
@Override
protected void onPause() {
    super.onPause();
    Singular.onActivityPaused();
    .... //other code if any
}

4.6. Using the JavaScript Interface

Singular provides a JavaScript interface that you can use in order to call Singular from within javaScript code in your app.

For example, if you set up the JavaScript interface, you can send events to Singular from JavaScript code as follows:

Event Example

SingularInterface.event('event');
SingularInterface.event('test',
JSON.stringify({"a1":"bar", "a2":"boo", "a3":"baz"}));

The interface supports the following SDK methods:

  • setCustomUserID
  • unsetCustomUserID
  • event
  • revenue

To enable the JavaScript interface, add the following lines of code to your main activity, where "myWebView" is the name of your webview.

SingularJSInterface singularJSInterfaceInstance = new SingularJSInterface(this);
singularJSInterfaceInstance.setWebViewId(R.id.webview);
myWebView.addjavascriptInterface(singularJSInterfaceInstance, "SingularInterface");

Notes:

  • If you have more than one webview, do this for each one.
  • We recommend placing the code in the onCreate method of your application.

Your onCreate method may look like this:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    WebView myWebView = (WebView) this.findViewById(R.id.webview);
    WebSettings webSettings = myWebView.getSettings();
    webSettings.setjavaScriptEnabled(true);
    myWebView.loadUrl("file:///android_asset/index.html");

    SingularConfig config = new SingularConfig("SDK KEY", "SDK SECRET");
    Singular.init(this, config);
    SingularJSInterface singularJSInterfaceInstance = 
        new SingularJSInterface(this);
    singularJSInterfaceInstance.setWebViewId(R.id.webview);
    myWebView.addjavascriptInterface(singularJSInterfaceInstance,
        "SingularInterface");
}

4.7. Collecting the OAID (Open Advertising ID)

In countries that do not use Google Play, Android devices do not have a Google Advertising ID (GAID, also called AIFA in Singular). Instead, the devices may offer an identifier called OAID (Open Advertising Identifier) that can be used to track sessions and events coming from the device.

OAID is currently offered on devices by Huawei and by brands that belong to the Mobile Security Alliance (MSA).

For your app to collect the OAID, you first have to integrate the MSA SDK and Huawei OAID SDK. You need to integrate both SDKs to be able to collect the OAID on all the platforms that offer it.

Then, to tell the Singular SDK to use OAID for tracking, add a call to withOAIDCollection in the config object before initializing Singular.

SingularConfig config = new SingularConfig("SDK KEY","SDK SECRET")
    .withOAIDCollection();
Singular.init(context, config);

The Singular SDK will automatically detect if the device has an OAID as well as which OAID SDK should be used to collect the identifier.

4.8. Collecting the IMEI Number

If your app is offered in countries that do not use Google Play, devices do not have a Google Advertising ID. In this case, you may want to collect the device IMEI (International Mobile Equipment Identity) instead.

Note: If you use Google Play Services, you should not collect the IMEI number because it's a violation of the Google Play service agreement.

To collect the IMEI number:

Add the android.permission.READ_PHONE_STATE permission to the app's AndroidManifest.xml file:

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

Add code like the following to fetch the device's IMEI number:

TelephonyManager telephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);

String imei = null;

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    imei = telephonyManager.getImei();
} else {
    imei = telephonyManager.getDeviceId();
}

To send the IMEI number to Singular, use one of the following methods:

Recommended: Set the IMEI number in SingularConfig using withIMEI before you initialize the Singular SDK, as in the following example. This makes the IMEI number available to Singular from the very first session.

SingularConfig config = new SingularConfig("SDK KEY","SDK SECRET")
.withIMEI("537769845792516"); Singular.init(context, config);

To set the IMEI number at any point in the code after the SDK initialization, call setIMEI.

Singular.setIMEI Method
Description Send the device's IMEI number to Singular.
Signature public void setIMEI(string IMEIString)
Usage Example
Singular.setIMEI(IMEIString);

4.9. Complying with Data Privacy Laws

Singular provides privacy-safeguarding functionality to help you cooperate with any partners who may be complying with consumer privacy laws such as GDPR and CCPA (California Consumer Privacy Act). These partners want to be notified if the end-user has consented to share their private information.

If you have implemented a way to ask users for consent to share their information, use the limitDataSharing method to notify Singular of the user's choice:

Use Singular.limitDataSharing(false) to indicate that the user consented (opted in) to share their information.

Use Singular.limitDataSharing(true) if the user did not consent.

Singular uses LimitDataSharing in "User Privacy Postbacks" as well as passing this information on to partners who require it in order to comply with relevant regulations. See "User Privacy and Limit Data Sharing" for more information.

Note: The use of the method is optional, but there may be attribution information that the partner will share with Singular only if specifically notified that the user has opted in.

Singular.limitDataSharing Method
Signature Singular.limitDataSharing(boolean shouldLimitDataSharing)
Description Notify Singular of user consent (opt-in) for sharing private data.
Usage Example
// User has opted into sharing data
Singular.limitDataSharing(false);

Additional Methods for GDPR Compliance

The Singular SDK provides several methods to help you comply with GDPR policies and let Singular know about user consent or non-consent for tracking.

Singular.trackingOptIn Method
Description Notify Singular of user consent (opt-in) for tracking.
Usage Example
Singular.trackingOptIn();
Singular.stopAllTracking Method
Description

Stop all tracking activities for this user on this app.

Note: Calling this method effectively disables the SDK, even after the app restarts (the state is persistent)! The only way to re-enable tracking is by calling resumeAllTracking().
Usage Example
Singular.stopAllTracking();
Singular.resumeAllTracking Method
Description Resume tracking for this user on this app.
Usage Example
Singular.resumeAllTracking();
Singular.isAllTrackingStopped Method
Description Check the tracking status for this user on this app. Returns true if tracking has been stopped using StopAllTracking() and not resumed.
Usage Example
Singular.isAllTrackingStopped();