Finish converting data module to kmp

pull/2898/head
Alex Baker 4 weeks ago
parent 19de0e08a5
commit 4ddfe937b0

@ -193,7 +193,7 @@ class TaskCreator @Inject constructor(
} catch (e: Throwable) {
Timber.e(e)
}
task.setTags(tags)
task.putTransitory(Tag.KEY, tags)
return task
}

@ -17,6 +17,7 @@ import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.entity.Task
import org.tasks.data.inTransaction
import org.tasks.data.pictureUri
import org.tasks.data.withTransaction
import org.tasks.files.FileHelper
import org.tasks.location.GeofenceApi

@ -20,6 +20,7 @@ import org.tasks.caldav.iCalendar.Companion.order
import org.tasks.caldav.iCalendar.Companion.parent
import org.tasks.data.CaldavTaskContainer
import org.tasks.data.Location
import org.tasks.data.convertPictureUri
import org.tasks.data.dao.CaldavDao
import org.tasks.data.dao.FilterDao
import org.tasks.data.dao.GoogleTaskListDao

@ -22,6 +22,7 @@ import kotlinx.serialization.json.jsonPrimitive
import org.tasks.LocalBroadcastManager
import org.tasks.R
import org.tasks.caldav.VtodoCache
import org.tasks.data.convertPictureUri
import org.tasks.data.dao.AlarmDao
import org.tasks.data.dao.CaldavDao
import org.tasks.data.dao.FilterDao

@ -2,7 +2,11 @@ package org.tasks.compose.edit
import android.net.Uri
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
@ -15,7 +19,8 @@ import org.tasks.R
import org.tasks.compose.DeleteButton
import org.tasks.compose.TaskEditRow
import org.tasks.data.entity.UserActivity
import java.util.*
import org.tasks.data.pictureUri
import java.util.Locale
@Composable
fun CommentsRow(

@ -0,0 +1,41 @@
package org.tasks.data
import android.net.Uri
import org.json.JSONException
import org.json.JSONObject
import org.tasks.data.entity.UserActivity
import timber.log.Timber
import java.io.File
fun UserActivity.setPicture(uri: Uri?) {
picture = uri?.toString()
}
val UserActivity.pictureUri: Uri?
get() = if (picture.isNullOrBlank()) null else Uri.parse(picture)
fun UserActivity.convertPictureUri() {
setPicture(getLegacyPictureUri(picture))
}
private fun getLegacyPictureUri(value: String?): Uri? {
return try {
if (value.isNullOrBlank()) {
return null
}
if (value.contains("uri") || value.contains("path")) {
val json = JSONObject(value)
if (json.has("uri")) {
return Uri.parse(json.getString("uri"))
}
if (json.has("path")) {
val path = json.getString("path")
return Uri.fromFile(File(path))
}
}
null
} catch (e: JSONException) {
Timber.e(e)
null
}
}

@ -57,6 +57,7 @@ import org.tasks.data.entity.Task.Companion.NOTIFY_MODE_NONSTOP
import org.tasks.data.entity.Task.Companion.hasDueTime
import org.tasks.data.entity.TaskAttachment
import org.tasks.data.entity.UserActivity
import org.tasks.data.setPicture
import org.tasks.date.DateTimeUtils.toDateTime
import org.tasks.files.FileHelper
import org.tasks.location.GeofenceApi

@ -2,6 +2,7 @@ plugins {
alias(libs.plugins.ksp).apply(false)
alias(libs.plugins.androidLibrary).apply(false)
alias(libs.plugins.kotlinMultiplatform).apply(false)
alias(libs.plugins.jetbrains.kotlin.jvm) apply false
}
buildscript {

@ -27,6 +27,7 @@ kotlin {
}
}
}
jvm()
sourceSets {
commonMain.dependencies {
implementation(libs.androidx.room)
@ -34,6 +35,7 @@ kotlin {
implementation(libs.kermit)
}
}
task("testClasses")
}
android {
namespace = "org.tasks.data"

@ -0,0 +1,13 @@
@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
package org.tasks
import android.os.Parcelable
import kotlinx.parcelize.RawValue
import org.tasks.data.BuildConfig
actual typealias CommonParcelable = Parcelable
actual typealias CommonRawValue = RawValue
actual val IS_DEBUG = BuildConfig.DEBUG

@ -1,7 +0,0 @@
@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
package org.tasks
import android.os.Parcelable
actual typealias CommonParcelable = Parcelable

@ -6,4 +6,10 @@ package org.tasks
@Retention(AnnotationRetention.BINARY)
annotation class CommonParcelize
expect interface CommonParcelable
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.BINARY)
expect annotation class CommonRawValue()
expect interface CommonParcelable
expect val IS_DEBUG: Boolean

