Skip to content

Commit 7afa9ee

Browse files
Await GAID before targeting and identify (#38)
Co-authored-by: Eugene Dorfman <eugene.dorfman@gmail.com>
1 parent 0a58422 commit 7afa9ee

10 files changed

Lines changed: 265 additions & 30 deletions

File tree

DemoApp/DemoAppJava/app/src/main/java/co/optable/demoappjava/TheApplication.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class TheApplication extends Application {
1212
public void onCreate() {
1313
super.onCreate();
1414

15-
OptableConfig config = new OptableConfig(this, "prebidtest", "js-sdk");
15+
OptableConfig config = new OptableConfig(this, "prebidtest", "android-sdk");
1616
optable = new OptableSDK(config);
1717
}
1818

DemoApp/DemoAppKotlin/app/src/main/java/co/optable/androidsdkdemo/TheApplication.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class TheApplication : Application() {
1313
override fun onCreate() {
1414
super.onCreate()
1515

16-
val config = OptableConfig(this, "prebidtest", "js-sdk")
16+
val config = OptableConfig(this, "prebidtest", "android-sdk", "ca.edge.optable.co")
1717
optable = OptableSDK(config)
1818
}
1919

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,3 +604,13 @@ To build the Kotlin demo app, from [Android Studio](https://developer.android.co
604604
open the `DemoApp/DemoAppKotlin` directory. To build the Java demo app, open the `DemoApp/DemoAppJava` directory. In
605605
both cases, you should be able to build and run the resulting project directly, since it will automatically download the
606606
`co.optable.android_sdk` library from the [JitPack](https://jitpack.io/) Maven repository.
607+
608+
### Development
609+
610+
Test coverage is measured using the Kotlinx Kover plugin. To generate a report, run the following command:
611+
612+
```bash
613+
./gradlew android_sdk:koverHtmlReportDebug
614+
```
615+
616+
The HTML report will be available at `./android_sdk/build/reports/kover/htmlDebug/index.html`.

android_sdk/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ dependencies {
6060
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.9.4"
6161

6262
// Advertising ID
63-
implementation 'com.google.android.gms:play-services-ads-identifier:18.2.0'
63+
implementation 'com.google.android.gms:play-services-ads-identifier:18.3.0'
6464

6565
// Unit tests
6666
testImplementation("junit:junit:4.13.2")

android_sdk/src/main/java/co/optable/sdk/OptableSDK.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ class OptableSDK(
3838
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO + ceh)
3939

4040
init {
41-
if (!config.skipAdvertisingIdDetection) {
42-
googleAdIdManager.updateAdvertisingId()
41+
scope.launch {
42+
googleAdIdManager.fetchAdvertisingId()
4343
}
4444
}
4545

@@ -49,6 +49,7 @@ class OptableSDK(
4949
*/
5050
fun identify(ids: List<OptableIdentifier>, listener: OptableResultListener<Unit>) {
5151
scope.launch {
52+
googleAdIdManager.deferredTask.await()
5253
val encodedIds = identifiersEncoder.encode(ids)
5354
val response = networkClient.identify(encodedIds)
5455

@@ -135,6 +136,7 @@ class OptableSDK(
135136
*/
136137
fun targeting(ids: List<OptableIdentifier>, listener: OptableResultListener<OptableTargeting>) {
137138
scope.launch {
139+
googleAdIdManager.deferredTask.await()
138140
val ids = identifiersEncoder.encode(ids)
139141
val response = networkClient.targeting(ids)
140142

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package co.optable.sdk.core
22

3+
import android.util.Log
34
import co.optable.sdk.OptableConfig
45
import com.google.android.gms.ads.identifier.AdvertisingIdClient
5-
import kotlinx.coroutines.GlobalScope
6-
import kotlinx.coroutines.MainScope
7-
import kotlinx.coroutines.launch
6+
import kotlinx.coroutines.CompletableDeferred
7+
import kotlinx.coroutines.Dispatchers
8+
import kotlinx.coroutines.withContext
9+
import kotlinx.coroutines.withTimeoutOrNull
810

911
internal class GoogleAdIdManager(
1012
val config: OptableConfig,
@@ -15,20 +17,7 @@ internal class GoogleAdIdManager(
1517
private var limitAdTracking: Boolean? = null
1618
}
1719

18-
fun updateAdvertisingId() {
19-
GlobalScope.launch {
20-
var adInfo: AdvertisingIdClient.Info? = null
21-
try {
22-
adInfo = AdvertisingIdClient.getAdvertisingIdInfo(config.context)
23-
} catch (_: Exception) {
24-
}
25-
26-
MainScope().launch {
27-
adId = adInfo?.id
28-
limitAdTracking = adInfo?.isLimitAdTrackingEnabled
29-
}
30-
}
31-
}
20+
val deferredTask = CompletableDeferred<String?>()
3221

3322
fun getId(): String? {
3423
if (limitAdTracking == true) {
@@ -37,4 +26,30 @@ internal class GoogleAdIdManager(
3726
return adId
3827
}
3928

29+
suspend fun fetchAdvertisingId() {
30+
if (config.skipAdvertisingIdDetection) {
31+
deferredTask.complete(null)
32+
return
33+
}
34+
35+
val id = withContext(Dispatchers.IO) {
36+
withTimeoutOrNull(3_000) {
37+
fetch()
38+
}
39+
}
40+
deferredTask.complete(id)
41+
}
42+
43+
private fun fetch(): String? {
44+
try {
45+
val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(config.context)
46+
adId = adInfo.id
47+
limitAdTracking = adInfo.isLimitAdTrackingEnabled
48+
return adInfo.id
49+
} catch (exception: Exception) {
50+
Log.w("OptableGaidManager", "Failed to fetch advertising ID: " + exception.message)
51+
}
52+
return null
53+
}
54+
4055
}

android_sdk/src/main/java/co/optable/sdk/core/IdentifiersEncoder.kt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ internal class IdentifiersEncoder(
3434
fun encode(identifiers: List<OptableIdentifier>): List<String> {
3535
val result = mutableListOf<String>()
3636

37-
var containsCustomGaid = false
37+
val googleAdId = googleAdIdManager.getId()
38+
if (googleAdId != null) {
39+
result.addIfNotNull(GAID, googleAdId, ::normalize)
40+
}
41+
3842
for (identifier in identifiers) {
3943
when (identifier) {
4044
is OptableIdentifier.Email -> result.addIfNotNull(EMAIL, identifier.value, ::encrypt)
@@ -51,8 +55,10 @@ internal class IdentifiersEncoder(
5155
is OptableIdentifier.Utiq -> result.addIfNotNull(UTIQ, identifier.value, ::normalize)
5256

5357
is OptableIdentifier.GoogleGaid -> {
58+
if (normalize(googleAdId ?: "") == normalize(identifier.value)) {
59+
continue
60+
}
5461
result.addIfNotNull(GAID, identifier.value, ::normalize)
55-
containsCustomGaid = true
5662
}
5763

5864
is OptableIdentifier.Custom -> {
@@ -67,10 +73,6 @@ internal class IdentifiersEncoder(
6773
}
6874
}
6975

70-
val googleAdId = googleAdIdManager.getId()
71-
if (!containsCustomGaid && googleAdId != null) {
72-
result.addIfNotNull(GAID, googleAdId, ::trim)
73-
}
7476

7577
return result
7678
}

android_sdk/src/test/java/co/optable/sdk/OptableIdentifiersTest.kt

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,19 +178,37 @@ class OptableIdentifiersTest {
178178

179179
@Test
180180
fun `custom gaid`() {
181-
every { mockGoogleAdIdManager.getId() } returns "managerId"
181+
every { mockGoogleAdIdManager.getId() } returns null
182182

183183
val actual = identifiersEncoder.encode(listOf(OptableIdentifier.GoogleGaid("customId")))
184184
val expected = listOf("g:customid")
185185
assertEquals(expected, actual)
186186
}
187187

188+
@Test
189+
fun `both gaids, send two`() {
190+
every { mockGoogleAdIdManager.getId() } returns "managerId"
191+
192+
val actual = identifiersEncoder.encode(listOf(OptableIdentifier.GoogleGaid("customId")))
193+
val expected = listOf("g:managerid", "g:customid")
194+
assertEquals(expected, actual)
195+
}
196+
197+
@Test
198+
fun `both gaids equals, send only one`() {
199+
every { mockGoogleAdIdManager.getId() } returns "sameId"
200+
201+
val actual = identifiersEncoder.encode(listOf(OptableIdentifier.GoogleGaid("sameid")))
202+
val expected = listOf("g:sameid")
203+
assertEquals(expected, actual)
204+
}
205+
188206
@Test
189207
fun `gaid from manager`() {
190208
every { mockGoogleAdIdManager.getId() } returns "managerId"
191209

192210
val actual = identifiersEncoder.encode(emptyList())
193-
val expected = listOf("g:managerId")
211+
val expected = listOf("g:managerid")
194212
assertEquals(expected, actual)
195213
}
196214

android_sdk/src/test/java/co/optable/sdk/OptableSDKTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ class OptableSDKTest {
4545
mockkStatic(Log::class)
4646
every { Log.e(any(), any()) } returns 0
4747
every { Log.e(any(), any(), any()) } returns 0
48+
every { Log.w(any(), any<String>()) } returns 0
4849

4950
mockkStatic(Base64::class)
51+
5052
every { Base64.encodeToString(any(), any()) } returns "mockedBase64String"
5153

5254
sdk = OptableSDK(mockConfig)

0 commit comments

Comments
 (0)