Git Product home page Git Product logo

ical4android's People

Contributors

abaker avatar arnyminerz avatar dschuermann avatar korelstar avatar patrickunterwegs avatar rfc2822 avatar sunkup avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

ical4android's Issues

Handling period durations of days with `T` prefix

There has been detected some instances where the ICS file has an invalid duration, for example this trigger:

TRIGGER:-P2DT

Trying to parse it in Kotlin Playground shows that is not a valid duration, which is what ICSx5 complains about when trying to load those kind of URLs.

The Java Documentation is not very clear about the use of the T prefix. However, this StackOverflow post doesn't mention D as a valid unit for periods with the T prefix, which this is the case. To be clearer:

TRIGGER:-PT2H    <- VALID
TRIGGER:-PT2D    <- INVALID

Unknown reltype get changed (not only interpret as) parent (RFC 9253)

The issue tasks/tasks#2527 above is created by DAVx5, further possibly by this library. The assumption is at follows:

The following code transfers unknown iCalendar RELTYPE’s to PARENT:

relatedTo.parameters.add(when (row.getAsInteger(Relation.RELATED_TYPE)) {
Relation.RELTYPE_CHILD ->
RelType.CHILD
Relation.RELTYPE_SIBLING ->
RelType.SIBLING
else /* Relation.RELTYPE_PARENT, default value */ ->
RelType.PARENT
})

The interpretation of unknown RELTYPE’s happens in compliance to 3.2.15, RFC 5545:

Applications MUST treat x-name and iana-token values they don't recognize the same way as they would the PARENT value.

However, this current implementation hinders depending apps from supporting further/custom RELTYPE’s and, more importantly, leads them to lose the original RELTYPE when saving iCal data back to a server. So users cannot connect e.g. DAVx5 to a server account, where new RELTYPE’s are already in use, without incurring data loss, making DAVx5 forward-incompatible. While RFC 5545 only emphasizes forward compatibility, RFC 9253 already defines new RELTYPE‘s, making this library & DAVx5 incompatible to RFC 9253.

IMO this library should allow its depending applications to interpret custom/unknown RELTYPE’s themselves. Then these apps can decide on their own how they want to handle unknown RELTYPE’s, making it possible to tolerate them. Falling back to the current behavior should only be possible with a warning attached that pushing the data back to a server can induce data loss to its users.


Depends on tasks/tasks#2527

Exception when unknown timezone in calendar provider

See bitfireAT/davx5-ose#133: when an event in the calendar provider contains an unknown time zone, for instance Europe/Kyiv (the newer version of Europe/Kiev), AndroidEvent.populateEvent throws an unhandled Exception:

j$.time.zone.c: Unknown time-zone ID: Europe/Kyiv
	at j$.time.zone.f.b(SourceFile:2)
	at j$.time.u.Q(SourceFile:3)
	at j$.time.ZoneId.L(SourceFile:2)
	at j$.time.ZoneId.of(Unknown Source:1)
	at at.bitfire.ical4android.util.TimeApiExtensions.toZoneIdCompat(Unknown Source:9)
	at at.bitfire.ical4android.util.TimeApiExtensions.requireZoneId(TimeApiExtensions.kt:3)
	at at.bitfire.ical4android.util.TimeApiExtensions.toZonedDateTime(Unknown Source:13)
	at at.bitfire.ical4android.AndroidEvent.populateEvent(AndroidEvent.kt:29)

This should be handled somehow; for instance by choosing the system default timezone as a last resort (the calendar provider stores the UNIX time of dtStart/dtEnd in any case, so this should be possible without losing the actual time).

"UnsupportedOperationException: TimeZone is not applicable to current value" when parsing iCalendar from iCloud

We got this parsing error over a support request:

EXCEPTION
at.bitfire.ical4android.InvalidCalendarException: Couldn't parse iCalendar
	at at.bitfire.ical4android.ICalendar$Companion.fromReader(ICalendar.kt:161)
	at at.bitfire.ical4android.Event$Companion.eventsFromReader(Event.kt:8)
…
Caused by: net.fortuna.ical4j.data.ParserException: Error at line 22:TimeZone is not applicable to current value
	at net.fortuna.ical4j.data.CalendarParserImpl.parse(CalendarParserImpl.java:17)
	at net.fortuna.ical4j.data.CalendarBuilder.build(CalendarBuilder.java:3)
	at net.fortuna.ical4j.data.CalendarBuilder.build(CalendarBuilder.java:2)
	at at.bitfire.ical4android.ICalendar$Companion.fromReader(ICalendar.kt:59)
	... 37 more
Caused by: java.lang.UnsupportedOperationException: TimeZone is not applicable to current value
	at net.fortuna.ical4j.model.property.DateListProperty.setTimeZone(DateListProperty.java:64)
	at net.fortuna.ical4j.data.DefaultContentHandler.resolveTimezones(DefaultContentHandler.java:63)
	at net.fortuna.ical4j.data.DefaultContentHandler.endCalendar(DefaultContentHandler.java:1)
	at net.fortuna.ical4j.data.CalendarParserImpl.parseCalendar(CalendarParserImpl.java:50)
	at net.fortuna.ical4j.data.CalendarParserImpl.parseCalendarList(CalendarParserImpl.java:15)
	at net.fortuna.ical4j.data.CalendarParserImpl.parse(CalendarParserImpl.java:13)
	... 40 more

I think it's because a TZID was applied to a DateListProperty without dates, something like RDATE;TZID=Some/TZ:. Unfortunately we don't have the iCalendar (I have requested it, but little hope to actually get it) and I can't reproduce with this test:

class Ical4jTest {