@ -114,7 +114,7 @@ fun SQLiteStatement.getTasks(): List<TaskContainer> {
remoteOrder = getLong(_cursorIndexOfRemoteOrder),
)
}
val accountType = getInt(_cursorIndexOfAccountType)
val accountType = getIntOrNull(_cursorIndexOfAccountType) ?: 0
val geofence = getLongOrNull(_cursorIndexOfId_2)?.takeIf { it > 0 }?.let {
Geofence(
id = it,
@ -156,7 +156,7 @@ fun SQLiteStatement.getTasks(): List<TaskContainer> {
children = getIntOrNull(_cursorIndexOfChildren) ?: 0,
primarySort = getLongOrNull(_cursorIndexOfPrimarySort) ?: 0,
secondarySort = getLongOrNull(_cursorIndexOfSecondarySort) ?: 0,
parentComplete = getBoolean(_cursorIndexOfParentComplete),
parentComplete = getBooleanOrNull(_cursorIndexOfParentComplete) ?: false,
)
)
}
@ -171,3 +171,6 @@ private fun SQLiteStatement.getLongOrNull(index: Int): Long? =
private fun SQLiteStatement.getIntOrNull(index: Int): Int? =
if (index == -1 || isNull(index)) null else this.getInt(index)
private fun SQLiteStatement.getBooleanOrNull(index: Int): Boolean? =
if (index == -1 || isNull(index)) null else this.getBoolean(index)

