Blocking daos delegate to suspending daos

pull/1043/head
Alex Baker 6 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) {
this.workManager = workManager
} }
fun needsRefresh(): List<Task> { fun needsRefresh(now: Long): List<Task> = runBlocking {
return needsRefresh(DateUtilities.now()) dao.needsRefresh(now)
} }
@Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0 AND (hideUntil > :now OR dueDate > :now)")
abstract fun needsRefresh(now: Long): List<Task>
fun fetchBlocking(id: Long) = runBlocking { fun fetchBlocking(id: Long) = runBlocking {
fetch(id) dao.fetchBlocking(id)
} }
@Query("SELECT * FROM tasks WHERE _id = :id LIMIT 1") fun fetch(id: Long): Task? = runBlocking {
abstract suspend fun fetch(id: Long): Task? dao.fetch(id)
}
fun fetch(ids: List<Long>): List<Task> = ids.chunkedMap(this::fetchInternal)
@Query("SELECT * FROM tasks WHERE _id IN (:ids)")
internal abstract fun fetchInternal(ids: List<Long>): List<Task>
@Query("SELECT COUNT(1) FROM tasks WHERE timerStart > 0 AND deleted = 0")
abstract fun activeTimers(): Int
@Query("SELECT tasks.* FROM tasks INNER JOIN notification ON tasks._id = notification.task")
abstract fun activeNotifications(): List<Task>
@Query("SELECT * FROM tasks WHERE remoteId = :remoteId")
abstract fun fetch(remoteId: String): Task?
@Query("SELECT * FROM tasks WHERE completed = 0 AND deleted = 0")
abstract fun getActiveTasks(): List<Task>
@Query("SELECT * FROM tasks WHERE hideUntil < (strftime('%s','now')*1000)")
abstract fun getVisibleTasks(): List<Task>
@Query("SELECT * FROM tasks WHERE remoteId IN (:remoteIds) " fun fetch(ids: List<Long>): List<Task> = runBlocking {
+ "AND recurrence IS NOT NULL AND LENGTH(recurrence) > 0") dao.fetch(ids)
abstract fun getRecurringTasks(remoteIds: List<String>): List<Task> }
@Query("UPDATE tasks SET completed = :completionDate " + "WHERE remoteId = :remoteId") internal fun fetchInternal(ids: List<Long>): List<Task> = runBlocking {
abstract fun setCompletionDate(remoteId: String, completionDate: Long) dao.fetchInternal(ids)
}
@Query("UPDATE tasks SET snoozeTime = :millis WHERE _id in (:taskIds)") fun activeTimers(): Int = runBlocking {
abstract fun snooze(taskIds: List<Long>, millis: Long) dao.activeTimers()
}
@Query("SELECT tasks.* FROM tasks " fun activeNotifications(): List<Task> = runBlocking {
+ "LEFT JOIN google_tasks ON tasks._id = google_tasks.gt_task " dao.activeNotifications()
+ "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 fetch(remoteId: String): Task? = runBlocking {
SELECT tasks.* dao.fetch(remoteId)
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 getActiveTasks(): List<Task> = runBlocking {
+ "WHERE completed = 0 AND deleted = 0 AND (notificationFlags > 0 OR notifications > 0)") dao.getActiveTasks()
abstract fun getTasksWithReminders(): List<Task> }
// --- SQL clause generators fun getVisibleTasks(): List<Task> = runBlocking {
@Query("SELECT * FROM tasks") dao.getVisibleTasks()
abstract fun getAll(): List<Task> }
@Query("SELECT calendarUri FROM tasks " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''") fun getRecurringTasks(remoteIds: List<String>): List<Task> = runBlocking {
abstract fun getAllCalendarEvents(): List<String> dao.getRecurringTasks(remoteIds)
}
@Query("UPDATE tasks SET calendarUri = '' " + "WHERE calendarUri IS NOT NULL AND calendarUri != ''") fun setCompletionDate(remoteId: String, completionDate: Long) = runBlocking {
abstract fun clearAllCalendarEvents(): Int dao.setCompletionDate(remoteId, completionDate)
}
@Query("SELECT calendarUri FROM tasks " fun snooze(taskIds: List<Long>, millis: Long) = runBlocking {
+ "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''") dao.snooze(taskIds, millis)
abstract fun getCompletedCalendarEvents(): List<String> }
@Query("UPDATE tasks SET calendarUri = '' " fun getGoogleTasksToPush(account: String): List<Task> = runBlocking {
+ "WHERE completed > 0 AND calendarUri IS NOT NULL AND calendarUri != ''") dao.getGoogleTasksToPush(account)
abstract fun clearCompletedCalendarEvents(): Int }
@Transaction fun getCaldavTasksToPush(calendar: String): List<Task> = runBlocking {
open fun fetchTasks(callback: (SubtaskInfo) -> List<String>): List<TaskContainer> { dao.getCaldavTasksToPush(calendar)
return runBlocking {
fetchTasks(callback, getSubtaskInfo())
} }
fun getTasksWithReminders(): List<Task> = runBlocking {
dao.getTasksWithReminders()
} }
@Transaction fun getAll(): List<Task> = runBlocking {
open fun fetchTasks(callback: (SubtaskInfo) -> List<String>, subtasks: SubtaskInfo): List<TaskContainer> { dao.getAll()
assertNotMainThread() }
val start = if (BuildConfig.DEBUG) DateUtilities.now() else 0 fun getAllCalendarEvents(): List<String> = runBlocking {
val queries = callback.invoke(subtasks) dao.getAllCalendarEvents()
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")) fun clearAllCalendarEvents(): Int = runBlocking {
return result dao.clearAllCalendarEvents()
} }
fun fetchTasks(preferences: Preferences, filter: Filter): List<TaskContainer> { fun getCompletedCalendarEvents(): List<String> = runBlocking {
return fetchTasks { dao.getCompletedCalendarEvents()
TaskListQuery.getQuery(preferences, filter, it)
} }
fun clearCompletedCalendarEvents(): Int = runBlocking {
dao.clearCompletedCalendarEvents()
} }
@RawQuery fun fetchTasks(callback: (SubtaskInfo) -> List<String>): List<TaskContainer> = runBlocking {
abstract fun fetchTasks(query: SimpleSQLiteQuery): List<TaskContainer> dao.fetchTasks(callback)
}
@RawQuery fun fetchTasks(callback: (SubtaskInfo) -> List<String>, subtasks: SubtaskInfo): List<TaskContainer> = runBlocking {
abstract fun count(query: SimpleSQLiteQuery): Int dao.fetchTasks(callback, subtasks)
}
@Query(""" fun fetchTasks(preferences: Preferences, filter: Filter): List<TaskContainer> = runBlocking {
SELECT EXISTS(SELECT 1 FROM tasks WHERE parent > 0 AND deleted = 0) AS hasSubtasks, dao.fetchTasks(preferences, filter)
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]) fun fetchTasks(query: SimpleSQLiteQuery): List<TaskContainer> = runBlocking {
abstract fun getTaskFactory( dao.fetchTasks(query)
query: SimpleSQLiteQuery): DataSource.Factory<Int, TaskContainer> }
fun touch(id: Long) = touch(listOf(id)) fun count(query: SimpleSQLiteQuery): Int = runBlocking {
dao.count(query)
}
fun touch(ids: List<Long>) { fun getSubtaskInfo(): SubtaskInfo = runBlocking {
ids.eachChunk { touchInternal(it) } dao.getSubtaskInfo()
workManager.sync(false)
} }
@Query("UPDATE tasks SET modified = :now WHERE _id in (:ids)") fun getTaskFactory(query: SimpleSQLiteQuery): DataSource.Factory<Int, TaskContainer> {
abstract fun touchInternal(ids: List<Long>, now: Long = currentTimeMillis()) return dao.getTaskFactory(query)
}
fun setParent(parent: Long, tasks: List<Long>) = fun touch(id: Long) = runBlocking {
tasks.eachChunk { setParentInternal(parent, it) } dao.touch(id)
}
@Query("UPDATE tasks SET parent = :parent WHERE _id IN (:children)") fun touch(ids: List<Long>) = runBlocking {
internal abstract fun setParentInternal(parent: Long, children: List<Long>) dao.touch(ids)
}
@Transaction fun touchInternal(ids: List<Long>, now: Long = currentTimeMillis()) = runBlocking {
open fun fetchChildren(id: Long): List<Task> { dao.touchInternal(ids, now)
return fetch(getChildren(id))
} }
fun getChildren(id: Long): List<Long> { fun setParent(parent: Long, tasks: List<Long>) = runBlocking {
return getChildren(listOf(id)) dao.setParent(parent, tasks)
} }
@Query("WITH RECURSIVE " internal fun setParentInternal(parent: Long, children: List<Long>) = runBlocking {
+ " recursive_tasks (task) AS ( " dao.setParentInternal(parent, children)
+ " 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 fetchChildren(id: Long): List<Task> = runBlocking {
abstract fun setCollapsed(id: Long, collapsed: Boolean) dao.fetchChildren(id)
}
@Transaction fun getChildren(id: Long): List<Long> = runBlocking {
open fun setCollapsed(preferences: Preferences, filter: Filter, collapsed: Boolean) { dao.getChildren(id)
fetchTasks(preferences, filter)
.filter(TaskContainer::hasChildren)
.map(TaskContainer::getId)
.eachChunk { collapse(it, collapsed) }
} }
@Query("UPDATE tasks SET collapsed = :collapsed WHERE _id IN (:ids)") fun getChildren(ids: List<Long>): List<Long> = runBlocking {
abstract fun collapse(ids: List<Long>, collapsed: Boolean) dao.getChildren(ids)
}
// --- save fun setCollapsed(id: Long, collapsed: Boolean) = runBlocking {
// TODO: get rid of this super-hack dao.setCollapsed(id, collapsed)
/**
* 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 setCollapsed(preferences: Preferences, filter: Filter, collapsed: Boolean) = runBlocking {
dao.setCollapsed(preferences, filter, collapsed)
} }
fun collapse(ids: List<Long>, collapsed: Boolean) = runBlocking {
dao.collapse(ids, collapsed)
} }
@Insert fun save(task: Task) = runBlocking {
abstract fun insert(task: Task): Long dao.save(task)
}
@Update fun save(task: Task, original: Task? = fetchBlocking(task.id)) = runBlocking {
abstract fun update(task: Task): Int dao.save(task, original)
}
fun createNew(task: Task) { fun insert(task: Task): Long = runBlocking {
task.id = NO_ID dao.insert(task)
if (task.creationDate == 0L) {
task.creationDate = DateUtilities.now()
} }
if (Task.isUuidEmpty(task.remoteId)) {
task.remoteId = UUIDHelper.newUUID() fun update(task: Task): Int = runBlocking {
dao.update(task)
} }
val insert = insert(task)
task.id = insert fun createNew(task: Task) = runBlocking {
dao.createNew(task)
} }
fun count(filter: Filter): Int { fun count(filter: Filter): Int = runBlocking {
val query = getQuery(filter.sqlQuery, Field.COUNT) dao.count(filter)
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)
}
@Query("SELECT * FROM caldav_lists WHERE cdl_uuid = :uuid LIMIT 1") fun accountCount(): Int = runBlocking {
abstract fun getCalendarByUuid(uuid: String): CaldavCalendar? dao.accountCount()
}
fun getAccounts(): List<CaldavAccount> = runBlocking {
dao.getAccounts()
}
@Query("SELECT * FROM caldav_lists WHERE cdl_account = :uuid") fun setCollapsed(id: Long, collapsed: Boolean) = runBlocking {
abstract fun getCalendarsByAccount(uuid: String): List<CaldavCalendar> dao.setCollapsed(id, collapsed)
}
@Query("SELECT * FROM caldav_accounts WHERE cda_uuid = :uuid LIMIT 1") fun insert(caldavAccount: CaldavAccount): Long = runBlocking {
abstract fun getAccountByUuid(uuid: String): CaldavAccount? dao.insert(caldavAccount)
}
fun update(caldavAccount: CaldavAccount) = runBlocking {
dao.update(caldavAccount)
}
@Query("SELECT COUNT(*) FROM caldav_accounts WHERE cda_account_type != 2") fun insert(caldavCalendar: CaldavCalendar) = runBlocking {
abstract fun accountCount(): Int dao.insert(caldavCalendar)
}
@Query("SELECT * FROM caldav_accounts ORDER BY cda_account_type, UPPER(cda_name)") fun insertInternal(caldavCalendar: CaldavCalendar): Long = runBlocking {
abstract fun getAccounts(): List<CaldavAccount> dao.insertInternal(caldavCalendar)
}
@Query("UPDATE caldav_accounts SET cda_collapsed = :collapsed WHERE cda_id = :id") fun update(caldavCalendar: CaldavCalendar) = runBlocking {
abstract fun setCollapsed(id: Long, collapsed: Boolean) dao.update(caldavCalendar)
}
@Insert fun insert(task: Task, caldavTask: CaldavTask, addToTop: Boolean): Long = runBlocking {
abstract fun insert(caldavAccount: CaldavAccount): Long dao.insert(task, caldavTask, addToTop)
}
@Update internal fun findFirstTask(calendar: String, parent: Long): Long? = runBlocking {
abstract fun update(caldavAccount: CaldavAccount) dao.findFirstTask(calendar, parent)
}
fun insert(caldavCalendar: CaldavCalendar) { internal fun findLastTask(calendar: String, parent: Long): Long? = runBlocking {
caldavCalendar.id = insertInternal(caldavCalendar) dao.findLastTask(calendar, parent)
} }
@Insert fun insert(caldavTask: CaldavTask): Long = runBlocking {
abstract fun insertInternal(caldavCalendar: CaldavCalendar): Long dao.insert(caldavTask)
}
@Update fun insert(tasks: Iterable<CaldavTask>) = runBlocking {
abstract fun update(caldavCalendar: CaldavCalendar) dao.insert(tasks)
}
@Transaction fun update(caldavTask: CaldavTask) = runBlocking {
open fun insert(task: Task, caldavTask: CaldavTask, addToTop: Boolean): Long { dao.update(caldavTask)
if (caldavTask.order != null) {
return insert(caldavTask)
} }
if (addToTop) {
caldavTask.order = findFirstTask(caldavTask.calendar!!, task.parent) fun update(caldavTask: SubsetCaldav) = runBlocking {
?.takeIf { task.creationDate.toAppleEpoch() >= it } dao.update(caldavTask)
?.minus(1)
} else {
caldavTask.order = findLastTask(caldavTask.calendar!!, task.parent)
?.takeIf { task.creationDate.toAppleEpoch() <= it }
?.plus(1)
} }
return insert(caldavTask)
internal fun update(id: Long, position: Long?, parent: String?) = runBlocking {
dao.update(id, position, parent)
} }
@Query("SELECT MIN(IFNULL(cd_order, (created - $APPLE_EPOCH) / 1000)) FROM caldav_tasks INNER JOIN tasks ON _id = cd_task WHERE cd_calendar = :calendar AND cd_deleted = 0 AND deleted = 0 AND parent = :parent") internal fun update(id: Long, position: Long?) = runBlocking {
internal abstract fun findFirstTask(calendar: String, parent: Long): Long? dao.update(id, position)
}
@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 fun update(id: Long, remoteParent: String?) = runBlocking {
internal abstract fun findLastTask(calendar: String, parent: Long): Long? dao.update(id, remoteParent)
}
@Insert fun update(tasks: Iterable<CaldavTask>) = runBlocking {
abstract fun insert(caldavTask: CaldavTask): Long dao.update(tasks)
}
@Insert fun delete(caldavTask: CaldavTask) = runBlocking {
abstract fun insert(tasks: Iterable<CaldavTask>) dao.delete(caldavTask)
}
@Update fun getDeleted(calendar: String): List<CaldavTask> = runBlocking {
abstract fun update(caldavTask: CaldavTask) dao.getDeleted(calendar)
}
fun update(caldavTask: SubsetCaldav) { fun markDeleted(tasks: List<Long>, now: Long = currentTimeMillis()) = runBlocking {
update(caldavTask.cd_id, caldavTask.cd_order, caldavTask.cd_remote_parent) dao.markDeleted(tasks, now)
} }
@Query("UPDATE caldav_tasks SET cd_order = :position, cd_remote_parent = :parent WHERE cd_id = :id") fun getTask(taskId: Long): CaldavTask? = runBlocking {
internal abstract fun update(id: Long, position: Long?, parent: String?) dao.getTask(taskId)
}
@Query("UPDATE caldav_tasks SET cd_order = :position WHERE cd_id = :id") fun getRemoteIdForTask(taskId: Long): String? = runBlocking {
internal abstract fun update(id: Long, position: Long?) dao.getRemoteIdForTask(taskId)
}
@Query("UPDATE caldav_tasks SET cd_remote_parent = :remoteParent WHERE cd_id = :id") fun getTask(calendar: String, obj: String): CaldavTask? = runBlocking {
internal abstract fun update(id: Long, remoteParent: String?) dao.getTask(calendar, obj)
}
@Update fun getTaskByRemoteId(calendar: String, remoteId: String): CaldavTask? = runBlocking {
abstract fun update(tasks: Iterable<CaldavTask>) dao.getTaskByRemoteId(calendar, remoteId)
}
@Delete fun getTasks(taskId: Long): List<CaldavTask> = runBlocking {
abstract fun delete(caldavTask: CaldavTask) dao.getTasks(taskId)
}
@Query("SELECT * FROM caldav_tasks WHERE cd_deleted > 0 AND cd_calendar = :calendar") fun getTasks(taskIds: List<Long>): List<CaldavTask> = runBlocking {
abstract fun getDeleted(calendar: String): List<CaldavTask> dao.getTasks(taskIds)
}
@Query("UPDATE caldav_tasks SET cd_deleted = :now WHERE cd_task IN (:tasks)") fun getTasks(): List<CaldavTaskContainer> = runBlocking {
abstract fun markDeleted(tasks: List<Long>, now: Long = currentTimeMillis()) dao.getTasks()
}
@Query("SELECT * FROM caldav_tasks WHERE cd_task = :taskId AND cd_deleted = 0 LIMIT 1") fun getCaldavTasksToPush(calendar: String): List<CaldavTaskContainer> = runBlocking {
abstract fun getTask(taskId: Long): CaldavTask? dao.getCaldavTasksToPush(calendar)
}
@Query("SELECT cd_remote_id FROM caldav_tasks WHERE cd_task = :taskId AND cd_deleted = 0") fun getCalendars(): List<CaldavCalendar> = runBlocking {
abstract fun getRemoteIdForTask(taskId: Long): String? dao.getCalendars()
}
@Query("SELECT * FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_object = :obj LIMIT 1") fun getCalendar(uuid: String): CaldavCalendar? = runBlocking {
abstract fun getTask(calendar: String, obj: String): CaldavTask? dao.getCalendar(uuid)
}
@Query("SELECT * FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_remote_id = :remoteId") fun getObjects(calendar: String): List<String> = runBlocking {
abstract fun getTaskByRemoteId(calendar: String, remoteId: String): CaldavTask? dao.getObjects(calendar)
}
@Query("SELECT * FROM caldav_tasks WHERE cd_task = :taskId") fun getTasks(calendar: String, objects: List<String>): List<Long> = runBlocking {
abstract fun getTasks(taskId: Long): List<CaldavTask> dao.getTasks(calendar, objects)
}
@Query("SELECT * FROM caldav_tasks WHERE cd_task in (:taskIds) AND cd_deleted = 0") fun getTasksInternal(calendar: String, objects: List<String>): List<Long> = runBlocking {
abstract fun getTasks(taskIds: List<Long>): List<CaldavTask> dao.getTasksInternal(calendar, objects)
}
@Query("SELECT task.*, caldav_task.* FROM tasks AS task " fun findDeletedCalendars(account: String, urls: List<String>): List<CaldavCalendar> = runBlocking {
+ "INNER JOIN caldav_tasks AS caldav_task ON _id = cd_task " dao.findDeletedCalendars(account, urls)
+ "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 " fun getCalendarByUrl(account: String, url: String): CaldavCalendar? = runBlocking {
+ "INNER JOIN caldav_tasks AS caldav_task ON _id = cd_task " dao.getCalendarByUrl(account, url)
+ "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") fun getAccountForTask(task: Long): CaldavAccount? = runBlocking {
abstract fun getCalendars(): List<CaldavCalendar> dao.getAccountForTask(task)
}
@Query("SELECT * FROM caldav_lists WHERE cdl_uuid = :uuid LIMIT 1") fun getCalendars(tasks: List<Long>): List<String> = runBlocking {
abstract fun getCalendar(uuid: String): CaldavCalendar? dao.getCalendars(tasks)
}
@Query("SELECT cd_object FROM caldav_tasks WHERE cd_calendar = :calendar") fun getCaldavFilters(uuid: String, now: Long = currentTimeMillis()): List<CaldavFilters> = runBlocking {
abstract fun getObjects(calendar: String): List<String> dao.getCaldavFilters(uuid, now)
}
fun getTasks(calendar: String, objects: List<String>): List<Long> = fun getTasksWithTags(): List<Long> = runBlocking {
objects.chunkedMap { getTasksInternal(calendar, it) } dao.getTasksWithTags()
}
@Query("SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_object IN (:objects)") fun updateParents() = runBlocking {
abstract fun getTasksInternal(calendar: String, objects: List<String>): List<Long> dao.updateParents()
}
@Query("SELECT * FROM caldav_lists WHERE cdl_account = :account AND cdl_url NOT IN (:urls)") fun updateParents(calendar: String) = runBlocking {
abstract fun findDeletedCalendars(account: String, urls: List<String>): List<CaldavCalendar> dao.updateParents(calendar)
}
@Query("SELECT * FROM caldav_lists WHERE cdl_account = :account AND cdl_url = :url LIMIT 1") fun move(task: TaskContainer, newParent: Long, newPosition: Long?) = runBlocking {
abstract fun getCalendarByUrl(account: String, url: String): CaldavCalendar? dao.move(task, newParent, newPosition)
}
@Query("SELECT caldav_accounts.* from caldav_accounts" fun shiftDown(calendar: String, parent: Long, from: Long, to: Long? = null) = runBlocking {
+ " INNER JOIN caldav_tasks ON cd_task = :task" dao.shiftDown(calendar, parent, from, to)
+ " 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)") internal fun touchInternal(ids: List<Long>, modificationTime: Long = now()) = runBlocking {
abstract fun getCalendars(tasks: List<Long>): List<String> dao.touchInternal(ids, modificationTime)
}
@Query("SELECT caldav_lists.*, COUNT(tasks._id) AS count" internal fun getTasksToShift(calendar: String, parent: Long, from: Long, to: Long?): List<CaldavTaskContainer> = runBlocking {
+ " FROM caldav_lists" dao.getTasksToShift(calendar, parent, from, to)
+ " 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 " fun resetOrders() = runBlocking {
+ "INNER JOIN tags ON tags.task = tasks._id " dao.resetOrders()
+ "INNER JOIN caldav_tasks ON cd_task = tasks._id " }
+ "GROUP BY tasks._id")
abstract fun getTasksWithTags(): List<Long>
@Query("UPDATE tasks SET parent = IFNULL((" fun setOrder(id: Long, order: Int) = runBlocking {
+ " SELECT p.cd_task FROM caldav_tasks AS p" dao.setOrder(id, order)
+ " 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((" fun setupLocalAccount(context: Context): CaldavAccount = runBlocking {
+ " SELECT p.cd_task FROM caldav_tasks AS p" dao.setupLocalAccount(context)
+ " 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 fun getLocalList(context: Context) = runBlocking {
open fun move(task: TaskContainer, newParent: Long, newPosition: Long?) { dao.getLocalList(context)
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)")
abstract fun deleteGoogleTasks(ids: List<Long>)
@Query("DELETE FROM tags WHERE task IN(:ids)")
abstract fun deleteTags(ids: List<Long>)
@Query("DELETE FROM geofences WHERE task IN(:ids)")
abstract fun deleteGeofences(ids: List<Long>)
@Query("DELETE FROM alarms WHERE task IN(:ids)") fun deleteGoogleTasks(ids: List<Long>) = runBlocking {
abstract fun deleteAlarms(ids: List<Long>) dao.deleteGoogleTasks(ids)
}
@Query("DELETE FROM tasks WHERE _id IN(:ids)") fun deleteTags(ids: List<Long>) = runBlocking {
abstract fun deleteTasks(ids: List<Long>) dao.deleteTags(ids)
}
@Transaction fun deleteGeofences(ids: List<Long>) = runBlocking {
open fun delete(ids: List<Long>) { dao.deleteGeofences(ids)
ids.eachChunk {
deleteAlarms(it)
deleteGeofences(it)
deleteTags(it)
deleteGoogleTasks(it)
deleteCaldavTasks(it)
deleteTasks(it)
} }
fun deleteAlarms(ids: List<Long>) = runBlocking {
dao.deleteAlarms(ids)
} }
@Query("UPDATE tasks " fun deleteTasks(ids: List<Long>) = runBlocking {
+ "SET modified = (strftime('%s','now')*1000), deleted = (strftime('%s','now')*1000)" dao.deleteTasks(ids)
+ "WHERE _id IN(:ids)") }
abstract fun markDeletedInternal(ids: List<Long>)
fun markDeleted(ids: Iterable<Long>) { fun delete(ids: List<Long>) = runBlocking {
ids.eachChunk(this::markDeletedInternal) dao.delete(ids)
} }
@Query("SELECT gt_task FROM google_tasks WHERE gt_deleted = 0 AND gt_list_id = :listId") fun markDeletedInternal(ids: List<Long>) = runBlocking {
abstract fun getActiveGoogleTasks(listId: String): List<Long> dao.markDeletedInternal(ids)
}
@Delete fun markDeleted(ids: Iterable<Long>) = runBlocking {
abstract fun deleteGoogleTaskList(googleTaskList: GoogleTaskList) dao.markDeleted(ids)
}
@Transaction fun getActiveGoogleTasks(listId: String): List<Long> = runBlocking {
open fun delete(googleTaskList: GoogleTaskList): List<Long> { dao.getActiveGoogleTasks(listId)
val tasks = getActiveGoogleTasks(googleTaskList.remoteId!!)
delete(tasks)
deleteGoogleTaskList(googleTaskList)
return tasks
} }
@Delete fun deleteGoogleTaskList(googleTaskList: GoogleTaskList) = runBlocking {
abstract fun deleteGoogleTaskAccount(googleTaskAccount: GoogleTaskAccount) dao.deleteGoogleTaskList(googleTaskList)
}
@Query("SELECT * FROM google_task_lists WHERE gtl_account = :account ORDER BY gtl_title ASC") fun delete(googleTaskList: GoogleTaskList): List<Long> = runBlocking {
abstract fun getLists(account: String): List<GoogleTaskList> dao.delete(googleTaskList)
}
@Transaction fun deleteGoogleTaskAccount(googleTaskAccount: GoogleTaskAccount) = runBlocking {
open fun delete(googleTaskAccount: GoogleTaskAccount): List<Long> { dao.deleteGoogleTaskAccount(googleTaskAccount)
val deleted = ArrayList<Long>()
for (list in getLists(googleTaskAccount.account!!)) {
deleted.addAll(delete(list))
} }
deleteGoogleTaskAccount(googleTaskAccount)
return deleted fun getLists(account: String): List<GoogleTaskList> = runBlocking {
dao.getLists(account)
} }
@Query("SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_deleted = 0") fun delete(googleTaskAccount: GoogleTaskAccount): List<Long> = runBlocking {
abstract fun getActiveCaldavTasks(calendar: String): List<Long> dao.delete(googleTaskAccount)
}
@Delete fun getActiveCaldavTasks(calendar: String): List<Long> = runBlocking {
abstract fun deleteCaldavCalendar(caldavCalendar: CaldavCalendar) dao.getActiveCaldavTasks(calendar)
}
@Transaction fun deleteCaldavCalendar(caldavCalendar: CaldavCalendar) = runBlocking {
open fun delete(caldavCalendar: CaldavCalendar): List<Long> { dao.deleteCaldavCalendar(caldavCalendar)
val tasks = getActiveCaldavTasks(caldavCalendar.uuid!!)
delete(tasks)
deleteCaldavCalendar(caldavCalendar)
return tasks
} }
@Query("SELECT * FROM caldav_lists WHERE cdl_account = :account") fun delete(caldavCalendar: CaldavCalendar): List<Long> = runBlocking {
abstract fun getCalendars(account: String): List<CaldavCalendar> dao.delete(caldavCalendar)
}
@Delete fun getCalendars(account: String): List<CaldavCalendar> = runBlocking {
abstract fun deleteCaldavAccount(caldavAccount: CaldavAccount) dao.getCalendars(account)
}
@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 deleteCaldavAccount(caldavAccount: CaldavAccount) = runBlocking {
abstract fun purgeDeleted() dao.deleteCaldavAccount(caldavAccount)
}
@Transaction fun purgeDeleted() = runBlocking {
open fun delete(caldavAccount: CaldavAccount): List<Long> { dao.purgeDeleted()
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 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)
@Insert
abstract fun insert(tasks: Iterable<GoogleTask>)
@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)
} }
@Query("UPDATE google_tasks SET gt_order = gt_order + 1 WHERE gt_list_id = :listId AND gt_parent = :parent AND gt_order >= :position") fun insert(tasks: Iterable<GoogleTask>) = runBlocking {
abstract fun shiftDown(listId: String, parent: Long, position: Long) dao.insert(tasks)
@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)
@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)
@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)
@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") fun insertAndShift(task: GoogleTask, top: Boolean) = runBlocking {
abstract fun getByTaskId(taskId: Long): GoogleTask? dao.insertAndShift(task, top)
@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") fun shiftDown(listId: String, parent: Long, position: Long) = runBlocking {
abstract fun update(id: Long, parent: Long, order: Long) dao.shiftDown(listId, parent, position)
}
@Query("UPDATE google_tasks SET gt_deleted = :now WHERE gt_task = :task OR gt_parent = :task") fun shiftUp(listId: String, parent: Long, from: Long, to: Long) = runBlocking {
abstract fun markDeleted(task: Long, now: Long = currentTimeMillis()) dao.shiftUp(listId, parent, from, to)
}
@Delete fun shiftDown(listId: String, parent: Long, from: Long, to: Long) = runBlocking {
abstract fun delete(deleted: GoogleTask) dao.shiftDown(listId, parent, from, to)
}
@Query("SELECT * FROM google_tasks WHERE gt_remote_id = :remoteId LIMIT 1") fun shiftUp(listId: String, parent: Long, position: Long) = runBlocking {
abstract fun getByRemoteId(remoteId: String): GoogleTask? dao.shiftUp(listId, parent, position)
}
@Query("SELECT * FROM google_tasks WHERE gt_task = :taskId AND gt_deleted > 0") fun move(task: SubsetGoogleTask, newParent: Long, newPosition: Long) = runBlocking {
abstract fun getDeletedByTaskId(taskId: Long): List<GoogleTask> dao.move(task, newParent, newPosition)
}
@Query("SELECT * FROM google_tasks WHERE gt_task = :taskId") fun setCollapsed(id: Long, collapsed: Boolean) = runBlocking {
abstract fun getAllByTaskId(taskId: Long): List<GoogleTask> dao.setCollapsed(id, collapsed)
}
@Query("SELECT DISTINCT gt_list_id FROM google_tasks WHERE gt_deleted = 0 AND gt_task IN (:tasks)") fun getByTaskId(taskId: Long): GoogleTask? = runBlocking {
abstract fun getLists(tasks: List<Long>): List<String> dao.getByTaskId(taskId)
}
@Query("SELECT gt_task FROM google_tasks WHERE gt_parent IN (:ids) AND gt_deleted = 0") fun update(googleTask: GoogleTask) = runBlocking {
abstract fun getChildren(ids: List<Long>): List<Long> dao.update(googleTask)
}
@Query("SELECT tasks.* FROM tasks JOIN google_tasks ON tasks._id = gt_task WHERE gt_parent = :taskId") fun update(id: Long, parent: Long, order: Long) = runBlocking {
abstract fun getChildTasks(taskId: Long): List<Task> dao.update(id, parent, order)
}
@Query("SELECT * FROM google_tasks WHERE gt_parent = :id AND gt_deleted = 0") fun markDeleted(task: Long, now: Long = currentTimeMillis()) = runBlocking {
abstract fun getChildren(id: Long): List<GoogleTask> dao.markDeleted(task, now)
}
@Query("SELECT IFNULL(MAX(gt_order), -1) + 1 FROM google_tasks WHERE gt_list_id = :listId AND gt_parent = :parent") fun delete(deleted: GoogleTask) = runBlocking {
abstract fun getBottom(listId: String, parent: Long): Long dao.delete(deleted)
}
@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") fun getByRemoteId(remoteId: String): GoogleTask? = runBlocking {
abstract fun getPrevious(listId: String, parent: Long, order: Long): String? dao.getByRemoteId(remoteId)
}
@Query("SELECT gt_remote_id FROM google_tasks WHERE gt_task = :task") fun getDeletedByTaskId(taskId: Long): List<GoogleTask> = runBlocking {
abstract fun getRemoteId(task: Long): String? dao.getDeletedByTaskId(taskId)
}
@Query("SELECT gt_task FROM google_tasks WHERE gt_remote_id = :remoteId") fun getAllByTaskId(taskId: Long): List<GoogleTask> = runBlocking {
abstract fun getTask(remoteId: String): Long dao.getAllByTaskId(taskId)
}
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH) fun getLists(tasks: List<Long>): List<String> = runBlocking {
@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") dao.getLists(tasks)
abstract fun getByLocalOrder(listId: String): List<GoogleTask> }
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH) fun getChildren(ids: List<Long>): List<Long> = runBlocking {
@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") dao.getChildren(ids)
abstract fun getByRemoteOrder(listId: String): List<GoogleTask> }
@Query("UPDATE google_tasks" fun getChildTasks(taskId: Long): List<Task> = runBlocking {
+ " SET gt_parent = IFNULL((" dao.getChildTasks(taskId)
+ " 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") fun getChildren(id: Long): List<GoogleTask> = runBlocking {
abstract fun updateParents(listId: String) dao.getChildren(id)
}
@Query("UPDATE google_tasks SET gt_remote_parent = :parent, gt_remote_order = :position WHERE gt_remote_id = :id") fun getBottom(listId: String, parent: Long): Long = runBlocking {
abstract fun updatePosition(id: String, parent: String, position: String) dao.getBottom(listId, parent)
}
@Transaction fun getPrevious(listId: String, parent: Long, order: Long): String? = runBlocking {
open fun reposition(listId: String) { dao.getPrevious(listId, parent, order)
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 { fun getRemoteId(task: Long): String? = runBlocking {
subtasks = 0 dao.getRemoteId(task)
if (task.order != parent && !task.isMoved) {
task.order = parent
update(task)
} }
parent++
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 validateSorting(listId: String) { fun updateParents() = runBlocking {
val orderedTasks = getByLocalOrder(listId) dao.updateParents()
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 { fun updateParents(listId: String) = runBlocking {
subtasks = 0 dao.updateParents(listId)
if (task.order != parent) {
throw IllegalStateException("Parent violation, expected $parent but was ${task.order}")
} }
parent++
fun updatePosition(id: String, parent: String, position: String) = runBlocking {
dao.updatePosition(id, parent, position)
} }
fun reposition(listId: String) = runBlocking {
dao.reposition(listId)
} }
fun validateSorting(listId: String) = runBlocking {
dao.validateSorting(listId)
} }
} }

@ -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
abstract fun insert(tag: Tag)
@Insert fun insert(tag: Tag) = runBlocking {
abstract fun insert(tags: Iterable<Tag>) dao.insert(tag)
}
@Query("DELETE FROM tags WHERE task = :taskId AND tag_uid in (:tagUids)") fun insert(tags: Iterable<Tag>) = runBlocking {
abstract fun deleteTags(taskId: Long, tagUids: List<String>) dao.insert(tags)
}
@Query("SELECT * FROM tags WHERE tag_uid = :tagUid") fun deleteTags(taskId: Long, tagUids: List<String>) = runBlocking {
abstract fun getByTagUid(tagUid: String): List<Tag> dao.deleteTags(taskId, tagUids)
}
@Query("SELECT * FROM tags WHERE task = :taskId") fun getByTagUid(tagUid: String): List<Tag> = runBlocking {
abstract fun getTagsForTask(taskId: Long): List<Tag> dao.getByTagUid(tagUid)
}
@Query("SELECT * FROM tags WHERE task = :taskId AND tag_uid = :tagUid") fun getTagsForTask(taskId: Long): List<Tag> = runBlocking {
abstract fun getTagByTaskAndTagUid(taskId: Long, tagUid: String): Tag? dao.getTagsForTask(taskId)
}
@Delete fun getTagByTaskAndTagUid(taskId: Long, tagUid: String): Tag? = runBlocking {
abstract fun delete(tags: List<Tag>) dao.getTagByTaskAndTagUid(taskId, tagUid)
}
@Transaction fun delete(tags: List<Tag>) = runBlocking {
open fun applyTags(task: Task, tagDataDao: TagDataDaoBlocking, current: List<TagData>): Boolean { dao.delete(tags)
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 applyTags(task: Task, tagDataDao: TagDataDaoBlocking, current: List<TagData>): Boolean = runBlocking {
if (!tags.isEmpty()) { dao.applyTags(task, tagDataDao, current)
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.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
@Dao @Deprecated("use coroutines")
abstract class TagDataDaoBlocking { class TagDataDaoBlocking @Inject constructor(private val dao: TagDataDao) {
@Query("SELECT * FROM tagdata") fun subscribeToTags(): LiveData<List<TagData>> {
abstract fun subscribeToTags(): LiveData<List<TagData>> return dao.subscribeToTags()
@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> { fun getTagByName(name: String): TagData? = runBlocking {
return searchTagsInternal("%$query%") dao.getTagByName(name)
.sortedWith(AlphanumComparator(TagData::name))
.toMutableList()
} }
@Query("SELECT * FROM tagdata WHERE name LIKE :query AND name NOT NULL AND name != ''") fun getTagWithCase(tag: String): String? = runBlocking {
protected abstract fun searchTagsInternal(query: String): List<TagData> dao.getTagWithCase(tag)
}
@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)") fun searchTags(query: String): List<TagData> = runBlocking {
abstract fun tagsToDelete(tasks: List<Long>, tagsToKeep: List<String>): List<Tag> dao.searchTags(query)
}
fun getTagSelections(tasks: List<Long>): Pair<Set<String>, Set<String>> { fun getAll(): List<TagData> = runBlocking {
val allTags = getAllTags(tasks) dao.getAll()
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)
} }
fun getByUuid(uuid: String): TagData? = runBlocking {
dao.getByUuid(uuid)
} }
fun getByUuid(uuids: Collection<String>): List<TagData> = runBlocking {
dao.getByUuid(uuids)
} }
partialTags.removeAll(commonTags!!)
return Pair(partialTags, commonTags) fun tagDataOrderedByName(): List<TagData> = runBlocking {
dao.tagDataOrderedByName()
} }
@Query("SELECT GROUP_CONCAT(DISTINCT(tag_uid)) FROM tasks" fun deleteTagData(tagData: TagData) = runBlocking {
+ " LEFT JOIN tags ON tags.task = tasks._id" dao.deleteTagData(tagData)
+ " WHERE tasks._id IN (:tasks)" }
+ " GROUP BY tasks._id")
abstract fun getAllTags(tasks: List<Long>): List<String>
@Transaction fun deleteTags(tagUid: String) = runBlocking {
open fun applyTags( dao.deleteTags(tagUid)
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) fun tagsToDelete(tasks: List<Long>, tagsToKeep: List<String>): List<Tag> = runBlocking {
if (added.isNotEmpty()) { dao.tagsToDelete(tasks, tagsToKeep)
modified.add(task.id)
insert(added.map { Tag(task, it) })
} }
fun getTagSelections(tasks: List<Long>): Pair<Set<String>, Set<String>> = runBlocking {
dao.getTagSelections(tasks)
} }
return ArrayList(modified)
fun getAllTags(tasks: List<Long>): List<String> = runBlocking {
dao.getAllTags(tasks)
} }
@Transaction fun applyTags(tasks: List<Task>, partiallySelected: List<TagData>, selected: List<TagData>): List<Long> = runBlocking {
open fun delete(tagData: TagData) { dao.applyTags(tasks, partiallySelected, selected)
deleteTags(tagData.remoteId!!)
deleteTagData(tagData)
} }
@Delete fun delete(tagData: TagData) = runBlocking {
abstract fun delete(tagData: List<TagData>) dao.delete(tagData)
}
@Delete fun delete(tagData: List<TagData>) = runBlocking {
abstract fun deleteTags(tags: List<Tag>) dao.delete(tagData)
}
@Query("SELECT tagdata.* FROM tagdata " fun deleteTags(tags: List<Tag>) = runBlocking {
+ "INNER JOIN tags ON tags.tag_uid = tagdata.remoteId " dao.deleteTags(tags)
+ "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)") fun getTagDataForTask(id: Long): List<TagData> = runBlocking {
abstract fun getTags(names: List<String>): List<TagData> dao.getTagDataForTask(id)
}
@Update fun getTags(names: List<String>): List<TagData> = runBlocking {
abstract fun update(tagData: TagData) dao.getTags(names)
}
@Insert fun update(tagData: TagData) = runBlocking {
abstract fun insert(tag: TagData): Long dao.update(tagData)
}
@Insert fun insert(tag: TagData): Long = runBlocking {
abstract fun insert(tags: Iterable<Tag>) dao.insert(tag)
}
fun createNew(tag: TagData) { fun insert(tags: Iterable<Tag>) = runBlocking {
if (Task.isUuidEmpty(tag.remoteId)) { dao.insert(tags)
tag.remoteId = UUIDHelper.newUUID()
} }
tag.id = insert(tag)
fun createNew(tag: TagData) = runBlocking {
dao.createNew(tag)
} }
@Query("SELECT tagdata.*, COUNT(tasks._id) AS count" fun getTagFilters(now: Long = currentTimeMillis()): List<TagFilters> = runBlocking {
+ " FROM tagdata" dao.getTagFilters(now)
+ " 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") fun resetOrders() = runBlocking {
abstract fun resetOrders() dao.resetOrders()
}
@Query("UPDATE tagdata SET td_order = :order WHERE _id = :id") fun setOrder(id: Long, order: Int) = runBlocking {
abstract fun setOrder(id: Long, order: Int) dao.setOrder(id, order)
}
} }

@ -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")
abstract fun getAttachments(task: Long): List<TaskAttachment>
@Query("SELECT * FROM task_attachments") fun getAttachments(task: Long): List<TaskAttachment> = runBlocking {
abstract fun getAttachments(): List<TaskAttachment> dao.getAttachments(task)
}
@Delete fun getAttachments(): List<TaskAttachment> = runBlocking {
abstract fun delete(taskAttachment: TaskAttachment) dao.getAttachments()
}
@Insert(onConflict = OnConflictStrategy.REPLACE) fun delete(taskAttachment: TaskAttachment) = runBlocking {
abstract fun insert(attachment: TaskAttachment) dao.delete(taskAttachment)
}
@Update fun insert(attachment: TaskAttachment) = runBlocking {
abstract fun update(attachment: TaskAttachment) dao.insert(attachment)
}
fun createNew(attachment: TaskAttachment) { fun update(attachment: TaskAttachment) = runBlocking {
if (Task.isUuidEmpty(attachment.remoteId)) { dao.update(attachment)
attachment.remoteId = UUIDHelper.newUUID()
} }
insert(attachment)
fun createNew(attachment: TaskAttachment) = runBlocking {
dao.createNew(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
@Dao @Deprecated("use coroutines")
abstract class UserActivityDaoBlocking { class UserActivityDaoBlocking @Inject constructor(private val dao: UserActivityDao) {
@Insert fun insert(userActivity: UserActivity) = runBlocking {
abstract fun insert(userActivity: UserActivity) dao.insert(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 ") fun update(userActivity: UserActivity) = runBlocking {
abstract fun getCommentsForTask(taskUuid: String): List<UserActivity> dao.update(userActivity)
}
@Query("SELECT userActivity.* FROM userActivity INNER JOIN tasks ON tasks._id = :task WHERE target_id = tasks.remoteId") fun delete(userActivity: UserActivity) = runBlocking {
abstract fun getComments(task: Long): List<UserActivity> dao.delete(userActivity)
}
@Query("SELECT * FROM userActivity") fun getCommentsForTask(taskUuid: String): List<UserActivity> = runBlocking {
abstract fun getComments(): List<UserActivity> dao.getCommentsForTask(taskUuid)
}
fun createNew(item: UserActivity) { fun getComments(task: Long): List<UserActivity> = runBlocking {
if (item.created == null || item.created == 0L) { dao.getComments(task)
item.created = DateUtilities.now()
} }
if (Task.isUuidEmpty(item.remoteId)) {
item.remoteId = UUIDHelper.newUUID() fun getComments(): List<UserActivity> = runBlocking {
dao.getComments()
} }
insert(item)
fun createNew(item: UserActivity) = runBlocking {
dao.createNew(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