    @Test
    fun testDateList_WithoutDates_WithTZ() {
        val cal = ICalendar.fromReader(StringReader("BEGIN:VCALENDAR\r\n" +
                "VERSION:2.0\r\n" +
                "BEGIN:VEVENT\r\n" +
                "SUMMARY:Test\r\n" +
                "DTSTART;TZID=Europe/Vienna:20230824T161334\r\n" +
                "RDATE;TZID=Europe/Vienna:\r\n" +
                "END:VEVENT\r\n" +
                "END:VCALENDAR"))
        val event = cal.getComponent<VEvent>(Component.VEVENT)
        assertEquals("Test", event.summary.value)

        val rDate = event.getProperty<RDate>(Property.RDATE)
        assertNull(rDate.dates)
    }

}

Here we see that dates is not null, but an empty array, and then the code that causes the exception doesn't make any problems:

https://github.com/ical4j/ical4j/blob/527c76a34456e5916ed2efc8f2c960ddba8e2790/src/main/java/net/fortuna/ical4j/model/property/DateListProperty.java#L130-L133

  • Manage to reproduce the problem
  • Provide a PR for ical4j so that this is ignored, at least in lenient mode – as far as I can see, there's no disadvantage in ignoring the timezone in this case.

Update to ical4j 3.2.4

The dependent issue has been solved, so we can update to 3.2.4 and the tests should run fine again.

The javamail dependency was also removed by ical4j – so we don't need that in build.gradle anymore, too.

Maybe some tests have to be adjusted.

Tasks: add tasks app package to PRODID

Currently, we're adding the mutators (the calendar apps that edited an event) to the PRODID like this:

# VEVENT
PRODID:DAVx5/4.2.3.4-ose ical4j/3.2.5 (org.withouthat.acalendarplus)

so that we can see from the ICalendar which calendar app has created the event.

There's no equivalent for tasks and journals. I suggest to add the package name of the used tasks backend to the PRODID like this:

# VTODO
PRODID:DAVx5/4.2.3.4-ose ical4j/3.2.5 (tasks.app.here)

  • Move userAgents from Event up to ICalendar
  • Add task app package name from taskList.provider to task.userAgents in AndroidTask.populateTask()
  • Extract
    if (userAgents.isEmpty())
    prodId
    else
    ProdId(prodId.value + " (" + userAgents.joinToString(",") + ")")
    to a new method ICalendar.prodId() which generates the PRODID with the userAgents (instead of var prodId).
  • Call this prodId() method in Event.write() and Task.write()
  • Similarly add jtx Board package name to
    ical.properties += ICalendar.prodId
  • Adapt PRODID test in TaskTest.testWrite()

@patrickunterwegs Where should we add the test in JtxICalObjectTest?

CodeQL fails

For some reason, CodeQL currently fails:

  /opt/hostedtoolcache/CodeQL/2.15.4/x64/codeql/codeql database finalize --finalize-dataset --threads=4 --ram=14567 /home/runner/work/_temp/codeql_databases/java
  CodeQL detected code written in Java/Kotlin and Java/Kotlin but could not process any of it. Review our troubleshooting guide at https://gh.io/troubleshooting-code-scanning/no-source-code-seen-during-build .
  Error: Encountered a fatal error while running "/opt/hostedtoolcache/CodeQL/2.15.4/x64/codeql/codeql database finalize --finalize-dataset --threads=4 --ram=14567 /home/runner/work/_temp/codeql_databases/java". Exit code was 32 and last log line was: CodeQL detected code written in Java/Kotlin and Java/Kotlin but could not process any of it. Review our troubleshooting guide at https://gh.io/troubleshooting-code-scanning/no-source-code-seen-during-build . See the logs for more details.

We should find out why and fix it. We could also update dependencies in the same run.

  • Update dependencies, including Kotlin, AGP and gradle
  • Find out why CodeQL fails and fix it
    • I'd remove the --no-daemon call in the CodeQL CI, because its not necessary
    • Maybe autobuild works in the meanwhile, then we wouldn't need gradle assemble. Otherwise keep the assemble task.

Remove whitespace values from content providers

Actual problem: event.RDATE is sometimes filled with whitespace like \n when an .ics is directly imported with a calendar app. For instance, Zoom seems to sometimes generate such RDATEs.

Then DAVx⁵ uploads the event with an empty RDATE line like:

RDATE;TZID=Europe/Paris:

and servers reject that with 415.

So I suggest to:

  • extend MiscUtils.removeEmptyStrings so that it removes not only empty, but also blank (= only white-space, including newline) values + test
  • apply MiscUtils.removeEmptyStrings to populateTask() like it's done for populateEvent() ?

Time of Event not correct with Events from Wordpress Events Manager

Discussed in bitfireAT/icsx5#149

Originally posted by derlucas April 21, 2023
I have trouble using the App with Calendars from Events Manager Wordpress Plugin.
The Events are saved with Timezone "Berlin" at 19:00 and shown correctly on the Wordpress Site. In the ICS file the Entry is like this:

DTSTART;TZID=Europe/Berlin:20230421T190000 DTEND;TZID=Europe/Berlin:20230421T235900

