Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 18585c9d authored by Ricki Hirner's avatar Ricki Hirner
Browse files

Tasks: support CATEGORIES

parent a5984857
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -9,10 +9,14 @@
package at.bitfire.ical4android

import android.content.ContentValues
import android.database.MatrixCursor
import androidx.test.filters.SmallTest
import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues
import at.bitfire.ical4android.MiscUtils.TextListHelper.toList
import net.fortuna.ical4j.data.CalendarBuilder
import net.fortuna.ical4j.model.Date
import net.fortuna.ical4j.model.DateTime
import net.fortuna.ical4j.model.TextList
import net.fortuna.ical4j.model.TimeZone
import net.fortuna.ical4j.model.component.VTimeZone
import net.fortuna.ical4j.model.property.DtStart
@@ -98,6 +102,26 @@ class MiscUtilsTest {
    }


    @Test
    @SmallTest
    fun testCursorToValues() {
        val columns = arrayOf("col1", "col2")
        val c = MatrixCursor(columns)
        c.addRow(arrayOf("row1_val1", "row1_val2"))
        c.moveToFirst()
        val values = c.toValues()
        assertEquals("row1_val1", values.getAsString("col1"))
        assertEquals("row1_val2", values.getAsString("col2"))
    }

    @Test
    @SmallTest
    fun testTextListToList() {
        assertEquals(listOf("str1", "str2"), TextList(arrayOf("str1", "str2")).toList())
        assertEquals(emptyList<String>(), TextList(arrayOf()).toList())
    }


    @Suppress("unused")
    private class TestClass {
        private val s = "test"
+3 −6
Original line number Diff line number Diff line
@@ -12,10 +12,10 @@ import android.accounts.Account
import android.content.ContentProviderClient
import android.content.ContentUris
import android.content.ContentValues
import android.database.DatabaseUtils
import android.net.Uri
import android.provider.CalendarContract
import android.provider.CalendarContract.*
import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues
import java.io.FileNotFoundException
import java.util.*
import java.util.logging.Level
@@ -177,11 +177,8 @@ abstract class AndroidCalendar<out T: AndroidEvent>(

        val events = LinkedList<T>()
        provider.query(eventsSyncURI(), null, where, whereArgs, null)?.use { cursor ->
            while (cursor.moveToNext()) {
                val values = ContentValues(cursor.columnCount)
                DatabaseUtils.cursorRowToContentValues(cursor, values)
                events += eventFactory.fromProvider(this, values)
            }
            while (cursor.moveToNext())
                events += eventFactory.fromProvider(this, cursor.toValues())
        }
        return events
    }
+2 −3
Original line number Diff line number Diff line
@@ -13,12 +13,12 @@ import android.content.ContentProviderOperation.Builder
import android.content.ContentUris
import android.content.ContentValues
import android.content.EntityIterator
import android.database.DatabaseUtils
import android.net.Uri
import android.os.RemoteException
import android.provider.CalendarContract
import android.provider.CalendarContract.*
import android.util.Base64
import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues
import net.fortuna.ical4j.model.*
import net.fortuna.ical4j.model.Date
import net.fortuna.ical4j.model.TimeZone
@@ -361,8 +361,7 @@ abstract class AndroidEvent(
                null,
                Events.ORIGINAL_ID + "=?", arrayOf(id.toString()), null)?.use { c ->
            while (c.moveToNext()) {
                val values = ContentValues(c.columnCount)
                DatabaseUtils.cursorRowToContentValues(c, values)
                val values = c.toValues()
                try {
                    val exception = calendar.eventFactory.fromProvider(calendar, values)

+61 −7
Original line number Diff line number Diff line
@@ -12,14 +12,16 @@ import android.content.ContentProviderOperation
import android.content.ContentProviderOperation.Builder
import android.content.ContentUris
import android.content.ContentValues
import android.database.DatabaseUtils
import android.net.Uri
import android.os.RemoteException
import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues
import net.fortuna.ical4j.model.Date
import net.fortuna.ical4j.model.DateTime
import net.fortuna.ical4j.model.Dur
import net.fortuna.ical4j.model.property.*
import org.dmfs.tasks.contract.TaskContract.Tasks
import org.dmfs.tasks.contract.TaskContract.*
import org.dmfs.tasks.contract.TaskContract.Properties
import org.dmfs.tasks.contract.TaskContract.Property.Category
import java.io.FileNotFoundException
import java.net.URI
import java.net.URISyntaxException
@@ -66,11 +68,25 @@ abstract class AndroidTask(
            val id = requireNotNull(id)

            task = Task()
            taskList.provider.client.query(taskSyncURI(), null, null, null, null)?.use { cursor ->
            val client = taskList.provider.client
            client.query(taskSyncURI(), null, null, null, null)?.use { cursor ->
                if (cursor.moveToFirst()) {
                    val values = ContentValues(cursor.columnCount)
                    DatabaseUtils.cursorRowToContentValues(cursor, values)
                    val values = cursor.toValues()
                    Constants.log.log(Level.FINER, "Found task", values)
                    populateTask(values)

                    if (values.getAsInteger(Tasks.HAS_PROPERTIES) != 0)
                        // fetch properties
                        client.query(taskList.tasksPropertiesSyncUri(), null,
                                "${Properties.TASK_ID}=?", arrayOf(id.toString()),
                                null)?.use { propCursor ->
                            while (propCursor.moveToNext()) {
                                val propValues = propCursor.toValues()
                                Constants.log.log(Level.FINER, "Found property", propValues)
                                populateProperty(propValues)
                            }
                        }

                    return task
                }
            }
@@ -161,6 +177,17 @@ abstract class AndroidTask(
        values.getAsString(Tasks.RRULE)?.let { task.rRule = RRule(it) }
    }

    protected open fun populateProperty(values: ContentValues) {
        val task = requireNotNull(task)
        val type = values.getAsString(Properties.MIMETYPE)
        when (type) {
            Category.CONTENT_ITEM_TYPE ->
                task.categories += values.getAsString(Category.CATEGORY_NAME)
            else ->
                Constants.log.warning("Found unknown property of type $type")
        }
    }


    fun add(): Uri {
        val batch = BatchOperation(taskList.provider.client)
@@ -169,8 +196,13 @@ abstract class AndroidTask(
        batch.enqueue(BatchOperation.Operation(builder))
        batch.commit()

        // TODO use backref mechanism so that only one commit is required for the whole task
        val result = batch.getResult(0) ?: throw CalendarStorageException("Empty result from provider when adding a task")
        id = ContentUris.parseId(result.uri)

        insertProperties(batch)
        batch.commit()

        return result.uri
    }

@@ -182,10 +214,30 @@ abstract class AndroidTask(
        val builder = ContentProviderOperation.newUpdate(uri)
        buildTask(builder, true)
        batch.enqueue(BatchOperation.Operation(builder))

        val deleteProperties = ContentProviderOperation.newDelete(taskList.tasksPropertiesSyncUri())
                .withSelection("${Properties.TASK_ID}=?", arrayOf(id.toString()))
        batch.enqueue(BatchOperation.Operation(deleteProperties))
        insertProperties(batch)

        batch.commit()
        return uri
    }

    private fun insertProperties(batch: BatchOperation) {
        val task = requireNotNull(task)

        // insert categories
        for (category in task.categories) {
            val builder = ContentProviderOperation.newInsert(taskList.tasksPropertiesSyncUri())
            builder .withValue(Category.TASK_ID, id)
                    .withValue(Category.MIMETYPE, Category.CONTENT_ITEM_TYPE)
                    .withValue(Category.CATEGORY_NAME, category)
            Constants.log.log(Level.FINE, "Inserting category", builder.build())
            batch.enqueue(BatchOperation.Operation(builder))
        }
    }

    fun delete(): Int {
        try {
            return taskList.provider.client.delete(taskSyncURI(), null, null)
@@ -268,7 +320,6 @@ abstract class AndroidTask(
                .withValue(Tasks.RRULE, task.rRule?.value)

        builder .withValue(Tasks.EXDATE, if (task.exDates.isEmpty()) null else DateUtils.recurrenceSetsToAndroidString(task.exDates, allDay))

        Constants.log.log(Level.FINE, "Built task object", builder.build())
    }

@@ -286,7 +337,10 @@ abstract class AndroidTask(

    protected fun taskSyncURI(): Uri {
        val id = requireNotNull(id)
        return ContentUris.withAppendedId(taskList.tasksSyncUri(), id)
        val builder = taskList.tasksSyncUri().buildUpon()
        return ContentUris.appendId(builder, id)
                .appendQueryParameter(LOAD_PROPERTIES, "1")
                .build()
    }


+7 −11
Original line number Diff line number Diff line
@@ -12,14 +12,15 @@ import android.accounts.Account
import android.content.ContentUris
import android.content.ContentValues
import android.content.Context
import android.database.DatabaseUtils
import android.net.Uri
import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues
import org.dmfs.tasks.contract.TaskContract
import org.dmfs.tasks.contract.TaskContract.TaskLists
import org.dmfs.tasks.contract.TaskContract.Tasks
import java.io.FileNotFoundException
import java.util.*


/**
 * Represents a locally stored task list, containing AndroidTasks (whose data objects are Tasks).
 * Communicates with third-party content providers to store the tasks.
@@ -67,9 +68,7 @@ abstract class AndroidTaskList<out T: AndroidTask>(
            provider.client.query(TaskProvider.syncAdapterUri(ContentUris.withAppendedId(provider.taskListsUri(), id), account), null, null, null, null)?.use { cursor ->
                if (cursor.moveToNext()) {
                    val taskList = factory.newInstance(account, provider, id)
                    val values = ContentValues(cursor.columnCount)
                    DatabaseUtils.cursorRowToContentValues(cursor, values)
                    taskList.populate(values)
                    taskList.populate(cursor.toValues())
                    return taskList
                }
            }
@@ -80,8 +79,7 @@ abstract class AndroidTaskList<out T: AndroidTask>(
            val taskLists = LinkedList<T>()
            provider.client.query(TaskProvider.syncAdapterUri(provider.taskListsUri(), account), null, where, whereArgs, null)?.use { cursor ->
                while (cursor.moveToNext()) {
                    val values = ContentValues(cursor.columnCount)
                    DatabaseUtils.cursorRowToContentValues(cursor, values)
                    val values = cursor.toValues()
                    val taskList = factory.newInstance(account, provider, values.getAsLong(TaskLists._ID))
                    taskList.populate(values)
                    taskLists += taskList
@@ -127,11 +125,8 @@ abstract class AndroidTaskList<out T: AndroidTask>(
                tasksSyncUri(),
                null,
                where, whereArgs, null)?.use { cursor ->
            while (cursor.moveToNext()) {
                val values = ContentValues(cursor.columnCount)
                DatabaseUtils.cursorRowToContentValues(cursor, values)
                tasks += taskFactory.fromProvider(this, values)
            }
            while (cursor.moveToNext())
                tasks += taskFactory.fromProvider(this, cursor.toValues())
        }
        return tasks
    }
@@ -142,5 +137,6 @@ abstract class AndroidTaskList<out T: AndroidTask>(

    fun taskListSyncUri() = TaskProvider.syncAdapterUri(ContentUris.withAppendedId(provider.taskListsUri(), id), account)
    fun tasksSyncUri() = TaskProvider.syncAdapterUri(provider.tasksUri(), account)
    fun tasksPropertiesSyncUri() = TaskProvider.syncAdapterUri(provider.propertiesUri(), account)

}
Loading