Implement drag and drop reordering for CalDAV

pull/996/head
Alex Baker 4 years ago
parent bb84f0d3c3
commit e6beaddbd6

@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 75,
"identityHash": "4a32edd266822b077c230270a019d9b3",
"identityHash": "ff70ddcfc863c0ebc97522480d78d23d",
"entities": [
{
"tableName": "notification",
@ -935,7 +935,7 @@
},
{
"tableName": "caldav_tasks",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`cd_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cd_task` INTEGER NOT NULL, `cd_calendar` TEXT, `cd_object` TEXT, `cd_remote_id` TEXT, `cd_etag` TEXT, `cd_last_sync` INTEGER NOT NULL, `cd_deleted` INTEGER NOT NULL, `cd_vtodo` TEXT, `cd_remote_parent` TEXT, `cd_order` INTEGER, `cd_remote_order` INTEGER, `cd_moved` INTEGER NOT NULL)",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`cd_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cd_task` INTEGER NOT NULL, `cd_calendar` TEXT, `cd_object` TEXT, `cd_remote_id` TEXT, `cd_etag` TEXT, `cd_last_sync` INTEGER NOT NULL, `cd_deleted` INTEGER NOT NULL, `cd_vtodo` TEXT, `cd_remote_parent` TEXT, `cd_order` INTEGER)",
"fields": [
{
"fieldPath": "id",
@ -1002,18 +1002,6 @@
"columnName": "cd_order",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "remoteOrder",
"columnName": "cd_remote_order",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "moved",
"columnName": "cd_moved",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
@ -1162,7 +1150,7 @@
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4a32edd266822b077c230270a019d9b3')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ff70ddcfc863c0ebc97522480d78d23d')"
]
}
}