But in my Android Calendar (Simple Mobile Tools Calendar the Event is show at 01:00 in the night instead of 19:00. I tested the Google Calendar App too, and it shows the same false Time.

What would be the best way to find the root of the Issue?

Depends on ical4j/ical4j#651

Tests should work under every system time zone

I noticed some tests fail when run in certain time zones. This is due to flipping dates when time of a DateTime object is close to midnight. For now I found the following tests fail with time zone America/New_York:

  • testRecurrenceSetsToAndroidString_TimeAlthoughAllDay
  • testRecurrenceSetsToAndroidString_Date
  • testHasUntilBeforeDtStart_DtStartDate_RRuleUntil_TimeBeforeDtStart_noTimezone

Redesign ical4android API

Objects should be immutable or at least less stateful; API should be well-designed, consistent and well-tested.

Open questions and ideas:

  • How much decoupling from DAVx⁵ is really required or desired?
  • How much extendability is required/desired and how should it be implemented?
  • Calls that can't work shouldn't be possible; get ideas from other persistence frameworks
  • Complete redesign (2.0) or improve incrementally?
  • Test-driven or at least heavily tested from the beginning

See also bitfireAT/vcard4android#2

Couldn't parse iCalendar / Unparseable date: ":20220331T084614Z"

Caused by: java.text.ParseException: Unparseable date: ":20220331T084614Z"

  • All my events are unable to be parsed.
  • My calendar provider is posteo.de
  • Happend in ICSx5 app
  • Side note: My calendar URL does not end on .ics (unlike the example URL). Dunno if this could be a problem.

Event example:

BEGIN:VEVENT
EXDATE;VALUE=DATE:20220101
EXDATE;VALUE=DATE:20211001
CREATED;VALUE=DATE-TIME:20211003T130823Z
DTEND;VALUE=DATE:20211011
DTSTART;VALUE=DATE:20211001
TRANSP:TRANSPARENT
DTSTAMP;VALUE=DATE-TIME:20220331T084614Z
LAST-MODIFIED;VALUE=DATE-TIME:20220104T115808Z
UID:3979A421-7245-4F51-9E72-F8DF0A1BD2BE
URL;VALUE=URI:
SUMMARY:<redacted>
RRULE:FREQ=MONTHLY;INTERVAL=3;BYMONTHDAY=1
SEQUENCE:0
X-APPLE-TRAVEL-ADVISORY-BEHAVIOR::AUTOMATIC
BEGIN:VALARM
TRIGGER:-PT15H
ATTACH;ENCODING=BASE64;FILENAME=attachment-not-shared.txt;VALUE=BINARY:<redacted>
ACTION:AUDIO
X-WR-ALARMUID::D8F053D3-C614-4FDB-A217-EE607C5750EC
UID::D8F053D3-C614-4FDB-A217-EE607C5750EC
X-APPLE-DEFAULT-ALARM::TRUE
ACKNOWLEDGED::20220331T084614Z
END:VALARM
END:VEVENT

Full stack trace:

at.bitfire.ical4android.InvalidCalendarException: Couldn't parse iCalendar
 at at.bitfire.ical4android.ICalendar$Companion.fromReader(ICalendar.kt:81)
 at at.bitfire.ical4android.Event$Companion.eventsFromReader(Event.kt:81)
 at at.bitfire.icsdroid.ui.AddCalendarValidationFragment$ValidationModel$initialize$downloader$1.onSuccess(AddCalendarValidationFragment.kt:115)
 at at.bitfire.icsdroid.CalendarFetcher.fetchLocal$icsx5_62_2_0_2_standardRelease(CalendarFetcher.kt:114)
 at at.bitfire.icsdroid.CalendarFetcher.run(CalendarFetcher.kt:48)
 at java.lang.Thread.run(Thread.java:919)
Caused by: net.fortuna.ical4j.data.ParserException: Error at line 63:Unparseable date: ":20220331T084614Z"
 at net.fortuna.ical4j.data.CalendarParserImpl.parse(CalendarParserImpl.java:172)
 at net.fortuna.ical4j.data.CalendarBuilder.build(CalendarBuilder.java:197)
 at net.fortuna.ical4j.data.CalendarBuilder.build(CalendarBuilder.java:185)
 at at.bitfire.ical4android.ICalendar$Companion.fromReader(ICalendar.kt:79)
 ... 5 more
Caused by: java.text.ParseException: Unparseable date: ":20220331T084614Z"
 at java.text.DateFormat.parse(DateFormat.java:362)
 at net.fortuna.ical4j.model.DateTime.setTime(DateTime.java:418)
 at net.fortuna.ical4j.model.DateTime.<init>(DateTime.java:349)
 at net.fortuna.ical4j.model.property.DateProperty.setValue(DateProperty.java:139)
 at net.fortuna.ical4j.model.property.Acknowledged.<init>(Acknowledged.java:124)
 at net.fortuna.ical4j.model.property.Acknowledged$Factory.createProperty(Acknowledged.java:158)
 at net.fortuna.ical4j.model.PropertyBuilder.build(PropertyBuilder.java:76)
 at net.fortuna.ical4j.data.DefaultContentHandler.endProperty(DefaultContentHandler.java:159)
 at net.fortuna.ical4j.data.CalendarParserImpl$PropertyParser.parse(CalendarParserImpl.java:309)
 at net.fortuna.ical4j.data.CalendarParserImpl$PropertyParser.access$1100(CalendarParserImpl.java:241)
 at net.fortuna.ical4j.data.CalendarParserImpl$PropertyListParser.parse(CalendarParserImpl.java:226)
 at net.fortuna.ical4j.data.CalendarParserImpl$ComponentParser.parse(CalendarParserImpl.java:446)
 at net.fortuna.ical4j.data.CalendarParserImpl$ComponentParser.access$900(CalendarParserImpl.java:422)
 at net.fortuna.ical4j.data.CalendarParserImpl$PropertyListParser.parse(CalendarParserImpl.java:224)
 at net.fortuna.ical4j.data.CalendarParserImpl$ComponentParser.parse(CalendarParserImpl.java:446)
 at net.fortuna.ical4j.data.CalendarParserImpl$ComponentParser.access$900(CalendarParserImpl.java:422)
 at net.fortuna.ical4j.data.CalendarParserImpl$PropertyListParser.parse(CalendarParserImpl.java:224)
 at net.fortuna.ical4j.data.CalendarParserImpl.parseCalendar(CalendarParserImpl.java:128)
 at net.fortuna.ical4j.data.CalendarParserImpl.parseCalendarList(CalendarParserImpl.java:194)
 at net.fortuna.ical4j.data.CalendarParserImpl.parse(CalendarParserImpl.java:163)
 ... 8 more

Any help appreciated :)

