Skip to content
This repository was archived by the owner on May 26, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import android.content.Entity
import at.bitfire.synctools.icalendar.AssociatedComponents
import at.bitfire.synctools.mapping.jtx.handler.DescriptionHandler
import at.bitfire.synctools.mapping.jtx.handler.JtxFieldHandler
import at.bitfire.synctools.mapping.jtx.handler.RecurrenceFieldsHandler
import at.bitfire.synctools.storage.jtx.JtxObjectAndExceptions
import at.techbee.jtx.JtxContract
import net.fortuna.ical4j.model.Property
Expand All @@ -29,7 +30,8 @@ class JtxObjectHandler(
private val prodId: ProdId
) {
private val fieldHandlers: Array<JtxFieldHandler> = arrayOf(
DescriptionHandler()
DescriptionHandler(),
RecurrenceFieldsHandler()
)
Comment thread
ArnyminerZ marked this conversation as resolved.

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* This file is part of bitfireAT/synctools which is released under GPLv3.
* Copyright © All Contributors. See the LICENSE and AUTHOR files in the root directory for details.
* SPDX-License-Identifier: GPL-3.0-or-later
*/

package at.bitfire.synctools.mapping.jtx.handler

import android.content.Entity
import at.bitfire.synctools.icalendar.plusAssign
import at.techbee.jtx.JtxContract
import at.techbee.jtx.JtxContract.JtxICalObject.TZ_ALLDAY
import net.fortuna.ical4j.model.DateList
import net.fortuna.ical4j.model.ParameterList
import net.fortuna.ical4j.model.component.CalendarComponent
import net.fortuna.ical4j.model.parameter.TzId
import net.fortuna.ical4j.model.property.ExDate
import net.fortuna.ical4j.model.property.RDate
import net.fortuna.ical4j.model.property.RRule
import net.fortuna.ical4j.model.property.RecurrenceId
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.ZonedDateTime
import java.time.temporal.Temporal
import java.util.logging.Level
import java.util.logging.Logger

/**
* Handler for recurrence-related fields of a jtx Board content provider data row.
*/
class RecurrenceFieldsHandler : JtxFieldHandler {

private val logger
get() = Logger.getLogger(javaClass.name)

override fun process(from: Entity, main: Entity, to: CalendarComponent) {
if (from === main) {
processMain(from, to)
} else {
processException(from, to)
}
}

private fun processMain(from: Entity, to: CalendarComponent) {
val values = from.entityValues
val dtstartTimezone = values.getAsString(JtxContract.JtxICalObject.DTSTART_TIMEZONE)

values.getAsString(JtxContract.JtxICalObject.RRULE)?.let { rruleString ->
try {
to += RRule<Temporal>(rruleString)
} catch (e: Exception) {
logger.log(Level.WARNING, "Couldn't parse RRULE, ignoring", e)
}
}

values.getAsString(JtxContract.JtxICalObject.RDATE)?.let { rdateString ->
try {
val timestamps = JtxContract.getLongListFromString(rdateString)
if (timestamps.isNotEmpty()) {
to += RDate(timestampsToDateList(timestamps, dtstartTimezone))
}
Comment thread
ArnyminerZ marked this conversation as resolved.
} catch (e: Exception) {
logger.log(Level.WARNING, "Couldn't parse RDATE, ignoring", e)
}
}

values.getAsString(JtxContract.JtxICalObject.EXDATE)?.let { exdateString ->
try {
val timestamps = JtxContract.getLongListFromString(exdateString)
if (timestamps.isNotEmpty()) {
to += ExDate(timestampsToDateList(timestamps, dtstartTimezone))
}
} catch (e: Exception) {
logger.log(Level.WARNING, "Couldn't parse EXDATE, ignoring", e)
}
}
Comment thread
ArnyminerZ marked this conversation as resolved.
Outdated
}

private fun processException(from: Entity, to: CalendarComponent) {
val values = from.entityValues
val recurid = values.getAsString(JtxContract.JtxICalObject.RECURID) ?: return
val recuridTimezone = values.getAsString(JtxContract.JtxICalObject.RECURID_TIMEZONE)

try {
to += if (recuridTimezone == TZ_ALLDAY || recuridTimezone.isNullOrEmpty()) {
RecurrenceId<Temporal>(recurid)
} else {
RecurrenceId<Temporal>(ParameterList(listOf(TzId(recuridTimezone))), recurid)
}
Comment thread
ArnyminerZ marked this conversation as resolved.
} catch (e: Exception) {
logger.log(Level.WARNING, "Couldn't parse RECURID, ignoring", e)
}
}

private fun timestampsToDateList(timestamps: List<Long>, dtstartTimezone: String?): DateList<*> =
when {
dtstartTimezone == TZ_ALLDAY ->
DateList(timestamps.map {
LocalDate.ofInstant(Instant.ofEpochMilli(it), ZoneOffset.UTC)
})
dtstartTimezone == ZoneOffset.UTC.id ->
DateList(timestamps.map {
Instant.ofEpochMilli(it)
})
dtstartTimezone.isNullOrEmpty() ->
DateList(timestamps.map {
LocalDateTime.ofInstant(Instant.ofEpochMilli(it), ZoneId.systemDefault())
})
else ->
DateList(timestamps.map {
ZonedDateTime.ofInstant(Instant.ofEpochMilli(it), ZoneId.of(dtstartTimezone))
})
}
}
Loading
Loading