bitfireat / ical4android Goto Github PK
View Code? Open in Web Editor NEWAllows usage of iCalendar files with the Android calendar provider
License: GNU General Public License v3.0
Allows usage of iCalendar files with the Android calendar provider
License: GNU General Public License v3.0
VTIMEZONE is wrongly registered tried to be mapped on an ICalObject, needs to be fixed
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
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:
ical4android/lib/src/main/kotlin/at/bitfire/ical4android/AndroidTask.kt
Lines 294 to 301 in 76e30b7
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
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).
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:
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.
We should use an extension function Uri.asSyncAdapter(account: Account)
instead of having syncAdapterUri()
methods in various places.
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)
userAgents
from Event
up to ICalendar
taskList.provider
to task.userAgents
in AndroidTask.populateTask()
ical4android/src/main/java/at/bitfire/ical4android/Event.kt
Lines 213 to 216 in 6d87d43
ICalendar.prodId()
which generates the PRODID
with the userAgents (instead of var prodId
).prodId()
method in Event.write()
and Task.write()
PRODID
test in TaskTest.testWrite()
@patrickunterwegs Where should we add the test in JtxICalObjectTest?
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.
--no-daemon
call in the CodeQL CI, because its not necessarygradle assemble
. Otherwise keep the assemble task.See ical4j/ical4j#641
I think it's only a one-letter PR + some tests :)
Maybe you can have a look at it? @ArnyminerZ
If there are questions just post here or we also can have a talk. Did you already check out and compile / run ical4j tests before?
Depends on ical4j/ical4j#641
Depends on ical4j/ical4j#643
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:
MiscUtils.removeEmptyStrings
so that it removes not only empty, but also blank (= only white-space, including newline) values + testMiscUtils.removeEmptyStrings
to populateTask()
like it's done for populateEvent()
?When importing an iCalendar with a given UID, Google Calendar doesn't set the UID_2445 field, but instead generates an ExtendedProperty named iCalUid
that contains the UID.
ical4android/DAVx⁵ currently doesn't process this ExtendedProperty and thus ignores the UID. We could instead take the UID from iCalUid
when UID_2445 == null
.
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
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:
Objects should be immutable or at least less stateful; API should be well-designed, consistent and well-tested.
Open questions and ideas:
See also bitfireAT/vcard4android#2
Caused by: java.text.ParseException: Unparseable date: ":20220331T084614Z"
.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 :)
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)
[...]
Events are validated and/or modified/repaired when
These validation rules shouldn't be mixed with other code and should have a proper module and tests.
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:
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:
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.
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.
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.
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 eventsYYYYMMDD,YYYYMMDD
for all-day eventsCurrently we don't handle multiple lines with multiple time zones in our conversion methods.
This is necessary/useful as also jtx Board is now using ical4android directly
ical4j comes with a time zone database that is used for its calculations and for instance to generate VTIMEZONE
s 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
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…
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.
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 dependencysample-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?
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
Reference: https://github.com/bitfireAT/davx5/issues/41
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
ical4android/src/main/java/at/bitfire/ical4android/Event.kt
Lines 264 to 267 in 9c970b5
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 ?
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).
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
We need a solution for bitfireAT/davx5-ose#133 until #56 is finished (and on Android level <26). Problem:
DTSTART;TZID=Europe/Kyiv:…
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.
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:
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.
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:
Expected result:
Unknown properties without value
should be ignored.
With ical4j/ical4j#454, invalid properties can now be ignored. This feature is available since ical4j 3.2.9.
ical4j 4.0 will be released soon, and new development will then only be available in 4.x.
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.
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. :/
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.