Stackoverflow on Standard in Vtimezone

Update by @rfc2822: already reported – depends on ical4j/ical4j#637


Below ics will result in a stack overflow error:

BEGIN:VCALENDAR
PRODID:-//K Desktop Environment//NONSGML libkcal 4.3//EN
VERSION:2.0
BEGIN:VTIMEZONE
TZID:Europe/Berlin
BEGIN:STANDARD
DTSTART;TZID=Europe/Berlin:19800928T030000
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
java.lang.StackOverflowError: stack size 1038KB
at net.fortuna.ical4j.model.Iso8601.toString(Iso8601.java:117)
at net.fortuna.ical4j.model.DateTime.toString(DateTime.java:503)
at net.fortuna.ical4j.model.component.Observance.calculateOnset(Observance.java:286)
at net.fortuna.ical4j.model.component.Observance.getLatestOnset(Observance.java:146)
at net.fortuna.ical4j.model.component.VTimeZone.getApplicableObservance(VTimeZone.java:200)
at net.fortuna.ical4j.model.TimeZone.getOffset(TimeZone.java:122)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2285)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2254)
at java.util.Calendar.setTimeInMillis(Calendar.java:1122)
at java.util.GregorianCalendar.<init>(GregorianCalendar.java:622)
at java.util.Calendar.createCalendar(Calendar.java:1025)
at java.util.Calendar.getInstance(Calendar.java:989)
at net.fortuna.ical4j.model.CalendarDateFormatFactory$DateFormat.format(CalendarDateFormatFactory.java:298)
at java.text.DateFormat.format(DateFormat.java:337)
at net.fortuna.ical4j.model.Iso8601.toString(Iso8601.java:134)
at net.fortuna.ical4j.model.DateTime.toString(DateTime.java:503)
at net.fortuna.ical4j.model.component.Observance.calculateOnset(Observance.java:286)
at net.fortuna.ical4j.model.component.Observance.getLatestOnset(Observance.java:146)
at net.fortuna.ical4j.model.component.VTimeZone.getApplicableObservance(VTimeZone.java:200)
at net.fortuna.ical4j.model.TimeZone.getOffset(TimeZone.java:122)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2285)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2254)
at java.util.Calendar.setTimeInMillis(Calendar.java:1122)
at java.util.GregorianCalendar.<init>(GregorianCalendar.java:622)
at java.util.Calendar.createCalendar(Calendar.java:1025)
at java.util.Calendar.getInstance(Calendar.java:989)
at net.fortuna.ical4j.model.CalendarDateFormatFactory$DateFormat.format(CalendarDateFormatFactory.java:298)
at java.text.DateFormat.format(DateFormat.java:337)
at net.fortuna.ical4j.model.Iso8601.toString(Iso8601.java:134)
at net.fortuna.ical4j.model.DateTime.toString(DateTime.java:503)
at net.fortuna.ical4j.model.component.Observance.calculateOnset(Observance.java:286)
at net.fortuna.ical4j.model.component.Observance.getLatestOnset(Observance.java:146)
at net.fortuna.ical4j.model.component.VTimeZone.getApplicableObservance(VTimeZone.java:200)
at net.fortuna.ical4j.model.TimeZone.getOffset(TimeZone.java:122)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2285)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2254)
at java.util.Calendar.setTimeInMillis(Calendar.java:1122)
at java.util.GregorianCalendar.<init>(GregorianCalendar.java:622)
at java.util.Calendar.createCalendar(Calendar.java:1025)
at java.util.Calendar.getInstance(Calendar.java:989)
at net.fortuna.ical4j.model.CalendarDateFormatFactory$DateFormat.format(CalendarDateFormatFactory.java:298)
at java.text.DateFormat.format(DateFormat.java:337)
at net.fortuna.ical4j.model.Iso8601.toString(Iso8601.java:134)
at net.fortuna.ical4j.model.DateTime.toString(DateTime.java:503)
at net.fortuna.ical4j.model.component.Observance.calculateOnset(Observance.java:286)
at net.fortuna.ical4j.model.component.Observance.getLatestOnset(Observance.java:146)
at net.fortuna.ical4j.model.component.VTimeZone.getApplicableObservance(VTimeZone.java:200)
at net.fortuna.ical4j.model.TimeZone.getOffset(TimeZone.java:122)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2285)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2254)
at java.util.Calendar.setTimeInMillis(Calendar.java:1122)
at java.util.GregorianCalendar.<init>(GregorianCalendar.java:622)
at java.util.Calendar.createCalendar(Calendar.java:1025)
at java.util.Calendar.getInstance(Calendar.java:989)
at net.fortuna.ical4j.model.CalendarDateFormatFactory$DateFormat.format(CalendarDateFormatFactory.java:298)
at java.text.DateFormat.format(DateFormat.java:337)
at net.fortuna.ical4j.model.Iso8601.toString(Iso8601.java:134)
at net.fortuna.ical4j.model.DateTime.toString(DateTime.java:503)
at net.fortuna.ical4j.model.component.Observance.calculateOnset(Observance.java:286)
at net.fortuna.ical4j.model.component.Observance.getLatestOnset(Observance.java:146)
at net.fortuna.ical4j.model.component.VTimeZone.getApplicableObservance(VTimeZone.java:200)
at net.fortuna.ical4j.model.TimeZone.getOffset(TimeZone.java:122)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2285)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2254)
at java.util.Calendar.setTimeInMillis(Calendar.java:1122)
at java.util.GregorianCalendar.<init>(GregorianCalendar.java:622)
at java.util.Calendar.createCalendar(Calendar.java:1025)
at java.util.Calendar.getInstance(Calendar.java:989)
at net.fortuna.ical4j.model.CalendarDateFormatFactory$DateFormat.format(CalendarDateFormatFactory.java:298)
at java.text.DateFormat.format(DateFormat.java:337)
at net.fortuna.ical4j.model.Iso8601.toString(Iso8601.java:134)
at net.fortuna.ical4j.model.DateTime.toString(DateTime.java:503)
at net.fortuna.ical4j.model.component.Observance.calculateOnset(Observance.java:286)
at net.fortuna.ical4j.model.component.Observance.getLatestOnset(Observance.java:146)
at net.fortuna.ical4j.model.component.VTimeZone.getApplicableObservance(VTimeZone.java:200)
at net.fortuna.ical4j.model.TimeZone.getOffset(TimeZone.java:122)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2285)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2254)
at java.util.Calendar.setTimeInMillis(Calendar.java:1122)
at java.util.GregorianCalendar.<init>(GregorianCalendar.java:622)
at java.util.Calendar.createCalendar(Calendar.java:1025)
at java.util.Calendar.getInstance(Calendar.java:989)
at net.fortuna.ical4j.model.CalendarDateFormatFactory$DateFormat.format(CalendarDateFormatFactory.java:298)
at java.text.DateFormat.format(DateFormat.java:337)
at net.fortuna.ical4j.model.Iso8601.toString(Iso8601.java:134)
at net.fortuna.ical4j.model.DateTime.toString(DateTime.java:503)
at net.fortuna.ical4j.model.component.Observance.calculateOnset(Observance.java:286)
at net.fortuna.ical4j.model.component.Observance.getLatestOnset(Observance.java:146)
at net.fortuna.ical4j.model.component.VTimeZone.getApplicableObservance(VTimeZone.java:200)
at net.fortuna.ical4j.model.TimeZone.getOffset(TimeZone.java:122)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2285)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2254)
at java.util.Calendar.setTimeInMillis(Calendar.java:1122)
at java.util.GregorianCalendar.<init>(GregorianCalendar.java:622)
at java.util.Calendar.createCalendar(Calendar.java:1025)
at java.util.Calendar.getInstance(Calendar.java:989)
at net.fortuna.ical4j.model.CalendarDateFormatFactory$DateFormat.format(CalendarDateFormatFactory.java:298)
at java.text.DateFormat.format(DateFormat.java:337)
at net.fortuna.ical4j.model.Iso8601.toString(Iso8601.java:134)
at net.fortuna.ical4j.model.DateTime.toString(DateTime.java:503)
at net.fortuna.ical4j.model.component.Observance.calculateOnset(Observance.java:286)
at net.fortuna.ical4j.model.component.Observance.getLatestOnset(Observance.java:146)
at net.fortuna.ical4j.model.component.VTimeZone.getApplicableObservance(VTimeZone.java:200)
at net.fortuna.ical4j.model.TimeZone.getOffset(TimeZone.java:122)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2285)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2254)
at java.util.Calendar.setTimeInMillis(Calendar.java:1122)
at java.util.GregorianCalendar.<init>(GregorianCalendar.java:622)
at java.util.Calendar.createCalendar(Calendar.java:1025)
at java.util.Calendar.getInstance(Calendar.java:989)
at net.fortuna.ical4j.model.CalendarDateFormatFactory$DateFormat.format(CalendarDateFormatFactory.java:298)
at java.text.DateFormat.format(DateFormat.java:337)
at net.fortuna.ical4j.model.Iso8601.toString(Iso8601.java:134)
at net.fortuna.ical4j.model.DateTime.toString(DateTime.java:503)
at net.fortuna.ical4j.model.component.Observance.calculateOnset(Observance.java:286)
at net.fortuna.ical4j.model.component.Observance.getLatestOnset(Observance.java:146)
at net.fortuna.ical4j.model.component.VTimeZone.getApplicableObservance(VTimeZone.java:200)
at net.fortuna.ical4j.model.TimeZone.getOffset(TimeZone.java:122)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2285)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2254)
at java.util.Calendar.setTimeInMillis(Calendar.java:1122)
at java.util.GregorianCalendar.<init>(GregorianCalendar.java:622)
at java.util.Calendar.createCalendar(Calendar.java:1025)
at java.util.Calendar.getInstance(Calendar.java:989)
at net.fortuna.ical4j.model.CalendarDateFormatFactory$DateFormat.format(CalendarDateFormatFactory.java:298)
at java.text.DateFormat.format(DateFormat.java:337)
at net.fortuna.ical4j.model.Iso8601.toString(Iso8601.java:134)
at net.fortuna.ical4j.model.DateTime.toString(DateTime.java:503)
at net.fortuna.ical4j.model.component.Observance.calculateOnset(Observance.java:286)
at net.fortuna.ical4j.model.component.Observance.getLatestOnset(Observance.java:146)
at net.fortuna.ical4j.model.component.VTimeZone.getApplicableObservance(VTimeZone.java:200)
at net.fortuna.ical4j.model.TimeZone.getOffset(TimeZone.java:122)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2285)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2254)
at java.util.Calendar.setTimeInMillis(Calendar.java:1122)
at java.util.GregorianCalendar.<init>(GregorianCalendar.java:622)
at java.util.Calendar.createCalendar(Calendar.java:1025)
at java.util.Calendar.getInstance(Calendar.java:989)
at net.fortuna.ical4j.model.CalendarDateFormatFactory$DateFormat.format(CalendarDateFormatFactory.java:298)
at java.text.DateFormat.format(DateFormat.java:337)
at net.fortuna.ical4j.model.Iso8601.toString(Iso8601.java:134)
at net.fortuna.ical4j.model.DateTime.toString(DateTime.java:503)
at net.fortuna.ical4j.model.component.Observance.calculateOnset(Observance.java:286)
at net.fortuna.ical4j.model.component.Observance.getLatestOnset(Observance.java:146)
at net.fortuna.ical4j.model.component.VTimeZone.getApplicableObservance(VTimeZone.java:200)
at net.fortuna.ical4j.model.TimeZone.getOffset(TimeZone.java:122)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2285)
at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2254)
at java.util.Calendar.setTimeInMillis(Calendar.java:1122)


