Push local Etebase changes before fetching updates

pull/1244/head
Alex Baker 5 years ago
parent 58520bcc29
commit f126e7e462

@ -3,9 +3,6 @@ package org.tasks.etebase
import android.content.Context
import com.etebase.client.*
import com.etebase.client.Collection
import com.etesync.journalmanager.Exceptions
import com.etesync.journalmanager.Exceptions.IntegrityException
import com.etesync.journalmanager.Exceptions.VersionTooNewException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.tasks.data.CaldavCalendar
@ -13,7 +10,6 @@ import org.tasks.data.CaldavDao
import org.tasks.data.CaldavTask
import org.tasks.time.DateTimeUtils.currentTimeMillis
import timber.log.Timber
import java.io.IOException
class EtebaseClient(
private val context: Context,
@ -23,10 +19,8 @@ class EtebaseClient(
) {
private val cache = EtebaseLocalCache.getInstance(context, username)
@Throws(IOException::class, Exceptions.HttpException::class)
fun getSession(): String = etebase.save(null)
@Throws(Exceptions.HttpException::class)
suspend fun getCollections(): List<Collection> {
val collectionManager = etebase.collectionManager
var stoken: String? = cache.loadStoken()
@ -49,7 +43,6 @@ class EtebaseClient(
return cache.collectionList(collectionManager)
}
@Throws(IntegrityException::class, Exceptions.HttpException::class, VersionTooNewException::class)
suspend fun fetchItems(
collection: Collection,
calendar: CaldavCalendar,
@ -118,20 +111,17 @@ class EtebaseClient(
}
}
@Throws(VersionTooNewException::class, IntegrityException::class, Exceptions.HttpException::class)
suspend fun makeCollection(name: String, color: Int) =
etebase
.collectionManager
.create(TYPE_TASKS, ItemMetadata(), "")
.let { setAndUpload(it, name, color) }
@Throws(VersionTooNewException::class, IntegrityException::class, Exceptions.HttpException::class)
suspend fun updateCollection(calendar: CaldavCalendar, name: String, color: Int) =
cache
.collectionGet(etebase.collectionManager, calendar.url!!)
.let { setAndUpload(it, name, color) }
@Throws(Exceptions.HttpException::class)
suspend fun deleteCollection(calendar: CaldavCalendar) =
cache
.collectionGet(etebase.collectionManager, calendar.url!!)

@ -5,9 +5,8 @@ import android.graphics.Color
import at.bitfire.ical4android.ICalendar.Companion.prodId
import com.etebase.client.Collection
import com.etebase.client.Item
import com.etesync.journalmanager.Exceptions
import com.etesync.journalmanager.Exceptions.IntegrityException
import com.etesync.journalmanager.Exceptions.VersionTooNewException
import com.etebase.client.exceptions.*
import com.todoroo.andlib.utility.DateUtilities.now
import com.todoroo.astrid.helper.UUIDHelper
import com.todoroo.astrid.service.TaskDeleter
import dagger.hilt.android.qualifiers.ApplicationContext
@ -22,10 +21,8 @@ import org.tasks.caldav.iCalendar.Companion.fromVtodo
import org.tasks.data.CaldavAccount
import org.tasks.data.CaldavCalendar
import org.tasks.data.CaldavDao
import org.tasks.data.CaldavTaskContainer
import org.tasks.time.DateTimeUtils.currentTimeMillis
import timber.log.Timber
import java.security.KeyManagementException
import java.security.NoSuchAlgorithmException
import java.util.*
import javax.inject.Inject
@ -56,20 +53,19 @@ class EtebaseSynchronizer @Inject constructor(
}
try {
synchronize(account)
} catch (e: KeyManagementException) {
setError(account, e.message)
} catch (e: NoSuchAlgorithmException) {
setError(account, e.message)
} catch (e: Exceptions.HttpException) {
setError(account, e.message)
} catch (e: IntegrityException) {
setError(account, e.message)
} catch (e: VersionTooNewException) {
setError(account, e.message)
} catch (e: ConnectionException) {
setError(account, e)
} catch (e: PermissionDeniedException) {
setError(account, e)
} catch (e: ServerErrorException) {
setError(account, e)
} catch (e: TemporaryServerErrorException) {
setError(account, e)
} catch (e: UnauthorizedException) {
setError(account, e)
}
}
@Throws(KeyManagementException::class, NoSuchAlgorithmException::class, Exceptions.HttpException::class, IntegrityException::class, VersionTooNewException::class)
private suspend fun synchronize(account: CaldavAccount) {
val client = clientProvider.forAccount(account)
val collections = client.getCollections()
@ -104,6 +100,9 @@ class EtebaseSynchronizer @Inject constructor(
setError(account, "")
}
private suspend fun setError(account: CaldavAccount, e: Throwable) =
setError(account, e.message)
private suspend fun setError(account: CaldavAccount, message: String?) {
account.error = message
caldavDao.update(account)
@ -113,34 +112,41 @@ class EtebaseSynchronizer @Inject constructor(
}
}
@Throws(IntegrityException::class, Exceptions.HttpException::class, VersionTooNewException::class)
private suspend fun sync(
client: EtebaseClient,
caldavCalendar: CaldavCalendar,
collection: Collection
) {
Timber.d("sync(%s)", caldavCalendar)
val localChanges = HashMap<String?, CaldavTaskContainer>()
for (task in caldavDao.getCaldavTasksToPush(caldavCalendar.uuid!!)) {
localChanges[task.remoteId] = task
}
val remoteCtag = collection.stoken
if (isNullOrEmpty(remoteCtag) || remoteCtag != caldavCalendar.ctag) {
Timber.d("${caldavCalendar.name}: Applying remote changes")
client.fetchItems(collection, caldavCalendar) {
applyEntries(caldavCalendar, it, localChanges.keys)
client.updateCache(collection, it.second)
}
} else {
pushLocalChanges(client, caldavCalendar, collection)
val localCtag = caldavCalendar.ctag
if (localCtag != null && localCtag == collection.stoken) {
Timber.d("${caldavCalendar.name} up to date")
return
}
client.fetchItems(collection, caldavCalendar) { (stoken, items) ->
applyEntries(caldavCalendar, items, stoken)
client.updateCache(collection, items)
}
Timber.d("UPDATE %s", caldavCalendar)
caldavDao.update(caldavCalendar)
caldavDao.updateParents(caldavCalendar.uuid!!)
localBroadcastManager.broadcastRefresh()
}
private suspend fun pushLocalChanges(
client: EtebaseClient,
caldavCalendar: CaldavCalendar,
collection: Collection
) {
val changes = ArrayList<Item>()
for (caldavTask in caldavDao.getMoved(caldavCalendar.uuid!!)) {
client.deleteItem(collection, caldavTask)
?.let { changes.add(it) }
?: caldavDao.delete(caldavTask)
}
for (change in localChanges.values) {
val syncTime = now()
for (change in caldavDao.getCaldavTasksToPush(caldavCalendar.uuid!!)) {
val task = change.task
val caldavTask = change.caldavTask
if (task.isDeleted) {
@ -148,35 +154,30 @@ class EtebaseSynchronizer @Inject constructor(
?.let { changes.add(it) }
?: taskDeleter.delete(task)
} else {
changes.add(client.updateItem(
collection,
caldavTask,
iCal.toVtodo(caldavTask, task)
))
changes.add(
client.updateItem(collection, caldavTask, iCal.toVtodo(caldavTask, task))
)
}
}
if (changes.isNotEmpty()) {
client.uploadChanges(collection, changes)
applyEntries(caldavCalendar, Pair(caldavCalendar.ctag, changes), HashSet())
applyEntries(caldavCalendar, changes, syncTime = syncTime, isLocalChange = true)
client.updateCache(collection, changes)
}
Timber.d("UPDATE %s", caldavCalendar)
caldavDao.update(caldavCalendar)
caldavDao.updateParents(caldavCalendar.uuid!!)
localBroadcastManager.broadcastRefresh()
}
private suspend fun applyEntries(
caldavCalendar: CaldavCalendar,
items: Pair<String?, List<Item>>,
dirty: MutableSet<String?>) {
for (item in items.second) {
items: List<Item>,
stoken: String? = null,
syncTime: Long = currentTimeMillis(),
isLocalChange: Boolean = false) {
for (item in items) {
val vtodo = item.contentString
val task = fromVtodo(vtodo) ?: continue
val remoteId = task.uid
val caldavTask = caldavDao.getTaskByRemoteId(caldavCalendar.uuid!!, remoteId!!)
if (item.isDeleted) {
dirty.remove(remoteId)
if (caldavTask != null) {
if (caldavTask.isDeleted()) {
caldavDao.delete(caldavTask)
@ -184,18 +185,20 @@ class EtebaseSynchronizer @Inject constructor(
taskDeleter.delete(caldavTask.task)
}
}
} else if (isLocalChange) {
caldavTask?.let {
it.vtodo = vtodo
it.lastSync = syncTime
caldavDao.update(it)
}
} else {
caldavTask?.`object` = item.uid
if (dirty.contains(remoteId)) {
caldavTask!!.vtodo = vtodo
caldavDao.update(caldavTask)
} else {
iCal.fromVtodo(caldavCalendar, caldavTask, task, vtodo, item.uid, null)
}
iCal.fromVtodo(caldavCalendar, caldavTask, task, vtodo, item.uid, null)
}
}
caldavCalendar.ctag = items.first
Timber.d("Setting stoken to ${caldavCalendar.ctag}")
caldavDao.update(caldavCalendar)
stoken?.let {
caldavCalendar.ctag = it
caldavDao.update(caldavCalendar)
}
}
}
Loading…
Cancel
Save