vtodo cache improvements

pull/3212/head
Alex Baker 11 months ago
parent f17a287e4f
commit aa477a7b11

@ -25,6 +25,7 @@ import at.bitfire.ical4android.ICalendar.Companion.prodId
import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.service.TaskDeleter
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.runBlocking
import net.fortuna.ical4j.model.property.ProdId
import okhttp3.Headers
import okhttp3.HttpUrl
@ -291,7 +292,9 @@ class CaldavSynchronizer @Inject constructor(
.takeIf { it.isNotEmpty() }
?.let {
Timber.d("DELETED $it")
taskDeleter.delete(caldavDao.getTasks(caldavCalendar.uuid!!, it.toList()))
val tasks = caldavDao.getTasks(caldavCalendar.uuid!!, it.toList())
vtodoCache.delete(caldavCalendar, tasks)
taskDeleter.delete(tasks.map { it.task })
}
caldavCalendar.ctag = remoteCtag
Timber.d("UPDATE %s", caldavCalendar)
@ -369,9 +372,11 @@ class CaldavSynchronizer @Inject constructor(
fromResponse(it)?.eTag?.takeIf(String::isNotBlank)?.let { etag ->
caldavTask.etag = etag
}
runBlocking {
vtodoCache.putVtodo(calendar, caldavTask, String(data))
}
}
}
} catch (e: HttpException) {
Timber.e(e)
return

@ -2,6 +2,8 @@ package org.tasks.caldav
import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import javax.inject.Inject
@ -18,9 +20,11 @@ class FileStorage @Inject constructor(
null
}
fun read(file: File?): String? = file?.takeIf { it.exists() }?.readText()
suspend fun read(file: File?): String? = withContext(Dispatchers.IO) {
file?.takeIf { it.exists() }?.readText()
}
fun write(file: File, data: String?) {
suspend fun write(file: File, data: String?) = withContext(Dispatchers.IO) {
if (data.isNullOrBlank()) {
file.delete()
} else {

@ -1,5 +1,7 @@
package org.tasks.caldav
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.tasks.data.dao.CaldavDao
import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavCalendar
@ -13,17 +15,18 @@ class VtodoCache @Inject constructor(
private val caldavDao: CaldavDao,
private val fileStorage: FileStorage,
) {
fun move(from: CaldavCalendar, to: CaldavCalendar, task: CaldavTask) {
suspend fun move(from: CaldavCalendar, to: CaldavCalendar, task: CaldavTask) =
withContext(Dispatchers.IO) {
val source =
fileStorage.getFile(from.account, from.uuid, task.obj)
if (source?.exists() != true) {
return
return@withContext
}
val target =
fileStorage.getFile(to.account, to.uuid)
?.apply { mkdirs() }
?.let { File(it, task.obj!!) }
?: return
?: return@withContext
source.copyTo(target, overwrite = true)
source.delete()
}
@ -36,7 +39,7 @@ class VtodoCache @Inject constructor(
return getVtodo(calendar, caldavTask)
}
fun getVtodo(calendar: CaldavCalendar?, caldavTask: CaldavTask?): String? {
suspend fun getVtodo(calendar: CaldavCalendar?, caldavTask: CaldavTask?): String? {
val file = fileStorage.getFile(
calendar?.account,
caldavTask?.calendar,
@ -45,36 +48,37 @@ class VtodoCache @Inject constructor(
return fileStorage.read(file)
}
fun putVtodo(calendar: CaldavCalendar, caldavTask: CaldavTask, vtodo: String?) {
suspend fun putVtodo(calendar: CaldavCalendar, caldavTask: CaldavTask, vtodo: String?) {
val `object` = caldavTask.obj?.takeIf { it.isNotBlank() } ?: return
withContext(Dispatchers.IO) {
val directory =
fileStorage
.getFile(calendar.account, caldavTask.calendar)
?.apply { mkdirs() }
?: return
?: return@withContext
fileStorage.write(File(directory, `object`), vtodo)
}
suspend fun delete(taskIds: List<Long>) {
val tasks = caldavDao.getTasks(taskIds).groupBy { it.calendar!! }
tasks.forEach { (c, t) ->
val calendar = caldavDao.getCalendar(c) ?: return@forEach
t.forEach { delete(calendar, it) }
}
suspend fun delete(calendar: CaldavCalendar, tasks: List<CaldavTask>) {
tasks.forEach { delete(calendar, it) }
}
fun delete(calendar: CaldavCalendar, caldavTask: CaldavTask) {
suspend fun delete(calendar: CaldavCalendar, caldavTask: CaldavTask) = withContext(Dispatchers.IO) {
fileStorage
.getFile(calendar.account, caldavTask.calendar, caldavTask.obj)
?.delete()
}
fun delete(calendar: CaldavCalendar) =
suspend fun delete(calendar: CaldavCalendar) = withContext(Dispatchers.IO) {
fileStorage.getFile(calendar.account, calendar.uuid)?.deleteRecursively()
}
fun delete(account: CaldavAccount) =
suspend fun delete(account: CaldavAccount) = withContext(Dispatchers.IO) {
fileStorage.getFile(account.uuid)?.deleteRecursively()
}
fun clear() =
suspend fun clear() = withContext(Dispatchers.IO) {
fileStorage.getFile()?.deleteRecursively()
}
}

@ -6,6 +6,7 @@ import androidx.room.migration.Migration
import androidx.sqlite.SQLiteConnection
import androidx.sqlite.execSQL
import androidx.sqlite.use
import kotlinx.coroutines.runBlocking
import org.tasks.R
import org.tasks.caldav.FileStorage
import org.tasks.data.NO_ORDER
@ -466,9 +467,11 @@ object Migrations {
?: continue
val `object` = it.getTextOrNull(2) ?: continue
val data = it.getTextOrNull(3) ?: continue
runBlocking {
fileStorage.write(File(file, `object`), data)
}
}
}
connection.execSQL("ALTER TABLE `caldav_tasks` RENAME TO `caldav_tasks-temp`")
connection.execSQL(
"CREATE TABLE IF NOT EXISTS `caldav_tasks` (`cd_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cd_task` INTEGER NOT NULL, `cd_calendar` TEXT, `cd_object` TEXT, `cd_remote_id` TEXT, `cd_etag` TEXT, `cd_last_sync` INTEGER NOT NULL, `cd_deleted` INTEGER NOT NULL, `cd_remote_parent` TEXT, `cd_order` INTEGER)"

@ -179,7 +179,8 @@ class OpenTasksSynchronizer @Inject constructor(
.takeIf { it.isNotEmpty() }
?.let {
Timber.d("DELETED $it")
taskDeleter.delete(caldavDao.getTasksByRemoteId(calendar, it.toList()))
val tasks = caldavDao.getTasksByRemoteId(calendar, it.toList())
taskDeleter.delete(tasks.map { it.task })
}
}

@ -4,13 +4,14 @@ import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.lifecycle.lifecycleScope
import org.tasks.data.db.Database
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
import org.tasks.R
import org.tasks.caldav.VtodoCache
import org.tasks.calendars.CalendarEventProvider
import org.tasks.data.dao.TaskDao
import org.tasks.data.db.Database
import org.tasks.etebase.EtebaseLocalCache
import org.tasks.extensions.Context.toast
import org.tasks.files.FileHelper
@ -141,11 +142,13 @@ class Advanced : InjectingPreferenceFragment() {
.setMessage(R.string.EPr_delete_task_data_warning)
.setPositiveButton(R.string.EPr_delete_task_data) { _, _ ->
val context = requireContext()
lifecycleScope.launch(NonCancellable) {
context.deleteDatabase(database.name)
vtodoCache.clear()
EtebaseLocalCache.clear(context)
restart()
}
}
.setNegativeButton(R.string.cancel, null)
.show()
}

@ -243,17 +243,17 @@ SELECT EXISTS(SELECT 1
@Query("SELECT cd_remote_id FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_deleted = 0 AND cd_last_sync > 0")
abstract suspend fun getRemoteIds(calendar: String): List<String>
suspend fun getTasksByRemoteId(calendar: String, remoteIds: List<String>): List<Long> =
suspend fun getTasksByRemoteId(calendar: String, remoteIds: List<String>): List<CaldavTask> =
remoteIds.chunkedMap { getTasksByRemoteIdInternal(calendar, it) }
@Query("SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_remote_id IN (:remoteIds)")
internal abstract suspend fun getTasksByRemoteIdInternal(calendar: String, remoteIds: List<String>): List<Long>
@Query("SELECT * FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_remote_id IN (:remoteIds)")
internal abstract suspend fun getTasksByRemoteIdInternal(calendar: String, remoteIds: List<String>): List<CaldavTask>
suspend fun getTasks(calendar: String, objects: List<String>): List<Long> =
suspend fun getTasks(calendar: String, objects: List<String>): List<CaldavTask> =
objects.chunkedMap { getTasksInternal(calendar, it) }
@Query("SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_object IN (:objects)")
internal abstract suspend fun getTasksInternal(calendar: String, objects: List<String>): List<Long>
@Query("SELECT * FROM caldav_tasks WHERE cd_calendar = :calendar AND cd_object IN (:objects)")
internal abstract suspend fun getTasksInternal(calendar: String, objects: List<String>): List<CaldavTask>
@Query("SELECT * FROM caldav_lists WHERE cdl_account = :account AND cdl_url NOT IN (:urls)")
abstract suspend fun findDeletedCalendars(account: String, urls: List<String>): List<CaldavCalendar>

Loading…
Cancel
Save