Gc database
This commit is contained in:
@@ -7,9 +7,13 @@ import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.ArrayMap
|
||||
import androidx.room.InvalidationTracker
|
||||
import androidx.room.withTransaction
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
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_FAVOURITE_CATEGORIES
|
||||
import org.koitharu.kotatsu.core.db.TABLE_HISTORY
|
||||
@@ -27,7 +31,10 @@ class SyncController(
|
||||
} else {
|
||||
TimeUnit.MINUTES.toMillis(4)
|
||||
}
|
||||
private val mutex = Mutex()
|
||||
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>) {
|
||||
requestSync(
|
||||
@@ -48,36 +55,49 @@ class SyncController(
|
||||
}
|
||||
|
||||
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) {
|
||||
requestSyncImpl(favourites, history)
|
||||
requestSyncImpl(favourites = favourites, history = history, db = null)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun requestSyncImpl(favourites: Boolean, history: Boolean) {
|
||||
private suspend fun requestSyncImpl(favourites: Boolean, history: Boolean, db: MangaDatabase?) = mutex.withLock {
|
||||
if (!favourites && !history) {
|
||||
return
|
||||
}
|
||||
val account = peekAccount() ?: return
|
||||
if (!ContentResolver.getMasterSyncAutomatically()) {
|
||||
val account = peekAccount()
|
||||
if (account == null || !ContentResolver.getMasterSyncAutomatically()) {
|
||||
db?.gc(favourites, history)
|
||||
return
|
||||
}
|
||||
var gcHistory = false
|
||||
var gcFavourites = false
|
||||
if (favourites) {
|
||||
scheduleSync(account, AUTHORITY_FAVOURITES)
|
||||
if (ContentResolver.getSyncAutomatically(account, AUTHORITY_FAVOURITES)) {
|
||||
scheduleSync(account, AUTHORITY_FAVOURITES)
|
||||
} else {
|
||||
gcFavourites = true
|
||||
}
|
||||
}
|
||||
if (history) {
|
||||
scheduleSync(account, AUTHORITY_HISTORY)
|
||||
if (ContentResolver.getSyncAutomatically(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) {
|
||||
if (
|
||||
!ContentResolver.getSyncAutomatically(account, AUTHORITY_FAVOURITES) ||
|
||||
ContentResolver.isSyncActive(account, authority) ||
|
||||
ContentResolver.isSyncPending(account, authority)
|
||||
) {
|
||||
if (ContentResolver.isSyncActive(account, authority) || ContentResolver.isSyncPending(account, authority)) {
|
||||
return
|
||||
}
|
||||
val job = jobs[authority]
|
||||
@@ -105,4 +125,15 @@ class SyncController(
|
||||
private fun peekAccount(): Account? {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import org.koitharu.kotatsu.utils.ext.parseJsonOrNull
|
||||
import org.koitharu.kotatsu.utils.ext.toContentValues
|
||||
import org.koitharu.kotatsu.utils.ext.toJson
|
||||
import org.koitharu.kotatsu.utils.ext.toRequestBody
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
const val AUTHORITY_HISTORY = "org.koitharu.kotatsu.history"
|
||||
const val AUTHORITY_FAVOURITES = "org.koitharu.kotatsu.favourites"
|
||||
@@ -43,6 +44,8 @@ class SyncHelper(
|
||||
.addInterceptor(GZipInterceptor())
|
||||
.build()
|
||||
private val baseUrl = context.getString(R.string.url_sync_server)
|
||||
private val defaultGcPeriod: Long // gc period if sync enabled
|
||||
get() = TimeUnit.DAYS.toMillis(4)
|
||||
|
||||
fun syncFavourites(syncResult: SyncResult) {
|
||||
val data = JSONObject()
|
||||
@@ -61,6 +64,7 @@ class SyncHelper(
|
||||
val favouritesResult = upsertFavourites(response.getJSONArray(TABLE_FAVOURITES), timestamp)
|
||||
syncResult.stats.numDeletes += favouritesResult.first().count?.toLong() ?: 0L
|
||||
syncResult.stats.numInserts += favouritesResult.drop(1).sumOf { it.count?.toLong() ?: 0L }
|
||||
gcFavourites()
|
||||
}
|
||||
|
||||
fun syncHistory(syncResult: SyncResult) {
|
||||
@@ -78,6 +82,7 @@ class SyncHelper(
|
||||
)
|
||||
syncResult.stats.numDeletes += result.first().count?.toLong() ?: 0L
|
||||
syncResult.stats.numInserts += result.drop(1).sumOf { it.count?.toLong() ?: 0L }
|
||||
gcHistory()
|
||||
}
|
||||
|
||||
private fun upsertHistory(json: JSONArray, timestamp: Long): Array<ContentProviderResult> {
|
||||
@@ -238,6 +243,21 @@ class SyncHelper(
|
||||
return requireNotNull(tag)
|
||||
}
|
||||
|
||||
private fun gcFavourites() {
|
||||
val deletedAt = System.currentTimeMillis() - defaultGcPeriod
|
||||
val selection = "deleted_at != 0 AND deleted_at < ?"
|
||||
val args = arrayOf(deletedAt.toString())
|
||||
provider.delete(uri(AUTHORITY_FAVOURITES, TABLE_FAVOURITES), selection, args)
|
||||
provider.delete(uri(AUTHORITY_FAVOURITES, TABLE_FAVOURITE_CATEGORIES), selection, args)
|
||||
}
|
||||
|
||||
private fun gcHistory() {
|
||||
val deletedAt = System.currentTimeMillis() - defaultGcPeriod
|
||||
val selection = "deleted_at != 0 AND deleted_at < ?"
|
||||
val args = arrayOf(deletedAt.toString())
|
||||
provider.delete(uri(AUTHORITY_HISTORY, TABLE_HISTORY), selection, args)
|
||||
}
|
||||
|
||||
private fun ContentProviderClient.query(authority: String, table: String): Cursor {
|
||||
val uri = uri(authority, table)
|
||||
return query(uri, null, null, null, null)
|
||||
|
||||
Reference in New Issue
Block a user