[...]

Formalize / modularize rules for validation and repairing

Events are validated and/or modified/repaired when

  • they're read from an iCalendar (DAVx5 context: when they come from the server),
  • they're read from the calendar provider.

These validation rules shouldn't be mixed with other code and should have a proper module and tests.

Invalid ZoneOffset when syncing in Persian language

When setting phone language to "fa" (Which is for Iran language), DAVx5 shows notification with Invalid ID for ZoneOffset, non numeric characters found: +۰۳۲۵۴۴ message every time I press sync button.

Steps to reproduce:

  1. set davx5 language to Persian (فارسی) with/or "fa" locale.
  2. Add an event with Etar calendar application. (it might be produced with other applications, but I tested this app so far). Make sure the event is not an AllDay event, otherwise the bug won't produce.
  3. Come back to DAVx5 application and hit sync button.

This is the bug I get:

j$.time.d: Invalid ID for ZoneOffset, non numeric characters found: +۰۳۲۵۴۴
	at j$.time.ZoneOffset.R(Unknown Source:83)
	at j$.time.ZoneOffset.of(Unknown Source:75)
	at net.fortuna.ical4j.model.property.TzOffsetFrom.setValue(TzOffsetFrom.java:3)
	at net.fortuna.ical4j.model.property.TzOffsetFrom.<init>(TzOffsetFrom.java:5)
	at net.fortuna.ical4j.model.property.TzOffsetFrom$Factory.createProperty(TzOffsetFrom.java:3)
	at net.fortuna.ical4j.model.property.TzOffsetFrom$Factory.createProperty(TzOffsetFrom.java:2)
	at net.fortuna.ical4j.model.Property.copy(Property.java:21)
	at net.fortuna.ical4j.model.PropertyList.<init>(PropertyList.java:5)
	at net.fortuna.ical4j.model.Component.copy(Component.java:5)
	at net.fortuna.ical4j.model.ComponentList.<init>(ComponentList.java:5)
	at net.fortuna.ical4j.model.Component.copy(Component.java:12)
	at at.bitfire.ical4android.ICalendar$Companion.minifyVTimeZone(ICalendar.kt:6)
	at at.bitfire.ical4android.Event.write(Event.kt:439)
	at at.bitfire.davdroid.syncadapter.CalendarSyncManager$generateUpload$1.invoke(CalendarSyncManager.kt:7)
	at at.bitfire.davdroid.syncadapter.CalendarSyncManager$generateUpload$1.invoke(CalendarSyncManager.kt:1)