@ -4,13 +4,13 @@ import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
import kotlinx.coroutines.flow.Flow
import org.tasks.data.CaldavFilters
import org.tasks.data.CaldavTaskContainer
import org.tasks.data.NO_ORDER
import org.tasks.data.TaskContainer
import org.tasks.data.db.Database
import org.tasks.data.db.DbUtils.dbchunk
import org.tasks.data.db.SuspendDbUtils.chunkedMap
import org.tasks.data.entity.CaldavAccount
@ -22,12 +22,13 @@ import org.tasks.data.entity.CaldavAccount.Companion.TYPE_TASKS
import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.entity.CaldavTask
import org.tasks.data.entity.Task
import org.tasks.data.withTransaction
import org.tasks.time.DateTimeUtils2.currentTimeMillis
const val APPLE_EPOCH = 978307200000L // 1/1/2001 GMT
@Dao
abstract class CaldavDao {
abstract class CaldavDao(private val database: Database) {
@Query("SELECT COUNT(*) FROM caldav_lists WHERE cdl_account = :account")
abstract suspend fun listCount(account: String): Int
@ -98,24 +99,24 @@ ORDER BY CASE cda_account_type
@Update
abstract suspend fun update(caldavCalendar: CaldavCalendar)
@Transaction
open suspend fun insert(task: Task, caldavTask: CaldavTask, addToTop: Boolean): Long {
if (task.order != null) {
return insert(caldavTask)
}
if (addToTop) {
task.order = findFirstTask(caldavTask.calendar!!, task.parent)
suspend fun insert(task: Task, caldavTask: CaldavTask, addToTop: Boolean): Long =
database.withTransaction {
if (task.order != null) {
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)
} else {
task.order = findLastTask(caldavTask.calendar!!, task.parent)
?.takeIf { task.creationDate.toAppleEpoch() <= it }
?.plus(1)
}
val id = insert(caldavTask)
update(task)
id
}
val id = insert(caldavTask)
update(task)
return id
}
@Query("""
SELECT MIN(IFNULL(`order`, (created - $APPLE_EPOCH) / 1000))
@ -311,46 +312,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)")
abstract suspend fun updateParents(calendar: String)
@Transaction
open suspend fun move(
suspend fun move(
task: TaskContainer,
previousParent: Long,
newParent: Long,
newPosition: Long?,
) {
val previousPosition = task.caldavSortOrder
if (newPosition != null) {
if (newParent == previousParent && newPosition < previousPosition) {
shiftDown(task.caldav!!, newParent, newPosition, previousPosition)
} else {
val list = newParent.takeIf { it > 0 }?.let { getTask(it)?.calendar } ?: task.caldav!!
shiftDown(list, newParent, newPosition)
database.withTransaction {
val previousPosition = task.caldavSortOrder
if (newPosition != null) {
if (newParent == previousParent && newPosition < previousPosition) {
shiftDown(task.caldav!!, newParent, newPosition, previousPosition)
} else {
val list =
newParent.takeIf { it > 0 }?.let { getTask(it)?.calendar } ?: task.caldav!!
shiftDown(list, newParent, newPosition)
}
}
task.task.order = newPosition
setTaskOrder(task.id, newPosition)
}
task.task.order = newPosition
setTaskOrder(task.id, newPosition)
}
@Transaction
open suspend fun shiftDown(calendar: String, parent: Long, from: Long, to: Long? = null) {
val updated = ArrayList<Task>()
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 task = task.task
task.order = current + 1
updated.add(task)
} else if (task.sortOrder > current) {
break
suspend fun shiftDown(calendar: String, parent: Long, from: Long, to: Long? = null) {
database.withTransaction {
val updated = ArrayList<Task>()
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 task = task.task
task.order = current + 1
updated.add(task)
} else if (task.sortOrder > current) {
break
}
}
}
updateTasks(updated)
updated
updateTasks(updated)
updated
.map(Task::id)
.dbchunk()
.forEach { touchInternal(it) }
}
}
@Query("UPDATE tasks SET modified = :modificationTime WHERE _id in (:ids)")

@ -3,15 +3,16 @@ package org.tasks.data.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Query
import androidx.room.Transaction
import org.tasks.data.dao.CaldavDao.Companion.LOCAL
import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.db.Database
import org.tasks.data.db.SuspendDbUtils.chunkedMap
import org.tasks.data.db.SuspendDbUtils.eachChunk
import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.withTransaction
@Dao
abstract class DeletionDao {
abstract class DeletionDao(private val database: Database) {
@Query("DELETE FROM tasks WHERE _id IN(:ids)")
internal abstract suspend fun deleteTasks(ids: List<Long>)
@ -41,12 +42,7 @@ WHERE recurring = 1
""")
abstract suspend fun internalHasRecurringAncestors(ids: List<Long>): List<Long>
@Transaction
open suspend fun delete(ids: List<Long>) {
ids.eachChunk {
deleteTasks(it)
}
}
suspend fun delete(ids: List<Long>) { ids.eachChunk { deleteTasks(it) } }
@Query("UPDATE tasks "
+ "SET modified = (strftime('%s','now')*1000), deleted = (strftime('%s','now')*1000)"
@ -63,13 +59,13 @@ WHERE recurring = 1
@Delete
internal abstract suspend fun deleteCaldavCalendar(caldavCalendar: CaldavCalendar)
@Transaction
open suspend fun delete(caldavCalendar: CaldavCalendar): List<Long> {
val tasks = getActiveCaldavTasks(caldavCalendar.uuid!!)
delete(tasks)
deleteCaldavCalendar(caldavCalendar)
return tasks
}
suspend fun delete(caldavCalendar: CaldavCalendar): List<Long> =
database.withTransaction {
val tasks = getActiveCaldavTasks(caldavCalendar.uuid!!)
delete(tasks)
deleteCaldavCalendar(caldavCalendar)
tasks
}
@Query("SELECT * FROM caldav_lists WHERE cdl_account = :account")
abstract suspend fun getCalendars(account: String): List<CaldavCalendar>
@ -80,13 +76,13 @@ 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)")
abstract suspend fun purgeDeleted()
@Transaction
open suspend fun delete(caldavAccount: CaldavAccount): List<Long> {
val deleted = ArrayList<Long>()
for (calendar in getCalendars(caldavAccount.uuid!!)) {
deleted.addAll(delete(calendar))
suspend fun delete(caldavAccount: CaldavAccount): List<Long> =
database.withTransaction {
val deleted = ArrayList<Long>()
for (calendar in getCalendars(caldavAccount.uuid!!)) {
deleted.addAll(delete(calendar))
}
deleteCaldavAccount(caldavAccount)
deleted
}
deleteCaldavAccount(caldavAccount)
return deleted
}
}

@ -4,30 +4,32 @@ import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
import org.tasks.data.entity.Task
import org.tasks.data.db.Database
import org.tasks.data.entity.CaldavAccount.Companion.TYPE_GOOGLE_TASKS
import org.tasks.data.entity.CaldavTask
import org.tasks.data.entity.Task
import org.tasks.data.withTransaction
@Dao
abstract class GoogleTaskDao {
abstract class GoogleTaskDao(private val database: Database) {
@Insert
abstract suspend fun insert(task: CaldavTask): Long
@Insert
abstract suspend fun insert(tasks: Iterable<CaldavTask>)
@Transaction
open suspend fun insertAndShift(task: Task, caldavTask: CaldavTask, top: Boolean) {
if (top) {
task.order = 0
shiftDown(caldavTask.calendar!!, task.parent, 0)
} else {
task.order = getBottom(caldavTask.calendar!!, task.parent)
suspend fun insertAndShift(task: Task, caldavTask: CaldavTask, top: Boolean) {
database.withTransaction {
if (top) {
task.order = 0
shiftDown(caldavTask.calendar!!, task.parent, 0)
} else {
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)")
@ -42,24 +44,25 @@ abstract class GoogleTaskDao {
@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)
@Transaction
open suspend fun move(task: Task, list: String, newParent: Long, newPosition: Long) {
val previousParent = task.parent
val previousPosition = task.order!!
if (newParent == previousParent) {
if (previousPosition < newPosition) {
shiftUp(list, newParent, previousPosition, newPosition)
suspend fun move(task: Task, list: String, newParent: Long, newPosition: Long) {
database.withTransaction {
val previousParent = task.parent
val previousPosition = task.order!!
if (newParent == previousParent) {
if (previousPosition < newPosition) {
shiftUp(list, newParent, previousPosition, newPosition)
} else {
shiftDown(list, newParent, previousPosition, newPosition)
}
} else {
shiftDown(list, newParent, previousPosition, newPosition)
shiftUp(list, previousParent, previousPosition)
shiftDown(list, newParent, newPosition)
}
} else {
shiftUp(list, previousParent, previousPosition)
shiftDown(list, newParent, newPosition)
task.parent = newParent
task.order = newPosition
update(task)
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")
@ -165,26 +168,27 @@ WHERE cd_remote_id = :id
""")
abstract suspend fun updatePosition(id: String, parent: String?, position: String)
@Transaction
open suspend fun reposition(caldavDao: CaldavDao, listId: String) {
caldavDao.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.order = subtasks
update(task)
}
subtasks++
} else {
subtasks = 0
if (task.order != parent) {
task.order = parent
update(task)
suspend fun reposition(caldavDao: CaldavDao, listId: String) {
database.withTransaction {
caldavDao.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.order = subtasks
update(task)
}
subtasks++
} else {
subtasks = 0
if (task.order != parent) {
task.order = parent
update(task)
}
parent++
}
parent++
}
}
}

@ -4,39 +4,43 @@ import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
import kotlinx.coroutines.flow.Flow
import org.tasks.data.PrincipalWithAccess
import org.tasks.data.db.Database
import org.tasks.data.entity.CaldavAccount
import org.tasks.data.entity.CaldavCalendar
import org.tasks.data.entity.Principal
import org.tasks.data.entity.PrincipalAccess
import org.tasks.data.withTransaction
@Dao
interface PrincipalDao {
abstract class PrincipalDao(private val database: Database) {
@Insert
suspend fun insert(principal: Principal): Long
abstract suspend fun insert(principal: Principal): Long
@Insert
suspend fun insert(access: PrincipalAccess): Long
abstract suspend fun insert(access: PrincipalAccess): Long
@Update
suspend fun update(access: PrincipalAccess)
abstract suspend fun update(access: PrincipalAccess)
@Query("""
DELETE
FROM principal_access
WHERE list = :list
AND id NOT IN (:access)""")
suspend fun deleteRemoved(list: Long, access: List<Long>)
abstract suspend fun deleteRemoved(list: Long, access: List<Long>)
@Delete
suspend fun delete(access: PrincipalAccess)
abstract suspend fun delete(access: PrincipalAccess)
suspend fun getAll(): List<PrincipalWithAccess> = database.withTransaction {
getAllInternal()
}
@Transaction
@Query("SELECT * FROM principal_access")
suspend fun getAll(): List<PrincipalWithAccess>
internal abstract suspend fun getAllInternal(): List<PrincipalWithAccess>
suspend fun getOrCreatePrincipal(account: CaldavAccount, href: String, displayName: String? = null) =
findPrincipal(account.id, href)
@ -65,12 +69,12 @@ WHERE list = :list
).apply { id = insert(this) }
@Query("SELECT * FROM principals WHERE account = :account AND href = :href")
suspend fun findPrincipal(account: Long, href: String): Principal?
abstract suspend fun findPrincipal(account: Long, href: String): Principal?
@Query("SELECT * FROM principal_access WHERE list = :list and principal = :principal")
suspend fun findAccess(list: Long, principal: Long): PrincipalAccess?
abstract suspend fun findAccess(list: Long, principal: Long): PrincipalAccess?
@Transaction
@Query("SELECT * FROM principal_access WHERE list = :id")
fun getPrincipals(id: Long): Flow<List<PrincipalWithAccess>>
abstract fun getPrincipals(id: Long): Flow<List<PrincipalWithAccess>>
}

@ -4,13 +4,14 @@ import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction
import org.tasks.data.db.Database
import org.tasks.data.entity.Tag
import org.tasks.data.entity.TagData
import org.tasks.data.entity.Task
import org.tasks.data.withTransaction
@Dao
abstract class TagDao {
abstract class TagDao(private val database: Database) {
@Query("UPDATE tags SET name = :name WHERE tag_uid = :tagUid")
abstract suspend fun rename(tagUid: String, name: String)
@ -35,15 +36,16 @@ abstract class TagDao {
@Delete
abstract suspend fun delete(tags: List<Tag>)
@Transaction
open suspend fun applyTags(task: Task, tagDataDao: TagDataDao, current: List<TagData>) {
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)
database.withTransaction {
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)
}
}
suspend fun insert(task: Task, tags: Collection<TagData>) {

@ -4,19 +4,20 @@ import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
import kotlinx.coroutines.flow.Flow
import org.tasks.data.NO_ORDER
import org.tasks.data.TagFilters
import org.tasks.data.db.Database
import org.tasks.data.db.DbUtils
import org.tasks.data.entity.Tag
import org.tasks.data.entity.TagData
import org.tasks.data.entity.Task
import org.tasks.data.withTransaction
import org.tasks.time.DateTimeUtils2.currentTimeMillis
@Dao
abstract class TagDataDao {
abstract class TagDataDao(private val database: Database) {
@Query("SELECT * FROM tagdata")
abstract fun subscribeToTags(): Flow<List<TagData>>
@ -79,9 +80,11 @@ abstract class TagDataDao {
+ " GROUP BY tasks._id")
internal abstract suspend fun getAllTags(tasks: List<Long>): List<String?>
@Transaction
open suspend fun applyTags(
tasks: List<Task>, partiallySelected: List<TagData>, selected: List<TagData>): List<Long> {
suspend fun applyTags(
tasks: List<Task>,
partiallySelected: List<TagData>,
selected: List<TagData>
): List<Long> = database.withTransaction {
val modified = HashSet<Long>()
val keep = partiallySelected.plus(selected).map { it.remoteId!! }
for (sublist in tasks.chunked(DbUtils.MAX_SQLITE_ARGS - keep.size)) {
@ -105,13 +108,14 @@ abstract class TagDataDao {
)
}
}
return ArrayList(modified)
ArrayList(modified)
}
@Transaction
open suspend fun delete(tagData: TagData) {
deleteTags(tagData.remoteId!!)
deleteTagData(tagData)
suspend fun delete(tagData: TagData) {
database.withTransaction {
deleteTags(tagData.remoteId!!)
deleteTagData(tagData)
}
}
@Delete

@ -6,7 +6,7 @@ import androidx.room.Query
import androidx.room.Update
import androidx.room.execSQL
import co.touchlab.kermit.Logger
import org.tasks.data.BuildConfig
import org.tasks.IS_DEBUG
import org.tasks.data.TaskContainer
import org.tasks.data.UUIDHelper
import org.tasks.data.db.Database
@ -111,7 +111,7 @@ FROM (
open suspend fun fetchTasks(callback: suspend () -> List<String>): List<TaskContainer> =
database.withTransaction {
val start = if (BuildConfig.DEBUG) DateTimeUtils2.currentTimeMillis() else 0
val start = if (IS_DEBUG) DateTimeUtils2.currentTimeMillis() else 0
val queries = callback()
val last = queries.size - 1
for (i in 0 until last) {
@ -205,7 +205,7 @@ FROM recursive_tasks
if (Task.isUuidEmpty(task.remoteId)) {
task.remoteId = UUIDHelper.newUUID()
}
if (BuildConfig.DEBUG) {
if (IS_DEBUG) {
require(task.remoteId?.isNotBlank() == true && task.remoteId != "0")
}
val insert = insert(task)

@ -1,18 +1,18 @@
package org.tasks.data.entity
import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.tasks.CommonParcelable
import org.tasks.CommonParcelize
import org.tasks.data.db.Table
import org.tasks.time.printTimestamp
import java.util.concurrent.TimeUnit
@Parcelize
@CommonParcelize
@Serializable
@Entity(
tableName = Alarm.TABLE_NAME,
@ -41,7 +41,7 @@ data class Alarm(
val repeat: Int = 0,
@ColumnInfo(name = "interval", defaultValue = "0")
val interval: Long = 0,
) : Parcelable {
) : CommonParcelable {
fun same(other: Alarm) =
type == other.type &&
time == other.time &&

@ -1,17 +1,17 @@
package org.tasks.data.entity
import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.tasks.CommonParcelable
import org.tasks.CommonParcelize
import org.tasks.data.db.Table
import java.net.HttpURLConnection
@Serializable
@Parcelize
@CommonParcelize
@Entity(tableName = "caldav_accounts")
data class CaldavAccount(
@PrimaryKey(autoGenerate = true)
@ -38,7 +38,7 @@ data class CaldavAccount(
val isCollapsed: Boolean = false,
@ColumnInfo(name = "cda_server_type")
var serverType: Int = SERVER_UNKNOWN,
) : Parcelable {
) : CommonParcelable {
val isCaldavAccount: Boolean
get() = accountType == TYPE_CALDAV

@ -1,18 +1,18 @@
package org.tasks.data.entity
import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.tasks.CommonParcelable
import org.tasks.CommonParcelize
import org.tasks.data.LIST
import org.tasks.data.NO_ORDER
import org.tasks.data.db.Table
@Serializable
@Parcelize
@CommonParcelize
@Entity(tableName = "caldav_lists")
data class CaldavCalendar(
@PrimaryKey(autoGenerate = true)
@ -28,7 +28,7 @@ data class CaldavCalendar(
@ColumnInfo(name = "cdl_order") val order: Int = NO_ORDER,
@ColumnInfo(name = "cdl_access") var access: Int = ACCESS_OWNER,
@ColumnInfo(name = "cdl_last_sync") val lastSync: Long = 0,
) : Parcelable {
) : CommonParcelable {
@Suppress("RedundantNullableReturnType")
fun getIcon(): Int? {
return (if (icon == null) LIST else icon!!)

@ -1,16 +1,16 @@
package org.tasks.data.entity
import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.tasks.CommonParcelable
import org.tasks.CommonParcelize
import org.tasks.data.NO_ORDER
@Serializable
@Parcelize
@CommonParcelize
@Entity(tableName = "filters")
data class Filter(
@PrimaryKey(autoGenerate = true)
@ -31,4 +31,4 @@ data class Filter(
val icon: Int? = -1,
@ColumnInfo(name = "f_order")
val order: Int = NO_ORDER,
) : Parcelable
) : CommonParcelable

@ -1,6 +1,5 @@
package org.tasks.data.entity
import android.os.Parcelable
import androidx.annotation.IntDef
import androidx.room.ColumnInfo
import androidx.room.Entity
@ -8,13 +7,14 @@ import androidx.room.Ignore
import androidx.room.Index
import androidx.room.PrimaryKey
import co.touchlab.kermit.Logger
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.RawValue
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.json.JsonNames
import org.tasks.CommonParcelable
import org.tasks.CommonParcelize
import org.tasks.CommonRawValue
import org.tasks.data.db.Table
import org.tasks.data.sql.Field
@ -22,7 +22,7 @@ const val SUPPRESS_SYNC = "suppress_sync"
const val FORCE_CALDAV_SYNC = "force_caldav_sync"
@Serializable
@Parcelize
@CommonParcelize
@Entity(
tableName = Task.TABLE_NAME,
indices = [
@ -82,8 +82,8 @@ data class Task @OptIn(ExperimentalSerializationApi::class) constructor(
var readOnly: Boolean = false,
@Ignore
@Transient
private var transitoryData: @RawValue HashMap<String, Any>? = null,
) : Parcelable {
private var transitoryData: @CommonRawValue HashMap<String, Any>? = null,
) : CommonParcelable {
var uuid: String
get() = if (remoteId.isNullOrEmpty()) NO_UUID else remoteId!!
set(uuid) {
@ -237,15 +237,8 @@ data class Task @OptIn(ExperimentalSerializationApi::class) constructor(
return getTransitory(Tag.KEY) ?: ArrayList()
}
fun setTags(tags: ArrayList<String>) {
if (transitoryData == null) {
transitoryData = HashMap()
}
transitoryData!![Tag.KEY] = tags
}
fun hasTransitory(key: String?): Boolean {
return transitoryData != null && transitoryData!!.containsKey(key)
return transitoryData?.containsKey(key) == true
}
fun <T> getTransitory(key: String?): T? = transitoryData?.get(key) as T?

@ -1,16 +1,16 @@
package org.tasks.data.entity
import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.tasks.CommonParcelable
import org.tasks.CommonParcelize
import org.tasks.data.UUIDHelper
@Serializable
@Parcelize
@CommonParcelize
@Entity(tableName = "attachment_file")
data class TaskAttachment(
@PrimaryKey(autoGenerate = true)
@ -23,7 +23,7 @@ data class TaskAttachment(
val name: String,
@ColumnInfo(name = "uri")
val uri: String,
) : Parcelable {
) : CommonParcelable {
companion object {
const val KEY = "attachment"
const val FILES_DIRECTORY_DEFAULT = "attachments"

@ -1,112 +1,37 @@
package org.tasks.data.entity
import android.net.Uri
import android.os.Parcel
import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey
import co.touchlab.kermit.Logger
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.json.JSONException
import org.json.JSONObject
import org.tasks.CommonParcelable
import org.tasks.CommonParcelize
import org.tasks.data.db.Table
import java.io.File
@Serializable
@CommonParcelize
@Entity(tableName = "userActivity")
class UserActivity : Parcelable {
data class UserActivity(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "_id")
@Transient
var id: Long? = null
var id: Long? = null,
@ColumnInfo(name = "remoteId")
var remoteId: String? = Task.NO_UUID
var remoteId: String? = Task.NO_UUID,
@ColumnInfo(name = "message")
var message: String? = ""
var message: String? = "",
@ColumnInfo(name = "picture")
var picture: String? = ""
var picture: String? = "",
@ColumnInfo(name = "target_id")
@Transient
var targetId: String? = Task.NO_UUID
var targetId: String? = Task.NO_UUID,
@ColumnInfo(name = "created_at")
var created: Long? = 0L
constructor()
@Ignore
private constructor(parcel: Parcel) {
with(parcel) {
id = readLong()
remoteId = readString()
message = readString()
picture = readString()
targetId = readString()
created = readLong()
}
}
fun setPicture(uri: Uri?) {
picture = uri?.toString()
}
val pictureUri: Uri?
get() = if (picture.isNullOrBlank()) null else Uri.parse(picture)
fun convertPictureUri() {
setPicture(getLegacyPictureUri(picture))
}
override fun describeContents() = 0
override fun writeToParcel(dest: Parcel, flags: Int) {
with(dest) {
writeLong(id!!)
writeString(remoteId)
writeString(message)
writeString(picture)
writeString(targetId)
writeLong(created!!)
}
}
var created: Long? = 0L,
) : CommonParcelable {
companion object {
@JvmField val TABLE = Table("userActivity")
@JvmField val TASK = TABLE.column("target_id")
@JvmField val MESSAGE = TABLE.column("message")
@JvmField val CREATOR: Parcelable.Creator<UserActivity> = object : Parcelable.Creator<UserActivity> {
override fun createFromParcel(source: Parcel): UserActivity = UserActivity(source)
override fun newArray(size: Int): Array<UserActivity?> = arrayOfNulls(size)
}
private fun getLegacyPictureUri(value: String?): Uri? {
return try {
if (value.isNullOrBlank()) {
return null
}
if (value.contains("uri") || value.contains("path")) {
val json = JSONObject(value)
if (json.has("uri")) {
return Uri.parse(json.getString("uri"))
}
if (json.has("path")) {
val path = json.getString("path")
return Uri.fromFile(File(path))
}
}
null
} catch (e: JSONException) {
Logger.e("Failed to parse picture uri", e, tag = "UserActivity")
null
}
}
}
}

@ -1,6 +1,6 @@
package org.tasks.time
import org.tasks.data.BuildConfig
import org.tasks.IS_DEBUG
import java.util.Date
object DateTimeUtils2 {
@ -24,4 +24,4 @@ object DateTimeUtils2 {
}
fun printTimestamp(timestamp: Long): String =
if (BuildConfig.DEBUG) Date(timestamp).toString() else timestamp.toString()
if (IS_DEBUG) Date(timestamp).toString() else timestamp.toString()

@ -0,0 +1,13 @@
@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
package org.tasks
actual interface CommonParcelable
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.BINARY)
annotation class RawValue
actual typealias CommonRawValue = RawValue
actual val IS_DEBUG = false

@ -164,3 +164,4 @@ kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref =
kotlin-compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version = "2.0.0-1.0.21" }
jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }

Loading…
Cancel
Save