Skip to content

Kotlin SDK for integrating with an Optable Data Connectivity Node from an Android application.

License

Notifications You must be signed in to change notification settings

Optable/optable-android-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

45 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Optable Android SDK

Kotlin SDK for integrating with an Optable Data Connectivity Node (DCN) from an Android application.

Contents

Installing

This SDK is published using JitPack, so you need to add the repository to your application build. Add the following in your root build.gradle at the end of repositories:

allprojects {
    repositories {
        ...
        maven {
            url 'https://jitpack.io'
        }
    }
}

Then add the dependency to the SDK in your app build.gradle:

dependencies {
    implementation 'com.github.Optable:optable-android-sdk:VERSION_TAG'
}

Replace VERSION_TAG with the latest or desired SDK release.

Using

To create the instance of the OptableSDK you have to create OptableConfig with the required parameters.

  • providedContext Android Context (Application or Activity).
  • tenant The tenant name associated with the configuration. E.g. acmeco.optable.co => acmeco.
  • originSlug The DCN's Source Slug. E.g. acmeco-sdk.

Creating instance

Kotlin

class TheApplication : Application() {

  companion object {
    lateinit var optable: OptableSDK
  }

  override fun onCreate() {
    super.onCreate()

    val config = OptableConfig(
      providedContext = this,
      tenant = "prebidtest",
      originSlug = "js-sdk",
    )
    optable = OptableSDK(config)
  }

}

Java

public class TheApplication extends Application {

  public static OptableSDK optable;

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

    OptableConfig config = new OptableConfig(this, "prebidtest", "js-sdk");
    optable = new OptableSDK(config);
  }

}

Additionally, you can set optional parameters to OptableConfig:

  • host The hostname of the Optable endpoint. Default value is "na.edge.optable.co".
  • path The API path to be appended to the host. Default value is "v2".
  • insecure Boolean flag that determines if insecure HTTP should be used instead of HTTPS. Default is false. Note that production DCNs only listen to TLS traffic. The insecure: true option is meant to be used by Optable developers running the DCN locally for testing.
  • apiKey An optional API key for authentication. If the API Endpoint is enabled as private, a Service Account API key will be required.
  • customUserAgent An optional custom user agent string for network requests.
  • skipAdvertisingIdDetection Boolean flag to skip the detection of advertising IDs. Default is false.
  • consents Optional OptableConsents object for providing custom consent information. If not provided, default values will be used.
val config = OptableConfig(
    providedContext = this,
    tenant = "prebidtest",
    originSlug = "js-sdk",
    host = "ca.edge.optable.co",
    path = "v2",
    insecure = false,
    apiKey = API_KEY,
    customUserAgent = "",
    skipAdvertisingIdDetection = false,
    consents = OptableConsents(),
)

You can then call various SDK APIs on the instance as shown in the examples below. It's also possible to configure multiple instances of OptableSDK in order to connect to other (e.g., partner) DCNs and/or reference other configured application slug IDs.

By default, the SDK detects the application user agent by sniffing settings.userAgentString from a WebView. The resulting user agent string is sent to your DCN for analytics purposes. To disable this behavior, you can provide an optional fifth string parameter customUserAgent, which allows you to set whatever user agent string you would like to send instead. For example, in Kotlin:

val config = OptableConfig(
  ...
  customUserAgent = "Chromium WebView",
)

Finally, an optional sixth boolean parameter skipAdvertisingIdDetection can be used to skip any ID info detection from AdvertisingIdClient which by default runs in a background co-routine. Disabling ad ID detection means that the SDK will not be able to automatically obtain the Google Advertising ID. For example, to disable ad ID detection, in Kotlin:

val config = OptableConfig(
  ...
  skipAdvertisingIdDetection = true,
)

OptableIdentifiers

There are many fields to identify the users. You can provide any user information using OptableIdentifiers class. Sensitive data as email and phone number encrypted using SHA256.

NOTE: You don't need to provide all fields. Only the fields that you have are required.

⚠️ Client-side email and phone hashing: The SDK will compute the SHA-256 hash of the sensitive information on the client-side and send the hashed value to the DCN. The email address and phone number is not sent by the device in plain text.

Kotlin

