Batch writes to opentasks-provider

pull/1066/head
Alex Baker 4 years ago
parent 0d1738212f
commit 5e6cf64b7b

@ -89,7 +89,7 @@ class TaskMoverTest : InjectingTestCase() {
createTasks(1) createTasks(1)
caldavDao.insert(newCaldavTask(with(CaldavTaskMaker.TASK, 1L), with(CALENDAR, "1"))) caldavDao.insert(newCaldavTask(with(CaldavTaskMaker.TASK, 1L), with(CALENDAR, "1")))
moveToCaldavList("2", 1) moveToCaldavList("2", 1)
val deleted = caldavDao.getDeleted("1") val deleted = caldavDao.getMoved("1")
assertEquals(1, deleted.size.toLong()) assertEquals(1, deleted.size.toLong())
assertEquals(1, deleted[0].task) assertEquals(1, deleted[0].task)
assertTrue(deleted[0].deleted > 0) assertTrue(deleted[0].deleted > 0)
@ -114,7 +114,7 @@ class TaskMoverTest : InjectingTestCase() {
with(CALENDAR, "1"), with(CALENDAR, "1"),
with(REMOTE_PARENT, "b")))) with(REMOTE_PARENT, "b"))))
moveToCaldavList("2", 1) moveToCaldavList("2", 1)
val deleted = caldavDao.getDeleted("1") val deleted = caldavDao.getMoved("1")
assertEquals(3, deleted.size.toLong()) assertEquals(3, deleted.size.toLong())
val task = caldavDao.getTask(3) val task = caldavDao.getTask(3)
assertEquals("2", task!!.calendar) assertEquals("2", task!!.calendar)
@ -249,7 +249,7 @@ class TaskMoverTest : InjectingTestCase() {
createTasks(1) createTasks(1)
caldavDao.insert(newCaldavTask(with(CaldavTaskMaker.TASK, 1L), with(CALENDAR, "1"))) caldavDao.insert(newCaldavTask(with(CaldavTaskMaker.TASK, 1L), with(CALENDAR, "1")))
moveToCaldavList("1", 1) moveToCaldavList("1", 1)
assertTrue(caldavDao.getDeleted("1").isEmpty()) assertTrue(caldavDao.getMoved("1").isEmpty())
assertEquals(1, caldavDao.getTasks(1).size.toLong()) assertEquals(1, caldavDao.getTasks(1).size.toLong())
} }

