Use AndroidTask to read OpenTask data

pull/1297/head
Alex Baker 5 years ago
parent e9c0bcfff3
commit 0f70ac0f5d

@ -142,7 +142,7 @@ val googleplayImplementation by configurations
dependencies { dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.1") coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.1")
implementation("com.gitlab.bitfireAT:dav4jvm:2.1.1") implementation("com.gitlab.bitfireAT:dav4jvm:2.1.1")
implementation("com.gitlab.abaker:ical4android:6ccdb1a") implementation("com.gitlab.abaker:ical4android:007f7751d5")
implementation("com.gitlab.bitfireAT:cert4android:0710fb57c1") implementation("com.gitlab.bitfireAT:cert4android:0710fb57c1")
implementation("com.github.dmfs.opentasks:opentasks-provider:1.2.4") { implementation("com.github.dmfs.opentasks:opentasks-provider:1.2.4") {
exclude("com.github.dmfs.opentasks", "opentasks-contract") exclude("com.github.dmfs.opentasks", "opentasks-contract")

@ -115,25 +115,16 @@ class iCalendar @Inject constructor(
calendar: CaldavCalendar, calendar: CaldavCalendar,
existing: CaldavTask?, existing: CaldavTask?,
remote: Task, remote: Task,
vtodo: String, vtodo: String?,
obj: String? = null, obj: String? = null,
eTag: String? = null) { eTag: String? = null) {
val task: com.todoroo.astrid.data.Task val task = existing?.task?.let { taskDao.fetch(it) }
val caldavTask: CaldavTask
if (existing == null) {
task = taskCreator.createWithValues("")
taskDao.createNew(task)
caldavTask = CaldavTask(task.id, calendar.uuid, remote.uid, obj)
} else {
caldavTask = existing
task = taskDao.fetch(existing.task)
?: taskCreator.createWithValues("").apply { ?: taskCreator.createWithValues("").apply {
taskDao.createNew(this) taskDao.createNew(this)
caldavTask.task = id existing?.task = id
}
} }
val caldavTask = existing ?: CaldavTask(task.id, calendar.uuid, remote.uid, obj)
CaldavConverter.apply(task, remote) CaldavConverter.apply(task, remote)
caldavTask.order = remote.order
setPlace(task.id, remote.geoPosition) setPlace(task.id, remote.geoPosition)
tagDao.applyTags(task, tagDataDao, getTags(remote.categories)) tagDao.applyTags(task, tagDataDao, getTags(remote.categories))
task.suppressSync() task.suppressSync()
@ -143,6 +134,7 @@ class iCalendar @Inject constructor(
caldavTask.etag = eTag caldavTask.etag = eTag
caldavTask.lastSync = task.modificationDate caldavTask.lastSync = task.modificationDate
caldavTask.remoteParent = remote.getParent() caldavTask.remoteParent = remote.getParent()
caldavTask.order = remote.order
if (caldavTask.id == com.todoroo.astrid.data.Task.NO_ID) { if (caldavTask.id == com.todoroo.astrid.data.Task.NO_ID) {
caldavTask.id = caldavDao.insert(caldavTask) caldavTask.id = caldavDao.insert(caldavTask)
Timber.d("NEW %s", caldavTask) Timber.d("NEW %s", caldavTask)

@ -5,6 +5,9 @@ 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
import at.bitfire.ical4android.AndroidTask
import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues
import at.bitfire.ical4android.Task
import at.bitfire.ical4android.UnknownProperty import at.bitfire.ical4android.UnknownProperty
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -127,22 +130,6 @@ class OpenTaskDao @Inject constructor(
} }
} }
suspend fun getTags(listId: Long, caldavTask: CaldavTask): List<String> = withContext(Dispatchers.IO) {
val id = getId(listId, caldavTask.remoteId)
val tags = ArrayList<String>()
cr.query(
properties,
arrayOf(Properties.DATA1),
"${Properties.TASK_ID} = $id AND ${Properties.MIMETYPE} = '${Category.CONTENT_ITEM_TYPE}'",
null,
null)?.use {
while (it.moveToNext()) {
it.getString(Properties.DATA1)?.let(tags::add)
}
}
return@withContext tags
}
fun setTags(id: Long, tags: List<String>): List<ContentProviderOperation> { fun setTags(id: Long, tags: List<String>): List<ContentProviderOperation> {
val delete = listOf( val delete = listOf(
newDelete(properties) newDelete(properties)
@ -162,26 +149,6 @@ class OpenTaskDao @Inject constructor(
return delete + inserts return delete + inserts
} }
suspend fun getRemoteOrder(listId: Long, caldavTask: CaldavTask): Long? = withContext(Dispatchers.IO) {
val id = getId(listId, caldavTask.remoteId) ?: return@withContext null
cr.query(
properties,
arrayOf(Properties.DATA0),
"${Properties.TASK_ID} = $id AND ${Properties.MIMETYPE} = '${UnknownProperty.CONTENT_ITEM_TYPE}' AND ${Properties.DATA0} LIKE '%$APPLE_SORT_ORDER%'",
null,
null)?.use {
while (it.moveToNext()) {
it.getString(Properties.DATA0)
?.let(UnknownProperty::fromJsonString)
?.takeIf { xprop -> xprop.name.equals(APPLE_SORT_ORDER, true) }
?.let { xprop ->
return@withContext xprop.value.toLong()
}
}
}
return@withContext null
}
fun setRemoteOrder(id: Long, caldavTask: CaldavTask): List<ContentProviderOperation> { fun setRemoteOrder(id: Long, caldavTask: CaldavTask): List<ContentProviderOperation> {
val operations = ArrayList<ContentProviderOperation>() val operations = ArrayList<ContentProviderOperation>()
operations.add( operations.add(
@ -226,15 +193,18 @@ class OpenTaskDao @Inject constructor(
return operations return operations
} }
suspend fun getParent(id: Long): String? = withContext(Dispatchers.IO) { suspend fun getTask(listId: Long, uid: String): Task? = withContext(Dispatchers.IO) {
cr.query( cr.query(
properties, Tasks.getContentUri(authority)
arrayOf(Relation.RELATED_UID), .buildUpon()
"${Relation.TASK_ID} = $id AND ${Properties.MIMETYPE} = '${Relation.CONTENT_ITEM_TYPE}' AND ${Relation.RELATED_TYPE} = ${Relation.RELTYPE_PARENT}", .appendQueryParameter(LOAD_PROPERTIES, "1")
.build(),
null,
"${Tasks.LIST_ID} = $listId AND ${Tasks._UID} = '$uid'",
null, null,
null)?.use { null)?.use {
if (it.moveToFirst()) { if (it.moveToFirst()) {
it.getString(Relation.RELATED_UID) MyAndroidTask(it).task
} else { } else {
null null
} }
@ -264,13 +234,31 @@ class OpenTaskDao @Inject constructor(
fun String?.isDecSync(): Boolean = this?.startsWith(ACCOUNT_TYPE_DECSYNC) == true fun String?.isDecSync(): Boolean = this?.startsWith(ACCOUNT_TYPE_DECSYNC) == true
fun Cursor.getString(columnName: String): String? = private fun Cursor.getString(columnName: String): String? =
getString(getColumnIndex(columnName)) getString(getColumnIndex(columnName))
fun Cursor.getInt(columnName: String): Int = fun Cursor.getInt(columnName: String): Int =
getInt(getColumnIndex(columnName)) getInt(getColumnIndex(columnName))
fun Cursor.getLong(columnName: String): Long = private fun Cursor.getLong(columnName: String): Long =
getLong(getColumnIndex(columnName)) getLong(getColumnIndex(columnName))
private class MyAndroidTask(cursor: Cursor) : AndroidTask(null) {
init {
val values = cursor.toValues()
task = Task()
populateTask(values)
populateRelatedTo(values)
if (values.containsKey(Properties.PROPERTY_ID)) {
// process the first property, which is combined with the task row
populateProperty(values)
while (cursor.moveToNext()) {
// process the other properties
populateProperty(cursor.toValues(true))
}
}
}
}
} }
} }

@ -3,19 +3,11 @@ package org.tasks.opentasks
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 com.todoroo.andlib.utility.DateUtilities.now
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task import com.todoroo.astrid.data.Task
import com.todoroo.astrid.data.Task.Companion.HIDE_UNTIL_SPECIFIC_DAY
import com.todoroo.astrid.data.Task.Companion.HIDE_UNTIL_SPECIFIC_DAY_TIME
import com.todoroo.astrid.data.Task.Companion.URGENCY_SPECIFIC_DAY
import com.todoroo.astrid.data.Task.Companion.URGENCY_SPECIFIC_DAY_TIME
import com.todoroo.astrid.data.Task.Companion.sanitizeRRule import com.todoroo.astrid.data.Task.Companion.sanitizeRRule
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 net.fortuna.ical4j.model.property.Geo
import net.fortuna.ical4j.model.property.RRule import net.fortuna.ical4j.model.property.RRule
import org.dmfs.tasks.contract.TaskContract.Tasks import org.dmfs.tasks.contract.TaskContract.Tasks
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
@ -23,20 +15,16 @@ import org.tasks.R
import org.tasks.analytics.Constants import org.tasks.analytics.Constants
import org.tasks.analytics.Firebase import org.tasks.analytics.Firebase
import org.tasks.billing.Inventory import org.tasks.billing.Inventory
import org.tasks.caldav.CaldavConverter
import org.tasks.caldav.CaldavConverter.toRemote import org.tasks.caldav.CaldavConverter.toRemote
import org.tasks.caldav.iCalendar import org.tasks.caldav.iCalendar
import org.tasks.data.* import org.tasks.data.*
import org.tasks.data.CaldavAccount.Companion.openTaskType import org.tasks.data.CaldavAccount.Companion.openTaskType
import org.tasks.data.OpenTaskDao.Companion.getInt import org.tasks.data.OpenTaskDao.Companion.getInt
import org.tasks.data.OpenTaskDao.Companion.getLong
import org.tasks.data.OpenTaskDao.Companion.getString
import org.tasks.data.OpenTaskDao.Companion.isDavx5 import org.tasks.data.OpenTaskDao.Companion.isDavx5
import org.tasks.data.OpenTaskDao.Companion.isDecSync import org.tasks.data.OpenTaskDao.Companion.isDecSync
import org.tasks.data.OpenTaskDao.Companion.isEteSync import org.tasks.data.OpenTaskDao.Companion.isEteSync
import org.tasks.data.OpenTaskDao.Companion.newAccounts import org.tasks.data.OpenTaskDao.Companion.newAccounts
import org.tasks.time.DateTime import org.tasks.time.DateTime
import org.tasks.time.DateTimeUtils.currentTimeMillis
import org.tasks.time.DateTimeUtils.startOfDay import org.tasks.time.DateTimeUtils.startOfDay
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
@ -49,13 +37,11 @@ class OpenTasksSynchronizer @Inject constructor(
private val caldavDao: CaldavDao, private val caldavDao: CaldavDao,
private val taskDeleter: TaskDeleter, private val taskDeleter: TaskDeleter,
private val localBroadcastManager: LocalBroadcastManager, private val localBroadcastManager: LocalBroadcastManager,
private val taskCreator: TaskCreator,
private val taskDao: TaskDao, private val taskDao: TaskDao,
private val firebase: Firebase, private val firebase: Firebase,
private val iCalendar: iCalendar, private val iCalendar: iCalendar,
private val locationDao: LocationDao, private val locationDao: LocationDao,
private val openTaskDao: OpenTaskDao, private val openTaskDao: OpenTaskDao,
private val tagDao: TagDao,
private val tagDataDao: TagDataDao, private val tagDataDao: TagDataDao,
private val inventory: Inventory) { private val inventory: Inventory) {
@ -293,90 +279,14 @@ class OpenTasksSynchronizer @Inject constructor(
listId: Long, listId: Long,
uid: String, uid: String,
etag: String?, etag: String?,
existing: CaldavTask?) { existing: CaldavTask?
cr.query( ) {
Tasks.getContentUri(openTaskDao.authority), openTaskDao.getTask(listId, uid)?.let {
null, iCalendar.fromVtodo(calendar, existing, it, null, null, etag)
"${Tasks.LIST_ID} = $listId AND ${Tasks._UID} = '$uid'",
null,
null)?.use {
if (!it.moveToFirst()) {
return
}
val task = existing?.task
?.let { task -> taskDao.fetch(task) }
?: taskCreator
.createWithValues("")
.apply { taskDao.createNew(this) }
val caldavTask = existing
?: CaldavTask(task.id, calendar.uuid, it.getString(Tasks._UID), null)
task.title = it.getString(Tasks.TITLE)
task.priority = CaldavConverter.fromRemote(it.getInt(Tasks.PRIORITY))
val completedAt = it.getLong(Tasks.COMPLETED)
task.completionDate = when {
completedAt > 0 -> completedAt
it.getInt(Tasks.STATUS) == Tasks.STATUS_COMPLETED ->
if (task.isCompleted) task.completionDate else now()
else -> 0L
}
task.notes = it.getString(Tasks.DESCRIPTION)
task.modificationDate = currentTimeMillis()
task.creationDate = it.getLong(Tasks.CREATED).toLocal()
val allDay = it.getBoolean(Tasks.IS_ALLDAY)
val due = it.getLong(Tasks.DUE)
task.dueDate = when {
due == 0L -> 0
allDay -> Task.createDueDate(URGENCY_SPECIFIC_DAY, due - DateTime(due).offset)
else -> Task.createDueDate(URGENCY_SPECIFIC_DAY_TIME, due)
}
val start = it.getLong(Tasks.DTSTART)
task.hideUntil = when {
start == 0L -> 0
allDay -> task.createHideUntil(HIDE_UNTIL_SPECIFIC_DAY, start - DateTime(start).offset)
else -> task.createHideUntil(HIDE_UNTIL_SPECIFIC_DAY_TIME, start)
}
iCalendar.setPlace(task.id, it.getString(Tasks.GEO).toGeo())
task.setRecurrence(it.getString(Tasks.RRULE).toRRule())
val tagNames = openTaskDao.getTags(listId, caldavTask)
val tags = iCalendar.getTags(tagNames)
caldavTask.etag = etag
caldavTask.order = openTaskDao.getRemoteOrder(listId, caldavTask)
caldavTask.remoteParent = openTaskDao.getParent(it.getLong(Tasks._ID))
task.suppressSync()
task.suppressRefresh()
taskDao.save(task)
tagDao.applyTags(task, tagDataDao, tags)
caldavTask.lastSync = task.modificationDate
if (caldavTask.id == Task.NO_ID) {
caldavTask.id = caldavDao.insert(caldavTask)
Timber.d("NEW $caldavTask")
} else {
caldavDao.update(caldavTask)
Timber.d("UPDATE $caldavTask")
}
} }
} }
companion object { companion object {
private fun Location?.toGeoString(): String? = this?.let { "$longitude,$latitude" } private fun Location?.toGeoString(): String? = this?.let { "$longitude,$latitude" }
private fun String?.toGeo(): Geo? =
this
?.takeIf { it.isNotBlank() }
?.split(",")
?.takeIf {
it.size == 2
&& it[0].toDoubleOrNull() != null
&& it[1].toDoubleOrNull() != null }
?.let { Geo("${it[1]};${it[0]}") }
private fun String?.toRRule(): RRule? =
this?.takeIf { it.isNotBlank() }?.let(::RRule)
private fun Cursor.getBoolean(columnName: String): Boolean =
getInt(getColumnIndex(columnName)) != 0
private fun Long.toLocal(): Long =
DateTime(this).toLocal().millis
} }
} }

@ -97,7 +97,7 @@
++--- com.gitlab.bitfireAT:dav4jvm:2.1.1 ++--- com.gitlab.bitfireAT:dav4jvm:2.1.1
+| +--- org.jetbrains.kotlin:kotlin-stdlib:1.3.72 -> 1.4.21 (*) +| +--- org.jetbrains.kotlin:kotlin-stdlib:1.3.72 -> 1.4.21 (*)
+| \--- org.apache.commons:commons-lang3:3.9 +| \--- org.apache.commons:commons-lang3:3.9
++--- com.gitlab.abaker:ical4android:6ccdb1a ++--- com.gitlab.abaker:ical4android:007f7751d5
+| +--- org.mnode.ical4j:ical4j:3.0.21 +| +--- org.mnode.ical4j:ical4j:3.0.21
+| | +--- org.slf4j:slf4j-api:1.7.25 -> 1.7.30 +| | +--- org.slf4j:slf4j-api:1.7.25 -> 1.7.30
+| | +--- commons-codec:commons-codec:1.11 +| | +--- commons-codec:commons-codec:1.11

@ -249,7 +249,7 @@
++--- com.gitlab.bitfireAT:dav4jvm:2.1.1 ++--- com.gitlab.bitfireAT:dav4jvm:2.1.1
+| +--- org.jetbrains.kotlin:kotlin-stdlib:1.3.72 -> 1.4.21 (*) +| +--- org.jetbrains.kotlin:kotlin-stdlib:1.3.72 -> 1.4.21 (*)
+| \--- org.apache.commons:commons-lang3:3.9 +| \--- org.apache.commons:commons-lang3:3.9
++--- com.gitlab.abaker:ical4android:6ccdb1a ++--- com.gitlab.abaker:ical4android:007f7751d5
+| +--- org.mnode.ical4j:ical4j:3.0.21 +| +--- org.mnode.ical4j:ical4j:3.0.21
+| | +--- org.slf4j:slf4j-api:1.7.25 -> 1.7.30 +| | +--- org.slf4j:slf4j-api:1.7.25 -> 1.7.30
+| | +--- commons-codec:commons-codec:1.11 +| | +--- commons-codec:commons-codec:1.11

Loading…
Cancel
Save