diff options
author | Ricki Hirner <hirner@bitfire.at> | 2022-03-23 19:02:31 +0300 |
---|---|---|
committer | Ricki Hirner <hirner@bitfire.at> | 2022-03-23 19:43:01 +0300 |
commit | 6c2cb6dd380fe897ce42ca173180f8970f2eea26 (patch) | |
tree | bd389d28101b40519fef0e74b24dc1a203143923 | |
parent | 9bca5faccdd7095f1ded5fcdb7bffd571584a34c (diff) |
Create fake main event for exceptions of recurring events where the main event is missing
* fixes bitfireAT/icsx5#27
* fixes bitfireAT/davx5#58
4 files changed, 48 insertions, 7 deletions
diff --git a/src/main/java/at/bitfire/ical4android/AndroidEvent.kt b/src/main/java/at/bitfire/ical4android/AndroidEvent.kt index 3005d30..a465c06 100644 --- a/src/main/java/at/bitfire/ical4android/AndroidEvent.kt +++ b/src/main/java/at/bitfire/ical4android/AndroidEvent.kt @@ -573,10 +573,10 @@ abstract class AndroidEvent( (it checks for RRULE and aborts if no RRULE is found). So I have chosen the method of inserting the exception event manually. - It's also noteworthy that the link between the main event and the exception is not - between ID and ORIGINAL_ID (as one could assume), but between _SYNC_ID and ORIGINAL_SYNC_ID. - So, if you don't set _SYNC_ID in the master event and ORIGINAL_SYNC_ID in the exception, - the exception will appear additionally (and not *instead* of the instance). + It's also noteworthy that linking the main event to the exception only works using _SYNC_ID + and ORIGINAL_SYNC_ID (and not ID and ORIGINAL_ID, as one could assume). So, if you don't + set _SYNC_ID in the main event and ORIGINAL_SYNC_ID in the exception, the exception will + appear additionally (and not *instead* of the instance). */ val recurrenceId = exception.recurrenceId diff --git a/src/main/java/at/bitfire/ical4android/Event.kt b/src/main/java/at/bitfire/ical4android/Event.kt index d96ac73..fcbb67f 100644 --- a/src/main/java/at/bitfire/ical4android/Event.kt +++ b/src/main/java/at/bitfire/ical4android/Event.kt @@ -94,7 +94,6 @@ class Event: ICalendar() { Ical4Android.log.fine("Assigning exceptions to main events") val mainEvents = mutableMapOf<String /* UID */,VEvent>() val exceptions = mutableMapOf<String /* UID */,MutableMap<String /* RECURRENCE-ID */,VEvent>>() - for (vEvent in vEvents) { val uid = vEvent.uid.value val sequence = vEvent.sequence?.sequenceNo ?: 0 @@ -124,11 +123,17 @@ class Event: ICalendar() { } } + /* There may be UIDs which have only RECURRENCE-ID entries and not a main entry (for instance, a recurring + event with an exception where the current user has been invited only to this exception. In this case, + the UID will not appear in mainEvents but only in exceptions. */ + val events = mutableListOf<Event>() for ((uid, vEvent) in mainEvents) { val event = fromVEvent(vEvent) - exceptions[uid]?.let { eventExceptions -> - event.exceptions.addAll(eventExceptions.map { (_,it) -> fromVEvent(it) }) + + // assign exceptions to main event and then remove them from exceptions array + exceptions.remove(uid)?.let { eventExceptions -> + event.exceptions.addAll(eventExceptions.values.map { fromVEvent(it) }) } // make sure that exceptions have at least a SUMMARY @@ -137,6 +142,16 @@ class Event: ICalendar() { events += event } + for ((uid, onlyExceptions) in exceptions) { + Ical4Android.log.info("UID $uid doesn't have a main event but only exceptions: $onlyExceptions") + + // create a fake main event from the first exception + val fakeEvent = fromVEvent(onlyExceptions.values.first()) + fakeEvent.exceptions.addAll(onlyExceptions.values.map { fromVEvent(it) }) + + events += fakeEvent + } + return events } diff --git a/src/test/java/at/bitfire/ical4android/EventTest.kt b/src/test/java/at/bitfire/ical4android/EventTest.kt index e4d25c8..6842974 100644 --- a/src/test/java/at/bitfire/ical4android/EventTest.kt +++ b/src/test/java/at/bitfire/ical4android/EventTest.kt @@ -158,6 +158,21 @@ class EventTest { } @Test + fun testRecurringOnlyException() { + val event = parseCalendar("recurring-only-exception.ics").first() + + assertEquals(1, event.exceptions.size) + val exception = event.exceptions.first + assertEquals("20150503T010203Z", exception.recurrenceId!!.value) + assertEquals("This is an exception", exception.summary) + + // fake main event + assertEquals(event.summary, exception.summary) + assertEquals(event.dtStart, exception.dtStart) + assertEquals(event.dtEnd, exception.dtEnd) + } + + @Test fun testStartEndTimes() { // event with start+end date-time val eViennaEvolution = parseCalendar("vienna-evolution.ics").first() diff --git a/src/test/resources/events/recurring-only-exception.ics b/src/test/resources/events/recurring-only-exception.ics new file mode 100644 index 0000000..26012e1 --- /dev/null +++ b/src/test/resources/events/recurring-only-exception.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +VERSION:2.0 +BEGIN:VEVENT +UID:fcb42e4d-bc6e-4499-97f0-6616a02da7bc +RECURRENCE-ID:20150503T010203Z +DTSTART:20150503T010203Z +DTEND:20150504T010203Z +SUMMARY:This is an exception +DESCRIPTION:The main event is not visible for us. +END:VEVENT +END:VCALENDAR |