Schedule sync with rate limit

This commit is contained in:
Koitharu
2022-05-16 10:36:36 +03:00
parent 318486d62b
commit a9f3ab259a
4 changed files with 53 additions and 11 deletions

View File

@@ -5,15 +5,16 @@ import android.accounts.AccountManager
import android.content.ContentResolver
import android.content.Context
import android.os.Bundle
import android.util.ArrayMap
import androidx.room.InvalidationTracker
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.*
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.db.TABLE_FAVOURITES
import org.koitharu.kotatsu.core.db.TABLE_FAVOURITE_CATEGORIES
import org.koitharu.kotatsu.core.db.TABLE_HISTORY
import org.koitharu.kotatsu.utils.ext.processLifecycleScope
import java.util.concurrent.TimeUnit
class SyncController(
context: Context,
@@ -21,6 +22,12 @@ class SyncController(
private val am = AccountManager.get(context)
private val accountType = context.getString(R.string.account_type_sync)
private val minSyncInterval = if (BuildConfig.DEBUG) {
TimeUnit.SECONDS.toMillis(5)
} else {
TimeUnit.MINUTES.toMillis(4)
}
private val jobs = ArrayMap<String, Job>(2)
override fun onInvalidated(tables: MutableSet<String>) {
requestSync(
@@ -29,6 +36,17 @@ class SyncController(
)
}
fun getLastSync(account: Account, authority: String): Long {
val key = "last_sync_" + authority.substringAfterLast('.')
val rawValue = am.getUserData(account, key) ?: return 0L
return rawValue.toLongOrNull() ?: 0L
}
fun setLastSync(account: Account, authority: String, time: Long) {
val key = "last_sync_" + authority.substringAfterLast('.')
am.setUserData(account, key, time.toString())
}
suspend fun requestFullSync() = withContext(Dispatchers.Default) {
requestSyncImpl(favourites = true, history = true)
}
@@ -46,22 +64,41 @@ class SyncController(
if (!ContentResolver.getMasterSyncAutomatically()) {
return
}
// TODO limit frequency
if (favourites) {
requestSyncForAuthority(account, AUTHORITY_FAVOURITES)
scheduleSync(account, AUTHORITY_FAVOURITES)
}
if (history) {
requestSyncForAuthority(account, AUTHORITY_HISTORY)
scheduleSync(account, AUTHORITY_HISTORY)
}
}
private fun requestSyncForAuthority(account: Account, authority: String) {
private fun scheduleSync(account: Account, authority: String) {
if (
ContentResolver.getSyncAutomatically(account, AUTHORITY_FAVOURITES) &&
!ContentResolver.isSyncActive(account, authority) &&
!ContentResolver.isSyncPending(account, authority)
!ContentResolver.getSyncAutomatically(account, AUTHORITY_FAVOURITES) ||
ContentResolver.isSyncActive(account, authority) ||
ContentResolver.isSyncPending(account, authority)
) {
return
}
val job = jobs[authority]
if (job?.isActive == true) {
// already scheduled
return
}
val lastSyncTime = getLastSync(account, authority)
val timeLeft = System.currentTimeMillis() - lastSyncTime + minSyncInterval
if (timeLeft <= 0) {
jobs.remove(authority)
ContentResolver.requestSync(account, authority, Bundle.EMPTY)
} else {
jobs[authority] = processLifecycleScope.launch(Dispatchers.Default) {
try {
delay(timeLeft)
} finally {
// run even if scope cancelled
ContentResolver.requestSync(account, authority, Bundle.EMPTY)
}
}
}
}

View File

@@ -6,6 +6,7 @@ import android.content.ContentProviderClient
import android.content.Context
import android.content.SyncResult
import android.os.Bundle
import org.koitharu.kotatsu.sync.domain.SyncController
import org.koitharu.kotatsu.sync.domain.SyncHelper
import org.koitharu.kotatsu.utils.ext.onError
@@ -21,6 +22,7 @@ class FavouritesSyncAdapter(context: Context) : AbstractThreadedSyncAdapter(cont
val syncHelper = SyncHelper(context, account, provider)
runCatching {
syncHelper.syncFavourites(syncResult)
SyncController(context).setLastSync(account, authority, System.currentTimeMillis())
}.onFailure(syncResult::onError)
}
}

View File

@@ -6,6 +6,7 @@ import android.content.ContentProviderClient
import android.content.Context
import android.content.SyncResult
import android.os.Bundle
import org.koitharu.kotatsu.sync.domain.SyncController
import org.koitharu.kotatsu.sync.domain.SyncHelper
import org.koitharu.kotatsu.utils.ext.onError
@@ -21,6 +22,7 @@ class HistorySyncAdapter(context: Context) : AbstractThreadedSyncAdapter(context
val syncHelper = SyncHelper(context, account, provider)
runCatching {
syncHelper.syncHistory(syncResult)
SyncController(context).setLastSync(account, authority, System.currentTimeMillis())
}.onFailure(syncResult::onError)
}
}

View File

@@ -15,13 +15,13 @@ import androidx.core.app.ActivityOptionsCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.coroutineScope
import androidx.work.CoroutineWorker
import kotlin.coroutines.resume
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import okio.IOException
import org.json.JSONException
import org.koitharu.kotatsu.BuildConfig
import kotlin.coroutines.resume
val Context.connectivityManager: ConnectivityManager
get() = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
@@ -82,4 +82,5 @@ fun SyncResult.onError(error: Throwable) {
is JSONException -> stats.numParseExceptions++
else -> if (BuildConfig.DEBUG) throw error
}
error.printStackTraceDebug()
}