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