Loading src/androidTest/java/at/bitfire/ical4android/AndroidTaskListTest.kt +24 −22 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ import net.fortuna.ical4j.model.property.RelatedTo import org.dmfs.tasks.contract.TaskContract import org.dmfs.tasks.contract.TaskContract.Properties import org.dmfs.tasks.contract.TaskContract.Property.Relation import org.dmfs.tasks.contract.TaskContract.Tasks import org.junit.After import org.junit.Assert.* import org.junit.Assume.assumeNotNull Loading Loading @@ -81,24 +82,27 @@ class AndroidTaskListTest { } @Test fun testCommitRelations() { fun testTouchRelations() { val taskList = createTaskList() assertTrue(taskList.useDelayedRelations) try { val parent = Task() parent.uid = "parent" parent.summary = "Parent task" val parentContentUri = TestTask(taskList, parent).add() val child = Task() child.uid = "child" child.summary = "Child task" child.relatedTo.add(RelatedTo(parent.uid)) // insert child before parent val childContentUri = TestTask(taskList, child).add() val childId = ContentUris.parseId(childContentUri) val parentContentUri = TestTask(taskList, parent).add() val parentId = ContentUris.parseId(parentContentUri) // there should be one DelayedRelation row // OpenTasks should provide the correct relation… taskList.provider.client.query(taskList.tasksPropertiesSyncUri(), null, "${Properties.TASK_ID}=?", arrayOf(ContentUris.parseId(childContentUri).toString()), "${Properties.TASK_ID}=?", arrayOf(childId.toString()), null, null)!!.use { cursor -> assertEquals(1, cursor.count) cursor.moveToNext() Loading @@ -106,28 +110,26 @@ class AndroidTaskListTest { val row = ContentValues() DatabaseUtils.cursorRowToContentValues(cursor, row) assertEquals(AndroidTask.DelayedRelation.CONTENT_ITEM_TYPE, row.getAsString(Properties.MIMETYPE)) assertNull(row.getAsLong(Relation.RELATED_ID)) assertEquals(Relation.CONTENT_ITEM_TYPE, row.getAsString(Properties.MIMETYPE)) assertEquals(parentId, row.getAsLong(Relation.RELATED_ID)) assertEquals(parent.uid, row.getAsString(Relation.RELATED_UID)) assertEquals(Relation.RELTYPE_PARENT, row.getAsInteger(Relation.RELATED_TYPE)) } // … BUT the parent_id is not updated (https://github.com/dmfs/opentasks/issues/877) taskList.provider.client.query(childContentUri, arrayOf(Tasks.PARENT_ID), null, null, null)!!.use { cursor -> assertTrue(cursor.moveToNext()) assertTrue(cursor.isNull(0)) } taskList.commitRelations() // now there must be a real Relation row taskList.provider.client.query(taskList.tasksPropertiesSyncUri(), null, "${Properties.TASK_ID}=?", arrayOf(ContentUris.parseId(childContentUri).toString()), null, null)!!.use { cursor -> assertEquals(1, cursor.count) cursor.moveToNext() val row = ContentValues() DatabaseUtils.cursorRowToContentValues(cursor, row) // touch the relations to update parent_id values taskList.touchRelations() assertEquals(Relation.CONTENT_ITEM_TYPE, row.getAsString(Properties.MIMETYPE)) assertEquals(ContentUris.parseId(parentContentUri), row.getAsLong(Relation.RELATED_ID)) assertEquals(parent.uid, row.getAsString(Relation.RELATED_UID)) assertEquals(Relation.RELTYPE_PARENT, row.getAsInteger(Relation.RELATED_TYPE)) // now parent_id should bet set taskList.provider.client.query(childContentUri, arrayOf(Tasks.PARENT_ID), null, null, null)!!.use { cursor -> assertTrue(cursor.moveToNext()) assertEquals(parentId, cursor.getLong(0)) } } finally { taskList.delete() Loading src/androidTest/java/at/bitfire/ical4android/AndroidTaskTest.kt +0 −1 Original line number Diff line number Diff line Loading @@ -49,7 +49,6 @@ class AndroidTaskTest { taskList = TestTaskList.create(testAccount, providerOrNull) assertNotNull("Couldn't find/create test task list", taskList) taskList!!.useDelayedRelations = false taskListUri = ContentUris.withAppendedId(provider!!.taskListsUri(), taskList!!.id) } Loading src/main/java/at/bitfire/ical4android/AndroidTask.kt +13 −36 Original line number Diff line number Diff line Loading @@ -10,12 +10,10 @@ package at.bitfire.ical4android import android.content.ContentProviderOperation import android.content.ContentProviderOperation.Builder import android.content.ContentResolver import android.content.ContentUris import android.content.ContentValues import android.net.Uri import android.os.RemoteException import at.bitfire.ical4android.AndroidTask.DelayedRelation.Companion.CONTENT_ITEM_TYPE import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues import net.fortuna.ical4j.model.* import net.fortuna.ical4j.model.Date Loading Loading @@ -79,28 +77,28 @@ abstract class AndroidTask( task = Task() val client = taskList.provider.client client.query(taskSyncURI(), null, null, null, null)?.use { cursor -> client.query(taskSyncURI(true), null, null, null, null)?.use { cursor -> if (cursor.moveToFirst()) { val values = cursor.toValues(true) Constants.log.log(Level.FINER, "Found task", values) populateTask(values) if (values.getAsInteger(Tasks.HAS_PROPERTIES) != 0) // fetch properties client.query(taskList.tasksPropertiesSyncUri(), null, "${Properties.TASK_ID}=?", arrayOf(id.toString()), null)?.use { propCursor -> while (propCursor.moveToNext()) populateProperty(propCursor.toValues(true)) 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)) } } // Special case: parent_id set, but no matching parent Relation row (like given by aCalendar+) // In this case, we create the relation ourselves. val relatedToList = task!!.relatedTo values.getAsLong(Tasks.PARENT_ID)?.let { parentId -> val hasParentRelation = relatedToList.any { relatedTo -> val relatedType = relatedTo.getParameter(Parameter.RELTYPE) relatedType == null || relatedType == RelType.PARENT relatedType == RelType.PARENT || relatedType == null /* RelType.PARENT is the default value */ } if (!hasParentRelation) { // get UID of parent task Loading Loading @@ -358,11 +356,6 @@ abstract class AndroidTask( } protected open fun insertRelatedTo(batch: BatchOperation) { val mimeType = if (taskList.useDelayedRelations) DelayedRelation.CONTENT_ITEM_TYPE else Relation.CONTENT_ITEM_TYPE for (relatedTo in requireNotNull(task).relatedTo) { val relType = when ((relatedTo.getParameter(Parameter.RELTYPE) as RelType?)) { RelType.CHILD -> Loading @@ -374,7 +367,7 @@ abstract class AndroidTask( } val builder = ContentProviderOperation.newInsert(taskList.tasksPropertiesSyncUri()) .withValue(Relation.TASK_ID, id) .withValue(Relation.MIMETYPE, mimeType) .withValue(Relation.MIMETYPE, Relation.CONTENT_ITEM_TYPE) .withValue(Relation.RELATED_UID, relatedTo.value) .withValue(Relation.RELATED_TYPE, relType) Constants.log.log(Level.FINE, "Inserting relation", builder.build()) Loading Loading @@ -494,28 +487,12 @@ abstract class AndroidTask( return tz ?: TimeZone.getDefault() } protected fun taskSyncURI(): Uri { protected fun taskSyncURI(loadProperties: Boolean = false): Uri { val id = requireNotNull(id) val builder = taskList.tasksSyncUri().buildUpon() return ContentUris.appendId(builder, id) .appendQueryParameter(LOAD_PROPERTIES, "1") .build() return ContentUris.withAppendedId(taskList.tasksSyncUri(loadProperties), id) } override fun toString() = MiscUtils.reflectionToString(this) /** * A delayed relation row represents a relation which possibly can't be resolved yet. * Same definition as [Relation], only the row type is [CONTENT_ITEM_TYPE] instead of [Relation.CONTENT_ITEM_TYPE]. */ class DelayedRelation { companion object { const val CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/vnd.ical4android.delayed-relation" } } } src/main/java/at/bitfire/ical4android/AndroidTaskList.kt +44 −42 Original line number Diff line number Diff line Loading @@ -16,9 +16,9 @@ import android.content.Context import android.net.Uri import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues import org.dmfs.tasks.contract.TaskContract import org.dmfs.tasks.contract.TaskContract.* import org.dmfs.tasks.contract.TaskContract.Properties import org.dmfs.tasks.contract.TaskContract.Property.Relation import org.dmfs.tasks.contract.TaskContract.TaskLists import org.dmfs.tasks.contract.TaskContract.Tasks import java.io.FileNotFoundException import java.util.* Loading Loading @@ -99,16 +99,6 @@ abstract class AndroidTaskList<out T: AndroidTask>( var isSynced = false var isVisible = false /** * When tasks are added or updated, they may refer to related tasks ([Task.relatedTo]), * but these related tasks may not be available yet (for instance, because they have not been * synchronized yet), so that the tasks provider can't establish the relation in the database. * * When delayed relations are used, [commitRelations] must be called after * operations which potentially add relations (namely [AndroidTask.add] and [AndroidTask.update]). */ var useDelayedRelations = true protected fun populate(values: ContentValues) { syncId = values.getAsString(TaskLists._SYNC_ID) Loading @@ -122,39 +112,43 @@ abstract class AndroidTaskList<out T: AndroidTask>( fun delete() = provider.client.delete(taskListSyncUri(), null, null) /** * Transforms [AndroidTask.DelayedRelation]s to real [org.dmfs.tasks.contract.TaskContract.Property.Relation]s. Only * useful when [useDelayedRelations] is active. * When tasks are added or updated, they may refer to related tasks by UID ([Relation.RELATED_UID]). * However, those related tasks may not be available (for instance, because they have not been * synchronized yet), so that the tasks provider can't establish the actual relation (= set * [Relation.TASK_ID]) in the database. * * As soon as such a related task is added, OpenTasks updates the [Relation.RELATED_ID], * but it does *not* update [Tasks.PARENT_ID] of the parent task: * https://github.com/dmfs/opentasks/issues/877 * * This method shall be called after all tasks have been synchronized. It touches * * - all [Relation] rows * - with [Relation.RELATED_ID] (→ related task is already synchronized) * - of tasks without [Tasks.PARENT_ID] (→ only touch relevant rows) * * so that missing [Tasks.PARENT_ID] fields are updated. * * @return number of touched [Relation] rows */ fun commitRelations() { Constants.log.fine("Commiting relations") val batch = BatchOperation(provider.client) provider.client.query(tasksPropertiesSyncUri(), arrayOf(Properties.PROPERTY_ID, Properties.TASK_ID, Relation.RELATED_TYPE, Relation.RELATED_UID), "${Properties.MIMETYPE}=?", arrayOf(AndroidTask.DelayedRelation.CONTENT_ITEM_TYPE), null)?.use { cursor -> fun touchRelations(): Int { Constants.log.fine("Touching relations to set parent_id") val batchOperation = BatchOperation(provider.client) provider.client.query(tasksSyncUri(true), null, "${Tasks.LIST_ID}=? AND ${Tasks.PARENT_ID} IS NULL AND ${Relation.MIMETYPE}=? AND ${Relation.RELATED_ID} IS NOT NULL", arrayOf(id.toString(), Relation.CONTENT_ITEM_TYPE), null, null)?.use { cursor -> while (cursor.moveToNext()) { val id = cursor.getLong(0) val taskId = cursor.getLong(1) val relatedType = cursor.getInt(2) val relatedUid = cursor.getString(3) // create new Relation row batch.enqueue(BatchOperation.Operation( ContentProviderOperation.newInsert(tasksPropertiesSyncUri()) .withValue(Relation.TASK_ID, taskId) .withValue(Relation.MIMETYPE, Relation.CONTENT_ITEM_TYPE) .withValue(Relation.RELATED_TYPE, relatedType) .withValue(Relation.RELATED_UID, relatedUid) )) // delete DelayedRelation row val delayedRelationUri = ContentUris.withAppendedId(tasksPropertiesSyncUri(), id) batch.enqueue(BatchOperation.Operation( ContentProviderOperation.newDelete(delayedRelationUri) val values = cursor.toValues() val id = values.getAsLong(Relation.PROPERTY_ID) val propertyContentUri = ContentUris.withAppendedId(tasksPropertiesSyncUri(), id) batchOperation.enqueue(BatchOperation.Operation( ContentProviderOperation.newUpdate(propertyContentUri) .withValue(Relation.RELATED_ID, values.getAsLong(Relation.RELATED_ID)) )) } } batch.commit() return batchOperation.commit() } Loading Loading @@ -187,7 +181,15 @@ abstract class AndroidTaskList<out T: AndroidTask>( fun taskListSyncUri() = TaskProvider.syncAdapterUri(ContentUris.withAppendedId(provider.taskListsUri(), id), account) fun tasksSyncUri() = TaskProvider.syncAdapterUri(provider.tasksUri(), account) fun tasksSyncUri(loadProperties: Boolean = false): Uri { val uri = TaskProvider.syncAdapterUri(provider.tasksUri(), account) return if (loadProperties) uri .buildUpon() .appendQueryParameter(TaskContract.LOAD_PROPERTIES, "1") .build() else uri } fun tasksPropertiesSyncUri() = TaskProvider.syncAdapterUri(provider.propertiesUri(), account) } Loading
src/androidTest/java/at/bitfire/ical4android/AndroidTaskListTest.kt +24 −22 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ import net.fortuna.ical4j.model.property.RelatedTo import org.dmfs.tasks.contract.TaskContract import org.dmfs.tasks.contract.TaskContract.Properties import org.dmfs.tasks.contract.TaskContract.Property.Relation import org.dmfs.tasks.contract.TaskContract.Tasks import org.junit.After import org.junit.Assert.* import org.junit.Assume.assumeNotNull Loading Loading @@ -81,24 +82,27 @@ class AndroidTaskListTest { } @Test fun testCommitRelations() { fun testTouchRelations() { val taskList = createTaskList() assertTrue(taskList.useDelayedRelations) try { val parent = Task() parent.uid = "parent" parent.summary = "Parent task" val parentContentUri = TestTask(taskList, parent).add() val child = Task() child.uid = "child" child.summary = "Child task" child.relatedTo.add(RelatedTo(parent.uid)) // insert child before parent val childContentUri = TestTask(taskList, child).add() val childId = ContentUris.parseId(childContentUri) val parentContentUri = TestTask(taskList, parent).add() val parentId = ContentUris.parseId(parentContentUri) // there should be one DelayedRelation row // OpenTasks should provide the correct relation… taskList.provider.client.query(taskList.tasksPropertiesSyncUri(), null, "${Properties.TASK_ID}=?", arrayOf(ContentUris.parseId(childContentUri).toString()), "${Properties.TASK_ID}=?", arrayOf(childId.toString()), null, null)!!.use { cursor -> assertEquals(1, cursor.count) cursor.moveToNext() Loading @@ -106,28 +110,26 @@ class AndroidTaskListTest { val row = ContentValues() DatabaseUtils.cursorRowToContentValues(cursor, row) assertEquals(AndroidTask.DelayedRelation.CONTENT_ITEM_TYPE, row.getAsString(Properties.MIMETYPE)) assertNull(row.getAsLong(Relation.RELATED_ID)) assertEquals(Relation.CONTENT_ITEM_TYPE, row.getAsString(Properties.MIMETYPE)) assertEquals(parentId, row.getAsLong(Relation.RELATED_ID)) assertEquals(parent.uid, row.getAsString(Relation.RELATED_UID)) assertEquals(Relation.RELTYPE_PARENT, row.getAsInteger(Relation.RELATED_TYPE)) } // … BUT the parent_id is not updated (https://github.com/dmfs/opentasks/issues/877) taskList.provider.client.query(childContentUri, arrayOf(Tasks.PARENT_ID), null, null, null)!!.use { cursor -> assertTrue(cursor.moveToNext()) assertTrue(cursor.isNull(0)) } taskList.commitRelations() // now there must be a real Relation row taskList.provider.client.query(taskList.tasksPropertiesSyncUri(), null, "${Properties.TASK_ID}=?", arrayOf(ContentUris.parseId(childContentUri).toString()), null, null)!!.use { cursor -> assertEquals(1, cursor.count) cursor.moveToNext() val row = ContentValues() DatabaseUtils.cursorRowToContentValues(cursor, row) // touch the relations to update parent_id values taskList.touchRelations() assertEquals(Relation.CONTENT_ITEM_TYPE, row.getAsString(Properties.MIMETYPE)) assertEquals(ContentUris.parseId(parentContentUri), row.getAsLong(Relation.RELATED_ID)) assertEquals(parent.uid, row.getAsString(Relation.RELATED_UID)) assertEquals(Relation.RELTYPE_PARENT, row.getAsInteger(Relation.RELATED_TYPE)) // now parent_id should bet set taskList.provider.client.query(childContentUri, arrayOf(Tasks.PARENT_ID), null, null, null)!!.use { cursor -> assertTrue(cursor.moveToNext()) assertEquals(parentId, cursor.getLong(0)) } } finally { taskList.delete() Loading
src/androidTest/java/at/bitfire/ical4android/AndroidTaskTest.kt +0 −1 Original line number Diff line number Diff line Loading @@ -49,7 +49,6 @@ class AndroidTaskTest { taskList = TestTaskList.create(testAccount, providerOrNull) assertNotNull("Couldn't find/create test task list", taskList) taskList!!.useDelayedRelations = false taskListUri = ContentUris.withAppendedId(provider!!.taskListsUri(), taskList!!.id) } Loading
src/main/java/at/bitfire/ical4android/AndroidTask.kt +13 −36 Original line number Diff line number Diff line Loading @@ -10,12 +10,10 @@ package at.bitfire.ical4android import android.content.ContentProviderOperation import android.content.ContentProviderOperation.Builder import android.content.ContentResolver import android.content.ContentUris import android.content.ContentValues import android.net.Uri import android.os.RemoteException import at.bitfire.ical4android.AndroidTask.DelayedRelation.Companion.CONTENT_ITEM_TYPE import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues import net.fortuna.ical4j.model.* import net.fortuna.ical4j.model.Date Loading Loading @@ -79,28 +77,28 @@ abstract class AndroidTask( task = Task() val client = taskList.provider.client client.query(taskSyncURI(), null, null, null, null)?.use { cursor -> client.query(taskSyncURI(true), null, null, null, null)?.use { cursor -> if (cursor.moveToFirst()) { val values = cursor.toValues(true) Constants.log.log(Level.FINER, "Found task", values) populateTask(values) if (values.getAsInteger(Tasks.HAS_PROPERTIES) != 0) // fetch properties client.query(taskList.tasksPropertiesSyncUri(), null, "${Properties.TASK_ID}=?", arrayOf(id.toString()), null)?.use { propCursor -> while (propCursor.moveToNext()) populateProperty(propCursor.toValues(true)) 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)) } } // Special case: parent_id set, but no matching parent Relation row (like given by aCalendar+) // In this case, we create the relation ourselves. val relatedToList = task!!.relatedTo values.getAsLong(Tasks.PARENT_ID)?.let { parentId -> val hasParentRelation = relatedToList.any { relatedTo -> val relatedType = relatedTo.getParameter(Parameter.RELTYPE) relatedType == null || relatedType == RelType.PARENT relatedType == RelType.PARENT || relatedType == null /* RelType.PARENT is the default value */ } if (!hasParentRelation) { // get UID of parent task Loading Loading @@ -358,11 +356,6 @@ abstract class AndroidTask( } protected open fun insertRelatedTo(batch: BatchOperation) { val mimeType = if (taskList.useDelayedRelations) DelayedRelation.CONTENT_ITEM_TYPE else Relation.CONTENT_ITEM_TYPE for (relatedTo in requireNotNull(task).relatedTo) { val relType = when ((relatedTo.getParameter(Parameter.RELTYPE) as RelType?)) { RelType.CHILD -> Loading @@ -374,7 +367,7 @@ abstract class AndroidTask( } val builder = ContentProviderOperation.newInsert(taskList.tasksPropertiesSyncUri()) .withValue(Relation.TASK_ID, id) .withValue(Relation.MIMETYPE, mimeType) .withValue(Relation.MIMETYPE, Relation.CONTENT_ITEM_TYPE) .withValue(Relation.RELATED_UID, relatedTo.value) .withValue(Relation.RELATED_TYPE, relType) Constants.log.log(Level.FINE, "Inserting relation", builder.build()) Loading Loading @@ -494,28 +487,12 @@ abstract class AndroidTask( return tz ?: TimeZone.getDefault() } protected fun taskSyncURI(): Uri { protected fun taskSyncURI(loadProperties: Boolean = false): Uri { val id = requireNotNull(id) val builder = taskList.tasksSyncUri().buildUpon() return ContentUris.appendId(builder, id) .appendQueryParameter(LOAD_PROPERTIES, "1") .build() return ContentUris.withAppendedId(taskList.tasksSyncUri(loadProperties), id) } override fun toString() = MiscUtils.reflectionToString(this) /** * A delayed relation row represents a relation which possibly can't be resolved yet. * Same definition as [Relation], only the row type is [CONTENT_ITEM_TYPE] instead of [Relation.CONTENT_ITEM_TYPE]. */ class DelayedRelation { companion object { const val CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/vnd.ical4android.delayed-relation" } } }
src/main/java/at/bitfire/ical4android/AndroidTaskList.kt +44 −42 Original line number Diff line number Diff line Loading @@ -16,9 +16,9 @@ import android.content.Context import android.net.Uri import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues import org.dmfs.tasks.contract.TaskContract import org.dmfs.tasks.contract.TaskContract.* import org.dmfs.tasks.contract.TaskContract.Properties import org.dmfs.tasks.contract.TaskContract.Property.Relation import org.dmfs.tasks.contract.TaskContract.TaskLists import org.dmfs.tasks.contract.TaskContract.Tasks import java.io.FileNotFoundException import java.util.* Loading Loading @@ -99,16 +99,6 @@ abstract class AndroidTaskList<out T: AndroidTask>( var isSynced = false var isVisible = false /** * When tasks are added or updated, they may refer to related tasks ([Task.relatedTo]), * but these related tasks may not be available yet (for instance, because they have not been * synchronized yet), so that the tasks provider can't establish the relation in the database. * * When delayed relations are used, [commitRelations] must be called after * operations which potentially add relations (namely [AndroidTask.add] and [AndroidTask.update]). */ var useDelayedRelations = true protected fun populate(values: ContentValues) { syncId = values.getAsString(TaskLists._SYNC_ID) Loading @@ -122,39 +112,43 @@ abstract class AndroidTaskList<out T: AndroidTask>( fun delete() = provider.client.delete(taskListSyncUri(), null, null) /** * Transforms [AndroidTask.DelayedRelation]s to real [org.dmfs.tasks.contract.TaskContract.Property.Relation]s. Only * useful when [useDelayedRelations] is active. * When tasks are added or updated, they may refer to related tasks by UID ([Relation.RELATED_UID]). * However, those related tasks may not be available (for instance, because they have not been * synchronized yet), so that the tasks provider can't establish the actual relation (= set * [Relation.TASK_ID]) in the database. * * As soon as such a related task is added, OpenTasks updates the [Relation.RELATED_ID], * but it does *not* update [Tasks.PARENT_ID] of the parent task: * https://github.com/dmfs/opentasks/issues/877 * * This method shall be called after all tasks have been synchronized. It touches * * - all [Relation] rows * - with [Relation.RELATED_ID] (→ related task is already synchronized) * - of tasks without [Tasks.PARENT_ID] (→ only touch relevant rows) * * so that missing [Tasks.PARENT_ID] fields are updated. * * @return number of touched [Relation] rows */ fun commitRelations() { Constants.log.fine("Commiting relations") val batch = BatchOperation(provider.client) provider.client.query(tasksPropertiesSyncUri(), arrayOf(Properties.PROPERTY_ID, Properties.TASK_ID, Relation.RELATED_TYPE, Relation.RELATED_UID), "${Properties.MIMETYPE}=?", arrayOf(AndroidTask.DelayedRelation.CONTENT_ITEM_TYPE), null)?.use { cursor -> fun touchRelations(): Int { Constants.log.fine("Touching relations to set parent_id") val batchOperation = BatchOperation(provider.client) provider.client.query(tasksSyncUri(true), null, "${Tasks.LIST_ID}=? AND ${Tasks.PARENT_ID} IS NULL AND ${Relation.MIMETYPE}=? AND ${Relation.RELATED_ID} IS NOT NULL", arrayOf(id.toString(), Relation.CONTENT_ITEM_TYPE), null, null)?.use { cursor -> while (cursor.moveToNext()) { val id = cursor.getLong(0) val taskId = cursor.getLong(1) val relatedType = cursor.getInt(2) val relatedUid = cursor.getString(3) // create new Relation row batch.enqueue(BatchOperation.Operation( ContentProviderOperation.newInsert(tasksPropertiesSyncUri()) .withValue(Relation.TASK_ID, taskId) .withValue(Relation.MIMETYPE, Relation.CONTENT_ITEM_TYPE) .withValue(Relation.RELATED_TYPE, relatedType) .withValue(Relation.RELATED_UID, relatedUid) )) // delete DelayedRelation row val delayedRelationUri = ContentUris.withAppendedId(tasksPropertiesSyncUri(), id) batch.enqueue(BatchOperation.Operation( ContentProviderOperation.newDelete(delayedRelationUri) val values = cursor.toValues() val id = values.getAsLong(Relation.PROPERTY_ID) val propertyContentUri = ContentUris.withAppendedId(tasksPropertiesSyncUri(), id) batchOperation.enqueue(BatchOperation.Operation( ContentProviderOperation.newUpdate(propertyContentUri) .withValue(Relation.RELATED_ID, values.getAsLong(Relation.RELATED_ID)) )) } } batch.commit() return batchOperation.commit() } Loading Loading @@ -187,7 +181,15 @@ abstract class AndroidTaskList<out T: AndroidTask>( fun taskListSyncUri() = TaskProvider.syncAdapterUri(ContentUris.withAppendedId(provider.taskListsUri(), id), account) fun tasksSyncUri() = TaskProvider.syncAdapterUri(provider.tasksUri(), account) fun tasksSyncUri(loadProperties: Boolean = false): Uri { val uri = TaskProvider.syncAdapterUri(provider.tasksUri(), account) return if (loadProperties) uri .buildUpon() .appendQueryParameter(TaskContract.LOAD_PROPERTIES, "1") .build() else uri } fun tasksPropertiesSyncUri() = TaskProvider.syncAdapterUri(provider.propertiesUri(), account) }