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

Unverified Commit 9d57f4c3 authored by Patrick Lang's avatar Patrick Lang Committed by GitHub
Browse files

Add option to export collections as .ics (#9)

* Refactored ics-generation, using ical4android now
Closes #15

* Added option to export collections as .ics
Fixed issue for exporting entries with attachments (affected also sync!)

* Added test for getICSForCollection()

* Closes #13 jtx: ICS generation always contains ORGANIZER although it is empty
parent 37f0216b
Loading
Loading
Loading
Loading
+33 −3
Original line number Diff line number Diff line
@@ -14,8 +14,7 @@ import at.bitfire.ical4android.MiscUtils.ContentProviderClientHelper.closeCompat
import at.bitfire.ical4android.impl.TestJtxCollection
import at.techbee.jtx.JtxContract
import at.techbee.jtx.JtxContract.asSyncAdapter
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNotNull
import junit.framework.TestCase.*
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -25,7 +24,6 @@ class JtxCollectionTest {
    private val testAccount = Account("TEST", JtxContract.JtxCollection.TEST_ACCOUNT_TYPE)
    private lateinit var contentResolver: ContentResolver
    private lateinit var client: ContentProviderClient
    var collection: JtxCollection<JtxICalObject>? = null
    lateinit var context: Context

    private val url = "https://jtx.techbee.at"
@@ -93,4 +91,36 @@ class JtxCollectionTest {

        assertEquals(1, icalobjects.size)
    }

    @Test
    fun getICSForCollection_test() {
        val collectionUri = JtxCollection.create(testAccount, client, cv)
        assertNotNull(collectionUri)

        val collections = JtxCollection.find(testAccount, client, context, TestJtxCollection.Factory, null, null)
        val items = collections[0].queryICalObjects(null, null)
        assertEquals(0, items.size)

        val cv1 = ContentValues().apply {
            put(JtxContract.JtxICalObject.SUMMARY, "summary")
            put(JtxContract.JtxICalObject.COMPONENT, JtxContract.JtxICalObject.Component.VJOURNAL.name)
            put(JtxContract.JtxICalObject.ICALOBJECT_COLLECTIONID, collections[0].id)
        }
        val cv2 = ContentValues().apply {
            put(JtxContract.JtxICalObject.SUMMARY, "entry2")
            put(JtxContract.JtxICalObject.COMPONENT, JtxContract.JtxICalObject.Component.VTODO.name)
            put(JtxContract.JtxICalObject.ICALOBJECT_COLLECTIONID, collections[0].id)
        }
        client.insert(JtxContract.JtxICalObject.CONTENT_URI.asSyncAdapter(testAccount), cv1)
        client.insert(JtxContract.JtxICalObject.CONTENT_URI.asSyncAdapter(testAccount), cv2)

        val ics = collections[0].getICSForCollection()

        assertTrue(ics.contains(Regex("BEGIN:VCALENDAR(\\n*|\\r*|\\t*|.*)*END:VCALENDAR")))
        assertTrue(ics.contains("PRODID:+//IDN bitfire.at//ical4android"))
        assertTrue(ics.contains("SUMMARY:summary"))
        assertTrue(ics.contains("SUMMARY:entry2"))
        assertTrue(ics.contains(Regex("BEGIN:VJOURNAL(\\n*|\\r*|\\t*|.*)*END:VJOURNAL")))
        assertTrue(ics.contains(Regex("BEGIN:VTODO(\\n*|\\r*|\\t*|.*)*END:VTODO")))
    }
}
+28 −0
Original line number Diff line number Diff line
@@ -13,6 +13,10 @@ import android.net.Uri
import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues
import at.techbee.jtx.JtxContract
import at.techbee.jtx.JtxContract.asSyncAdapter
import net.fortuna.ical4j.model.Calendar
import net.fortuna.ical4j.model.component.VJournal
import net.fortuna.ical4j.model.component.VToDo
import net.fortuna.ical4j.model.property.Version
import java.util.*

open class JtxCollection<out T: JtxICalObject>(val account: Account,
@@ -262,4 +266,28 @@ open class JtxCollection<out T: JtxICalObject>(val account: Account,
        }
    }


    /**
     * @return a string with all JtxICalObjects within the collection as iCalendar
     */
    fun getICSForCollection(): String {
        client.query(JtxContract.JtxICalObject.CONTENT_URI.asSyncAdapter(account), null, "${JtxContract.JtxICalObject.ICALOBJECT_COLLECTIONID} = ? AND ${JtxContract.JtxICalObject.DELETED} = ?", arrayOf(id.toString(), "0"), null).use { cursor ->
            Ical4Android.log.fine("getICSForCollection: found ${cursor?.count} records in ${account.name}")

            val ical = Calendar()
            ical.properties += Version.VERSION_2_0
            ical.properties += ICalendar.prodId

            while (cursor?.moveToNext() == true) {
                val jtxIcalObject = JtxICalObject(this)
                jtxIcalObject.populateFromContentValues(cursor.toValues())
                val singleICS = jtxIcalObject.getICalendarFormat()
                singleICS?.components?.forEach { component ->
                    if(component is VToDo || component is VJournal)
                        ical.components += component
                }
            }
            return ical.toString()
        }
    }
}
 No newline at end of file
+2 −3
Original line number Diff line number Diff line
@@ -675,8 +675,6 @@ open class JtxICalObject(
                Ical4Android.log.log(Level.WARNING, "Ignoring invalid task URL: $url", e)
            }
        }
        //organizer?.let { props += it }


        classification?.let { props += Clazz(it) }
        status?.let { props += Status(it) }
@@ -1518,6 +1516,7 @@ duration?.let(props::add)
            language = organizerContentValues?.getAsString(JtxContract.JtxOrganizer.LANGUAGE)
            other = organizerContentValues?.getAsString(JtxContract.JtxOrganizer.OTHER)
        }
        if(orgnzr.caladdress?.isNotEmpty() == true)   // we only take the organizer if there was a caladdress (otherwise an empty ORGANIZER is created)
            organizer = orgnzr

        // Take care of attachments