Make daos suspending

pull/1043/head
Alex Baker 4 years ago
parent 72e59bdcd0
commit 122a2c2170

@ -24,8 +24,8 @@ import org.tasks.data.Place
import org.tasks.data.SubtaskInfo import org.tasks.data.SubtaskInfo
import org.tasks.data.TaskContainer import org.tasks.data.TaskContainer
import org.tasks.data.TaskListQuery import org.tasks.data.TaskListQuery
import org.tasks.db.DbUtils.chunkedMap import org.tasks.db.SuspendDbUtils.chunkedMap
import org.tasks.db.DbUtils.eachChunk import org.tasks.db.SuspendDbUtils.eachChunk
import org.tasks.jobs.WorkManager import org.tasks.jobs.WorkManager
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.time.DateTimeUtils.currentTimeMillis import org.tasks.time.DateTimeUtils.currentTimeMillis
@ -39,56 +39,52 @@ abstract class TaskDao(private val database: Database) {
this.workManager = workManager this.workManager = workManager
} }
fun needsRefresh(): List<Task> {
return needsRefresh(DateUtilities.now())
}
@Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0 AND (hideUntil > :now OR dueDate > :now)") @Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0 AND (hideUntil > :now OR dueDate > :now)")
abstract fun needsRefresh(now: Long): List<Task> abstract suspend fun needsRefresh(now: Long = DateUtilities.now()): List<Task>
fun fetchBlocking(id: Long) = runBlocking { suspend fun fetchBlocking(id: Long) = runBlocking {
fetch(id) fetch(id)
} }
@Query("SELECT * FROM tasks WHERE _id = :id LIMIT 1") @Query("SELECT * FROM tasks WHERE _id = :id LIMIT 1")
abstract suspend fun fetch(id: Long): Task? abstract suspend fun fetch(id: Long): Task?
fun fetch(ids: List<Long>): List<Task> = ids.chunkedMap(this::fetchInternal) suspend fun fetch(ids: List<Long>): List<Task> = ids.chunkedMap(this::fetchInternal)
@Query("SELECT * FROM tasks WHERE _id IN (:ids)") @Query("SELECT * FROM tasks WHERE _id IN (:ids)")
internal abstract fun fetchInternal(ids: List<Long>): List<Task> internal abstract suspend fun fetchInternal(ids: List<Long>): List<Task>
@Query("SELECT COUNT(1) FROM tasks WHERE timerStart > 0 AND deleted = 0") @Query("SELECT COUNT(1) FROM tasks WHERE timerStart > 0 AND deleted = 0")
abstract fun activeTimers(): Int abstract suspend fun activeTimers(): Int
@Query("SELECT tasks.* FROM tasks INNER JOIN notification ON tasks._id = notification.task") @Query("SELECT tasks.* FROM tasks INNER JOIN notification ON tasks._id = notification.task")
abstract fun activeNotifications(): List<Task> abstract suspend fun activeNotifications(): List<Task>
@Query("SELECT * FROM tasks WHERE remoteId = :remoteId") @Query("SELECT * FROM tasks WHERE remoteId = :remoteId")
abstract fun fetch(remoteId: String): Task? abstract suspend fun fetch(remoteId: String): Task?
@Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0") @Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0")
abstract fun getActiveTasks(): List<Task> abstract suspend fun getActiveTasks(): List<Task>
@Query("SELECT * FROM tasks WHERE hideUntil < (strftime('%s','now')*1000)") @Query("SELECT * FROM tasks WHERE hideUntil < (strftime('%s','now')*1000)")
abstract fun getVisibleTasks(): List<Task> abstract suspend fun getVisibleTasks(): List<Task>
@Query("SELECT * FROM tasks WHERE remoteId IN (:remoteIds) " @Query("SELECT * FROM tasks WHERE remoteId IN (:remoteIds) "
+ "AND recurrence IS NOT NULL AND LENGTH(recurrence) > 0") + "AND recurrence IS NOT NULL AND LENGTH(recurrence) > 0")
abstract fun getRecurringTasks(remoteIds: List<String>): List<Task> abstract suspend fun getRecurringTasks(remoteIds: List<String>): List<Task>
@Query("UPDATE tasks SET completed = :completionDate " + "WHERE remoteId = :remoteId") @Query("UPDATE tasks SET completed = :completionDate " + "WHERE remoteId = :remoteId")
abstract fun setCompletionDate(remoteId: String, completionDate: Long) abstract suspend fun setCompletionDate(remoteId: String, completionDate: Long)
@Query("UPDATE tasks SET snoozeTime = :millis WHERE _id in (:taskIds)") @Query("UPDATE tasks SET snoozeTime = :millis WHERE _id in (:taskIds)")
abstract fun snooze(taskIds: List<Long>, millis: Long) abstract suspend fun snooze(taskIds: List<Long>, millis: Long)
@Query("SELECT tasks.* FROM tasks " @Query("SELECT tasks.* FROM tasks "
+ "LEFT JOIN google_tasks ON tasks._id = google_tasks.gt_task " + "LEFT JOIN google_tasks ON tasks._id = google_tasks.gt_task "
+ "WHERE gt_list_id IN (SELECT gtl_remote_id FROM google_task_lists WHERE gtl_account = :account)" + "WHERE gt_list_id IN (SELECT gtl_remote_id FROM google_task_lists WHERE gtl_account = :account)"
+ "AND (tasks.modified > google_tasks.gt_last_sync OR google_tasks.gt_remote_id = '' OR google_tasks.gt_deleted > 0) " + "AND (tasks.modified > google_tasks.gt_last_sync OR google_tasks.gt_remote_id = '' OR google_tasks.gt_deleted > 0) "
+ "ORDER BY CASE WHEN gt_parent = 0 THEN 0 ELSE 1 END, gt_order ASC") + "ORDER BY CASE WHEN gt_parent = 0 THEN 0 ELSE 1 END, gt_order ASC")
abstract fun getGoogleTasksToPush(account: String): List<Task> abstract suspend fun getGoogleTasksToPush(account: String): List<Task>
@Query(""" @Query("""
SELECT tasks.* SELECT tasks.*
@ -96,39 +92,37 @@ abstract class TaskDao(private val database: Database) {
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 (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)""")
abstract fun getCaldavTasksToPush(calendar: String): List<Task> abstract suspend fun getCaldavTasksToPush(calendar: String): List<Task>
@Query("SELECT * FROM TASKS " @Query("SELECT * FROM TASKS "
+ "WHERE completed = 0 AND deleted = 0 AND (notificationFlags > 0 OR notifications > 0)") + "WHERE completed = 0 AND deleted = 0 AND (notificationFlags > 0 OR notifications > 0)")
abstract fun getTasksWithReminders(): List<Task> abstract suspend fun getTasksWithReminders(): List<Task>
// --- SQL clause generators // --- SQL clause generators
@Query("SELECT * FROM tasks") @Query("SELECT * FROM tasks")
abstract fun getAll(): List<Task> abstract suspend fun getAll(): List<Task>
@Query("SELECT calendarUri FROM tasks " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''") @Query("SELECT calendarUri FROM tasks " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''")
abstract fun getAllCalendarEvents(): List<String> abstract suspend fun getAllCalendarEvents(): List<String>
@Query("UPDATE tasks SET calendarUri = '' " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''") @Query("UPDATE tasks SET calendarUri = '' " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''")
abstract fun clearAllCalendarEvents(): Int abstract suspend fun clearAllCalendarEvents(): Int
@Query("SELECT calendarUri FROM tasks " @Query("SELECT calendarUri FROM tasks "
+ "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''") + "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''")
abstract fun getCompletedCalendarEvents(): List<String> abstract suspend fun getCompletedCalendarEvents(): List<String>
@Query("UPDATE tasks SET calendarUri = '' " @Query("UPDATE tasks SET calendarUri = '' "
+ "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''") + "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''")
abstract fun clearCompletedCalendarEvents(): Int abstract suspend fun clearCompletedCalendarEvents(): Int
@Transaction @Transaction
open fun fetchTasks(callback: (SubtaskInfo) -> List<String>): List<TaskContainer> { open suspend fun fetchTasks(callback: (SubtaskInfo) -> List<String>): List<TaskContainer> {
return runBlocking { return fetchTasks(callback, getSubtaskInfo())
fetchTasks(callback, getSubtaskInfo())
}
} }
@Transaction @Transaction
open fun fetchTasks(callback: (SubtaskInfo) -> List<String>, subtasks: SubtaskInfo): List<TaskContainer> { open suspend fun fetchTasks(callback: (SubtaskInfo) -> List<String>, subtasks: SubtaskInfo): List<TaskContainer> {
assertNotMainThread() assertNotMainThread()
val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0 val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0
@ -143,17 +137,17 @@ abstract class TaskDao(private val database: Database) {
return result return result
} }
fun fetchTasks(preferences: Preferences, filter: Filter): List<TaskContainer> { suspend fun fetchTasks(preferences: Preferences, filter: Filter): List<TaskContainer> {
return fetchTasks { return fetchTasks {
TaskListQuery.getQuery(preferences, filter, it) TaskListQuery.getQuery(preferences, filter, it)
} }
} }
@RawQuery @RawQuery
abstract fun fetchTasks(query: SimpleSQLiteQuery): List<TaskContainer> abstract suspend fun fetchTasks(query: SimpleSQLiteQuery): List<TaskContainer>
@RawQuery @RawQuery
abstract fun count(query: SimpleSQLiteQuery): Int abstract suspend fun count(query: SimpleSQLiteQuery): Int
@Query(""" @Query("""
SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtasks, SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtasks,
@ -167,31 +161,30 @@ SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtas
abstract suspend fun getSubtaskInfo(): SubtaskInfo abstract suspend fun getSubtaskInfo(): SubtaskInfo
@RawQuery(observedEntities = [Place::class]) @RawQuery(observedEntities = [Place::class])
abstract fun getTaskFactory( abstract fun getTaskFactory(query: SimpleSQLiteQuery): DataSource.Factory<Int, TaskContainer>
query: SimpleSQLiteQuery): DataSource.Factory<Int, TaskContainer>
fun touch(id: Long) = touch(listOf(id)) suspend fun touch(id: Long) = touch(listOf(id))
fun touch(ids: List<Long>) { suspend fun touch(ids: List<Long>) {
ids.eachChunk { touchInternal(it) } ids.eachChunk { touchInternal(it) }
workManager.sync(false) workManager.sync(false)
} }
@Query("UPDATE tasks SET modified = :now WHERE _id in (:ids)") @Query("UPDATE tasks SET modified = :now WHERE _id in (:ids)")
abstract fun touchInternal(ids: List<Long>, now: Long = currentTimeMillis()) abstract suspend fun touchInternal(ids: List<Long>, now: Long = currentTimeMillis())
fun setParent(parent: Long, tasks: List<Long>) = suspend fun setParent(parent: Long, tasks: List<Long>) =
tasks.eachChunk { setParentInternal(parent, it) } tasks.eachChunk { setParentInternal(parent, it) }
@Query("UPDATE tasks SET parent = :parent WHERE _id IN (:children)") @Query("UPDATE tasks SET parent = :parent WHERE _id IN (:children)")
internal abstract fun setParentInternal(parent: Long, children: List<Long>) internal abstract suspend fun setParentInternal(parent: Long, children: List<Long>)
@Transaction @Transaction
open fun fetchChildren(id: Long): List<Task> { open suspend fun fetchChildren(id: Long): List<Task> {
return fetch(getChildren(id)) return fetch(getChildren(id))
} }
fun getChildren(id: Long): List<Long> { suspend fun getChildren(id: Long): List<Long> {
return getChildren(listOf(id)) return getChildren(listOf(id))
} }
@ -207,13 +200,13 @@ SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtas
+ " ON recursive_tasks.task = tasks.parent" + " ON recursive_tasks.task = tasks.parent"
+ " WHERE tasks.deleted = 0)" + " WHERE tasks.deleted = 0)"
+ "SELECT task FROM recursive_tasks") + "SELECT task FROM recursive_tasks")
abstract fun getChildren(ids: List<Long>): List<Long> abstract suspend fun getChildren(ids: List<Long>): List<Long>
@Query("UPDATE tasks SET collapsed = :collapsed WHERE _id = :id") @Query("UPDATE tasks SET collapsed = :collapsed WHERE _id = :id")
abstract fun setCollapsed(id: Long, collapsed: Boolean) abstract suspend fun setCollapsed(id: Long, collapsed: Boolean)
@Transaction @Transaction
open fun setCollapsed(preferences: Preferences, filter: Filter, collapsed: Boolean) { open suspend fun setCollapsed(preferences: Preferences, filter: Filter, collapsed: Boolean) {
fetchTasks(preferences, filter) fetchTasks(preferences, filter)
.filter(TaskContainer::hasChildren) .filter(TaskContainer::hasChildren)
.map(TaskContainer::getId) .map(TaskContainer::getId)
@ -221,7 +214,7 @@ SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtas
} }
@Query("UPDATE tasks SET collapsed = :collapsed WHERE _id IN (:ids)") @Query("UPDATE tasks SET collapsed = :collapsed WHERE _id IN (:ids)")
abstract fun collapse(ids: List<Long>, collapsed: Boolean) abstract suspend fun collapse(ids: List<Long>, collapsed: Boolean)
// --- save // --- save
// TODO: get rid of this super-hack // TODO: get rid of this super-hack
@ -229,8 +222,10 @@ SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtas
* Saves the given task to the database.getDatabase(). Task must already exist. Returns true on * Saves the given task to the database.getDatabase(). Task must already exist. Returns true on
* success. * success.
*/ */
@JvmOverloads
fun save(task: Task, original: Task? = fetchBlocking(task.id)) { suspend fun save(task: Task) = save(task, fetchBlocking(task.id))
suspend fun save(task: Task, original: Task?) {
if (!task.insignificantChange(original)) { if (!task.insignificantChange(original)) {
task.modificationDate = DateUtilities.now() task.modificationDate = DateUtilities.now()
} }
@ -240,12 +235,12 @@ SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtas
} }
@Insert @Insert
abstract fun insert(task: Task): Long abstract suspend fun insert(task: Task): Long
@Update @Update
abstract fun update(task: Task): Int abstract suspend fun update(task: Task): Int
fun createNew(task: Task) { suspend fun createNew(task: Task) {
task.id = NO_ID task.id = NO_ID
if (task.creationDate == 0L) { if (task.creationDate == 0L) {
task.creationDate = DateUtilities.now() task.creationDate = DateUtilities.now()
@ -257,7 +252,7 @@ SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtas
task.id = insert task.id = insert
} }
fun count(filter: Filter): Int { suspend fun count(filter: Filter): Int {
val query = getQuery(filter.sqlQuery, Field.COUNT) val query = getQuery(filter.sqlQuery, Field.COUNT)
val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0 val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0
val count = count(query) val count = count(query)
@ -265,11 +260,11 @@ SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtas
return count return count
} }
fun fetchFiltered(filter: Filter): List<Task> { suspend fun fetchFiltered(filter: Filter): List<Task> {
return fetchFiltered(filter.getSqlQuery()) return fetchFiltered(filter.getSqlQuery())
} }
fun fetchFiltered(queryTemplate: String): List<Task> { suspend fun fetchFiltered(queryTemplate: String): List<Task> {
val query = getQuery(queryTemplate, Task.FIELDS) val query = getQuery(queryTemplate, Task.FIELDS)
val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0 val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0
val tasks = fetchTasks(query) val tasks = fetchTasks(query)
@ -278,7 +273,7 @@ SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtas
} }
@Query("SELECT _id FROM tasks LEFT JOIN google_tasks ON _id = gt_task AND gt_deleted = 0 LEFT JOIN caldav_tasks ON _id = cd_task AND cd_deleted = 0 WHERE gt_id IS NULL AND cd_id IS NULL AND parent = 0") @Query("SELECT _id FROM tasks LEFT JOIN google_tasks ON _id = gt_task AND gt_deleted = 0 LEFT JOIN caldav_tasks ON _id = cd_task AND cd_deleted = 0 WHERE gt_id IS NULL AND cd_id IS NULL AND parent = 0")
abstract fun getLocalTasks(): List<Long> abstract suspend fun getLocalTasks(): List<Long>
/** Generates SQL clauses */ /** Generates SQL clauses */
object TaskCriteria { object TaskCriteria {

@ -10,22 +10,22 @@ interface AlarmDao {
@Query("SELECT alarms.* FROM alarms INNER JOIN tasks ON tasks._id = alarms.task " @Query("SELECT alarms.* FROM alarms INNER JOIN tasks ON tasks._id = alarms.task "
+ "WHERE tasks.completed = 0 AND tasks.deleted = 0 AND tasks.lastNotified < alarms.time " + "WHERE tasks.completed = 0 AND tasks.deleted = 0 AND tasks.lastNotified < alarms.time "
+ "ORDER BY time ASC") + "ORDER BY time ASC")
fun getActiveAlarms(): List<Alarm> suspend fun getActiveAlarms(): List<Alarm>
@Query("SELECT alarms.* FROM alarms INNER JOIN tasks ON tasks._id = alarms.task " @Query("SELECT alarms.* FROM alarms INNER JOIN tasks ON tasks._id = alarms.task "
+ "WHERE tasks._id = :taskId AND tasks.completed = 0 AND tasks.deleted = 0 AND tasks.lastNotified < alarms.time " + "WHERE tasks._id = :taskId AND tasks.completed = 0 AND tasks.deleted = 0 AND tasks.lastNotified < alarms.time "
+ "ORDER BY time ASC") + "ORDER BY time ASC")
fun getActiveAlarms(taskId: Long): List<Alarm> suspend fun getActiveAlarms(taskId: Long): List<Alarm>
@Query("SELECT * FROM alarms WHERE task = :taskId ORDER BY time ASC") @Query("SELECT * FROM alarms WHERE task = :taskId ORDER BY time ASC")
fun getAlarms(taskId: Long): List<Alarm> suspend fun getAlarms(taskId: Long): List<Alarm>
@Delete @Delete
fun delete(alarm: Alarm) suspend fun delete(alarm: Alarm)
@Insert @Insert
fun insert(alarm: Alarm): Long suspend fun insert(alarm: Alarm): Long
@Insert @Insert
fun insert(alarms: Iterable<Alarm>) suspend fun insert(alarms: Iterable<Alarm>)
} }

@ -10,7 +10,7 @@ import com.todoroo.astrid.data.Task
import com.todoroo.astrid.helper.UUIDHelper import com.todoroo.astrid.helper.UUIDHelper
import org.tasks.R import org.tasks.R
import org.tasks.date.DateTimeUtils.toAppleEpoch import org.tasks.date.DateTimeUtils.toAppleEpoch
import org.tasks.db.DbUtils.chunkedMap import org.tasks.db.SuspendDbUtils.chunkedMap
import org.tasks.filters.CaldavFilters import org.tasks.filters.CaldavFilters
import org.tasks.time.DateTimeUtils.currentTimeMillis import org.tasks.time.DateTimeUtils.currentTimeMillis
@ -20,41 +20,41 @@ abstract class CaldavDao {
abstract fun subscribeToCalendars(): LiveData<List<CaldavCalendar>> abstract fun subscribeToCalendars(): LiveData<List<CaldavCalendar>>
@Query("SELECT * FROM caldav_lists WHERE cdl_uuid = :uuid LIMIT 1") @Query("SELECT * FROM caldav_lists WHERE cdl_uuid = :uuid LIMIT 1")
abstract fun getCalendarByUuid(uuid: String): CaldavCalendar? abstract suspend fun getCalendarByUuid(uuid: String): CaldavCalendar?
@Query("SELECT * FROM caldav_lists WHERE cdl_account = :uuid") @Query("SELECT * FROM caldav_lists WHERE cdl_account = :uuid")
abstract fun getCalendarsByAccount(uuid: String): List<CaldavCalendar> abstract suspend fun getCalendarsByAccount(uuid: String): List<CaldavCalendar>
@Query("SELECT * FROM caldav_accounts WHERE cda_uuid = :uuid LIMIT 1") @Query("SELECT * FROM caldav_accounts WHERE cda_uuid = :uuid LIMIT 1")
abstract fun getAccountByUuid(uuid: String): CaldavAccount? abstract suspend fun getAccountByUuid(uuid: String): CaldavAccount?
@Query("SELECT COUNT(*) FROM caldav_accounts WHERE cda_account_type != 2") @Query("SELECT COUNT(*) FROM caldav_accounts WHERE cda_account_type != 2")
abstract fun accountCount(): Int abstract suspend fun accountCount(): Int
@Query("SELECT * FROM caldav_accounts ORDER BY cda_account_type, UPPER(cda_name)") @Query("SELECT * FROM caldav_accounts ORDER BY cda_account_type, UPPER(cda_name)")
abstract fun getAccounts(): List<CaldavAccount> abstract suspend fun getAccounts(): List<CaldavAccount>
@Query("UPDATE caldav_accounts SET cda_collapsed = :collapsed WHERE cda_id = :id") @Query("UPDATE caldav_accounts SET cda_collapsed = :collapsed WHERE cda_id = :id")
abstract fun setCollapsed(id: Long, collapsed: Boolean) abstract suspend fun setCollapsed(id: Long, collapsed: Boolean)
@Insert @Insert
abstract fun insert(caldavAccount: CaldavAccount): Long abstract suspend fun insert(caldavAccount: CaldavAccount): Long
@Update @Update
abstract fun update(caldavAccount: CaldavAccount) abstract suspend fun update(caldavAccount: CaldavAccount)
fun insert(caldavCalendar: CaldavCalendar) { suspend fun insert(caldavCalendar: CaldavCalendar) {
caldavCalendar.id = insertInternal(caldavCalendar) caldavCalendar.id = insertInternal(caldavCalendar)
} }
@Insert @Insert
abstract fun insertInternal(caldavCalendar: CaldavCalendar): Long abstract suspend fun insertInternal(caldavCalendar: CaldavCalendar): Long
@Update @Update
abstract fun update(caldavCalendar: CaldavCalendar) abstract suspend fun update(caldavCalendar: CaldavCalendar)
@Transaction @Transaction
open fun insert(task: Task, caldavTask: CaldavTask, addToTop: Boolean): Long { open suspend fun insert(task: Task, caldavTask: CaldavTask, addToTop: Boolean): Long {
if (caldavTask.order != null) { if (caldavTask.order != null) {
return insert(caldavTask) return insert(caldavTask)
} }
@ -71,104 +71,104 @@ abstract class CaldavDao {
} }
@Query("SELECT MIN(IFNULL(cd_order, (created - $APPLE_EPOCH) / 1000)) FROM caldav_tasks INNER JOIN tasks ON _id = cd_task WHERE cd_calendar = :calendar AND cd_deleted = 0 AND deleted = 0 AND parent = :parent") @Query("SELECT MIN(IFNULL(cd_order, (created - $APPLE_EPOCH) / 1000)) FROM caldav_tasks INNER JOIN tasks ON _id = cd_task WHERE cd_calendar = :calendar AND cd_deleted = 0 AND deleted = 0 AND parent = :parent")
internal abstract fun findFirstTask(calendar: String, parent: Long): Long? internal abstract suspend fun findFirstTask(calendar: String, parent: Long): Long?
@Query("SELECT MAX(IFNULL(cd_order, (created - $APPLE_EPOCH) / 1000)) FROM caldav_tasks INNER JOIN tasks ON _id = cd_task WHERE cd_calendar = :calendar AND cd_deleted = 0 AND deleted = 0 AND parent = :parent") @Query("SELECT MAX(IFNULL(cd_order, (created - $APPLE_EPOCH) / 1000)) FROM caldav_tasks INNER JOIN tasks ON _id = cd_task WHERE cd_calendar = :calendar AND cd_deleted = 0 AND deleted = 0 AND parent = :parent")
internal abstract fun findLastTask(calendar: String, parent: Long): Long? internal abstract suspend fun findLastTask(calendar: String, parent: Long): Long?
@Insert @Insert
abstract fun insert(caldavTask: CaldavTask): Long abstract suspend fun insert(caldavTask: CaldavTask): Long
@Insert @Insert
abstract fun insert(tasks: Iterable<CaldavTask>) abstract suspend fun insert(tasks: Iterable<CaldavTask>)
@Update @Update
abstract fun update(caldavTask: CaldavTask) abstract suspend fun update(caldavTask: CaldavTask)
fun update(caldavTask: SubsetCaldav) { suspend fun update(caldavTask: SubsetCaldav) {
update(caldavTask.cd_id, caldavTask.cd_order, caldavTask.cd_remote_parent) update(caldavTask.cd_id, caldavTask.cd_order, caldavTask.cd_remote_parent)
} }
@Query("UPDATE caldav_tasks SET cd_order = :position, cd_remote_parent = :parent WHERE cd_id = :id") @Query("UPDATE caldav_tasks SET cd_order = :position, cd_remote_parent = :parent WHERE cd_id = :id")
internal abstract fun update(id: Long, position: Long?, parent: String?) internal abstract suspend fun update(id: Long, position: Long?, parent: String?)
@Query("UPDATE caldav_tasks SET cd_order = :position WHERE cd_id = :id") @Query("UPDATE caldav_tasks SET cd_order = :position WHERE cd_id = :id")
internal abstract fun update(id: Long, position: Long?) internal abstract suspend fun update(id: Long, position: Long?)
@Query("UPDATE caldav_tasks SET cd_remote_parent = :remoteParent WHERE cd_id = :id") @Query("UPDATE caldav_tasks SET cd_remote_parent = :remoteParent WHERE cd_id = :id")
internal abstract fun update(id: Long, remoteParent: String?) internal abstract suspend fun update(id: Long, remoteParent: String?)
@Update @Update
abstract fun update(tasks: Iterable<CaldavTask>) abstract suspend fun update(tasks: Iterable<CaldavTask>)
@Delete @Delete
abstract fun delete(caldavTask: CaldavTask) abstract suspend fun delete(caldavTask: 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 fun getDeleted(calendar: String): List<CaldavTask> abstract suspend fun getDeleted(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 fun markDeleted(tasks: List<Long>, now: Long = currentTimeMillis()) abstract suspend fun markDeleted(tasks: List<Long>, now: Long = currentTimeMillis())
@Query("SELECT * FROM caldav_tasks WHERE cd_task = :taskId AND cd_deleted = 0 LIMIT 1") @Query("SELECT * FROM caldav_tasks WHERE cd_task = :taskId AND cd_deleted = 0 LIMIT 1")
abstract fun getTask(taskId: Long): CaldavTask? abstract suspend fun getTask(taskId: Long): CaldavTask?
@Query("SELECT cd_remote_id FROM caldav_tasks WHERE cd_task = :taskId AND cd_deleted = 0") @Query("SELECT cd_remote_id FROM caldav_tasks WHERE cd_task = :taskId AND cd_deleted = 0")
abstract fun getRemoteIdForTask(taskId: Long): String? abstract suspend fun getRemoteIdForTask(taskId: Long): String?
@Query("SELECT * FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_object = :obj LIMIT 1") @Query("SELECT * FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_object = :obj LIMIT 1")
abstract fun getTask(calendar: String, obj: String): CaldavTask? abstract suspend fun getTask(calendar: String, obj: String): CaldavTask?
@Query("SELECT * FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_remote_id = :remoteId") @Query("SELECT * FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_remote_id = :remoteId")
abstract fun getTaskByRemoteId(calendar: String, remoteId: String): CaldavTask? abstract suspend fun getTaskByRemoteId(calendar: String, remoteId: String): CaldavTask?
@Query("SELECT * FROM caldav_tasks WHERE cd_task = :taskId") @Query("SELECT * FROM caldav_tasks WHERE cd_task = :taskId")
abstract fun getTasks(taskId: Long): List<CaldavTask> abstract suspend fun getTasks(taskId: Long): List<CaldavTask>
@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 fun getTasks(taskIds: List<Long>): List<CaldavTask> abstract suspend fun getTasks(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 "
+ "WHERE cd_deleted = 0 AND cd_vtodo IS NOT NULL AND cd_vtodo != ''") + "WHERE cd_deleted = 0 AND cd_vtodo IS NOT NULL AND cd_vtodo != ''")
abstract fun getTasks(): List<CaldavTaskContainer> abstract suspend fun getTasks(): List<CaldavTaskContainer>
@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 "
+ "WHERE cd_calendar = :calendar " + "WHERE cd_calendar = :calendar "
+ "AND modified > cd_last_sync " + "AND modified > cd_last_sync "
+ "AND cd_deleted = 0") + "AND cd_deleted = 0")
abstract fun getCaldavTasksToPush(calendar: String): List<CaldavTaskContainer> abstract suspend fun getCaldavTasksToPush(calendar: String): List<CaldavTaskContainer>
@Query("SELECT * FROM caldav_lists ORDER BY cdl_name COLLATE NOCASE") @Query("SELECT * FROM caldav_lists ORDER BY cdl_name COLLATE NOCASE")
abstract fun getCalendars(): List<CaldavCalendar> abstract suspend fun getCalendars(): List<CaldavCalendar>
@Query("SELECT * FROM caldav_lists WHERE cdl_uuid = :uuid LIMIT 1") @Query("SELECT * FROM caldav_lists WHERE cdl_uuid = :uuid LIMIT 1")
abstract fun getCalendar(uuid: String): CaldavCalendar? abstract suspend fun getCalendar(uuid: String): CaldavCalendar?
@Query("SELECT cd_object FROM caldav_tasks WHERE cd_calendar = :calendar") @Query("SELECT cd_object FROM caldav_tasks WHERE cd_calendar = :calendar")
abstract fun getObjects(calendar: String): List<String> abstract suspend fun getObjects(calendar: String): List<String>
fun getTasks(calendar: String, objects: List<String>): List<Long> = suspend fun getTasks(calendar: String, objects: List<String>): List<Long> =
objects.chunkedMap { getTasksInternal(calendar, it) } objects.chunkedMap { getTasksInternal(calendar, it) }
@Query("SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_object IN (:objects)") @Query("SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_object IN (:objects)")
abstract fun getTasksInternal(calendar: String, objects: List<String>): List<Long> abstract suspend fun getTasksInternal(calendar: String, objects: List<String>): List<Long>
@Query("SELECT * FROM caldav_lists WHERE cdl_account = :account AND cdl_url NOT IN (:urls)") @Query("SELECT * FROM caldav_lists WHERE cdl_account = :account AND cdl_url NOT IN (:urls)")
abstract fun findDeletedCalendars(account: String, urls: List<String>): List<CaldavCalendar> abstract suspend fun findDeletedCalendars(account: String, urls: List<String>): List<CaldavCalendar>
@Query("SELECT * FROM caldav_lists WHERE cdl_account = :account AND cdl_url = :url LIMIT 1") @Query("SELECT * FROM caldav_lists WHERE cdl_account = :account AND cdl_url = :url LIMIT 1")
abstract fun getCalendarByUrl(account: String, url: String): CaldavCalendar? abstract suspend fun getCalendarByUrl(account: String, url: String): CaldavCalendar?
@Query("SELECT caldav_accounts.* from caldav_accounts" @Query("SELECT caldav_accounts.* from caldav_accounts"
+ " INNER JOIN caldav_tasks ON cd_task = :task" + " INNER JOIN caldav_tasks ON cd_task = :task"
+ " INNER JOIN caldav_lists ON cd_calendar = cdl_uuid" + " INNER JOIN caldav_lists ON cd_calendar = cdl_uuid"
+ " WHERE cdl_account = cda_uuid") + " WHERE cdl_account = cda_uuid")
abstract fun getAccountForTask(task: Long): CaldavAccount? abstract suspend fun getAccountForTask(task: Long): CaldavAccount?
@Query("SELECT DISTINCT cd_calendar FROM caldav_tasks WHERE cd_deleted = 0 AND cd_task IN (:tasks)") @Query("SELECT DISTINCT cd_calendar FROM caldav_tasks WHERE cd_deleted = 0 AND cd_task IN (:tasks)")
abstract fun getCalendars(tasks: List<Long>): List<String> abstract suspend fun getCalendars(tasks: List<Long>): List<String>
@Query("SELECT caldav_lists.*, COUNT(tasks._id) AS count" @Query("SELECT caldav_lists.*, COUNT(tasks._id) AS count"
+ " FROM caldav_lists" + " FROM caldav_lists"
@ -176,13 +176,13 @@ abstract class CaldavDao {
+ " LEFT JOIN tasks ON caldav_tasks.cd_task = tasks._id AND tasks.deleted = 0 AND tasks.completed = 0 AND tasks.hideUntil < :now AND cd_deleted = 0" + " LEFT JOIN tasks ON caldav_tasks.cd_task = tasks._id AND tasks.deleted = 0 AND tasks.completed = 0 AND tasks.hideUntil < :now AND cd_deleted = 0"
+ " WHERE caldav_lists.cdl_account = :uuid" + " WHERE caldav_lists.cdl_account = :uuid"
+ " GROUP BY caldav_lists.cdl_uuid") + " GROUP BY caldav_lists.cdl_uuid")
abstract fun getCaldavFilters(uuid: String, now: Long = currentTimeMillis()): List<CaldavFilters> abstract suspend fun getCaldavFilters(uuid: String, now: Long = currentTimeMillis()): List<CaldavFilters>
@Query("SELECT tasks._id FROM tasks " @Query("SELECT tasks._id FROM tasks "
+ "INNER JOIN tags ON tags.task = tasks._id " + "INNER JOIN tags ON tags.task = tasks._id "
+ "INNER JOIN caldav_tasks ON cd_task = tasks._id " + "INNER JOIN caldav_tasks ON cd_task = tasks._id "
+ "GROUP BY tasks._id") + "GROUP BY tasks._id")
abstract fun getTasksWithTags(): List<Long> abstract suspend fun getTasksWithTags(): List<Long>
@Query("UPDATE tasks SET parent = IFNULL((" @Query("UPDATE tasks SET parent = IFNULL(("
+ " SELECT p.cd_task FROM caldav_tasks AS p" + " SELECT p.cd_task FROM caldav_tasks AS p"
@ -191,7 +191,7 @@ abstract class CaldavDao {
+ " AND p.cd_calendar = caldav_tasks.cd_calendar" + " AND p.cd_calendar = caldav_tasks.cd_calendar"
+ " AND p.cd_deleted = 0), 0)" + " AND p.cd_deleted = 0), 0)"
+ "WHERE _id IN (SELECT _id FROM tasks INNER JOIN caldav_tasks ON _id = cd_task WHERE cd_deleted = 0)") + "WHERE _id IN (SELECT _id FROM tasks INNER JOIN caldav_tasks ON _id = cd_task WHERE cd_deleted = 0)")
abstract fun updateParents() abstract suspend fun updateParents()
@Query("UPDATE tasks SET parent = IFNULL((" @Query("UPDATE tasks SET parent = IFNULL(("
+ " SELECT p.cd_task FROM caldav_tasks AS p" + " SELECT p.cd_task FROM caldav_tasks AS p"
@ -202,10 +202,10 @@ abstract class CaldavDao {
+ " AND p.cd_calendar = caldav_tasks.cd_calendar" + " AND p.cd_calendar = caldav_tasks.cd_calendar"
+ " AND caldav_tasks.cd_deleted = 0), 0)" + " AND caldav_tasks.cd_deleted = 0), 0)"
+ "WHERE _id IN (SELECT _id FROM tasks INNER JOIN caldav_tasks ON _id = cd_task WHERE cd_deleted = 0 AND cd_calendar = :calendar)") + "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) abstract suspend fun updateParents(calendar: String)
@Transaction @Transaction
open fun move(task: TaskContainer, newParent: Long, newPosition: Long?) { open suspend fun move(task: TaskContainer, newParent: Long, newPosition: Long?) {
val previousParent = task.parent val previousParent = task.parent
val caldavTask = task.caldavTask val caldavTask = task.caldavTask
val previousPosition = task.caldavSortOrder val previousPosition = task.caldavSortOrder
@ -221,7 +221,7 @@ abstract class CaldavDao {
} }
@Transaction @Transaction
open fun shiftDown(calendar: String, parent: Long, from: Long, to: Long? = null) { open suspend fun shiftDown(calendar: String, parent: Long, from: Long, to: Long? = null) {
val updated = ArrayList<CaldavTask>() val updated = ArrayList<CaldavTask>()
val tasks = getTasksToShift(calendar, parent, from, to) val tasks = getTasksToShift(calendar, parent, from, to)
for (i in tasks.indices) { for (i in tasks.indices) {
@ -240,32 +240,32 @@ abstract class CaldavDao {
} }
@Query("UPDATE tasks SET modified = :modificationTime WHERE _id in (:ids)") @Query("UPDATE tasks SET modified = :modificationTime WHERE _id in (:ids)")
internal abstract fun touchInternal(ids: List<Long>, modificationTime: Long = now()) internal abstract suspend fun touchInternal(ids: List<Long>, modificationTime: Long = now())
@Query("SELECT task.*, caldav_task.*, IFNULL(cd_order, (created - $APPLE_EPOCH) / 1000) AS primary_sort FROM caldav_tasks AS caldav_task INNER JOIN tasks AS task ON _id = cd_task WHERE cd_calendar = :calendar AND parent = :parent AND cd_deleted = 0 AND deleted = 0 AND primary_sort >= :from AND primary_sort < IFNULL(:to, ${Long.MAX_VALUE}) ORDER BY primary_sort") @Query("SELECT task.*, caldav_task.*, IFNULL(cd_order, (created - $APPLE_EPOCH) / 1000) AS primary_sort FROM caldav_tasks AS caldav_task INNER JOIN tasks AS task ON _id = cd_task WHERE cd_calendar = :calendar AND parent = :parent AND cd_deleted = 0 AND deleted = 0 AND primary_sort >= :from AND primary_sort < IFNULL(:to, ${Long.MAX_VALUE}) ORDER BY primary_sort")
internal abstract fun getTasksToShift(calendar: String, parent: Long, from: Long, to: Long?): List<CaldavTaskContainer> internal abstract suspend fun getTasksToShift(calendar: String, parent: Long, from: Long, to: Long?): List<CaldavTaskContainer>
@Query("UPDATE caldav_lists SET cdl_order = $NO_ORDER") @Query("UPDATE caldav_lists SET cdl_order = $NO_ORDER")
abstract fun resetOrders() abstract suspend fun resetOrders()
@Query("UPDATE caldav_lists SET cdl_order = :order WHERE cdl_id = :id") @Query("UPDATE caldav_lists SET cdl_order = :order WHERE cdl_id = :id")
abstract fun setOrder(id: Long, order: Int) abstract suspend fun setOrder(id: Long, order: Int)
fun setupLocalAccount(context: Context): CaldavAccount { suspend fun setupLocalAccount(context: Context): CaldavAccount {
val account = getLocalAccount() val account = getLocalAccount()
getLocalList(context, account) getLocalList(context, account)
return account return account
} }
fun getLocalList(context: Context) = getLocalList(context, getLocalAccount()) suspend fun getLocalList(context: Context) = getLocalList(context, getLocalAccount())
private fun getLocalAccount() = getAccountByUuid(LOCAL) ?: CaldavAccount().apply { private suspend fun getLocalAccount() = getAccountByUuid(LOCAL) ?: CaldavAccount().apply {
accountType = CaldavAccount.TYPE_LOCAL accountType = CaldavAccount.TYPE_LOCAL
uuid = LOCAL uuid = LOCAL
id = insert(this) id = insert(this)
} }
private fun getLocalList(context: Context, account: CaldavAccount): CaldavCalendar = private suspend fun getLocalList(context: Context, account: CaldavAccount): CaldavCalendar =
getCalendarsByAccount(account.uuid!!).getOrNull(0) getCalendarsByAccount(account.uuid!!).getOrNull(0)
?: CaldavCalendar(context.getString(R.string.default_list), UUIDHelper.newUUID()).apply { ?: CaldavCalendar(context.getString(R.string.default_list), UUIDHelper.newUUID()).apply {
this.account = account.uuid this.account = account.uuid

@ -10,7 +10,7 @@ import com.todoroo.astrid.data.Task
@Dao @Dao
interface ContentProviderDao { interface ContentProviderDao {
@Query("SELECT name FROM tags WHERE task = :taskId ORDER BY UPPER(name) ASC") @Query("SELECT name FROM tags WHERE task = :taskId ORDER BY UPPER(name) ASC")
fun getTagNames(taskId: Long): List<String> suspend fun getTagNames(taskId: Long): List<String>
@Query(""" @Query("""
SELECT * SELECT *
@ -25,10 +25,10 @@ interface ContentProviderDao {
172800000 * importance 172800000 * importance
ASC ASC
LIMIT 100""") LIMIT 100""")
fun getAstrid2TaskProviderTasks(): List<Task> suspend fun getAstrid2TaskProviderTasks(): List<Task>
@Query("SELECT * FROM tagdata WHERE name IS NOT NULL AND name != '' ORDER BY UPPER(name) ASC") @Query("SELECT * FROM tagdata WHERE name IS NOT NULL AND name != '' ORDER BY UPPER(name) ASC")
fun tagDataOrderedByName(): List<TagData> suspend fun tagDataOrderedByName(): List<TagData>
@Query("SELECT * FROM tasks") @Query("SELECT * FROM tasks")
fun getTasks(): Cursor fun getTasks(): Cursor

@ -5,31 +5,31 @@ import androidx.room.Delete
import androidx.room.Query import androidx.room.Query
import androidx.room.Transaction import androidx.room.Transaction
import org.tasks.data.CaldavDao.Companion.LOCAL import org.tasks.data.CaldavDao.Companion.LOCAL
import org.tasks.db.DbUtils.eachChunk import org.tasks.db.SuspendDbUtils.eachChunk
import java.util.* import java.util.*
@Dao @Dao
abstract class DeletionDao { abstract class DeletionDao {
@Query("DELETE FROM caldav_tasks WHERE cd_task IN(:ids)") @Query("DELETE FROM caldav_tasks WHERE cd_task IN(:ids)")
abstract fun deleteCaldavTasks(ids: List<Long>) abstract suspend fun deleteCaldavTasks(ids: List<Long>)
@Query("DELETE FROM google_tasks WHERE gt_task IN(:ids)") @Query("DELETE FROM google_tasks WHERE gt_task IN(:ids)")
abstract fun deleteGoogleTasks(ids: List<Long>) abstract suspend fun deleteGoogleTasks(ids: List<Long>)
@Query("DELETE FROM tags WHERE task IN(:ids)") @Query("DELETE FROM tags WHERE task IN(:ids)")
abstract fun deleteTags(ids: List<Long>) abstract suspend fun deleteTags(ids: List<Long>)
@Query("DELETE FROM geofences WHERE task IN(:ids)") @Query("DELETE FROM geofences WHERE task IN(:ids)")
abstract fun deleteGeofences(ids: List<Long>) abstract suspend fun deleteGeofences(ids: List<Long>)
@Query("DELETE FROM alarms WHERE task IN(:ids)") @Query("DELETE FROM alarms WHERE task IN(:ids)")
abstract fun deleteAlarms(ids: List<Long>) abstract suspend fun deleteAlarms(ids: List<Long>)
@Query("DELETE FROM tasks WHERE _id IN(:ids)") @Query("DELETE FROM tasks WHERE _id IN(:ids)")
abstract fun deleteTasks(ids: List<Long>) abstract suspend fun deleteTasks(ids: List<Long>)
@Transaction @Transaction
open fun delete(ids: List<Long>) { open suspend fun delete(ids: List<Long>) {
ids.eachChunk { ids.eachChunk {
deleteAlarms(it) deleteAlarms(it)
deleteGeofences(it) deleteGeofences(it)
@ -43,20 +43,20 @@ abstract class DeletionDao {
@Query("UPDATE tasks " @Query("UPDATE tasks "
+ "SET modified = (strftime('%s','now')*1000), deleted = (strftime('%s','now')*1000)" + "SET modified = (strftime('%s','now')*1000), deleted = (strftime('%s','now')*1000)"
+ "WHERE _id IN(:ids)") + "WHERE _id IN(:ids)")
abstract fun markDeletedInternal(ids: List<Long>) abstract suspend fun markDeletedInternal(ids: List<Long>)
fun markDeleted(ids: Iterable<Long>) { suspend fun markDeleted(ids: Iterable<Long>) {
ids.eachChunk(this::markDeletedInternal) ids.eachChunk(this::markDeletedInternal)
} }
@Query("SELECT gt_task FROM google_tasks WHERE gt_deleted = 0 AND gt_list_id = :listId") @Query("SELECT gt_task FROM google_tasks WHERE gt_deleted = 0 AND gt_list_id = :listId")
abstract fun getActiveGoogleTasks(listId: String): List<Long> abstract suspend fun getActiveGoogleTasks(listId: String): List<Long>
@Delete @Delete
abstract fun deleteGoogleTaskList(googleTaskList: GoogleTaskList) abstract suspend fun deleteGoogleTaskList(googleTaskList: GoogleTaskList)
@Transaction @Transaction
open fun delete(googleTaskList: GoogleTaskList): List<Long> { open suspend fun delete(googleTaskList: GoogleTaskList): List<Long> {
val tasks = getActiveGoogleTasks(googleTaskList.remoteId!!) val tasks = getActiveGoogleTasks(googleTaskList.remoteId!!)
delete(tasks) delete(tasks)
deleteGoogleTaskList(googleTaskList) deleteGoogleTaskList(googleTaskList)
@ -64,13 +64,13 @@ abstract class DeletionDao {
} }
@Delete @Delete
abstract fun deleteGoogleTaskAccount(googleTaskAccount: GoogleTaskAccount) abstract suspend fun deleteGoogleTaskAccount(googleTaskAccount: GoogleTaskAccount)
@Query("SELECT * FROM google_task_lists WHERE gtl_account = :account ORDER BY gtl_title ASC") @Query("SELECT * FROM google_task_lists WHERE gtl_account = :account ORDER BY gtl_title ASC")
abstract fun getLists(account: String): List<GoogleTaskList> abstract suspend fun getLists(account: String): List<GoogleTaskList>
@Transaction @Transaction
open fun delete(googleTaskAccount: GoogleTaskAccount): List<Long> { open suspend fun delete(googleTaskAccount: GoogleTaskAccount): List<Long> {
val deleted = ArrayList<Long>() val deleted = ArrayList<Long>()
for (list in getLists(googleTaskAccount.account!!)) { for (list in getLists(googleTaskAccount.account!!)) {
deleted.addAll(delete(list)) deleted.addAll(delete(list))
@ -80,13 +80,13 @@ abstract class DeletionDao {
} }
@Query("SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_deleted = 0") @Query("SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_deleted = 0")
abstract fun getActiveCaldavTasks(calendar: String): List<Long> abstract suspend fun getActiveCaldavTasks(calendar: String): List<Long>
@Delete @Delete
abstract fun deleteCaldavCalendar(caldavCalendar: CaldavCalendar) abstract suspend fun deleteCaldavCalendar(caldavCalendar: CaldavCalendar)
@Transaction @Transaction
open fun delete(caldavCalendar: CaldavCalendar): List<Long> { open suspend fun delete(caldavCalendar: CaldavCalendar): List<Long> {
val tasks = getActiveCaldavTasks(caldavCalendar.uuid!!) val tasks = getActiveCaldavTasks(caldavCalendar.uuid!!)
delete(tasks) delete(tasks)
deleteCaldavCalendar(caldavCalendar) deleteCaldavCalendar(caldavCalendar)
@ -94,16 +94,16 @@ abstract class DeletionDao {
} }
@Query("SELECT * FROM caldav_lists WHERE cdl_account = :account") @Query("SELECT * FROM caldav_lists WHERE cdl_account = :account")
abstract fun getCalendars(account: String): List<CaldavCalendar> abstract suspend fun getCalendars(account: String): List<CaldavCalendar>
@Delete @Delete
abstract fun deleteCaldavAccount(caldavAccount: CaldavAccount) abstract suspend fun deleteCaldavAccount(caldavAccount: CaldavAccount)
@Query("DELETE FROM tasks WHERE _id IN (SELECT _id FROM tasks INNER JOIN caldav_tasks ON _id = cd_task INNER JOIN caldav_lists ON cdl_uuid = cd_calendar WHERE cdl_account = '$LOCAL' AND deleted > 0)") @Query("DELETE FROM tasks WHERE _id IN (SELECT _id FROM tasks INNER JOIN caldav_tasks ON _id = cd_task INNER JOIN caldav_lists ON cdl_uuid = cd_calendar WHERE cdl_account = '$LOCAL' AND deleted > 0)")
abstract fun purgeDeleted() abstract suspend fun purgeDeleted()
@Transaction @Transaction
open fun delete(caldavAccount: CaldavAccount): List<Long> { open suspend fun delete(caldavAccount: CaldavAccount): List<Long> {
val deleted = ArrayList<Long>() val deleted = ArrayList<Long>()
for (calendar in getCalendars(caldavAccount.uuid!!)) { for (calendar in getCalendars(caldavAccount.uuid!!)) {
deleted.addAll(delete(calendar)) deleted.addAll(delete(calendar))

@ -9,29 +9,29 @@ import com.todoroo.astrid.api.FilterListItem.NO_ORDER
@Dao @Dao
interface FilterDao { interface FilterDao {
@Update @Update
fun update(filter: Filter) suspend fun update(filter: Filter)
@Query("DELETE FROM filters WHERE _id = :id") @Query("DELETE FROM filters WHERE _id = :id")
fun delete(id: Long) suspend fun delete(id: Long)
@Query("SELECT * FROM filters WHERE title = :title COLLATE NOCASE LIMIT 1") @Query("SELECT * FROM filters WHERE title = :title COLLATE NOCASE LIMIT 1")
fun getByName(title: String): Filter? suspend fun getByName(title: String): Filter?
@Insert @Insert
fun insert(filter: Filter): Long suspend fun insert(filter: Filter): Long
@Query("SELECT * FROM filters") @Query("SELECT * FROM filters")
fun getFilters(): List<Filter> suspend fun getFilters(): List<Filter>
@Query("SELECT * FROM filters WHERE _id = :id LIMIT 1") @Query("SELECT * FROM filters WHERE _id = :id LIMIT 1")
fun getById(id: Long): Filter? suspend fun getById(id: Long): Filter?
@Query("SELECT * FROM filters") @Query("SELECT * FROM filters")
fun getAll(): List<Filter> suspend fun getAll(): List<Filter>
@Query("UPDATE filters SET f_order = $NO_ORDER") @Query("UPDATE filters SET f_order = $NO_ORDER")
fun resetOrders() suspend fun resetOrders()
@Query("UPDATE filters SET f_order = :order WHERE _id = :id") @Query("UPDATE filters SET f_order = :order WHERE _id = :id")
fun setOrder(id: Long, order: Int) suspend fun setOrder(id: Long, order: Int)
} }

@ -7,13 +7,13 @@ import org.tasks.time.DateTimeUtils.currentTimeMillis
@Dao @Dao
abstract class GoogleTaskDao { abstract class GoogleTaskDao {
@Insert @Insert
abstract fun insert(task: GoogleTask): Long abstract suspend fun insert(task: GoogleTask): Long
@Insert @Insert
abstract fun insert(tasks: Iterable<GoogleTask>) abstract suspend fun insert(tasks: Iterable<GoogleTask>)
@Transaction @Transaction
open fun insertAndShift(task: GoogleTask, top: Boolean) { open suspend fun insertAndShift(task: GoogleTask, top: Boolean) {
if (top) { if (top) {
task.order = 0 task.order = 0
shiftDown(task.listId!!, task.parent, 0) shiftDown(task.listId!!, task.parent, 0)
@ -24,19 +24,19 @@ abstract class GoogleTaskDao {
} }
@Query("UPDATE google_tasks SET gt_order = gt_order + 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order >= :position") @Query("UPDATE google_tasks SET gt_order = gt_order + 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order >= :position")
abstract fun shiftDown(listId: String, parent: Long, position: Long) abstract suspend fun shiftDown(listId: String, parent: Long, position: Long)
@Query("UPDATE google_tasks SET gt_order = gt_order - 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order > :from AND gt_order <= :to") @Query("UPDATE google_tasks SET gt_order = gt_order - 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order > :from AND gt_order <= :to")
abstract fun shiftUp(listId: String, parent: Long, from: Long, to: Long) abstract suspend fun shiftUp(listId: String, parent: Long, from: Long, to: Long)
@Query("UPDATE google_tasks SET gt_order = gt_order + 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order < :from AND gt_order >= :to") @Query("UPDATE google_tasks SET gt_order = gt_order + 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order < :from AND gt_order >= :to")
abstract fun shiftDown(listId: String, parent: Long, from: Long, to: Long) abstract suspend fun shiftDown(listId: String, parent: Long, from: Long, to: Long)
@Query("UPDATE google_tasks SET gt_order = gt_order - 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order >= :position") @Query("UPDATE google_tasks SET gt_order = gt_order - 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order >= :position")
abstract fun shiftUp(listId: String, parent: Long, position: Long) abstract suspend fun shiftUp(listId: String, parent: Long, position: Long)
@Transaction @Transaction
open fun move(task: SubsetGoogleTask, newParent: Long, newPosition: Long) { open suspend fun move(task: SubsetGoogleTask, newParent: Long, newPosition: Long) {
val previousParent = task.parent val previousParent = task.parent
val previousPosition = task.order val previousPosition = task.order
if (newParent == previousParent) { if (newParent == previousParent) {
@ -55,67 +55,67 @@ abstract class GoogleTaskDao {
} }
@Query("UPDATE google_task_accounts SET gta_collapsed = :collapsed WHERE gta_id = :id") @Query("UPDATE google_task_accounts SET gta_collapsed = :collapsed WHERE gta_id = :id")
abstract fun setCollapsed(id: Long, collapsed: Boolean) abstract suspend fun setCollapsed(id: Long, collapsed: Boolean)
@Query("SELECT * FROM google_tasks WHERE gt_task = :taskId AND gt_deleted = 0 LIMIT 1") @Query("SELECT * FROM google_tasks WHERE gt_task = :taskId AND gt_deleted = 0 LIMIT 1")
abstract fun getByTaskId(taskId: Long): GoogleTask? abstract suspend fun getByTaskId(taskId: Long): GoogleTask?
@Update @Update
abstract fun update(googleTask: GoogleTask) abstract suspend fun update(googleTask: GoogleTask)
private fun update(googleTask: SubsetGoogleTask) { private suspend fun update(googleTask: SubsetGoogleTask) {
update(googleTask.id, googleTask.parent, googleTask.order) update(googleTask.id, googleTask.parent, googleTask.order)
} }
@Query("UPDATE google_tasks SET gt_order = :order, gt_parent = :parent, gt_moved = 1 WHERE gt_id = :id") @Query("UPDATE google_tasks SET gt_order = :order, gt_parent = :parent, gt_moved = 1 WHERE gt_id = :id")
abstract fun update(id: Long, parent: Long, order: Long) abstract suspend fun update(id: Long, parent: Long, order: Long)
@Query("UPDATE google_tasks SET gt_deleted = :now WHERE gt_task = :task OR gt_parent = :task") @Query("UPDATE google_tasks SET gt_deleted = :now WHERE gt_task = :task OR gt_parent = :task")
abstract fun markDeleted(task: Long, now: Long = currentTimeMillis()) abstract suspend fun markDeleted(task: Long, now: Long = currentTimeMillis())
@Delete @Delete
abstract fun delete(deleted: GoogleTask) abstract suspend fun delete(deleted: GoogleTask)
@Query("SELECT * FROM google_tasks WHERE gt_remote_id = :remoteId LIMIT 1") @Query("SELECT * FROM google_tasks WHERE gt_remote_id = :remoteId LIMIT 1")
abstract fun getByRemoteId(remoteId: String): GoogleTask? abstract suspend fun getByRemoteId(remoteId: String): GoogleTask?
@Query("SELECT * FROM google_tasks WHERE gt_task = :taskId AND gt_deleted > 0") @Query("SELECT * FROM google_tasks WHERE gt_task = :taskId AND gt_deleted > 0")
abstract fun getDeletedByTaskId(taskId: Long): List<GoogleTask> abstract suspend fun getDeletedByTaskId(taskId: Long): List<GoogleTask>
@Query("SELECT * FROM google_tasks WHERE gt_task = :taskId") @Query("SELECT * FROM google_tasks WHERE gt_task = :taskId")
abstract fun getAllByTaskId(taskId: Long): List<GoogleTask> abstract suspend fun getAllByTaskId(taskId: Long): List<GoogleTask>
@Query("SELECT DISTINCT gt_list_id FROM google_tasks WHERE gt_deleted = 0 AND gt_task IN (:tasks)") @Query("SELECT DISTINCT gt_list_id FROM google_tasks WHERE gt_deleted = 0 AND gt_task IN (:tasks)")
abstract fun getLists(tasks: List<Long>): List<String> abstract suspend fun getLists(tasks: List<Long>): List<String>
@Query("SELECT gt_task FROM google_tasks WHERE gt_parent IN (:ids) AND gt_deleted = 0") @Query("SELECT gt_task FROM google_tasks WHERE gt_parent IN (:ids) AND gt_deleted = 0")
abstract fun getChildren(ids: List<Long>): List<Long> abstract suspend fun getChildren(ids: List<Long>): List<Long>
@Query("SELECT tasks.* FROM tasks JOIN google_tasks ON tasks._id = gt_task WHERE gt_parent = :taskId") @Query("SELECT tasks.* FROM tasks JOIN google_tasks ON tasks._id = gt_task WHERE gt_parent = :taskId")
abstract fun getChildTasks(taskId: Long): List<Task> abstract suspend fun getChildTasks(taskId: Long): List<Task>
@Query("SELECT * FROM google_tasks WHERE gt_parent = :id AND gt_deleted = 0") @Query("SELECT * FROM google_tasks WHERE gt_parent = :id AND gt_deleted = 0")
abstract fun getChildren(id: Long): List<GoogleTask> abstract suspend fun getChildren(id: Long): List<GoogleTask>
@Query("SELECT IFNULL(MAX(gt_order), -1) + 1 FROM google_tasks WHERE gt_list_id = :listId AND gt_parent = :parent") @Query("SELECT IFNULL(MAX(gt_order), -1) + 1 FROM google_tasks WHERE gt_list_id = :listId AND gt_parent = :parent")
abstract fun getBottom(listId: String, parent: Long): Long abstract suspend fun getBottom(listId: String, parent: Long): Long
@Query("SELECT gt_remote_id FROM google_tasks JOIN tasks ON tasks._id = gt_task WHERE deleted = 0 AND gt_list_id = :listId AND gt_parent = :parent AND gt_order < :order AND gt_remote_id IS NOT NULL AND gt_remote_id != '' ORDER BY gt_order DESC") @Query("SELECT gt_remote_id FROM google_tasks JOIN tasks ON tasks._id = gt_task WHERE deleted = 0 AND gt_list_id = :listId AND gt_parent = :parent AND gt_order < :order AND gt_remote_id IS NOT NULL AND gt_remote_id != '' ORDER BY gt_order DESC")
abstract fun getPrevious(listId: String, parent: Long, order: Long): String? abstract suspend fun getPrevious(listId: String, parent: Long, order: Long): String?
@Query("SELECT gt_remote_id FROM google_tasks WHERE gt_task = :task") @Query("SELECT gt_remote_id FROM google_tasks WHERE gt_task = :task")
abstract fun getRemoteId(task: Long): String? abstract suspend fun getRemoteId(task: Long): String?
@Query("SELECT gt_task FROM google_tasks WHERE gt_remote_id = :remoteId") @Query("SELECT gt_task FROM google_tasks WHERE gt_remote_id = :remoteId")
abstract fun getTask(remoteId: String): Long abstract suspend fun getTask(remoteId: String): Long
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH) @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
@Query("SELECT google_tasks.*, gt_order AS primary_sort, NULL AS secondary_sort FROM google_tasks JOIN tasks ON tasks._id = gt_task WHERE gt_parent = 0 AND gt_list_id = :listId AND tasks.deleted = 0 UNION SELECT c.*, p.gt_order AS primary_sort, c.gt_order AS secondary_sort FROM google_tasks AS c LEFT JOIN google_tasks AS p ON c.gt_parent = p.gt_task JOIN tasks ON tasks._id = c.gt_task WHERE c.gt_parent > 0 AND c.gt_list_id = :listId AND tasks.deleted = 0 ORDER BY primary_sort ASC, secondary_sort ASC") @Query("SELECT google_tasks.*, gt_order AS primary_sort, NULL AS secondary_sort FROM google_tasks JOIN tasks ON tasks._id = gt_task WHERE gt_parent = 0 AND gt_list_id = :listId AND tasks.deleted = 0 UNION SELECT c.*, p.gt_order AS primary_sort, c.gt_order AS secondary_sort FROM google_tasks AS c LEFT JOIN google_tasks AS p ON c.gt_parent = p.gt_task JOIN tasks ON tasks._id = c.gt_task WHERE c.gt_parent > 0 AND c.gt_list_id = :listId AND tasks.deleted = 0 ORDER BY primary_sort ASC, secondary_sort ASC")
abstract fun getByLocalOrder(listId: String): List<GoogleTask> abstract suspend fun getByLocalOrder(listId: String): List<GoogleTask>
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH) @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
@Query("SELECT google_tasks.*, gt_remote_order AS primary_sort, NULL AS secondary_sort FROM google_tasks JOIN tasks ON tasks._id = gt_task WHERE gt_parent = 0 AND gt_list_id = :listId AND tasks.deleted = 0 UNION SELECT c.*, p.gt_remote_order AS primary_sort, c.gt_remote_order AS secondary_sort FROM google_tasks AS c LEFT JOIN google_tasks AS p ON c.gt_parent = p.gt_task JOIN tasks ON tasks._id = c.gt_task WHERE c.gt_parent > 0 AND c.gt_list_id = :listId AND tasks.deleted = 0 ORDER BY primary_sort ASC, secondary_sort ASC") @Query("SELECT google_tasks.*, gt_remote_order AS primary_sort, NULL AS secondary_sort FROM google_tasks JOIN tasks ON tasks._id = gt_task WHERE gt_parent = 0 AND gt_list_id = :listId AND tasks.deleted = 0 UNION SELECT c.*, p.gt_remote_order AS primary_sort, c.gt_remote_order AS secondary_sort FROM google_tasks AS c LEFT JOIN google_tasks AS p ON c.gt_parent = p.gt_task JOIN tasks ON tasks._id = c.gt_task WHERE c.gt_parent > 0 AND c.gt_list_id = :listId AND tasks.deleted = 0 ORDER BY primary_sort ASC, secondary_sort ASC")
abstract fun getByRemoteOrder(listId: String): List<GoogleTask> abstract suspend fun getByRemoteOrder(listId: String): List<GoogleTask>
@Query("UPDATE google_tasks" @Query("UPDATE google_tasks"
+ " SET gt_parent = IFNULL((" + " SET gt_parent = IFNULL(("
@ -125,16 +125,16 @@ abstract class GoogleTaskDao {
+ " AND p.gt_deleted = 0)," + " AND p.gt_deleted = 0),"
+ " 0)" + " 0)"
+ " WHERE gt_moved = 0") + " WHERE gt_moved = 0")
abstract fun updateParents() abstract suspend fun updateParents()
@Query("UPDATE google_tasks SET gt_parent = IFNULL((SELECT gt_task FROM google_tasks AS p WHERE p.gt_remote_id = google_tasks.gt_remote_parent), 0) WHERE gt_list_id = :listId AND gt_moved = 0") @Query("UPDATE google_tasks SET gt_parent = IFNULL((SELECT gt_task FROM google_tasks AS p WHERE p.gt_remote_id = google_tasks.gt_remote_parent), 0) WHERE gt_list_id = :listId AND gt_moved = 0")
abstract fun updateParents(listId: String) abstract suspend fun updateParents(listId: String)
@Query("UPDATE google_tasks SET gt_remote_parent = :parent, gt_remote_order = :position WHERE gt_remote_id = :id") @Query("UPDATE google_tasks SET gt_remote_parent = :parent, gt_remote_order = :position WHERE gt_remote_id = :id")
abstract fun updatePosition(id: String, parent: String, position: String) abstract suspend fun updatePosition(id: String, parent: String, position: String)
@Transaction @Transaction
open fun reposition(listId: String) { open suspend fun reposition(listId: String) {
updateParents(listId) updateParents(listId)
val orderedTasks = getByRemoteOrder(listId) val orderedTasks = getByRemoteOrder(listId)
var subtasks = 0L var subtasks = 0L
@ -157,7 +157,7 @@ abstract class GoogleTaskDao {
} }
} }
fun validateSorting(listId: String) { suspend fun validateSorting(listId: String) {
val orderedTasks = getByLocalOrder(listId) val orderedTasks = getByLocalOrder(listId)
var subtasks = 0L var subtasks = 0L
var parent = 0L var parent = 0L

@ -9,52 +9,52 @@ import org.tasks.time.DateTimeUtils.currentTimeMillis
@Dao @Dao
interface GoogleTaskListDao { interface GoogleTaskListDao {
@Query("SELECT COUNT(*) FROM google_task_accounts") @Query("SELECT COUNT(*) FROM google_task_accounts")
fun accountCount(): Int suspend fun accountCount(): Int
@Query("SELECT * FROM google_task_accounts") @Query("SELECT * FROM google_task_accounts")
fun getAccounts(): List<GoogleTaskAccount> suspend fun getAccounts(): List<GoogleTaskAccount>
@Query("SELECT * FROM google_task_accounts WHERE gta_account = :account COLLATE NOCASE LIMIT 1") @Query("SELECT * FROM google_task_accounts WHERE gta_account = :account COLLATE NOCASE LIMIT 1")
fun getAccount(account: String): GoogleTaskAccount? suspend fun getAccount(account: String): GoogleTaskAccount?
@Query("SELECT * FROM google_task_lists WHERE gtl_id = :id") @Query("SELECT * FROM google_task_lists WHERE gtl_id = :id")
fun getById(id: Long): GoogleTaskList? suspend fun getById(id: Long): GoogleTaskList?
@Query("SELECT * FROM google_task_lists WHERE gtl_account = :account ORDER BY gtl_title ASC") @Query("SELECT * FROM google_task_lists WHERE gtl_account = :account ORDER BY gtl_title ASC")
fun getLists(account: String): List<GoogleTaskList> suspend fun getLists(account: String): List<GoogleTaskList>
@Query("SELECT * FROM google_task_lists WHERE gtl_remote_id = :remoteId LIMIT 1") @Query("SELECT * FROM google_task_lists WHERE gtl_remote_id = :remoteId LIMIT 1")
fun getByRemoteId(remoteId: String): GoogleTaskList? suspend fun getByRemoteId(remoteId: String): GoogleTaskList?
@Query("SELECT * FROM google_task_lists WHERE gtl_remote_id IN (:remoteIds)") @Query("SELECT * FROM google_task_lists WHERE gtl_remote_id IN (:remoteIds)")
fun getByRemoteId(remoteIds: List<String>): List<GoogleTaskList> suspend fun getByRemoteId(remoteIds: List<String>): List<GoogleTaskList>
@Query("SELECT * FROM google_task_lists") @Query("SELECT * FROM google_task_lists")
fun subscribeToLists(): LiveData<List<GoogleTaskList>> fun subscribeToLists(): LiveData<List<GoogleTaskList>>
@Query("SELECT * FROM google_task_lists WHERE gtl_remote_id = :remoteId AND IFNULL(gtl_account, '') = ''") @Query("SELECT * FROM google_task_lists WHERE gtl_remote_id = :remoteId AND IFNULL(gtl_account, '') = ''")
fun findExistingList(remoteId: String): GoogleTaskList? suspend fun findExistingList(remoteId: String): GoogleTaskList?
@Query("SELECT * FROM google_task_lists") @Query("SELECT * FROM google_task_lists")
fun getAllLists(): List<GoogleTaskList> suspend fun getAllLists(): List<GoogleTaskList>
@Query("UPDATE google_task_lists SET gtl_last_sync = 0 WHERE gtl_account = :account") @Query("UPDATE google_task_lists SET gtl_last_sync = 0 WHERE gtl_account = :account")
fun resetLastSync(account: String) suspend fun resetLastSync(account: String)
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertOrReplace(googleTaskList: GoogleTaskList): Long suspend fun insertOrReplace(googleTaskList: GoogleTaskList): Long
@Insert @Insert
fun insert(googleTaskList: GoogleTaskList): Long suspend fun insert(googleTaskList: GoogleTaskList): Long
@Insert @Insert
fun insert(googleTaskAccount: GoogleTaskAccount) suspend fun insert(googleTaskAccount: GoogleTaskAccount)
@Update @Update
fun update(account: GoogleTaskAccount) suspend fun update(account: GoogleTaskAccount)
@Update @Update
fun update(list: GoogleTaskList) suspend fun update(list: GoogleTaskList)
@Query("SELECT google_task_lists.*, COUNT(tasks._id) AS count" @Query("SELECT google_task_lists.*, COUNT(tasks._id) AS count"
+ " FROM google_task_lists " + " FROM google_task_lists "
@ -62,11 +62,11 @@ interface GoogleTaskListDao {
+ " LEFT JOIN tasks ON google_tasks.gt_task = tasks._id AND tasks.deleted = 0 AND tasks.completed = 0 AND tasks.hideUntil < :now AND gt_deleted = 0" + " LEFT JOIN tasks ON google_tasks.gt_task = tasks._id AND tasks.deleted = 0 AND tasks.completed = 0 AND tasks.hideUntil < :now AND gt_deleted = 0"
+ " WHERE google_task_lists.gtl_account = :account" + " WHERE google_task_lists.gtl_account = :account"
+ " GROUP BY google_task_lists.gtl_remote_id") + " GROUP BY google_task_lists.gtl_remote_id")
fun getGoogleTaskFilters(account: String, now: Long = currentTimeMillis()): List<GoogleTaskFilters> suspend fun getGoogleTaskFilters(account: String, now: Long = currentTimeMillis()): List<GoogleTaskFilters>
@Query("UPDATE google_task_lists SET gtl_remote_order = $NO_ORDER") @Query("UPDATE google_task_lists SET gtl_remote_order = $NO_ORDER")
fun resetOrders() suspend fun resetOrders()
@Query("UPDATE google_task_lists SET gtl_remote_order = :order WHERE gtl_id = :id") @Query("UPDATE google_task_lists SET gtl_remote_order = :order WHERE gtl_id = :id")
fun setOrder(id: Long, order: Int) suspend fun setOrder(id: Long, order: Int)
} }

@ -15,7 +15,7 @@ interface LocationDao {
+ " WHERE tasks.completed = 0 AND tasks.deleted = 0" + " WHERE tasks.completed = 0 AND tasks.deleted = 0"
+ " AND (geofences.arrival > 0 OR geofences.departure > 0)" + " AND (geofences.arrival > 0 OR geofences.departure > 0)"
+ " GROUP BY places.uid") + " GROUP BY places.uid")
fun getPlacesWithGeofences(): List<Place> suspend fun getPlacesWithGeofences(): List<Place>
@Query("SELECT places.*," @Query("SELECT places.*,"
+ " max(geofences.arrival) as arrival," + " max(geofences.arrival) as arrival,"
@ -27,92 +27,92 @@ interface LocationDao {
+ " WHERE place = :uid AND tasks.completed = 0 AND tasks.deleted = 0" + " WHERE place = :uid AND tasks.completed = 0 AND tasks.deleted = 0"
+ " AND (geofences.arrival > 0 OR geofences.departure > 0)" + " AND (geofences.arrival > 0 OR geofences.departure > 0)"
+ " GROUP BY places.uid") + " GROUP BY places.uid")
fun getGeofencesByPlace(uid: String): MergedGeofence? suspend fun getGeofencesByPlace(uid: String): MergedGeofence?
@Query("DELETE FROM geofences WHERE place = :place") @Query("DELETE FROM geofences WHERE place = :place")
fun deleteGeofencesByPlace(place: String) suspend fun deleteGeofencesByPlace(place: String)
@Query("SELECT geofences.* FROM geofences" @Query("SELECT geofences.* FROM geofences"
+ " INNER JOIN tasks ON tasks._id = geofences.task" + " INNER JOIN tasks ON tasks._id = geofences.task"
+ " WHERE place = :place AND arrival = 1 AND tasks.completed = 0" + " WHERE place = :place AND arrival = 1 AND tasks.completed = 0"
+ " AND tasks.deleted = 0 AND tasks.snoozeTime < :now AND tasks.hideUntil < :now") + " AND tasks.deleted = 0 AND tasks.snoozeTime < :now AND tasks.hideUntil < :now")
fun getArrivalGeofences(place: String, now: Long): List<Geofence> suspend fun getArrivalGeofences(place: String, now: Long): List<Geofence>
@Query("SELECT geofences.* FROM geofences" @Query("SELECT geofences.* FROM geofences"
+ " INNER JOIN tasks ON tasks._id = geofences.task" + " INNER JOIN tasks ON tasks._id = geofences.task"
+ " WHERE place = :place AND departure = 1 AND tasks.completed = 0" + " WHERE place = :place AND departure = 1 AND tasks.completed = 0"
+ " AND tasks.deleted = 0 AND tasks.snoozeTime < :now AND tasks.hideUntil < :now") + " AND tasks.deleted = 0 AND tasks.snoozeTime < :now AND tasks.hideUntil < :now")
fun getDepartureGeofences(place: String, now: Long): List<Geofence> suspend fun getDepartureGeofences(place: String, now: Long): List<Geofence>
@Query("SELECT * FROM geofences" @Query("SELECT * FROM geofences"
+ " INNER JOIN places ON geofences.place = places.uid" + " INNER JOIN places ON geofences.place = places.uid"
+ " WHERE task = :taskId ORDER BY name ASC LIMIT 1") + " WHERE task = :taskId ORDER BY name ASC LIMIT 1")
fun getGeofences(taskId: Long): Location? suspend fun getGeofences(taskId: Long): Location?
@Query("SELECT geofences.*, places.* FROM geofences INNER JOIN places ON geofences.place = places.uid INNER JOIN tasks ON tasks._id = geofences.task WHERE tasks._id = :taskId AND tasks.deleted = 0 AND tasks.completed = 0") @Query("SELECT geofences.*, places.* FROM geofences INNER JOIN places ON geofences.place = places.uid INNER JOIN tasks ON tasks._id = geofences.task WHERE tasks._id = :taskId AND tasks.deleted = 0 AND tasks.completed = 0")
fun getActiveGeofences(taskId: Long): List<Location> suspend fun getActiveGeofences(taskId: Long): List<Location>
@Query("SELECT places.*" @Query("SELECT places.*"
+ " FROM places" + " FROM places"
+ " INNER JOIN geofences ON geofences.place = places.uid" + " INNER JOIN geofences ON geofences.place = places.uid"
+ " WHERE geofences.task = :taskId") + " WHERE geofences.task = :taskId")
fun getPlaceForTask(taskId: Long): Place? suspend fun getPlaceForTask(taskId: Long): Place?
@Query("SELECT geofences.*, places.* FROM geofences INNER JOIN places ON geofences.place = places.uid INNER JOIN tasks ON tasks._id = geofences.task WHERE tasks.deleted = 0 AND tasks.completed = 0") @Query("SELECT geofences.*, places.* FROM geofences INNER JOIN places ON geofences.place = places.uid INNER JOIN tasks ON tasks._id = geofences.task WHERE tasks.deleted = 0 AND tasks.completed = 0")
fun getActiveGeofences(): List<Location> suspend fun getActiveGeofences(): List<Location>
@Query("SELECT COUNT(*) FROM geofences") @Query("SELECT COUNT(*) FROM geofences")
suspend fun geofenceCount(): Int suspend fun geofenceCount(): Int
@Delete @Delete
fun delete(location: Geofence) suspend fun delete(location: Geofence)
@Delete @Delete
fun delete(place: Place) suspend fun delete(place: Place)
@Insert @Insert
fun insert(location: Geofence): Long suspend fun insert(location: Geofence): Long
@Insert(onConflict = OnConflictStrategy.IGNORE) @Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(place: Place): Long suspend fun insert(place: Place): Long
@Update @Update
fun update(place: Place) suspend fun update(place: Place)
@Update @Update
fun update(geofence: Geofence) suspend fun update(geofence: Geofence)
@Query("SELECT * FROM places WHERE uid = :uid LIMIT 1") @Query("SELECT * FROM places WHERE uid = :uid LIMIT 1")
fun getByUid(uid: String): Place? suspend fun getByUid(uid: String): Place?
@Query("SELECT * FROM geofences WHERE task = :taskId") @Query("SELECT * FROM geofences WHERE task = :taskId")
fun getGeofencesForTask(taskId: Long): List<Geofence> suspend fun getGeofencesForTask(taskId: Long): List<Geofence>
@Query("SELECT * FROM places") @Query("SELECT * FROM places")
fun getPlaces(): List<Place> suspend fun getPlaces(): List<Place>
@Query("SELECT * FROM places WHERE place_id = :id") @Query("SELECT * FROM places WHERE place_id = :id")
fun getPlace(id: Long): Place? suspend fun getPlace(id: Long): Place?
@Query("SELECT * FROM places WHERE uid = :uid") @Query("SELECT * FROM places WHERE uid = :uid")
fun getPlace(uid: String): Place? suspend fun getPlace(uid: String): Place?
@Query("SELECT places.*, IFNULL(COUNT(geofence_id),0) AS count FROM places LEFT OUTER JOIN geofences ON geofences.place = places.uid GROUP BY uid ORDER BY COUNT(geofence_id) DESC") @Query("SELECT places.*, IFNULL(COUNT(geofence_id),0) AS count FROM places LEFT OUTER JOIN geofences ON geofences.place = places.uid GROUP BY uid ORDER BY COUNT(geofence_id) DESC")
fun getPlaceUsage(): LiveData<List<PlaceUsage>> fun getPlaceUsage(): LiveData<List<PlaceUsage>>
@Query("SELECT * FROM places WHERE latitude LIKE :latitude AND longitude LIKE :longitude") @Query("SELECT * FROM places WHERE latitude LIKE :latitude AND longitude LIKE :longitude")
fun findPlace(latitude: String, longitude: String): Place? suspend fun findPlace(latitude: String, longitude: String): Place?
@Query("SELECT places.*, COUNT(tasks._id) AS count FROM places " @Query("SELECT places.*, COUNT(tasks._id) AS count FROM places "
+ " LEFT JOIN geofences ON geofences.place = places.uid " + " LEFT JOIN geofences ON geofences.place = places.uid "
+ " LEFT JOIN tasks ON geofences.task = tasks._id AND tasks.completed = 0 AND tasks.deleted = 0 AND tasks.hideUntil < :now" + " LEFT JOIN tasks ON geofences.task = tasks._id AND tasks.completed = 0 AND tasks.deleted = 0 AND tasks.hideUntil < :now"
+ " GROUP BY places.uid" + " GROUP BY places.uid"
+ " ORDER BY name COLLATE NOCASE ASC") + " ORDER BY name COLLATE NOCASE ASC")
fun getPlaceFilters(now: Long = currentTimeMillis()): List<LocationFilters> suspend fun getPlaceFilters(now: Long = currentTimeMillis()): List<LocationFilters>
@Query("UPDATE places SET place_order = $NO_ORDER") @Query("UPDATE places SET place_order = $NO_ORDER")
fun resetOrders() suspend fun resetOrders()
@Query("UPDATE places SET place_order = :order WHERE place_id = :id") @Query("UPDATE places SET place_order = :order WHERE place_id = :id")
fun setOrder(id: Long, order: Int) suspend fun setOrder(id: Long, order: Int)
} }

@ -6,31 +6,31 @@ import com.todoroo.astrid.data.Task
@Dao @Dao
abstract class TagDao { abstract class TagDao {
@Query("UPDATE tags SET name = :name WHERE tag_uid = :tagUid") @Query("UPDATE tags SET name = :name WHERE tag_uid = :tagUid")
abstract fun rename(tagUid: String, name: String) abstract suspend fun rename(tagUid: String, name: String)
@Insert @Insert
abstract fun insert(tag: Tag) abstract suspend fun insert(tag: Tag)
@Insert @Insert
abstract fun insert(tags: Iterable<Tag>) abstract suspend fun insert(tags: Iterable<Tag>)
@Query("DELETE FROM tags WHERE task = :taskId AND tag_uid in (:tagUids)") @Query("DELETE FROM tags WHERE task = :taskId AND tag_uid in (:tagUids)")
abstract fun deleteTags(taskId: Long, tagUids: List<String>) abstract suspend fun deleteTags(taskId: Long, tagUids: List<String>)
@Query("SELECT * FROM tags WHERE tag_uid = :tagUid") @Query("SELECT * FROM tags WHERE tag_uid = :tagUid")
abstract fun getByTagUid(tagUid: String): List<Tag> abstract suspend fun getByTagUid(tagUid: String): List<Tag>
@Query("SELECT * FROM tags WHERE task = :taskId") @Query("SELECT * FROM tags WHERE task = :taskId")
abstract fun getTagsForTask(taskId: Long): List<Tag> abstract suspend fun getTagsForTask(taskId: Long): List<Tag>
@Query("SELECT * FROM tags WHERE task = :taskId AND tag_uid = :tagUid") @Query("SELECT * FROM tags WHERE task = :taskId AND tag_uid = :tagUid")
abstract fun getTagByTaskAndTagUid(taskId: Long, tagUid: String): Tag? abstract suspend fun getTagByTaskAndTagUid(taskId: Long, tagUid: String): Tag?
@Delete @Delete
abstract fun delete(tags: List<Tag>) abstract suspend fun delete(tags: List<Tag>)
@Transaction @Transaction
open fun applyTags(task: Task, tagDataDao: TagDataDaoBlocking, current: List<TagData>): Boolean { open suspend fun applyTags(task: Task, tagDataDao: TagDataDaoBlocking, current: List<TagData>): Boolean {
val taskId = task.id val taskId = task.id
val existing = HashSet(tagDataDao.getTagDataForTask(taskId)) val existing = HashSet(tagDataDao.getTagDataForTask(taskId))
val selected = HashSet<TagData>(current) val selected = HashSet<TagData>(current)
@ -41,7 +41,7 @@ abstract class TagDao {
return removed.isNotEmpty() || added.isNotEmpty() return removed.isNotEmpty() || added.isNotEmpty()
} }
fun insert(task: Task, tags: Collection<TagData>) { suspend fun insert(task: Task, tags: Collection<TagData>) {
if (!tags.isEmpty()) { if (!tags.isEmpty()) {
insert(tags.map { Tag(task, it) }) insert(tags.map { Tag(task, it) })
} }

@ -19,47 +19,47 @@ abstract class TagDataDao {
abstract fun subscribeToTags(): LiveData<List<TagData>> abstract fun subscribeToTags(): LiveData<List<TagData>>
@Query("SELECT * FROM tagdata WHERE name = :name COLLATE NOCASE LIMIT 1") @Query("SELECT * FROM tagdata WHERE name = :name COLLATE NOCASE LIMIT 1")
abstract fun getTagByName(name: String): TagData? abstract suspend fun getTagByName(name: String): TagData?
/** /**
* If a tag already exists in the database that case insensitively matches the given tag, return * If a tag already exists in the database that case insensitively matches the given tag, return
* that. Otherwise, return the argument * that. Otherwise, return the argument
*/ */
fun getTagWithCase(tag: String): String? { suspend fun getTagWithCase(tag: String): String? {
return getTagByName(tag)?.name ?: tag return getTagByName(tag)?.name ?: tag
} }
fun searchTags(query: String): List<TagData> { suspend fun searchTags(query: String): List<TagData> {
return searchTagsInternal("%$query%") return searchTagsInternal("%$query%")
.sortedWith(AlphanumComparator(TagData::name)) .sortedWith(AlphanumComparator(TagData::name))
.toMutableList() .toMutableList()
} }
@Query("SELECT * FROM tagdata WHERE name LIKE :query AND name NOT NULL AND name != ''") @Query("SELECT * FROM tagdata WHERE name LIKE :query AND name NOT NULL AND name != ''")
protected abstract fun searchTagsInternal(query: String): List<TagData> protected abstract suspend fun searchTagsInternal(query: String): List<TagData>
@Query("SELECT * FROM tagdata") @Query("SELECT * FROM tagdata")
abstract fun getAll(): List<TagData> abstract suspend fun getAll(): List<TagData>
@Query("SELECT * FROM tagdata WHERE remoteId = :uuid LIMIT 1") @Query("SELECT * FROM tagdata WHERE remoteId = :uuid LIMIT 1")
abstract fun getByUuid(uuid: String): TagData? abstract suspend fun getByUuid(uuid: String): TagData?
@Query("SELECT * FROM tagdata WHERE remoteId IN (:uuids)") @Query("SELECT * FROM tagdata WHERE remoteId IN (:uuids)")
abstract fun getByUuid(uuids: Collection<String>): List<TagData> abstract suspend fun getByUuid(uuids: Collection<String>): List<TagData>
@Query("SELECT * FROM tagdata WHERE name IS NOT NULL AND name != '' ORDER BY UPPER(name) ASC") @Query("SELECT * FROM tagdata WHERE name IS NOT NULL AND name != '' ORDER BY UPPER(name) ASC")
abstract fun tagDataOrderedByName(): List<TagData> abstract suspend fun tagDataOrderedByName(): List<TagData>
@Delete @Delete
abstract fun deleteTagData(tagData: TagData) abstract suspend fun deleteTagData(tagData: TagData)
@Query("DELETE FROM tags WHERE tag_uid = :tagUid") @Query("DELETE FROM tags WHERE tag_uid = :tagUid")
abstract fun deleteTags(tagUid: String) abstract suspend fun deleteTags(tagUid: String)
@Query("SELECT * FROM tags WHERE task IN (:tasks) AND tag_uid NOT IN (:tagsToKeep)") @Query("SELECT * FROM tags WHERE task IN (:tasks) AND tag_uid NOT IN (:tagsToKeep)")
abstract fun tagsToDelete(tasks: List<Long>, tagsToKeep: List<String>): List<Tag> abstract suspend fun tagsToDelete(tasks: List<Long>, tagsToKeep: List<String>): List<Tag>
fun getTagSelections(tasks: List<Long>): Pair<Set<String>, Set<String>> { suspend fun getTagSelections(tasks: List<Long>): Pair<Set<String>, Set<String>> {
val allTags = getAllTags(tasks) val allTags = getAllTags(tasks)
val tags = allTags.map { t: String? -> HashSet<String>(t?.split(",") ?: emptySet()) } val tags = allTags.map { t: String? -> HashSet<String>(t?.split(",") ?: emptySet()) }
val partialTags = tags.flatten().toMutableSet() val partialTags = tags.flatten().toMutableSet()
@ -83,10 +83,10 @@ abstract class TagDataDao {
+ " LEFT JOIN tags ON tags.task = tasks._id" + " LEFT JOIN tags ON tags.task = tasks._id"
+ " WHERE tasks._id IN (:tasks)" + " WHERE tasks._id IN (:tasks)"
+ " GROUP BY tasks._id") + " GROUP BY tasks._id")
abstract fun getAllTags(tasks: List<Long>): List<String> abstract suspend fun getAllTags(tasks: List<Long>): List<String>
@Transaction @Transaction
open fun applyTags( open suspend fun applyTags(
tasks: List<Task>, partiallySelected: List<TagData>, selected: List<TagData>): List<Long> { tasks: List<Task>, partiallySelected: List<TagData>, selected: List<TagData>): List<Long> {
val modified = HashSet<Long>() val modified = HashSet<Long>()
val keep = partiallySelected.plus(selected).map { it.remoteId!! } val keep = partiallySelected.plus(selected).map { it.remoteId!! }
@ -106,36 +106,36 @@ abstract class TagDataDao {
} }
@Transaction @Transaction
open fun delete(tagData: TagData) { open suspend fun delete(tagData: TagData) {
deleteTags(tagData.remoteId!!) deleteTags(tagData.remoteId!!)
deleteTagData(tagData) deleteTagData(tagData)
} }
@Delete @Delete
abstract fun delete(tagData: List<TagData>) abstract suspend fun delete(tagData: List<TagData>)
@Delete @Delete
abstract fun deleteTags(tags: List<Tag>) abstract suspend fun deleteTags(tags: List<Tag>)
@Query("SELECT tagdata.* FROM tagdata " @Query("SELECT tagdata.* FROM tagdata "
+ "INNER JOIN tags ON tags.tag_uid = tagdata.remoteId " + "INNER JOIN tags ON tags.tag_uid = tagdata.remoteId "
+ "WHERE tags.task = :id " + "WHERE tags.task = :id "
+ "ORDER BY UPPER(tagdata.name) ASC") + "ORDER BY UPPER(tagdata.name) ASC")
abstract fun getTagDataForTask(id: Long): List<TagData> abstract suspend fun getTagDataForTask(id: Long): List<TagData>
@Query("SELECT * FROM tagdata WHERE name IN (:names)") @Query("SELECT * FROM tagdata WHERE name IN (:names)")
abstract fun getTags(names: List<String>): List<TagData> abstract suspend fun getTags(names: List<String>): List<TagData>
@Update @Update
abstract fun update(tagData: TagData) abstract suspend fun update(tagData: TagData)
@Insert @Insert
abstract fun insert(tag: TagData): Long abstract suspend fun insert(tag: TagData): Long
@Insert @Insert
abstract fun insert(tags: Iterable<Tag>) abstract suspend fun insert(tags: Iterable<Tag>)
fun createNew(tag: TagData) { suspend fun createNew(tag: TagData) {
if (Task.isUuidEmpty(tag.remoteId)) { if (Task.isUuidEmpty(tag.remoteId)) {
tag.remoteId = UUIDHelper.newUUID() tag.remoteId = UUIDHelper.newUUID()
} }
@ -148,11 +148,11 @@ abstract class TagDataDao {
+ " LEFT JOIN tasks ON tags.task = tasks._id AND tasks.deleted = 0 AND tasks.completed = 0 AND tasks.hideUntil < :now" + " LEFT JOIN tasks ON tags.task = tasks._id AND tasks.deleted = 0 AND tasks.completed = 0 AND tasks.hideUntil < :now"
+ " WHERE tagdata.name IS NOT NULL AND tagdata.name != ''" + " WHERE tagdata.name IS NOT NULL AND tagdata.name != ''"
+ " GROUP BY tagdata.remoteId") + " GROUP BY tagdata.remoteId")
abstract fun getTagFilters(now: Long = currentTimeMillis()): List<TagFilters> abstract suspend fun getTagFilters(now: Long = currentTimeMillis()): List<TagFilters>
@Query("UPDATE tagdata SET td_order = $NO_ORDER") @Query("UPDATE tagdata SET td_order = $NO_ORDER")
abstract fun resetOrders() abstract suspend fun resetOrders()
@Query("UPDATE tagdata SET td_order = :order WHERE _id = :id") @Query("UPDATE tagdata SET td_order = :order WHERE _id = :id")
abstract fun setOrder(id: Long, order: Int) abstract suspend fun setOrder(id: Long, order: Int)
} }

@ -7,24 +7,24 @@ import com.todoroo.astrid.helper.UUIDHelper
@Dao @Dao
abstract class TaskAttachmentDao { abstract class TaskAttachmentDao {
@Query("SELECT * FROM task_attachments WHERE task_id = :taskUuid") @Query("SELECT * FROM task_attachments WHERE task_id = :taskUuid")
abstract fun getAttachments(taskUuid: String): List<TaskAttachment> abstract suspend fun getAttachments(taskUuid: String): List<TaskAttachment>
@Query("SELECT task_attachments.* FROM task_attachments INNER JOIN tasks ON tasks._id = :task WHERE task_id = tasks.remoteId") @Query("SELECT task_attachments.* FROM task_attachments INNER JOIN tasks ON tasks._id = :task WHERE task_id = tasks.remoteId")
abstract fun getAttachments(task: Long): List<TaskAttachment> abstract suspend fun getAttachments(task: Long): List<TaskAttachment>
@Query("SELECT * FROM task_attachments") @Query("SELECT * FROM task_attachments")
abstract fun getAttachments(): List<TaskAttachment> abstract suspend fun getAttachments(): List<TaskAttachment>
@Delete @Delete
abstract fun delete(taskAttachment: TaskAttachment) abstract suspend fun delete(taskAttachment: TaskAttachment)
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun insert(attachment: TaskAttachment) abstract suspend fun insert(attachment: TaskAttachment)
@Update @Update
abstract fun update(attachment: TaskAttachment) abstract suspend fun update(attachment: TaskAttachment)
fun createNew(attachment: TaskAttachment) { suspend fun createNew(attachment: TaskAttachment) {
if (Task.isUuidEmpty(attachment.remoteId)) { if (Task.isUuidEmpty(attachment.remoteId)) {
attachment.remoteId = UUIDHelper.newUUID() attachment.remoteId = UUIDHelper.newUUID()
} }

@ -8,18 +8,18 @@ import androidx.room.Update
@Dao @Dao
abstract class TaskListMetadataDao { abstract class TaskListMetadataDao {
@Query("SELECT * from task_list_metadata where tag_uuid = :tagUuid OR filter = :tagUuid LIMIT 1") @Query("SELECT * from task_list_metadata where tag_uuid = :tagUuid OR filter = :tagUuid LIMIT 1")
abstract fun fetchByTagOrFilter(tagUuid: String): TaskListMetadata? abstract suspend fun fetchByTagOrFilter(tagUuid: String): TaskListMetadata?
@Query("SELECT * FROM task_list_metadata") @Query("SELECT * FROM task_list_metadata")
abstract fun getAll(): List<TaskListMetadata> abstract suspend fun getAll(): List<TaskListMetadata>
@Update @Update
abstract fun update(taskListMetadata: TaskListMetadata) abstract suspend fun update(taskListMetadata: TaskListMetadata)
@Insert @Insert
abstract fun insert(taskListMetadata: TaskListMetadata): Long abstract suspend fun insert(taskListMetadata: TaskListMetadata): Long
fun createNew(taskListMetadata: TaskListMetadata) { suspend fun createNew(taskListMetadata: TaskListMetadata) {
taskListMetadata.id = insert(taskListMetadata) taskListMetadata.id = insert(taskListMetadata)
} }
} }

@ -8,24 +8,24 @@ import com.todoroo.astrid.helper.UUIDHelper
@Dao @Dao
abstract class UserActivityDao { abstract class UserActivityDao {
@Insert @Insert
abstract fun insert(userActivity: UserActivity) abstract suspend fun insert(userActivity: UserActivity)
@Update @Update
abstract fun update(userActivity: UserActivity) abstract suspend fun update(userActivity: UserActivity)
@Delete @Delete
abstract fun delete(userActivity: UserActivity) abstract suspend fun delete(userActivity: UserActivity)
@Query("SELECT * FROM userActivity WHERE target_id = :taskUuid ORDER BY created_at DESC ") @Query("SELECT * FROM userActivity WHERE target_id = :taskUuid ORDER BY created_at DESC ")
abstract fun getCommentsForTask(taskUuid: String): List<UserActivity> abstract suspend fun getCommentsForTask(taskUuid: String): List<UserActivity>
@Query("SELECT userActivity.* FROM userActivity INNER JOIN tasks ON tasks._id = :task WHERE target_id = tasks.remoteId") @Query("SELECT userActivity.* FROM userActivity INNER JOIN tasks ON tasks._id = :task WHERE target_id = tasks.remoteId")
abstract fun getComments(task: Long): List<UserActivity> abstract suspend fun getComments(task: Long): List<UserActivity>
@Query("SELECT * FROM userActivity") @Query("SELECT * FROM userActivity")
abstract fun getComments(): List<UserActivity> abstract suspend fun getComments(): List<UserActivity>
fun createNew(item: UserActivity) { suspend fun createNew(item: UserActivity) {
if (item.created == null || item.created == 0L) { if (item.created == null || item.created == 0L) {
item.created = DateUtilities.now() item.created = DateUtilities.now()
} }

@ -0,0 +1,11 @@
package org.tasks.db
import org.tasks.db.DbUtils.dbchunk
object SuspendDbUtils {
suspend fun <T> Iterable<T>.eachChunk(action: suspend (List<T>) -> Unit) =
dbchunk().forEach { action.invoke(it) }
suspend fun <T, R> Iterable<T>.chunkedMap(transform: suspend (List<T>) -> Iterable<R>): List<R> =
dbchunk().flatMap { transform.invoke(it) }
}

@ -8,20 +8,20 @@ import androidx.room.Query
@Dao @Dao
interface NotificationDao { interface NotificationDao {
@Query("SELECT task FROM notification") @Query("SELECT task FROM notification")
fun getAll(): List<Long> suspend fun getAll(): List<Long>
@Query("SELECT * FROM notification ORDER BY timestamp DESC") @Query("SELECT * FROM notification ORDER BY timestamp DESC")
fun getAllOrdered(): List<Notification> suspend fun getAllOrdered(): List<Notification>
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(notifications: List<Notification>) suspend fun insertAll(notifications: List<Notification>)
@Query("DELETE FROM notification WHERE task = :taskId") @Query("DELETE FROM notification WHERE task = :taskId")
fun delete(taskId: Long) suspend fun delete(taskId: Long)
@Query("DELETE FROM notification WHERE task IN(:taskIds)") @Query("DELETE FROM notification WHERE task IN(:taskIds)")
fun deleteAll(taskIds: List<Long>) suspend fun deleteAll(taskIds: List<Long>)
@Query("SELECT MAX(timestamp) FROM notification") @Query("SELECT MAX(timestamp) FROM notification")
fun latestTimestamp(): Long suspend fun latestTimestamp(): Long
} }
Loading…
Cancel
Save