Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import net.fortuna.ical4j.model.PropertyContainer
import net.fortuna.ical4j.model.PropertyList
import net.fortuna.ical4j.model.component.CalendarComponent
import net.fortuna.ical4j.model.component.VEvent
import net.fortuna.ical4j.model.property.DtEnd
import net.fortuna.ical4j.model.property.DtStart
import net.fortuna.ical4j.model.property.RecurrenceId
import net.fortuna.ical4j.model.property.Sequence
Expand Down Expand Up @@ -52,6 +53,10 @@ fun <T: Temporal> CalendarComponent.dtStart(): DtStart<T>? {
return getProperty<DtStart<T>>(Property.DTSTART).getOrNull()
}

fun <T: Temporal> CalendarComponent.dtEnd(): DtEnd<T>? {
return getProperty<DtEnd<T>>(Property.DTEND).getOrNull()
}

fun <T: Temporal> VEvent.requireDtStart(): DtStart<T> =
getProperty<DtStart<T>>(Property.DTSTART).getOrNull() ?: throw InvalidICalendarException("Missing DTSTART in VEVENT")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,8 @@ import at.bitfire.synctools.mapping.calendar.handler.UnknownPropertiesHandler
import at.bitfire.synctools.mapping.calendar.handler.UrlHandler
import at.bitfire.synctools.storage.calendar.EventAndExceptions
import at.bitfire.synctools.storage.calendar.EventsContract
import net.fortuna.ical4j.model.DateList
import net.fortuna.ical4j.model.Property
import net.fortuna.ical4j.model.TimeZoneRegistryFactory
import net.fortuna.ical4j.model.component.VEvent
import net.fortuna.ical4j.model.parameter.Value
import net.fortuna.ical4j.model.property.ExDate
import net.fortuna.ical4j.model.property.ProdId
import net.fortuna.ical4j.model.property.RDate
Expand All @@ -58,18 +55,16 @@ class AndroidEventHandler(
private val prodIdGenerator: ProdIdGenerator
) {

private val tzRegistry = TimeZoneRegistryFactory.getInstance().createRegistry()

private val fieldHandlers: Array<AndroidEventFieldHandler> = arrayOf(
// event row fields
UidHandler(),
OriginalInstanceTimeHandler(tzRegistry),
OriginalInstanceTimeHandler(),
TitleHandler(),
LocationHandler(),
StartTimeHandler(tzRegistry),
EndTimeHandler(tzRegistry),
DurationHandler(tzRegistry),
RecurrenceFieldsHandler(tzRegistry),
StartTimeHandler(),
EndTimeHandler(),
DurationHandler(),
RecurrenceFieldsHandler(),
DescriptionHandler(),
ColorHandler(),
AccessLevelHandler(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import android.content.Entity
import android.provider.CalendarContract.Events
import android.provider.CalendarContract.ExtendedProperties
import at.bitfire.ical4android.UnknownProperty
import at.bitfire.synctools.icalendar.plusAssign
import net.fortuna.ical4j.model.component.VEvent
import net.fortuna.ical4j.model.property.Clazz
import net.fortuna.ical4j.model.property.immutable.ImmutableClazz
import org.json.JSONException

class AccessLevelHandler: AndroidEventFieldHandler {
Expand All @@ -20,22 +22,21 @@ class AccessLevelHandler: AndroidEventFieldHandler {
val values = from.entityValues

// take classification from main row
TODO("ical4j 4.x")
/*val classification = when (values.getAsInteger(Events.ACCESS_LEVEL)) {
val classification = when (values.getAsInteger(Events.ACCESS_LEVEL)) {
Events.ACCESS_PUBLIC ->
Clazz.PUBLIC
ImmutableClazz.PUBLIC

Events.ACCESS_PRIVATE ->
Clazz.PRIVATE
ImmutableClazz.PRIVATE

Events.ACCESS_CONFIDENTIAL ->
Clazz.CONFIDENTIAL
ImmutableClazz.CONFIDENTIAL

else *//* Events.ACCESS_DEFAULT *//* ->
else /* Events.ACCESS_DEFAULT */ ->
retainedClassification(from)
}
if (classification != null)
to.properties += classification*/
to += classification
}

private fun retainedClassification(from: Entity): Clazz? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ import net.fortuna.ical4j.model.Date
import net.fortuna.ical4j.model.DateTime
import net.fortuna.ical4j.model.TimeZoneRegistry
import net.fortuna.ical4j.util.TimeZones
import java.time.Instant
import java.time.LocalDate
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.ZonedDateTime
import java.time.temporal.Temporal

/**
* Converts timestamps from the [android.provider.CalendarContract.Events.DTSTART] or [android.provider.CalendarContract.Events.DTEND]
Expand All @@ -25,17 +30,41 @@ class AndroidTimeField(
private val timestamp: Long,
private val timeZone: String?,
private val allDay: Boolean,
private val tzRegistry: TimeZoneRegistry
private val tzRegistry: TimeZoneRegistry? = null
) {

/** ID of system default timezone */
private val defaultTzId by lazy { ZoneId.systemDefault().id }

/**
* Converts the given Android date/time into java time temporal object.
*
* @return `Loca` in case of an all-day event, `DateTime` in case of a non-all-day event
*/
fun asTemporal(): Temporal {
val instant = Instant.ofEpochMilli(timestamp)

if (allDay)
return LocalDate.ofInstant(instant, ZoneId.systemDefault())

// non-all-day
val tzId = timeZone
?: ZoneId.systemDefault().id // safe fallback (should never be used/needed because the calendar provider requires EVENT_TIMEZONE)

val timezone = if (tzId == AndroidTimeUtils.TZID_UTC || tzId == TimeZones.UTC_ID || tzId == TimeZones.IBM_UTC_ID)
ZoneOffset.UTC
else
ZoneId.of(tzId)

return ZonedDateTime.ofInstant(instant, timezone)
}

/**
* Converts the given Android date/time into an ical4j date property.
*
* @return `Date` in case of an all-day event, `DateTime` in case of a non-all-day event
*/
@Deprecated("Use asTemporal() instead.")
fun asIcal4jDate(): Date {
if (allDay)
return Date(timestamp)
Expand All @@ -54,7 +83,7 @@ class AndroidTimeField(
val timezone = if (tzId == AndroidTimeUtils.TZID_UTC || tzId == TimeZones.UTC_ID || tzId == TimeZones.IBM_UTC_ID)
null // indicates UTC
else
(tzRegistry.getTimeZone(tzId) ?: tzRegistry.getTimeZone(defaultTzId))
(tzRegistry?.getTimeZone(tzId) ?: tzRegistry?.getTimeZone(defaultTzId))

return DateTime(timestamp).also { dateTime ->
if (timezone == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package at.bitfire.synctools.mapping.calendar.handler
import android.content.Entity
import android.provider.CalendarContract.Events
import at.bitfire.synctools.icalendar.Css3Color
import at.bitfire.synctools.icalendar.plusAssign
import net.fortuna.ical4j.model.component.VEvent
import net.fortuna.ical4j.model.property.Color
import java.util.logging.Logger
Expand All @@ -34,9 +35,8 @@ class ColorHandler: AndroidEventFieldHandler {
Css3Color.entries.firstOrNull { it.argb == color }
}

TODO("ical4j 4.x")
/*if (color != null)
to.properties += Color(null, color.name)*/
if (color != null)
to += Color(null, color.name)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ package at.bitfire.synctools.mapping.calendar.handler

import android.content.Entity
import android.provider.CalendarContract.Events
import at.bitfire.synctools.icalendar.plusAssign
import at.bitfire.vcard4android.Utils.trimToNull
import net.fortuna.ical4j.model.component.VEvent
import net.fortuna.ical4j.model.property.Description

class DescriptionHandler: AndroidEventFieldHandler {

override fun process(from: Entity, main: Entity, to: VEvent) {
TODO("ical4j 4.x")
/*val description = from.entityValues.getAsString(Events.DESCRIPTION).trimToNull()
val description = from.entityValues.getAsString(Events.DESCRIPTION).trimToNull()
if (description != null)
to.properties += Description(description)*/
to += Description(description)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,9 @@ package at.bitfire.synctools.mapping.calendar.handler
import android.content.Entity
import android.provider.CalendarContract.Events
import at.bitfire.ical4android.util.TimeApiExtensions.abs
import at.bitfire.ical4android.util.TimeApiExtensions.toIcal4jDate
import at.bitfire.ical4android.util.TimeApiExtensions.toIcal4jDateTime
import at.bitfire.ical4android.util.TimeApiExtensions.toZonedDateTime
import at.bitfire.synctools.icalendar.plusAssign
import at.bitfire.synctools.mapping.calendar.builder.AndroidTemporalMapper.toZonedDateTime
import at.bitfire.synctools.util.AndroidTimeUtils
import net.fortuna.ical4j.model.DateTime
import net.fortuna.ical4j.model.TimeZoneRegistry
import net.fortuna.ical4j.model.component.VEvent
import net.fortuna.ical4j.model.property.DtEnd
import java.time.Instant
Expand All @@ -28,9 +25,7 @@ import java.time.ZoneOffset
* - [Events.DTEND] is present / not null (because DTEND then takes precedence over DURATION), and/or
* - [Events.DURATION] is null / not present.
*/
class DurationHandler(
private val tzRegistry: TimeZoneRegistry
): AndroidEventFieldHandler {
class DurationHandler: AndroidEventFieldHandler {

override fun process(from: Entity, main: Entity, to: VEvent) {
val values = from.entityValues
Expand All @@ -52,28 +47,26 @@ class DurationHandler(
val tsStart = values.getAsLong(Events.DTSTART) ?: return
val allDay = (values.getAsInteger(Events.ALL_DAY) ?: 0) != 0

TODO("ical4j 4.x")
/*if (allDay) {
if (allDay) {
val startTimeUTC = Instant.ofEpochMilli(tsStart).atOffset(ZoneOffset.UTC)
val endDate = (startTimeUTC + duration).toLocalDate()

// DATE
to.properties += DtEnd(endDate.toIcal4jDate())
to += DtEnd(endDate)

} else {
// DATE-TIME
val startDateTime = AndroidTimeField(
timestamp = tsStart,
timeZone = values.getAsString(Events.EVENT_TIMEZONE),
allDay = false,
tzRegistry = tzRegistry
).asIcal4jDate() as DateTime
allDay = false
).asTemporal()

val start = startDateTime.toZonedDateTime()
val end = start + duration

to.properties += DtEnd(end.toIcal4jDateTime(tzRegistry))
}*/
to += DtEnd(end)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ package at.bitfire.synctools.mapping.calendar.handler
import android.content.Entity
import android.provider.CalendarContract.Events
import androidx.annotation.VisibleForTesting
import net.fortuna.ical4j.model.TimeZoneRegistry
import at.bitfire.synctools.icalendar.plusAssign
import net.fortuna.ical4j.model.component.VEvent
import net.fortuna.ical4j.model.property.DtEnd
import java.time.Duration
import java.time.Instant
import java.util.logging.Logger

/**
* Maps a potentially present [Events.DTEND] to a VEvent [DtEnd] property.
Expand All @@ -24,12 +23,7 @@ import java.util.logging.Logger
* - If [Events.DURATION] is present / not null, [DurationHandler] is responsible for generating the VEvent's [DtEnd].
* - If [Events.DURATION] is null / not present, this class is responsible for generating the VEvent's [DtEnd].
*/
class EndTimeHandler(
private val tzRegistry: TimeZoneRegistry
): AndroidEventFieldHandler {

private val logger
get() = Logger.getLogger(javaClass.name)
class EndTimeHandler: AndroidEventFieldHandler {

override fun process(from: Entity, main: Entity, to: VEvent) {
val values = from.entityValues
Expand All @@ -56,12 +50,10 @@ class EndTimeHandler(
timestamp = tsEnd,
timeZone = values.getAsString(Events.EVENT_END_TIMEZONE)
?: values.getAsString(Events.EVENT_TIMEZONE), // if end timezone is not present, assume same as for start
allDay = allDay,
tzRegistry = tzRegistry
).asIcal4jDate()
allDay = allDay
).asTemporal()

TODO("ical4j 4.x")
//to.properties += DtEnd(end)
to += DtEnd(end)
}

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package at.bitfire.synctools.mapping.calendar.handler

import android.content.Entity
import android.provider.CalendarContract.Events
import at.bitfire.synctools.icalendar.plusAssign
import at.bitfire.vcard4android.Utils.trimToNull
import net.fortuna.ical4j.model.component.VEvent
import net.fortuna.ical4j.model.property.Location
Expand All @@ -16,9 +17,8 @@ class LocationHandler: AndroidEventFieldHandler {

override fun process(from: Entity, main: Entity, to: VEvent) {
val location = from.entityValues.getAsString(Events.EVENT_LOCATION).trimToNull()
TODO("ical4j 4.x")
/*if (location != null)
to.properties += Location(location)*/
if (location != null)
to += Location(location)
}

}
Loading
Loading