Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate threading model to Coroutines - long running branch #89

Merged
merged 116 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
116 commits
Select commit Hold shift + click to select a range
0395b31
tests: add functional test for validating flushing queue
wzieba Oct 31, 2023
4d5ca5d
refactor: extract `FlushManager` to separate class
wzieba Oct 30, 2023
8b9a247
Rename .java to .kt
wzieba Oct 30, 2023
a5511de
refactor: move the class to Kotlin
wzieba Oct 30, 2023
3fc205f
refactor: make `stop` return `void`
wzieba Oct 30, 2023
171ed10
build: add coroutines dependency
wzieba Oct 30, 2023
7f3e28b
feat: rewrite `FlushManager` to coroutines
wzieba Oct 30, 2023
ae2bac5
tests: add unit tests for `FlushManager`
wzieba Oct 30, 2023
9b9e527
tests: "double start" `FlushManager` test case
wzieba Oct 30, 2023
4f3f4c1
refactor: remove unnecessary `null` check on `job`
wzieba Oct 30, 2023
b5bc33e
tests: reduce time of default interval in tests to 5sec
wzieba Oct 31, 2023
1326644
tests: increase request waiting timeout to 2s
wzieba Oct 31, 2023
aad4629
style: simplify FlushManager to have one-liners
wzieba Nov 3, 2023
86bac35
Merge pull request #86 from Parsely/extract_flush_manager
wzieba Nov 3, 2023
93682b0
Merge branch 'extract_queuemanager_local_storage' into coroutines
wzieba Nov 3, 2023
dafdc62
Merge branch 'extract_queuemanager_local_storage' into coroutines
wzieba Nov 3, 2023
80454a8
Merge branch 'main' into coroutines
wzieba Nov 7, 2023
0013445
tests: add functional stress test
wzieba Nov 6, 2023
cbae82d
refactor: replace `AsyncTask` with Kotlin Coroutines in `QueueManager`
wzieba Nov 6, 2023
d3b7726
fix: close file streams when finished using
wzieba Nov 6, 2023
e9d88d6
feat: add `remove` and `persistEvent` methods
wzieba Nov 7, 2023
71cc691
refactor: remove `QueueManager`. Replace with `InMemoryBuffer`.
wzieba Nov 7, 2023
0487719
feat: make `LocalStorageRepository#insertEvents` thread safe.
wzieba Nov 7, 2023
1905ffc
feat: make SDK's `CoroutineScope` `internal`
wzieba Nov 7, 2023
8a518ce
feat: check the buffer every second
wzieba Nov 7, 2023
01503d6
tests: add unit tests for `InMemoryBuffer`
wzieba Nov 7, 2023
f963a0e
feat: introduce listener after adding an event
wzieba Nov 7, 2023
54e66a9
ci: add androidTest-results to CI job artifacts
wzieba Nov 8, 2023
52c7a43
tests: update stress test to not verify removed logic
wzieba Nov 8, 2023
3e127ba
tests: update basic tracks events scenario
wzieba Nov 8, 2023
e1234ac
style: improve log messages
wzieba Nov 8, 2023
5e590c2
Merge branch 'main' into coroutines
wzieba Nov 8, 2023
e14615f
Merge branch 'coroutines' into coroutines-queue-manager
wzieba Nov 8, 2023
9efd59f
style: remove in-memory `eventQueue`
wzieba Nov 8, 2023
5c999cc
refactor: extract json mapping logic to Kotlin object
wzieba Nov 9, 2023
de701d2
refactor: introduce `SendEvents` use case with same logic as `Parsely…
wzieba Nov 9, 2023
4ae78b2
refactor: migrate `ParselyAPIConnection` to Coroutines
wzieba Nov 9, 2023
920e995
test: update `ParselyAPIConnectionTest` to support Coroutines
wzieba Nov 9, 2023
17e7ecd
tests: remove unit tests that checks for `GET` request on empty payload
wzieba Nov 9, 2023
5bff337
tests: update `ROOT_URL` before `ParselyTracker` constructor
wzieba Nov 9, 2023
51c89b9
feat: closing the connection after successful request
wzieba Nov 9, 2023
479d262
feat: close the connection after successful request
wzieba Nov 9, 2023
742fc5d
feat: make `ParselyAPIConnection` return `Result`
wzieba Nov 9, 2023
9668a91
refactor: move handling HTTP request results to `SendEvents`
wzieba Nov 9, 2023
b7c98a7
tests: update unit tests to reflect current scope of work of `Parsely…
wzieba Nov 9, 2023
591a72d
style: move log out of
wzieba Nov 9, 2023
86ac0eb
tests: split adding new event tests
wzieba Nov 9, 2023
8c4ce70
style: fix test name
wzieba Nov 9, 2023
845947b
Merge pull request #91 from Parsely/coroutines-queue-manager
wzieba Nov 9, 2023
1be8dfc
Merge branch 'coroutines' into coroutines-send-event
wzieba Nov 9, 2023
038c0ab
refactor: use `LocalStorageRepository` methods directly
wzieba Nov 10, 2023
f294f73
refactor: use `FlushManager` methods directly
wzieba Nov 11, 2023
c93b567
fix: on successful request, remove only sent events
wzieba Nov 11, 2023
debd023
style: remove unused methods of `LocalStorageRepository`
wzieba Nov 11, 2023
940b241
tests: add tests for SendEvents usecase
wzieba Nov 11, 2023
fa73ccd
refactor: move serialization details to `JsonSerializer`
wzieba Nov 11, 2023
fab4007
fix: do not stop flush manager if local queue is not empty
wzieba Nov 11, 2023
4237fcd
fix: add mutual execution for `SendEvents#invoke`
wzieba Nov 11, 2023
69f12b3
refactor: remove `FlushQueue` AsyncTask
wzieba Nov 11, 2023
ca330ab
style: remove unused methods
wzieba Nov 11, 2023
8d4edd4
fix: make `LocalStorageRepository#getStoredQueue` thread safe and off…
wzieba Nov 11, 2023
643defe
fix: do not create a deadlock
wzieba Nov 11, 2023
39f8831
fix: do not specify context for `ParselyAPIConnection`
wzieba Nov 13, 2023
dbc73e1
tests: fix loading an empty file for `ParselyAPIConnectionTest`
wzieba Nov 13, 2023
448c1bd
tests: add functional test for engagement session
wzieba Nov 13, 2023
868a74d
Rename .java to .kt
wzieba Nov 13, 2023
6dfbf42
refactor: rewrite `EngagementManager` to Kotlin
wzieba Nov 13, 2023
8279392
tests: add test for `inc` parameter and `EngagementManager#stop` beha…
wzieba Nov 13, 2023
728fd26
feat: use Coroutines to enqueue events in `EngagementManager`
wzieba Nov 13, 2023
56f1ea6
tests: narrow down assertions for `EngagementManager`
wzieba Nov 13, 2023
68ba19e
tests: add tests for checking `inc` parameter
wzieba Nov 13, 2023
7e1d591
style: remove unused code
wzieba Nov 13, 2023
38ef0bb
tests: add unit tests for `EngagementManager#isRunning`
wzieba Nov 13, 2023
d137dc4
refactor: use `Job#isActive` to determine `EngagementManager` state
wzieba Nov 13, 2023
47d2303
style: make `baseEvent` private and immutable
wzieba Nov 13, 2023
5cbea9f
refactor: drop usages of `java.util.Calendar`
wzieba Nov 13, 2023
d90b0d6
refactor: create `deviceInfo` for every `buildEvent`
wzieba Nov 14, 2023
9a420f0
refactor: extract creating "device info" to a separate class
wzieba Nov 14, 2023
17c5e82
Rename .java to .kt
wzieba Nov 14, 2023
65e25dd
refactor: move `DeviceInfoRepository` to Kotlin
wzieba Nov 14, 2023
ce1d56c
refactor: make `EventsBuilderTest` not test `DeviceInfoRepository`
wzieba Nov 14, 2023
96acab0
refactor: move `GetAdKey` to Coroutines
wzieba Nov 14, 2023
2c0cc68
refactor: simplify AdKey retrieval
wzieba Nov 14, 2023
25d06f1
fix: remove `appname` from device info
wzieba Nov 14, 2023
c5962bc
style: remove unused imports
wzieba Nov 14, 2023
4c91494
tests: make `EventsBuilderTest` not Robolectric test
wzieba Nov 14, 2023
9cd6cce
refactor: extract getting ad key to `AdvertisementIdProvider`
wzieba Nov 14, 2023
6ff145a
refactor: extract getting uuid to `UuidProvider`
wzieba Nov 14, 2023
59a83d8
tests: add unit tests for `UuidProvider`
wzieba Nov 14, 2023
3bcbb38
refactor: do not query `ANDROID_ID` from `SharedPreferences`.
wzieba Nov 14, 2023
c6f68bf
tests: add unit tests for `AndroidDeviceInfoRepository`
wzieba Nov 14, 2023
366dae5
refactor: rewrite SdkInit to simple function
wzieba Nov 17, 2023
acae5f6
refactor: introduce `FlushManager` interface
wzieba Nov 17, 2023
77303c6
refactor: introduce `QueueManager` interface
wzieba Nov 17, 2023
be74b28
refactor: introduce `QueueManager` interface
wzieba Nov 17, 2023
a13485c
refactor: introduce `RestClient` interface
wzieba Nov 17, 2023
cb5113c
feat: start `FlushManager` without checking state of stored queue first
wzieba Nov 17, 2023
4b4e471
refactor: pass `onFlush` lambda to `ParselyFlushManager`
wzieba Nov 17, 2023
c9872d4
refactor: rename `SendEvents` to `FlushQueue` and `isDebug` to `skipS…
wzieba Nov 17, 2023
269a9d6
style: improve position of logging statements in `FlushQueue`
wzieba Nov 17, 2023
409c42d
style: return from `FlushQueue` if `skipSendingEvents`
wzieba Nov 17, 2023
95f7a65
style: make variables test-local where possible
wzieba Nov 24, 2023
c32302f
feat: update lock logic on local storage repo
wzieba Nov 24, 2023
2c0ce6e
style: remove unnecessary `else` from `FlushQueue`
wzieba Nov 24, 2023
784a021
feat: do not stop FlushManager on successful flush
wzieba Nov 24, 2023
62cfa12
Merge pull request #92 from Parsely/coroutines-send-event
wzieba Nov 28, 2023
30eb83f
Merge branch 'coroutines' into get_add_key_coroutines
wzieba Nov 29, 2023
0a526b5
Merge branch 'coroutines' into engagement_manager_coroutines
wzieba Nov 29, 2023
9d99164
Merge branch 'engagement_manager_coroutines' into get_add_key_coroutines
wzieba Nov 29, 2023
d282f8b
refactor: make `startTime` a local variable
wzieba Dec 1, 2023
ca714a7
Merge pull request #95 from Parsely/engagement_manager_coroutines
wzieba Dec 4, 2023
988ff4b
Merge branch 'coroutines' into get_add_key_coroutines
wzieba Dec 5, 2023
7533f97
fix: assign advertisement id in AdvertisementIdProvider
wzieba Dec 5, 2023
473dc7e
tests: make SUT test-scoped in AndroidDeviceInfoRepositoryTest
wzieba Dec 5, 2023
0c3102d
chore: add a comment for AdvertisementIdProvider#provide
wzieba Dec 7, 2023
ec44c99
Merge pull request #93 from Parsely/get_add_key_coroutines
wzieba Dec 11, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/readme.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,6 @@ jobs:
if: always()
with:
name: artifact
path: ./parsely/build/reports/*
path: |
./parsely/build/reports/*
./parsely/build/outputs/androidTest-results
21 changes: 3 additions & 18 deletions example/src/main/java/com/example/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,33 +30,18 @@ protected void onCreate(Bundle savedInstanceState) {
// Set debugging to true so we don't actually send things to Parse.ly
ParselyTracker.sharedInstance().setDebug(true);

final TextView queueView = (TextView) findViewById(R.id.queue_size);
queueView.setText(String.format("Queued events: %d", ParselyTracker.sharedInstance().queueSize()));

final TextView storedView = (TextView) findViewById(R.id.stored_size);
storedView.setText(String.format("Stored events: %d", ParselyTracker.sharedInstance().storedEventsCount()));

final TextView intervalView = (TextView) findViewById(R.id.interval);
storedView.setText(String.format("Flush interval: %d", ParselyTracker.sharedInstance().getFlushInterval()));

updateEngagementStrings();

final TextView views[] = new TextView[3];
views[0] = queueView;
views[1] = storedView;
views[2] = intervalView;
final TextView views[] = new TextView[1];
views[0] = intervalView;

final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
TextView[] v = (TextView[]) msg.obj;
TextView qView = v[0];
qView.setText(String.format("Queued events: %d", ParselyTracker.sharedInstance().queueSize()));

TextView sView = v[1];
sView.setText(String.format("Stored events: %d", ParselyTracker.sharedInstance().storedEventsCount()));

TextView iView = v[2];
TextView iView = v[0];
if (ParselyTracker.sharedInstance().flushTimerIsActive()) {
iView.setText(String.format("Flush Interval: %d", ParselyTracker.sharedInstance().getFlushInterval()));
} else {
Expand Down
19 changes: 2 additions & 17 deletions example/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,26 +68,11 @@
android:onClick="trackReset"
android:text="@string/button_reset_video" />

<TextView
android:id="@+id/queue_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/reset_video_button"
android:layout_centerHorizontal="true"
android:text="Queued events: 0" />

<TextView android:id="@+id/stored_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="@id/queue_size"
android:text="Stored events: 0"/>

<TextView android:id="@+id/interval"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="@id/stored_size"
android:layout_below="@id/reset_video_button"
android:text="Flush timer inactive"/>

<TextView
Expand All @@ -107,4 +92,4 @@
android:text="Video is inactive." />


</RelativeLayout>
</RelativeLayout>
3 changes: 3 additions & 0 deletions parsely/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ plugins {

ext {
assertJVersion = '3.24.2'
coroutinesVersion = '1.7.3'
mockWebServerVersion = '4.12.0'
}

Expand Down Expand Up @@ -63,12 +64,14 @@ dependencies {
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.3'
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
implementation 'androidx.lifecycle:lifecycle-process:2.6.2'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"

testImplementation 'org.robolectric:robolectric:4.10.3'
testImplementation 'androidx.test:core:1.5.0'
testImplementation "org.assertj:assertj-core:$assertJVersion"
testImplementation 'junit:junit:4.13.2'
testImplementation "com.squareup.okhttp3:mockwebserver:$mockWebServerVersion"
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion"

androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test:rules:1.5.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import okhttp3.mockwebserver.RecordedRequest
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.within
import org.junit.Test
import org.junit.runner.RunWith

Expand All @@ -49,12 +50,12 @@ class FunctionalTests {
}

/**
* In this scenario, the consumer application tracks more than 50 events-threshold during a flush interval.
* In this scenario, the consumer application tracks 51 events-threshold during a flush interval.
* The SDK will save the events to disk and send them in the next flush interval.
* At the end, when all events are sent, the SDK will delete the content of local storage file.
*/
@Test
fun appTracksEventsAboveQueueSizeLimit() {
fun appTracksEventsDuringTheFlushInterval() {
ActivityScenario.launch(SampleActivity::class.java).use { scenario ->
scenario.onActivity { activity: Activity ->
beforeEach(activity)
Expand All @@ -76,6 +77,47 @@ class FunctionalTests {
}
}

/**
* In this scenario, the consumer app tracks 2 events during the first flush interval.
* Then, we validate, that after flush interval passed the SDK sends the events
* to Parse.ly servers.
*
* Then, the consumer app tracks another event and we validate that the SDK sends the event
* to Parse.ly servers as well.
*/
@Test
fun appFlushesEventsAfterFlushInterval() {
ActivityScenario.launch(SampleActivity::class.java).use { scenario ->
scenario.onActivity { activity: Activity ->
beforeEach(activity)
server.enqueue(MockResponse().setResponseCode(200))
parselyTracker = initializeTracker(activity)

parselyTracker.trackPageview("url", null, null, null)
}

Thread.sleep((defaultFlushInterval / 2).inWholeMilliseconds)

scenario.onActivity {
parselyTracker.trackPageview("url", null, null, null)
}

Thread.sleep((defaultFlushInterval / 2).inWholeMilliseconds)

val firstRequestPayload = server.takeRequest(2000, TimeUnit.MILLISECONDS)?.toMap()
assertThat(firstRequestPayload!!["events"]).hasSize(2)

scenario.onActivity {
parselyTracker.trackPageview("url", null, null, null)
}

Thread.sleep(defaultFlushInterval.inWholeMilliseconds)

val secondRequestPayload = server.takeRequest(2000, TimeUnit.MILLISECONDS)?.toMap()
assertThat(secondRequestPayload!!["events"]).hasSize(1)
}
}

/**
* In this scenario, the consumer application:
* 1. Goes to the background
Expand Down Expand Up @@ -113,6 +155,132 @@ class FunctionalTests {
}
}

/**
* In this scenario we "stress test" the concurrency model to see if we have any conflict during
*
* - Unexpectedly high number of recorded events in small intervals (I/O locking)
* - Scenario in which a request is sent at the same time as new events are recorded
*/
@Test
fun stressTest() {
val eventsToSend = 500

ActivityScenario.launch(SampleActivity::class.java).use { scenario ->
scenario.onActivity { activity: Activity ->
beforeEach(activity)
server.enqueue(MockResponse().setResponseCode(200))
parselyTracker = initializeTracker(activity)

repeat(eventsToSend) {
parselyTracker.trackPageview("url", null, null, null)
}
}

// Wait some time to give events chance to be saved in local data storage
Thread.sleep((defaultFlushInterval * 2).inWholeMilliseconds)

// Catch up to 10 requests. We don't know how many requests the device we test on will
// perform. It's probably more like 1-2, but we're on safe (not flaky) side here.
val requests = (1..10).mapNotNull {
runCatching { server.takeRequest(100, TimeUnit.MILLISECONDS) }.getOrNull()
}.flatMap {
it.toMap()["events"]!!
}

assertThat(requests).hasSize(eventsToSend)
}
}

/**
* In this scenario consumer app starts an engagement session and after 27150 ms,
* it stops the session.
*
* Intervals:
* With current implementation of `HeartbeatIntervalCalculator`, the next intervals are:
* - 10500ms for the first interval
* - 13650ms for the second interval
*
* So after ~27,2s we should observe
* - 2 `heartbeat` events from `startEngagement` + 1 `heartbeat` event caused by `stopEngagement` which is triggered during engagement interval
*
* Time off-differences in assertions are acceptable, because it's a time-sensitive test
*/
@Test
fun engagementManagerTest() {
val engagementUrl = "engagementUrl"
var startTimestamp = Duration.ZERO
val firstInterval = 10500.milliseconds
val secondInterval = 13650.milliseconds
val pauseInterval = 3.seconds
ActivityScenario.launch(SampleActivity::class.java).use { scenario ->
// given
scenario.onActivity { activity: Activity ->
beforeEach(activity)
server.enqueue(MockResponse().setResponseCode(200))
parselyTracker = initializeTracker(activity, flushInterval = 30.seconds)

// when
startTimestamp = System.currentTimeMillis().milliseconds
parselyTracker.startEngagement(engagementUrl, null)
}

Thread.sleep((firstInterval + secondInterval + pauseInterval).inWholeMilliseconds)
parselyTracker.stopEngagement()

// then
val request = server.takeRequest(35, TimeUnit.SECONDS)!!.toMap()["events"]!!

assertThat(
request.sortedBy { it.data.timestamp }
.filter { it.action == "heartbeat" }
).hasSize(3)
.satisfies({
val firstEvent = it[0]
val secondEvent = it[1]
val thirdEvent = it[2]

assertThat(firstEvent.data.timestamp).isCloseTo(
(startTimestamp + firstInterval).inWholeMilliseconds,
within(1.seconds.inWholeMilliseconds)
)
assertThat(firstEvent.totalTime).isCloseTo(
firstInterval.inWholeMilliseconds,
within(100L)
)
assertThat(firstEvent.incremental).isCloseTo(
firstInterval.inWholeSeconds,
within(1L)
)

assertThat(secondEvent.data.timestamp).isCloseTo(
(startTimestamp + firstInterval + secondInterval).inWholeMilliseconds,
within(1.seconds.inWholeMilliseconds)
)
assertThat(secondEvent.totalTime).isCloseTo(
(firstInterval + secondInterval).inWholeMilliseconds,
within(100L)
)
assertThat(secondEvent.incremental).isCloseTo(
secondInterval.inWholeSeconds,
within(1L)
)

assertThat(thirdEvent.data.timestamp).isCloseTo(
(startTimestamp + firstInterval + secondInterval + pauseInterval).inWholeMilliseconds,
within(1.seconds.inWholeMilliseconds)
)
assertThat(thirdEvent.totalTime).isCloseTo(
(firstInterval + secondInterval + pauseInterval).inWholeMilliseconds,
within(100L)
)
assertThat(thirdEvent.incremental).isCloseTo(
(pauseInterval).inWholeSeconds,
within(1L)
)
})
}
}

private fun RecordedRequest.toMap(): Map<String, List<Event>> {
val listType: TypeReference<Map<String, List<Event>>> =
object : TypeReference<Map<String, List<Event>>>() {}
Expand All @@ -123,6 +291,15 @@ class FunctionalTests {
@JsonIgnoreProperties(ignoreUnknown = true)
data class Event(
@JsonProperty("idsite") var idsite: String,
@JsonProperty("action") var action: String,
@JsonProperty("data") var data: ExtraData,
@JsonProperty("tt") var totalTime: Long,
@JsonProperty("inc") var incremental: Long,
)

@JsonIgnoreProperties(ignoreUnknown = true)
data class ExtraData(
@JsonProperty("ts") var timestamp: Long,
)

private val locallyStoredEvents
Expand All @@ -137,19 +314,18 @@ class FunctionalTests {
activity: Activity,
flushInterval: Duration = defaultFlushInterval
): ParselyTracker {
val field: Field = ParselyTracker::class.java.getDeclaredField("ROOT_URL")
field.isAccessible = true
field.set(this, url)
return ParselyTracker.sharedInstance(
siteId, flushInterval.inWholeSeconds.toInt(), activity.application
).apply {
val f: Field = this::class.java.getDeclaredField("ROOT_URL")
f.isAccessible = true
f.set(this, url)
}
)
}

private companion object {
const val siteId = "123"
const val localStorageFileName = "parsely-events.ser"
val defaultFlushInterval = 10.seconds
val defaultFlushInterval = 5.seconds
}

class SampleActivity : Activity()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.parsely.parselyandroid

import android.content.Context
import android.provider.Settings
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

internal class AdvertisementIdProvider(
private val context: Context,
coroutineScope: CoroutineScope
) : IdProvider {

private var adKey: String? = null

init {
coroutineScope.launch {
try {
adKey = AdvertisingIdClient.getAdvertisingIdInfo(context).id
} catch (e: Exception) {
ParselyTracker.PLog("No Google play services or error!")
}
}
}

/**
* @return advertisement id if the coroutine in the constructor finished executing AdvertisingIdClient#getAdvertisingIdInfo
* null otherwise
*/
override fun provide(): String? = adKey
}

internal class AndroidIdProvider(private val context: Context) : IdProvider {
override fun provide(): String? {
val uuid = try {
Settings.Secure.getString(
context.applicationContext.contentResolver,
Settings.Secure.ANDROID_ID
)
} catch (ex: Exception) {
null
}
ParselyTracker.PLog(String.format("Android ID: %s", uuid))
return uuid
}
}

internal fun interface IdProvider {
fun provide(): String?
}
Loading