From f37129c30c36690fab2093bd67a766c394a50599 Mon Sep 17 00:00:00 2001 From: Bosko Milekic Date: Tue, 8 Dec 2020 19:22:13 -0500 Subject: [PATCH] useragent override and skipAdvertisingIdDetection (#17) * Improve unit tests for eidFromURI * skipAdvertisingIdDetection and useragent override - OptableSDK constructor now accepts a useragent String argument, null by default. When not null, disables WebView based user agent detection and overrides with provided value. - OptableSDK constructor now accepts a skipAdvertisingIdDetection Boolean argument, false by default. When true, disables AdvertisingIdClient ad info detection which runs in a background coroutine. If the caller doesn't use the identify(email: String, gaid: Boolean, ppid: String) variant then ad ID detection is not needed and can be skipped. - Document in README --- README.md | 12 ++++++++++++ .../java/co/optable/android_sdk/OptableSDK.kt | 4 ++-- .../java/co/optable/android_sdk/core/Client.kt | 16 +++++++++++----- .../optable/android_sdk/OptableSDKUnitTest.kt | 18 +++++++++++++++++- 4 files changed, 42 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 8a81368..113b828 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,18 @@ MainActivity.OPTABLE = OptableSDK(this, "sandbox.customer.com", "my-app", true) However, since production sandboxes only listen to TLS traffic, the above is really only useful for developers of `optable-sandbox` running the sandbox locally for testing. +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 sandbox for analytics purposes. To disable this behavior, you can provide an optional fifth string parameter `useragent`, which allows you to set whatever user agent string you would like to send instead. For example, in Kotlin: + +```kotlin +MainActivity.OPTABLE = OptableSDK(this, "sandbox.customer.com", "my-app", false, "custom-ua") +``` + +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: + +```kotlin +MainActivity.OPTABLE = OptableSDK(this, "sandbox.customer.com", "my-app", false, null, true) +``` + ### 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: diff --git a/android_sdk/src/main/java/co/optable/android_sdk/OptableSDK.kt b/android_sdk/src/main/java/co/optable/android_sdk/OptableSDK.kt index a71bb30..d744cef 100644 --- a/android_sdk/src/main/java/co/optable/android_sdk/OptableSDK.kt +++ b/android_sdk/src/main/java/co/optable/android_sdk/OptableSDK.kt @@ -67,9 +67,9 @@ typealias OptableTargetingResponse = HashMap> * unique to the app across devices. */ -class OptableSDK @JvmOverloads constructor(context: Context, host: String, app: String, insecure: Boolean = false) { +class OptableSDK @JvmOverloads constructor(context: Context, host: String, app: String, insecure: Boolean = false, useragent: String? = null, skipAdvertisingIdDetection: Boolean = false) { val config = Config(host, app, insecure) - val client = Client(config, context) + val client = Client(config, context, useragent, skipAdvertisingIdDetection) /* * OptableSDK.Status lists all of the possible OptableSDK API result statuses. diff --git a/android_sdk/src/main/java/co/optable/android_sdk/core/Client.kt b/android_sdk/src/main/java/co/optable/android_sdk/core/Client.kt index 65f38dc..c6d662f 100644 --- a/android_sdk/src/main/java/co/optable/android_sdk/core/Client.kt +++ b/android_sdk/src/main/java/co/optable/android_sdk/core/Client.kt @@ -18,13 +18,13 @@ import okhttp3.* import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory -class Client(private val config: Config, private val context: Context) { +class Client(private val config: Config, private val context: Context, private val useragent: String?, private val skipAdvertisingIdDetection: Boolean) { var gaid: String? = null var gaidLAT: Boolean? = true private val edgeService: EdgeService? - private val userAgent = this.userAgent() private val storage = LocalStorage(this.config, this.context) + private var userAgent: String? = this.useragent private class RequestInterceptor(private val userAgent: String, private val storage: LocalStorage): Interceptor { override fun intercept(chain: Interceptor.Chain): Response { @@ -59,10 +59,16 @@ class Client(private val config: Config, private val context: Context) { } init { - this.determineAdvertisingInfo() + if (!this.skipAdvertisingIdDetection) { + this.determineAdvertisingInfo() + } + + if (this.userAgent == null) { + this.userAgent = this.userAgent() + } val client = OkHttpClient.Builder() - .addInterceptor(RequestInterceptor(userAgent, storage)) + .addInterceptor(RequestInterceptor(this.userAgent!!, storage)) .addInterceptor(ResponseInterceptor(storage)) .build() @@ -122,7 +128,7 @@ class Client(private val config: Config, private val context: Context) { } fun GAID(): String? { - return gaid!! + return gaid } private fun userAgent(): String { diff --git a/android_sdk/src/test/java/co/optable/android_sdk/OptableSDKUnitTest.kt b/android_sdk/src/test/java/co/optable/android_sdk/OptableSDKUnitTest.kt index 851ac1e..1e01b3b 100644 --- a/android_sdk/src/test/java/co/optable/android_sdk/OptableSDKUnitTest.kt +++ b/android_sdk/src/test/java/co/optable/android_sdk/OptableSDKUnitTest.kt @@ -72,13 +72,29 @@ class OptableSDKUnitTest { } @Test - fun eidFromURI_returnsEmptyWhenOeidAbsent() { + fun eidFromURI_returnsEmptyWhenOeidAbsentFromQuerystr() { val url = "http://some.domain.com/some/path?some=query&something=else" val expected = "" assertEquals(expected, OptableSDK.eidFromURI(Uri.parse(url))) } + @Test + fun eidFromURI_returnsEmptyWhenQuerystrAbsent() { + val url = "http://some.domain.com/some/path" + val expected = "" + + assertEquals(expected, OptableSDK.eidFromURI(Uri.parse(url))) + } + + @Test + fun eidFromURI_returnsEmptyWhenInputEmptyString() { + val url = "" + val expected = "" + + assertEquals(expected, OptableSDK.eidFromURI(Uri.parse(url))) + } + @Test fun eidFromURI_expectsSHA256() { val url = "http://some.domain.com/some/path?some=query&something=else&oeid=AAAAAAAa665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3&foo=bar&baz"