|
|
|
@ -7,9 +7,13 @@ import android.content.Context
|
|
|
|
import android.os.Bundle
|
|
|
|
import android.os.Bundle
|
|
|
|
import android.util.ArrayMap
|
|
|
|
import android.util.ArrayMap
|
|
|
|
import androidx.room.InvalidationTracker
|
|
|
|
import androidx.room.InvalidationTracker
|
|
|
|
|
|
|
|
import androidx.room.withTransaction
|
|
|
|
import kotlinx.coroutines.*
|
|
|
|
import kotlinx.coroutines.*
|
|
|
|
|
|
|
|
import kotlinx.coroutines.sync.Mutex
|
|
|
|
|
|
|
|
import kotlinx.coroutines.sync.withLock
|
|
|
|
import org.koitharu.kotatsu.BuildConfig
|
|
|
|
import org.koitharu.kotatsu.BuildConfig
|
|
|
|
import org.koitharu.kotatsu.R
|
|
|
|
import org.koitharu.kotatsu.R
|
|
|
|
|
|
|
|
import org.koitharu.kotatsu.core.db.MangaDatabase
|
|
|
|
import org.koitharu.kotatsu.core.db.TABLE_FAVOURITES
|
|
|
|
import org.koitharu.kotatsu.core.db.TABLE_FAVOURITES
|
|
|
|
import org.koitharu.kotatsu.core.db.TABLE_FAVOURITE_CATEGORIES
|
|
|
|
import org.koitharu.kotatsu.core.db.TABLE_FAVOURITE_CATEGORIES
|
|
|
|
import org.koitharu.kotatsu.core.db.TABLE_HISTORY
|
|
|
|
import org.koitharu.kotatsu.core.db.TABLE_HISTORY
|
|
|
|
@ -27,7 +31,10 @@ class SyncController(
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
TimeUnit.MINUTES.toMillis(4)
|
|
|
|
TimeUnit.MINUTES.toMillis(4)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private val mutex = Mutex()
|
|
|
|
private val jobs = ArrayMap<String, Job>(2)
|
|
|
|
private val jobs = ArrayMap<String, Job>(2)
|
|
|
|
|
|
|
|
private val defaultGcPeriod: Long // gc period if sync disabled
|
|
|
|
|
|
|
|
get() = TimeUnit.DAYS.toMillis(2)
|
|
|
|
|
|
|
|
|
|
|
|
override fun onInvalidated(tables: MutableSet<String>) {
|
|
|
|
override fun onInvalidated(tables: MutableSet<String>) {
|
|
|
|
requestSync(
|
|
|
|
requestSync(
|
|
|
|
@ -48,36 +55,49 @@ class SyncController(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
suspend fun requestFullSync() = withContext(Dispatchers.Default) {
|
|
|
|
suspend fun requestFullSync() = withContext(Dispatchers.Default) {
|
|
|
|
requestSyncImpl(favourites = true, history = true)
|
|
|
|
requestSyncImpl(favourites = true, history = true, db = null)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
suspend fun requestFullSyncAndGc(database: MangaDatabase) = withContext(Dispatchers.Default) {
|
|
|
|
|
|
|
|
requestSyncImpl(favourites = true, history = true, db = database)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun requestSync(favourites: Boolean, history: Boolean) = processLifecycleScope.launch(Dispatchers.Default) {
|
|
|
|
private fun requestSync(favourites: Boolean, history: Boolean) = processLifecycleScope.launch(Dispatchers.Default) {
|
|
|
|
requestSyncImpl(favourites, history)
|
|
|
|
requestSyncImpl(favourites = favourites, history = history, db = null)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
private suspend fun requestSyncImpl(favourites: Boolean, history: Boolean, db: MangaDatabase?) = mutex.withLock {
|
|
|
|
private fun requestSyncImpl(favourites: Boolean, history: Boolean) {
|
|
|
|
|
|
|
|
if (!favourites && !history) {
|
|
|
|
if (!favourites && !history) {
|
|
|
|
return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
val account = peekAccount() ?: return
|
|
|
|
val account = peekAccount()
|
|
|
|
if (!ContentResolver.getMasterSyncAutomatically()) {
|
|
|
|
if (account == null || !ContentResolver.getMasterSyncAutomatically()) {
|
|
|
|
|
|
|
|
db?.gc(favourites, history)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var gcHistory = false
|
|
|
|
|
|
|
|
var gcFavourites = false
|
|
|
|
if (favourites) {
|
|
|
|
if (favourites) {
|
|
|
|
|
|
|
|
if (ContentResolver.getSyncAutomatically(account, AUTHORITY_FAVOURITES)) {
|
|
|
|
scheduleSync(account, AUTHORITY_FAVOURITES)
|
|
|
|
scheduleSync(account, AUTHORITY_FAVOURITES)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
gcFavourites = true
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (history) {
|
|
|
|
if (history) {
|
|
|
|
|
|
|
|
if (ContentResolver.getSyncAutomatically(account, AUTHORITY_HISTORY)) {
|
|
|
|
scheduleSync(account, AUTHORITY_HISTORY)
|
|
|
|
scheduleSync(account, AUTHORITY_HISTORY)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
gcHistory = true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (db != null && (gcHistory || gcFavourites)) {
|
|
|
|
|
|
|
|
db.gc(gcFavourites, gcHistory)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun scheduleSync(account: Account, authority: String) {
|
|
|
|
private fun scheduleSync(account: Account, authority: String) {
|
|
|
|
if (
|
|
|
|
if (ContentResolver.isSyncActive(account, authority) || ContentResolver.isSyncPending(account, authority)) {
|
|
|
|
!ContentResolver.getSyncAutomatically(account, AUTHORITY_FAVOURITES) ||
|
|
|
|
|
|
|
|
ContentResolver.isSyncActive(account, authority) ||
|
|
|
|
|
|
|
|
ContentResolver.isSyncPending(account, authority)
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
val job = jobs[authority]
|
|
|
|
val job = jobs[authority]
|
|
|
|
@ -105,4 +125,15 @@ class SyncController(
|
|
|
|
private fun peekAccount(): Account? {
|
|
|
|
private fun peekAccount(): Account? {
|
|
|
|
return am.getAccountsByType(accountType).firstOrNull()
|
|
|
|
return am.getAccountsByType(accountType).firstOrNull()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private suspend fun MangaDatabase.gc(favourites: Boolean, history: Boolean) = withTransaction {
|
|
|
|
|
|
|
|
val deletedAt = System.currentTimeMillis() - defaultGcPeriod
|
|
|
|
|
|
|
|
if (history) {
|
|
|
|
|
|
|
|
historyDao.gc(deletedAt)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (favourites) {
|
|
|
|
|
|
|
|
favouritesDao.gc(deletedAt)
|
|
|
|
|
|
|
|
favouriteCategoriesDao.gc(deletedAt)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|