val ids = listOf(
  OptableIdentifier.Email("john.doe+test@example.com"),
  OptableIdentifier.PhoneNumber("+1(555)1234567"),
  OptableIdentifier.PostalCode("12345"),
  OptableIdentifier.IPv4("192.168.0.1"),
  OptableIdentifier.IPv6("2001:db8::1"),
  OptableIdentifier.AppleIdfa("a1b2c3d4-e5f6-7890-abcd-ef0123456789"),
  OptableIdentifier.GoogleGaid("38400000-8cf0-11bd-b23e-10b96e40000d"),
  OptableIdentifier.RokuRida("roku-rida-123"),
  OptableIdentifier.SamsungTifa("tifaabc"),
  OptableIdentifier.AmazonFireAfai("afaixyz"),
  OptableIdentifier.NetId("netid123"),
  OptableIdentifier.ID5("id5token"),
  OptableIdentifier.Utiq("utiqvalue"),
  OptableIdentifier.Custom("v", "vidvalue"),
  OptableIdentifier.Custom("c1", "custom one"),
  OptableIdentifier.Raw("raw:alreadyEncoded"),
  OptableIdentifier.Raw("ultra:someRawValue")
)

Java

ArrayList<OptableIdentifier> ids = Lists.newArrayList(
  new OptableIdentifier.Email("john.doe+test@example.com"),
  new OptableIdentifier.PhoneNumber("+1(555)1234567"),
  new OptableIdentifier.PostalCode("12345"),
  new OptableIdentifier.IPv4("192.168.0.1"),
  new OptableIdentifier.IPv6("2001:db8::1"),
  new OptableIdentifier.AppleIdfa("a1b2c3d4-e5f6-7890-abcd-ef0123456789"),
  new OptableIdentifier.GoogleGaid("38400000-8cf0-11bd-b23e-10b96e40000d"),
  new OptableIdentifier.RokuRida("roku-rida-123"),
  new OptableIdentifier.SamsungTifa("tifaabc"),
  new OptableIdentifier.AmazonFireAfai("afaixyz"),
  new OptableIdentifier.NetId("netid123"),
  new OptableIdentifier.ID5("id5token"),
  new OptableIdentifier.Utiq("utiqvalue"),
  new OptableIdentifier.Custom("v", "vidvalue"),
  new OptableIdentifier.Custom("c1", "custom one"),
  new OptableIdentifier.Raw("raw:alreadyEncoded"),
  new OptableIdentifier.Raw("ultra:someRawValue")
);

Identify API

To associate a user device with an authenticated identifier such as an Email address, or with other known IDs such as the Google Advertising ID, or even your own vendor or app level PPID, you can call the identify API as follows:

Kotlin

val ids = listOf(
  OptableIdentifier.Email("john.doe+test@example.com"),
  OptableIdentifier.PhoneNumber("+1(555)1234567")
)
optable.identify(ids) { result ->
  val msg = when (result) {
    is OptableResult.Success -> "Identify success"
    is OptableResult.Error -> "Identify error: ${result.message}"
  }
  Log.d(TAG, msg)
}

Java

ArrayList<OptableIdentifier> ids = Lists.newArrayList(
    new OptableIdentifier.Email("john.doe+test@example.com"),
    new OptableIdentifier.PhoneNumber("+1(555)1234567")
);
optable.identify(ids, result -> {
    if (result instanceof OptableResult.Success) {
        Log.d(TAG, "Identify success");
    } else if (result instanceof OptableResult.Error<Unit> error) {
        Log.d(TAG, "Identify error: " + error.getMessage());
    }
});

The SDK identify() method will asynchronously connect to the configured DCN and send IDs for resolution. You can register an observer to understand successful completion or errors.

The frequency of invocation of identify is up to you, however for optimal identity resolution we recommended to call the identify() method on your SDK instance every time you authenticate a user, as well as periodically, such as for example once every 15 to 60 minutes while the application is being actively used and an internet connection is available.

Profile API

To associate key value traits with the user's device, for eventual audience assembly, you can call the profile API as follows:

Kotlin

val traits = hashMapOf<String, Any>("gender" to "F", "age" to 38, "hasAccount" to true)
optable.profile(traits) { result ->
  when (result) {
    is OptableResult.Success<*> -> {
      Log.d(TAG, "Profile success")
    }

    is OptableResult.Error -> {
      Log.d(TAG, "Profile error: ${result.message}")
    }
  }
}

Java

HashMap<String, Object> traits = new HashMap<>();
traits.put("gender", "F");
traits.put("age", 38);
traits.put("hasAccount", true);

optable.profile(traits, result -> {
    if (result instanceof OptableResult.Success) {
        Log.d(TAG, "Profile success");
    } else if (result instanceof OptableResult.Error<Unit> error) {
        Log.d(TAG, "Profile error: " + error.getMessage());
    }
});

The specified traits are associated with the user's device and can be used for matching during audience assembly. The traits are of type HashMap<String, Any>, and should consist only of key-value pairs where the key is a string and the value is either a string, number, or boolean.

Additionally you can provide id and neighbors to associate the profile with other identifiers. The id parameter takes precedence over the passport if provided.

optable.profile(traits, "c:12", setOf("c:id1", "c:id2")) { ... }

