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

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

@ -6,275 +6,211 @@
package com.todoroo.astrid.dao package com.todoroo.astrid.dao
import androidx.paging.DataSource import androidx.paging.DataSource
import androidx.room.*
import androidx.sqlite.db.SimpleSQLiteQuery 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.api.Filter
import com.todoroo.astrid.dao.TaskDao.Companion.getQuery
import com.todoroo.astrid.data.Task 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 kotlinx.coroutines.runBlocking
import org.tasks.BuildConfig
import org.tasks.data.Place
import org.tasks.data.SubtaskInfo import org.tasks.data.SubtaskInfo
import org.tasks.data.TaskContainer import org.tasks.data.TaskContainer
import org.tasks.data.TaskListQuery
import org.tasks.db.DbUtils.chunkedMap
import org.tasks.db.DbUtils.eachChunk
import org.tasks.jobs.WorkManager
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.time.DateTimeUtils.currentTimeMillis import org.tasks.time.DateTimeUtils.currentTimeMillis
import timber.log.Timber import javax.inject.Inject
@Dao @Deprecated("use coroutines")
abstract class TaskDaoBlocking(private val database: Database) { class TaskDaoBlocking @Inject constructor(private val dao: TaskDao) {
private lateinit var workManager: WorkManager fun needsRefresh(): List<Task> = runBlocking {
dao.needsRefresh()
}
fun initialize(workManager: WorkManager) { fun needsRefresh(now: Long): List<Task> = runBlocking {
this.workManager = workManager dao.needsRefresh(now)
} }
fun needsRefresh(): List<Task> { fun fetchBlocking(id: Long) = runBlocking {
return needsRefresh(DateUtilities.now()) dao.fetchBlocking(id)
} }
@Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0 AND (hideUntil > :now OR dueDate > :now)") fun fetch(id: Long): Task? = runBlocking {
abstract fun needsRefresh(now: Long): List<Task> dao.fetch(id)
}
fun fetchBlocking(id: Long) = runBlocking { fun fetch(ids: List<Long>): List<Task> = runBlocking {
fetch(id) 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") fun getAll(): List<Task> = runBlocking {
abstract suspend fun fetch(id: Long): Task? 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)") fun clearAllCalendarEvents(): Int = runBlocking {
internal abstract fun fetchInternal(ids: List<Long>): List<Task> dao.clearAllCalendarEvents()
}
@Query("SELECT COUNT(1) FROM tasks WHERE timerStart > 0 AND deleted = 0") fun getCompletedCalendarEvents(): List<String> = runBlocking {
abstract fun activeTimers(): Int dao.getCompletedCalendarEvents()
}
@Query("SELECT tasks.* FROM tasks INNER JOIN notification ON tasks._id = notification.task") fun clearCompletedCalendarEvents(): Int = runBlocking {
abstract fun activeNotifications(): List<Task> dao.clearCompletedCalendarEvents()
}
@Query("SELECT * FROM tasks WHERE remoteId = :remoteId") fun fetchTasks(callback: (SubtaskInfo) -> List<String>): List<TaskContainer> = runBlocking {
abstract fun fetch(remoteId: String): Task? dao.fetchTasks(callback)
}
@Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0") fun fetchTasks(callback: (SubtaskInfo) -> List<String>, subtasks: SubtaskInfo): List<TaskContainer> = runBlocking {
abstract fun getActiveTasks(): List<Task> dao.fetchTasks(callback, subtasks)
}
@Query("SELECT * FROM tasks WHERE hideUntil < (strftime('%s','now')*1000)") fun fetchTasks(preferences: Preferences, filter: Filter): List<TaskContainer> = runBlocking {
abstract fun getVisibleTasks(): List<Task> dao.fetchTasks(preferences, filter)
}
@Query("SELECT * FROM tasks WHERE remoteId IN (:remoteIds) " fun fetchTasks(query: SimpleSQLiteQuery): List<TaskContainer> = runBlocking {
+ "AND recurrence IS NOT NULL AND LENGTH(recurrence) > 0") dao.fetchTasks(query)
abstract fun getRecurringTasks(remoteIds: List<String>): List<Task> }
@Query("UPDATE tasks SET completed = :completionDate " + "WHERE remoteId = :remoteId") fun count(query: SimpleSQLiteQuery): Int = runBlocking {
abstract fun setCompletionDate(remoteId: String, completionDate: Long) dao.count(query)
}
@Query("UPDATE tasks SET snoozeTime = :millis WHERE _id in (:taskIds)") fun getSubtaskInfo(): SubtaskInfo = runBlocking {
abstract fun snooze(taskIds: List<Long>, millis: Long) dao.getSubtaskInfo()
}
@Query("SELECT tasks.* FROM tasks " fun getTaskFactory(query: SimpleSQLiteQuery): DataSource.Factory<Int, TaskContainer> {
+ "LEFT JOIN google_tasks ON tasks._id = google_tasks.gt_task " return dao.getTaskFactory(query)
+ "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>
@Query(""" fun touch(id: Long) = runBlocking {
SELECT tasks.* dao.touch(id)
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>
@Query("SELECT * FROM TASKS " fun touch(ids: List<Long>) = runBlocking {
+ "WHERE completed = 0 AND deleted = 0 AND (notificationFlags > 0 OR notifications > 0)") dao.touch(ids)
abstract fun getTasksWithReminders(): List<Task> }
// --- SQL clause generators fun touchInternal(ids: List<Long>, now: Long = currentTimeMillis()) = runBlocking {
@Query("SELECT * FROM tasks") dao.touchInternal(ids, now)
abstract fun getAll(): List<Task> }
@Query("SELECT calendarUri FROM tasks " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''") fun setParent(parent: Long, tasks: List<Long>) = runBlocking {
abstract fun getAllCalendarEvents(): List<String> dao.setParent(parent, tasks)
}
@Query("UPDATE tasks SET calendarUri = '' " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''") internal fun setParentInternal(parent: Long, children: List<Long>) = runBlocking {
abstract fun clearAllCalendarEvents(): Int dao.setParentInternal(parent, children)
}
@Query("SELECT calendarUri FROM tasks " fun fetchChildren(id: Long): List<Task> = runBlocking {
+ "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''") dao.fetchChildren(id)
abstract fun getCompletedCalendarEvents(): List<String> }
@Query("UPDATE tasks SET calendarUri = '' " fun getChildren(id: Long): List<Long> = runBlocking {
+ "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''") dao.getChildren(id)
abstract fun clearCompletedCalendarEvents(): Int }
@Transaction fun getChildren(ids: List<Long>): List<Long> = runBlocking {
open fun fetchTasks(callback: (SubtaskInfo) -> List<String>): List<TaskContainer> { dao.getChildren(ids)
return runBlocking {
fetchTasks(callback, getSubtaskInfo())
}
} }
@Transaction fun setCollapsed(id: Long, collapsed: Boolean) = runBlocking {
open fun fetchTasks(callback: (SubtaskInfo) -> List<String>, subtasks: SubtaskInfo): List<TaskContainer> { dao.setCollapsed(id, collapsed)
assertNotMainThread() }
val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0 fun setCollapsed(preferences: Preferences, filter: Filter, collapsed: Boolean) = runBlocking {
val queries = callback.invoke(subtasks) dao.setCollapsed(preferences, filter, collapsed)
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 fetchTasks(preferences: Preferences, filter: Filter): List<TaskContainer> { fun collapse(ids: List<Long>, collapsed: Boolean) = runBlocking {
return fetchTasks { dao.collapse(ids, collapsed)
TaskListQuery.getQuery(preferences, filter, it)
}
} }
@RawQuery fun save(task: Task) = runBlocking {
abstract fun fetchTasks(query: SimpleSQLiteQuery): List<TaskContainer> dao.save(task)
}
@RawQuery
abstract fun count(query: SimpleSQLiteQuery): Int fun save(task: Task, original: Task? = fetchBlocking(task.id)) = runBlocking {
dao.save(task, original)
@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>
@Query("UPDATE tasks SET collapsed = :collapsed WHERE _id = :id") fun insert(task: Task): Long = runBlocking {
abstract fun setCollapsed(id: Long, collapsed: Boolean) dao.insert(task)
@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)
}
} }
@Insert fun update(task: Task): Int = runBlocking {
abstract fun insert(task: Task): Long dao.update(task)
}
@Update fun createNew(task: Task) = runBlocking {
abstract fun update(task: Task): Int dao.createNew(task)
}
fun createNew(task: Task) { fun count(filter: Filter): Int = runBlocking {
task.id = NO_ID dao.count(filter)
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 fetchFiltered(filter: Filter): List<Task> { fun fetchFiltered(filter: Filter): List<Task> = runBlocking {
return fetchFiltered(filter.getSqlQuery()) dao.fetchFiltered(filter)
} }
fun fetchFiltered(queryTemplate: String): List<Task> { fun fetchFiltered(queryTemplate: String): List<Task> = runBlocking {
val query = getQuery(queryTemplate, Task.FIELDS) dao.fetchFiltered(queryTemplate)
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") fun getLocalTasks(): List<Long> = runBlocking {
abstract fun getLocalTasks(): List<Long> dao.getLocalTasks()
}
} }

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

@ -2,274 +2,232 @@ package org.tasks.data
import android.content.Context import android.content.Context
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.room.*
import com.todoroo.andlib.utility.DateUtilities.now 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.data.Task
import com.todoroo.astrid.helper.UUIDHelper import kotlinx.coroutines.runBlocking
import org.tasks.R
import org.tasks.data.CaldavDao.Companion.LOCAL
import org.tasks.date.DateTimeUtils.toAppleEpoch
import org.tasks.db.DbUtils.chunkedMap
import org.tasks.filters.CaldavFilters import org.tasks.filters.CaldavFilters
import org.tasks.time.DateTimeUtils.currentTimeMillis import org.tasks.time.DateTimeUtils.currentTimeMillis
import javax.inject.Inject
@Dao @Deprecated("use coroutines")
abstract class CaldavDaoBlocking { class CaldavDaoBlocking @Inject constructor(private val dao: CaldavDao) {
@Query("SELECT * FROM caldav_lists") fun subscribeToCalendars(): LiveData<List<CaldavCalendar>> {
abstract 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") internal fun update(id: Long, position: Long?, parent: String?) = runBlocking {
abstract fun getCalendarByUuid(uuid: String): CaldavCalendar? dao.update(id, position, parent)
}
@Query("SELECT * FROM caldav_lists WHERE cdl_account = :uuid") internal fun update(id: Long, position: Long?) = runBlocking {
abstract fun getCalendarsByAccount(uuid: String): List<CaldavCalendar> dao.update(id, position)
}
@Query("SELECT * FROM caldav_accounts WHERE cda_uuid = :uuid LIMIT 1") internal fun update(id: Long, remoteParent: String?) = runBlocking {
abstract fun getAccountByUuid(uuid: String): CaldavAccount? dao.update(id, remoteParent)
}
@Query("SELECT COUNT(*) FROM caldav_accounts WHERE cda_account_type != 2") fun update(tasks: Iterable<CaldavTask>) = runBlocking {
abstract fun accountCount(): Int dao.update(tasks)
}
@Query("SELECT * FROM caldav_accounts ORDER BY cda_account_type, UPPER(cda_name)") fun delete(caldavTask: CaldavTask) = runBlocking {
abstract fun getAccounts(): List<CaldavAccount> dao.delete(caldavTask)
}
@Query("UPDATE caldav_accounts SET cda_collapsed = :collapsed WHERE cda_id = :id") fun getDeleted(calendar: String): List<CaldavTask> = runBlocking {
abstract fun setCollapsed(id: Long, collapsed: Boolean) dao.getDeleted(calendar)
}
@Insert fun markDeleted(tasks: List<Long>, now: Long = currentTimeMillis()) = runBlocking {
abstract fun insert(caldavAccount: CaldavAccount): Long dao.markDeleted(tasks, now)
}
@Update fun getTask(taskId: Long): CaldavTask? = runBlocking {
abstract fun update(caldavAccount: CaldavAccount) dao.getTask(taskId)
}
fun insert(caldavCalendar: CaldavCalendar) { fun getRemoteIdForTask(taskId: Long): String? = runBlocking {
caldavCalendar.id = insertInternal(caldavCalendar) dao.getRemoteIdForTask(taskId)
} }
@Insert fun getTask(calendar: String, obj: String): CaldavTask? = runBlocking {
abstract fun insertInternal(caldavCalendar: CaldavCalendar): Long dao.getTask(calendar, obj)
}
@Update fun getTaskByRemoteId(calendar: String, remoteId: String): CaldavTask? = runBlocking {
abstract fun update(caldavCalendar: CaldavCalendar) dao.getTaskByRemoteId(calendar, remoteId)
}
@Transaction fun getTasks(taskId: Long): List<CaldavTask> = runBlocking {
open fun insert(task: Task, caldavTask: CaldavTask, addToTop: Boolean): Long { dao.getTasks(taskId)
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)
} }
@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") fun getTasks(taskIds: List<Long>): List<CaldavTask> = runBlocking {
internal abstract fun findFirstTask(calendar: String, parent: Long): Long? 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") fun getTasks(): List<CaldavTaskContainer> = runBlocking {
internal abstract fun findLastTask(calendar: String, parent: Long): Long? dao.getTasks()
}
@Insert fun getCaldavTasksToPush(calendar: String): List<CaldavTaskContainer> = runBlocking {
abstract fun insert(caldavTask: CaldavTask): Long dao.getCaldavTasksToPush(calendar)
}
@Insert fun getCalendars(): List<CaldavCalendar> = runBlocking {
abstract fun insert(tasks: Iterable<CaldavTask>) dao.getCalendars()
}
@Update fun getCalendar(uuid: String): CaldavCalendar? = runBlocking {
abstract fun update(caldavTask: CaldavTask) dao.getCalendar(uuid)
}
fun update(caldavTask: SubsetCaldav) { fun getObjects(calendar: String): List<String> = runBlocking {
update(caldavTask.cd_id, caldavTask.cd_order, caldavTask.cd_remote_parent) dao.getObjects(calendar)
} }
@Query("UPDATE caldav_tasks SET cd_order = :position, cd_remote_parent = :parent WHERE cd_id = :id") fun getTasks(calendar: String, objects: List<String>): List<Long> = runBlocking {
internal abstract fun update(id: Long, position: Long?, parent: String?) dao.getTasks(calendar, objects)
}
@Query("UPDATE caldav_tasks SET cd_order = :position WHERE cd_id = :id") fun getTasksInternal(calendar: String, objects: List<String>): List<Long> = runBlocking {
internal abstract fun update(id: Long, position: Long?) dao.getTasksInternal(calendar, objects)
}
@Query("UPDATE caldav_tasks SET cd_remote_parent = :remoteParent WHERE cd_id = :id") fun findDeletedCalendars(account: String, urls: List<String>): List<CaldavCalendar> = runBlocking {
internal abstract fun update(id: Long, remoteParent: String?) dao.findDeletedCalendars(account, urls)
}
@Update fun getCalendarByUrl(account: String, url: String): CaldavCalendar? = runBlocking {
abstract fun update(tasks: Iterable<CaldavTask>) dao.getCalendarByUrl(account, url)
}
@Delete fun getAccountForTask(task: Long): CaldavAccount? = runBlocking {
abstract fun delete(caldavTask: CaldavTask) 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") fun updateParents(calendar: String) = runBlocking {
abstract fun getDeleted(calendar: String): List<CaldavTask> dao.updateParents(calendar)
}
@Query("UPDATE caldav_tasks SET cd_deleted = :now WHERE cd_task IN (:tasks)") fun move(task: TaskContainer, newParent: Long, newPosition: Long?) = runBlocking {
abstract fun markDeleted(tasks: List<Long>, now: Long = currentTimeMillis()) dao.move(task, newParent, newPosition)
}
@Query("SELECT * FROM caldav_tasks WHERE cd_task = :taskId AND cd_deleted = 0 LIMIT 1") fun shiftDown(calendar: String, parent: Long, from: Long, to: Long? = null) = runBlocking {
abstract fun getTask(taskId: Long): CaldavTask? dao.shiftDown(calendar, parent, from, to)
}
@Query("SELECT cd_remote_id FROM caldav_tasks WHERE cd_task = :taskId AND cd_deleted = 0") internal fun touchInternal(ids: List<Long>, modificationTime: Long = now()) = runBlocking {
abstract fun getRemoteIdForTask(taskId: Long): String? dao.touchInternal(ids, modificationTime)
}
@Query("SELECT * FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_object = :obj LIMIT 1") internal fun getTasksToShift(calendar: String, parent: Long, from: Long, to: Long?): List<CaldavTaskContainer> = runBlocking {
abstract fun getTask(calendar: String, obj: String): CaldavTask? dao.getTasksToShift(calendar, parent, from, to)
}
@Query("SELECT * FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_remote_id = :remoteId") fun resetOrders() = runBlocking {
abstract fun getTaskByRemoteId(calendar: String, remoteId: String): CaldavTask? dao.resetOrders()
}
@Query("SELECT * FROM caldav_tasks WHERE cd_task = :taskId") fun setOrder(id: Long, order: Int) = runBlocking {
abstract fun getTasks(taskId: Long): List<CaldavTask> dao.setOrder(id, order)
}
@Query("SELECT * FROM caldav_tasks WHERE cd_task in (:taskIds) AND cd_deleted = 0") fun setupLocalAccount(context: Context): CaldavAccount = runBlocking {
abstract fun getTasks(taskIds: List<Long>): List<CaldavTask> dao.setupLocalAccount(context)
}
@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?
@Query("SELECT DISTINCT cd_calendar FROM caldav_tasks WHERE cd_deleted = 0 AND cd_task IN (:tasks)") fun getLocalList(context: Context) = runBlocking {
abstract fun getCalendars(tasks: List<Long>): List<String> dao.getLocalList(context)
}
@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)
}
} }

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

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

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

@ -1,179 +1,137 @@
package org.tasks.data package org.tasks.data
import androidx.room.*
import com.todoroo.astrid.data.Task import com.todoroo.astrid.data.Task
import kotlinx.coroutines.runBlocking
import org.tasks.time.DateTimeUtils.currentTimeMillis import org.tasks.time.DateTimeUtils.currentTimeMillis
import javax.inject.Inject
@Dao @Deprecated("use coroutines")
abstract class GoogleTaskDaoBlocking { class GoogleTaskDaoBlocking @Inject constructor(private val dao: GoogleTaskDao) {
@Insert fun insert(task: GoogleTask): Long = runBlocking {
abstract fun insert(task: GoogleTask): Long 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 fun getByRemoteId(remoteId: String): GoogleTask? = runBlocking {
abstract fun insert(tasks: Iterable<GoogleTask>) 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 fun getChildren(ids: List<Long>): List<Long> = runBlocking {
open fun insertAndShift(task: GoogleTask, top: Boolean) { dao.getChildren(ids)
if (top) {
task.order = 0
shiftDown(task.listId!!, task.parent, 0)
} else {
task.order = getBottom(task.listId!!, task.parent)
}
task.id = insert(task)
} }
@Query("UPDATE google_tasks SET gt_order = gt_order + 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order >= :position") fun getChildTasks(taskId: Long): List<Task> = runBlocking {
abstract fun shiftDown(listId: String, parent: Long, position: Long) 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") fun getBottom(listId: String, parent: Long): Long = runBlocking {
abstract fun shiftUp(listId: String, parent: Long, from: Long, to: Long) 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") fun getPrevious(listId: String, parent: Long, order: Long): String? = runBlocking {
abstract fun shiftDown(listId: String, parent: Long, from: Long, to: Long) 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") fun getTask(remoteId: String): Long = runBlocking {
abstract fun shiftUp(listId: String, parent: Long, position: Long) 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 fun validateSorting(listId: String) = runBlocking {
open fun move(task: SubsetGoogleTask, newParent: Long, newPosition: Long) { dao.validateSorting(listId)
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++
}
}
} }
} }

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

@ -1,118 +1,114 @@
package org.tasks.data package org.tasks.data
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.room.* import kotlinx.coroutines.runBlocking
import com.todoroo.astrid.api.FilterListItem.NO_ORDER
import org.tasks.filters.LocationFilters import org.tasks.filters.LocationFilters
import org.tasks.time.DateTimeUtils.currentTimeMillis import org.tasks.time.DateTimeUtils.currentTimeMillis
import javax.inject.Inject
@Dao @Deprecated("use coroutines")
interface LocationDaoBlocking { class LocationDaoBlocking @Inject constructor(private val dao: LocationDao) {
@Query("SELECT places.*" fun getPlacesWithGeofences(): List<Place> = runBlocking {
+ " FROM places" dao.getPlacesWithGeofences()
+ " 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()
@Query("UPDATE places SET place_order = :order WHERE place_id = :id") fun getGeofencesByPlace(uid: String): MergedGeofence? = runBlocking {
fun setOrder(id: Long, order: Int) 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 package org.tasks.data
import androidx.room.*
import com.todoroo.astrid.data.Task import com.todoroo.astrid.data.Task
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
@Dao @Deprecated("use coroutines")
abstract class TagDaoBlocking { class TagDaoBlocking @Inject constructor(private val dao: TagDao) {
@Query("UPDATE tags SET name = :name WHERE tag_uid = :tagUid") fun rename(tagUid: String, name: String) = runBlocking {
abstract fun rename(tagUid: String, name: String) dao.rename(tagUid, name)
}
@Insert fun insert(tag: Tag) = runBlocking {
abstract fun insert(tag: Tag) dao.insert(tag)
}
@Insert fun insert(tags: Iterable<Tag>) = runBlocking {
abstract fun insert(tags: Iterable<Tag>) dao.insert(tags)
}
@Query("DELETE FROM tags WHERE task = :taskId AND tag_uid in (:tagUids)") fun deleteTags(taskId: Long, tagUids: List<String>) = runBlocking {
abstract fun deleteTags(taskId: Long, tagUids: List<String>) dao.deleteTags(taskId, tagUids)
}
@Query("SELECT * FROM tags WHERE tag_uid = :tagUid") fun getByTagUid(tagUid: String): List<Tag> = runBlocking {
abstract fun getByTagUid(tagUid: String): List<Tag> dao.getByTagUid(tagUid)
}
@Query("SELECT * FROM tags WHERE task = :taskId") fun getTagsForTask(taskId: Long): List<Tag> = runBlocking {
abstract fun getTagsForTask(taskId: Long): List<Tag> dao.getTagsForTask(taskId)
}
@Query("SELECT * FROM tags WHERE task = :taskId AND tag_uid = :tagUid") fun getTagByTaskAndTagUid(taskId: Long, tagUid: String): Tag? = runBlocking {
abstract fun getTagByTaskAndTagUid(taskId: Long, tagUid: String): Tag? dao.getTagByTaskAndTagUid(taskId, tagUid)
}
@Delete fun delete(tags: List<Tag>) = runBlocking {
abstract fun delete(tags: List<Tag>) dao.delete(tags)
}
@Transaction fun applyTags(task: Task, tagDataDao: TagDataDaoBlocking, current: List<TagData>): Boolean = runBlocking {
open fun applyTags(task: Task, tagDataDao: TagDataDaoBlocking, current: List<TagData>): Boolean { dao.applyTags(task, tagDataDao, current)
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 insert(task: Task, tags: Collection<TagData>) { fun insert(task: Task, tags: Collection<TagData>) = runBlocking {
if (!tags.isEmpty()) { dao.insert(task, tags)
insert(tags.map { Tag(task, it) })
}
} }
} }

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

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

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

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

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

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