Here is also the debug report:

davx5-debug.zip

Store UID correctly when creating related-to entries

In some cases the UID is not stored correctly when a mirrored relation is generated by jtx Board causing a self-reference. This did not have an effect in jtx Board in the first place, but when jtx Board synchronizes the entries with to the server, the wrong reference causes problems in Nextcloud and tasks.org. Only when those entries are synchronized again to jtx Board, they will cause problems there. However, the origin is in the jtx Board downward sync.

CI tests fail at testLargeTransactionManyRows

See https://github.com/bitfireAT/ical4android/runs/6677616922

> Task :packageDebugAndroidTest
> Task :createDebugAndroidTestApkListingFileRedirect
[EmulatorConsole]: Failed to start Emulator console for 5554

> Task :connectedDebugAndroidTest
additionalTestOutput is not supported on this device running API level 28 because the additional test output directory could not be found
Starting 400 tests on emulator-5554 - 9

at.bitfire.ical4android.AndroidEventTest > testLargeTransactionManyRows[emulator-5554 - 9] FAILED 

Tests on emulator-5554 - 9 failed: There was 1 failure(s).

Test results saved as file:/__w/ical4android/ical4android/build/outputs/androidTest-results/connected/test-result.pb. Inspect these results in Android Studio by selecting Run > Import Tests From File from the menu bar and importing test-result.pb.

Since the commit on 26 May 2022: https://github.com/bitfireAT/ical4android/commits/main

Maybe this is not even related to the changes of the commit.

Drop RULES with UNTIL before DTSTART

From time to time, events with an RRULE whose UNTIL is before the DTSTART of the event appear, like:

DTSTART: 2022/05/31 13:00
RRULE: … UNTIL 2022/05/31 12:59

Such events are invalid and sometimes cause problems when they're uploaded again to the server (because the server reject them, which is OK).

Thus such RRULEs should be dropped, both when they're received from the server and when they're found in the calendar provider.

recurrenceSetsToAndroidString, androidStringToRecurrenceSet: handle multiple entries separated by newline

  • Check whether Android can have multiple entries in the recurrence set columns (RDATE, …), possibly with different time zones.
  • Handle these cases accordingly in recurrenceSetsToAndroidString
  • and androidStringToRecurrenceSet

