From e9f56d074c743acc5d0ed714e39f3e616017d28c Mon Sep 17 00:00:00 2001 From: Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com> Date: Sat, 14 Mar 2026 17:24:55 -0700 Subject: [PATCH 1/5] Skip priority in ics generation if priority is empty for jtx Board --- lib/src/main/kotlin/at/bitfire/ical4android/JtxICalObject.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/src/main/kotlin/at/bitfire/ical4android/JtxICalObject.kt b/lib/src/main/kotlin/at/bitfire/ical4android/JtxICalObject.kt index 545aace4..74d92e76 100644 --- a/lib/src/main/kotlin/at/bitfire/ical4android/JtxICalObject.kt +++ b/lib/src/main/kotlin/at/bitfire/ical4android/JtxICalObject.kt @@ -1110,9 +1110,6 @@ duration?.let(props::add) priority?.let { props += Priority(it) } - else { - props += Priority(Priority.UNDEFINED.level) - } due?.let { when { From 36f0ebc2486d86eaed9685fe95d18045707f4195 Mon Sep 17 00:00:00 2001 From: Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:33:08 -0500 Subject: [PATCH 2/5] Updated tests to properly ignore empty priority value --- .../kotlin/at/bitfire/ical4android/JtxICalObjectTest.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/src/androidTest/kotlin/at/bitfire/ical4android/JtxICalObjectTest.kt b/lib/src/androidTest/kotlin/at/bitfire/ical4android/JtxICalObjectTest.kt index 4dcb3122..885c7ea1 100644 --- a/lib/src/androidTest/kotlin/at/bitfire/ical4android/JtxICalObjectTest.kt +++ b/lib/src/androidTest/kotlin/at/bitfire/ical4android/JtxICalObjectTest.kt @@ -844,6 +844,12 @@ class JtxICalObjectTest { if(inProp.name == "DTSTAMP" || exceptions?.contains(inProp.name) == true) return@forEach val outProp = iCalOut.components[i].properties.getProperty(inProp.name) + + if(inProp.name == "PRIORITY" && inProp.value == "0") { + assertNull(outProp) + return@forEach + } + assertEquals(inProp, outProp) } } From e3114ebc1a32fe7fa939c3f91d6008dd35101e8e Mon Sep 17 00:00:00 2001 From: Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com> Date: Tue, 17 Mar 2026 16:25:35 -0500 Subject: [PATCH 3/5] Only process attendees with valid uri, keep VTimeZones as components --- .../at/bitfire/ical4android/JtxICalObject.kt | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/lib/src/main/kotlin/at/bitfire/ical4android/JtxICalObject.kt b/lib/src/main/kotlin/at/bitfire/ical4android/JtxICalObject.kt index 74d92e76..5a5e4b7c 100644 --- a/lib/src/main/kotlin/at/bitfire/ical4android/JtxICalObject.kt +++ b/lib/src/main/kotlin/at/bitfire/ical4android/JtxICalObject.kt @@ -36,6 +36,7 @@ import net.fortuna.ical4j.model.TextList import net.fortuna.ical4j.model.TimeZoneRegistryFactory import net.fortuna.ical4j.model.component.VAlarm import net.fortuna.ical4j.model.component.VJournal +import net.fortuna.ical4j.model.component.VTimeZone import net.fortuna.ical4j.model.component.VToDo import net.fortuna.ical4j.model.parameter.AltRep import net.fortuna.ical4j.model.parameter.Cn @@ -649,6 +650,9 @@ open class JtxICalObject( ical.components += calComponent addProperties(calComponent.properties) + for (vTimeZone in getVTimeZones(calComponent.properties)) + ical.components += vTimeZone + alarms.forEach { alarm -> val vAlarm = VAlarm() @@ -825,7 +829,12 @@ open class JtxICalObject( attendees.forEach { attendee -> val attendeeProp = net.fortuna.ical4j.model.property.Attendee().apply { - this.calAddress = URI(attendee.caladdress) + this.calAddress = try { + URI(attendee.caladdress) + } catch (_: Exception) { + logger.warning("Ignoring invalid attendee URI: ${attendee.caladdress}") + return@forEach + } attendee.cn?.let { this.parameters.add(Cn(it)) @@ -1082,17 +1091,9 @@ open class JtxICalObject( props += dur } - - /* -// remember used time zones -val usedTimeZones = HashSet() -duration?.let(props::add) -*/ - - if(component == JtxContract.JtxICalObject.Component.VTODO.name) { completed?.let { - //Completed is defines as always DateTime! And is always UTC! But the X_PROP_COMPLETEDTIMEZONE can still define a timezone + //Completed is defined as always DateTime! And is always UTC! But the X_PROP_COMPLETEDTIMEZONE can still define a timezone props += Completed(DateTime(it)) // only take completedTimezone if there was the completed time set @@ -1130,19 +1131,24 @@ duration?.let(props::add) } } } + } + - /* - - // determine earliest referenced date - val earliest = arrayOf( - dtStart?.date, - due?.date, - completedAt?.date - ).filterNotNull().min() - // add VTIMEZONE components - for (tz in usedTimeZones) - ical.components += ICalendar.minifyVTimeZone(tz.vTimeZone, earliest) -*/ + /** + * This function maps the date-time properties DtStart, Due and Completed to VTimeZone + */ + private fun getVTimeZones(props: PropertyList): List { + + val dates = arrayOf( + props.getProperty("DtStart").takeIf { it.timeZone != null }, + props.getProperty("Due").takeIf { it.timeZone != null }, + props.getProperty("Completed").takeIf { it.timeZone != null }, + ) + val earliest = dates.mapNotNull { it?.date }.min() + + return dates.mapNotNull { date -> + date?.timeZone?.vTimeZone?.let { ICalendar.minifyVTimeZone(it, earliest) } + } } From cebb7519301b262816b9980f1c2421209260bf33 Mon Sep 17 00:00:00 2001 From: Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com> Date: Tue, 17 Mar 2026 21:26:48 -0500 Subject: [PATCH 4/5] safe call update --- .../main/kotlin/at/bitfire/ical4android/JtxICalObject.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/main/kotlin/at/bitfire/ical4android/JtxICalObject.kt b/lib/src/main/kotlin/at/bitfire/ical4android/JtxICalObject.kt index 5a5e4b7c..2976bbe6 100644 --- a/lib/src/main/kotlin/at/bitfire/ical4android/JtxICalObject.kt +++ b/lib/src/main/kotlin/at/bitfire/ical4android/JtxICalObject.kt @@ -1140,9 +1140,9 @@ open class JtxICalObject( private fun getVTimeZones(props: PropertyList): List { val dates = arrayOf( - props.getProperty("DtStart").takeIf { it.timeZone != null }, - props.getProperty("Due").takeIf { it.timeZone != null }, - props.getProperty("Completed").takeIf { it.timeZone != null }, + props.getProperty("DtStart")?.takeIf { it.timeZone != null }, + props.getProperty("Due")?.takeIf { it.timeZone != null }, + props.getProperty("Completed")?.takeIf { it.timeZone != null }, ) val earliest = dates.mapNotNull { it?.date }.min() From a491849be061a71afe7fceef8d7190e5ae101ca0 Mon Sep 17 00:00:00 2001 From: Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com> Date: Tue, 17 Mar 2026 21:40:40 -0500 Subject: [PATCH 5/5] Attempt to solve NullPointerException --- .../at/bitfire/ical4android/JtxICalObject.kt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/src/main/kotlin/at/bitfire/ical4android/JtxICalObject.kt b/lib/src/main/kotlin/at/bitfire/ical4android/JtxICalObject.kt index 2976bbe6..49107ce9 100644 --- a/lib/src/main/kotlin/at/bitfire/ical4android/JtxICalObject.kt +++ b/lib/src/main/kotlin/at/bitfire/ical4android/JtxICalObject.kt @@ -1139,15 +1139,16 @@ open class JtxICalObject( */ private fun getVTimeZones(props: PropertyList): List { - val dates = arrayOf( - props.getProperty("DtStart")?.takeIf { it.timeZone != null }, - props.getProperty("Due")?.takeIf { it.timeZone != null }, - props.getProperty("Completed")?.takeIf { it.timeZone != null }, - ) - val earliest = dates.mapNotNull { it?.date }.min() + val dates = listOfNotNull( + props.getProperty(Property.DTSTART), + props.getProperty(Property.DUE), + props.getProperty(Property.COMPLETED) + ).filter { it.timeZone != null } + + val earliest = dates.mapNotNull { it.date }.minOrNull() return dates.mapNotNull { date -> - date?.timeZone?.vTimeZone?.let { ICalendar.minifyVTimeZone(it, earliest) } + date.timeZone?.vTimeZone?.let { ICalendar.minifyVTimeZone(it, earliest) } } }