@ -25,35 +25,49 @@ open class CaldavManualSortTaskAdapter internal constructor(private val taskDao:
override fun moved(from: Int, to: Int, indent: Int) {
val task = getTask(from)
val previous = if (to > 0) getTask(to - 1) else null
var newParent = task.parent
if (indent == 0) {
newParent = 0
} else if (previous != null) {
when {
indent == previous.getIndent() -> newParent = previous.parent
indent > previous.getIndent() -> newParent = previous.id
indent < previous.getIndent() -> {
newParent = previous.parent
var currentIndex = to
for (i in 0 until previous.getIndent() - indent) {
var thisParent = newParent
while (newParent == thisParent) {
thisParent = getTask(--currentIndex).parent
}
newParent = thisParent
}
}
}
}
val newParent = findNewParent(indent, to)
// If nothing is changing, return
if (newParent == task.parent) {
if (newParent == task.parent && from == to) {
return
}
changeParent(task, newParent)
if (newParent != task.parent) {
changeParent(task, newParent)
}
if (from != to) {
val newPosition = when {
previous == null -> 1
indent > previous.getIndent() -> 1
indent == previous.getIndent() -> previous.caldavSortOrder + 1
else -> getTask((to - 1 downTo 0).find { getTask(it).indent == indent }!!).caldavSortOrder + 1
}
caldavDao.move(task, newParent, newPosition)
}
taskDao.touch(task.id)
}
internal fun findNewParent(indent: Int, to: Int): Long {
val previous = if (to > 0) getTask(to - 1) else null
return when {
indent == 0 || previous == null -> 0
indent == previous.getIndent() -> previous.parent
indent > previous.getIndent() -> previous.id
else -> {
var newParent = previous.parent
var currentIndex = to
for (i in 0 until previous.getIndent() - indent) {
var thisParent = newParent
while (newParent == thisParent) {
thisParent = getTask(--currentIndex).parent
}
newParent = thisParent
}
newParent
}
}
}
internal fun changeParent(task: TaskContainer, newParent: Long) {
val caldavTask = task.getCaldavTask()
if (newParent == 0L) {
@ -64,9 +78,8 @@ open class CaldavManualSortTaskAdapter internal constructor(private val taskDao:
caldavTask.cd_remote_parent = parentTask.remoteId
task.parent = newParent
}
caldavDao.update(caldavTask)
caldavDao.updateParent(caldavTask)
taskDao.save(task.getTask(), null)
taskDao.touch(task.id)
}
private fun taskIsChild(source: TaskContainer, destinationIndex: Int): Boolean {

@ -8,36 +8,9 @@ class CaldavTaskAdapter internal constructor(taskDao: TaskDao, caldavDao: Caldav
override fun moved(from: Int, to: Int, indent: Int) {
val task = getTask(from)
val previous = if (to > 0) getTask(to - 1) else null
var newParent = task.parent
if (indent == 0) {
newParent = 0
} else if (previous != null) {
when {
indent == previous.getIndent() -> {
newParent = previous.parent
}
indent > previous.getIndent() -> {
newParent = previous.id
}
indent < previous.getIndent() -> {
newParent = previous.parent
var currentIndex = to
for (i in 0 until previous.getIndent() - indent) {
var thisParent = newParent
while (newParent == thisParent) {
thisParent = getTask(--currentIndex).parent
}
newParent = thisParent
}
}
}
val newParent = findNewParent(indent, to)
if (newParent != task.parent) {
changeParent(task, newParent)
}
// If nothing is changing, return
if (newParent == task.parent) {
return
}
changeParent(task, newParent)
}
}

@ -213,7 +213,6 @@ public class Upgrader {
Long order = iCalendar.Companion.getOrder(remoteTask);
if (order != null) {
task.setOrder(order);
task.setRemoteOrder(order);
caldavDao.update(task);
}
}

@ -7,9 +7,11 @@ import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.helper.UUIDHelper
import com.todoroo.astrid.service.TaskCreator
import net.fortuna.ical4j.model.Parameter
import net.fortuna.ical4j.model.Property
import net.fortuna.ical4j.model.parameter.RelType
import net.fortuna.ical4j.model.property.Geo
import net.fortuna.ical4j.model.property.RelatedTo
import net.fortuna.ical4j.model.property.XProperty
import org.tasks.Strings.isNullOrEmpty
import org.tasks.caldav.GeoUtils.equalish
import org.tasks.caldav.GeoUtils.toGeo
@ -36,10 +38,16 @@ class iCalendar @Inject constructor(
private val caldavDao: CaldavDao) {
companion object {
private const val APPLE_SORT_ORDER = "X-APPLE-SORT-ORDER"
private val IS_PARENT = { r: RelatedTo? ->
r!!.parameters.isEmpty || r.getParameter(Parameter.RELTYPE) === RelType.PARENT
}
private val IS_APPLE_SORT_ORDER = { x: Property? ->
x?.name.equals(APPLE_SORT_ORDER, true)
}
fun fromVtodo(vtodo: String): Task? {
try {
val tasks = tasksFromReader(StringReader(vtodo))
@ -70,10 +78,24 @@ class iCalendar @Inject constructor(
}
}
val Task.order: Long?
var Task.order: Long?
get() = unknownProperties
.find { it.name?.equals("x-apple-sort-order", true) == true }
.let { it?.value?.toLong() }
.find { it.name?.equals(APPLE_SORT_ORDER, true) == true }
.let { it?.value?.toLongOrNull() }
set(order) {
if (order == null) {
unknownProperties.removeAll(unknownProperties.filter(IS_APPLE_SORT_ORDER))
} else {
val existingOrder = unknownProperties
.find { it.name?.equals(APPLE_SORT_ORDER, true) == true }
if (existingOrder != null) {
existingOrder.value = order.toString()
} else {
unknownProperties.add(XProperty(APPLE_SORT_ORDER, order.toString()))
}
}
}
}
fun setPlace(taskId: Long, geo: Geo) {
@ -116,6 +138,7 @@ class iCalendar @Inject constructor(
fun toVtodo(caldavTask: CaldavTask, task: com.todoroo.astrid.data.Task): ByteArray {
val remoteModel = CaldavConverter.toCaldav(caldavTask, task)
remoteModel.order = caldavTask.order
val categories = remoteModel.categories
categories.clear()
categories.addAll(tagDataDao.getTagDataForTask(task.id).map { it.name!! })
@ -155,8 +178,7 @@ class iCalendar @Inject constructor(
caldavTask = existing
}
CaldavConverter.apply(task, remote)
caldavTask.remoteOrder = remote.order
caldavTask.order = caldavTask.remoteOrder // TODO: remove me
caldavTask.order = remote.order
val geo = remote.geoPosition
if (geo == null) {
locationDao.getActiveGeofences(task.id).forEach {

@ -53,12 +53,15 @@ abstract class CaldavDao {
@Update
abstract fun update(caldavTask: CaldavTask)
fun update(caldavTask: SubsetCaldav) {
fun updateParent(caldavTask: SubsetCaldav) {
update(caldavTask.cd_id, caldavTask.cd_remote_parent)
}
@Query("UPDATE caldav_tasks SET cd_order = :position WHERE cd_id = :id")
internal abstract fun update(id: Long, position: Long)
@Query("UPDATE caldav_tasks SET cd_remote_parent = :remoteParent WHERE cd_id = :id")
abstract fun update(id: Long, remoteParent: String?)
internal abstract fun update(id: Long, remoteParent: String?)
@Update
abstract fun update(tasks: Iterable<CaldavTask>)
@ -167,6 +170,20 @@ abstract class CaldavDao {
+ "WHERE _id IN (SELECT _id FROM tasks INNER JOIN caldav_tasks ON _id = cd_task WHERE cd_deleted = 0 AND cd_calendar = :calendar)")
abstract fun updateParents(calendar: String)
@Transaction
open fun move(task: TaskContainer, newParent: Long, newPosition: Long) {
val previousParent = task.parent
val caldavTask = task.caldavTask
val previousPosition = task.caldavSortOrder
if (newParent == previousParent && newPosition < previousPosition) {
shiftDown(task.caldav, newParent, newPosition, previousPosition)
} else {
shiftDown(task.caldav, newParent, newPosition)
}
caldavTask.cd_order = newPosition
update(caldavTask.cd_id, caldavTask.cd_order)
}
@Transaction
open fun shiftDown(calendar: String, parent: Long, from: Long, to: Long? = null) {
val updated = ArrayList<CaldavTask>()

@ -44,13 +44,6 @@ class CaldavTask {
@Transient
var order: Long? = null
@ColumnInfo(name = "cd_remote_order")
var remoteOrder: Long? = null
@ColumnInfo(name = "cd_moved")
@Transient
var moved = false
constructor()
@Ignore
@ -72,7 +65,7 @@ class CaldavTask {
fun isDeleted() = deleted > 0
override fun toString(): String {
return "CaldavTask(id=$id, task=$task, calendar=$calendar, `object`=$`object`, remoteId=$remoteId, etag=$etag, lastSync=$lastSync, deleted=$deleted, vtodo=$vtodo, remoteParent=$remoteParent, order=$order, remoteOrder=$remoteOrder, moved=$moved)"
return "CaldavTask(id=$id, task=$task, calendar=$calendar, `object`=$`object`, remoteId=$remoteId, etag=$etag, lastSync=$lastSync, deleted=$deleted, vtodo=$vtodo, remoteParent=$remoteParent, order=$order)"
}
companion object {

@ -197,4 +197,8 @@ public class TaskContainer {
public boolean isCollapsed() {
return task.isCollapsed();
}
public long getCaldavSortOrder() {
return indent == 0 ? primarySort : secondarySort;
}
}

@ -433,8 +433,6 @@ public class Migrations {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `caldav_tasks` ADD COLUMN `cd_order` INTEGER");
database.execSQL("ALTER TABLE `caldav_tasks` ADD COLUMN `cd_remote_order` INTEGER");
database.execSQL("ALTER TABLE `caldav_tasks` ADD COLUMN `cd_moved` INTEGER NOT NULL DEFAULT 0");
}
};

Loading…
Cancel
Save