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

Commit c1442429 authored by Ricki Hirner's avatar Ricki Hirner
Browse files

Events: process CATEGORIES explicitly (and not only as unknown properties)

- process/store CATEGORIES in Event data class
- AndroidEvent reads/saves categories in the same format used by the AOSP Exchange ActiveSync adapter
- ICalPreprocessor: rewrite invalid CREATED properties which are not in UTC to UTC
parent a3dc0d06
Loading
Loading
Loading
Loading
+0 −9
Original line number Diff line number Diff line
@@ -12,11 +12,9 @@ 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
@@ -114,13 +112,6 @@ class MiscUtilsTest {
        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 {
+34 −1
Original line number Diff line number Diff line
@@ -57,6 +57,18 @@ abstract class AndroidEvent(
        @Deprecated("New content item MIME type", ReplaceWith("UnknownProperty.CONTENT_ITEM_TYPE"))
        const val EXT_UNKNOWN_PROPERTY2 = "unknown-property.v2"

        /**
         * VEVENT CATEGORIES will be stored as an extended property with this [ExtendedProperties.NAME].
         *
         * The [ExtendedProperties.VALUE] format is the same as used by the AOSP Exchange ActiveSync adapter:
         * the category values are stored as list, separated by [EXT_CATEGORIES_SEPARATOR]. (If a category
         * value contains [EXT_CATEGORIES_SEPARATOR], [EXT_CATEGORIES_SEPARATOR] will be dropped.)
         *
         * Example: `Cat1\Cat2`
         */
        const val EXT_CATEGORIES = "categories"
        const val EXT_CATEGORIES_SEPARATOR = '\\'

        /**
         * EMAIL parameter name (as used for ORGANIZER). Not declared in ical4j Parameters class yet.
         */
@@ -340,11 +352,17 @@ abstract class AndroidEvent(
    }

    protected open fun populateExtended(row: ContentValues) {
        Constants.log.log(Level.FINE, "Read extended property from calender provider", row.getAsString(ExtendedProperties.NAME))
        val name = row.getAsString(ExtendedProperties.NAME)
        Constants.log.log(Level.FINE, "Read extended property from calender provider (name=$name)")
        val event = requireNotNull(event)

        try {
            when (row.getAsString(ExtendedProperties.NAME)) {
                EXT_CATEGORIES -> {
                    val rawCategories = row.getAsString(ExtendedProperties.VALUE)
                    event.categories += rawCategories.split(EXT_CATEGORIES_SEPARATOR)
                }

                EXT_UNKNOWN_PROPERTY -> {
                    // deserialize unknown property (deprecated format)
                    val stream = ByteArrayInputStream(Base64.decode(row.getAsString(ExtendedProperties.VALUE), Base64.NO_WRAP))
@@ -426,6 +444,8 @@ abstract class AndroidEvent(

        // add unknown properties
        retainClassification()
        if (event.categories.isNotEmpty())
            insertCategories(batch, idxEvent)
        event.unknownProperties.forEach { insertUnknownProperty(batch, idxEvent, it) }

        // add exceptions
@@ -711,6 +731,18 @@ abstract class AndroidEvent(
        batch.enqueue(BatchOperation.Operation(builder, Attendees.EVENT_ID, idxEvent))
    }

    protected open fun insertCategories(batch: BatchOperation, idxEvent: Int) {
        val rawCategories = event!!.categories
                .map { it.filter { it != EXT_CATEGORIES_SEPARATOR } }   // drop backslashes
                .joinToString(EXT_CATEGORIES_SEPARATOR.toString())      // concatenate, separate by backslash
        val builder = ContentProviderOperation.newInsert(calendar.syncAdapterURI(ExtendedProperties.CONTENT_URI))
                .withValue(ExtendedProperties.NAME, EXT_CATEGORIES)
                .withValue(ExtendedProperties.VALUE, rawCategories)

        Constants.log.log(Level.FINE, "Built categories", builder.build())
        batch.enqueue(BatchOperation.Operation(builder, ExtendedProperties.EVENT_ID, idxEvent))
    }

    protected open fun insertUnknownProperty(batch: BatchOperation, idxEvent: Int, property: Property) {
        if (property.value.length > UnknownProperty.MAX_UNKNOWN_PROPERTY_SIZE) {
            Constants.log.warning("Ignoring unknown property with ${property.value.length} octets (too long)")
@@ -721,6 +753,7 @@ abstract class AndroidEvent(
                .withValue(ExtendedProperties.NAME, UnknownProperty.CONTENT_ITEM_TYPE)
                .withValue(ExtendedProperties.VALUE, UnknownProperty.toJsonString(property))

        Constants.log.log(Level.FINE, "Built unknown property: ${property.name}")
        batch.enqueue(BatchOperation.Operation(builder, ExtendedProperties.EVENT_ID, idxEvent))
    }

+6 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ class Event: ICalendar() {

    var lastModified: LastModified? = null

    val categories = LinkedList<String>()
    val unknownProperties = LinkedList<Property>()

    companion object {
@@ -150,6 +151,9 @@ class Event: ICalendar() {
                    is Summary -> e.summary = prop.value
                    is Location -> e.location = prop.value
                    is Description -> e.description = prop.value
                    is Categories ->
                        for (category in prop.categories)
                            e.categories += category
                    is Color -> e.color = Css3Color.fromString(prop.value)
                    is DtStart -> e.dtStart = prop
                    is DtEnd -> e.dtEnd = prop
@@ -271,6 +275,8 @@ class Event: ICalendar() {
        organizer?.let { props += it }
        props.addAll(attendees)

        if (categories.isNotEmpty())
            props += Categories(TextList(categories.toTypedArray()))
        props.addAll(unknownProperties)

        lastModified?.let { props += it }
+2 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ package at.bitfire.ical4android

import net.fortuna.ical4j.model.Calendar
import net.fortuna.ical4j.model.Property
import net.fortuna.ical4j.transform.rfc5545.CreatedPropertyRule
import net.fortuna.ical4j.transform.rfc5545.DateListPropertyRule
import net.fortuna.ical4j.transform.rfc5545.DatePropertyRule
import net.fortuna.ical4j.transform.rfc5545.Rfc5545PropertyRule
@@ -17,6 +18,7 @@ import java.util.logging.Level
object ICalPreprocessor {

    private val propertyRules = arrayOf(
            CreatedPropertyRule(),      // make sure CREATED is UTC
            DatePropertyRule(),
            DateListPropertyRule()
    )
+0 −13
Original line number Diff line number Diff line
@@ -119,17 +119,4 @@ object MiscUtils {

    }


    object TextListHelper {

        fun TextList.toList(): List<String> {
            val list = LinkedList<String>()
            val it = iterator()
            while (it.hasNext())
                list += it.next()
            return list
        }

    }

}
 No newline at end of file
Loading