Skip to content

Commit c487ad9

Browse files
committed
Migrate builders for sub-rows
1 parent 4307e2b commit c487ad9

File tree

14 files changed

+898
-596
lines changed

14 files changed

+898
-596
lines changed

lib/src/androidTest/kotlin/at/bitfire/synctools/mapping/calendar/LegacyAndroidEventBuilder2Test.kt

Lines changed: 0 additions & 506 deletions
Large diffs are not rendered by default.

lib/src/main/kotlin/at/bitfire/synctools/mapping/calendar/LegacyAndroidEventBuilder2.kt

Lines changed: 13 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,10 @@ package at.bitfire.synctools.mapping.calendar
88

99
import android.content.ContentValues
1010
import android.content.Entity
11-
import android.provider.CalendarContract.Attendees
1211
import android.provider.CalendarContract.Colors
1312
import android.provider.CalendarContract.Events
14-
import android.provider.CalendarContract.Reminders
1513
import androidx.core.content.contentValuesOf
1614
import at.bitfire.ical4android.Event
17-
import at.bitfire.ical4android.ICalendar
1815
import at.bitfire.ical4android.util.AndroidTimeUtils
1916
import at.bitfire.ical4android.util.DateUtils
2017
import at.bitfire.ical4android.util.MiscUtils.asSyncAdapter
@@ -26,10 +23,12 @@ import at.bitfire.ical4android.util.TimeApiExtensions.toLocalTime
2623
import at.bitfire.ical4android.util.TimeApiExtensions.toRfc5545Duration
2724
import at.bitfire.ical4android.util.TimeApiExtensions.toZonedDateTime
2825
import at.bitfire.synctools.exception.InvalidLocalResourceException
29-
import at.bitfire.synctools.mapping.calendar.builder.AndroidEventFieldBuilder
26+
import at.bitfire.synctools.mapping.calendar.builder.AndroidEntityBuilder
27+
import at.bitfire.synctools.mapping.calendar.builder.AttendeesBuilder
3028
import at.bitfire.synctools.mapping.calendar.builder.CategoriesBuilder
3129
import at.bitfire.synctools.mapping.calendar.builder.DescriptionBuilder
3230
import at.bitfire.synctools.mapping.calendar.builder.LocationBuilder
31+
import at.bitfire.synctools.mapping.calendar.builder.RemindersBuilder
3332
import at.bitfire.synctools.mapping.calendar.builder.RetainedClassificationBuilder
3433
import at.bitfire.synctools.mapping.calendar.builder.TitleBuilder
3534
import at.bitfire.synctools.mapping.calendar.builder.UnknownPropertiesBuilder
@@ -42,20 +41,14 @@ import net.fortuna.ical4j.model.DateList
4241
import net.fortuna.ical4j.model.DateTime
4342
import net.fortuna.ical4j.model.Parameter
4443
import net.fortuna.ical4j.model.TimeZoneRegistryFactory
45-
import net.fortuna.ical4j.model.component.VAlarm
46-
import net.fortuna.ical4j.model.parameter.Cn
4744
import net.fortuna.ical4j.model.parameter.Email
48-
import net.fortuna.ical4j.model.parameter.PartStat
49-
import net.fortuna.ical4j.model.property.Action
50-
import net.fortuna.ical4j.model.property.Attendee
5145
import net.fortuna.ical4j.model.property.Clazz
5246
import net.fortuna.ical4j.model.property.DtEnd
5347
import net.fortuna.ical4j.model.property.RDate
5448
import net.fortuna.ical4j.model.property.Status
5549
import java.time.Duration
5650
import java.time.Period
5751
import java.time.ZonedDateTime
58-
import java.util.Locale
5952
import java.util.logging.Logger
6053

6154
/**
@@ -80,12 +73,16 @@ class LegacyAndroidEventBuilder2(
8073
private val flags: Int
8174
) {
8275

83-
private val fieldBuilders: Array<AndroidEventFieldBuilder> = arrayOf(
84-
CategoriesBuilder(),
76+
private val fieldBuilders: Array<AndroidEntityBuilder> = arrayOf(
77+
// event fields (order as in CalendarContract.EventsColumns)
78+
TitleBuilder(),
8579
DescriptionBuilder(),
8680
LocationBuilder(),
81+
// sub-rows (alphabetically, by class name)
82+
AttendeesBuilder(calendar),
83+
CategoriesBuilder(),
84+
RemindersBuilder(),
8785
RetainedClassificationBuilder(),
88-
TitleBuilder(),
8986
UnknownPropertiesBuilder(),
9087
UrlBuilder()
9188
)
@@ -105,21 +102,13 @@ class LegacyAndroidEventBuilder2(
105102
)
106103

107104
fun buildEvent(recurrence: Event?): Entity {
108-
// build row from legacy builders
105+
// build main row from legacy builders
109106
val row = buildEventRow(recurrence)
110107

111-
val entity = Entity(row)
112-
val from = recurrence ?: event
113-
114-
for (reminder in from.alarms)
115-
entity.addSubValue(Reminders.CONTENT_URI, buildReminder(reminder))
116-
117-
for (attendee in from.attendees)
118-
entity.addSubValue(Attendees.CONTENT_URI, buildAttendee(attendee))
119-
120108
// additionally apply new builders
109+
val entity = Entity(row)
121110
for (builder in fieldBuilders)
122-
builder.build(from = from, main = event, to = entity)
111+
builder.build(from = recurrence ?: event, main = event, to = entity)
123112

124113
return entity
125114
}
@@ -391,61 +380,4 @@ class LegacyAndroidEventBuilder2(
391380
return row
392381
}
393382

394-
private fun buildAttendee(attendee: Attendee): ContentValues {
395-
val values = ContentValues()
396-
val organizer = event.organizerEmail ?:
397-
/* no ORGANIZER, use current account owner as ORGANIZER */
398-
calendar.ownerAccount ?: calendar.account.name
399-
400-
val member = attendee.calAddress
401-
if (member.scheme.equals("mailto", true)) // attendee identified by email
402-
values.put(Attendees.ATTENDEE_EMAIL, member.schemeSpecificPart)
403-
else {
404-
// attendee identified by other URI
405-
values.put(Attendees.ATTENDEE_ID_NAMESPACE, member.scheme)
406-
values.put(Attendees.ATTENDEE_IDENTITY, member.schemeSpecificPart)
407-
408-
attendee.getParameter<Email>(Parameter.EMAIL)?.let { email ->
409-
values.put(Attendees.ATTENDEE_EMAIL, email.value)
410-
}
411-
}
412-
413-
attendee.getParameter<Cn>(Parameter.CN)?.let { cn ->
414-
values.put(Attendees.ATTENDEE_NAME, cn.value)
415-
}
416-
417-
// type/relation mapping is complex and thus outsourced to AttendeeMappings
418-
AttendeeMappings.iCalendarToAndroid(attendee, values, organizer)
419-
420-
val status = when(attendee.getParameter(Parameter.PARTSTAT) as? PartStat) {
421-
PartStat.ACCEPTED -> Attendees.ATTENDEE_STATUS_ACCEPTED
422-
PartStat.DECLINED -> Attendees.ATTENDEE_STATUS_DECLINED
423-
PartStat.TENTATIVE -> Attendees.ATTENDEE_STATUS_TENTATIVE
424-
PartStat.DELEGATED -> Attendees.ATTENDEE_STATUS_NONE
425-
else /* default: PartStat.NEEDS_ACTION */ -> Attendees.ATTENDEE_STATUS_INVITED
426-
}
427-
values.put(Attendees.ATTENDEE_STATUS, status)
428-
429-
return values
430-
}
431-
432-
private fun buildReminder(alarm: VAlarm): ContentValues {
433-
val method = when (alarm.action?.value?.uppercase(Locale.ROOT)) {
434-
Action.DISPLAY.value,
435-
Action.AUDIO.value -> Reminders.METHOD_ALERT // will trigger an alarm on the Android device
436-
437-
// Note: The calendar provider doesn't support saving specific attendees for email reminders.
438-
Action.EMAIL.value -> Reminders.METHOD_EMAIL
439-
440-
else -> Reminders.METHOD_DEFAULT // won't trigger an alarm on the Android device
441-
}
442-
443-
val minutes = ICalendar.vAlarmToMin(alarm, event, false)?.second ?: Reminders.MINUTES_DEFAULT
444-
445-
return contentValuesOf(
446-
Reminders.METHOD to method,
447-
Reminders.MINUTES to minutes
448-
)
449-
}
450-
451383
}