As far as I have found out, the Android RDATE field is a list of lines, separated by new-line (\n). Every line looks like this:

  • [Europe/Vienna;]YYYYMMDDThhmmss[Z],YYYYMMDDThhmmss[Z] for non-all-day events
  • YYYYMMDD,YYYYMMDD for all-day events

Currently we don't handle multiple lines with multiple time zones in our conversion methods.

Better separation of AndroidEvent <-> (iCalendar) Event

ical4j comes with a time zone database that is used for its calculations and for instance to generate VTIMEZONEs which are then exported.

However, Java/Android has its own database (which is used by the calendar provider and the ZoneId, java.util.TimeZone classes). We always need to know which times with time zone are in the "ical4j namespace" and which ones are in "Android namespace".

There should be a proper separation and it should be documented and enforced.

ical4j 4.x comes with major changes. This issue should be done when we have ical4j 4.x (if at all). Depends on #106

Android always expects RRULE UNTIL to be in UTC format

Currently, we insert RRULE with UNTIL as it comes from the event (as defined in RFC 5445).

However, some code parts of the Android calendar provider seem to always expect UNTIL to be in UTC format, as specified in the obsolete RFC 2445.

So RRULE UNTIL should always be converted to UTC before inserting it into the calendar provider. Not doing so might result in losing the last occurrence.

Side note: ical4j parses UNTIL in the system default timezone and not in the DTSTART timezone. This may cause further problems…

androidify populated start dates, too

See bitfireAT/davx5-ose#133 (comment)

It happens because this code:

            if (duration != null) {
                // Some servers have problems with DURATION, so we always generate DTEND.
                val zonedStart = dtStartDateTime.toZonedDateTime()
                tsEnd = (zonedStart + duration).toInstant().toEpochMilli()
                duration = null
            }

fails when dtStartTime has the new Europe/Kyiv time zone.

Make this a real library (instead of a submodule)

We would have to care less about compile-time settings when this would be a real library. It would be easier to integrate with other projects.

I think of this is a separate library that

  • can be added as a normal dependency and then be used in other projects

  • has two components, something like a lib and and sample-app directory:

    • lib contains the lib and is what is imported when other projects add the dependency
    • sample-app contains a minimum sample app that also allows to test cert4android in real when debugging (not imported with the dependency)
  • make it a jitpack library

  • document how to embed the library

  • separate demo app from lib ¿Should we?

    • build action for jitpack should only build lib
    • demo app can be opened in Android Studio to test and work on the lib

Crash when using `SMH` suffix with durations

We have FixInvalidDayOffsetPreprocessor in ical4android, which handles cases when PT is not properly formatted for triggers and durations, however, it doesn't contemplate the S suffix, as stated in the Java Documentation for "seconds", so for example:

BEGIN:VALARM
TRIGGER:-P5S
ACTION:DISPLAY
END:VALARM

crashes the app.

Internal DAVx5 reference: https://github.com/bitfireAT/davx5/issues/482
Ticket reference: https://bitfire-at.zammad.com/#ticket/zoom/3057

Arabic ZoneOffset cannot be parsed

This has been discussed in disc-257, however, I wanted to keep track of this issue separately, just to keep track on the progress.

This issue is really related to ical4j/ical4j#458, however, since that issue has been stale since 2020, maybe we can make a workaround. I think this is an issue that might affect a lot of users that are not in the "English-speaking" timezones.

Just by searching around Google, I managed to find a Java function that converts all the arabic characters to decimal. I've converted that function to Kotlin:

/**
 * Converts an string with arabic numbers to their decimal equivalents.
 * @param number The number to convert.
 * @return An string that has all the arabic numbers found in [number] replaced by their English
 * representations.
 */
fun arabicToDecimal(number: String): String {
    val chars = CharArray(number.length)
    for (i in number.indices) {
        var ch = number[i].code
        if (ch in 0x0660..0x669)
            ch -= 0x0660 - '0'.code
        else if (ch in 0x06f0..0x06F9)
            ch -= 0x06f0 - '0'.code
        chars[i] = ch.toChar()
    }
    return String(chars)
}

By adding this to MiscUtils, this test:

@Test
fun testArabicToDecimal() {
    val arabic = "+۰۳۲۵۴۴"
    val decimal = MiscUtils.arabicToDecimal(arabic)
    assertEquals(decimal, "+032544")
}

shows that the function works properly. We can also use a website like this to check if the conversion is correct, which as far as I'm concerned, it works correctly.

Now the next step would be to add this conversion to the calendar parser. We can't, however, convert the whole calendar, since if the user has numbers in their event name, for example, they would be replaced by the decimal representations, which I don't think is a good option. We should search for all the properties that have the Time Zone type, and replace their values.

I'm not sure where to make this change, since, first, we should make a test that doesn't pass because of this. Here

// add VTIMEZONE components
for (tz in usedTimeZones)
ical.components += minifyVTimeZone(tz.vTimeZone, earliest)

Maybe it's too late, and the error has already been thrown, and we should really work it out in ICalendar, otherwise the error might still occur. However, it's difficult to replace the Timezone properties without having parsed the calendar before, so running the fix on ICalendar.fromReader may be too early...

I will keep investigating. What do you think @rfc2822 ?

Accept tasks with DUE=DTSTART

See bitfireAT/davx5-ose#243. Proposed solution:

As the DUE>DTSTART constraint is enforced by OpenTasks (which is not recommended anymore), we could drop DTSTARTs that are >= DUE only when OpenTasks is the tasks backend. So if jtx Board and tasks.org accept tasks with DTSTART=DUE, we could sync such tasks to those backends as they are. Then everything including the alarms should be retained. For OpenTasks, we keep everything as it is because it's legacy code and will be removed sooner or later anyway.