@ -226,7 +226,7 @@ class CaldavSynchronizer @Inject constructor(
private suspend fun pushLocalChanges( private suspend fun pushLocalChanges(
caldavCalendar: CaldavCalendar, httpClient: OkHttpClient, httpUrl: HttpUrl) { caldavCalendar: CaldavCalendar, httpClient: OkHttpClient, httpUrl: HttpUrl) {
for (task in caldavDao.getDeleted(caldavCalendar.uuid!!)) { for (task in caldavDao.getMoved(caldavCalendar.uuid!!)) {
deleteRemoteResource(httpClient, httpUrl, task) deleteRemoteResource(httpClient, httpUrl, task)
} }
for (task in taskDao.getCaldavTasksToPush(caldavCalendar.uuid!!)) { for (task in taskDao.getCaldavTasksToPush(caldavCalendar.uuid!!)) {

@ -107,8 +107,11 @@ abstract class CaldavDao {
@Delete @Delete
abstract suspend fun delete(caldavTask: CaldavTask) abstract suspend fun delete(caldavTask: CaldavTask)
@Delete
abstract suspend fun delete(caldavTasks: List<CaldavTask>)
@Query("SELECT * FROM caldav_tasks WHERE cd_deleted > 0 AND cd_calendar = :calendar") @Query("SELECT * FROM caldav_tasks WHERE cd_deleted > 0 AND cd_calendar = :calendar")
abstract suspend fun getDeleted(calendar: String): List<CaldavTask> abstract suspend fun getMoved(calendar: String): List<CaldavTask>
@Query("UPDATE caldav_tasks SET cd_deleted = :now WHERE cd_task IN (:tasks)") @Query("UPDATE caldav_tasks SET cd_deleted = :now WHERE cd_task IN (:tasks)")
abstract suspend fun markDeleted(tasks: List<Long>, now: Long = currentTimeMillis()) abstract suspend fun markDeleted(tasks: List<Long>, now: Long = currentTimeMillis())
@ -138,8 +141,11 @@ SELECT EXISTS(SELECT 1
""") """)
abstract suspend fun isAccountType(id: Long, type: Int): Boolean abstract suspend fun isAccountType(id: Long, type: Int): Boolean
suspend fun getTasks(taskIds: List<Long>): List<CaldavTask> =
taskIds.chunkedMap { getTasksInternal(it) }
@Query("SELECT * FROM caldav_tasks WHERE cd_task in (:taskIds) AND cd_deleted = 0") @Query("SELECT * FROM caldav_tasks WHERE cd_task in (:taskIds) AND cd_deleted = 0")
abstract suspend fun getTasks(taskIds: List<Long>): List<CaldavTask> internal abstract suspend fun getTasksInternal(taskIds: List<Long>): List<CaldavTask>
@Query("SELECT task.*, caldav_task.* FROM tasks AS task " @Query("SELECT task.*, caldav_task.* FROM tasks AS task "
+ "INNER JOIN caldav_tasks AS caldav_task ON _id = cd_task " + "INNER JOIN caldav_tasks AS caldav_task ON _id = cd_task "

@ -1,5 +1,7 @@
package org.tasks.data package org.tasks.data
import android.content.ContentProviderOperation
import android.content.ContentProviderOperation.*
import android.content.ContentValues import android.content.ContentValues
import android.content.Context import android.content.Context
import android.database.Cursor import android.database.Cursor
@ -21,6 +23,8 @@ class OpenTaskDao @Inject constructor(@ApplicationContext context: Context) {
private val cr = context.contentResolver private val cr = context.contentResolver
val authority = context.getString(R.string.opentasks_authority) val authority = context.getString(R.string.opentasks_authority)
private val tasks = Tasks.getContentUri(authority)
private val properties = Properties.getContentUri(authority)
suspend fun accounts(): List<String> = getLists().map { it.account!! }.distinct() suspend fun accounts(): List<String> = getLists().map { it.account!! }.distinct()
@ -53,7 +57,7 @@ class OpenTaskDao @Inject constructor(@ApplicationContext context: Context) {
suspend fun getEtags(listId: Long): List<Pair<String, String>> = withContext(Dispatchers.IO) { suspend fun getEtags(listId: Long): List<Pair<String, String>> = withContext(Dispatchers.IO) {
val items = ArrayList<Pair<String, String>>() val items = ArrayList<Pair<String, String>>()
cr.query( cr.query(
Tasks.getContentUri(authority), tasks,
arrayOf(Tasks._SYNC_ID, "version"), arrayOf(Tasks._SYNC_ID, "version"),
"${Tasks.LIST_ID} = $listId", "${Tasks.LIST_ID} = $listId",
null, null,
@ -65,37 +69,58 @@ class OpenTaskDao @Inject constructor(@ApplicationContext context: Context) {
items items
} }
suspend fun delete(listId: Long, item: String): Int = withContext(Dispatchers.IO) { fun delete(listId: Long, item: String): ContentProviderOperation =
cr.delete( newDelete(tasks)
Tasks.getContentUri(authority), .withSelection(
"${Tasks.LIST_ID} = $listId AND ${Tasks._SYNC_ID} = '$item'", "${Tasks.LIST_ID} = $listId AND ${Tasks._SYNC_ID} = '$item'",
null) null)
} .build()
suspend fun getId(uid: String?): Long = fun insert(values: ContentValues): ContentProviderOperation =
uid?.let { newInsert(tasks)
withContext(Dispatchers.IO) { .withValues(values)
cr.query( .build()
Tasks.getContentUri(authority),
arrayOf(Tasks._ID), fun update(listId: Long, item: String, values: ContentValues): ContentProviderOperation =
"${Tasks._UID} = '$uid'", newUpdate(tasks)
null, .withSelection(
null)?.use { "${Tasks.LIST_ID} = $listId AND ${Tasks._SYNC_ID} = '$item'",
if (it.moveToFirst()) { null)
it.getLong(Tasks._ID) .withValues(values)
} else { .build()
Timber.e("No task with uid=$uid")
null suspend fun getId(listId: Long, uid: String?): Long? =
uid
?.takeIf { it.isNotBlank() }
?.let {
withContext(Dispatchers.IO) {
cr.query(
tasks,
arrayOf(Tasks._ID),
"${Tasks.LIST_ID} = $listId AND ${Tasks._UID} = '$uid'",
null,
null)?.use {
if (it.moveToFirst()) {
it.getLong(Tasks._ID)
} else {
null
}
}
} }
} }
} ?: Timber.e("No task with uid=$uid").let { null }
} ?: 0L
suspend fun batch(operations: List<ContentProviderOperation>) = withContext(Dispatchers.IO) {
operations.chunked(OPENTASK_BATCH_LIMIT).forEach {
cr.applyBatch(authority, ArrayList(it))
}
}
suspend fun getTags(caldavTask: CaldavTask): List<String> = withContext(Dispatchers.IO) { suspend fun getTags(listId: Long, caldavTask: CaldavTask): List<String> = withContext(Dispatchers.IO) {
val id = getId(caldavTask.remoteId) val id = getId(listId, caldavTask.remoteId)
val tags = ArrayList<String>() val tags = ArrayList<String>()
cr.query( cr.query(
Properties.getContentUri(authority), properties,
arrayOf(Properties.DATA1), arrayOf(Properties.DATA1),
"${Properties.TASK_ID} = $id AND ${Properties.MIMETYPE} = '${Category.CONTENT_ITEM_TYPE}'", "${Properties.TASK_ID} = $id AND ${Properties.MIMETYPE} = '${Category.CONTENT_ITEM_TYPE}'",
null, null,
@ -107,25 +132,29 @@ class OpenTaskDao @Inject constructor(@ApplicationContext context: Context) {
return@withContext tags return@withContext tags
} }
suspend fun setTags(caldavTask: CaldavTask, tags: List<String>) = withContext(Dispatchers.IO) { fun setTags(id: Long, tags: List<String>): List<ContentProviderOperation> {
val id = getId(caldavTask.remoteId) val delete = listOf(
cr.delete( newDelete(properties)
Properties.getContentUri(authority), .withSelection(
"${Properties.TASK_ID} = $id AND ${Properties.MIMETYPE} = '${Category.CONTENT_ITEM_TYPE}'", "${Properties.TASK_ID} = $id AND ${Properties.MIMETYPE} = '${Category.CONTENT_ITEM_TYPE}'",
null) null)
tags.forEach { .build())
cr.insert(Properties.getContentUri(authority), ContentValues().apply { val inserts = tags.map {
put(Category.MIMETYPE, Category.CONTENT_ITEM_TYPE) newInsert(properties)
put(Category.TASK_ID, id) .withValues(ContentValues().apply {
put(Category.CATEGORY_NAME, it) put(Category.MIMETYPE, Category.CONTENT_ITEM_TYPE)
}) put(Category.TASK_ID, id)
put(Category.CATEGORY_NAME, it)
})
.build()
} }
return delete + inserts
} }
suspend fun getRemoteOrder(caldavTask: CaldavTask): Long? = withContext(Dispatchers.IO) { suspend fun getRemoteOrder(listId: Long, caldavTask: CaldavTask): Long? = withContext(Dispatchers.IO) {
val id = getId(caldavTask.remoteId) val id = getId(listId, caldavTask.remoteId) ?: return@withContext null
cr.query( cr.query(
Properties.getContentUri(authority), properties,
arrayOf(Properties.DATA0), arrayOf(Properties.DATA0),
"${Properties.TASK_ID} = $id AND ${Properties.MIMETYPE} = '${UnknownProperty.CONTENT_ITEM_TYPE}' AND ${Properties.DATA0} LIKE '%$APPLE_SORT_ORDER%'", "${Properties.TASK_ID} = $id AND ${Properties.MIMETYPE} = '${UnknownProperty.CONTENT_ITEM_TYPE}' AND ${Properties.DATA0} LIKE '%$APPLE_SORT_ORDER%'",
null, null,
@ -142,37 +171,53 @@ class OpenTaskDao @Inject constructor(@ApplicationContext context: Context) {
return@withContext null return@withContext null
} }
suspend fun setRemoteOrder(caldavTask: CaldavTask) = withContext(Dispatchers.IO) { fun setRemoteOrder(id: Long, caldavTask: CaldavTask): List<ContentProviderOperation> {
val id = getId(caldavTask.remoteId) val operations = ArrayList<ContentProviderOperation>()
cr.delete( operations.add(
Properties.getContentUri(authority), newDelete(properties)
"${Properties.TASK_ID} = $id AND ${Properties.MIMETYPE} = '${UnknownProperty.CONTENT_ITEM_TYPE}' AND ${Properties.DATA0} LIKE '%$APPLE_SORT_ORDER%'", .withSelection(
null) "${Properties.TASK_ID} = $id AND ${Properties.MIMETYPE} = '${UnknownProperty.CONTENT_ITEM_TYPE}' AND ${Properties.DATA0} LIKE '%$APPLE_SORT_ORDER%'",
null)
.build())
caldavTask.order?.let { caldavTask.order?.let {
cr.insert(Properties.getContentUri(authority), ContentValues().apply { operations.add(
put(Properties.MIMETYPE, UnknownProperty.CONTENT_ITEM_TYPE) newInsert(properties)
put(Properties.TASK_ID, id) .withValues(ContentValues().apply {
put(Properties.DATA0, UnknownProperty.toJsonString(XProperty(APPLE_SORT_ORDER, it.toString()))) put(Properties.MIMETYPE, UnknownProperty.CONTENT_ITEM_TYPE)
}) put(Properties.TASK_ID, id)
put(Properties.DATA0, UnknownProperty.toJsonString(XProperty(APPLE_SORT_ORDER, it.toString())))
})
.build())
} }
return operations
} }
suspend fun updateParent(caldavTask: CaldavTask) = withContext(Dispatchers.IO) { fun updateParent(id: Long, parent: Long?): List<ContentProviderOperation> {
caldavTask.remoteParent val operations = ArrayList<ContentProviderOperation>()
?.takeIf { it.isNotBlank() } operations.add(
?.let { newDelete(properties)
cr.insert(Properties.getContentUri(authority), ContentValues().apply { .withSelection(
put(Relation.MIMETYPE, Relation.CONTENT_ITEM_TYPE) "${Properties.TASK_ID} = $id AND ${Properties.MIMETYPE} = '${Relation.CONTENT_ITEM_TYPE}' AND ${Relation.RELATED_TYPE} = ${Relation.RELTYPE_PARENT}",
put(Relation.TASK_ID, getId(caldavTask.remoteId)) null
put(Relation.RELATED_TYPE, Relation.RELTYPE_PARENT) )
put(Relation.RELATED_ID, getId(caldavTask.remoteParent)) .build())
}) parent?.let {
} operations.add(
newInsert(properties)
.withValues(ContentValues().apply {
put(Relation.MIMETYPE, Relation.CONTENT_ITEM_TYPE)
put(Relation.TASK_ID, id)
put(Relation.RELATED_TYPE, Relation.RELTYPE_PARENT)
put(Relation.RELATED_ID, parent)
})
.build())
}
return operations
} }
suspend fun getParent(id: Long): String? = withContext(Dispatchers.IO) { suspend fun getParent(id: Long): String? = withContext(Dispatchers.IO) {
cr.query( cr.query(
Properties.getContentUri(authority), properties,
arrayOf(Relation.RELATED_UID), arrayOf(Relation.RELATED_UID),
"${Relation.TASK_ID} = $id AND ${Properties.MIMETYPE} = '${Relation.CONTENT_ITEM_TYPE}' AND ${Relation.RELATED_TYPE} = ${Relation.RELTYPE_PARENT}", "${Relation.TASK_ID} = $id AND ${Properties.MIMETYPE} = '${Relation.CONTENT_ITEM_TYPE}' AND ${Relation.RELATED_TYPE} = ${Relation.RELTYPE_PARENT}",
null, null,
@ -186,6 +231,7 @@ class OpenTaskDao @Inject constructor(@ApplicationContext context: Context) {
} }
companion object { companion object {
private const val OPENTASK_BATCH_LIMIT = 500
const val ACCOUNT_TYPE_DAVx5 = "bitfire.at.davdroid" const val ACCOUNT_TYPE_DAVx5 = "bitfire.at.davdroid"
const val ACCOUNT_TYPE_ETESYNC = "com.etesync.syncadapter" const val ACCOUNT_TYPE_ETESYNC = "com.etesync.syncadapter"

@ -68,6 +68,7 @@ abstract class TaskDao(private val database: Database) {
FROM tasks FROM tasks
INNER JOIN caldav_tasks ON tasks._id = caldav_tasks.cd_task INNER JOIN caldav_tasks ON tasks._id = caldav_tasks.cd_task
WHERE caldav_tasks.cd_calendar = :calendar WHERE caldav_tasks.cd_calendar = :calendar
AND cd_deleted = 0
AND (tasks.modified > caldav_tasks.cd_last_sync OR caldav_tasks.cd_last_sync = 0) AND (tasks.modified > caldav_tasks.cd_last_sync OR caldav_tasks.cd_last_sync = 0)
ORDER BY created""") ORDER BY created""")
abstract suspend fun getCaldavTasksToPush(calendar: String): List<Task> abstract suspend fun getCaldavTasksToPush(calendar: String): List<Task>

@ -144,7 +144,7 @@ class EteSynchronizer @Inject constructor(
Timber.d("%s up to date", caldavCalendar.name) Timber.d("%s up to date", caldavCalendar.name)
} }
val changes: MutableList<SyncEntry> = ArrayList() val changes: MutableList<SyncEntry> = ArrayList()
for (task in caldavDao.getDeleted(caldavCalendar.uuid!!)) { for (task in caldavDao.getMoved(caldavCalendar.uuid!!)) {
val vtodo = task.vtodo val vtodo = task.vtodo
if (!isNullOrEmpty(vtodo)) { if (!isNullOrEmpty(vtodo)) {
changes.add(SyncEntry(vtodo!!, SyncEntry.Actions.DELETE)) changes.add(SyncEntry(vtodo!!, SyncEntry.Actions.DELETE))

@ -1,5 +1,6 @@
package org.tasks.opentasks package org.tasks.opentasks
import android.content.ContentProviderOperation
import android.content.ContentValues import android.content.ContentValues
import android.content.Context import android.content.Context
import android.database.Cursor import android.database.Cursor
@ -13,8 +14,6 @@ import com.todoroo.astrid.helper.UUIDHelper
import com.todoroo.astrid.service.TaskCreator import com.todoroo.astrid.service.TaskCreator
import com.todoroo.astrid.service.TaskDeleter import com.todoroo.astrid.service.TaskDeleter
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import net.fortuna.ical4j.model.Date import net.fortuna.ical4j.model.Date
import net.fortuna.ical4j.model.property.Geo import net.fortuna.ical4j.model.property.Geo
import net.fortuna.ical4j.model.property.RRule import net.fortuna.ical4j.model.property.RRule
@ -104,22 +103,38 @@ class OpenTasksSynchronizer @Inject constructor(
private suspend fun sync(calendar: CaldavCalendar, ctag: String?, listId: Long) { private suspend fun sync(calendar: CaldavCalendar, ctag: String?, listId: Long) {
Timber.d("SYNC $calendar") Timber.d("SYNC $calendar")
caldavDao.getDeleted(calendar.uuid!!).forEach { val moved = caldavDao.getMoved(calendar.uuid!!)
openTaskDao.delete(listId, it.`object`!!) val (deleted, updated) =
caldavDao.delete(it) taskDao.getCaldavTasksToPush(calendar.uuid!!).partition { it.isDeleted }
}
(moved + deleted.map(Task::id).let { caldavDao.getTasks(it) })
.mapNotNull { it.`object` }
.map { openTaskDao.delete(listId, it) }
.let { openTaskDao.batch(it) }
caldavDao.delete(moved)
taskDeleter.delete(deleted.map { it.id })
taskDao openTaskDao.batch(updated.mapNotNull { toOperation(it, listId) })
.getCaldavTasksToPush(calendar.uuid!!)
.mapNotNull { push(it, listId) } val caldavTasks = updated.let { caldavDao.getTasks(it.map(Task::id)) }
.forEach { openTaskDao.batch(caldavTasks.flatMap {
val tags = tagDataDao.getTagDataForTask(it.task).mapNotNull(TagData::name) val id = openTaskDao
openTaskDao.setTags(it, tags) .getId(listId, it.remoteId)
openTaskDao.setRemoteOrder(it) ?: return@flatMap emptyList<ContentProviderOperation>()
openTaskDao.updateParent(it) val tags = tagDataDao.getTagDataForTask(it.task).mapNotNull(TagData::name)
it.lastSync = currentTimeMillis() val parent = openTaskDao.getId(listId, it.remoteParent)
openTaskDao.setTags(id, tags)
.plus(openTaskDao.setRemoteOrder(id, it))
.plus(openTaskDao.updateParent(id, parent))
})
caldavTasks
.takeIf { it.isNotEmpty() }
?.let {
val lastSync = currentTimeMillis()
caldavTasks.forEach { t -> t.lastSync = lastSync }
caldavDao.update(it) caldavDao.update(it)
Timber.d("SENT $it") Timber.d("SENT ${it.joinToString("\n")}")
} }
ctag?.let { ctag?.let {
@ -132,7 +147,9 @@ class OpenTasksSynchronizer @Inject constructor(
val etags = openTaskDao.getEtags(listId) val etags = openTaskDao.getEtags(listId)
etags.forEach { (syncId, etag) -> etags.forEach { (syncId, etag) ->
val caldavTask = caldavDao.getTask(calendar.uuid!!, syncId) val caldavTask = caldavDao.getTask(calendar.uuid!!, syncId)
applyChanges(calendar, listId, syncId, etag, caldavTask) if (caldavTask?.etag == null || caldavTask.etag != etag) {
applyChanges(calendar, listId, syncId, etag, caldavTask)
}
} }
removeDeleted(calendar.uuid!!, etags.map { it.first }) removeDeleted(calendar.uuid!!, etags.map { it.first })
@ -163,13 +180,8 @@ class OpenTasksSynchronizer @Inject constructor(
} }
} }
private suspend fun push(task: Task, listId: Long): CaldavTask? = withContext(Dispatchers.IO) { private suspend fun toOperation(task: Task, listId: Long): ContentProviderOperation? {
val caldavTask = caldavDao.getTask(task.id) ?: return@withContext null val caldavTask = caldavDao.getTask(task.id) ?: return null
if (task.isDeleted) {
openTaskDao.delete(listId, caldavTask.`object`!!)
taskDeleter.delete(task)
return@withContext null
}
val values = ContentValues() val values = ContentValues()
values.put(Tasks._SYNC_ID, caldavTask.`object`) values.put(Tasks._SYNC_ID, caldavTask.`object`)
values.put(Tasks.LIST_ID, listId) values.put(Tasks.LIST_ID, listId)
@ -213,26 +225,17 @@ class OpenTasksSynchronizer @Inject constructor(
values.put(Tasks.PRIORITY, toRemote(it.getInt(Tasks.PRIORITY), task.priority)) values.put(Tasks.PRIORITY, toRemote(it.getInt(Tasks.PRIORITY), task.priority))
true true
} ?: false } ?: false
try { return try {
if (existing) { if (existing) {
val updated = cr.update( openTaskDao.update(listId, caldavTask.`object`!!, values)
Tasks.getContentUri(openTaskDao.authority),
values,
"${Tasks.LIST_ID} = $listId AND ${Tasks._SYNC_ID} = '${caldavTask.`object`}'",
null)
if (updated <= 0) {
throw Exception("update failed")
}
} else { } else {
values.put(Tasks._UID, caldavTask.remoteId) values.put(Tasks._UID, caldavTask.remoteId)
values.put(Tasks.PRIORITY, toRemote(task.priority, task.priority)) values.put(Tasks.PRIORITY, toRemote(task.priority, task.priority))
cr.insert(Tasks.getContentUri(openTaskDao.authority), values) openTaskDao.insert(values)
?: throw Exception("insert returned null")
} }
caldavTask
} catch (e: Exception) { } catch (e: Exception) {
firebase.reportException(e) firebase.reportException(e)
return@withContext null null
} }
} }
@ -262,32 +265,30 @@ class OpenTasksSynchronizer @Inject constructor(
task = taskDao.fetch(existing.task)!! task = taskDao.fetch(existing.task)!!
caldavTask = existing caldavTask = existing
} }
if (caldavTask.etag == null || caldavTask.etag != etag) { task.title = it.getString(Tasks.TITLE)
task.title = it.getString(Tasks.TITLE) task.priority = CaldavConverter.fromRemote(it.getInt(Tasks.PRIORITY))
task.priority = CaldavConverter.fromRemote(it.getInt(Tasks.PRIORITY)) task.completionDate = it.getLong(Tasks.COMPLETED)
task.completionDate = it.getLong(Tasks.COMPLETED) task.notes = it.getString(Tasks.DESCRIPTION)
task.notes = it.getString(Tasks.DESCRIPTION) task.modificationDate = currentTimeMillis()
task.modificationDate = currentTimeMillis() task.creationDate = it.getLong(Tasks.CREATED).toLocal()
task.creationDate = it.getLong(Tasks.CREATED).toLocal() task.setDueDateAdjustingHideUntil(it.getLong(Tasks.DUE).let { due ->
task.setDueDateAdjustingHideUntil(it.getLong(Tasks.DUE).let { due -> when {
when { due == 0L -> 0
due == 0L -> 0 it.getBoolean(Tasks.IS_ALLDAY) ->
it.getBoolean(Tasks.IS_ALLDAY) -> Task.createDueDate(URGENCY_SPECIFIC_DAY, due - DateTime(due).offset)
Task.createDueDate(URGENCY_SPECIFIC_DAY, due - DateTime(due).offset) else -> Task.createDueDate(URGENCY_SPECIFIC_DAY_TIME, due)
else -> Task.createDueDate(URGENCY_SPECIFIC_DAY_TIME, due) }
} })
}) iCalendar.setPlace(task.id, it.getString(Tasks.GEO).toGeo())
iCalendar.setPlace(task.id, it.getString(Tasks.GEO).toGeo()) task.setRecurrence(it.getString(Tasks.RRULE).toRRule())
task.setRecurrence(it.getString(Tasks.RRULE).toRRule()) task.suppressSync()
task.suppressSync() task.suppressRefresh()
task.suppressRefresh() taskDao.save(task)
taskDao.save(task) caldavTask.lastSync = DateUtilities.now() + 1000L
caldavTask.lastSync = DateUtilities.now() + 1000L caldavTask.etag = etag
caldavTask.etag = etag val tags = openTaskDao.getTags(listId, caldavTask)
}
val tags = openTaskDao.getTags(caldavTask)
tagDao.applyTags(task, tagDataDao, iCalendar.getTags(tags)) tagDao.applyTags(task, tagDataDao, iCalendar.getTags(tags))
caldavTask.order = openTaskDao.getRemoteOrder(caldavTask) caldavTask.order = openTaskDao.getRemoteOrder(listId, caldavTask)
caldavTask.remoteParent = openTaskDao.getParent(it.getLong(Tasks._ID)) caldavTask.remoteParent = openTaskDao.getParent(it.getLong(Tasks._ID))
if (caldavTask.id == Task.NO_ID) { if (caldavTask.id == Task.NO_ID) {
caldavTask.id = caldavDao.insert(caldavTask) caldavTask.id = caldavDao.insert(caldavTask)

Loading…
Cancel
Save