Legacy TaskDao delegates to new TaskDao

pull/1055/head
Alex Baker 4 years ago
parent 8710dab252
commit 9559d90c25

@ -9,7 +9,6 @@ import dagger.hilt.android.testing.UninstallModules
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.tasks.data.CaldavCalendar
import org.tasks.data.CaldavDao
@ -43,12 +42,6 @@ class TaskMoverTest : InjectingTestCase() {
@Inject lateinit var caldavDao: CaldavDao
@Inject lateinit var taskMover: TaskMover
@Before
override fun setUp() {
super.setUp()
taskDaoAsync.initialize(workManager)
}
@Test
fun moveBetweenGoogleTaskLists() = runBlocking {
createTasks(1)

@ -4,6 +4,7 @@ import androidx.room.Database
import androidx.room.RoomDatabase
import com.todoroo.astrid.data.Task
import org.tasks.data.*
import org.tasks.data.TaskDao
import org.tasks.notifications.Notification
import org.tasks.notifications.NotificationDao

@ -6,204 +6,107 @@
package com.todoroo.astrid.dao
import androidx.paging.DataSource
import androidx.room.*
import androidx.sqlite.db.SimpleSQLiteQuery
import com.todoroo.andlib.sql.Criterion
import com.todoroo.andlib.sql.Field
import com.todoroo.andlib.sql.Functions
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.PermaSql
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.data.Task.Companion.NO_ID
import com.todoroo.astrid.helper.UUIDHelper
import org.tasks.BuildConfig
import org.tasks.data.Place
import org.tasks.data.SubtaskInfo
import org.tasks.data.TaskContainer
import org.tasks.data.TaskListQuery
import org.tasks.db.SuspendDbUtils.chunkedMap
import org.tasks.data.TaskDao
import org.tasks.db.SuspendDbUtils.eachChunk
import org.tasks.jobs.WorkManager
import org.tasks.preferences.Preferences
import org.tasks.time.DateTimeUtils.currentTimeMillis
import timber.log.Timber
import javax.inject.Inject
@Dao
abstract class TaskDao(private val database: Database) {
private lateinit var workManager: WorkManager
class TaskDao @Inject constructor(
private val workManager: WorkManager,
private val taskDao: TaskDao) {
fun initialize(workManager: WorkManager) {
this.workManager = workManager
}
@Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0 AND (hideUntil > :now OR dueDate > :now)")
internal abstract suspend fun needsRefresh(now: Long = DateUtilities.now()): List<Task>
@Query("SELECT * FROM tasks WHERE _id = :id LIMIT 1")
abstract suspend fun fetch(id: Long): Task?
internal suspend fun needsRefresh(now: Long = DateUtilities.now()): List<Task> =
taskDao.needsRefresh(now)
suspend fun fetch(ids: List<Long>): List<Task> = ids.chunkedMap(this::fetchInternal)
suspend fun fetch(id: Long): Task? = taskDao.fetch(id)
@Query("SELECT * FROM tasks WHERE _id IN (:ids)")
internal abstract suspend fun fetchInternal(ids: List<Long>): List<Task>
suspend fun fetch(ids: List<Long>): List<Task> = taskDao.fetch(ids)
@Query("SELECT COUNT(1) FROM tasks WHERE timerStart > 0 AND deleted = 0")
abstract suspend fun activeTimers(): Int
suspend fun activeTimers(): Int = taskDao.activeTimers()
@Query("SELECT tasks.* FROM tasks INNER JOIN notification ON tasks._id = notification.task")
abstract suspend fun activeNotifications(): List<Task>
suspend fun activeNotifications(): List<Task> = taskDao.activeNotifications()
@Query("SELECT * FROM tasks WHERE remoteId = :remoteId")
abstract suspend fun fetch(remoteId: String): Task?
suspend fun fetch(remoteId: String): Task? = taskDao.fetch(remoteId)
@Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0")
abstract suspend fun getActiveTasks(): List<Task>
suspend fun getActiveTasks(): List<Task> = taskDao.getActiveTasks()
@Query("SELECT * FROM tasks WHERE remoteId IN (:remoteIds) "
+ "AND recurrence IS NOT NULL AND LENGTH(recurrence) > 0")
abstract suspend fun getRecurringTasks(remoteIds: List<String>): List<Task>
suspend fun getRecurringTasks(remoteIds: List<String>): List<Task> =
taskDao.getRecurringTasks(remoteIds)
@Query("UPDATE tasks SET completed = :completionDate " + "WHERE remoteId = :remoteId")
abstract suspend fun setCompletionDate(remoteId: String, completionDate: Long)
suspend fun setCompletionDate(remoteId: String, completionDate: Long) =
taskDao.setCompletionDate(remoteId, completionDate)
@Query("UPDATE tasks SET snoozeTime = :millis WHERE _id in (:taskIds)")
abstract suspend fun snooze(taskIds: List<Long>, millis: Long)
suspend fun snooze(taskIds: List<Long>, millis: Long) = taskDao.snooze(taskIds, millis)
@Query("SELECT tasks.* FROM tasks "
+ "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)"
+ "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")
abstract suspend fun getGoogleTasksToPush(account: String): List<Task>
suspend fun getGoogleTasksToPush(account: String): List<Task> =
taskDao.getGoogleTasksToPush(account)
@Query("""
SELECT tasks.*
FROM tasks
INNER JOIN caldav_tasks ON tasks._id = caldav_tasks.cd_task
WHERE caldav_tasks.cd_calendar = :calendar
AND (tasks.modified > caldav_tasks.cd_last_sync OR caldav_tasks.cd_last_sync = 0)""")
abstract suspend fun getCaldavTasksToPush(calendar: String): List<Task>
suspend fun getCaldavTasksToPush(calendar: String): List<Task> =
taskDao.getCaldavTasksToPush(calendar)
@Query("SELECT * FROM TASKS "
+ "WHERE completed = 0 AND deleted = 0 AND (notificationFlags > 0 OR notifications > 0)")
abstract suspend fun getTasksWithReminders(): List<Task>
suspend fun getTasksWithReminders(): List<Task> = taskDao.getTasksWithReminders()
// --- SQL clause generators
@Query("SELECT * FROM tasks")
abstract suspend fun getAll(): List<Task>
suspend fun getAll(): List<Task> = taskDao.getAll()
@Query("SELECT calendarUri FROM tasks " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''")
abstract suspend fun getAllCalendarEvents(): List<String>
suspend fun getAllCalendarEvents(): List<String> = taskDao.getAllCalendarEvents()
@Query("UPDATE tasks SET calendarUri = '' " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''")
abstract suspend fun clearAllCalendarEvents(): Int
suspend fun clearAllCalendarEvents(): Int = taskDao.clearAllCalendarEvents()
@Query("SELECT calendarUri FROM tasks "
+ "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''")
abstract suspend fun getCompletedCalendarEvents(): List<String>
suspend fun getCompletedCalendarEvents(): List<String> = taskDao.getCompletedCalendarEvents()
@Query("UPDATE tasks SET calendarUri = '' "
+ "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''")
abstract suspend fun clearCompletedCalendarEvents(): Int
suspend fun clearCompletedCalendarEvents(): Int = taskDao.clearCompletedCalendarEvents()
@Transaction
open suspend fun fetchTasks(callback: suspend (SubtaskInfo) -> List<String>): List<TaskContainer> {
return fetchTasks(callback, getSubtaskInfo())
}
suspend fun fetchTasks(callback: suspend (SubtaskInfo) -> List<String>): List<TaskContainer> =
taskDao.fetchTasks(callback)
@Transaction
open suspend fun fetchTasks(callback: suspend (SubtaskInfo) -> List<String>, subtasks: SubtaskInfo): List<TaskContainer> {
val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0
val queries = callback.invoke(subtasks)
val db = database.openHelper.writableDatabase
val last = queries.size - 1
for (i in 0 until last) {
db.execSQL(queries[i])
}
val result = fetchTasks(SimpleSQLiteQuery(queries[last]))
Timber.v("%sms: %s", DateUtilities.now() - start, queries.joinToString(";\n"))
return result
}
suspend fun fetchTasks(callback: suspend (SubtaskInfo) -> List<String>, subtasks: SubtaskInfo): List<TaskContainer> =
taskDao.fetchTasks(callback, subtasks)
suspend fun fetchTasks(preferences: Preferences, filter: Filter): List<TaskContainer> {
return fetchTasks {
TaskListQuery.getQuery(preferences, filter, it)
}
}
suspend fun fetchTasks(preferences: Preferences, filter: Filter): List<TaskContainer> =
taskDao.fetchTasks(preferences, filter)
@RawQuery
abstract suspend fun fetchTasks(query: SimpleSQLiteQuery): List<TaskContainer>
suspend fun fetchTasks(query: SimpleSQLiteQuery): List<TaskContainer> =
taskDao.fetchTasks(query)
@RawQuery
abstract suspend fun count(query: SimpleSQLiteQuery): Int
suspend fun count(query: SimpleSQLiteQuery): Int = taskDao.count(query)
@Query("""
SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtasks,
EXISTS(SELECT 1
FROM google_tasks
INNER JOIN tasks ON gt_task = _id
WHERE deleted = 0
AND gt_parent > 0
AND gt_deleted = 0) AS hasGoogleSubtasks
""")
abstract suspend fun getSubtaskInfo(): SubtaskInfo
suspend fun getSubtaskInfo(): SubtaskInfo = taskDao.getSubtaskInfo()
@RawQuery(observedEntities = [Place::class])
abstract fun getTaskFactory(query: SimpleSQLiteQuery): DataSource.Factory<Int, TaskContainer>
fun getTaskFactory(query: SimpleSQLiteQuery): DataSource.Factory<Int, TaskContainer> =
taskDao.getTaskFactory(query)
suspend fun touch(id: Long) = touch(listOf(id))
suspend fun touch(ids: List<Long>) {
ids.eachChunk { touchInternal(it) }
ids.eachChunk { taskDao.touch(ids) }
workManager.sync(false)
}
@Query("UPDATE tasks SET modified = :now WHERE _id in (:ids)")
internal abstract suspend fun touchInternal(ids: List<Long>, now: Long = currentTimeMillis())
suspend fun setParent(parent: Long, tasks: List<Long>) =
tasks.eachChunk { setParentInternal(parent, it) }
@Query("UPDATE tasks SET parent = :parent WHERE _id IN (:children)")
internal abstract suspend fun setParentInternal(parent: Long, children: List<Long>)
private suspend fun setParentInternal(parent: Long, children: List<Long>) =
taskDao.setParentInternal(parent, children)
@Transaction
open suspend fun fetchChildren(id: Long): List<Task> {
return fetch(getChildren(id))
}
suspend fun fetchChildren(id: Long): List<Task> = taskDao.fetchChildren(id)
suspend fun getChildren(id: Long): List<Long> {
return getChildren(listOf(id))
}
suspend fun getChildren(id: Long): List<Long> = taskDao.getChildren(id)
@Query("WITH RECURSIVE "
+ " recursive_tasks (task) AS ( "
+ " SELECT _id "
+ " FROM tasks "
+ "WHERE parent IN (:ids)"
+ "UNION ALL "
+ " SELECT _id "
+ " FROM tasks "
+ " INNER JOIN recursive_tasks "
+ " ON recursive_tasks.task = tasks.parent"
+ " WHERE tasks.deleted = 0)"
+ "SELECT task FROM recursive_tasks")
abstract suspend fun getChildren(ids: List<Long>): List<Long>
@Query("UPDATE tasks SET collapsed = :collapsed WHERE _id = :id")
abstract suspend fun setCollapsed(id: Long, collapsed: Boolean)
@Transaction
open suspend fun setCollapsed(preferences: Preferences, filter: Filter, collapsed: Boolean) {
fetchTasks(preferences, filter)
.filter(TaskContainer::hasChildren)
.map(TaskContainer::getId)
.eachChunk { collapse(it, collapsed) }
}
suspend fun getChildren(ids: List<Long>): List<Long> = taskDao.getChildren(ids)
@Query("UPDATE tasks SET collapsed = :collapsed WHERE _id IN (:ids)")
internal abstract suspend fun collapse(ids: List<Long>, collapsed: Boolean)
suspend fun setCollapsed(id: Long, collapsed: Boolean) = taskDao.setCollapsed(id, collapsed)
suspend fun setCollapsed(preferences: Preferences, filter: Filter, collapsed: Boolean) =
taskDao.setCollapsed(preferences, filter, collapsed)
// --- save
// TODO: get rid of this super-hack
@ -226,49 +129,20 @@ SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtas
}
}
@Insert
abstract suspend fun insert(task: Task): Long
suspend fun insert(task: Task): Long = taskDao.insert(task)
@Update
abstract suspend fun update(task: Task): Int
suspend fun update(task: Task): Int = taskDao.update(task)
suspend fun createNew(task: Task) {
task.id = NO_ID
if (task.creationDate == 0L) {
task.creationDate = DateUtilities.now()
}
if (Task.isUuidEmpty(task.remoteId)) {
task.remoteId = UUIDHelper.newUUID()
}
if (BuildConfig.DEBUG) {
require(task.remoteId?.isNotBlank() == true && task.remoteId != "0")
}
val insert = insert(task)
task.id = insert
}
suspend fun createNew(task: Task) = taskDao.createNew(task)
suspend fun count(filter: Filter): Int {
val query = getQuery(filter.sqlQuery, Field.COUNT)
val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0
val count = count(query)
Timber.v("%sms: %s", DateUtilities.now() - start, query.sql)
return count
}
suspend fun count(filter: Filter): Int = taskDao.count(filter)
suspend fun fetchFiltered(filter: Filter): List<Task> {
return fetchFiltered(filter.getSqlQuery())
}
suspend fun fetchFiltered(filter: Filter): List<Task> = taskDao.fetchFiltered(filter)
suspend fun fetchFiltered(queryTemplate: String): List<Task> {
val query = getQuery(queryTemplate, Task.FIELDS)
val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0
val tasks = fetchTasks(query)
Timber.v("%sms: %s", DateUtilities.now() - start, query.sql)
return tasks.map(TaskContainer::getTask)
}
suspend fun fetchFiltered(queryTemplate: String): List<Task> =
taskDao.fetchFiltered(queryTemplate)
@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 suspend fun getLocalTasks(): List<Long>
suspend fun getLocalTasks(): List<Long> = taskDao.getLocalTasks()
/** Generates SQL clauses */
object TaskCriteria {
@ -281,15 +155,4 @@ SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtas
Task.HIDE_UNTIL.lte(Functions.now()))
}
}
companion object {
const val TRANS_SUPPRESS_REFRESH = "suppress-refresh"
fun getQuery(queryTemplate: String, vararg fields: Field): SimpleSQLiteQuery {
return SimpleSQLiteQuery(
com.todoroo.andlib.sql.Query.select(*fields)
.withQueryTemplate(PermaSql.replacePlaceholdersForQuery(queryTemplate))
.from(Task.TABLE)
.toString())
}
}
}

@ -376,7 +376,7 @@ class Task : Parcelable {
@Synchronized
fun suppressRefresh() {
putTransitory(TaskDao.TRANS_SUPPRESS_REFRESH, true)
putTransitory(TRANS_SUPPRESS_REFRESH, true)
}
@Synchronized
@ -560,6 +560,8 @@ class Task : Parcelable {
const val URGENCY_NEXT_WEEK = 4
const val URGENCY_IN_TWO_WEEKS = 5
const val TRANS_SUPPRESS_REFRESH = "suppress-refresh"
/**
* Creates due date for this task. If this due date has no time associated, we move it to the last
* millisecond of the day.

@ -0,0 +1,252 @@
package org.tasks.data
import androidx.paging.DataSource
import androidx.room.*
import androidx.sqlite.db.SimpleSQLiteQuery
import com.todoroo.andlib.sql.Criterion
import com.todoroo.andlib.sql.Field
import com.todoroo.andlib.sql.Functions
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.PermaSql
import com.todoroo.astrid.dao.Database
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.data.Task.Companion.NO_ID
import com.todoroo.astrid.helper.UUIDHelper
import org.tasks.BuildConfig
import org.tasks.db.SuspendDbUtils.chunkedMap
import org.tasks.db.SuspendDbUtils.eachChunk
import org.tasks.preferences.Preferences
import org.tasks.time.DateTimeUtils.currentTimeMillis
import timber.log.Timber
@Dao
abstract class TaskDao(private val database: Database) {
@Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0 AND (hideUntil > :now OR dueDate > :now)")
internal abstract suspend fun needsRefresh(now: Long = DateUtilities.now()): List<Task>
@Query("SELECT * FROM tasks WHERE _id = :id LIMIT 1")
abstract suspend fun fetch(id: Long): Task?
suspend fun fetch(ids: List<Long>): List<Task> = ids.chunkedMap(this::fetchInternal)
@Query("SELECT * FROM tasks WHERE _id IN (:ids)")
internal abstract suspend fun fetchInternal(ids: List<Long>): List<Task>
@Query("SELECT COUNT(1) FROM tasks WHERE timerStart > 0 AND deleted = 0")
abstract suspend fun activeTimers(): Int
@Query("SELECT tasks.* FROM tasks INNER JOIN notification ON tasks._id = notification.task")
abstract suspend fun activeNotifications(): List<Task>
@Query("SELECT * FROM tasks WHERE remoteId = :remoteId")
abstract suspend fun fetch(remoteId: String): Task?
@Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0")
abstract suspend fun getActiveTasks(): List<Task>
@Query("SELECT * FROM tasks WHERE remoteId IN (:remoteIds) "
+ "AND recurrence IS NOT NULL AND LENGTH(recurrence) > 0")
abstract suspend fun getRecurringTasks(remoteIds: List<String>): List<Task>
@Query("UPDATE tasks SET completed = :completionDate " + "WHERE remoteId = :remoteId")
abstract suspend fun setCompletionDate(remoteId: String, completionDate: Long)
@Query("UPDATE tasks SET snoozeTime = :millis WHERE _id in (:taskIds)")
abstract suspend fun snooze(taskIds: List<Long>, millis: Long)
@Query("SELECT tasks.* FROM tasks "
+ "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)"
+ "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")
abstract suspend fun getGoogleTasksToPush(account: String): List<Task>
@Query("""
SELECT tasks.*
FROM tasks
INNER JOIN caldav_tasks ON tasks._id = caldav_tasks.cd_task
WHERE caldav_tasks.cd_calendar = :calendar
AND (tasks.modified > caldav_tasks.cd_last_sync OR caldav_tasks.cd_last_sync = 0)""")
abstract suspend fun getCaldavTasksToPush(calendar: String): List<Task>
@Query("SELECT * FROM TASKS "
+ "WHERE completed = 0 AND deleted = 0 AND (notificationFlags > 0 OR notifications > 0)")
abstract suspend fun getTasksWithReminders(): List<Task>
// --- SQL clause generators
@Query("SELECT * FROM tasks")
abstract suspend fun getAll(): List<Task>
@Query("SELECT calendarUri FROM tasks " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''")
abstract suspend fun getAllCalendarEvents(): List<String>
@Query("UPDATE tasks SET calendarUri = '' " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''")
abstract suspend fun clearAllCalendarEvents(): Int
@Query("SELECT calendarUri FROM tasks "
+ "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''")
abstract suspend fun getCompletedCalendarEvents(): List<String>
@Query("UPDATE tasks SET calendarUri = '' "
+ "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''")
abstract suspend fun clearCompletedCalendarEvents(): Int
@Transaction
open suspend fun fetchTasks(callback: suspend (SubtaskInfo) -> List<String>): List<TaskContainer> {
return fetchTasks(callback, getSubtaskInfo())
}
@Transaction
open suspend fun fetchTasks(callback: suspend (SubtaskInfo) -> List<String>, subtasks: SubtaskInfo): List<TaskContainer> {
val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0
val queries = callback.invoke(subtasks)
val db = database.openHelper.writableDatabase
val last = queries.size - 1
for (i in 0 until last) {
db.execSQL(queries[i])
}
val result = fetchTasks(SimpleSQLiteQuery(queries[last]))
Timber.v("%sms: %s", DateUtilities.now() - start, queries.joinToString(";\n"))
return result
}
suspend fun fetchTasks(preferences: Preferences, filter: Filter): List<TaskContainer> {
return fetchTasks {
TaskListQuery.getQuery(preferences, filter, it)
}
}
@RawQuery
abstract suspend fun fetchTasks(query: SimpleSQLiteQuery): List<TaskContainer>
@RawQuery
abstract suspend fun count(query: SimpleSQLiteQuery): Int
@Query("""
SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtasks,
EXISTS(SELECT 1
FROM google_tasks
INNER JOIN tasks ON gt_task = _id
WHERE deleted = 0
AND gt_parent > 0
AND gt_deleted = 0) AS hasGoogleSubtasks
""")
abstract suspend fun getSubtaskInfo(): SubtaskInfo
@RawQuery(observedEntities = [Place::class])
abstract fun getTaskFactory(query: SimpleSQLiteQuery): DataSource.Factory<Int, TaskContainer>
@Query("UPDATE tasks SET modified = :now WHERE _id in (:ids)")
abstract suspend fun touch(ids: List<Long>, now: Long = currentTimeMillis())
suspend fun setParent(parent: Long, tasks: List<Long>) =
tasks.eachChunk { setParentInternal(parent, it) }
@Query("UPDATE tasks SET parent = :parent WHERE _id IN (:children)")
internal abstract suspend fun setParentInternal(parent: Long, children: List<Long>)
@Transaction
open suspend fun fetchChildren(id: Long): List<Task> {
return fetch(getChildren(id))
}
suspend fun getChildren(id: Long): List<Long> {
return getChildren(listOf(id))
}
@Query("WITH RECURSIVE "
+ " recursive_tasks (task) AS ( "
+ " SELECT _id "
+ " FROM tasks "
+ "WHERE parent IN (:ids)"
+ "UNION ALL "
+ " SELECT _id "
+ " FROM tasks "
+ " INNER JOIN recursive_tasks "
+ " ON recursive_tasks.task = tasks.parent"
+ " WHERE tasks.deleted = 0)"
+ "SELECT task FROM recursive_tasks")
abstract suspend fun getChildren(ids: List<Long>): List<Long>
@Query("UPDATE tasks SET collapsed = :collapsed WHERE _id = :id")
abstract suspend fun setCollapsed(id: Long, collapsed: Boolean)
@Transaction
open suspend fun setCollapsed(preferences: Preferences, filter: Filter, collapsed: Boolean) {
fetchTasks(preferences, filter)
.filter(TaskContainer::hasChildren)
.map(TaskContainer::getId)
.eachChunk { collapse(it, collapsed) }
}
@Query("UPDATE tasks SET collapsed = :collapsed WHERE _id IN (:ids)")
internal abstract suspend fun collapse(ids: List<Long>, collapsed: Boolean)
@Insert
abstract suspend fun insert(task: Task): Long
@Update
abstract suspend fun update(task: Task): Int
suspend fun createNew(task: Task) {
task.id = NO_ID
if (task.creationDate == 0L) {
task.creationDate = DateUtilities.now()
}
if (Task.isUuidEmpty(task.remoteId)) {
task.remoteId = UUIDHelper.newUUID()
}
if (BuildConfig.DEBUG) {
require(task.remoteId?.isNotBlank() == true && task.remoteId != "0")
}
val insert = insert(task)
task.id = insert
}
suspend fun count(filter: Filter): Int {
val query = getQuery(filter.sqlQuery, Field.COUNT)
val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0
val count = count(query)
Timber.v("%sms: %s", DateUtilities.now() - start, query.sql)
return count
}
suspend fun fetchFiltered(filter: Filter): List<Task> {
return fetchFiltered(filter.getSqlQuery())
}
suspend fun fetchFiltered(queryTemplate: String): List<Task> {
val query = getQuery(queryTemplate, Task.FIELDS)
val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0
val tasks = fetchTasks(query)
Timber.v("%sms: %s", DateUtilities.now() - start, query.sql)
return tasks.map(TaskContainer::getTask)
}
@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 suspend fun getLocalTasks(): List<Long>
/** Generates SQL clauses */
object TaskCriteria {
/** @return tasks that have not yet been completed or deleted
*/
@JvmStatic fun activeAndVisible(): Criterion {
return Criterion.and(
Task.COMPLETION_DATE.lte(0),
Task.DELETION_DATE.lte(0),
Task.HIDE_UNTIL.lte(Functions.now()))
}
}
companion object {
fun getQuery(queryTemplate: String, vararg fields: Field): SimpleSQLiteQuery {
return SimpleSQLiteQuery(
com.todoroo.andlib.sql.Query.select(*fields)
.withQueryTemplate(PermaSql.replacePlaceholdersForQuery(queryTemplate))
.from(Task.TABLE)
.toString())
}
}
}

@ -2,7 +2,6 @@ package org.tasks.injection
import android.content.Context
import com.todoroo.astrid.dao.Database
import com.todoroo.astrid.dao.TaskDao
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -13,7 +12,6 @@ import org.tasks.billing.BillingClient
import org.tasks.billing.BillingClientImpl
import org.tasks.billing.Inventory
import org.tasks.data.*
import org.tasks.jobs.WorkManager
import org.tasks.locale.Locale
import org.tasks.location.Geocoder
import org.tasks.location.MapboxGeocoder
@ -81,11 +79,7 @@ class ApplicationModule {
@Provides
@Singleton
fun getTaskDao(db: Database, workManager: WorkManager): TaskDao {
val taskDao = db.taskDao
taskDao.initialize(workManager)
return taskDao
}
fun getTaskDao(db: Database): TaskDao = db.taskDao
@Provides
@Singleton

@ -114,7 +114,7 @@ class AfterSaveWork @WorkerInject constructor(
.putBoolean(EXTRA_PUSH_GTASKS, !suppress && !current.googleTaskUpToDate(original))
.putBoolean(
EXTRA_PUSH_CALDAV, !suppress && (!current.caldavUpToDate(original) || forceCaldav))
.putBoolean(EXTRA_SUPPRESS_REFRESH, current.checkTransitory(TaskDao.TRANS_SUPPRESS_REFRESH))
.putBoolean(EXTRA_SUPPRESS_REFRESH, current.checkTransitory(Task.TRANS_SUPPRESS_REFRESH))
if (original != null) {
builder
.putLong(EXTRA_ORIG_COMPLETED, original.completionDate)

Loading…
Cancel
Save