You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tasks/app/src/main/java/com/todoroo/astrid/service/TaskMover.kt

186 lines
7.4 KiB
Kotlin

package com.todoroo.astrid.service
import android.content.Context
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.CaldavFilter
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.GtasksFilter
import com.todoroo.astrid.data.Task
import dagger.hilt.android.qualifiers.ApplicationContext
import org.tasks.BuildConfig
import org.tasks.LocalBroadcastManager
import org.tasks.caldav.VtodoCache
import org.tasks.data.*
import org.tasks.db.DbUtils.dbchunk
import org.tasks.preferences.Preferences
import org.tasks.sync.SyncAdapters
import javax.inject.Inject
class TaskMover @Inject constructor(
@param:ApplicationContext private val context: Context,
private val taskDao: TaskDao,
private val caldavDao: CaldavDao,
private val googleTaskDao: GoogleTaskDao,
private val googleTaskListDao: GoogleTaskListDao,
private val preferences: Preferences,
private val localBroadcastManager: LocalBroadcastManager,
private val syncAdapters: SyncAdapters,
private val vtodoCache: VtodoCache,
) {
suspend fun getSingleFilter(tasks: List<Long>): Filter? {
val caldavCalendars = caldavDao.getCalendars(tasks)
val googleTaskLists = googleTaskDao.getLists(tasks)
if (caldavCalendars.isEmpty()) {
if (googleTaskLists.size == 1) {
return GtasksFilter(googleTaskListDao.getByRemoteId(googleTaskLists[0]))
}
} else if (googleTaskLists.isEmpty()) {
if (caldavCalendars.size == 1) {
return CaldavFilter(caldavDao.getCalendar(caldavCalendars[0]))
}
}
return null
}
suspend fun move(ids: List<Long>, selectedList: Filter) {
val tasks = ids
.dbchunk()
.flatMap { taskDao.getChildren(it) }
.let { taskDao.fetch(ids.minus(it.toSet())) }
.filterNot { it.readOnly }
val taskIds = tasks.map { it.id }
taskDao.setParent(0, ids.intersect(taskIds.toSet()).toList())
tasks.forEach { performMove(it, selectedList) }
if (selectedList is CaldavFilter) {
caldavDao.updateParents(selectedList.uuid)
}
taskIds.dbchunk().forEach {
taskDao.touch(it)
}
localBroadcastManager.broadcastRefresh()
syncAdapters.sync()
}
suspend fun migrateLocalTasks() {
val list = caldavDao.getLocalList(context)
move(taskDao.getLocalTasks(), CaldavFilter(list))
}
private suspend fun performMove(task: Task, selectedList: Filter) {
googleTaskDao.getByTaskId(task.id)?.let {
moveGoogleTask(task, it, selectedList)
return
}
caldavDao.getTask(task.id)?.let {
moveCaldavTask(task, it, selectedList)
return
}
moveLocalTask(task, selectedList)
}
private suspend fun moveGoogleTask(task: Task, googleTask: CaldavTask, selected: Filter) {
if (selected is GtasksFilter && googleTask.calendar == selected.remoteId) {
return
}
val id = task.id
val children = taskDao.getChildren(id)
caldavDao.markDeleted(children + id, DateUtilities.now())
when(selected) {
is GtasksFilter -> {
val listId = selected.remoteId
googleTaskDao.insertAndShift(
task = task,
caldavTask = CaldavTask(id, listId, remoteId = null),
top = preferences.addTasksToTop()
)
children.takeIf { it.isNotEmpty() }
?.map { CaldavTask(task = it, calendar = listId, remoteId = null) }
?.let { googleTaskDao.insert(it) }
}
is CaldavFilter -> {
val listId = selected.uuid
val newParent = CaldavTask(id, listId)
caldavDao.insert(task, newParent, preferences.addTasksToTop())
children.map {
val newChild = CaldavTask(it, listId)
newChild.remoteParent = newParent.remoteId
newChild
}.let { caldavDao.insert(it) }
}
else -> require(!BuildConfig.DEBUG)
}
}
private suspend fun moveCaldavTask(task: Task, caldavTask: CaldavTask, selected: Filter) {
if (selected is CaldavFilter
&& caldavTask.calendar == selected.uuid) {
return
}
val id = task.id
val childIds = taskDao.getChildren(id)
val toDelete = arrayListOf(id)
var children: List<CaldavTask> = emptyList()
if (childIds.isNotEmpty()) {
children = caldavDao.getTasks(childIds)
toDelete.addAll(childIds)
}
caldavDao.markDeleted(toDelete, DateUtilities.now())
when (selected) {
is CaldavFilter -> {
val from = caldavDao.getCalendar(caldavTask.calendar!!)
val id1 = caldavTask.task
val listId = selected.uuid
val newParent = CaldavTask(id1, listId, caldavTask.remoteId, caldavTask.`object`)
vtodoCache.move(from!!, selected.calendar, caldavTask)
caldavDao.insert(task, newParent, preferences.addTasksToTop())
children.takeIf { it.isNotEmpty() }
?.map {
val newChild = CaldavTask(it.task, listId, it.remoteId, it.`object`)
vtodoCache.move(from, selected.calendar, it)
newChild.remoteParent = it.remoteParent
newChild
}
?.let { caldavDao.insert(it) }
}
is GtasksFilter -> moveToGoogleTasks(id, childIds, selected)
else -> require(!BuildConfig.DEBUG)
}
}
private suspend fun moveLocalTask(task: Task, selected: Filter) {
when (selected) {
is GtasksFilter -> moveToGoogleTasks(task.id, taskDao.getChildren(task.id), selected)
is CaldavFilter -> {
val id = task.id
val listId = selected.uuid
val tasks: MutableMap<Long, CaldavTask> = HashMap()
val root = CaldavTask(id, listId)
val children = taskDao.getChildren(id).mapNotNull { taskDao.fetch(it) }
for (child in children) {
val newTask = CaldavTask(child.id, listId)
val parent = child.parent
newTask.remoteParent = (if (parent == id) root else tasks[parent])!!.remoteId
tasks[child.id] = newTask
}
caldavDao.insert(task, root, preferences.addTasksToTop())
caldavDao.insert(tasks.values)
}
else -> require(!BuildConfig.DEBUG)
}
}
private suspend fun moveToGoogleTasks(id: Long, children: List<Long>, filter: GtasksFilter) {
val task = taskDao.fetch(id) ?: return
taskDao.setParent(id, children)
val listId = filter.remoteId
googleTaskDao.insertAndShift(
task,
CaldavTask(id, listId, remoteId = null),
preferences.addTasksToTop()
)
children.takeIf { it.isNotEmpty() }
?.map { CaldavTask(it, listId, remoteId = null) }
?.let { googleTaskDao.insert(it) }
}
}