Blocking daos delegate to suspending daos

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

@ -3,10 +3,12 @@ package com.todoroo.astrid.service
import com.natpryce.makeiteasy.MakeItEasy.with
import com.todoroo.astrid.api.CaldavFilter
import com.todoroo.astrid.api.GtasksFilter
import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.dao.TaskDaoBlocking
import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.UninstallModules
import org.junit.Assert.*
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.tasks.data.CaldavCalendar
@ -34,6 +36,7 @@ import javax.inject.Inject
@UninstallModules(ProductionModule::class)
@HiltAndroidTest
class TaskMoverTest : InjectingTestCase() {
@Inject lateinit var taskDaoAsync: TaskDao
@Inject lateinit var taskDao: TaskDaoBlocking
@Inject lateinit var googleTaskDao: GoogleTaskDaoBlocking
@Inject lateinit var workManager: WorkManager
@ -43,7 +46,7 @@ class TaskMoverTest : InjectingTestCase() {
@Before
override fun setUp() {
super.setUp()
taskDao.initialize(workManager)
taskDaoAsync.initialize(workManager)
}
@Test

@ -5,7 +5,7 @@ import androidx.room.RoomDatabase
import com.todoroo.astrid.data.Task
import org.tasks.data.*
import org.tasks.notifications.Notification
import org.tasks.notifications.NotificationDaoBlocking
import org.tasks.notifications.NotificationDao
@Database(
entities = [
@ -28,21 +28,21 @@ import org.tasks.notifications.NotificationDaoBlocking
GoogleTaskAccount::class],
version = 76)
abstract class Database : RoomDatabase() {
abstract fun notificationDao(): NotificationDaoBlocking
abstract val tagDataDao: TagDataDaoBlocking
abstract val userActivityDao: UserActivityDaoBlocking
abstract val taskAttachmentDao: TaskAttachmentDaoBlocking
abstract val taskListMetadataDao: TaskListMetadataDaoBlocking
abstract val alarmDao: AlarmDaoBlocking
abstract val locationDao: LocationDaoBlocking
abstract val tagDao: TagDaoBlocking
abstract val googleTaskDao: GoogleTaskDaoBlocking
abstract val filterDao: FilterDaoBlocking
abstract val googleTaskListDao: GoogleTaskListDaoBlocking
abstract val taskDao: TaskDaoBlocking
abstract val caldavDao: CaldavDaoBlocking
abstract val deletionDao: DeletionDaoBlocking
abstract val contentProviderDao: ContentProviderDaoBlocking
abstract fun notificationDao(): NotificationDao
abstract val tagDataDao: TagDataDao
abstract val userActivityDao: UserActivityDao
abstract val taskAttachmentDao: TaskAttachmentDao
abstract val taskListMetadataDao: TaskListMetadataDao
abstract val alarmDao: AlarmDao
abstract val locationDao: LocationDao
abstract val tagDao: TagDao
abstract val googleTaskDao: GoogleTaskDao
abstract val filterDao: FilterDao
abstract val googleTaskListDao: GoogleTaskListDao
abstract val taskDao: TaskDao
abstract val caldavDao: CaldavDao
abstract val deletionDao: DeletionDao
abstract val contentProviderDao: ContentProviderDao
/** @return human-readable database name for debugging
*/

@ -6,275 +6,211 @@
package com.todoroo.astrid.dao
import androidx.paging.DataSource
import androidx.room.*
import androidx.sqlite.db.SimpleSQLiteQuery
import com.todoroo.andlib.sql.Field
import com.todoroo.andlib.utility.AndroidUtilities.assertNotMainThread
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.dao.TaskDao.Companion.getQuery
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.data.Task.Companion.NO_ID
import com.todoroo.astrid.helper.UUIDHelper
import kotlinx.coroutines.runBlocking
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.DbUtils.chunkedMap
import org.tasks.db.DbUtils.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 TaskDaoBlocking(private val database: Database) {
private lateinit var workManager: WorkManager
@Deprecated("use coroutines")
class TaskDaoBlocking @Inject constructor(private val dao: TaskDao) {
fun needsRefresh(): List<Task> = runBlocking {
dao.needsRefresh()
}
fun initialize(workManager: WorkManager) {
this.workManager = workManager
fun needsRefresh(now: Long): List<Task> = runBlocking {
dao.needsRefresh(now)
}
fun needsRefresh(): List<Task> {
return needsRefresh(DateUtilities.now())
fun fetchBlocking(id: Long) = runBlocking {
dao.fetchBlocking(id)
}
@Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0 AND (hideUntil > :now OR dueDate > :now)")
abstract fun needsRefresh(now: Long): List<Task>
fun fetch(id: Long): Task? = runBlocking {
dao.fetch(id)
}
fun fetchBlocking(id: Long) = runBlocking {
fetch(id)
fun fetch(ids: List<Long>): List<Task> = runBlocking {
dao.fetch(ids)
}
internal fun fetchInternal(ids: List<Long>): List<Task> = runBlocking {
dao.fetchInternal(ids)
}
fun activeTimers(): Int = runBlocking {
dao.activeTimers()
}
fun activeNotifications(): List<Task> = runBlocking {
dao.activeNotifications()
}
fun fetch(remoteId: String): Task? = runBlocking {
dao.fetch(remoteId)
}
fun getActiveTasks(): List<Task> = runBlocking {
dao.getActiveTasks()
}
fun getVisibleTasks(): List<Task> = runBlocking {
dao.getVisibleTasks()
}
fun getRecurringTasks(remoteIds: List<String>): List<Task> = runBlocking {
dao.getRecurringTasks(remoteIds)
}
fun setCompletionDate(remoteId: String, completionDate: Long) = runBlocking {
dao.setCompletionDate(remoteId, completionDate)
}
fun snooze(taskIds: List<Long>, millis: Long) = runBlocking {
dao.snooze(taskIds, millis)
}
fun getGoogleTasksToPush(account: String): List<Task> = runBlocking {
dao.getGoogleTasksToPush(account)
}
fun getCaldavTasksToPush(calendar: String): List<Task> = runBlocking {
dao.getCaldavTasksToPush(calendar)
}
fun getTasksWithReminders(): List<Task> = runBlocking {
dao.getTasksWithReminders()
}
@Query("SELECT * FROM tasks WHERE _id = :id LIMIT 1")
abstract suspend fun fetch(id: Long): Task?
fun getAll(): List<Task> = runBlocking {
dao.getAll()
}
fun fetch(ids: List<Long>): List<Task> = ids.chunkedMap(this::fetchInternal)
fun getAllCalendarEvents(): List<String> = runBlocking {
dao.getAllCalendarEvents()
}
@Query("SELECT * FROM tasks WHERE _id IN (:ids)")
internal abstract fun fetchInternal(ids: List<Long>): List<Task>
fun clearAllCalendarEvents(): Int = runBlocking {
dao.clearAllCalendarEvents()
}
@Query("SELECT COUNT(1) FROM tasks WHERE timerStart > 0 AND deleted = 0")
abstract fun activeTimers(): Int
fun getCompletedCalendarEvents(): List<String> = runBlocking {
dao.getCompletedCalendarEvents()
}
@Query("SELECT tasks.* FROM tasks INNER JOIN notification ON tasks._id = notification.task")
abstract fun activeNotifications(): List<Task>
fun clearCompletedCalendarEvents(): Int = runBlocking {
dao.clearCompletedCalendarEvents()
}
@Query("SELECT * FROM tasks WHERE remoteId = :remoteId")
abstract fun fetch(remoteId: String): Task?
fun fetchTasks(callback: (SubtaskInfo) -> List<String>): List<TaskContainer> = runBlocking {
dao.fetchTasks(callback)
}
@Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0")
abstract fun getActiveTasks(): List<Task>
fun fetchTasks(callback: (SubtaskInfo) -> List<String>, subtasks: SubtaskInfo): List<TaskContainer> = runBlocking {
dao.fetchTasks(callback, subtasks)
}
@Query("SELECT * FROM tasks WHERE hideUntil < (strftime('%s','now')*1000)")
abstract fun getVisibleTasks(): List<Task>
fun fetchTasks(preferences: Preferences, filter: Filter): List<TaskContainer> = runBlocking {
dao.fetchTasks(preferences, filter)
}
@Query("SELECT * FROM tasks WHERE remoteId IN (:remoteIds) "
+ "AND recurrence IS NOT NULL AND LENGTH(recurrence) > 0")
abstract fun getRecurringTasks(remoteIds: List<String>): List<Task>
fun fetchTasks(query: SimpleSQLiteQuery): List<TaskContainer> = runBlocking {
dao.fetchTasks(query)
}
@Query("UPDATE tasks SET completed = :completionDate " + "WHERE remoteId = :remoteId")
abstract fun setCompletionDate(remoteId: String, completionDate: Long)
fun count(query: SimpleSQLiteQuery): Int = runBlocking {
dao.count(query)
}
@Query("UPDATE tasks SET snoozeTime = :millis WHERE _id in (:taskIds)")
abstract fun snooze(taskIds: List<Long>, millis: Long)
fun getSubtaskInfo(): SubtaskInfo = runBlocking {
dao.getSubtaskInfo()
}
@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 fun getGoogleTasksToPush(account: String): List<Task>
fun getTaskFactory(query: SimpleSQLiteQuery): DataSource.Factory<Int, TaskContainer> {
return dao.getTaskFactory(query)
}
@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 fun getCaldavTasksToPush(calendar: String): List<Task>
fun touch(id: Long) = runBlocking {
dao.touch(id)
}
@Query("SELECT * FROM TASKS "
+ "WHERE completed = 0 AND deleted = 0 AND (notificationFlags > 0 OR notifications > 0)")
abstract fun getTasksWithReminders(): List<Task>
fun touch(ids: List<Long>) = runBlocking {
dao.touch(ids)
}
// --- SQL clause generators
@Query("SELECT * FROM tasks")
abstract fun getAll(): List<Task>
fun touchInternal(ids: List<Long>, now: Long = currentTimeMillis()) = runBlocking {
dao.touchInternal(ids, now)
}
@Query("SELECT calendarUri FROM tasks " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''")
abstract fun getAllCalendarEvents(): List<String>
fun setParent(parent: Long, tasks: List<Long>) = runBlocking {
dao.setParent(parent, tasks)
}
@Query("UPDATE tasks SET calendarUri = '' " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''")
abstract fun clearAllCalendarEvents(): Int
internal fun setParentInternal(parent: Long, children: List<Long>) = runBlocking {
dao.setParentInternal(parent, children)
}
@Query("SELECT calendarUri FROM tasks "
+ "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''")
abstract fun getCompletedCalendarEvents(): List<String>
fun fetchChildren(id: Long): List<Task> = runBlocking {
dao.fetchChildren(id)
}
@Query("UPDATE tasks SET calendarUri = '' "
+ "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''")
abstract fun clearCompletedCalendarEvents(): Int
fun getChildren(id: Long): List<Long> = runBlocking {
dao.getChildren(id)
}
@Transaction
open fun fetchTasks(callback: (SubtaskInfo) -> List<String>): List<TaskContainer> {
return runBlocking {
fetchTasks(callback, getSubtaskInfo())
}
fun getChildren(ids: List<Long>): List<Long> = runBlocking {
dao.getChildren(ids)
}
@Transaction
open fun fetchTasks(callback: (SubtaskInfo) -> List<String>, subtasks: SubtaskInfo): List<TaskContainer> {
assertNotMainThread()
fun setCollapsed(id: Long, collapsed: Boolean) = runBlocking {
dao.setCollapsed(id, collapsed)
}
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
fun setCollapsed(preferences: Preferences, filter: Filter, collapsed: Boolean) = runBlocking {
dao.setCollapsed(preferences, filter, collapsed)
}
fun fetchTasks(preferences: Preferences, filter: Filter): List<TaskContainer> {
return fetchTasks {
TaskListQuery.getQuery(preferences, filter, it)
}
fun collapse(ids: List<Long>, collapsed: Boolean) = runBlocking {
dao.collapse(ids, collapsed)
}
@RawQuery
abstract fun fetchTasks(query: SimpleSQLiteQuery): List<TaskContainer>
@RawQuery
abstract 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>
fun touch(id: Long) = touch(listOf(id))
fun touch(ids: List<Long>) {
ids.eachChunk { touchInternal(it) }
workManager.sync(false)
}
@Query("UPDATE tasks SET modified = :now WHERE _id in (:ids)")
abstract fun touchInternal(ids: List<Long>, now: Long = currentTimeMillis())
fun setParent(parent: Long, tasks: List<Long>) =
tasks.eachChunk { setParentInternal(parent, it) }
@Query("UPDATE tasks SET parent = :parent WHERE _id IN (:children)")
internal abstract fun setParentInternal(parent: Long, children: List<Long>)
@Transaction
open fun fetchChildren(id: Long): List<Task> {
return fetch(getChildren(id))
}
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 fun getChildren(ids: List<Long>): List<Long>
fun save(task: Task) = runBlocking {
dao.save(task)
}
fun save(task: Task, original: Task? = fetchBlocking(task.id)) = runBlocking {
dao.save(task, original)
}
@Query("UPDATE tasks SET collapsed = :collapsed WHERE _id = :id")
abstract fun setCollapsed(id: Long, collapsed: Boolean)
@Transaction
open 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)")
abstract fun collapse(ids: List<Long>, collapsed: Boolean)
// --- save
// TODO: get rid of this super-hack
/**
* Saves the given task to the database.getDatabase(). Task must already exist. Returns true on
* success.
*/
@JvmOverloads
fun save(task: Task, original: Task? = fetchBlocking(task.id)) {
if (!task.insignificantChange(original)) {
task.modificationDate = DateUtilities.now()
}
if (update(task) == 1) {
workManager.afterSave(task, original)
}
fun insert(task: Task): Long = runBlocking {
dao.insert(task)
}
@Insert
abstract fun insert(task: Task): Long
fun update(task: Task): Int = runBlocking {
dao.update(task)
}
@Update
abstract fun update(task: Task): Int
fun createNew(task: Task) = runBlocking {
dao.createNew(task)
}
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()
}
val insert = insert(task)
task.id = insert
}
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
fun count(filter: Filter): Int = runBlocking {
dao.count(filter)
}
fun fetchFiltered(filter: Filter): List<Task> {
return fetchFiltered(filter.getSqlQuery())
fun fetchFiltered(filter: Filter): List<Task> = runBlocking {
dao.fetchFiltered(filter)
}
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)
fun fetchFiltered(queryTemplate: String): List<Task> = runBlocking {
dao.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 fun getLocalTasks(): List<Long>
fun getLocalTasks(): List<Long> = runBlocking {
dao.getLocalTasks()
}
}

@ -1,31 +1,31 @@
package org.tasks.data
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
@Dao
interface AlarmDaoBlocking {
@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 "
+ "ORDER BY time ASC")
fun getActiveAlarms(): List<Alarm>
@Deprecated("use coroutines")
class AlarmDaoBlocking @Inject constructor(private val dao: AlarmDao) {
fun getActiveAlarms(): List<Alarm> = runBlocking {
dao.getActiveAlarms()
}
@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 "
+ "ORDER BY time ASC")
fun getActiveAlarms(taskId: Long): List<Alarm>
fun getActiveAlarms(taskId: Long): List<Alarm> = runBlocking {
dao.getActiveAlarms(taskId)
}
@Query("SELECT * FROM alarms WHERE task = :taskId ORDER BY time ASC")
fun getAlarms(taskId: Long): List<Alarm>
fun getAlarms(taskId: Long): List<Alarm> = runBlocking {
dao.getAlarms(taskId)
}
@Delete
fun delete(alarm: Alarm)
fun delete(alarm: Alarm) = runBlocking {
dao.delete(alarm)
}
@Insert
fun insert(alarm: Alarm): Long
fun insert(alarm: Alarm): Long = runBlocking {
dao.insert(alarm)
}
@Insert
fun insert(alarms: Iterable<Alarm>)
fun insert(alarms: Iterable<Alarm>) = runBlocking {
dao.insert(alarms)
}
}

@ -2,274 +2,232 @@ package org.tasks.data
import android.content.Context
import androidx.lifecycle.LiveData
import androidx.room.*
import com.todoroo.andlib.utility.DateUtilities.now
import com.todoroo.astrid.api.FilterListItem.NO_ORDER
import com.todoroo.astrid.core.SortHelper.APPLE_EPOCH
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.helper.UUIDHelper
import org.tasks.R
import org.tasks.data.CaldavDao.Companion.LOCAL
import org.tasks.date.DateTimeUtils.toAppleEpoch
import org.tasks.db.DbUtils.chunkedMap
import kotlinx.coroutines.runBlocking
import org.tasks.filters.CaldavFilters
import org.tasks.time.DateTimeUtils.currentTimeMillis
import javax.inject.Inject
@Dao
abstract class CaldavDaoBlocking {
@Query("SELECT * FROM caldav_lists")
abstract fun subscribeToCalendars(): LiveData<List<CaldavCalendar>>
@Deprecated("use coroutines")
class CaldavDaoBlocking @Inject constructor(private val dao: CaldavDao) {
fun subscribeToCalendars(): LiveData<List<CaldavCalendar>> {
return dao.subscribeToCalendars()
}
fun getCalendarByUuid(uuid: String): CaldavCalendar? = runBlocking {
dao.getCalendarByUuid(uuid)
}
fun getCalendarsByAccount(uuid: String): List<CaldavCalendar> = runBlocking {
dao.getCalendarsByAccount(uuid)
}
fun getAccountByUuid(uuid: String): CaldavAccount? = runBlocking {
dao.getAccountByUuid(uuid)
}
fun accountCount(): Int = runBlocking {
dao.accountCount()
}
fun getAccounts(): List<CaldavAccount> = runBlocking {
dao.getAccounts()
}
fun setCollapsed(id: Long, collapsed: Boolean) = runBlocking {
dao.setCollapsed(id, collapsed)
}
fun insert(caldavAccount: CaldavAccount): Long = runBlocking {
dao.insert(caldavAccount)
}
fun update(caldavAccount: CaldavAccount) = runBlocking {
dao.update(caldavAccount)
}
fun insert(caldavCalendar: CaldavCalendar) = runBlocking {
dao.insert(caldavCalendar)
}
fun insertInternal(caldavCalendar: CaldavCalendar): Long = runBlocking {
dao.insertInternal(caldavCalendar)
}
fun update(caldavCalendar: CaldavCalendar) = runBlocking {
dao.update(caldavCalendar)
}
fun insert(task: Task, caldavTask: CaldavTask, addToTop: Boolean): Long = runBlocking {
dao.insert(task, caldavTask, addToTop)
}
internal fun findFirstTask(calendar: String, parent: Long): Long? = runBlocking {
dao.findFirstTask(calendar, parent)
}
internal fun findLastTask(calendar: String, parent: Long): Long? = runBlocking {
dao.findLastTask(calendar, parent)
}
fun insert(caldavTask: CaldavTask): Long = runBlocking {
dao.insert(caldavTask)
}
fun insert(tasks: Iterable<CaldavTask>) = runBlocking {
dao.insert(tasks)
}
fun update(caldavTask: CaldavTask) = runBlocking {
dao.update(caldavTask)
}
fun update(caldavTask: SubsetCaldav) = runBlocking {
dao.update(caldavTask)
}
@Query("SELECT * FROM caldav_lists WHERE cdl_uuid = :uuid LIMIT 1")
abstract fun getCalendarByUuid(uuid: String): CaldavCalendar?
internal fun update(id: Long, position: Long?, parent: String?) = runBlocking {
dao.update(id, position, parent)
}
@Query("SELECT * FROM caldav_lists WHERE cdl_account = :uuid")
abstract fun getCalendarsByAccount(uuid: String): List<CaldavCalendar>
internal fun update(id: Long, position: Long?) = runBlocking {
dao.update(id, position)
}
@Query("SELECT * FROM caldav_accounts WHERE cda_uuid = :uuid LIMIT 1")
abstract fun getAccountByUuid(uuid: String): CaldavAccount?
internal fun update(id: Long, remoteParent: String?) = runBlocking {
dao.update(id, remoteParent)
}
@Query("SELECT COUNT(*) FROM caldav_accounts WHERE cda_account_type != 2")
abstract fun accountCount(): Int
fun update(tasks: Iterable<CaldavTask>) = runBlocking {
dao.update(tasks)
}
@Query("SELECT * FROM caldav_accounts ORDER BY cda_account_type, UPPER(cda_name)")
abstract fun getAccounts(): List<CaldavAccount>
fun delete(caldavTask: CaldavTask) = runBlocking {
dao.delete(caldavTask)
}
@Query("UPDATE caldav_accounts SET cda_collapsed = :collapsed WHERE cda_id = :id")
abstract fun setCollapsed(id: Long, collapsed: Boolean)
fun getDeleted(calendar: String): List<CaldavTask> = runBlocking {
dao.getDeleted(calendar)
}
@Insert
abstract fun insert(caldavAccount: CaldavAccount): Long
fun markDeleted(tasks: List<Long>, now: Long = currentTimeMillis()) = runBlocking {
dao.markDeleted(tasks, now)
}
@Update
abstract fun update(caldavAccount: CaldavAccount)
fun getTask(taskId: Long): CaldavTask? = runBlocking {
dao.getTask(taskId)
}
fun insert(caldavCalendar: CaldavCalendar) {
caldavCalendar.id = insertInternal(caldavCalendar)
fun getRemoteIdForTask(taskId: Long): String? = runBlocking {
dao.getRemoteIdForTask(taskId)
}
@Insert
abstract fun insertInternal(caldavCalendar: CaldavCalendar): Long
fun getTask(calendar: String, obj: String): CaldavTask? = runBlocking {
dao.getTask(calendar, obj)
}
@Update
abstract fun update(caldavCalendar: CaldavCalendar)
fun getTaskByRemoteId(calendar: String, remoteId: String): CaldavTask? = runBlocking {
dao.getTaskByRemoteId(calendar, remoteId)
}
@Transaction
open fun insert(task: Task, caldavTask: CaldavTask, addToTop: Boolean): Long {
if (caldavTask.order != null) {
return insert(caldavTask)
}
if (addToTop) {
caldavTask.order = findFirstTask(caldavTask.calendar!!, task.parent)
?.takeIf { task.creationDate.toAppleEpoch() >= it }
?.minus(1)
} else {
caldavTask.order = findLastTask(caldavTask.calendar!!, task.parent)
?.takeIf { task.creationDate.toAppleEpoch() <= it }
?.plus(1)
}
return insert(caldavTask)
fun getTasks(taskId: Long): List<CaldavTask> = runBlocking {
dao.getTasks(taskId)
}
@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?
fun getTasks(taskIds: List<Long>): List<CaldavTask> = runBlocking {
dao.getTasks(taskIds)
}
@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?
fun getTasks(): List<CaldavTaskContainer> = runBlocking {
dao.getTasks()
}
@Insert
abstract fun insert(caldavTask: CaldavTask): Long
fun getCaldavTasksToPush(calendar: String): List<CaldavTaskContainer> = runBlocking {
dao.getCaldavTasksToPush(calendar)
}
@Insert
abstract fun insert(tasks: Iterable<CaldavTask>)
fun getCalendars(): List<CaldavCalendar> = runBlocking {
dao.getCalendars()
}
@Update
abstract fun update(caldavTask: CaldavTask)
fun getCalendar(uuid: String): CaldavCalendar? = runBlocking {
dao.getCalendar(uuid)
}
fun update(caldavTask: SubsetCaldav) {
update(caldavTask.cd_id, caldavTask.cd_order, caldavTask.cd_remote_parent)
fun getObjects(calendar: String): List<String> = runBlocking {
dao.getObjects(calendar)
}
@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?)
fun getTasks(calendar: String, objects: List<String>): List<Long> = runBlocking {
dao.getTasks(calendar, objects)
}
@Query("UPDATE caldav_tasks SET cd_order = :position WHERE cd_id = :id")
internal abstract fun update(id: Long, position: Long?)
fun getTasksInternal(calendar: String, objects: List<String>): List<Long> = runBlocking {
dao.getTasksInternal(calendar, objects)
}
@Query("UPDATE caldav_tasks SET cd_remote_parent = :remoteParent WHERE cd_id = :id")
internal abstract fun update(id: Long, remoteParent: String?)
fun findDeletedCalendars(account: String, urls: List<String>): List<CaldavCalendar> = runBlocking {
dao.findDeletedCalendars(account, urls)
}
@Update
abstract fun update(tasks: Iterable<CaldavTask>)
fun getCalendarByUrl(account: String, url: String): CaldavCalendar? = runBlocking {
dao.getCalendarByUrl(account, url)
}
@Delete
abstract fun delete(caldavTask: CaldavTask)
fun getAccountForTask(task: Long): CaldavAccount? = runBlocking {
dao.getAccountForTask(task)
}
fun getCalendars(tasks: List<Long>): List<String> = runBlocking {
dao.getCalendars(tasks)
}
fun getCaldavFilters(uuid: String, now: Long = currentTimeMillis()): List<CaldavFilters> = runBlocking {
dao.getCaldavFilters(uuid, now)
}
fun getTasksWithTags(): List<Long> = runBlocking {
dao.getTasksWithTags()
}
fun updateParents() = runBlocking {
dao.updateParents()
}
@Query("SELECT * FROM caldav_tasks WHERE cd_deleted > 0 AND cd_calendar = :calendar")
abstract fun getDeleted(calendar: String): List<CaldavTask>
fun updateParents(calendar: String) = runBlocking {
dao.updateParents(calendar)
}
@Query("UPDATE caldav_tasks SET cd_deleted = :now WHERE cd_task IN (:tasks)")
abstract fun markDeleted(tasks: List<Long>, now: Long = currentTimeMillis())
fun move(task: TaskContainer, newParent: Long, newPosition: Long?) = runBlocking {
dao.move(task, newParent, newPosition)
}
@Query("SELECT * FROM caldav_tasks WHERE cd_task = :taskId AND cd_deleted = 0 LIMIT 1")
abstract fun getTask(taskId: Long): CaldavTask?
fun shiftDown(calendar: String, parent: Long, from: Long, to: Long? = null) = runBlocking {
dao.shiftDown(calendar, parent, from, to)
}
@Query("SELECT cd_remote_id FROM caldav_tasks WHERE cd_task = :taskId AND cd_deleted = 0")
abstract fun getRemoteIdForTask(taskId: Long): String?
internal fun touchInternal(ids: List<Long>, modificationTime: Long = now()) = runBlocking {
dao.touchInternal(ids, modificationTime)
}
@Query("SELECT * FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_object = :obj LIMIT 1")
abstract fun getTask(calendar: String, obj: String): CaldavTask?
internal fun getTasksToShift(calendar: String, parent: Long, from: Long, to: Long?): List<CaldavTaskContainer> = runBlocking {
dao.getTasksToShift(calendar, parent, from, to)
}
@Query("SELECT * FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_remote_id = :remoteId")
abstract fun getTaskByRemoteId(calendar: String, remoteId: String): CaldavTask?
fun resetOrders() = runBlocking {
dao.resetOrders()
}
@Query("SELECT * FROM caldav_tasks WHERE cd_task = :taskId")
abstract fun getTasks(taskId: Long): List<CaldavTask>
fun setOrder(id: Long, order: Int) = runBlocking {
dao.setOrder(id, order)
}
@Query("SELECT * FROM caldav_tasks WHERE cd_task in (:taskIds) AND cd_deleted = 0")
abstract fun getTasks(taskIds: List<Long>): List<CaldavTask>
@Query("SELECT task.*, caldav_task.* FROM tasks AS 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 != ''")
abstract fun getTasks(): List<CaldavTaskContainer>
@Query("SELECT task.*, caldav_task.* FROM tasks AS task "
+ "INNER JOIN caldav_tasks AS caldav_task ON _id = cd_task "
+ "WHERE cd_calendar = :calendar "
+ "AND modified > cd_last_sync "
+ "AND cd_deleted = 0")
abstract fun getCaldavTasksToPush(calendar: String): List<CaldavTaskContainer>
@Query("SELECT * FROM caldav_lists ORDER BY cdl_name COLLATE NOCASE")
abstract fun getCalendars(): List<CaldavCalendar>
@Query("SELECT * FROM caldav_lists WHERE cdl_uuid = :uuid LIMIT 1")
abstract fun getCalendar(uuid: String): CaldavCalendar?
@Query("SELECT cd_object FROM caldav_tasks WHERE cd_calendar = :calendar")
abstract fun getObjects(calendar: String): List<String>
fun getTasks(calendar: String, objects: List<String>): List<Long> =
objects.chunkedMap { getTasksInternal(calendar, it) }
@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>
@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>
@Query("SELECT * FROM caldav_lists WHERE cdl_account = :account AND cdl_url = :url LIMIT 1")
abstract fun getCalendarByUrl(account: String, url: String): CaldavCalendar?
@Query("SELECT caldav_accounts.* from caldav_accounts"
+ " INNER JOIN caldav_tasks ON cd_task = :task"
+ " INNER JOIN caldav_lists ON cd_calendar = cdl_uuid"
+ " WHERE cdl_account = cda_uuid")
abstract fun getAccountForTask(task: Long): CaldavAccount?
fun setupLocalAccount(context: Context): CaldavAccount = runBlocking {
dao.setupLocalAccount(context)
}
@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>
@Query("SELECT caldav_lists.*, COUNT(tasks._id) AS count"
+ " FROM caldav_lists"
+ " LEFT JOIN caldav_tasks ON caldav_tasks.cd_calendar = caldav_lists.cdl_uuid"
+ " 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"
+ " GROUP BY caldav_lists.cdl_uuid")
abstract fun getCaldavFilters(uuid: String, now: Long = currentTimeMillis()): List<CaldavFilters>
@Query("SELECT tasks._id FROM tasks "
+ "INNER JOIN tags ON tags.task = tasks._id "
+ "INNER JOIN caldav_tasks ON cd_task = tasks._id "
+ "GROUP BY tasks._id")
abstract fun getTasksWithTags(): List<Long>
@Query("UPDATE tasks SET parent = IFNULL(("
+ " SELECT p.cd_task FROM caldav_tasks AS p"
+ " INNER JOIN caldav_tasks ON caldav_tasks.cd_task = tasks._id"
+ " WHERE p.cd_remote_id = caldav_tasks.cd_remote_parent"
+ " AND p.cd_calendar = caldav_tasks.cd_calendar"
+ " 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)")
abstract fun updateParents()
@Query("UPDATE tasks SET parent = IFNULL(("
+ " SELECT p.cd_task FROM caldav_tasks AS p"
+ " INNER JOIN caldav_tasks "
+ " ON caldav_tasks.cd_task = tasks._id"
+ " AND caldav_tasks.cd_calendar = :calendar"
+ " WHERE p.cd_remote_id = caldav_tasks.cd_remote_parent"
+ " AND p.cd_calendar = caldav_tasks.cd_calendar"
+ " 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)")
abstract fun updateParents(calendar: String)
@Transaction
open fun move(task: TaskContainer, newParent: Long, newPosition: Long?) {
val previousParent = task.parent
val caldavTask = task.caldavTask
val previousPosition = task.caldavSortOrder
if (newPosition != null) {
if (newParent == previousParent && newPosition < previousPosition) {
shiftDown(task.caldav!!, newParent, newPosition, previousPosition)
} else {
shiftDown(task.caldav!!, newParent, newPosition)
}
}
caldavTask.cd_order = newPosition
update(caldavTask.cd_id, caldavTask.cd_order)
}
@Transaction
open fun shiftDown(calendar: String, parent: Long, from: Long, to: Long? = null) {
val updated = ArrayList<CaldavTask>()
val tasks = getTasksToShift(calendar, parent, from, to)
for (i in tasks.indices) {
val task = tasks[i]
val current = from + i
if (task.sortOrder == current) {
val caldavTask = task.caldavTask
caldavTask.order = current + 1
updated.add(caldavTask)
} else if (task.sortOrder > current) {
break
}
}
update(updated)
touchInternal(updated.map(CaldavTask::task))
}
@Query("UPDATE tasks SET modified = :modificationTime WHERE _id in (:ids)")
internal abstract 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")
internal abstract fun getTasksToShift(calendar: String, parent: Long, from: Long, to: Long?): List<CaldavTaskContainer>
@Query("UPDATE caldav_lists SET cdl_order = $NO_ORDER")
abstract fun resetOrders()
@Query("UPDATE caldav_lists SET cdl_order = :order WHERE cdl_id = :id")
abstract fun setOrder(id: Long, order: Int)
fun setupLocalAccount(context: Context): CaldavAccount {
val account = getLocalAccount()
getLocalList(context, account)
return account
}
fun getLocalList(context: Context) = getLocalList(context, getLocalAccount())
private fun getLocalAccount() = getAccountByUuid(LOCAL) ?: CaldavAccount().apply {
accountType = CaldavAccount.TYPE_LOCAL
uuid = LOCAL
id = insert(this)
}
private fun getLocalList(context: Context, account: CaldavAccount): CaldavCalendar =
getCalendarsByAccount(account.uuid!!).getOrNull(0)
?: CaldavCalendar(context.getString(R.string.default_list), UUIDHelper.newUUID()).apply {
this.account = account.uuid
insert(this)
}
fun getLocalList(context: Context) = runBlocking {
dao.getLocalList(context)
}
}

@ -1,47 +1,38 @@
package org.tasks.data
import android.database.Cursor
import androidx.room.Dao
import androidx.room.Query
import androidx.room.RawQuery
import androidx.sqlite.db.SupportSQLiteQuery
import com.todoroo.astrid.data.Task
@Dao
interface ContentProviderDaoBlocking {
@Query("SELECT name FROM tags WHERE task = :taskId ORDER BY UPPER(name) ASC")
fun getTagNames(taskId: Long): List<String>
@Query("""
SELECT *
FROM tasks
WHERE completed = 0
AND deleted = 0
AND hideUntil < (strftime('%s', 'now') * 1000)
ORDER BY (CASE
WHEN (dueDate = 0) THEN
(strftime('%s', 'now') * 1000) * 2
ELSE ((CASE WHEN (dueDate / 1000) % 60 > 0 THEN dueDate ELSE (dueDate + 43140000) END)) END) +
172800000 * importance
ASC
LIMIT 100""")
fun getAstrid2TaskProviderTasks(): List<Task>
@Query("SELECT * FROM tagdata WHERE name IS NOT NULL AND name != '' ORDER BY UPPER(name) ASC")
fun tagDataOrderedByName(): List<TagData>
@Query("SELECT * FROM tasks")
fun getTasks(): Cursor
@Query("""
SELECT caldav_lists.*, caldav_accounts.cda_name
FROM caldav_lists
INNER JOIN caldav_accounts ON cdl_account = cda_uuid""")
fun getLists(): Cursor
@Query("SELECT * FROM google_task_lists")
fun getGoogleTaskLists(): Cursor
@RawQuery
fun rawQuery(query: SupportSQLiteQuery): Cursor
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
@Deprecated("use coroutines")
class ContentProviderDaoBlocking @Inject constructor(private val dao: ContentProviderDao) {
fun getTagNames(taskId: Long): List<String> = runBlocking {
dao.getTagNames(taskId)
}
fun getAstrid2TaskProviderTasks(): List<Task> = runBlocking {
dao.getAstrid2TaskProviderTasks()
}
fun tagDataOrderedByName(): List<TagData> = runBlocking {
dao.tagDataOrderedByName()
}
fun getTasks(): Cursor {
return dao.getTasks()
}
fun getLists(): Cursor {
return dao.getLists()
}
fun getGoogleTaskLists(): Cursor {
return dao.getGoogleTaskLists()
}
fun rawQuery(query: SupportSQLiteQuery): Cursor {
return dao.rawQuery(query)
}
}

@ -1,114 +1,95 @@
package org.tasks.data
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Query
import androidx.room.Transaction
import org.tasks.data.CaldavDao.Companion.LOCAL
import org.tasks.db.DbUtils.eachChunk
import java.util.*
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
@Dao
abstract class DeletionDaoBlocking {
@Query("DELETE FROM caldav_tasks WHERE cd_task IN(:ids)")
abstract fun deleteCaldavTasks(ids: List<Long>)
@Deprecated("use coroutines")
class DeletionDaoBlocking @Inject constructor(private val dao: DeletionDao) {
fun deleteCaldavTasks(ids: List<Long>) = runBlocking {
dao.deleteCaldavTasks(ids)
}
@Query("DELETE FROM google_tasks WHERE gt_task IN(:ids)")
abstract fun deleteGoogleTasks(ids: List<Long>)
fun deleteGoogleTasks(ids: List<Long>) = runBlocking {
dao.deleteGoogleTasks(ids)
}
@Query("DELETE FROM tags WHERE task IN(:ids)")
abstract fun deleteTags(ids: List<Long>)
fun deleteTags(ids: List<Long>) = runBlocking {
dao.deleteTags(ids)
}
@Query("DELETE FROM geofences WHERE task IN(:ids)")
abstract fun deleteGeofences(ids: List<Long>)
fun deleteGeofences(ids: List<Long>) = runBlocking {
dao.deleteGeofences(ids)
}
@Query("DELETE FROM alarms WHERE task IN(:ids)")
abstract fun deleteAlarms(ids: List<Long>)
fun deleteAlarms(ids: List<Long>) = runBlocking {
dao.deleteAlarms(ids)
}
@Query("DELETE FROM tasks WHERE _id IN(:ids)")
abstract fun deleteTasks(ids: List<Long>)
fun deleteTasks(ids: List<Long>) = runBlocking {
dao.deleteTasks(ids)
}
@Transaction
open fun delete(ids: List<Long>) {
ids.eachChunk {
deleteAlarms(it)
deleteGeofences(it)
deleteTags(it)
deleteGoogleTasks(it)
deleteCaldavTasks(it)
deleteTasks(it)
}
fun delete(ids: List<Long>) = runBlocking {
dao.delete(ids)
}
@Query("UPDATE tasks "
+ "SET modified = (strftime('%s','now')*1000), deleted = (strftime('%s','now')*1000)"
+ "WHERE _id IN(:ids)")
abstract fun markDeletedInternal(ids: List<Long>)
fun markDeletedInternal(ids: List<Long>) = runBlocking {
dao.markDeletedInternal(ids)
}
fun markDeleted(ids: Iterable<Long>) {
ids.eachChunk(this::markDeletedInternal)
fun markDeleted(ids: Iterable<Long>) = runBlocking {
dao.markDeleted(ids)
}
@Query("SELECT gt_task FROM google_tasks WHERE gt_deleted = 0 AND gt_list_id = :listId")
abstract fun getActiveGoogleTasks(listId: String): List<Long>
fun getActiveGoogleTasks(listId: String): List<Long> = runBlocking {
dao.getActiveGoogleTasks(listId)
}
@Delete
abstract fun deleteGoogleTaskList(googleTaskList: GoogleTaskList)
fun deleteGoogleTaskList(googleTaskList: GoogleTaskList) = runBlocking {
dao.deleteGoogleTaskList(googleTaskList)
}
@Transaction
open fun delete(googleTaskList: GoogleTaskList): List<Long> {
val tasks = getActiveGoogleTasks(googleTaskList.remoteId!!)
delete(tasks)
deleteGoogleTaskList(googleTaskList)
return tasks
fun delete(googleTaskList: GoogleTaskList): List<Long> = runBlocking {
dao.delete(googleTaskList)
}
@Delete
abstract fun deleteGoogleTaskAccount(googleTaskAccount: GoogleTaskAccount)
fun deleteGoogleTaskAccount(googleTaskAccount: GoogleTaskAccount) = runBlocking {
dao.deleteGoogleTaskAccount(googleTaskAccount)
}
@Query("SELECT * FROM google_task_lists WHERE gtl_account = :account ORDER BY gtl_title ASC")
abstract fun getLists(account: String): List<GoogleTaskList>
fun getLists(account: String): List<GoogleTaskList> = runBlocking {
dao.getLists(account)
}
@Transaction
open fun delete(googleTaskAccount: GoogleTaskAccount): List<Long> {
val deleted = ArrayList<Long>()
for (list in getLists(googleTaskAccount.account!!)) {
deleted.addAll(delete(list))
}
deleteGoogleTaskAccount(googleTaskAccount)
return deleted
fun delete(googleTaskAccount: GoogleTaskAccount): List<Long> = runBlocking {
dao.delete(googleTaskAccount)
}
@Query("SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_deleted = 0")
abstract fun getActiveCaldavTasks(calendar: String): List<Long>
fun getActiveCaldavTasks(calendar: String): List<Long> = runBlocking {
dao.getActiveCaldavTasks(calendar)
}
@Delete
abstract fun deleteCaldavCalendar(caldavCalendar: CaldavCalendar)
fun deleteCaldavCalendar(caldavCalendar: CaldavCalendar) = runBlocking {
dao.deleteCaldavCalendar(caldavCalendar)
}
@Transaction
open fun delete(caldavCalendar: CaldavCalendar): List<Long> {
val tasks = getActiveCaldavTasks(caldavCalendar.uuid!!)
delete(tasks)
deleteCaldavCalendar(caldavCalendar)
return tasks
fun delete(caldavCalendar: CaldavCalendar): List<Long> = runBlocking {
dao.delete(caldavCalendar)
}
@Query("SELECT * FROM caldav_lists WHERE cdl_account = :account")
abstract fun getCalendars(account: String): List<CaldavCalendar>
fun getCalendars(account: String): List<CaldavCalendar> = runBlocking {
dao.getCalendars(account)
}
@Delete
abstract fun deleteCaldavAccount(caldavAccount: CaldavAccount)
fun deleteCaldavAccount(caldavAccount: CaldavAccount) = runBlocking {
dao.deleteCaldavAccount(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)")
abstract fun purgeDeleted()
fun purgeDeleted() = runBlocking {
dao.purgeDeleted()
}
@Transaction
open fun delete(caldavAccount: CaldavAccount): List<Long> {
val deleted = ArrayList<Long>()
for (calendar in getCalendars(caldavAccount.uuid!!)) {
deleted.addAll(delete(calendar))
}
deleteCaldavAccount(caldavAccount)
return deleted
fun delete(caldavAccount: CaldavAccount): List<Long> = runBlocking {
dao.delete(caldavAccount)
}
}

@ -1,37 +1,43 @@
package org.tasks.data
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.todoroo.astrid.api.FilterListItem.NO_ORDER
@Dao
interface FilterDaoBlocking {
@Update
fun update(filter: Filter)
@Query("DELETE FROM filters WHERE _id = :id")
fun delete(id: Long)
@Query("SELECT * FROM filters WHERE title = :title COLLATE NOCASE LIMIT 1")
fun getByName(title: String): Filter?
@Insert
fun insert(filter: Filter): Long
@Query("SELECT * FROM filters")
fun getFilters(): List<Filter>
@Query("SELECT * FROM filters WHERE _id = :id LIMIT 1")
fun getById(id: Long): Filter?
@Query("SELECT * FROM filters")
fun getAll(): List<Filter>
@Query("UPDATE filters SET f_order = $NO_ORDER")
fun resetOrders()
@Query("UPDATE filters SET f_order = :order WHERE _id = :id")
fun setOrder(id: Long, order: Int)
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
@Deprecated("use coroutines")
class FilterDaoBlocking @Inject constructor(private val dao: FilterDao) {
fun update(filter: Filter) = runBlocking {
dao.update(filter)
}
fun delete(id: Long) = runBlocking {
dao.delete(id)
}
fun getByName(title: String): Filter? = runBlocking {
dao.getByName(title)
}
fun insert(filter: Filter): Long = runBlocking {
dao.insert(filter)
}
fun getFilters(): List<Filter> = runBlocking {
dao.getFilters()
}
fun getById(id: Long): Filter? = runBlocking {
dao.getById(id)
}
fun getAll(): List<Filter> = runBlocking {
dao.getAll()
}
fun resetOrders() = runBlocking {
dao.resetOrders()
}
fun setOrder(id: Long, order: Int) = runBlocking {
dao.setOrder(id, order)
}
}

@ -1,179 +1,137 @@
package org.tasks.data
import androidx.room.*
import com.todoroo.astrid.data.Task
import kotlinx.coroutines.runBlocking
import org.tasks.time.DateTimeUtils.currentTimeMillis
import javax.inject.Inject
@Dao
abstract class GoogleTaskDaoBlocking {
@Insert
abstract fun insert(task: GoogleTask): Long
@Deprecated("use coroutines")
class GoogleTaskDaoBlocking @Inject constructor(private val dao: GoogleTaskDao) {
fun insert(task: GoogleTask): Long = runBlocking {
dao.insert(task)
}
fun insert(tasks: Iterable<GoogleTask>) = runBlocking {
dao.insert(tasks)
}
fun insertAndShift(task: GoogleTask, top: Boolean) = runBlocking {
dao.insertAndShift(task, top)
}
fun shiftDown(listId: String, parent: Long, position: Long) = runBlocking {
dao.shiftDown(listId, parent, position)
}
fun shiftUp(listId: String, parent: Long, from: Long, to: Long) = runBlocking {
dao.shiftUp(listId, parent, from, to)
}
fun shiftDown(listId: String, parent: Long, from: Long, to: Long) = runBlocking {
dao.shiftDown(listId, parent, from, to)
}
fun shiftUp(listId: String, parent: Long, position: Long) = runBlocking {
dao.shiftUp(listId, parent, position)
}
fun move(task: SubsetGoogleTask, newParent: Long, newPosition: Long) = runBlocking {
dao.move(task, newParent, newPosition)
}
fun setCollapsed(id: Long, collapsed: Boolean) = runBlocking {
dao.setCollapsed(id, collapsed)
}
fun getByTaskId(taskId: Long): GoogleTask? = runBlocking {
dao.getByTaskId(taskId)
}
fun update(googleTask: GoogleTask) = runBlocking {
dao.update(googleTask)
}
fun update(id: Long, parent: Long, order: Long) = runBlocking {
dao.update(id, parent, order)
}
fun markDeleted(task: Long, now: Long = currentTimeMillis()) = runBlocking {
dao.markDeleted(task, now)
}
fun delete(deleted: GoogleTask) = runBlocking {
dao.delete(deleted)
}
@Insert
abstract fun insert(tasks: Iterable<GoogleTask>)
fun getByRemoteId(remoteId: String): GoogleTask? = runBlocking {
dao.getByRemoteId(remoteId)
}
fun getDeletedByTaskId(taskId: Long): List<GoogleTask> = runBlocking {
dao.getDeletedByTaskId(taskId)
}
fun getAllByTaskId(taskId: Long): List<GoogleTask> = runBlocking {
dao.getAllByTaskId(taskId)
}
fun getLists(tasks: List<Long>): List<String> = runBlocking {
dao.getLists(tasks)
}
@Transaction
open fun insertAndShift(task: GoogleTask, top: Boolean) {
if (top) {
task.order = 0
shiftDown(task.listId!!, task.parent, 0)
} else {
task.order = getBottom(task.listId!!, task.parent)
}
task.id = insert(task)
fun getChildren(ids: List<Long>): List<Long> = runBlocking {
dao.getChildren(ids)
}
@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)
fun getChildTasks(taskId: Long): List<Task> = runBlocking {
dao.getChildTasks(taskId)
}
fun getChildren(id: Long): List<GoogleTask> = runBlocking {
dao.getChildren(id)
}
@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)
fun getBottom(listId: String, parent: Long): Long = runBlocking {
dao.getBottom(listId, parent)
}
@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)
fun getPrevious(listId: String, parent: Long, order: Long): String? = runBlocking {
dao.getPrevious(listId, parent, order)
}
fun getRemoteId(task: Long): String? = runBlocking {
dao.getRemoteId(task)
}
@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)
fun getTask(remoteId: String): Long = runBlocking {
dao.getTask(remoteId)
}
fun getByLocalOrder(listId: String): List<GoogleTask> = runBlocking {
dao.getByLocalOrder(listId)
}
fun getByRemoteOrder(listId: String): List<GoogleTask> = runBlocking {
dao.getByRemoteOrder(listId)
}
fun updateParents() = runBlocking {
dao.updateParents()
}
fun updateParents(listId: String) = runBlocking {
dao.updateParents(listId)
}
fun updatePosition(id: String, parent: String, position: String) = runBlocking {
dao.updatePosition(id, parent, position)
}
fun reposition(listId: String) = runBlocking {
dao.reposition(listId)
}
@Transaction
open fun move(task: SubsetGoogleTask, newParent: Long, newPosition: Long) {
val previousParent = task.parent
val previousPosition = task.order
if (newParent == previousParent) {
if (previousPosition < newPosition) {
shiftUp(task.listId, newParent, previousPosition, newPosition)
} else {
shiftDown(task.listId, newParent, previousPosition, newPosition)
}
} else {
shiftUp(task.listId, previousParent, previousPosition)
shiftDown(task.listId, newParent, newPosition)
}
task.parent = newParent
task.order = newPosition
update(task)
}
@Query("UPDATE google_task_accounts SET gta_collapsed = :collapsed WHERE gta_id = :id")
abstract fun setCollapsed(id: Long, collapsed: Boolean)
@Query("SELECT * FROM google_tasks WHERE gt_task = :taskId AND gt_deleted = 0 LIMIT 1")
abstract fun getByTaskId(taskId: Long): GoogleTask?
@Update
abstract fun update(googleTask: GoogleTask)
private fun update(googleTask: SubsetGoogleTask) {
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")
abstract fun update(id: Long, parent: Long, order: Long)
@Query("UPDATE google_tasks SET gt_deleted = :now WHERE gt_task = :task OR gt_parent = :task")
abstract fun markDeleted(task: Long, now: Long = currentTimeMillis())
@Delete
abstract fun delete(deleted: GoogleTask)
@Query("SELECT * FROM google_tasks WHERE gt_remote_id = :remoteId LIMIT 1")
abstract fun getByRemoteId(remoteId: String): GoogleTask?
@Query("SELECT * FROM google_tasks WHERE gt_task = :taskId AND gt_deleted > 0")
abstract fun getDeletedByTaskId(taskId: Long): List<GoogleTask>
@Query("SELECT * FROM google_tasks WHERE gt_task = :taskId")
abstract fun getAllByTaskId(taskId: Long): List<GoogleTask>
@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>
@Query("SELECT gt_task FROM google_tasks WHERE gt_parent IN (:ids) AND gt_deleted = 0")
abstract fun getChildren(ids: List<Long>): List<Long>
@Query("SELECT tasks.* FROM tasks JOIN google_tasks ON tasks._id = gt_task WHERE gt_parent = :taskId")
abstract fun getChildTasks(taskId: Long): List<Task>
@Query("SELECT * FROM google_tasks WHERE gt_parent = :id AND gt_deleted = 0")
abstract 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")
abstract 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")
abstract fun getPrevious(listId: String, parent: Long, order: Long): String?
@Query("SELECT gt_remote_id FROM google_tasks WHERE gt_task = :task")
abstract fun getRemoteId(task: Long): String?
@Query("SELECT gt_task FROM google_tasks WHERE gt_remote_id = :remoteId")
abstract fun getTask(remoteId: String): Long
@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")
abstract fun getByLocalOrder(listId: String): List<GoogleTask>
@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")
abstract fun getByRemoteOrder(listId: String): List<GoogleTask>
@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"
+ " AND p.gt_list_id = google_tasks.gt_list_id "
+ " AND p.gt_deleted = 0),"
+ " 0)"
+ " WHERE gt_moved = 0")
abstract 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")
abstract fun updateParents(listId: String)
@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)
@Transaction
open fun reposition(listId: String) {
updateParents(listId)
val orderedTasks = getByRemoteOrder(listId)
var subtasks = 0L
var parent = 0L
for (task in orderedTasks) {
if (task.parent > 0) {
if (task.order != subtasks && !task.isMoved) {
task.order = subtasks
update(task)
}
subtasks++
} else {
subtasks = 0
if (task.order != parent && !task.isMoved) {
task.order = parent
update(task)
}
parent++
}
}
}
fun validateSorting(listId: String) {
val orderedTasks = getByLocalOrder(listId)
var subtasks = 0L
var parent = 0L
for (task in orderedTasks) {
if (task.parent > 0) {
if (task.order != subtasks) {
throw IllegalStateException("Subtask violation, expected $subtasks but was ${task.order}")
}
subtasks++
} else {
subtasks = 0
if (task.order != parent) {
throw IllegalStateException("Parent violation, expected $parent but was ${task.order}")
}
parent++
}
}
fun validateSorting(listId: String) = runBlocking {
dao.validateSorting(listId)
}
}

@ -1,72 +1,86 @@
package org.tasks.data
import androidx.lifecycle.LiveData
import androidx.room.*
import com.todoroo.astrid.api.FilterListItem.NO_ORDER
import kotlinx.coroutines.runBlocking
import org.tasks.filters.GoogleTaskFilters
import org.tasks.time.DateTimeUtils.currentTimeMillis
import javax.inject.Inject
@Dao
interface GoogleTaskListDaoBlocking {
@Query("SELECT COUNT(*) FROM google_task_accounts")
fun accountCount(): Int
@Deprecated("use coroutines")
class GoogleTaskListDaoBlocking @Inject constructor(private val dao: GoogleTaskListDao) {
fun accountCount(): Int = runBlocking {
dao.accountCount()
}
@Query("SELECT * FROM google_task_accounts")
fun getAccounts(): List<GoogleTaskAccount>
fun getAccounts(): List<GoogleTaskAccount> = runBlocking {
dao.getAccounts()
}
@Query("SELECT * FROM google_task_accounts WHERE gta_account = :account COLLATE NOCASE LIMIT 1")
fun getAccount(account: String): GoogleTaskAccount?
fun getAccount(account: String): GoogleTaskAccount? = runBlocking {
dao.getAccount(account)
}
@Query("SELECT * FROM google_task_lists WHERE gtl_id = :id")
fun getById(id: Long): GoogleTaskList?
fun getById(id: Long): GoogleTaskList? = runBlocking {
dao.getById(id)
}
@Query("SELECT * FROM google_task_lists WHERE gtl_account = :account ORDER BY gtl_title ASC")
fun getLists(account: String): List<GoogleTaskList>
fun getLists(account: String): List<GoogleTaskList> = runBlocking {
dao.getLists(account)
}
@Query("SELECT * FROM google_task_lists WHERE gtl_remote_id = :remoteId LIMIT 1")
fun getByRemoteId(remoteId: String): GoogleTaskList?
fun getByRemoteId(remoteId: String): GoogleTaskList? = runBlocking {
dao.getByRemoteId(remoteId)
}
@Query("SELECT * FROM google_task_lists WHERE gtl_remote_id IN (:remoteIds)")
fun getByRemoteId(remoteIds: List<String>): List<GoogleTaskList>
fun getByRemoteId(remoteIds: List<String>): List<GoogleTaskList> = runBlocking {
dao.getByRemoteId(remoteIds)
}
@Query("SELECT * FROM google_task_lists")
fun subscribeToLists(): LiveData<List<GoogleTaskList>>
fun subscribeToLists(): LiveData<List<GoogleTaskList>> {
return dao.subscribeToLists()
}
@Query("SELECT * FROM google_task_lists WHERE gtl_remote_id = :remoteId AND IFNULL(gtl_account, '') = ''")
fun findExistingList(remoteId: String): GoogleTaskList?
fun findExistingList(remoteId: String): GoogleTaskList? = runBlocking {
dao.findExistingList(remoteId)
}
@Query("SELECT * FROM google_task_lists")
fun getAllLists(): List<GoogleTaskList>
fun getAllLists(): List<GoogleTaskList> = runBlocking {
dao.getAllLists()
}
@Query("UPDATE google_task_lists SET gtl_last_sync = 0 WHERE gtl_account = :account")
fun resetLastSync(account: String)
fun resetLastSync(account: String) = runBlocking {
dao.resetLastSync(account)
}
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertOrReplace(googleTaskList: GoogleTaskList): Long
fun insertOrReplace(googleTaskList: GoogleTaskList): Long = runBlocking {
dao.insertOrReplace(googleTaskList)
}
@Insert
fun insert(googleTaskList: GoogleTaskList): Long
fun insert(googleTaskList: GoogleTaskList): Long = runBlocking {
dao.insert(googleTaskList)
}
@Insert
fun insert(googleTaskAccount: GoogleTaskAccount)
fun insert(googleTaskAccount: GoogleTaskAccount) = runBlocking {
dao.insert(googleTaskAccount)
}
@Update
fun update(account: GoogleTaskAccount)
fun update(account: GoogleTaskAccount) = runBlocking {
dao.update(account)
}
@Update
fun update(list: GoogleTaskList)
fun update(list: GoogleTaskList) = runBlocking {
dao.update(list)
}
@Query("SELECT google_task_lists.*, COUNT(tasks._id) AS count"
+ " FROM google_task_lists "
+ " LEFT JOIN google_tasks ON google_tasks.gt_list_id = google_task_lists.gtl_remote_id"
+ " 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"
+ " GROUP BY google_task_lists.gtl_remote_id")
fun getGoogleTaskFilters(account: String, now: Long = currentTimeMillis()): List<GoogleTaskFilters>
fun getGoogleTaskFilters(account: String, now: Long = currentTimeMillis()): List<GoogleTaskFilters> = runBlocking {
dao.getGoogleTaskFilters(account, now)
}
@Query("UPDATE google_task_lists SET gtl_remote_order = $NO_ORDER")
fun resetOrders()
fun resetOrders() = runBlocking {
dao.resetOrders()
}
@Query("UPDATE google_task_lists SET gtl_remote_order = :order WHERE gtl_id = :id")
fun setOrder(id: Long, order: Int)
fun setOrder(id: Long, order: Int) = runBlocking {
dao.setOrder(id, order)
}
}

@ -1,118 +1,114 @@
package org.tasks.data
import androidx.lifecycle.LiveData
import androidx.room.*
import com.todoroo.astrid.api.FilterListItem.NO_ORDER
import kotlinx.coroutines.runBlocking
import org.tasks.filters.LocationFilters
import org.tasks.time.DateTimeUtils.currentTimeMillis
import javax.inject.Inject
@Dao
interface LocationDaoBlocking {
@Query("SELECT places.*"
+ " FROM places"
+ " INNER JOIN geofences ON geofences.place = places.uid"
+ " INNER JOIN tasks ON geofences.task = tasks._id"
+ " WHERE tasks.completed = 0 AND tasks.deleted = 0"
+ " AND (geofences.arrival > 0 OR geofences.departure > 0)"
+ " GROUP BY places.uid")
fun getPlacesWithGeofences(): List<Place>
@Query("SELECT places.*,"
+ " max(geofences.arrival) as arrival,"
+ " max(geofences.departure) as departure,"
+ " min(geofences.radius) as radius"
+ " FROM places"
+ " INNER JOIN geofences ON geofences.place = places.uid"
+ " INNER JOIN tasks ON tasks._id = geofences.task"
+ " WHERE place = :uid AND tasks.completed = 0 AND tasks.deleted = 0"
+ " AND (geofences.arrival > 0 OR geofences.departure > 0)"
+ " GROUP BY places.uid")
fun getGeofencesByPlace(uid: String): MergedGeofence?
@Query("DELETE FROM geofences WHERE place = :place")
fun deleteGeofencesByPlace(place: String)
@Query("SELECT geofences.* FROM geofences"
+ " INNER JOIN tasks ON tasks._id = geofences.task"
+ " WHERE place = :place AND arrival = 1 AND tasks.completed = 0"
+ " AND tasks.deleted = 0 AND tasks.snoozeTime < :now AND tasks.hideUntil < :now")
fun getArrivalGeofences(place: String, now: Long): List<Geofence>
@Query("SELECT geofences.* FROM geofences"
+ " INNER JOIN tasks ON tasks._id = geofences.task"
+ " WHERE place = :place AND departure = 1 AND tasks.completed = 0"
+ " AND tasks.deleted = 0 AND tasks.snoozeTime < :now AND tasks.hideUntil < :now")
fun getDepartureGeofences(place: String, now: Long): List<Geofence>
@Query("SELECT * FROM geofences"
+ " INNER JOIN places ON geofences.place = places.uid"
+ " WHERE task = :taskId ORDER BY name ASC LIMIT 1")
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")
fun getActiveGeofences(taskId: Long): List<Location>
@Query("SELECT places.*"
+ " FROM places"
+ " INNER JOIN geofences ON geofences.place = places.uid"
+ " WHERE geofences.task = :taskId")
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")
fun getActiveGeofences(): List<Location>
@Query("SELECT COUNT(*) FROM geofences")
suspend fun geofenceCount(): Int
@Delete
fun delete(location: Geofence)
@Delete
fun delete(place: Place)
@Insert
fun insert(location: Geofence): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(place: Place): Long
@Update
fun update(place: Place)
@Update
fun update(geofence: Geofence)
@Query("SELECT * FROM places WHERE uid = :uid LIMIT 1")
fun getByUid(uid: String): Place?
@Query("SELECT * FROM geofences WHERE task = :taskId")
fun getGeofencesForTask(taskId: Long): List<Geofence>
@Query("SELECT * FROM places")
fun getPlaces(): List<Place>
@Query("SELECT * FROM places WHERE place_id = :id")
fun getPlace(id: Long): Place?
@Query("SELECT * FROM places WHERE uid = :uid")
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")
fun getPlaceUsage(): LiveData<List<PlaceUsage>>
@Query("SELECT * FROM places WHERE latitude LIKE :latitude AND longitude LIKE :longitude")
fun findPlace(latitude: String, longitude: String): Place?
@Query("SELECT places.*, COUNT(tasks._id) AS count FROM places "
+ " 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"
+ " GROUP BY places.uid"
+ " ORDER BY name COLLATE NOCASE ASC")
fun getPlaceFilters(now: Long = currentTimeMillis()): List<LocationFilters>
@Query("UPDATE places SET place_order = $NO_ORDER")
fun resetOrders()
@Deprecated("use coroutines")
class LocationDaoBlocking @Inject constructor(private val dao: LocationDao) {
fun getPlacesWithGeofences(): List<Place> = runBlocking {
dao.getPlacesWithGeofences()
}
@Query("UPDATE places SET place_order = :order WHERE place_id = :id")
fun setOrder(id: Long, order: Int)
fun getGeofencesByPlace(uid: String): MergedGeofence? = runBlocking {
dao.getGeofencesByPlace(uid)
}
fun deleteGeofencesByPlace(place: String) = runBlocking {
dao.deleteGeofencesByPlace(place)
}
fun getArrivalGeofences(place: String, now: Long): List<Geofence> = runBlocking {
dao.getArrivalGeofences(place, now)
}
fun getDepartureGeofences(place: String, now: Long): List<Geofence> = runBlocking {
dao.getDepartureGeofences(place, now)
}
fun getGeofences(taskId: Long): Location? = runBlocking {
dao.getGeofences(taskId)
}
fun getActiveGeofences(taskId: Long): List<Location> = runBlocking {
dao.getActiveGeofences(taskId)
}
fun getPlaceForTask(taskId: Long): Place? = runBlocking {
dao.getPlaceForTask(taskId)
}
fun getActiveGeofences(): List<Location> = runBlocking {
dao.getActiveGeofences()
}
suspend fun geofenceCount(): Int {
return dao.geofenceCount()
}
fun delete(location: Geofence) = runBlocking {
dao.delete(location)
}
fun delete(place: Place) = runBlocking {
dao.delete(place)
}
fun insert(location: Geofence): Long = runBlocking {
dao.insert(location)
}
fun insert(place: Place): Long = runBlocking {
dao.insert(place)
}
fun update(place: Place) = runBlocking {
dao.update(place)
}
fun update(geofence: Geofence) = runBlocking {
dao.update(geofence)
}
fun getByUid(uid: String): Place? = runBlocking {
dao.getByUid(uid)
}
fun getGeofencesForTask(taskId: Long): List<Geofence> = runBlocking {
dao.getGeofencesForTask(taskId)
}
fun getPlaces(): List<Place> = runBlocking {
dao.getPlaces()
}
fun getPlace(id: Long): Place? = runBlocking {
dao.getPlace(id)
}
fun getPlace(uid: String): Place? = runBlocking {
dao.getPlace(uid)
}
fun getPlaceUsage(): LiveData<List<PlaceUsage>> {
return dao.getPlaceUsage()
}
fun findPlace(latitude: String, longitude: String): Place? = runBlocking {
dao.findPlace(latitude, longitude)
}
fun getPlaceFilters(now: Long = currentTimeMillis()): List<LocationFilters> = runBlocking {
dao.getPlaceFilters(now)
}
fun resetOrders() = runBlocking {
dao.resetOrders()
}
fun setOrder(id: Long, order: Int) = runBlocking {
dao.setOrder(id, order)
}
}

@ -1,49 +1,48 @@
package org.tasks.data
import androidx.room.*
import com.todoroo.astrid.data.Task
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
@Dao
abstract class TagDaoBlocking {
@Query("UPDATE tags SET name = :name WHERE tag_uid = :tagUid")
abstract fun rename(tagUid: String, name: String)
@Deprecated("use coroutines")
class TagDaoBlocking @Inject constructor(private val dao: TagDao) {
fun rename(tagUid: String, name: String) = runBlocking {
dao.rename(tagUid, name)
}
@Insert
abstract fun insert(tag: Tag)
fun insert(tag: Tag) = runBlocking {
dao.insert(tag)
}
@Insert
abstract fun insert(tags: Iterable<Tag>)
fun insert(tags: Iterable<Tag>) = runBlocking {
dao.insert(tags)
}
@Query("DELETE FROM tags WHERE task = :taskId AND tag_uid in (:tagUids)")
abstract fun deleteTags(taskId: Long, tagUids: List<String>)
fun deleteTags(taskId: Long, tagUids: List<String>) = runBlocking {
dao.deleteTags(taskId, tagUids)
}
@Query("SELECT * FROM tags WHERE tag_uid = :tagUid")
abstract fun getByTagUid(tagUid: String): List<Tag>
fun getByTagUid(tagUid: String): List<Tag> = runBlocking {
dao.getByTagUid(tagUid)
}
@Query("SELECT * FROM tags WHERE task = :taskId")
abstract fun getTagsForTask(taskId: Long): List<Tag>
fun getTagsForTask(taskId: Long): List<Tag> = runBlocking {
dao.getTagsForTask(taskId)
}
@Query("SELECT * FROM tags WHERE task = :taskId AND tag_uid = :tagUid")
abstract fun getTagByTaskAndTagUid(taskId: Long, tagUid: String): Tag?
fun getTagByTaskAndTagUid(taskId: Long, tagUid: String): Tag? = runBlocking {
dao.getTagByTaskAndTagUid(taskId, tagUid)
}
@Delete
abstract fun delete(tags: List<Tag>)
fun delete(tags: List<Tag>) = runBlocking {
dao.delete(tags)
}
@Transaction
open fun applyTags(task: Task, tagDataDao: TagDataDaoBlocking, current: List<TagData>): Boolean {
val taskId = task.id
val existing = HashSet(tagDataDao.getTagDataForTask(taskId))
val selected = HashSet<TagData>(current)
val added = selected subtract existing
val removed = existing subtract selected
deleteTags(taskId, removed.map { td -> td.remoteId!! })
insert(task, added)
return removed.isNotEmpty() || added.isNotEmpty()
fun applyTags(task: Task, tagDataDao: TagDataDaoBlocking, current: List<TagData>): Boolean = runBlocking {
dao.applyTags(task, tagDataDao, current)
}
fun insert(task: Task, tags: Collection<TagData>) {
if (!tags.isEmpty()) {
insert(tags.map { Tag(task, it) })
}
fun insert(task: Task, tags: Collection<TagData>) = runBlocking {
dao.insert(task, tags)
}
}

@ -2,157 +2,115 @@ package org.tasks.data
import androidx.core.util.Pair
import androidx.lifecycle.LiveData
import androidx.room.*
import com.todoroo.astrid.api.FilterListItem.NO_ORDER
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.helper.UUIDHelper
import org.tasks.db.DbUtils
import org.tasks.filters.AlphanumComparator
import kotlinx.coroutines.runBlocking
import org.tasks.filters.TagFilters
import org.tasks.time.DateTimeUtils.currentTimeMillis
import java.util.*
import kotlin.collections.HashSet
@Dao
abstract class TagDataDaoBlocking {
@Query("SELECT * FROM tagdata")
abstract fun subscribeToTags(): LiveData<List<TagData>>
@Query("SELECT * FROM tagdata WHERE name = :name COLLATE NOCASE LIMIT 1")
abstract fun getTagByName(name: String): TagData?
/**
* If a tag already exists in the database that case insensitively matches the given tag, return
* that. Otherwise, return the argument
*/
fun getTagWithCase(tag: String): String? {
return getTagByName(tag)?.name ?: tag
}
fun searchTags(query: String): List<TagData> {
return searchTagsInternal("%$query%")
.sortedWith(AlphanumComparator(TagData::name))
.toMutableList()
}
@Query("SELECT * FROM tagdata WHERE name LIKE :query AND name NOT NULL AND name != ''")
protected abstract fun searchTagsInternal(query: String): List<TagData>
@Query("SELECT * FROM tagdata")
abstract fun getAll(): List<TagData>
@Query("SELECT * FROM tagdata WHERE remoteId = :uuid LIMIT 1")
abstract fun getByUuid(uuid: String): TagData?
@Query("SELECT * FROM tagdata WHERE remoteId IN (:uuids)")
abstract fun getByUuid(uuids: Collection<String>): List<TagData>
@Query("SELECT * FROM tagdata WHERE name IS NOT NULL AND name != '' ORDER BY UPPER(name) ASC")
abstract fun tagDataOrderedByName(): List<TagData>
@Delete
abstract fun deleteTagData(tagData: TagData)
@Query("DELETE FROM tags WHERE tag_uid = :tagUid")
abstract fun deleteTags(tagUid: String)
@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>
fun getTagSelections(tasks: List<Long>): Pair<Set<String>, Set<String>> {
val allTags = getAllTags(tasks)
val tags = allTags.map { t: String? -> HashSet<String>(t?.split(",") ?: emptySet()) }
val partialTags = tags.flatten().toMutableSet()
var commonTags: MutableSet<String>? = null
if (tags.isEmpty()) {
commonTags = HashSet()
} else {
for (s in tags) {
if (commonTags == null) {
commonTags = s.toMutableSet()
} else {
commonTags.retainAll(s)
}
}
}
partialTags.removeAll(commonTags!!)
return Pair(partialTags, commonTags)
}
@Query("SELECT GROUP_CONCAT(DISTINCT(tag_uid)) FROM tasks"
+ " LEFT JOIN tags ON tags.task = tasks._id"
+ " WHERE tasks._id IN (:tasks)"
+ " GROUP BY tasks._id")
abstract fun getAllTags(tasks: List<Long>): List<String>
@Transaction
open fun applyTags(
tasks: List<Task>, partiallySelected: List<TagData>, selected: List<TagData>): List<Long> {
val modified = HashSet<Long>()
val keep = partiallySelected.plus(selected).map { it.remoteId!! }
for (sublist in tasks.chunked(DbUtils.MAX_SQLITE_ARGS - keep.size)) {
val tags = tagsToDelete(sublist.map(Task::id), keep)
deleteTags(tags)
modified.addAll(tags.map(Tag::task))
}
for (task in tasks) {
val added = selected subtract getTagDataForTask(task.id)
if (added.isNotEmpty()) {
modified.add(task.id)
insert(added.map { Tag(task, it) })
}
}
return ArrayList(modified)
}
@Transaction
open fun delete(tagData: TagData) {
deleteTags(tagData.remoteId!!)
deleteTagData(tagData)
}
@Delete
abstract fun delete(tagData: List<TagData>)
@Delete
abstract fun deleteTags(tags: List<Tag>)
@Query("SELECT tagdata.* FROM tagdata "
+ "INNER JOIN tags ON tags.tag_uid = tagdata.remoteId "
+ "WHERE tags.task = :id "
+ "ORDER BY UPPER(tagdata.name) ASC")
abstract fun getTagDataForTask(id: Long): List<TagData>
@Query("SELECT * FROM tagdata WHERE name IN (:names)")
abstract fun getTags(names: List<String>): List<TagData>
@Update
abstract fun update(tagData: TagData)
@Insert
abstract fun insert(tag: TagData): Long
@Insert
abstract fun insert(tags: Iterable<Tag>)
fun createNew(tag: TagData) {
if (Task.isUuidEmpty(tag.remoteId)) {
tag.remoteId = UUIDHelper.newUUID()
}
tag.id = insert(tag)
}
@Query("SELECT tagdata.*, COUNT(tasks._id) AS count"
+ " FROM tagdata"
+ " LEFT JOIN tags ON tags.tag_uid = tagdata.remoteId"
+ " 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 != ''"
+ " GROUP BY tagdata.remoteId")
abstract fun getTagFilters(now: Long = currentTimeMillis()): List<TagFilters>
@Query("UPDATE tagdata SET td_order = $NO_ORDER")
abstract fun resetOrders()
@Query("UPDATE tagdata SET td_order = :order WHERE _id = :id")
abstract fun setOrder(id: Long, order: Int)
import javax.inject.Inject
@Deprecated("use coroutines")
class TagDataDaoBlocking @Inject constructor(private val dao: TagDataDao) {
fun subscribeToTags(): LiveData<List<TagData>> {
return dao.subscribeToTags()
}
fun getTagByName(name: String): TagData? = runBlocking {
dao.getTagByName(name)
}
fun getTagWithCase(tag: String): String? = runBlocking {
dao.getTagWithCase(tag)
}
fun searchTags(query: String): List<TagData> = runBlocking {
dao.searchTags(query)
}
fun getAll(): List<TagData> = runBlocking {
dao.getAll()
}
fun getByUuid(uuid: String): TagData? = runBlocking {
dao.getByUuid(uuid)
}
fun getByUuid(uuids: Collection<String>): List<TagData> = runBlocking {
dao.getByUuid(uuids)
}
fun tagDataOrderedByName(): List<TagData> = runBlocking {
dao.tagDataOrderedByName()
}
fun deleteTagData(tagData: TagData) = runBlocking {
dao.deleteTagData(tagData)
}
fun deleteTags(tagUid: String) = runBlocking {
dao.deleteTags(tagUid)
}
fun tagsToDelete(tasks: List<Long>, tagsToKeep: List<String>): List<Tag> = runBlocking {
dao.tagsToDelete(tasks, tagsToKeep)
}
fun getTagSelections(tasks: List<Long>): Pair<Set<String>, Set<String>> = runBlocking {
dao.getTagSelections(tasks)
}
fun getAllTags(tasks: List<Long>): List<String> = runBlocking {
dao.getAllTags(tasks)
}
fun applyTags(tasks: List<Task>, partiallySelected: List<TagData>, selected: List<TagData>): List<Long> = runBlocking {
dao.applyTags(tasks, partiallySelected, selected)
}
fun delete(tagData: TagData) = runBlocking {
dao.delete(tagData)
}
fun delete(tagData: List<TagData>) = runBlocking {
dao.delete(tagData)
}
fun deleteTags(tags: List<Tag>) = runBlocking {
dao.deleteTags(tags)
}
fun getTagDataForTask(id: Long): List<TagData> = runBlocking {
dao.getTagDataForTask(id)
}
fun getTags(names: List<String>): List<TagData> = runBlocking {
dao.getTags(names)
}
fun update(tagData: TagData) = runBlocking {
dao.update(tagData)
}
fun insert(tag: TagData): Long = runBlocking {
dao.insert(tag)
}
fun insert(tags: Iterable<Tag>) = runBlocking {
dao.insert(tags)
}
fun createNew(tag: TagData) = runBlocking {
dao.createNew(tag)
}
fun getTagFilters(now: Long = currentTimeMillis()): List<TagFilters> = runBlocking {
dao.getTagFilters(now)
}
fun resetOrders() = runBlocking {
dao.resetOrders()
}
fun setOrder(id: Long, order: Int) = runBlocking {
dao.setOrder(id, order)
}
}

@ -1,33 +1,35 @@
package org.tasks.data
import androidx.room.*
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.helper.UUIDHelper
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
@Dao
abstract class TaskAttachmentDaoBlocking {
@Query("SELECT * FROM task_attachments WHERE task_id = :taskUuid")
abstract fun getAttachments(taskUuid: String): List<TaskAttachment>
@Deprecated("use coroutines")
class TaskAttachmentDaoBlocking @Inject constructor(private val dao: TaskAttachmentDao) {
fun getAttachments(taskUuid: String): List<TaskAttachment> = runBlocking {
dao.getAttachments(taskUuid)
}
@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>
fun getAttachments(task: Long): List<TaskAttachment> = runBlocking {
dao.getAttachments(task)
}
@Query("SELECT * FROM task_attachments")
abstract fun getAttachments(): List<TaskAttachment>
fun getAttachments(): List<TaskAttachment> = runBlocking {
dao.getAttachments()
}
@Delete
abstract fun delete(taskAttachment: TaskAttachment)
fun delete(taskAttachment: TaskAttachment) = runBlocking {
dao.delete(taskAttachment)
}
@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun insert(attachment: TaskAttachment)
fun insert(attachment: TaskAttachment) = runBlocking {
dao.insert(attachment)
}
@Update
abstract fun update(attachment: TaskAttachment)
fun update(attachment: TaskAttachment) = runBlocking {
dao.update(attachment)
}
fun createNew(attachment: TaskAttachment) {
if (Task.isUuidEmpty(attachment.remoteId)) {
attachment.remoteId = UUIDHelper.newUUID()
}
insert(attachment)
fun createNew(attachment: TaskAttachment) = runBlocking {
dao.createNew(attachment)
}
}

@ -1,25 +1,27 @@
package org.tasks.data
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
@Dao
abstract class TaskListMetadataDaoBlocking {
@Query("SELECT * from task_list_metadata where tag_uuid = :tagUuid OR filter = :tagUuid LIMIT 1")
abstract fun fetchByTagOrFilter(tagUuid: String): TaskListMetadata?
@Deprecated("use coroutines")
class TaskListMetadataDaoBlocking @Inject constructor(private val dao: TaskListMetadataDao) {
fun fetchByTagOrFilter(tagUuid: String): TaskListMetadata? = runBlocking {
dao.fetchByTagOrFilter(tagUuid)
}
@Query("SELECT * FROM task_list_metadata")
abstract fun getAll(): List<TaskListMetadata>
fun getAll(): List<TaskListMetadata> = runBlocking {
dao.getAll()
}
@Update
abstract fun update(taskListMetadata: TaskListMetadata)
fun update(taskListMetadata: TaskListMetadata) = runBlocking {
dao.update(taskListMetadata)
}
@Insert
abstract fun insert(taskListMetadata: TaskListMetadata): Long
fun insert(taskListMetadata: TaskListMetadata): Long = runBlocking {
dao.insert(taskListMetadata)
}
fun createNew(taskListMetadata: TaskListMetadata) {
taskListMetadata.id = insert(taskListMetadata)
fun createNew(taskListMetadata: TaskListMetadata) = runBlocking {
dao.createNew(taskListMetadata)
}
}

@ -1,37 +1,35 @@
package org.tasks.data
import androidx.room.*
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.helper.UUIDHelper
@Dao
abstract class UserActivityDaoBlocking {
@Insert
abstract fun insert(userActivity: UserActivity)
@Update
abstract fun update(userActivity: UserActivity)
@Delete
abstract fun delete(userActivity: UserActivity)
@Query("SELECT * FROM userActivity WHERE target_id = :taskUuid ORDER BY created_at DESC ")
abstract fun getCommentsForTask(taskUuid: String): List<UserActivity>
@Query("SELECT userActivity.* FROM userActivity INNER JOIN tasks ON tasks._id = :task WHERE target_id = tasks.remoteId")
abstract fun getComments(task: Long): List<UserActivity>
@Query("SELECT * FROM userActivity")
abstract fun getComments(): List<UserActivity>
fun createNew(item: UserActivity) {
if (item.created == null || item.created == 0L) {
item.created = DateUtilities.now()
}
if (Task.isUuidEmpty(item.remoteId)) {
item.remoteId = UUIDHelper.newUUID()
}
insert(item)
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
@Deprecated("use coroutines")
class UserActivityDaoBlocking @Inject constructor(private val dao: UserActivityDao) {
fun insert(userActivity: UserActivity) = runBlocking {
dao.insert(userActivity)
}
fun update(userActivity: UserActivity) = runBlocking {
dao.update(userActivity)
}
fun delete(userActivity: UserActivity) = runBlocking {
dao.delete(userActivity)
}
fun getCommentsForTask(taskUuid: String): List<UserActivity> = runBlocking {
dao.getCommentsForTask(taskUuid)
}
fun getComments(task: Long): List<UserActivity> = runBlocking {
dao.getComments(task)
}
fun getComments(): List<UserActivity> = runBlocking {
dao.getComments()
}
fun createNew(item: UserActivity) = runBlocking {
dao.createNew(item)
}
}

@ -2,7 +2,7 @@ package org.tasks.injection
import android.content.Context
import com.todoroo.astrid.dao.Database
import com.todoroo.astrid.dao.TaskDaoBlocking
import com.todoroo.astrid.dao.TaskDao
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -17,7 +17,7 @@ import org.tasks.jobs.WorkManager
import org.tasks.locale.Locale
import org.tasks.location.Geocoder
import org.tasks.location.MapboxGeocoder
import org.tasks.notifications.NotificationDaoBlocking
import org.tasks.notifications.NotificationDao
import javax.inject.Singleton
@Module
@ -33,55 +33,55 @@ class ApplicationModule {
@Provides
@Singleton
fun getNotificationDao(db: Database): NotificationDaoBlocking = db.notificationDao()
fun getNotificationDao(db: Database): NotificationDao = db.notificationDao()
@Provides
@Singleton
fun getTagDataDao(db: Database): TagDataDaoBlocking = db.tagDataDao
fun getTagDataDao(db: Database): TagDataDao = db.tagDataDao
@Provides
@Singleton
fun getUserActivityDao(db: Database): UserActivityDaoBlocking = db.userActivityDao
fun getUserActivityDao(db: Database): UserActivityDao = db.userActivityDao
@Provides
@Singleton
fun getTaskAttachmentDao(db: Database): TaskAttachmentDaoBlocking = db.taskAttachmentDao
fun getTaskAttachmentDao(db: Database): TaskAttachmentDao = db.taskAttachmentDao
@Provides
@Singleton
fun getTaskListMetadataDao(db: Database): TaskListMetadataDaoBlocking = db.taskListMetadataDao
fun getTaskListMetadataDao(db: Database): TaskListMetadataDao = db.taskListMetadataDao
@Provides
@Singleton
fun getGoogleTaskDao(db: Database): GoogleTaskDaoBlocking = db.googleTaskDao
fun getGoogleTaskDao(db: Database): GoogleTaskDao = db.googleTaskDao
@Provides
@Singleton
fun getAlarmDao(db: Database): AlarmDaoBlocking = db.alarmDao
fun getAlarmDao(db: Database): AlarmDao = db.alarmDao
@Provides
@Singleton
fun getGeofenceDao(db: Database): LocationDaoBlocking = db.locationDao
fun getGeofenceDao(db: Database): LocationDao = db.locationDao
@Provides
@Singleton
fun getTagDao(db: Database): TagDaoBlocking = db.tagDao
fun getTagDao(db: Database): TagDao = db.tagDao
@Provides
@Singleton
fun getFilterDao(db: Database): FilterDaoBlocking = db.filterDao
fun getFilterDao(db: Database): FilterDao = db.filterDao
@Provides
@Singleton
fun getGoogleTaskListDao(db: Database): GoogleTaskListDaoBlocking = db.googleTaskListDao
fun getGoogleTaskListDao(db: Database): GoogleTaskListDao = db.googleTaskListDao
@Provides
@Singleton
fun getCaldavDao(db: Database): CaldavDaoBlocking = db.caldavDao
fun getCaldavDao(db: Database): CaldavDao = db.caldavDao
@Provides
@Singleton
fun getTaskDao(db: Database, workManager: WorkManager): TaskDaoBlocking {
fun getTaskDao(db: Database, workManager: WorkManager): TaskDao {
val taskDao = db.taskDao
taskDao.initialize(workManager)
return taskDao
@ -89,11 +89,11 @@ class ApplicationModule {
@Provides
@Singleton
fun getDeletionDao(db: Database): DeletionDaoBlocking = db.deletionDao
fun getDeletionDao(db: Database): DeletionDao = db.deletionDao
@Provides
@Singleton
fun getContentProviderDao(db: Database): ContentProviderDaoBlocking = db.contentProviderDao
fun getContentProviderDao(db: Database): ContentProviderDao = db.contentProviderDao
@Provides
fun getBillingClient(@ApplicationContext context: Context, inventory: Inventory, firebase: Firebase): BillingClient

@ -1,27 +1,31 @@
package org.tasks.notifications
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
@Dao
interface NotificationDaoBlocking {
@Query("SELECT task FROM notification")
fun getAll(): List<Long>
@Deprecated("use coroutines")
class NotificationDaoBlocking @Inject constructor(private val dao: NotificationDao) {
fun getAll(): List<Long> = runBlocking {
dao.getAll()
}
@Query("SELECT * FROM notification ORDER BY timestamp DESC")
fun getAllOrdered(): List<Notification>
fun getAllOrdered(): List<Notification> = runBlocking {
dao.getAllOrdered()
}
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(notifications: List<Notification>)
fun insertAll(notifications: List<Notification>) = runBlocking {
dao.insertAll(notifications)
}
@Query("DELETE FROM notification WHERE task = :taskId")
fun delete(taskId: Long)
fun delete(taskId: Long) = runBlocking {
dao.delete(taskId)
}
@Query("DELETE FROM notification WHERE task IN(:taskIds)")
fun deleteAll(taskIds: List<Long>)
fun deleteAll(taskIds: List<Long>) = runBlocking {
dao.deleteAll(taskIds)
}
@Query("SELECT MAX(timestamp) FROM notification")
fun latestTimestamp(): Long
fun latestTimestamp(): Long = runBlocking {
dao.latestTimestamp()
}
}
Loading…
Cancel
Save