Sync on demand
This commit is contained in:
@@ -1,14 +1,19 @@
|
||||
package org.koitharu.kotatsu.sync
|
||||
|
||||
import androidx.room.InvalidationTracker
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||
import org.koin.dsl.bind
|
||||
import org.koin.dsl.module
|
||||
import org.koitharu.kotatsu.sync.data.SyncAuthApi
|
||||
import org.koitharu.kotatsu.sync.domain.SyncController
|
||||
import org.koitharu.kotatsu.sync.ui.SyncAuthViewModel
|
||||
|
||||
val syncModule
|
||||
get() = module {
|
||||
|
||||
single { SyncController(androidContext()) } bind InvalidationTracker.Observer::class
|
||||
|
||||
factory { SyncAuthApi(androidContext(), get()) }
|
||||
|
||||
viewModel { SyncAuthViewModel(get()) }
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.koitharu.kotatsu.sync.domain
|
||||
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import androidx.room.InvalidationTracker
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
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
|
||||
|
||||
class SyncController(
|
||||
context: Context,
|
||||
) : InvalidationTracker.Observer(arrayOf(TABLE_HISTORY, TABLE_FAVOURITES, TABLE_FAVOURITE_CATEGORIES)) {
|
||||
|
||||
private val am = AccountManager.get(context)
|
||||
private val accountType = context.getString(R.string.account_type_sync)
|
||||
|
||||
override fun onInvalidated(tables: MutableSet<String>) {
|
||||
requestSync(
|
||||
favourites = TABLE_FAVOURITES in tables || TABLE_FAVOURITE_CATEGORIES in tables,
|
||||
history = TABLE_HISTORY in tables,
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun requestFullSync() = withContext(Dispatchers.Default) {
|
||||
requestSyncImpl(favourites = true, history = true)
|
||||
}
|
||||
|
||||
private fun requestSync(favourites: Boolean, history: Boolean) = processLifecycleScope.launch(Dispatchers.Default) {
|
||||
requestSyncImpl(favourites, history)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun requestSyncImpl(favourites: Boolean, history: Boolean) {
|
||||
if (!favourites && !history) {
|
||||
return
|
||||
}
|
||||
val account = peekAccount() ?: return
|
||||
if (!ContentResolver.getMasterSyncAutomatically()) {
|
||||
return
|
||||
}
|
||||
// TODO limit frequency
|
||||
if (favourites) {
|
||||
requestSyncForAuthority(account, AUTHORITY_FAVOURITES)
|
||||
}
|
||||
if (history) {
|
||||
requestSyncForAuthority(account, AUTHORITY_HISTORY)
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestSyncForAuthority(account: Account, authority: String) {
|
||||
if (
|
||||
ContentResolver.getSyncAutomatically(account, AUTHORITY_FAVOURITES) &&
|
||||
!ContentResolver.isSyncActive(account, authority) &&
|
||||
!ContentResolver.isSyncPending(account, authority)
|
||||
) {
|
||||
ContentResolver.requestSync(account, authority, Bundle.EMPTY)
|
||||
}
|
||||
}
|
||||
|
||||
private fun peekAccount(): Account? {
|
||||
return am.getAccountsByType(accountType).firstOrNull()
|
||||
}
|
||||
}
|
||||
@@ -13,17 +13,17 @@ import org.json.JSONObject
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.db.*
|
||||
import org.koitharu.kotatsu.parsers.util.json.mapJSONTo
|
||||
import org.koitharu.kotatsu.parsers.util.parseJson
|
||||
import org.koitharu.kotatsu.sync.data.SyncAuthApi
|
||||
import org.koitharu.kotatsu.sync.data.SyncAuthenticator
|
||||
import org.koitharu.kotatsu.sync.data.SyncInterceptor
|
||||
import org.koitharu.kotatsu.utils.GZipInterceptor
|
||||
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
|
||||
|
||||
private const val AUTHORITY_HISTORY = "org.koitharu.kotatsu.history"
|
||||
private const val AUTHORITY_FAVOURITES = "org.koitharu.kotatsu.favourites"
|
||||
const val AUTHORITY_HISTORY = "org.koitharu.kotatsu.history"
|
||||
const val AUTHORITY_FAVOURITES = "org.koitharu.kotatsu.favourites"
|
||||
|
||||
private const val FIELD_TIMESTAMP = "timestamp"
|
||||
|
||||
@@ -53,7 +53,7 @@ class SyncHelper(
|
||||
.url("$baseUrl/resource/$TABLE_FAVOURITES")
|
||||
.post(data.toRequestBody())
|
||||
.build()
|
||||
val response = httpClient.newCall(request).execute().parseJson()
|
||||
val response = httpClient.newCall(request).execute().parseJsonOrNull() ?: return
|
||||
val timestamp = response.getLong(FIELD_TIMESTAMP)
|
||||
val categoriesResult = upsertFavouriteCategories(response.getJSONArray(TABLE_FAVOURITE_CATEGORIES), timestamp)
|
||||
syncResult.stats.numDeletes += categoriesResult.first().count?.toLong() ?: 0L
|
||||
@@ -71,7 +71,7 @@ class SyncHelper(
|
||||
.url("$baseUrl/resource/$TABLE_HISTORY")
|
||||
.post(data.toRequestBody())
|
||||
.build()
|
||||
val response = httpClient.newCall(request).execute().parseJson()
|
||||
val response = httpClient.newCall(request).execute().parseJsonOrNull() ?: return
|
||||
val result = upsertHistory(
|
||||
json = response.getJSONArray(TABLE_HISTORY),
|
||||
timestamp = response.getLong(FIELD_TIMESTAMP),
|
||||
|
||||
@@ -9,7 +9,7 @@ import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
|
||||
class SyncAuthenticator(private val context: Context) : AbstractAccountAuthenticator(context) {
|
||||
class SyncAccountAuthenticator(private val context: Context) : AbstractAccountAuthenticator(context) {
|
||||
|
||||
override fun editProperties(response: AccountAuthenticatorResponse?, accountType: String?): Bundle? = null
|
||||
|
||||
@@ -52,7 +52,6 @@ class SyncAuthenticator(private val context: Context) : AbstractAccountAuthentic
|
||||
} else {
|
||||
val intent = Intent(context, SyncAuthActivity::class.java)
|
||||
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response)
|
||||
// intent.putExtra(SyncAuthActivity.EXTRA_TOKEN_TYPE, authTokenType)
|
||||
val bundle = Bundle()
|
||||
if (options != null) {
|
||||
bundle.putAll(options)
|
||||
@@ -6,11 +6,11 @@ import android.os.IBinder
|
||||
|
||||
class SyncAuthenticatorService : Service() {
|
||||
|
||||
private lateinit var authenticator: SyncAuthenticator
|
||||
private lateinit var authenticator: SyncAccountAuthenticator
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
authenticator = SyncAuthenticator(this)
|
||||
authenticator = SyncAccountAuthenticator(this)
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
|
||||
Reference in New Issue
Block a user