From f126e7e46238eba210580b4b1134eacebb0d0de3 Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Thu, 3 Dec 2020 11:47:09 -0600 Subject: [PATCH] Push local Etebase changes before fetching updates --- .../java/org/tasks/etebase/EtebaseClient.kt | 10 -- .../org/tasks/etebase/EtebaseSynchronizer.kt | 111 +++++++++--------- 2 files changed, 57 insertions(+), 64 deletions(-) diff --git a/app/src/main/java/org/tasks/etebase/EtebaseClient.kt b/app/src/main/java/org/tasks/etebase/EtebaseClient.kt index a3271fb34..ed9f3c8bf 100644 --- a/app/src/main/java/org/tasks/etebase/EtebaseClient.kt +++ b/app/src/main/java/org/tasks/etebase/EtebaseClient.kt @@ -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 { 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!!) diff --git a/app/src/main/java/org/tasks/etebase/EtebaseSynchronizer.kt b/app/src/main/java/org/tasks/etebase/EtebaseSynchronizer.kt index 618fd647d..a6d0f5161 100644 --- a/app/src/main/java/org/tasks/etebase/EtebaseSynchronizer.kt +++ b/app/src/main/java/org/tasks/etebase/EtebaseSynchronizer.kt @@ -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() - 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() 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>, - dirty: MutableSet) { - for (item in items.second) { + items: List, + 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) + } } } \ No newline at end of file