fix(import): don't lose Fossify's contact birthdays and anniversaries (#225)

Found the actual cause. Fossify mirrors Contacts birthdays and
anniversaries with startTS == endTS (MainActivity), so its exporter's
dayCode(endTS + 12h) rounds back to the starting day and writes
DTEND == DTSTART. We read that literally: a yearly series with
DURATION:P0D, which the provider expands into no instances at all. The
import reported success and the events were nowhere — matching the
report exactly ("birthdays, memorials, dates").

An all-day event may no longer end at or before it starts, and an all-day
DURATION is floored at P1D. Verified against the released parser, which
returns days=0.0 for all three fixture events.

Do not generalise this into sniffing PRODID: the same exporter is correct
for UI-created events (endTS anchors at noon of the last day) and for
CalDAV rows, and Fossify's bundled holiday files are conformant while
naming Fossify in their PRODID. Both are kept as fixtures.

Also: existingUids counted DELETED rows, so re-importing after deleting
events skipped everything as duplicates.

Closes #225
This commit is contained in:
2026-08-19 20:38:41 +02:00
parent 337785a426
commit 4d9f35b849
5 changed files with 126 additions and 21 deletions

View File

@@ -800,8 +800,12 @@ class AndroidCalendarDataSource @Inject constructor(
override fun existingUids(calendarId: Long): Set<String> = resolver.query(
CalendarContract.Events.CONTENT_URI,
arrayOf(CalendarContract.Events.UID_2445),
// DELETED rows linger until a sync adapter purges them; counting those
// as present would make a re-import skip everything the user has since
// deleted, reporting "all duplicates" and importing nothing.
"${CalendarContract.Events.CALENDAR_ID} = ? AND " +
"${CalendarContract.Events.UID_2445} IS NOT NULL",
"${CalendarContract.Events.UID_2445} IS NOT NULL AND " +
"${CalendarContract.Events.DELETED} = 0",
arrayOf(calendarId.toString()),
null,
)?.use { c ->