Targeting API

To get the targeting key values associated by the configured DCN with the device in real-time, you can call the targeting API as follows:

Kotlin

val ids = listOf(
  OptableIdentifier.Email("test@test.com"),
)
optable.targeting(ids) { result ->
    val requestBuilder = AdManagerAdRequest.Builder()

    when (result) {
        is OptableResult.Success<OptableTargeting> -> {
            val audiences = result.data.audiences
            if (audiences != null) {
                // Apply 
            }
        }

        is OptableResult.Error<OptableTargeting> -> {
            Log.d(TAG, "Targeting error: ${result.message}")
        }
    }
}

Java

ArrayList<OptableIdentifier> ids = Lists.newArrayList(
        new OptableIdentifier.Email("test@test.com")
)
optable.targeting(ids, result -> {
    if (result instanceof OptableResult.Success<OptableTargeting> success) {
        Map<String, List<String>> audiences = success.getData().getAudiences();
        if (audiences != null) {
            // Apply
        }
    } else if (result instanceof OptableResult.Error<OptableTargeting> error) {
        Log.d(TAG, "Targeting error: " + error.getMessage());
    }
})    

On success, the resulting key values are typically sent as part of a subsequent ad call. Therefore we recommend that you either call targeting() before each ad call, or in parallel periodically, caching the resulting key values which you then provide in ad calls.

Caching Targeting Data

The targeting API will automatically cache resulting key value data in client storage on success. You can subsequently retrieve the cached key value data as follows:

Kotlin
val targeting = optable.targetingFromCache()
if (targeting != null) {
    // Apply
}
Java
OptableTargeting optableTargeting = optable.targetingFromCache();
if (optableTargeting != null) {
    // Apply
}

You can also clear the locally cached targeting data:

optable.targetingClearCache()

Note that both targetingFromCache() and targetingClearCache() are synchronous.

Witness API

To send real-time event data from the user's device to the DCN for eventual audience assembly, you can call the witness API as follows:

Kotlin

optable.witness(
  "GAMBannerFragment.loadAdButtonClicked",
  hashMapOf("exampleKey" to "exampleValue", "anotherExample" to 123, "foo" to false)
) { result ->
  when (result) {
    is OptableResult.Success<*> -> {
      Log.d(TAG, "Witness success")
    }

    is OptableResult.Error -> {
      Log.d(TAG, "Witness error: ${result.message}")
    }
  }
}

Java

HashMap<String, Object> eventProperties = new HashMap<>();
eventProperties.put("exampleKey", "exampleValue");
eventProperties.put("exampleKey2", 123);
eventProperties.put("exampleKey3", false);

optable.witness("GAMBannerFragment.loadAdButtonClicked", eventProperties, result -> {
    if (result instanceof OptableResult.Success) {
        Log.d(TAG, "Witness success");
    } else if (result instanceof OptableResult.Error<Unit> error) {
        Log.d(TAG, "Witness error: " + error.getMessage());
    }
});

The specified event type and properties are associated with the logged event and which can be used for matching during audience assembly. The optional witness event properties are of type HashMap<String, Any>, and should consist only of key-value pairs where the key is a string and the value is either a string, number, or boolean.

Integrating Google Ad Manager

We can further extend the above targeting example to show an integration with a Google Ad Manager ad server account:

Kotlin

private lateinit var optable: OptableSDK

private fun onClickLoadAd() {
  val ids = OptableIdentifiers(email = "test@test.com")
  optable.targeting(ids) { result ->
    val requestBuilder = AdManagerAdRequest.Builder()

    when (result) {
      is OptableResult.Success<OptableTargeting> -> {
        applyOptableToGam(requestBuilder, result.data)
      }

      is OptableResult.Error<OptableTargeting> -> {
        Log.d(TAG, "Targeting error: ${result.message}")
      }
    }

    adView.loadAd(requestBuilder.build())
  }
}

private fun applyOptableToGam(builder: AdManagerAdRequest.Builder, targeting: OptableTargeting?) {
  if (targeting == null) return

  val audiences = targeting.gamTargetingKeywords
  if (audiences != null) {
    for (entry in audiences.entries) {
      builder.addCustomTargeting(entry.key, entry.value)
    }
  }
}

