mirror of
https://github.com/bitfireAT/davx5-ose
synced 2024-10-01 17:00:45 +00:00
Unsubscribe push from unsynced collections (#1011)
* Unsubscribe push from unsynced collections * Remove subscription from DB, too * Subscription: catch HTTP errors
This commit is contained in:
parent
0581417bba
commit
0b9d4cd3b3
|
@ -72,6 +72,9 @@ interface CollectionDao {
|
|||
@Query("SELECT * FROM collection WHERE sync AND supportsWebPush AND pushTopic IS NOT NULL")
|
||||
suspend fun getPushCapableSyncCollections(): List<Collection>
|
||||
|
||||
@Query("SELECT * FROM collection WHERE pushSubscription IS NOT NULL AND NOT sync")
|
||||
suspend fun getPushRegisteredAndNotSyncable(): List<Collection>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
fun insert(collection: Collection): Long
|
||||
|
||||
|
@ -85,7 +88,7 @@ interface CollectionDao {
|
|||
suspend fun updateForceReadOnly(id: Long, forceReadOnly: Boolean)
|
||||
|
||||
@Query("UPDATE collection SET pushSubscription=:pushSubscription, pushSubscriptionCreated=:updatedAt WHERE id=:id")
|
||||
suspend fun updatePushSubscription(id: Long, pushSubscription: String, updatedAt: Long = System.currentTimeMillis())
|
||||
fun updatePushSubscription(id: Long, pushSubscription: String?, updatedAt: Long = System.currentTimeMillis())
|
||||
|
||||
@Query("UPDATE collection SET sync=:sync WHERE id=:id")
|
||||
suspend fun updateSync(id: Long, sync: Boolean)
|
||||
|
|
|
@ -19,6 +19,7 @@ import at.bitfire.dav4jvm.DavResource
|
|||
import at.bitfire.dav4jvm.Property
|
||||
import at.bitfire.dav4jvm.XmlUtils
|
||||
import at.bitfire.dav4jvm.XmlUtils.insertTag
|
||||
import at.bitfire.dav4jvm.exception.DavException
|
||||
import at.bitfire.dav4jvm.property.push.NS_WEBDAV_PUSH
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.db.Collection
|
||||
|
@ -35,11 +36,13 @@ import dagger.hilt.InstallIn
|
|||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.IntoSet
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import java.io.StringWriter
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.logging.Level
|
||||
import java.util.logging.Logger
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -48,7 +51,8 @@ import javax.inject.Inject
|
|||
* To be run as soon as a collection that supports push is changed (selected for sync status
|
||||
* changes, or collection is created, deleted, etc).
|
||||
*
|
||||
* TODO Should run periodically, too. Not required for a first demonstration version.
|
||||
* TODO Should run periodically, too (to refresh registrations that are about to expire).
|
||||
* Not required for a first demonstration version.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
@HiltWorker
|
||||
|
@ -85,7 +89,16 @@ class PushRegistrationWorker @AssistedInject constructor(
|
|||
}
|
||||
|
||||
|
||||
private suspend fun requestPushRegistration(collection: Collection, account: Account, endpoint: String) {
|
||||
override suspend fun doWork(): Result {
|
||||
logger.info("Running push registration worker")
|
||||
|
||||
registerSyncable()
|
||||
unregisterNotSyncable()
|
||||
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
private suspend fun registerPushSubscription(collection: Collection, account: Account, endpoint: String) {
|
||||
val settings = accountSettingsFactory.forAccount(account)
|
||||
|
||||
runInterruptible {
|
||||
|
@ -112,7 +125,7 @@ class PushRegistrationWorker @AssistedInject constructor(
|
|||
|
||||
val xml = writer.toString().toRequestBody(DavResource.MIME_XML)
|
||||
DavCollection(httpClient, collection.url).post(xml) { response ->
|
||||
if (response.isSuccessful) runBlocking {
|
||||
if (response.isSuccessful) {
|
||||
response.header("Location")?.let { subscriptionUrl ->
|
||||
collectionRepository.updatePushSubscription(collection.id, subscriptionUrl)
|
||||
}
|
||||
|
@ -123,23 +136,61 @@ class PushRegistrationWorker @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
logger.info("Running push registration worker")
|
||||
|
||||
private suspend fun registerSyncable() {
|
||||
val endpoint = preferenceRepository.unifiedPushEndpoint()
|
||||
|
||||
// register push subscription for syncable collections
|
||||
if (endpoint != null)
|
||||
for (collection in collectionRepository.getSyncableAndPushCapable()) {
|
||||
for (collection in collectionRepository.getPushCapableAndSyncable()) {
|
||||
logger.info("Registering push for ${collection.url}")
|
||||
val service = serviceRepository.get(collection.serviceId) ?: continue
|
||||
val account = Account(service.accountName, applicationContext.getString(R.string.account_type))
|
||||
|
||||
requestPushRegistration(collection, account, endpoint)
|
||||
serviceRepository.get(collection.serviceId)?.let { service ->
|
||||
val account = Account(service.accountName, applicationContext.getString(R.string.account_type))
|
||||
try {
|
||||
registerPushSubscription(collection, account, endpoint)
|
||||
} catch (e: DavException) {
|
||||
// catch possible per-collection exception so that all collections can be processed
|
||||
logger.log(Level.WARNING, "Couldn't register push for ${collection.url}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
logger.info("No UnifiedPush endpoint configured")
|
||||
}
|
||||
|
||||
return Result.success()
|
||||
private suspend fun unregisterPushSubscription(collection: Collection, account: Account, url: HttpUrl) {
|
||||
val settings = accountSettingsFactory.forAccount(account)
|
||||
|
||||
runInterruptible {
|
||||
HttpClient.Builder(applicationContext, settings)
|
||||
.setForeground(true)
|
||||
.build()
|
||||
.use { client ->
|
||||
val httpClient = client.okHttpClient
|
||||
|
||||
try {
|
||||
DavResource(httpClient, url).delete {
|
||||
// deleted
|
||||
}
|
||||
} catch (e: DavException) {
|
||||
logger.log(Level.WARNING, "Couldn't unregister push for ${collection.url}", e)
|
||||
}
|
||||
|
||||
// remove registration URL from DB in any case
|
||||
collectionRepository.updatePushSubscription(collection.id, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun unregisterNotSyncable() {
|
||||
for (collection in collectionRepository.getPushRegisteredAndNotSyncable()) {
|
||||
logger.info("Unregistering push for ${collection.url}")
|
||||
collection.pushSubscription?.toHttpUrlOrNull()?.let { url ->
|
||||
serviceRepository.get(collection.serviceId)?.let { service ->
|
||||
val account = Account(service.accountName, applicationContext.getString(R.string.account_type))
|
||||
unregisterPushSubscription(collection, account, url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -6,8 +6,6 @@ package at.bitfire.davdroid.repository
|
|||
|
||||
import android.accounts.Account
|
||||
import android.content.Context
|
||||
import android.provider.CalendarContract
|
||||
import android.provider.ContactsContract
|
||||
import at.bitfire.dav4jvm.DavResource
|
||||
import at.bitfire.dav4jvm.XmlUtils
|
||||
import at.bitfire.dav4jvm.XmlUtils.insertTag
|
||||
|
@ -29,7 +27,6 @@ import at.bitfire.davdroid.network.HttpClient
|
|||
import at.bitfire.davdroid.servicedetection.RefreshCollectionsWorker
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import at.bitfire.davdroid.util.DavUtils
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import at.bitfire.ical4android.util.DateUtils
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
|
@ -196,9 +193,12 @@ class DavCollectionRepository @Inject constructor(
|
|||
fun getSyncTaskLists(serviceId: Long) = dao.getSyncTaskLists(serviceId)
|
||||
|
||||
/** Returns all collections that are both selected for synchronization and push-capable. */
|
||||
suspend fun getSyncableAndPushCapable(): List<Collection> =
|
||||
suspend fun getPushCapableAndSyncable(): List<Collection> =
|
||||
dao.getPushCapableSyncCollections()
|
||||
|
||||
suspend fun getPushRegisteredAndNotSyncable(): List<Collection> =
|
||||
dao.getPushRegisteredAndNotSyncable()
|
||||
|
||||
/**
|
||||
* Inserts or updates the collection. On update it will not update flag values ([Collection.sync],
|
||||
* [Collection.forceReadOnly]), but use the values of the already existing collection.
|
||||
|
@ -246,7 +246,7 @@ class DavCollectionRepository @Inject constructor(
|
|||
notifyOnChangeListeners()
|
||||
}
|
||||
|
||||
suspend fun updatePushSubscription(id: Long, subscriptionUrl: String) {
|
||||
fun updatePushSubscription(id: Long, subscriptionUrl: String?) {
|
||||
dao.updatePushSubscription(id, subscriptionUrl)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue