Skip to content

Commit b3522b1

Browse files
committed
AndroidEventProcessor: also update SEQUENCE
1 parent 6ec2ccc commit b3522b1

File tree

2 files changed

+70
-11
lines changed

2 files changed

+70
-11
lines changed

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

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package at.bitfire.synctools.mapping.calendar
88

99
import android.content.Entity
10+
import android.provider.CalendarContract
1011
import android.provider.CalendarContract.Events
1112
import android.provider.CalendarContract.ExtendedProperties
1213
import at.bitfire.synctools.icalendar.AssociatedEvents
@@ -90,21 +91,27 @@ class AndroidEventProcessor(
9091
/**
9192
* Result of the [mapToVEvents] operation.
9293
*
93-
* @param uid UID of the generated iCalendar
94-
* @param generatedUid whether [uid] was generated by [mapToVEvents] (*false*: UID was already present before mapping)
9594
* @param associatedEvents mapped events
95+
* @param uid UID of the mapped events
96+
* @param generatedUid whether [uid] was generated by [mapToVEvents] (*false*: `UID` was already present before mapping)
97+
* @param updatedSequence the new increased `SEQUENCE` of the main event (*null* if sequence was not increased by [mapToVEvents])
9698
*/
9799
class MappingResult(
100+
val associatedEvents: AssociatedEvents,
98101
val uid: String,
99102
val generatedUid: Boolean,
100-
val associatedEvents: AssociatedEvents
103+
val updatedSequence: Int?
101104
)
102105

103106
/**
104107
* Maps an Android event with its exceptions to VEVENTs.
105108
*
106-
* VEVENTs must have a valid UID, so this method (or better so say, the [UidProcessor] that it calls)
107-
* generates an UID, if necessary.
109+
* VEVENTs must have a valid `UID`, so this method (or better so say, the [UidProcessor] that it calls)
110+
* generates an UID, if necessary. If an `UID` was generated, it is noted in the result.
111+
*
112+
* This method also increases the `SEQUENCE`, if necessary, so that the mapped `SEQUENCE`
113+
* can be different than the original `SEQUENCE` in [eventAndExceptions]. If the `SEQUENCE` was
114+
* increased, it is noted in the result.
108115
*/
109116
fun mapToVEvents(eventAndExceptions: EventAndExceptions): MappingResult {
110117
// make sure that main event has a UID
@@ -114,6 +121,8 @@ class AndroidEventProcessor(
114121
UUID.randomUUID().toString()
115122
}
116123

124+
val updatedSequence = increaseSequence(eventAndExceptions.main)
125+
117126
// map main event
118127
val main = populateEvent(
119128
entity = eventAndExceptions.main,
@@ -149,9 +158,10 @@ class AndroidEventProcessor(
149158
prodId = generateProdId(eventAndExceptions.main)
150159
)
151160
return MappingResult(
161+
associatedEvents = mappedEvents,
152162
uid = uid,
153163
generatedUid = generatedUid,
154-
associatedEvents = mappedEvents
164+
updatedSequence = updatedSequence
155165
)
156166
}
157167

@@ -179,6 +189,47 @@ class AndroidEventProcessor(
179189
return prodIdGenerator.generateProdId(packages)
180190
}
181191

192+
/**
193+
* Increases the event's SEQUENCE, if necessary.
194+
*
195+
* @param main event to be checked (**will be modified** when SEQUENCE needs to be increased)
196+
*
197+
* @return updated sequence (or *null* if sequence was not increased/modified)
198+
*/
199+
private fun increaseSequence(main: Entity): Int? {
200+
val mainValues = main.entityValues
201+
202+
val weAreOrganizer: Boolean by lazy {
203+
mainValues.getAsInteger(Events.IS_ORGANIZER) == 1
204+
}
205+
val groupScheduled: Boolean by lazy {
206+
main.subValues.any { it.uri == CalendarContract.Attendees.CONTENT_URI }
207+
}
208+
209+
val currentSeq = mainValues.getAsInteger(EventsContract.COLUMN_SEQUENCE)
210+
return when {
211+
currentSeq == null -> {
212+
/* First upload, request to set to 0 in calendar provider after upload.
213+
We can let it empty in the Entity because then no SEQUENCE property will be generated,
214+
which is equal to SEQUENCE:0. */
215+
0
216+
}
217+
// currentSequence != null for the following branches
218+
groupScheduled && weAreOrganizer -> {
219+
/* Upload of a group-scheduled event and we are the organizer, so we increase the SEQUENCE.
220+
We also have to store it into the Entity so that it the new value will be mapped. */
221+
(currentSeq + 1).also { newSeq ->
222+
mainValues.put(EventsContract.COLUMN_SEQUENCE, newSeq)
223+
}
224+
}
225+
else -> {
226+
/* Standard upload (either not group-scheduled or we are not the organizer). We don't
227+
increase the SEQUENCE and so we don't have to modify the Entity, too. */
228+
null
229+
}
230+
}
231+
}
232+
182233
/**
183234
* Reads data of an event from the calendar provider, i.e. converts the [entity] values into a [VEvent].
184235
*
@@ -198,23 +249,23 @@ class AndroidEventProcessor(
198249
* Makes sure that the event has a UID ([Events.UID_2445] in the main event row).
199250
*
200251
* If the event doesn't have a UID, a new one is generated using [uidGenerator] and
201-
* put into [entity].
252+
* put into [main].
202253
*
203-
* @param entity event to be checked (**will be modified** if it doesn't already have a UID)
254+
* @param main event to be checked (**will be modified** if it doesn't already have a UID)
204255
*/
205256
private fun provideUid(
206-
entity: Entity,
257+
main: Entity,
207258
generateUid: () -> String
208259
): String {
209-
val mainValues = entity.entityValues
260+
val mainValues = main.entityValues
210261
val existingUid = mainValues.getAsString(Events.UID_2445)
211262
if (existingUid != null) {
212263
// UID already present, nothing to do
213264
return existingUid
214265
}
215266

216267
// have a look at extended properties (Google Calendar)
217-
val googleCalendarUid = entity.subValues.firstOrNull {
268+
val googleCalendarUid = main.subValues.firstOrNull {
218269
it.uri == ExtendedProperties.CONTENT_URI &&
219270
it.values.getAsString(ExtendedProperties.NAME) == EventsContract.EXTNAME_GOOGLE_CALENDAR_UID
220271
}?.values?.getAsString(ExtendedProperties.VALUE)

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,29 @@ object EventsContract {
1919

2020
/**
2121
* Custom sync column to store the last known ETag of an event.
22+
*
23+
* Type: [String]
2224
*/
2325
const val COLUMN_ETAG = CalendarContract.Events.SYNC_DATA1
2426

2527
/**
2628
* Custom sync column to store sync flags of an event.
29+
*
30+
* Type: [Int]
2731
*/
2832
const val COLUMN_FLAGS = CalendarContract.Events.SYNC_DATA2
2933

3034
/**
3135
* Custom sync column to store the SEQUENCE of an event.
36+
*
37+
* Type: [Int]
3238
*/
3339
const val COLUMN_SEQUENCE = CalendarContract.Events.SYNC_DATA3
3440

3541
/**
3642
* Custom sync column to store the Schedule-Tag of an event.
43+
*
44+
* Type: [String]
3745
*/
3846
const val COLUMN_SCHEDULE_TAG = CalendarContract.Events.SYNC_DATA4
3947

0 commit comments

Comments
 (0)