Java

    private AdManagerAdView mAdView;

    private void onClickLoadAd() {
      OptableIdentifiers ids = new OptableIdentifiers.Builder().email("test@test.com").build();
      optable.targeting(ids, result -> {
        AdManagerAdRequest.Builder requestBuilder = new AdManagerAdRequest.Builder();
    
        if (result instanceof OptableResult.Success<OptableTargeting> success) {
          applyOptableToGam(requestBuilder, success.getData());
        } else if (result instanceof OptableResult.Error<OptableTargeting> error) {
          Log.d(TAG, "Targeting error: " + error.getMessage());
        }
        mAdView.loadAd(requestBuilder.build());
      });
    }

    private void applyOptableToGam(AdManagerAdRequest.Builder builder, @Nullable OptableTargeting targeting) {
      if (targeting == null) return;
    
      Map<String, List<String>> audiences = targeting.getGamTargetingKeywords();
      if (audiences != null) {
        for (Map.Entry<String, List<String>> entry : audiences.entrySet()) {
          builder.addCustomTargeting(entry.getKey(), entry.getValue());
        }
      }
    }

Working examples are available in the Kotlin and Java SDK demo applications.

Identify from URL

You can identify visitors arriving from Email newsletters. If you send Email newsletters that contain links to your application (e.g., deep links), then you may want to automatically identify visitors that have clicked on any such links via their Email address.

Insert oeid into your Email newsletter template

To enable automatic identification of visitors originating from your Email newsletter, you first need to include an * oeid* parameter in the query string of all links to your website in your Email newsletter template. The value of the * oeid* parameter should be set to the SHA256 hash of the lowercased Email address of the recipient. For example, if you are using Braze to send your newsletters, you can easily encode the SHA256 hash value of the recipient's Email address by setting the oeid parameter in the query string of any links to your application as follows:

oeid={{${email_address} | downcase | sha2}}

The above example uses various personalization tags as documented in Braze's user guide to dynamically insert the required data into an oeid parameter, all of which should make up a part of the destination URL in your template.

Capture clicks on deep links in your application

In order for your application to open on devices where it is installed when a link to your domain is clicked, you need to configure and prepare your application to handle deep links first.

When Android launches your app after a user clicks on a link, it will start your app activity with your configured intent filters. You can then obtain the Uri of the link by calling getData(), and pass it to the SDK's tryIdentifyFromUrl() API which will automatically look for oeid in the query parameters of the String and call identify with its value if found.

Kotlin

optable.tryIdentifyFromUrl(url) { result ->
  val msg = when (result) {
    is OptableResult.Success -> "Identify success"
    is OptableResult.Error -> "Identify error: ${result.message}"
  }
  Log.d(TAG, msg)
}

Java

optable.tryIdentifyFromUrl(url, result -> {
    if (result instanceof OptableResult.Success) {
        Log.d(TAG, "Identify success");
    } else if (result instanceof OptableResult.Error<Unit> error) {
        Log.d(TAG, "Identify error: " + error.getMessage());
    }
});

Consents

The SDK supports GDPR, IAB TCF, IAB GPP. You can set the most common consent values like this:

val consents = OptableConsents(
  gdprSubject = true,               // User is in scope of GDPR
  gdprConsent = "consent-string",   // IAB TCF consent string
  gpp = "gpp-string",               // IAB GPP string
  gppSid = "1,2",                   // GPP section IDs (comma-separated)
  req = "gdpr",                     // Regulatory framework to apply (e.g., "gdpr")
)

optable.setConsents(consents)

Parameters:

  • gdprSubject A boolean indicating whether GDPR applies. This value should be present when gdpr_consent is supplied. If not set, SDK will try to fetch data from SharedPreferences (key IABTCF_gdprApplies), as stated in standard
  • gdprConsent TCF EU v2 consent string. If not set, SDK will try to fetch data from SharedPreferences (key IABTCF_TCString), as stated in standard.
  • gpp GPP privacy string. If not set, SDK will try to fetch data from SharedPreferences (key IABGPP_2_TCString), as stated in standard
  • gppSid A comma-separated list of up to two sections applicable in a given GPP privacy string. This value is required when gpp is present.
  • req Optable privacy regulation override, which can be one of: gdpr, can, us, or null and will override all other privacy regulations when present.

NOTE: The custom values provided to the OptableConfig will override all other privacy regulations when present.

Demo Applications

The Kotlin and Java demo applications show a working example of the APIs, as well as an integration with the Google Ad Manager ad server, enabling the targeting of ads served by GAM to audiences activated in the Optable DCN.

By default, the demo applications will connect to the Optable demo DCN at sandbox.optable.coand reference application slug android-sdk-demo. The demo apps depend on the GAM Mobile Ads SDK for Android and load ads from a GAM account operated by Optable.

Building

To build the Kotlin demo app, from Android Studio, navigate to File > Open and open the DemoApp/DemoAppKotlin directory. To build the Java demo app, open the DemoApp/DemoAppJava directory. In both cases, you should be able to build and run the resulting project directly, since it will automatically download the co.optable.android_sdk library from the JitPack Maven repository.

About

Kotlin SDK for integrating with an Optable Data Connectivity Node from an Android application.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •