Use transaction annotation

pull/3311/head
Alex Baker 10 months ago
parent 8ef23c1593
commit 8a27f5df4b

@ -103,7 +103,6 @@ import org.tasks.data.entity.Task
import org.tasks.data.listSettingsClass import org.tasks.data.listSettingsClass
import org.tasks.data.open import org.tasks.data.open
import org.tasks.data.sql.QueryTemplate import org.tasks.data.sql.QueryTemplate
import org.tasks.data.withTransaction
import org.tasks.databinding.FragmentTaskListBinding import org.tasks.databinding.FragmentTaskListBinding
import org.tasks.dialogs.DateTimePicker.Companion.newDateTimePicker import org.tasks.dialogs.DateTimePicker.Companion.newDateTimePicker
import org.tasks.dialogs.DialogBuilder import org.tasks.dialogs.DialogBuilder
@ -1058,10 +1057,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
(intent.getSerializableExtra(EXTRAS_TASK_ID) as? ArrayList<Long>) (intent.getSerializableExtra(EXTRAS_TASK_ID) as? ArrayList<Long>)
?.let { ?.let {
Timber.d("Repeating tasks: $it") Timber.d("Repeating tasks: $it")
// hack to wait for task save transaction to complete taskDao.fetch(it)
database.withTransaction {
taskDao.fetch(it)
}
} }
?.filterNot { it.readOnly } ?.filterNot { it.readOnly }
?.takeIf { it.isNotEmpty() } ?.takeIf { it.isNotEmpty() }

@ -98,22 +98,19 @@ class TaskDao @Inject constructor(
*/ */
suspend fun save(task: Task) = save(task, fetch(task.id)) suspend fun save(task: Task) = save(task, fetch(task.id))
suspend fun save(tasks: List<Task>, originals: List<Task>) {
Timber.d("Saving $tasks")
taskDao.updateInternal(tasks)
tasks.forEach { task -> afterUpdate(task, originals.find { it.id == task.id }) }
}
suspend fun save(task: Task, original: Task?) { suspend fun save(task: Task, original: Task?) {
if (taskDao.update(task, original)) { if (taskDao.update(task, original)) {
Timber.d("Saved $task") Timber.d("Saved $task")
afterUpdate(task, original) afterUpdate(task, original)
if (!task.isSuppressRefresh()) {
localBroadcastManager.broadcastRefresh()
}
workManager.triggerNotifications() workManager.triggerNotifications()
workManager.scheduleRefresh() workManager.scheduleRefresh()
} }
} }
private suspend fun afterUpdate(task: Task, original: Task?) { suspend fun afterUpdate(task: Task, original: Task?) {
val completionDateModified = task.completionDate != (original?.completionDate ?: 0) val completionDateModified = task.completionDate != (original?.completionDate ?: 0)
val deletionDateModified = task.deletionDate != (original?.deletionDate ?: 0) val deletionDateModified = task.deletionDate != (original?.deletionDate ?: 0)
val justCompleted = completionDateModified && task.isCompleted val justCompleted = completionDateModified && task.isCompleted
@ -131,9 +128,6 @@ class TaskDao @Inject constructor(
if (completionDateModified || deletionDateModified) { if (completionDateModified || deletionDateModified) {
geofenceApi.update(task.id) geofenceApi.update(task.id)
} }
if (!task.isSuppressRefresh()) {
localBroadcastManager.broadcastRefresh()
}
syncAdapters.sync(task, original) syncAdapters.sync(task, original)
} }

@ -11,7 +11,6 @@ import com.todoroo.astrid.gcal.GCalHelper
import net.fortuna.ical4j.model.Date import net.fortuna.ical4j.model.Date
import net.fortuna.ical4j.model.Recur import net.fortuna.ical4j.model.Recur
import net.fortuna.ical4j.model.WeekDay import net.fortuna.ical4j.model.WeekDay
import org.tasks.LocalBroadcastManager
import org.tasks.data.createDueDate import org.tasks.data.createDueDate
import org.tasks.data.entity.Alarm import org.tasks.data.entity.Alarm
import org.tasks.data.entity.Alarm.Companion.TYPE_SNOOZE import org.tasks.data.entity.Alarm.Companion.TYPE_SNOOZE
@ -33,12 +32,11 @@ class RepeatTaskHelper @Inject constructor(
private val gcalHelper: GCalHelper, private val gcalHelper: GCalHelper,
private val alarmService: AlarmService, private val alarmService: AlarmService,
private val taskDao: TaskDao, private val taskDao: TaskDao,
private val localBroadcastManager: LocalBroadcastManager,
) { ) {
suspend fun handleRepeat(task: Task) { suspend fun handleRepeat(task: Task): Boolean {
val recurrence = task.recurrence val recurrence = task.recurrence
if (recurrence.isNullOrBlank()) { if (recurrence.isNullOrBlank()) {
return return false
} }
val repeatAfterCompletion = task.repeatFrom == RepeatFrom.COMPLETION_DATE val repeatAfterCompletion = task.repeatFrom == RepeatFrom.COMPLETION_DATE
val newDueDate: Long val newDueDate: Long
@ -48,17 +46,15 @@ class RepeatTaskHelper @Inject constructor(
rrule = initRRule(recurrence) rrule = initRRule(recurrence)
count = rrule.count count = rrule.count
if (count == 1) { if (count == 1) {
broadcastCompletion(task) return true
return
} }
newDueDate = computeNextDueDate(task, recurrence, repeatAfterCompletion) newDueDate = computeNextDueDate(task, recurrence, repeatAfterCompletion)
if (newDueDate == -1L) { if (newDueDate == -1L) {
broadcastCompletion(task) return true
return
} }
} catch (e: ParseException) { } catch (e: ParseException) {
Timber.e(e) Timber.e(e)
return return false
} }
if (count > 1) { if (count > 1) {
rrule.count = count - 1 rrule.count = count - 1
@ -70,18 +66,9 @@ class RepeatTaskHelper @Inject constructor(
task.setDueDateAdjustingHideUntil(newDueDate) task.setDueDateAdjustingHideUntil(newDueDate)
gcalHelper.rescheduleRepeatingTask(task) gcalHelper.rescheduleRepeatingTask(task)
taskDao.save(task) taskDao.save(task)
val previousDueDate = val previousDueDate = oldDueDate.takeIf { it > 0 } ?: computePreviousDueDate(task)
oldDueDate
.takeIf { it > 0 }
?: (newDueDate - (computeNextDueDate(task, recurrence, repeatAfterCompletion) - newDueDate))
rescheduleAlarms(task.id, previousDueDate, newDueDate) rescheduleAlarms(task.id, previousDueDate, newDueDate)
broadcastCompletion(task, previousDueDate) return true
}
private fun broadcastCompletion(task: Task, oldDueDate: Long = 0L) {
if (!task.isSuppressRefresh()) {
localBroadcastManager.broadcastTaskCompleted(task.id, oldDueDate)
}
} }
suspend fun undoRepeat(task: Task, oldDueDate: Long) { suspend fun undoRepeat(task: Task, oldDueDate: Long) {
@ -131,6 +118,9 @@ class RepeatTaskHelper @Inject constructor(
companion object { companion object {
private val weekdayCompare = Comparator { object1: WeekDay, object2: WeekDay -> WeekDay.getCalendarDay(object1) - WeekDay.getCalendarDay(object2) } private val weekdayCompare = Comparator { object1: WeekDay, object2: WeekDay -> WeekDay.getCalendarDay(object1) - WeekDay.getCalendarDay(object2) }
fun computePreviousDueDate(task: Task): Long =
task.dueDate - (computeNextDueDate(task, task.recurrence!!, task.repeatFrom == RepeatFrom.COMPLETION_DATE) - task.dueDate)
/** Compute next due date */ /** Compute next due date */
@Throws(ParseException::class) @Throws(ParseException::class)
fun computeNextDueDate(task: Task, recurrence: String, repeatAfterCompletion: Boolean): Long { fun computeNextDueDate(task: Task, recurrence: String, repeatAfterCompletion: Boolean): Long {

@ -8,13 +8,12 @@ import android.media.RingtoneManager
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.gcal.GCalHelper import com.todoroo.astrid.gcal.GCalHelper
import com.todoroo.astrid.repeats.RepeatTaskHelper import com.todoroo.astrid.repeats.RepeatTaskHelper
import com.todoroo.astrid.repeats.RepeatTaskHelper.Companion.computePreviousDueDate
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.data.dao.AlarmDao
import org.tasks.data.dao.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.data.db.Database import org.tasks.data.dao.CompletionDao
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
import org.tasks.data.withTransaction
import org.tasks.jobs.WorkManager import org.tasks.jobs.WorkManager
import org.tasks.notifications.NotificationManager import org.tasks.notifications.NotificationManager
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
@ -24,7 +23,6 @@ import javax.inject.Inject
class TaskCompleter @Inject internal constructor( class TaskCompleter @Inject internal constructor(
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
private val database: Database,
private val taskDao: TaskDao, private val taskDao: TaskDao,
private val preferences: Preferences, private val preferences: Preferences,
private val notificationManager: NotificationManager, private val notificationManager: NotificationManager,
@ -33,7 +31,7 @@ class TaskCompleter @Inject internal constructor(
private val caldavDao: CaldavDao, private val caldavDao: CaldavDao,
private val gCalHelper: GCalHelper, private val gCalHelper: GCalHelper,
private val workManager: WorkManager, private val workManager: WorkManager,
private val alarmDao: AlarmDao, private val completionDao: CompletionDao,
) { ) {
suspend fun setComplete(taskId: Long, completed: Boolean = true) = suspend fun setComplete(taskId: Long, completed: Boolean = true) =
taskDao taskDao
@ -56,10 +54,10 @@ class TaskCompleter @Inject internal constructor(
.filterNotNull() .filterNotNull()
.filter { it.isCompleted != completionDate > 0 } .filter { it.isCompleted != completionDate > 0 }
.filterNot { it.readOnly } .filterNot { it.readOnly }
.let { .let { tasks ->
setComplete(it, completionDate) setComplete(tasks, completionDate)
if (completed && !item.isRecurring) { if (completed && !item.isRecurring) {
localBroadcastManager.broadcastTaskCompleted(ArrayList(it.map(Task::id))) localBroadcastManager.broadcastTaskCompleted(tasks.map { it.id })
} }
} }
} }
@ -70,27 +68,24 @@ class TaskCompleter @Inject internal constructor(
} }
tasks.forEach { notificationManager.cancel(it.id) } tasks.forEach { notificationManager.cancel(it.id) }
val completed = completionDate > 0 val completed = completionDate > 0
val modified = currentTimeMillis() val repeated = ArrayList<Task>()
Timber.d("Completing $tasks") Timber.d("Completing $tasks")
database.withTransaction { completionDao.complete(
alarmDao.deleteSnoozed(tasks.map { it.id }) tasks = tasks,
tasks completionDate = completionDate,
.map { afterSave = { updated ->
it.copy( updated.forEach { saved ->
completionDate = completionDate, val original = tasks.find { it.id == saved.id }
modificationDate = modified, taskDao.afterUpdate(saved, original)
)
} }
.also { completed -> updated.forEach { task ->
completed.subList(0, completed.lastIndex).forEach { it.suppressRefresh() }
taskDao.save(completed, tasks)
}
.forEach { task ->
if (completed && task.isRecurring) { if (completed && task.isRecurring) {
gCalHelper.updateEvent(task) gCalHelper.updateEvent(task)
if (caldavDao.getAccountForTask(task.id)?.isSuppressRepeatingTasks != true) { if (caldavDao.getAccountForTask(task.id)?.isSuppressRepeatingTasks != true) {
repeatTaskHelper.handleRepeat(task) if (repeatTaskHelper.handleRepeat(task)) {
repeated.add(task)
}
if (task.completionDate == 0L) { if (task.completionDate == 0L) {
// un-complete children // un-complete children
setComplete(task, false) setComplete(task, false)
@ -98,9 +93,16 @@ class TaskCompleter @Inject internal constructor(
} }
} }
} }
} }
)
localBroadcastManager.broadcastRefresh()
workManager.triggerNotifications() workManager.triggerNotifications()
workManager.scheduleRefresh() workManager.scheduleRefresh()
repeated.lastOrNull()?.let { task ->
val oldDueDate = tasks.find { it.id == task.id }?.dueDate?.takeIf { it > 0 }
?: computePreviousDueDate(task)
localBroadcastManager.broadcastTaskCompleted(arrayListOf(task.id), oldDueDate)
}
if (completed && notificationManager.currentInterruptionFilter == INTERRUPTION_FILTER_ALL) { if (completed && notificationManager.currentInterruptionFilter == INTERRUPTION_FILTER_ALL) {
preferences preferences
.completionSound .completionSound

@ -10,23 +10,19 @@ import org.tasks.data.dao.DeletionDao
import org.tasks.data.dao.LocationDao import org.tasks.data.dao.LocationDao
import org.tasks.data.dao.TaskDao import org.tasks.data.dao.TaskDao
import org.tasks.data.dao.UserActivityDao import org.tasks.data.dao.UserActivityDao
import org.tasks.data.db.Database
import org.tasks.data.db.SuspendDbUtils.chunkedMap import org.tasks.data.db.SuspendDbUtils.chunkedMap
import org.tasks.data.entity.CaldavAccount import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
import org.tasks.data.pictureUri import org.tasks.data.pictureUri
import org.tasks.data.withTransaction
import org.tasks.files.FileHelper import org.tasks.files.FileHelper
import org.tasks.location.GeofenceApi import org.tasks.location.GeofenceApi
import org.tasks.notifications.NotificationManager import org.tasks.notifications.NotificationManager
import org.tasks.sync.SyncAdapters import org.tasks.sync.SyncAdapters
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class TaskDeleter @Inject constructor( class TaskDeleter @Inject constructor(
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
private val database: Database,
private val deletionDao: DeletionDao, private val deletionDao: DeletionDao,
private val taskDao: TaskDao, private val taskDao: TaskDao,
private val localBroadcastManager: LocalBroadcastManager, private val localBroadcastManager: LocalBroadcastManager,
@ -47,11 +43,10 @@ class TaskDeleter @Inject constructor(
.let { taskDao.fetch(it.toList()) } .let { taskDao.fetch(it.toList()) }
.filterNot { it.readOnly } .filterNot { it.readOnly }
.map { it.id } .map { it.id }
Timber.d("markDeleted $ids") deletionDao.markDeleted(
database.withTransaction { ids = ids,
deletionDao.markDeleted(ids) cleanup = { cleanup(it) }
cleanup(ids) )
}
syncAdapters.sync() syncAdapters.sync()
localBroadcastManager.broadcastRefresh() localBroadcastManager.broadcastRefresh()
taskDao.fetch(ids) taskDao.fetch(ids)
@ -62,31 +57,28 @@ class TaskDeleter @Inject constructor(
suspend fun delete(task: Long) = delete(listOf(task)) suspend fun delete(task: Long) = delete(listOf(task))
suspend fun delete(tasks: List<Long>) { suspend fun delete(tasks: List<Long>) {
Timber.d("Deleting $tasks") deletionDao.delete(
database.withTransaction { ids = tasks,
deletionDao.delete(tasks) cleanup = { cleanup(it) }
cleanup(tasks) )
}
localBroadcastManager.broadcastRefresh() localBroadcastManager.broadcastRefresh()
} }
suspend fun delete(list: CaldavCalendar) { suspend fun delete(list: CaldavCalendar) {
vtodoCache.delete(list) vtodoCache.delete(list)
Timber.d("Deleting $list") deletionDao.delete(
database.withTransaction { caldavCalendar = list,
val tasks = deletionDao.delete(list) cleanup = { cleanup(it) }
delete(tasks) )
}
localBroadcastManager.broadcastRefreshList() localBroadcastManager.broadcastRefreshList()
} }
suspend fun delete(account: CaldavAccount) { suspend fun delete(account: CaldavAccount) {
vtodoCache.delete(account) vtodoCache.delete(account)
Timber.d("Deleting $account") deletionDao.delete(
database.withTransaction { caldavAccount = account,
val tasks = deletionDao.delete(account) cleanup = { cleanup(it) }
delete(tasks) )
}
localBroadcastManager.broadcastRefreshList() localBroadcastManager.broadcastRefreshList()
} }

@ -5,7 +5,6 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.google.common.collect.Lists
import com.todoroo.astrid.api.AstridApiConstants import com.todoroo.astrid.api.AstridApiConstants
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import org.tasks.widget.AppWidgetManager import org.tasks.widget.AppWidgetManager
@ -56,17 +55,9 @@ class LocalBroadcastManager @Inject constructor(
localBroadcastManager.sendBroadcast(Intent(REFRESH_PREFERENCES)) localBroadcastManager.sendBroadcast(Intent(REFRESH_PREFERENCES))
} }
fun broadcastTaskCompleted(id: Long, oldDueDate: Long) { fun broadcastTaskCompleted(id: List<Long>, oldDueDate: Long = 0L) {
broadcastTaskCompleted(Lists.newArrayList(id), oldDueDate)
}
fun broadcastTaskCompleted(id: ArrayList<Long>) {
broadcastTaskCompleted(id, 0)
}
private fun broadcastTaskCompleted(id: ArrayList<Long>, oldDueDate: Long) {
val intent = Intent(TASK_COMPLETED) val intent = Intent(TASK_COMPLETED)
intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, id) intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, ArrayList(id))
intent.putExtra(AstridApiConstants.EXTRAS_OLD_DUE_DATE, oldDueDate) intent.putExtra(AstridApiConstants.EXTRAS_OLD_DUE_DATE, oldDueDate)
localBroadcastManager.sendBroadcast(intent) localBroadcastManager.sendBroadcast(intent)
} }

@ -15,6 +15,7 @@ import org.tasks.analytics.Firebase
import org.tasks.billing.BillingClient import org.tasks.billing.BillingClient
import org.tasks.billing.BillingClientImpl import org.tasks.billing.BillingClientImpl
import org.tasks.billing.Inventory import org.tasks.billing.Inventory
import org.tasks.compose.drawer.DrawerConfiguration
import org.tasks.data.dao.AlarmDao import org.tasks.data.dao.AlarmDao
import org.tasks.data.dao.Astrid2ContentProviderDao import org.tasks.data.dao.Astrid2ContentProviderDao
import org.tasks.data.dao.CaldavDao import org.tasks.data.dao.CaldavDao
@ -30,13 +31,12 @@ import org.tasks.data.dao.TaskDao
import org.tasks.data.dao.TaskListMetadataDao import org.tasks.data.dao.TaskListMetadataDao
import org.tasks.data.dao.UserActivityDao import org.tasks.data.dao.UserActivityDao
import org.tasks.data.db.Database import org.tasks.data.db.Database
import org.tasks.filters.FilterProvider
import org.tasks.filters.PreferenceDrawerConfiguration import org.tasks.filters.PreferenceDrawerConfiguration
import org.tasks.jobs.WorkManager import org.tasks.jobs.WorkManager
import org.tasks.kmp.createDataStore import org.tasks.kmp.createDataStore
import org.tasks.compose.drawer.DrawerConfiguration
import org.tasks.filters.FilterProvider
import org.tasks.preferences.TasksPreferences
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.preferences.TasksPreferences
import java.util.Locale import java.util.Locale
import javax.inject.Singleton import javax.inject.Singleton
@ -117,6 +117,10 @@ class ApplicationModule {
@Singleton @Singleton
fun getPrincipalDao(db: Database) = db.principalDao() fun getPrincipalDao(db: Database) = db.principalDao()
@Provides
@Singleton
fun getCompletionDao(db: Database) = db.completionDao()
@Provides @Provides
fun getBillingClient( fun getBillingClient(
@ApplicationContext context: Context, @ApplicationContext context: Context,

@ -1,13 +0,0 @@
package org.tasks.data
import androidx.room.RoomDatabase
import androidx.room.TransactionScope
import androidx.room.Transactor
import androidx.room.useWriterConnection
suspend fun <T> RoomDatabase.withTransaction(block: suspend TransactionScope<T>.() -> T): T =
useWriterConnection { transactor ->
transactor.withTransaction(Transactor.SQLiteTransactionType.IMMEDIATE) {
block()
}
}

@ -5,6 +5,7 @@ import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update import androidx.room.Update
import co.touchlab.kermit.Logger import co.touchlab.kermit.Logger
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -12,7 +13,6 @@ import org.tasks.data.CaldavFilters
import org.tasks.data.CaldavTaskContainer import org.tasks.data.CaldavTaskContainer
import org.tasks.data.NO_ORDER import org.tasks.data.NO_ORDER
import org.tasks.data.TaskContainer import org.tasks.data.TaskContainer
import org.tasks.data.db.Database
import org.tasks.data.db.DbUtils.dbchunk import org.tasks.data.db.DbUtils.dbchunk
import org.tasks.data.db.SuspendDbUtils.chunkedMap import org.tasks.data.db.SuspendDbUtils.chunkedMap
import org.tasks.data.entity.CaldavAccount import org.tasks.data.entity.CaldavAccount
@ -22,13 +22,12 @@ import org.tasks.data.entity.CaldavAccount.Companion.TYPE_TASKS
import org.tasks.data.entity.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.entity.CaldavTask import org.tasks.data.entity.CaldavTask
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
import org.tasks.data.withTransaction
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
const val APPLE_EPOCH = 978307200000L // 1/1/2001 GMT const val APPLE_EPOCH = 978307200000L // 1/1/2001 GMT
@Dao @Dao
abstract class CaldavDao(private val database: Database) { abstract class CaldavDao {
@Query("SELECT COUNT(*) FROM caldav_lists WHERE cdl_account = :account") @Query("SELECT COUNT(*) FROM caldav_lists WHERE cdl_account = :account")
abstract suspend fun listCount(account: String): Int abstract suspend fun listCount(account: String): Int
@ -104,23 +103,21 @@ ORDER BY CASE cda_account_type
suspend fun insert(task: Task, caldavTask: CaldavTask, addToTop: Boolean): Long { suspend fun insert(task: Task, caldavTask: CaldavTask, addToTop: Boolean): Long {
Logger.d("CaldavDao") { "insert task=$task caldavTask=$caldavTask addToTop=$addToTop)" } Logger.d("CaldavDao") { "insert task=$task caldavTask=$caldavTask addToTop=$addToTop)" }
return database.withTransaction { if (task.order != null) {
if (task.order != null) { return insert(caldavTask)
return@withTransaction insert(caldavTask)
}
if (addToTop) {
task.order = findFirstTask(caldavTask.calendar!!, task.parent)
?.takeIf { task.creationDate.toAppleEpoch() >= it }
?.minus(1)
} else {
task.order = findLastTask(caldavTask.calendar!!, task.parent)
?.takeIf { task.creationDate.toAppleEpoch() <= it }
?.plus(1)
}
val id = insert(caldavTask)
update(task)
id
} }
if (addToTop) {
task.order = findFirstTask(caldavTask.calendar!!, task.parent)
?.takeIf { task.creationDate.toAppleEpoch() >= it }
?.minus(1)
} else {
task.order = findLastTask(caldavTask.calendar!!, task.parent)
?.takeIf { task.creationDate.toAppleEpoch() <= it }
?.plus(1)
}
val id = insert(caldavTask)
update(task)
return id
} }
@Query(""" @Query("""
@ -309,51 +306,49 @@ GROUP BY caldav_lists.cdl_uuid
+ "WHERE _id IN (SELECT _id FROM tasks INNER JOIN caldav_tasks ON _id = cd_task WHERE cd_deleted = 0 AND cd_calendar = :calendar)") + "WHERE _id IN (SELECT _id FROM tasks INNER JOIN caldav_tasks ON _id = cd_task WHERE cd_deleted = 0 AND cd_calendar = :calendar)")
abstract suspend fun updateParents(calendar: String) abstract suspend fun updateParents(calendar: String)
suspend fun move( @Transaction
open suspend fun move(
task: TaskContainer, task: TaskContainer,
previousParent: Long, previousParent: Long,
newParent: Long, newParent: Long,
newPosition: Long?, newPosition: Long?,
) { ) {
Logger.d("CaldavDao") { "move task=$task previousParent=$previousParent newParent=$newParent newPosition=$newPosition" } Logger.d("CaldavDao") { "move task=$task previousParent=$previousParent newParent=$newParent newPosition=$newPosition" }
database.withTransaction { val previousPosition = task.caldavSortOrder
val previousPosition = task.caldavSortOrder if (newPosition != null) {
if (newPosition != null) { if (newParent == previousParent && newPosition < previousPosition) {
if (newParent == previousParent && newPosition < previousPosition) { shiftDown(task.caldav!!, newParent, newPosition, previousPosition)
shiftDown(task.caldav!!, newParent, newPosition, previousPosition) } else {
} else { val list =
val list = newParent.takeIf { it > 0 }?.let { getTask(it)?.calendar } ?: task.caldav!!
newParent.takeIf { it > 0 }?.let { getTask(it)?.calendar } ?: task.caldav!! shiftDown(list, newParent, newPosition)
shiftDown(list, newParent, newPosition)
}
} }
task.task.order = newPosition
setTaskOrder(task.id, newPosition)
} }
task.task.order = newPosition
setTaskOrder(task.id, newPosition)
} }
suspend fun shiftDown(calendar: String, parent: Long, from: Long, to: Long? = null) { @Transaction
open suspend fun shiftDown(calendar: String, parent: Long, from: Long, to: Long? = null) {
Logger.d("CaldavDao") { "shiftDown calendar=$calendar parent=$parent from=$from to=$to" } Logger.d("CaldavDao") { "shiftDown calendar=$calendar parent=$parent from=$from to=$to" }
database.withTransaction { val updated = ArrayList<Task>()
val updated = ArrayList<Task>() val tasks = getTasksToShift(calendar, parent, from, to)
val tasks = getTasksToShift(calendar, parent, from, to) for (i in tasks.indices) {
for (i in tasks.indices) { val task = tasks[i]
val task = tasks[i] val current = from + i
val current = from + i if (task.sortOrder == current) {
if (task.sortOrder == current) { val task = task.task
val task = task.task task.order = current + 1
task.order = current + 1 updated.add(task)
updated.add(task) } else if (task.sortOrder > current) {
} else if (task.sortOrder > current) { break
break
}
} }
updateTasks(updated)
updated
.map(Task::id)
.dbchunk()
.forEach { touchInternal(it) }
} }
updateTasks(updated)
updated
.map(Task::id)
.dbchunk()
.forEach { touchInternal(it) }
} }
@Query("UPDATE tasks SET modified = :modificationTime WHERE _id in (:ids)") @Query("UPDATE tasks SET modified = :modificationTime WHERE _id in (:ids)")

@ -0,0 +1,31 @@
package org.tasks.data.dao
import androidx.room.Dao
import androidx.room.Transaction
import co.touchlab.kermit.Logger
import org.tasks.data.db.Database
import org.tasks.data.entity.Task
import org.tasks.time.DateTimeUtils2.currentTimeMillis
@Dao
abstract class CompletionDao(private val db: Database) {
@Transaction
open suspend fun complete(
tasks: List<Task>,
completionDate: Long,
afterSave: suspend (List<Task>) -> Unit,
) {
Logger.d("CompletionDao") { "complete tasks=$tasks completionDate=$completionDate" }
val modified = currentTimeMillis()
val updated = tasks
.map {
it.copy(
completionDate = completionDate,
modificationDate = modified,
)
}
db.alarmDao().deleteSnoozed(tasks.map { it.id })
db.taskDao().updateInternal(updated)
afterSave(updated)
}
}

@ -3,17 +3,16 @@ package org.tasks.data.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Query import androidx.room.Query
import androidx.room.Transaction
import co.touchlab.kermit.Logger import co.touchlab.kermit.Logger
import org.tasks.data.dao.CaldavDao.Companion.LOCAL import org.tasks.data.dao.CaldavDao.Companion.LOCAL
import org.tasks.data.db.Database
import org.tasks.data.db.SuspendDbUtils.chunkedMap import org.tasks.data.db.SuspendDbUtils.chunkedMap
import org.tasks.data.db.SuspendDbUtils.eachChunk import org.tasks.data.db.SuspendDbUtils.eachChunk
import org.tasks.data.entity.CaldavAccount import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.withTransaction
@Dao @Dao
abstract class DeletionDao(private val database: Database) { abstract class DeletionDao {
@Query("DELETE FROM tasks WHERE _id IN(:ids)") @Query("DELETE FROM tasks WHERE _id IN(:ids)")
internal abstract suspend fun deleteTasks(ids: List<Long>) internal abstract suspend fun deleteTasks(ids: List<Long>)
@ -43,15 +42,29 @@ WHERE recurring = 1
""") """)
abstract suspend fun internalHasRecurringAncestors(ids: List<Long>): List<Long> abstract suspend fun internalHasRecurringAncestors(ids: List<Long>): List<Long>
suspend fun delete(ids: List<Long>) { ids.eachChunk { deleteTasks(it) } } @Transaction
open suspend fun delete(
ids: List<Long>,
cleanup: suspend (List<Long>) -> Unit,
) {
Logger.d("DeletionDao") { "delete ids=$ids" }
ids.eachChunk { deleteTasks(it) }
cleanup(ids)
}
@Query("UPDATE tasks " @Query("UPDATE tasks "
+ "SET modified = (strftime('%s','now')*1000), deleted = (strftime('%s','now')*1000)" + "SET modified = (strftime('%s','now')*1000), deleted = (strftime('%s','now')*1000)"
+ "WHERE _id IN(:ids)") + "WHERE _id IN(:ids)")
internal abstract suspend fun markDeletedInternal(ids: List<Long>) internal abstract suspend fun markDeletedInternal(ids: List<Long>)
suspend fun markDeleted(ids: Iterable<Long>) { @Transaction
open suspend fun markDeleted(
ids: Iterable<Long>,
cleanup: suspend (List<Long>) -> Unit,
) {
Logger.d("DeletionDao") { "markDeleted ids=$ids" }
ids.eachChunk(this::markDeletedInternal) ids.eachChunk(this::markDeletedInternal)
cleanup(ids.toList())
} }
@Query("SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_deleted = 0") @Query("SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_deleted = 0")
@ -60,14 +73,15 @@ WHERE recurring = 1
@Delete @Delete
internal abstract suspend fun deleteCaldavCalendar(caldavCalendar: CaldavCalendar) internal abstract suspend fun deleteCaldavCalendar(caldavCalendar: CaldavCalendar)
suspend fun delete(caldavCalendar: CaldavCalendar): List<Long> { @Transaction
open suspend fun delete(
caldavCalendar: CaldavCalendar,
cleanup: suspend (List<Long>) -> Unit,
) {
Logger.d("DeletionDao") { "deleting $caldavCalendar" } Logger.d("DeletionDao") { "deleting $caldavCalendar" }
return database.withTransaction { val tasks = getActiveCaldavTasks(caldavCalendar.uuid!!)
val tasks = getActiveCaldavTasks(caldavCalendar.uuid!!) delete(tasks, cleanup)
delete(tasks) deleteCaldavCalendar(caldavCalendar)
deleteCaldavCalendar(caldavCalendar)
tasks
}
} }
@Query("SELECT * FROM caldav_lists WHERE cdl_account = :account") @Query("SELECT * FROM caldav_lists WHERE cdl_account = :account")
@ -79,15 +93,15 @@ WHERE recurring = 1
@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 AND cd_deleted = 0)") @Query("DELETE FROM tasks WHERE _id IN (SELECT _id FROM tasks INNER JOIN caldav_tasks ON _id = cd_task INNER JOIN caldav_lists ON cdl_uuid = cd_calendar WHERE cdl_account = '$LOCAL' AND deleted > 0 AND cd_deleted = 0)")
abstract suspend fun purgeDeleted() abstract suspend fun purgeDeleted()
suspend fun delete(caldavAccount: CaldavAccount): List<Long> { @Transaction
open suspend fun delete(
caldavAccount: CaldavAccount,
cleanup: suspend (List<Long>) -> Unit,
) {
Logger.d("DeletionDao") { "deleting $caldavAccount" } Logger.d("DeletionDao") { "deleting $caldavAccount" }
return database.withTransaction { for (calendar in getCalendars(caldavAccount.uuid!!)) {
val deleted = ArrayList<Long>() delete(calendar, cleanup)
for (calendar in getCalendars(caldavAccount.uuid!!)) {
deleted.addAll(delete(calendar))
}
deleteCaldavAccount(caldavAccount)
deleted
} }
deleteCaldavAccount(caldavAccount)
} }
} }

@ -4,34 +4,32 @@ import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update import androidx.room.Update
import co.touchlab.kermit.Logger import co.touchlab.kermit.Logger
import org.tasks.data.db.Database
import org.tasks.data.entity.CaldavAccount.Companion.TYPE_GOOGLE_TASKS import org.tasks.data.entity.CaldavAccount.Companion.TYPE_GOOGLE_TASKS
import org.tasks.data.entity.CaldavTask import org.tasks.data.entity.CaldavTask
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
import org.tasks.data.withTransaction
@Dao @Dao
abstract class GoogleTaskDao(private val database: Database) { abstract class GoogleTaskDao {
@Insert @Insert
abstract suspend fun insert(task: CaldavTask): Long abstract suspend fun insert(task: CaldavTask): Long
@Insert @Insert
abstract suspend fun insert(tasks: Iterable<CaldavTask>) abstract suspend fun insert(tasks: Iterable<CaldavTask>)
suspend fun insertAndShift(task: Task, caldavTask: CaldavTask, top: Boolean) { @Transaction
open suspend fun insertAndShift(task: Task, caldavTask: CaldavTask, top: Boolean) {
Logger.d("GoogleTaskDao") { "insertAndShift task=$task caldavTask=$caldavTask top=$top" } Logger.d("GoogleTaskDao") { "insertAndShift task=$task caldavTask=$caldavTask top=$top" }
database.withTransaction { if (top) {
if (top) { task.order = 0
task.order = 0 shiftDown(caldavTask.calendar!!, task.parent, 0)
shiftDown(caldavTask.calendar!!, task.parent, 0) } else {
} else { task.order = getBottom(caldavTask.calendar!!, task.parent)
task.order = getBottom(caldavTask.calendar!!, task.parent)
}
insert(caldavTask)
update(task)
} }
insert(caldavTask)
update(task)
} }
@Query("UPDATE tasks SET `order` = `order` + 1 WHERE parent = :parent AND `order` >= :position AND _id IN (SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :listId)") @Query("UPDATE tasks SET `order` = `order` + 1 WHERE parent = :parent AND `order` >= :position AND _id IN (SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :listId)")
@ -46,26 +44,25 @@ abstract class GoogleTaskDao(private val database: Database) {
@Query("UPDATE tasks SET `order` = `order` - 1 WHERE parent = :parent AND `order` >= :position AND _id IN (SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :listId)") @Query("UPDATE tasks SET `order` = `order` - 1 WHERE parent = :parent AND `order` >= :position AND _id IN (SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :listId)")
internal abstract suspend fun shiftUp(listId: String, parent: Long, position: Long) internal abstract suspend fun shiftUp(listId: String, parent: Long, position: Long)
suspend fun move(task: Task, list: String, newParent: Long, newPosition: Long) { @Transaction
open suspend fun move(task: Task, list: String, newParent: Long, newPosition: Long) {
Logger.d("GoogleTaskDao") { "move task=$task list=$list newParent=$newParent newPosition=$newPosition" } Logger.d("GoogleTaskDao") { "move task=$task list=$list newParent=$newParent newPosition=$newPosition" }
database.withTransaction { val previousParent = task.parent
val previousParent = task.parent val previousPosition = task.order!!
val previousPosition = task.order!! if (newParent == previousParent) {
if (newParent == previousParent) { if (previousPosition < newPosition) {
if (previousPosition < newPosition) { shiftUp(list, newParent, previousPosition, newPosition)
shiftUp(list, newParent, previousPosition, newPosition)
} else {
shiftDown(list, newParent, previousPosition, newPosition)
}
} else { } else {
shiftUp(list, previousParent, previousPosition) shiftDown(list, newParent, previousPosition, newPosition)
shiftDown(list, newParent, newPosition)
} }
task.parent = newParent } else {
task.order = newPosition shiftUp(list, previousParent, previousPosition)
update(task) shiftDown(list, newParent, newPosition)
setMoved(task.id, list)
} }
task.parent = newParent
task.order = newPosition
update(task)
setMoved(task.id, list)
} }
@Query("UPDATE caldav_tasks SET gt_moved = 1 WHERE cd_task = :task and cd_calendar = :list") @Query("UPDATE caldav_tasks SET gt_moved = 1 WHERE cd_task = :task and cd_calendar = :list")
@ -170,26 +167,24 @@ WHERE cd_remote_id = :id
suspend fun reposition(caldavDao: CaldavDao, listId: String) { suspend fun reposition(caldavDao: CaldavDao, listId: String) {
Logger.d("GoogleTaskDao") { "reposition listId=$listId" } Logger.d("GoogleTaskDao") { "reposition listId=$listId" }
database.withTransaction { caldavDao.updateParents(listId)
caldavDao.updateParents(listId) val orderedTasks = getByRemoteOrder(listId)
val orderedTasks = getByRemoteOrder(listId) var subtasks = 0L
var subtasks = 0L var parent = 0L
var parent = 0L for (task in orderedTasks) {
for (task in orderedTasks) { if (task.parent > 0) {
if (task.parent > 0) { if (task.order != subtasks) {
if (task.order != subtasks) { task.order = subtasks
task.order = subtasks update(task)
update(task)
}
subtasks++
} else {
subtasks = 0
if (task.order != parent) {
task.order = parent
update(task)
}
parent++
} }
subtasks++
} else {
subtasks = 0
if (task.order != parent) {
task.order = parent
update(task)
}
parent++
} }
} }
} }

@ -4,19 +4,17 @@ import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update import androidx.room.Update
import co.touchlab.kermit.Logger
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import org.tasks.data.PrincipalWithAccess import org.tasks.data.PrincipalWithAccess
import org.tasks.data.db.Database
import org.tasks.data.entity.CaldavAccount import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavCalendar import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.entity.Principal import org.tasks.data.entity.Principal
import org.tasks.data.entity.PrincipalAccess import org.tasks.data.entity.PrincipalAccess
import org.tasks.data.withTransaction
@Dao @Dao
abstract class PrincipalDao(private val database: Database) { abstract class PrincipalDao {
@Insert @Insert
abstract suspend fun insert(principal: Principal): Long abstract suspend fun insert(principal: Principal): Long
@ -36,15 +34,9 @@ WHERE list = :list
@Delete @Delete
abstract suspend fun delete(access: PrincipalAccess) abstract suspend fun delete(access: PrincipalAccess)
suspend fun getAll(): List<PrincipalWithAccess> { @Transaction
Logger.d("PrincipalDao") { "getAll" }
return database.withTransaction {
getAllInternal()
}
}
@Query("SELECT * FROM principal_access") @Query("SELECT * FROM principal_access")
internal abstract suspend fun getAllInternal(): List<PrincipalWithAccess> abstract suspend fun getAll(): List<PrincipalWithAccess>
suspend fun getOrCreatePrincipal(account: CaldavAccount, href: String, displayName: String? = null) = suspend fun getOrCreatePrincipal(account: CaldavAccount, href: String, displayName: String? = null) =
findPrincipal(account.id, href) findPrincipal(account.id, href)

@ -4,15 +4,14 @@ import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import androidx.room.Transaction
import co.touchlab.kermit.Logger import co.touchlab.kermit.Logger
import org.tasks.data.db.Database
import org.tasks.data.entity.Tag import org.tasks.data.entity.Tag
import org.tasks.data.entity.TagData import org.tasks.data.entity.TagData
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
import org.tasks.data.withTransaction
@Dao @Dao
abstract class TagDao(private val database: Database) { abstract class TagDao {
@Query("UPDATE tags SET name = :name WHERE tag_uid = :tagUid") @Query("UPDATE tags SET name = :name WHERE tag_uid = :tagUid")
abstract suspend fun rename(tagUid: String, name: String) abstract suspend fun rename(tagUid: String, name: String)
@ -37,17 +36,16 @@ abstract class TagDao(private val database: Database) {
@Delete @Delete
abstract suspend fun delete(tags: List<Tag>) abstract suspend fun delete(tags: List<Tag>)
@Transaction
open suspend fun applyTags(task: Task, tagDataDao: TagDataDao, current: Collection<TagData>) { open suspend fun applyTags(task: Task, tagDataDao: TagDataDao, current: Collection<TagData>) {
Logger.d("TagDao") { "applyTags task=$task current=$current" } Logger.d("TagDao") { "applyTags task=$task current=$current" }
database.withTransaction { val taskId = task.id
val taskId = task.id val existing = HashSet(tagDataDao.getTagDataForTask(taskId))
val existing = HashSet(tagDataDao.getTagDataForTask(taskId)) val selected = current.toMutableSet()
val selected = current.toMutableSet() val added = selected subtract existing
val added = selected subtract existing val removed = existing subtract selected
val removed = existing subtract selected deleteTags(taskId, removed.map { td -> td.remoteId!! })
deleteTags(taskId, removed.map { td -> td.remoteId!! }) insert(task, added)
insert(task, added)
}
} }
suspend fun insert(task: Task, tags: Collection<TagData>) { suspend fun insert(task: Task, tags: Collection<TagData>) {

@ -4,21 +4,20 @@ import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update import androidx.room.Update
import co.touchlab.kermit.Logger import co.touchlab.kermit.Logger
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import org.tasks.data.NO_ORDER import org.tasks.data.NO_ORDER
import org.tasks.data.TagFilters import org.tasks.data.TagFilters
import org.tasks.data.db.Database
import org.tasks.data.db.DbUtils import org.tasks.data.db.DbUtils
import org.tasks.data.entity.Tag import org.tasks.data.entity.Tag
import org.tasks.data.entity.TagData import org.tasks.data.entity.TagData
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
import org.tasks.data.withTransaction
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
@Dao @Dao
abstract class TagDataDao(private val database: Database) { abstract class TagDataDao {
@Query("SELECT * FROM tagdata") @Query("SELECT * FROM tagdata")
abstract fun subscribeToTags(): Flow<List<TagData>> abstract fun subscribeToTags(): Flow<List<TagData>>
@ -81,46 +80,44 @@ abstract class TagDataDao(private val database: Database) {
+ " GROUP BY tasks._id") + " GROUP BY tasks._id")
internal abstract suspend fun getAllTags(tasks: List<Long>): List<String?> internal abstract suspend fun getAllTags(tasks: List<Long>): List<String?>
suspend fun applyTags( @Transaction
open suspend fun applyTags(
tasks: List<Task>, tasks: List<Task>,
partiallySelected: List<TagData>, partiallySelected: List<TagData>,
selected: List<TagData> selected: List<TagData>
): List<Long> { ): List<Long> {
Logger.d("TagDataDao") { "applyTags tasks=$tasks partiallySelected=$partiallySelected selected=$selected" } Logger.d("TagDataDao") { "applyTags tasks=$tasks partiallySelected=$partiallySelected selected=$selected" }
return database.withTransaction { val modified = HashSet<Long>()
val modified = HashSet<Long>() val keep = partiallySelected.plus(selected).map { it.remoteId!! }
val keep = partiallySelected.plus(selected).map { it.remoteId!! } for (sublist in tasks.chunked(DbUtils.MAX_SQLITE_ARGS - keep.size)) {
for (sublist in tasks.chunked(DbUtils.MAX_SQLITE_ARGS - keep.size)) { val tags = tagsToDelete(sublist.map(Task::id), keep)
val tags = tagsToDelete(sublist.map(Task::id), keep) deleteTags(tags)
deleteTags(tags) modified.addAll(tags.map(Tag::task))
modified.addAll(tags.map(Tag::task)) }
} for (task in tasks) {
for (task in tasks) { val added = selected subtract getTagDataForTask(task.id)
val added = selected subtract getTagDataForTask(task.id) if (added.isNotEmpty()) {
if (added.isNotEmpty()) { modified.add(task.id)
modified.add(task.id) insert(
insert( added.map {
added.map { Tag(
Tag( task = task.id,
task = task.id, taskUid = task.uuid,
taskUid = task.uuid, name = it.name,
name = it.name, tagUid = it.remoteId
tagUid = it.remoteId )
) }
} )
)
}
} }
ArrayList(modified)
} }
return ArrayList(modified)
} }
suspend fun delete(tagData: TagData) { @Transaction
open suspend fun delete(tagData: TagData) {
Logger.d("TagDataDao") { "deleting $tagData" } Logger.d("TagDataDao") { "deleting $tagData" }
database.withTransaction { deleteTags(tagData.remoteId!!)
deleteTags(tagData.remoteId!!) deleteTagData(tagData)
deleteTagData(tagData)
}
} }
@Delete @Delete

@ -6,6 +6,7 @@ import androidx.room.RoomDatabase
import org.tasks.data.dao.AlarmDao import org.tasks.data.dao.AlarmDao
import org.tasks.data.dao.Astrid2ContentProviderDao import org.tasks.data.dao.Astrid2ContentProviderDao
import org.tasks.data.dao.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.data.dao.CompletionDao
import org.tasks.data.dao.DeletionDao import org.tasks.data.dao.DeletionDao
import org.tasks.data.dao.FilterDao import org.tasks.data.dao.FilterDao
import org.tasks.data.dao.GoogleTaskDao import org.tasks.data.dao.GoogleTaskDao
@ -80,6 +81,7 @@ abstract class Database : RoomDatabase() {
abstract fun contentProviderDao(): Astrid2ContentProviderDao abstract fun contentProviderDao(): Astrid2ContentProviderDao
abstract fun upgraderDao(): UpgraderDao abstract fun upgraderDao(): UpgraderDao
abstract fun principalDao(): PrincipalDao abstract fun principalDao(): PrincipalDao
abstract fun completionDao(): CompletionDao
/** @return human-readable database name for debugging /** @return human-readable database name for debugging
*/ */

Loading…
Cancel
Save