lib/src/main/kotlin/at/bitfire/synctools/mapping/calendar/builder/AndroidEventFieldBuilder.kt renamed to lib/src/main/kotlin/at/bitfire/synctools/mapping/calendar/builder/AndroidEntityBuilder.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ package at.bitfire.synctools.mapping.calendar.builder
99
import android.content.Entity
1010
import at.bitfire.ical4android.Event
1111

12-
interface AndroidEventFieldBuilder {
12+
interface AndroidEntityBuilder {
1313

1414
/**
15-
* Maps the given event into the provided [Entity].
15+
* Maps a specific part of the given event into the provided [Entity].
1616
*
1717
* If [from] references the same object as [main], this method is called for a main event (not an exception).
1818
* If [from] references another object as [main], this method is called for an exception (not a main event).
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* This file is part of bitfireAT/synctools which is released under GPLv3.
3+
* Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details.
4+
* SPDX-License-Identifier: GPL-3.0-or-later
5+
*/
6+
7+
package at.bitfire.synctools.mapping.calendar.builder
8+
9+
import android.content.ContentValues
10+
import android.content.Entity
11+
import android.provider.CalendarContract.Attendees
12+
import at.bitfire.ical4android.Event
13+
import at.bitfire.synctools.mapping.calendar.AttendeeMappings
14+
import at.bitfire.synctools.storage.calendar.AndroidCalendar
15+
import net.fortuna.ical4j.model.Parameter
16+
import net.fortuna.ical4j.model.parameter.Cn
17+
import net.fortuna.ical4j.model.parameter.Email
18+
import net.fortuna.ical4j.model.parameter.PartStat
19+
import net.fortuna.ical4j.model.property.Attendee
20+
21+
class AttendeesBuilder(
22+
private val calendar: AndroidCalendar
23+
): AndroidEntityBuilder {
24+
25+
override fun build(from: Event, main: Event, to: Entity) {
26+
for (attendee in from.attendees)
27+
to.addSubValue(Attendees.CONTENT_URI, buildAttendee(attendee, from))
28+
}
29+
30+
private fun buildAttendee(attendee: Attendee, event: Event): ContentValues {
31+
val values = ContentValues()
32+
val organizer = event.organizerEmail ?:
33+
/* no ORGANIZER, use current account owner as ORGANIZER */
34+
calendar.ownerAccount ?: calendar.account.name
35+
36+
val member = attendee.calAddress
37+
if (member.scheme.equals("mailto", true)) // attendee identified by email
38+
values.put(Attendees.ATTENDEE_EMAIL, member.schemeSpecificPart)
39+
else {
40+
// attendee identified by other URI
41+
values.put(Attendees.ATTENDEE_ID_NAMESPACE, member.scheme)
42+
values.put(Attendees.ATTENDEE_IDENTITY, member.schemeSpecificPart)
43+
44+
attendee.getParameter<Email>(Parameter.EMAIL)?.let { email ->
45+
values.put(Attendees.ATTENDEE_EMAIL, email.value)
46+
}
47+
}
48+
49+
attendee.getParameter<Cn>(Parameter.CN)?.let { cn ->
50+
values.put(Attendees.ATTENDEE_NAME, cn.value)
51+
}
52+
53+
// type/relation mapping is complex and thus outsourced to AttendeeMappings
54+
AttendeeMappings.iCalendarToAndroid(attendee, values, organizer)
55+
56+
val status = when(attendee.getParameter(Parameter.PARTSTAT) as? PartStat) {
57+
PartStat.ACCEPTED -> Attendees.ATTENDEE_STATUS_ACCEPTED
58+
PartStat.DECLINED -> Attendees.ATTENDEE_STATUS_DECLINED
59+
PartStat.TENTATIVE -> Attendees.ATTENDEE_STATUS_TENTATIVE
60+
PartStat.DELEGATED -> Attendees.ATTENDEE_STATUS_NONE
61+
else /* default: PartStat.NEEDS_ACTION */ -> Attendees.ATTENDEE_STATUS_INVITED
62+
}
63+
values.put(Attendees.ATTENDEE_STATUS, status)
64+
65+
return values
66+
}
67+
68+
}

lib/src/main/kotlin/at/bitfire/synctools/mapping/calendar/builder/CategoriesBuilder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import androidx.core.content.contentValuesOf
1212
import at.bitfire.ical4android.Event
1313
import at.bitfire.synctools.storage.calendar.AndroidEvent2
1414

15-
class CategoriesBuilder: AndroidEventFieldBuilder {
15+
class CategoriesBuilder: AndroidEntityBuilder {
1616

1717
override fun build(from: Event, main: Event, to: Entity) {
1818
val categories = from.categories

lib/src/main/kotlin/at/bitfire/synctools/mapping/calendar/builder/DescriptionBuilder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import android.provider.CalendarContract.Events
1111
import at.bitfire.ical4android.Event
1212
import at.bitfire.vcard4android.Utils.trimToNull
1313

14-
class DescriptionBuilder: AndroidEventFieldBuilder {
14+
class DescriptionBuilder: AndroidEntityBuilder {
1515

1616
override fun build(from: Event, main: Event, to: Entity) {
1717
to.entityValues.put(Events.DESCRIPTION, from.description.trimToNull())

lib/src/main/kotlin/at/bitfire/synctools/mapping/calendar/builder/LocationBuilder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import android.provider.CalendarContract.Events
1111
import at.bitfire.ical4android.Event
1212
import at.bitfire.vcard4android.Utils.trimToNull
1313

14-
class LocationBuilder: AndroidEventFieldBuilder {
14+
class LocationBuilder: AndroidEntityBuilder {
1515

1616
override fun build(from: Event, main: Event, to: Entity) {
1717
to.entityValues.put(Events.EVENT_LOCATION, from.location.trimToNull())
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* This file is part of bitfireAT/synctools which is released under GPLv3.
3+
* Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details.
4+
* SPDX-License-Identifier: GPL-3.0-or-later
5+
*/
6+
7+
package at.bitfire.synctools.mapping.calendar.builder
8+
9+
import android.content.ContentValues
10+
import android.content.Entity
11+
import android.provider.CalendarContract.Reminders
12+
import androidx.core.content.contentValuesOf
13+
import at.bitfire.ical4android.Event
14+
import at.bitfire.ical4android.ICalendar
15+
import net.fortuna.ical4j.model.component.VAlarm
16+
import net.fortuna.ical4j.model.property.Action
17+
import java.util.Locale
18+
19+
class RemindersBuilder: AndroidEntityBuilder {
20+
21+
override fun build(from: Event, main: Event, to: Entity) {
22+
for (reminder in from.alarms)
23+
to.addSubValue(Reminders.CONTENT_URI, buildReminder(reminder, from))
24+
}
25+
26+
private fun buildReminder(alarm: VAlarm, event: Event): ContentValues {
27+
val method = when (alarm.action?.value?.uppercase(Locale.ROOT)) {
28+
Action.DISPLAY.value,
29+
Action.AUDIO.value -> Reminders.METHOD_ALERT // will trigger an alarm on the Android device
30+
31+
// Note: The calendar provider doesn't support saving specific attendees for email reminders.
32+
Action.EMAIL.value -> Reminders.METHOD_EMAIL
33+
34+
else -> Reminders.METHOD_DEFAULT // won't trigger an alarm on the Android device
35+
}
36+
37+
val minutes = ICalendar.vAlarmToMin(alarm, event, false)?.second ?: Reminders.MINUTES_DEFAULT
38+
39+
return contentValuesOf(
40+
Reminders.METHOD to method,
41+
Reminders.MINUTES to minutes
42+
)
43+
}
44+
45+
}

lib/src/main/kotlin/at/bitfire/synctools/mapping/calendar/builder/RetainedClassificationBuilder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import net.fortuna.ical4j.model.property.Clazz
2020
* Should not be returned as an unknown property in the future, but as a specific separate
2121
* extended property.
2222
*/
23-
class RetainedClassificationBuilder: AndroidEventFieldBuilder {
23+
class RetainedClassificationBuilder: AndroidEntityBuilder {
2424

2525
override fun build(from: Event, main: Event, to: Entity) {
2626
val classification = from.classification

lib/src/main/kotlin/at/bitfire/synctools/mapping/calendar/builder/TitleBuilder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import android.provider.CalendarContract.Events
1111
import at.bitfire.ical4android.Event
1212
import at.bitfire.vcard4android.Utils.trimToNull
1313

14-
class TitleBuilder: AndroidEventFieldBuilder {
14+
class TitleBuilder: AndroidEntityBuilder {
1515

1616
override fun build(from: Event, main: Event, to: Entity) {
1717
to.entityValues.put(Events.TITLE, from.summary.trimToNull())

0 commit comments

Comments
 (0)