Skip to content

Commit 017b112

Browse files
committed
Simplify numDirectInstances / numInstances to only numInstances
1 parent 706c725 commit 017b112

File tree

2 files changed

+33
-129
lines changed

2 files changed

+33
-129
lines changed

lib/src/androidTest/kotlin/at/bitfire/synctools/storage/calendar/AndroidCalendarTest.kt

Lines changed: 22 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -286,42 +286,42 @@ class AndroidCalendarTest {
286286
// event instances
287287

288288
@Test
289-
fun testNumDirectInstances_SingleInstance() {
289+
fun testNumInstances_SingleInstance() {
290290
val id = calendar.addEvent(Entity(contentValuesOf(
291291
Events.CALENDAR_ID to calendar.id,
292292
Events.DTSTART to now,
293293
Events.DTEND to now + 3600000,
294294
Events.TITLE to "Event with 1 instance"
295295
)))
296-
assertEquals(1, calendar.numDirectInstances(id))
296+
assertEquals(1, calendar.numInstances(id))
297297
}
298298

299299
@Test
300-
fun testNumDirectInstances_Recurring() {
300+
fun testNumInstances_Recurring() {
301301
val id = calendar.addEvent(Entity(contentValuesOf(
302302
Events.CALENDAR_ID to calendar.id,
303303
Events.DTSTART to now,
304304
Events.DURATION to "PT1H",
305305
Events.TITLE to "Event with 5 instances",
306306
Events.RRULE to "FREQ=DAILY;COUNT=5"
307307
)))
308-
assertEquals(5, calendar.numDirectInstances(id))
308+
assertEquals(5, calendar.numInstances(id))
309309
}
310310

311311
@Test
312-
fun testNumDirectInstances_Recurring_Endless() {
312+
fun testNumInstances_Recurring_Endless() {
313313
val id = calendar.addEvent(Entity(contentValuesOf(
314314
Events.CALENDAR_ID to calendar.id,
315315
Events.DTSTART to now,
316316
Events.DURATION to "PT1H",
317317
Events.TITLE to "Event without end",
318318
Events.RRULE to "FREQ=DAILY"
319319
)))
320-
assertNull(calendar.numDirectInstances(id))
320+
assertNull(calendar.numInstances(id))
321321
}
322322

323323
@Test
324-
fun testNumDirectInstances_Recurring_LateEnd() {
324+
fun testNumInstances_Recurring_LateEnd() {
325325
val id = calendar.addEvent(Entity(contentValuesOf(
326326
Events.CALENDAR_ID to calendar.id,
327327
Events.DTSTART to 1642640523000,
@@ -331,13 +331,13 @@ class AndroidCalendarTest {
331331
)))
332332

333333
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
334-
assertEquals(52, calendar.numDirectInstances(id))
334+
assertEquals(52, calendar.numInstances(id))
335335
else // year 2074 is not supported by Android <11 Calendar Storage
336-
assertNull(calendar.numDirectInstances(id))
336+
assertNull(calendar.numInstances(id))
337337
}
338338

339339
@Test
340-
fun testNumDirectInstances_Recurring_ManyInstances() {
340+
fun testNumInstances_Recurring_Until() {
341341
val id = calendar.addEvent(Entity(contentValuesOf(
342342
Events.CALENDAR_ID to calendar.id,
343343
Events.DTSTART to 1642640523000,
@@ -350,12 +350,12 @@ class AndroidCalendarTest {
350350
365 * 2 // Android <9: does not include UNTIL (incorrect!)
351351
else
352352
365 * 2 + 1, // Android ≥9: includes UNTIL (correct)
353-
calendar.numDirectInstances(id)
353+
calendar.numInstances(id)
354354
)
355355
}
356356

357357
@Test
358-
fun testNumDirectInstances_RecurringWithExdate() {
358+
fun testNumInstances_RecurringWithExdate() {
359359
val id = calendar.addEvent(Entity(contentValuesOf(
360360
Events.CALENDAR_ID to calendar.id,
361361
Events.DTSTART to 1642640523000,
@@ -364,18 +364,18 @@ class AndroidCalendarTest {
364364
Events.RRULE to "FREQ=DAILY;COUNT=5",
365365
Events.EXDATE to "20220121T010203Z"
366366
)))
367-
assertEquals(4, calendar.numDirectInstances(id))
367+
assertEquals(4, calendar.numInstances(id))
368368
}
369369

370370
@Test
371-
fun testNumDirectInstances_RecurringWithExceptions() {
371+
fun testNumInstances_RecurringWithExceptions_MatchingOrigInstanceTime() {
372372
val syncId = "recurring-with-exceptions"
373373
val id = calendar.addEvent(Entity(contentValuesOf(
374374
Events.CALENDAR_ID to calendar.id,
375375
Events._SYNC_ID to syncId,
376376
Events.DTSTART to 1642640523000,
377377
Events.DURATION to "PT1H",
378-
Events.TITLE to "Event with 5 instances, two of them excluded",
378+
Events.TITLE to "Event with 5 instances, two of them are exceptions",
379379
Events.RRULE to "FREQ=DAILY;COUNT=5"
380380
)))
381381
calendar.addEvent(Entity(contentValuesOf(
@@ -394,88 +394,19 @@ class AndroidCalendarTest {
394394
Events.DURATION to "PT1H",
395395
Events.TITLE to "Exception on 5th day",
396396
)))
397-
assertEquals(5 - 2, calendar.numDirectInstances(id))
398-
}
399-
400-
@Test
401-
fun testNumInstances_SingleInstance() {
402-
val id = calendar.addEvent(Entity(contentValuesOf(
403-
Events.CALENDAR_ID to calendar.id,
404-
Events.DTSTART to now,
405-
Events.DTEND to now + 3600000,
406-
Events.TITLE to "Event with 1 instance"
407-
)))
408-
assertEquals(1, calendar.numInstances(id))
397+
assertEquals(5 - 2, calendar.numInstances(id))
409398
}
410399

411400
@Test
412-
fun testNumInstances_Recurring() {
413-
val id = calendar.addEvent(Entity(contentValuesOf(
414-
Events.CALENDAR_ID to calendar.id,
415-
Events.DTSTART to now,
416-
Events.DURATION to "PT1H",
417-
Events.TITLE to "Event with 5 instances",
418-
Events.RRULE to "FREQ=DAILY;COUNT=5"
419-
)))
420-
assertEquals(5, calendar.numInstances(id))
421-
}
422-
423-
@Test
424-
fun testNumInstances_Recurring_Endless() {
425-
val id = calendar.addEvent(Entity(contentValuesOf(
426-
Events.CALENDAR_ID to calendar.id,
427-
Events.DTSTART to now,
428-
Events.DURATION to "PT1H",
429-
Events.TITLE to "Event without end",
430-
Events.RRULE to "FREQ=DAILY"
431-
)))
432-
assertNull(calendar.numInstances(id))
433-
}
434-
435-
@Test
436-
fun testNumInstances_Recurring_LateEnd() {
437-
val id = calendar.addEvent(Entity(contentValuesOf(
438-
Events.CALENDAR_ID to calendar.id,
439-
Events.DTSTART to 1642640523000,
440-
Events.DURATION to "PT1H",
441-
Events.TITLE to "Event until 2074",
442-
Events.RRULE to "FREQ=YEARLY;UNTIL=20740119T010203Z"
443-
)))
444-
445-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
446-
assertEquals(52, calendar.numInstances(id))
447-
else
448-
assertNull(calendar.numInstances(id))
449-
}
450-
451-
@Test
452-
fun testNumInstances_Recurring_ManyInstances() {
453-
val id = calendar.addEvent(Entity(contentValuesOf(
454-
Events.CALENDAR_ID to calendar.id,
455-
Events.DTSTART to 1642640523000,
456-
Events.DURATION to "PT1H",
457-
Events.TITLE to "Event with 2 years",
458-
Events.RRULE to "FREQ=DAILY;UNTIL=20240120T010203Z"
459-
)))
460-
assertEquals(
461-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P)
462-
365 * 2 // Android <9: does not include UNTIL (incorrect!)
463-
else
464-
365 * 2 + 1, // Android ≥9: includes UNTIL (correct)
465-
calendar.numInstances(id)
466-
)
467-
}
468-
469-
@Test
470-
fun testNumInstances_RecurringWithExceptions() {
401+
fun testNumInstances_RecurringWithExceptions_NotMatchingOrigInstanceTime() {
471402
val syncId = "recurring-with-exceptions"
472403
val id = calendar.addEvent(Entity(contentValuesOf(
473404
Events.CALENDAR_ID to calendar.id,
474405
Events._SYNC_ID to syncId,
475406
Events.DTSTART to 1642640523000,
476407
Events.DURATION to "PT1H",
477-
Events.TITLE to "Event with 6 instances",
478-
Events.RRULE to "FREQ=DAILY;COUNT=6"
408+
Events.TITLE to "Event with 5 instances, two of them are exceptions",
409+
Events.RRULE to "FREQ=DAILY;COUNT=5"
479410
)))
480411
calendar.addEvent(Entity(contentValuesOf(
481412
Events.CALENDAR_ID to calendar.id,
@@ -488,13 +419,12 @@ class AndroidCalendarTest {
488419
calendar.addEvent(Entity(contentValuesOf(
489420
Events.CALENDAR_ID to calendar.id,
490421
Events.ORIGINAL_SYNC_ID to syncId,
491-
Events.ORIGINAL_INSTANCE_TIME to 1642640523000 + 4*86400000,
422+
Events.ORIGINAL_INSTANCE_TIME to 1642640523000 + 4*86400000 + 100, // doesn't match original instance time!
492423
Events.DTSTART to 1642640523000 + 4*86400000 + 3600000, // one hour later
493424
Events.DURATION to "PT1H",
494-
Events.TITLE to "Exception on 5th day",
425+
Events.TITLE to "Exception on 5th day (wrong instance time)",
495426
)))
496-
497-
assertEquals(6, calendar.numInstances(id))
427+
assertEquals(5 - 1, calendar.numInstances(id))
498428
}
499429

500430
}

lib/src/main/kotlin/at/bitfire/synctools/storage/calendar/AndroidCalendar.kt

Lines changed: 11 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import android.provider.CalendarContract.Calendars
1616
import android.provider.CalendarContract.Events
1717
import android.provider.CalendarContract.EventsEntity
1818
import android.provider.CalendarContract.ExtendedProperties
19+
import android.provider.CalendarContract.Instances
1920
import android.provider.CalendarContract.Reminders
2021
import at.bitfire.ical4android.UnknownProperty
2122
import at.bitfire.ical4android.util.MiscUtils.asSyncAdapter
@@ -342,24 +343,25 @@ class AndroidCalendar(
342343

343344

344345

345-
// event instances (these methods operate directly with event IDs and without the events themselves and thus belong to the calendar class)
346+
// event instances (these methods operate directly with event IDs and without the events
347+
// themselves and thus belong to the calendar class)
346348

347349
/**
348-
* Finds the amount of direct instances this event has (without exceptions); used by [numInstances]
349-
* to find the number of instances of exceptions.
350+
* Finds the amount of direct instances this event has. Exceptions have their own instances
351+
* and are not taken into account by this value.
350352
*
351-
* The number of returned instances may vary with the Android version.
353+
* Use [numInstances] to find the total number of instances (including exceptions) of this event.
352354
*
353355
* @return number of direct event instances (not counting instances of exceptions); *null* if
354356
* the number can't be determined or if the event has no last date (recurring event without last instance)
355357
*
356358
* @throws LocalStorageException when the content provider returns an error
357359
*/
358-
fun numDirectInstances(eventId: Long): Int? {
360+
fun numInstances(eventId: Long): Int? {
359361
// query event to get first and last instance
360362
var first: Long? = null
361363
var last: Long? = null
362-
getEventRow(id, arrayOf(Events.DTSTART, Events.LAST_DATE))?.let { values ->
364+
getEventRow(eventId, arrayOf(Events.DTSTART, Events.LAST_DATE))?.let { values ->
363365
first = values.getAsLong(Events.DTSTART)
364366
last = values.getAsLong(Events.LAST_DATE)
365367
}
@@ -370,7 +372,7 @@ class AndroidCalendar(
370372
/* We can't use Long.MIN_VALUE and Long.MAX_VALUE because Android generates the instances
371373
on the fly and it doesn't accept those values. So we use the first/last actual occurrence
372374
of the event (as calculated by Android). */
373-
val instancesUri = CalendarContract.Instances.CONTENT_URI.asSyncAdapter(account)
375+
val instancesUri = Instances.CONTENT_URI.asSyncAdapter(account)
374376
.buildUpon()
375377
.appendPath(first.toString()) // begin timestamp
376378
.appendPath(last.toString()) // end timestamp
@@ -379,8 +381,8 @@ class AndroidCalendar(
379381
var numInstances: Int? = null
380382
try {
381383
client.query(
382-
instancesUri, null,
383-
"${CalendarContract.Instances.EVENT_ID}=?", arrayOf(eventId.toString()),
384+
instancesUri, arrayOf(/* we're only interested in the number of results */),
385+
"${Instances.EVENT_ID}=?", arrayOf(eventId.toString()),
384386
null
385387
)?.use { cursor ->
386388
numInstances = cursor.count
@@ -391,34 +393,6 @@ class AndroidCalendar(
391393
return numInstances
392394
}
393395

394-
/**
395-
* Finds the total number of instances this event has (including instances of exceptions)
396-
*
397-
* The number of returned instances may vary with the Android version.
398-
*
399-
* @return number of event instances (including instances of exceptions); *null* if
400-
* the number can't be determined or if the event has no last date (recurring event without last instance)
401-
*/
402-
fun numInstances(eventId: Long): Int? {
403-
// num instances of the main event
404-
val numDirectInstances = numDirectInstances(eventId) ?: return null
405-
406-
// add the number of instances of every main event's exception
407-
var numExInstances = 0
408-
iterateEventRows(
409-
arrayOf(Events._ID),
410-
"${Events.ORIGINAL_ID}=?", // get exception events of the main event
411-
arrayOf(eventId.toString())
412-
) { values ->
413-
val exceptionEventId = values.getAsLong(Events._ID)
414-
val exceptionInstances = numDirectInstances(exceptionEventId)
415-
416-
if (exceptionInstances != null)
417-
numExInstances += exceptionInstances
418-
}
419-
return numDirectInstances - numExInstances
420-
}
421-
422396

423397
// shortcuts to upper level
424398

0 commit comments

Comments
 (0)