Also relevant for @TechbeeAT @patrickunterwegs (jtx Board UI changes for DTSTART=DUE as discussed in chat).

Ignore empty X-APPLE-CALENDAR-COLOR

Hi,

using ICSx⁵ I got the following exception

length=0; index=0

java.lang.StringIndexOutOfBoundsException: length=0; index=0
	at java.lang.String.charAt(Native Method)
	at android.graphics.Color.parseColor(Color.java:1384)
	at at.bitfire.ical4android.Css3Color$Companion.colorFromString(Css3Color.kt:178)
	at at.bitfire.icsdroid.ui.AddCalendarValidationFragment$ValidationModel$initialize$downloader$1.onSuccess(AddCalendarValidationFragment.kt:126)
	at at.bitfire.icsdroid.CalendarFetcher.fetchNetwork$icsx5_62_2_0_2_standardRelease(CalendarFetcher.kt:155)
	at at.bitfire.icsdroid.CalendarFetcher.run(CalendarFetcher.kt:46)
	at java.lang.Thread.run(Thread.java:784)

which I think might come from the line X-APPLE-CALENDAR-COLOR: (value is empty) in my ics-file.

I suspect that Color.parseColor(…) not only throws IllegalArgumentException as caught in Css3Color line 189, but also StringIndexOutOfBoundsException for empty strings.

If necessary I will try to assemble a minimum working example.

Greetings Nmxcgeo

Always prefer Android time zone names to ical4j time zone names

We need a solution for bitfireAT/davx5-ose#133 until #56 is finished (and on Android level <26). Problem:

  1. Event with time zone Europe/Kiev is created locally in Android.
  2. ical4android feeds the event to ical4j and understands Europe/Kiev as an alias for the the newer Europe/Kyiv.
  3. So, the exported iCalendar file has DTSTART;TZID=Europe/Kyiv:…
  4. When the iCalendar is imported later, DAVx5 sees that Android doesn't understand Europe/Kyiv and rewrites it to the current system time zone.

I think the straightest solution is that when a time zone is present in both ical4j and Android, but with different names (like Europe/Kiev and Europe/Kyiv), the Android name should always be preferred (because Android sometimes has to be able to process the time zone information, too) and also exported (so that it can be imported again without information loss).

This is also related to AndroidEvent.androidifyTimeZone, which must never return a time zone Android doesn't know.

Not syncing VTODO with Tasks application

Hi there. I'm currently facing a problem in which the tasks are not being synced with server and local application (Tasks). The platforms I'm using are:

  1. NextCloud server (I've tested The Good Cloud & Qloud providers),
  2. DAVx5 for synchronizing tasks,
  3. Tasks application as a client to see the tasks.
    All the synchronization works fine when I log in to my account for the first time using DAVx5. It syncs the task data, and Tasks would show them with the latest changes.
    But when I make any changes in the web service, then clicking on sync button inside DAVx5, and navigating to Tasks application, nothing is updated. The problem persists vise versa. Like, if I make any changes using Tasks and clicking on DAVx5's sync button , nothing would change in web service.

I tried changing service provider but the problem was still existing, so it is NOT the server.
I tried using Jtx Board to see if the problem exists. Surprisingly, the tasks were updating after any changes.
So I am not completely sure if it's Tasks application's bug or DAVx5's. But I'm sure the synchronization between these two, are working fine for the first time and are not working perfectly for the afterward changes.

UnknownProperty: handle null values

ICalendars containing:

ATTACH;ENCODING="BASE64,BASE64";ID=rfc2445.ics;VALUE=BINARY:

seemingly cause the value of the parsed iCalendar property to be null (makes sense, because the value couldn't be decoded, because BASE64,BASE64 is not a valid encoding).

So the property.value.length() access causes an NPE:

if (property.value.length > UnknownProperty.MAX_UNKNOWN_PROPERTY_SIZE) {

Expected result:

Unknown properties without value should be ignored.

Ignore invalid properties

With ical4j/ical4j#454, invalid properties can now be ignored. This feature is available since ical4j 3.2.9.

  • Update to ical4j
  • Enable suppressing invalid properties
  • Test whether everything (including the new "ignore invalid properties") works as expected, maybe add a test for some invalid property

Migrate ical4j 3.x → 4.x

ical4j 4.0 will be released soon, and new development will then only be available in 4.x.

  • Wait until ical4j 4.x is released and stable
  • Create branch that uses ical4j 4.x
  • Make sure that everything is working (including tests)

Update: Because this is such a large change, we should do it with extreme care (as it has the potential to break DAVx5 in many ways), extensive testing and when it's time for it.

CalendarStorageException when infinite RRULE + RDATE present

Discussed in bitfireAT/davx5-ose#402

Originally posted by navid-zamani September 10, 2023
Since there is no place to report bugs, I’m forced to post this here in a free format:

I get the above mentioned error when trying to sync.
I suspect it was caused by a bug in Lightning, but I switched to KOrganzizer as a result and it lets me edit and save the event just fine, which I did, to make sure the event on the Radicale server was valid. Lightning is fine with it now too (but I set it to read-only there, to avoid Lightning messing up some more).

I’ve added the debug info ZIP below, and also added the ICS file from the server to it.
davx5-debug.zip
Everything starting with “My…” is anonymized manually.

I am unable to find anything wrong with the ICS file, and since KOrganizer, Radicale, Thunderbird and an online validator handle it fine now, but DAVx⁵ (or more correctly, Android’s calendar provider or something) does not, all I can do it post it here and hope somebody more experienced can see what’s wrong.

It’s bad because I can’t use my calendar on my phone because of it right now. :/

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.