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,7 +372,9 @@ class CaldavSynchronizer @Inject constructor(
fromResponse(it)?.eTag?.takeIf(String::isNotBlank)?.let { etag ->
caldavTask.etag = etag
}
vtodoCache.putVtodo(calendar, caldavTask, String(data))
runBlocking {
vtodoCache.putVtodo(calendar, caldavTask, String(data))
}
}
}
} catch (e: HttpException) {

@ -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,13 +20,15 @@ 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 {
file.writeText(data)
}
}
}
}

@ -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,20 +15,21 @@ class VtodoCache @Inject constructor(
private val caldavDao: CaldavDao,
private val fileStorage: FileStorage,
) {
fun move(from: CaldavCalendar, to: CaldavCalendar, task: CaldavTask) {
val source =
fileStorage.getFile(from.account, from.uuid, task.obj)
if (source?.exists() != true) {
return
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@withContext
}
val target =
fileStorage.getFile(to.account, to.uuid)
?.apply { mkdirs() }
?.let { File(it, task.obj!!) }
?: return@withContext
source.copyTo(target, overwrite = true)
source.delete()
}
val target =
fileStorage.getFile(to.account, to.uuid)
?.apply { mkdirs() }
?.let { File(it, task.obj!!) }
?: return
source.copyTo(target, overwrite = true)
source.delete()
}
suspend fun getVtodo(caldavTask: CaldavTask?): String? {
if (caldavTask == null) {
@ -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
val directory =
fileStorage
.getFile(calendar.account, caldavTask.calendar)
?.apply { mkdirs() }
?: return
fileStorage.write(File(directory, `object`), vtodo)
withContext(Dispatchers.IO) {
val directory =
fileStorage
.getFile(calendar.account, caldavTask.calendar)
?.apply { mkdirs() }
?: 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,7 +467,9 @@ object Migrations {
?: continue
val `object` = it.getTextOrNull(2) ?: continue
val data = it.getTextOrNull(3) ?: continue
fileStorage.write(File(file, `object`), data)
runBlocking {
fileStorage.write(File(file, `object`), data)
}
}
}
connection.execSQL("ALTER TABLE `caldav_tasks` RENAME TO `caldav_tasks-temp`")

@ -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,10 +142,12 @@ class Advanced : InjectingPreferenceFragment() {
.setMessage(R.string.EPr_delete_task_data_warning)
.setPositiveButton(R.string.EPr_delete_task_data) { _, _ ->
val context = requireContext()
context.deleteDatabase(database.name)
vtodoCache.clear()
EtebaseLocalCache.clear(context)
restart()
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