Skip to content

Commit

Permalink
Merged and resolved conficts
Browse files Browse the repository at this point in the history
  • Loading branch information
semsudin-tafilovic committed Dec 2, 2024
2 parents 15d1030 + 8f5beab commit 6080266
Show file tree
Hide file tree
Showing 12 changed files with 515 additions and 167 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Change Log
## Version 5.1.11 *(2024-12-02)*
* Bug fixes and performance improvements

# Change Log
## Version 5.1.10 *(2024-09-25)*
* Changed exception tracking to include 'ct=webtrekk_ignore' as a query parameter in a tracking requests.
Expand Down
13 changes: 9 additions & 4 deletions android-sdk/src/main/java/webtrekk/android/sdk/core/Scheduler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,25 @@ internal interface Scheduler {
* @param repeatInterval the periodic time that will be used by [WorkManager] to send the requests from the cache to the server.
* @param constraints the [WorkManager] constraints that will be applied on that worker.
*/
fun scheduleSendRequests(repeatInterval: Long, constraints: Constraints)
suspend fun scheduleSendRequests(repeatInterval: Long, constraints: Constraints)

/**
* A one time worker that will be used to send all available requests in the cache to the server, then cleaning up the cache. Used for Opt out.
*/
fun sendRequestsThenCleanUp()
suspend fun sendRequestsThenCleanUp()

/**
* A worker that is scheduled to clean up the requests in the cache that are already sent to the server.
*/
fun scheduleCleanUp()
suspend fun scheduleCleanUp()

/**
* Cancel current periodic worker that is used to send the request every n times. Used for Opt out.
*/
fun cancelScheduleSendRequests()
suspend fun cancelScheduleSendRequests()

/**
* Delete records about completed or canceled works
*/
suspend fun pruneWorks()
}
128 changes: 69 additions & 59 deletions android-sdk/src/main/java/webtrekk/android/sdk/core/SchedulerImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,18 @@ import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.OutOfQuotaPolicy
import androidx.work.PeriodicWorkRequest
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkInfo
import androidx.work.WorkManager
import androidx.work.WorkQuery
import androidx.work.await
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import webtrekk.android.sdk.Config
import webtrekk.android.sdk.domain.worker.CleanUpWorker
import webtrekk.android.sdk.domain.worker.SendRequestsWorker
import webtrekk.android.sdk.module.AppModule
import webtrekk.android.sdk.util.webtrekkLogger
import java.util.concurrent.TimeUnit

Expand All @@ -51,65 +55,71 @@ internal class SchedulerImpl(
private val config: Config,
) :
Scheduler {
private val mutex = Mutex()

override fun scheduleSendRequests(
override suspend fun scheduleSendRequests(
repeatInterval: Long,
constraints: Constraints,
) {
webtrekkLogger.debug("SEND WORKER - scheduleSendRequests")
synchronized(mutex) {
val data = Data.Builder().apply {
putStringArray("trackIds", config.trackIds.toTypedArray())
putString("trackDomain", config.trackDomain)
}.build()

val workBuilder = PeriodicWorkRequest.Builder(
SendRequestsWorker::class.java,
repeatInterval,
TimeUnit.MINUTES
).setConstraints(constraints)
.setInitialDelay(0, TimeUnit.MILLISECONDS)
.setInputData(data)
.addTag(SendRequestsWorker.TAG)

val sendRequestsWorker = workBuilder.build()

workManager.enqueueUniquePeriodicWork(
SEND_REQUESTS_WORKER,
ExistingPeriodicWorkPolicy.UPDATE,
sendRequestsWorker
)
withContext(AppModule.dispatchers.mainDispatcher) {
webtrekkLogger.debug("SEND WORKER - scheduleSendRequests")
mutex.withLock {
val data = Data.Builder().apply {
putStringArray("trackIds", config.trackIds.toTypedArray())
putString("trackDomain", config.trackDomain)
}.build()

val workBuilder = PeriodicWorkRequestBuilder<SendRequestsWorker>(
repeatInterval,
TimeUnit.MINUTES
).setConstraints(constraints)
.setInitialDelay(0, TimeUnit.MILLISECONDS)
.setInputData(data)

val sendRequestsWorker = workBuilder.build()

workManager.enqueueUniquePeriodicWork(
SEND_REQUESTS_WORKER,
ExistingPeriodicWorkPolicy.UPDATE,
sendRequestsWorker
)
}
}
}

// To be changed to clean up after executing the requests
override fun scheduleCleanUp() {
val data = Data.Builder().apply {
putStringArray("trackIds", config.trackIds.toTypedArray())
putString("trackDomain", config.trackDomain)
}.build()

val cleanWorkBuilder = PeriodicWorkRequestBuilder<CleanUpWorker>(60, TimeUnit.MINUTES)
.addTag(CleanUpWorker.TAG)
.setInitialDelay(5, TimeUnit.MINUTES)
.setInputData(data)

workManager.enqueueUniquePeriodicWork(
CLEAN_UP_WORKER,
ExistingPeriodicWorkPolicy.UPDATE,
cleanWorkBuilder.build()
)
override suspend fun scheduleCleanUp() {
withContext(AppModule.dispatchers.mainDispatcher) {
mutex.withLock {
val data = Data.Builder().apply {
putStringArray("trackIds", config.trackIds.toTypedArray())
putString("trackDomain", config.trackDomain)
}.build()

val cleanWorkBuilder =
PeriodicWorkRequestBuilder<CleanUpWorker>(
60,
TimeUnit.MINUTES
).setInitialDelay(5, TimeUnit.MINUTES)
.setInputData(data)

workManager.enqueueUniquePeriodicWork(
CLEAN_UP_WORKER,
ExistingPeriodicWorkPolicy.UPDATE,
cleanWorkBuilder.build()
)
}
}
}

override fun sendRequestsThenCleanUp() {
webtrekkLogger.debug("SEND WORKER - sendRequestsThenCleanUp")
synchronized(mutex) {
// check if SendRequestsWorker already running as periodic work request
val future = workManager.getWorkInfosForUniqueWork(ONE_TIME_REQUEST)
val workers = future.get()
if (workers.none { it.state in listOf(WorkInfo.State.RUNNING) }) {
scheduleSendAndCleanWorkers()
override suspend fun sendRequestsThenCleanUp() {
withContext(AppModule.dispatchers.mainDispatcher) {
webtrekkLogger.debug("SEND WORKER - sendRequestsThenCleanUp")
mutex.withLock {
// check if SendRequestsWorker already running as periodic work request
val query = WorkQuery.fromTags(SendRequestsWorker::class.java.name)
val workers = workManager.getWorkInfos(query).get()
if (workers.none { it.state in listOf(WorkInfo.State.RUNNING) }) {
scheduleSendAndCleanWorkers()
}
}
}
}
Expand All @@ -122,33 +132,33 @@ internal class SchedulerImpl(

val sendWorkBuilder = OneTimeWorkRequest.Builder(SendRequestsWorker::class.java)
.setInputData(data)
.addTag(SendRequestsWorker.TAG)

val cleanWorkBuilder = OneTimeWorkRequest.Builder(CleanUpWorker::class.java)
.setInputData(data)
.addTag(CleanUpWorker.TAG)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
sendWorkBuilder.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
cleanWorkBuilder.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
}

workManager.beginUniqueWork(
ONE_TIME_REQUEST,
ExistingWorkPolicy.REPLACE,
sendWorkBuilder.build()
)
workManager
.beginUniqueWork(ONE_TIME_REQUEST, ExistingWorkPolicy.REPLACE, sendWorkBuilder.build())
.then(cleanWorkBuilder.build())
.enqueue()
}

override fun cancelScheduleSendRequests() {
override suspend fun cancelScheduleSendRequests() {
workManager.cancelUniqueWork(CLEAN_UP_WORKER)
workManager.cancelUniqueWork(ONE_TIME_REQUEST)
workManager.cancelUniqueWork(SEND_REQUESTS_WORKER)
}

override suspend fun pruneWorks() {
workManager.pruneWork().await()
}

companion object {
private val mutex = Mutex()
const val SEND_REQUESTS_WORKER = "send_requests_worker"
const val ONE_TIME_REQUEST = "one_time_request_send_and_clean"
const val CLEAN_UP_WORKER = "CleanUpWorker"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -546,8 +546,14 @@ constructor() : Webtrekk(),
if (config.exceptionLogLevel.isUncaughtAllowed()) {
initUncaughtExceptionTracking()
}
// Scheduling the workers for cleaning up the current cache, and setting up the periodic worker for sending the requests.

// Clean all finished works (SUCCEEDED, FAILED, CANCELED)
scheduler.pruneWorks()

// Scheduling the workers for clearing sent requests.
scheduler.scheduleCleanUp()

// Scheduling the periodic worker for sending requests
scheduler.scheduleSendRequests(
repeatInterval = config.requestsInterval,
constraints = config.workManagerConstraints
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import android.content.Context
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import webtrekk.android.sdk.core.AppState
import webtrekk.android.sdk.core.Scheduler
Expand All @@ -52,7 +53,7 @@ internal class Optout(
private val clearTrackRequests: ClearTrackRequests
) : ExternalInteractor<Optout.Params> {

private val _job = Job()
private val _job = SupervisorJob()
override val scope =
CoroutineScope(_job + coroutineContext) // Starting a new job with context of the parent.

Expand All @@ -62,23 +63,23 @@ internal class Optout(
private val logger by lazy { AppModule.logger }

override fun invoke(invokeParams: Params, coroutineDispatchers: CoroutineDispatchers) {
// Store the opt out value in the shared preferences.
sessions.optOut(invokeParams.optOutValue)
scope.launch(
context = coroutineDispatchers.ioDispatcher + coroutineExceptionHandler(
logger
),
start = CoroutineStart.DEFAULT
) {
// Store the opt out value in the shared preferences.
sessions.optOut(invokeParams.optOutValue)

// If opt out value is set to true, then disable tracking data, cancel all work manager workers and delete or send then delete current data in the data base.
if (invokeParams.optOutValue) {
appState.disable(invokeParams.context) // Disable the auto track
scheduler.cancelScheduleSendRequests() // Cancel the work manager workers
// If sendCurrentData is true, then one time worker will send current data requests to the server, then clean up the data base.
if (invokeParams.sendCurrentData) {
scheduler.sendRequestsThenCleanUp()
} else {
scope.launch(
context = coroutineDispatchers.ioDispatcher + coroutineExceptionHandler(
logger
),
start = CoroutineStart.DEFAULT
) {
// If opt out value is set to true, then disable tracking data, cancel all work manager workers and delete or send then delete current data in the data base.
if (invokeParams.optOutValue) {
appState.disable(invokeParams.context) // Disable the auto track
scheduler.cancelScheduleSendRequests() // Cancel the work manager workers
// If sendCurrentData is true, then one time worker will send current data requests to the server, then clean up the data base.
if (invokeParams.sendCurrentData) {
scheduler.sendRequestsThenCleanUp()
} else {
clearTrackRequests(ClearTrackRequests.Params(trackRequests = emptyList()))
.onSuccess { logger.debug("Cleared all track requests, opt out is active") }
.onFailure { logger.error("Failed to clear the track requests while opting out") }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ package webtrekk.android.sdk.domain.external
import android.content.Context
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import webtrekk.android.sdk.Config
import webtrekk.android.sdk.core.Scheduler
import webtrekk.android.sdk.domain.ExternalInteractor
Expand All @@ -42,12 +44,14 @@ internal class SendAndClean(
private val scheduler: Scheduler
) : ExternalInteractor<SendAndClean.Params> {

private val _job = Job()
private val _job = SupervisorJob()
override val scope =
CoroutineScope(_job + coroutineContext) // Starting a new job with context of the parent.

override fun invoke(invokeParams: Params, coroutineDispatchers: CoroutineDispatchers) {
scheduler.sendRequestsThenCleanUp()
scope.launch {
scheduler.sendRequestsThenCleanUp()
}
}

/**
Expand Down
Loading

0 comments on commit 6080266

Please sign in to comment.