Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/bitfireAT/ical4android.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Lang <patrick@techbee.at>2022-09-15 00:06:56 +0300
committerPatrick Lang <patrick@techbee.at>2022-09-15 00:06:56 +0300
commitc9c6f9e8dbc8a75629bfc17f53c6bc92a0acb8a4 (patch)
tree26cb7a9dc763b5d680cd900f93491ddce8d9689b
parentd28fe4d1e5b1fb8f92ff725943768ceaad202a66 (diff)
parent238a3f25511d00559672f5f9e94d2afad8c19a18 (diff)
Merge branch 'main' into preserve-original-filename-on-syncpreserve-original-filename-on-sync
-rw-r--r--build.gradle12
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/AndroidCalendarTest.kt4
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/AndroidCompatTimeZoneRegistryTest.kt70
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/AndroidEventTest.kt21
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/AndroidTaskTest.kt1
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/AndroidTimeZonesTest.kt1
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/AospTest.kt2
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/BatchOperationTest.kt6
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/EventTest.kt (renamed from src/test/java/at/bitfire/ical4android/EventTest.kt)8
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/ICalPreprocessorTest.kt (renamed from src/test/java/at/bitfire/ical4android/ICalPreprocessorTest.kt)0
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/ICalendarTest.kt (renamed from src/test/java/at/bitfire/ical4android/ICalendarTest.kt)99
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/Ical4jTest.kt (renamed from src/test/java/at/bitfire/ical4android/Ical4jTest.kt)20
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/JtxCollectionTest.kt2
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/JtxICalObjectTest.kt2
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/LocaleNonWesternDigitsTest.kt24
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/TaskTest.kt (renamed from src/test/java/at/bitfire/ical4android/TaskTest.kt)1
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/impl/TestEvent.kt1
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/impl/TestJtxCollection.kt2
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/impl/TestJtxIcalObject.kt4
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/util/AndroidTimeUtilsTest.kt (renamed from src/test/java/at/bitfire/ical4android/AndroidTimeUtilsTest.kt)7
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/util/DateUtilsTest.kt (renamed from src/test/java/at/bitfire/ical4android/DateUtilsTest.kt)2
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/util/MiscUtilsTest.kt (renamed from src/androidTest/java/at/bitfire/ical4android/MiscUtilsAndroidTest.kt)8
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/util/TimeApiExtensionsTest.kt (renamed from src/test/java/at/bitfire/ical4android/TimeApiExtensionsTest.kt)57
-rw-r--r--src/androidTest/java/at/bitfire/ical4android/validation/EventValidatorTest.kt (renamed from src/test/java/at/bitfire/ical4android/validation/EventValidatorTest.kt)109
-rw-r--r--src/androidTest/resources/events/all-day-0sec.ics (renamed from src/test/resources/events/all-day-0sec.ics)0
-rw-r--r--src/androidTest/resources/events/all-day-10days.ics (renamed from src/test/resources/events/all-day-10days.ics)0
-rw-r--r--src/androidTest/resources/events/all-day-1day.ics (renamed from src/test/resources/events/all-day-1day.ics)0
-rw-r--r--src/androidTest/resources/events/dst-only-vtimezone.ics (renamed from src/test/resources/events/dst-only-vtimezone.ics)0
-rw-r--r--src/androidTest/resources/events/event-on-that-day.ics (renamed from src/test/resources/events/event-on-that-day.ics)0
-rw-r--r--src/androidTest/resources/events/latin1.ics (renamed from src/test/resources/events/latin1.ics)0
-rw-r--r--src/androidTest/resources/events/multiple.ics (renamed from src/test/resources/events/multiple.ics)0
-rw-r--r--src/androidTest/resources/events/one-event-with-exception-one-without.ics (renamed from src/test/resources/events/one-event-with-exception-one-without.ics)0
-rw-r--r--src/androidTest/resources/events/one-event-with-multiple-exceptions-one-without.ics (renamed from src/test/resources/events/one-event-with-multiple-exceptions-one-without.ics)0
-rw-r--r--src/androidTest/resources/events/outlook1.ics (renamed from src/test/resources/events/outlook1.ics)0
-rw-r--r--src/androidTest/resources/events/recurring-only-exception.ics (renamed from src/test/resources/events/recurring-only-exception.ics)0
-rw-r--r--src/androidTest/resources/events/recurring-with-exception1.ics (renamed from src/test/resources/events/recurring-with-exception1.ics)0
-rw-r--r--src/androidTest/resources/events/two-events-without-exceptions.ics (renamed from src/test/resources/events/two-events-without-exceptions.ics)0
-rw-r--r--src/androidTest/resources/events/two-line-description-without-crlf.ics (renamed from src/test/resources/events/two-line-description-without-crlf.ics)0
-rw-r--r--src/androidTest/resources/events/utf8.ics (renamed from src/test/resources/events/utf8.ics)0
-rw-r--r--src/androidTest/resources/events/vienna-evolution.ics (renamed from src/test/resources/events/vienna-evolution.ics)0
-rw-r--r--src/androidTest/resources/tasks/empty-priority.ics (renamed from src/test/resources/tasks/empty-priority.ics)0
-rw-r--r--src/androidTest/resources/tasks/latin1.ics (renamed from src/test/resources/tasks/latin1.ics)0
-rw-r--r--src/androidTest/resources/tasks/most-fields1.ics (renamed from src/test/resources/tasks/most-fields1.ics)0
-rw-r--r--src/androidTest/resources/tasks/most-fields2.ics (renamed from src/test/resources/tasks/most-fields2.ics)0
-rw-r--r--src/androidTest/resources/tasks/rfc5545-sample1.ics (renamed from src/test/resources/tasks/rfc5545-sample1.ics)0
-rw-r--r--src/androidTest/resources/tasks/utf8.ics (renamed from src/test/resources/tasks/utf8.ics)0
-rw-r--r--src/androidTest/resources/tz/Karachi.ics (renamed from src/test/resources/tz/Karachi.ics)0
-rw-r--r--src/androidTest/resources/tz/Mogadishu.ics (renamed from src/test/resources/tz/Mogadishu.ics)0
-rw-r--r--src/androidTest/resources/tz/Vienna.ics (renamed from src/test/resources/tz/Vienna.ics)0
-rw-r--r--src/main/java/at/bitfire/ical4android/AndroidCalendar.kt4
-rw-r--r--src/main/java/at/bitfire/ical4android/AndroidCompatTimeZoneRegistry.kt83
-rw-r--r--src/main/java/at/bitfire/ical4android/AndroidEvent.kt17
-rw-r--r--src/main/java/at/bitfire/ical4android/AndroidTask.kt4
-rw-r--r--src/main/java/at/bitfire/ical4android/AndroidTaskList.kt4
-rw-r--r--src/main/java/at/bitfire/ical4android/Event.kt2
-rw-r--r--src/main/java/at/bitfire/ical4android/ICalendar.kt1
-rw-r--r--src/main/java/at/bitfire/ical4android/JtxCollection.kt2
-rw-r--r--src/main/java/at/bitfire/ical4android/JtxICalObject.kt2
-rw-r--r--src/main/java/at/bitfire/ical4android/Task.kt1
-rw-r--r--src/main/java/at/bitfire/ical4android/TaskProvider.kt2
-rw-r--r--src/main/java/at/bitfire/ical4android/util/AndroidTimeUtils.kt3
-rw-r--r--src/main/java/at/bitfire/ical4android/util/DateUtils.kt (renamed from src/main/java/at/bitfire/ical4android/DateUtils.kt)4
-rw-r--r--src/main/java/at/bitfire/ical4android/util/MiscUtils.kt (renamed from src/main/java/at/bitfire/ical4android/MiscUtils.kt)2
-rw-r--r--src/main/java/at/bitfire/ical4android/util/TimeApiExtensions.kt19
-rw-r--r--src/main/java/at/bitfire/ical4android/validation/EventValidator.kt81
-rw-r--r--src/main/resources/ical4j.properties1
-rw-r--r--src/test/README.txt6
-rw-r--r--src/test/java/at/bitfire/ical4android/MiscUtilsTest.kt1
68 files changed, 507 insertions, 195 deletions
diff --git a/build.gradle b/build.gradle
index a45d059..866151f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,13 +1,12 @@
-
/***************************************************************************************************
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
**************************************************************************************************/
buildscript {
ext.versions = [
- kotlin: '1.7.0',
+ kotlin: '1.7.10',
dokka: '1.5.0',
- ical4j: '3.2.4',
+ ical4j: '3.2.5',
// latest Apache Commons versions that don't require Java 8 (Android 7)
commonsIO: '2.6'
]
@@ -18,7 +17,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:7.2.1'
+ classpath 'com.android.tools.build:gradle:7.2.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}"
}
@@ -84,7 +83,10 @@ dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.6'
api("org.mnode.ical4j:ical4j:${versions.ical4j}") {
+ // exclude modules which are in conflict with system libraries
exclude group: 'commons-logging'
+ exclude group: 'org.json', module: 'json'
+ // exclude groovy because we don't need it
exclude group: 'org.codehaus.groovy', module: 'groovy'
exclude group: 'org.codehaus.groovy', module: 'groovy-dateutil'
}
@@ -97,7 +99,7 @@ dependencies {
// noinspection GradleDependency
implementation "commons-io:commons-io:${versions.commonsIO}"
- implementation 'org.slf4j:slf4j-jdk14:1.7.32'
+ implementation 'org.slf4j:slf4j-jdk14:1.7.36'
implementation 'androidx.core:core-ktx:1.8.0'
androidTestImplementation 'androidx.test:core:1.4.0'
diff --git a/src/androidTest/java/at/bitfire/ical4android/AndroidCalendarTest.kt b/src/androidTest/java/at/bitfire/ical4android/AndroidCalendarTest.kt
index a8ab2f4..dbd9c4c 100644
--- a/src/androidTest/java/at/bitfire/ical4android/AndroidCalendarTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/AndroidCalendarTest.kt
@@ -14,10 +14,10 @@ import android.provider.CalendarContract.Calendars
import android.provider.CalendarContract.Colors
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
-import at.bitfire.ical4android.MiscUtils.ContentProviderClientHelper.closeCompat
-import at.bitfire.ical4android.MiscUtils.UriHelper.asSyncAdapter
import at.bitfire.ical4android.impl.TestCalendar
import at.bitfire.ical4android.impl.TestEvent
+import at.bitfire.ical4android.util.MiscUtils.ContentProviderClientHelper.closeCompat
+import at.bitfire.ical4android.util.MiscUtils.UriHelper.asSyncAdapter
import net.fortuna.ical4j.model.property.DtEnd
import net.fortuna.ical4j.model.property.DtStart
import org.junit.*
diff --git a/src/androidTest/java/at/bitfire/ical4android/AndroidCompatTimeZoneRegistryTest.kt b/src/androidTest/java/at/bitfire/ical4android/AndroidCompatTimeZoneRegistryTest.kt
new file mode 100644
index 0000000..5958e16
--- /dev/null
+++ b/src/androidTest/java/at/bitfire/ical4android/AndroidCompatTimeZoneRegistryTest.kt
@@ -0,0 +1,70 @@
+/***************************************************************************************************
+ * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
+ **************************************************************************************************/
+
+package at.bitfire.ical4android
+
+import net.fortuna.ical4j.model.DefaultTimeZoneRegistryFactory
+import net.fortuna.ical4j.model.TimeZoneRegistry
+import org.junit.Assert.*
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+import java.time.ZoneId
+import java.time.zone.ZoneRulesException
+
+class AndroidCompatTimeZoneRegistryTest {
+
+ lateinit var ical4jRegistry: TimeZoneRegistry
+ lateinit var registry: TimeZoneRegistry
+
+ val systemKnowsKyiv =
+ try {
+ ZoneId.of("Europe/Kyiv")
+ true
+ } catch (e: ZoneRulesException) {
+ false
+ }
+
+ @Before
+ fun createRegistry() {
+ ical4jRegistry = DefaultTimeZoneRegistryFactory.getInstance().createRegistry()
+ registry = AndroidCompatTimeZoneRegistry.Factory().createRegistry()
+ }
+
+
+ @Test
+ fun getTimeZone_Existing() {
+ assertEquals(
+ ical4jRegistry.getTimeZone("Europe/Vienna"),
+ registry.getTimeZone("Europe/Vienna")
+ )
+ }
+
+ @Test
+ fun getTimeZone_Existing_Kiev() {
+ Assume.assumeFalse(systemKnowsKyiv)
+ val tz = registry.getTimeZone("Europe/Kiev")
+ assertFalse(tz === ical4jRegistry.getTimeZone("Europe/Kiev")) // we have made a copy
+ assertEquals("Europe/Kiev", tz?.id)
+ assertEquals("Europe/Kiev", tz?.vTimeZone?.timeZoneId?.value)
+ }
+
+ @Test
+ fun getTimeZone_Existing_Kyiv() {
+ Assume.assumeFalse(systemKnowsKyiv)
+
+ /* Unfortunately, AndroidCompatTimeZoneRegistry can't rewrite to Europy/Kyiv to anything because
+ it doesn't know a valid Android name for it. */
+ assertEquals(
+ ical4jRegistry.getTimeZone("Europe/Kyiv"),
+ registry.getTimeZone("Europe/Kyiv")
+ )
+ }
+
+ @Test
+ fun getTimeZone_NotExisting() {
+ assertNull(registry.getTimeZone("Test/NotExisting"))
+ }
+
+} \ No newline at end of file
diff --git a/src/androidTest/java/at/bitfire/ical4android/AndroidEventTest.kt b/src/androidTest/java/at/bitfire/ical4android/AndroidEventTest.kt
index c9ff0b2..cb094ca 100644
--- a/src/androidTest/java/at/bitfire/ical4android/AndroidEventTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/AndroidEventTest.kt
@@ -13,11 +13,12 @@ import android.net.Uri
import android.provider.CalendarContract.*
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.test.rule.GrantPermissionRule
-import at.bitfire.ical4android.MiscUtils.ContentProviderClientHelper.closeCompat
-import at.bitfire.ical4android.MiscUtils.UriHelper.asSyncAdapter
import at.bitfire.ical4android.impl.TestCalendar
import at.bitfire.ical4android.impl.TestEvent
import at.bitfire.ical4android.util.AndroidTimeUtils
+import at.bitfire.ical4android.util.DateUtils
+import at.bitfire.ical4android.util.MiscUtils.ContentProviderClientHelper.closeCompat
+import at.bitfire.ical4android.util.MiscUtils.UriHelper.asSyncAdapter
import net.fortuna.ical4j.model.*
import net.fortuna.ical4j.model.component.VAlarm
import net.fortuna.ical4j.model.parameter.*
@@ -28,6 +29,7 @@ import org.junit.Assert.*
import java.net.URI
import java.time.Duration
import java.time.Period
+import java.util.TimeZone
class AndroidEventTest {
@@ -1459,6 +1461,21 @@ class AndroidEventTest {
}
@Test
+ fun testPopulateEvent_NonAllDay_Recurring_Duration_KievTimeZone() {
+ populateEvent(false) {
+ put(Events.DTSTART, 1592733600000L) // 21/06/2020 18:00 +0800
+ put(Events.EVENT_TIMEZONE, "Europe/Kiev")
+ put(Events.DURATION, "PT1H")
+ put(Events.RRULE, "FREQ=DAILY;COUNT=2")
+ }.let { result ->
+ assertEquals(1592733600000L, result.dtStart?.date?.time)
+ assertEquals(1592733600000L + 3600000, result.dtEnd?.date?.time)
+ assertEquals("Europe/Kiev", result.dtStart?.timeZone?.id)
+ assertEquals("Europe/Kiev", result.dtEnd?.timeZone?.id)
+ }
+ }
+
+ @Test
fun testPopulateEvent_NonAllDay_NonRecurring_NoTime() {
populateEvent(false) {
put(Events.DTSTART, 1592742600000L) // 21/06/2020 14:30 +0200
diff --git a/src/androidTest/java/at/bitfire/ical4android/AndroidTaskTest.kt b/src/androidTest/java/at/bitfire/ical4android/AndroidTaskTest.kt
index c907439..61565cb 100644
--- a/src/androidTest/java/at/bitfire/ical4android/AndroidTaskTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/AndroidTaskTest.kt
@@ -12,6 +12,7 @@ import android.net.Uri
import androidx.test.filters.MediumTest
import at.bitfire.ical4android.impl.TestTask
import at.bitfire.ical4android.impl.TestTaskList
+import at.bitfire.ical4android.util.DateUtils
import net.fortuna.ical4j.model.Date
import net.fortuna.ical4j.model.DateList
import net.fortuna.ical4j.model.DateTime
diff --git a/src/androidTest/java/at/bitfire/ical4android/AndroidTimeZonesTest.kt b/src/androidTest/java/at/bitfire/ical4android/AndroidTimeZonesTest.kt
index d51c1ad..d6199ad 100644
--- a/src/androidTest/java/at/bitfire/ical4android/AndroidTimeZonesTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/AndroidTimeZonesTest.kt
@@ -4,6 +4,7 @@
package at.bitfire.ical4android
+import at.bitfire.ical4android.util.DateUtils
import org.junit.Assert
import org.junit.Assert.assertNotNull
import org.junit.Test
diff --git a/src/androidTest/java/at/bitfire/ical4android/AospTest.kt b/src/androidTest/java/at/bitfire/ical4android/AospTest.kt
index bf18803..c048f25 100644
--- a/src/androidTest/java/at/bitfire/ical4android/AospTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/AospTest.kt
@@ -12,7 +12,7 @@ import android.net.Uri
import android.provider.CalendarContract
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
-import at.bitfire.ical4android.MiscUtils.ContentProviderClientHelper.closeCompat
+import at.bitfire.ical4android.util.MiscUtils.ContentProviderClientHelper.closeCompat
import org.junit.After
import org.junit.Assert.assertNotNull
import org.junit.Before
diff --git a/src/androidTest/java/at/bitfire/ical4android/BatchOperationTest.kt b/src/androidTest/java/at/bitfire/ical4android/BatchOperationTest.kt
index 27debf5..4ef3148 100644
--- a/src/androidTest/java/at/bitfire/ical4android/BatchOperationTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/BatchOperationTest.kt
@@ -13,15 +13,15 @@ import android.provider.CalendarContract
import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
-import at.bitfire.ical4android.MiscUtils.ContentProviderClientHelper.closeCompat
import at.bitfire.ical4android.impl.TestCalendar
import at.bitfire.ical4android.impl.TestEvent
+import at.bitfire.ical4android.util.MiscUtils.ContentProviderClientHelper.closeCompat
import net.fortuna.ical4j.model.property.Attendee
import net.fortuna.ical4j.model.property.DtEnd
import net.fortuna.ical4j.model.property.DtStart
import org.junit.*
-import org.junit.Assert.*
-
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
import java.net.URI
import java.util.*
diff --git a/src/test/java/at/bitfire/ical4android/EventTest.kt b/src/androidTest/java/at/bitfire/ical4android/EventTest.kt
index 6842974..e016b07 100644
--- a/src/test/java/at/bitfire/ical4android/EventTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/EventTest.kt
@@ -1,8 +1,10 @@
/***************************************************************************************************
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
**************************************************************************************************/
+
package at.bitfire.ical4android
+import at.bitfire.ical4android.util.DateUtils
import net.fortuna.ical4j.model.Date
import net.fortuna.ical4j.model.DateTime
import net.fortuna.ical4j.model.Parameter
@@ -325,8 +327,8 @@ class EventTest {
}
private fun parseCalendar(fname: String, charset: Charset = Charsets.UTF_8): List<Event> =
- javaClass.classLoader!!.getResourceAsStream("events/$fname").use { stream ->
- return Event.eventsFromReader(InputStreamReader(stream, charset))
- }
+ javaClass.classLoader!!.getResourceAsStream("events/$fname").use { stream ->
+ return Event.eventsFromReader(InputStreamReader(stream, charset))
+ }
}
diff --git a/src/test/java/at/bitfire/ical4android/ICalPreprocessorTest.kt b/src/androidTest/java/at/bitfire/ical4android/ICalPreprocessorTest.kt
index e1d614b..e1d614b 100644
--- a/src/test/java/at/bitfire/ical4android/ICalPreprocessorTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/ICalPreprocessorTest.kt
diff --git a/src/test/java/at/bitfire/ical4android/ICalendarTest.kt b/src/androidTest/java/at/bitfire/ical4android/ICalendarTest.kt
index f2693be..a62fb72 100644
--- a/src/test/java/at/bitfire/ical4android/ICalendarTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/ICalendarTest.kt
@@ -4,6 +4,7 @@
package at.bitfire.ical4android
+import at.bitfire.ical4android.util.DateUtils
import net.fortuna.ical4j.data.CalendarBuilder
import net.fortuna.ical4j.model.Component
import net.fortuna.ical4j.model.Date
@@ -53,15 +54,17 @@ class ICalendarTest {
@Test
fun testFromReader_calendarProperties() {
val calendar = ICalendar.fromReader(
- StringReader("BEGIN:VCALENDAR\n" +
- "VERSION:2.0\n" +
- "METHOD:PUBLISH\n" +
- "PRODID:something\n" +
- "X-WR-CALNAME:Some Calendar\n" +
- "COLOR:darkred\n" +
- "X-APPLE-CALENDAR-COLOR:#123456\n" +
- "END:VCALENDAR"
- ))
+ StringReader(
+ "BEGIN:VCALENDAR\n" +
+ "VERSION:2.0\n" +
+ "METHOD:PUBLISH\n" +
+ "PRODID:something\n" +
+ "X-WR-CALNAME:Some Calendar\n" +
+ "COLOR:darkred\n" +
+ "X-APPLE-CALENDAR-COLOR:#123456\n" +
+ "END:VCALENDAR"
+ )
+ )
assertEquals("Some Calendar", calendar.getProperty<Property>(ICalendar.CALENDAR_NAME).value)
assertEquals("darkred", calendar.getProperty<Property>(Color.PROPERTY_NAME).value)
assertEquals("#123456", calendar.getProperty<Property>(ICalendar.CALENDAR_COLOR).value)
@@ -141,28 +144,31 @@ class ICalendarTest {
@Test
fun testTimezoneDefToTzId_Valid() {
- assertEquals("US-Eastern", ICalendar.timezoneDefToTzId("BEGIN:VCALENDAR\n" +
- "PRODID:-//Example Corp.//CalDAV Client//EN\n" +
- "VERSION:2.0\n" +
- "BEGIN:VTIMEZONE\n" +
- "TZID:US-Eastern\n" +
- "LAST-MODIFIED:19870101T000000Z\n" +
- "BEGIN:STANDARD\n" +
- "DTSTART:19671029T020000\n" +
- "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\n" +
- "TZOFFSETFROM:-0400\n" +
- "TZOFFSETTO:-0500\n" +
- "TZNAME:Eastern Standard Time (US &amp; Canada)\n" +
- "END:STANDARD\n" +
- "BEGIN:DAYLIGHT\n" +
- "DTSTART:19870405T020000\n" +
- "RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4\n" +
- "TZOFFSETFROM:-0500\n" +
- "TZOFFSETTO:-0400\n" +
- "TZNAME:Eastern Daylight Time (US &amp; Canada)\n" +
- "END:DAYLIGHT\n" +
- "END:VTIMEZONE\n" +
- "END:VCALENDAR"))
+ assertEquals("US-Eastern", ICalendar.timezoneDefToTzId(
+ "BEGIN:VCALENDAR\n" +
+ "PRODID:-//Example Corp.//CalDAV Client//EN\n" +
+ "VERSION:2.0\n" +
+ "BEGIN:VTIMEZONE\n" +
+ "TZID:US-Eastern\n" +
+ "LAST-MODIFIED:19870101T000000Z\n" +
+ "BEGIN:STANDARD\n" +
+ "DTSTART:19671029T020000\n" +
+ "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\n" +
+ "TZOFFSETFROM:-0400\n" +
+ "TZOFFSETTO:-0500\n" +
+ "TZNAME:Eastern Standard Time (US &amp; Canada)\n" +
+ "END:STANDARD\n" +
+ "BEGIN:DAYLIGHT\n" +
+ "DTSTART:19870405T020000\n" +
+ "RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4\n" +
+ "TZOFFSETFROM:-0500\n" +
+ "TZOFFSETTO:-0400\n" +
+ "TZNAME:Eastern Daylight Time (US &amp; Canada)\n" +
+ "END:DAYLIGHT\n" +
+ "END:VTIMEZONE\n" +
+ "END:VCALENDAR"
+ )
+ )
}
@Test
@@ -171,10 +177,14 @@ class ICalendarTest {
assertNull(ICalendar.timezoneDefToTzId("/* invalid content */"))
// time zone without TZID
- assertNull(ICalendar.timezoneDefToTzId("BEGIN:VCALENDAR\n" +
- "PRODID:-//Inverse inc./SOGo 2.2.10//EN\n" +
- "VERSION:2.0\n" +
- "END:VCALENDAR"))
+ assertNull(
+ ICalendar.timezoneDefToTzId(
+ "BEGIN:VCALENDAR\n" +
+ "PRODID:-//Inverse inc./SOGo 2.2.10//EN\n" +
+ "VERSION:2.0\n" +
+ "END:VCALENDAR"
+ )
+ )
}
@@ -182,8 +192,9 @@ class ICalendarTest {
fun testVAlarmToMin_TriggerDuration_Negative() {
// TRIGGER;REL=START:-P1DT1H1M29S
val (ref, min) = ICalendar.vAlarmToMin(
- VAlarm(Duration.parse("-P1DT1H1M29S")),
- Event(), false)!!
+ VAlarm(Duration.parse("-P1DT1H1M29S")),
+ Event(), false
+ )!!
assertEquals(Related.START, ref)
assertEquals(60*24 + 60 + 1, min)
}
@@ -192,8 +203,9 @@ class ICalendarTest {
fun testVAlarmToMin_TriggerDuration_OnlySeconds() {
// TRIGGER;REL=START:-PT3600S
val (ref, min) = ICalendar.vAlarmToMin(
- VAlarm(Duration.parse("-PT3600S")),
- Event(), false)!!
+ VAlarm(Duration.parse("-PT3600S")),
+ Event(), false
+ )!!
assertEquals(Related.START, ref)
assertEquals(60, min)
}
@@ -202,8 +214,9 @@ class ICalendarTest {
fun testVAlarmToMin_TriggerDuration_Positive() {
// TRIGGER;REL=START:P1DT1H1M30S (alarm *after* start)
val (ref, min) = ICalendar.vAlarmToMin(
- VAlarm(Duration.parse("P1DT1H1M30S")),
- Event(), false)!!
+ VAlarm(Duration.parse("P1DT1H1M30S")),
+ Event(), false
+ )!!
assertEquals(Related.START, ref)
assertEquals(-(60*24 + 60 + 1), min)
}
@@ -270,8 +283,8 @@ class ICalendarTest {
val event = Event()
event.dtStart = DtStart(Date(currentTime))
val (ref, min) = ICalendar.vAlarmToMin(
- VAlarm(Period.parse("-P1W1D")),
- event, false
+ VAlarm(Period.parse("-P1W1D")),
+ event, false
)!!
assertEquals(Related.START, ref)
assertEquals(8*24*60, min)
diff --git a/src/test/java/at/bitfire/ical4android/Ical4jTest.kt b/src/androidTest/java/at/bitfire/ical4android/Ical4jTest.kt
index ad8d11b..7bd9292 100644
--- a/src/test/java/at/bitfire/ical4android/Ical4jTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/Ical4jTest.kt
@@ -21,14 +21,18 @@ class Ical4jTest {
@Test
fun testEmailParameter() {
// https://github.com/ical4j/ical4j/issues/418
- val e = Event.eventsFromReader(StringReader("BEGIN:VCALENDAR\n" +
- "VERSION:2.0\n" +
- "BEGIN:VEVENT\n" +
- "SUMMARY:Test\n" +
- "DTSTART;VALUE=DATE:20200702\n" +
- "ATTENDEE;EMAIL=attendee1@example.com:sample:attendee1\n" +
- "END:VEVENT\n" +
- "END:VCALENDAR")).first()
+ val e = Event.eventsFromReader(
+ StringReader(
+ "BEGIN:VCALENDAR\n" +
+ "VERSION:2.0\n" +
+ "BEGIN:VEVENT\n" +
+ "SUMMARY:Test\n" +
+ "DTSTART;VALUE=DATE:20200702\n" +
+ "ATTENDEE;EMAIL=attendee1@example.com:sample:attendee1\n" +
+ "END:VEVENT\n" +
+ "END:VCALENDAR"
+ )
+ ).first()
assertEquals("attendee1@example.com", e.attendees.first.getParameter<Email>(Parameter.EMAIL).value)
}
diff --git a/src/androidTest/java/at/bitfire/ical4android/JtxCollectionTest.kt b/src/androidTest/java/at/bitfire/ical4android/JtxCollectionTest.kt
index 29b5157..3e9f886 100644
--- a/src/androidTest/java/at/bitfire/ical4android/JtxCollectionTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/JtxCollectionTest.kt
@@ -9,8 +9,8 @@ import android.content.ContentProviderClient
import android.content.ContentValues
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
-import at.bitfire.ical4android.MiscUtils.ContentProviderClientHelper.closeCompat
import at.bitfire.ical4android.impl.TestJtxCollection
+import at.bitfire.ical4android.util.MiscUtils.ContentProviderClientHelper.closeCompat
import at.techbee.jtx.JtxContract
import at.techbee.jtx.JtxContract.asSyncAdapter
import junit.framework.TestCase.*
diff --git a/src/androidTest/java/at/bitfire/ical4android/JtxICalObjectTest.kt b/src/androidTest/java/at/bitfire/ical4android/JtxICalObjectTest.kt
index f82c07d..e0cb10a 100644
--- a/src/androidTest/java/at/bitfire/ical4android/JtxICalObjectTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/JtxICalObjectTest.kt
@@ -11,8 +11,8 @@ import android.database.DatabaseUtils
import android.os.ParcelFileDescriptor
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
-import at.bitfire.ical4android.MiscUtils.ContentProviderClientHelper.closeCompat
import at.bitfire.ical4android.impl.TestJtxCollection
+import at.bitfire.ical4android.util.MiscUtils.ContentProviderClientHelper.closeCompat
import at.techbee.jtx.JtxContract
import at.techbee.jtx.JtxContract.JtxICalObject
import at.techbee.jtx.JtxContract.JtxICalObject.Component
diff --git a/src/androidTest/java/at/bitfire/ical4android/LocaleNonWesternDigitsTest.kt b/src/androidTest/java/at/bitfire/ical4android/LocaleNonWesternDigitsTest.kt
index 81f0783..81f91c1 100644
--- a/src/androidTest/java/at/bitfire/ical4android/LocaleNonWesternDigitsTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/LocaleNonWesternDigitsTest.kt
@@ -5,8 +5,9 @@
package at.bitfire.ical4android
import net.fortuna.ical4j.model.property.TzOffsetFrom
+import org.junit.AfterClass
import org.junit.Assert.assertEquals
-import org.junit.Before
+import org.junit.BeforeClass
import org.junit.ComparisonFailure
import org.junit.Test
import java.time.ZoneOffset
@@ -15,13 +16,22 @@ import java.util.*
class LocaleNonWesternDigitsTest {
companion object {
- val locale = Locale("fa", "ir", "u-un-arabext")
- }
+ val origLocale = Locale.getDefault()
+ val testLocale = Locale("fa", "ir", "u-un-arabext")
+
+ @BeforeClass
+ @JvmStatic
+ fun setFaIrArabLocale() {
+ assertEquals("Persian (Iran) locale not available", "fa", testLocale.language)
+ Locale.setDefault(testLocale)
+ }
+
+ @AfterClass
+ @JvmStatic
+ fun resetLocale() {
+ Locale.setDefault(origLocale)
+ }
- @Before
- fun verifyLocale() {
- assertEquals("Persian (Iran) locale not available", "fa", locale.language)
- Locale.setDefault(locale)
}
@Test
diff --git a/src/test/java/at/bitfire/ical4android/TaskTest.kt b/src/androidTest/java/at/bitfire/ical4android/TaskTest.kt
index cbe0bcc..f9f72af 100644
--- a/src/test/java/at/bitfire/ical4android/TaskTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/TaskTest.kt
@@ -4,6 +4,7 @@
package at.bitfire.ical4android
+import at.bitfire.ical4android.util.DateUtils
import net.fortuna.ical4j.model.*
import net.fortuna.ical4j.model.component.VAlarm
import net.fortuna.ical4j.model.parameter.RelType
diff --git a/src/androidTest/java/at/bitfire/ical4android/impl/TestEvent.kt b/src/androidTest/java/at/bitfire/ical4android/impl/TestEvent.kt
index f9c9571..11270a1 100644
--- a/src/androidTest/java/at/bitfire/ical4android/impl/TestEvent.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/impl/TestEvent.kt
@@ -5,7 +5,6 @@
package at.bitfire.ical4android.impl
import android.content.ContentValues
-import android.provider.CalendarContract
import android.provider.CalendarContract.Events
import at.bitfire.ical4android.*
import java.util.*
diff --git a/src/androidTest/java/at/bitfire/ical4android/impl/TestJtxCollection.kt b/src/androidTest/java/at/bitfire/ical4android/impl/TestJtxCollection.kt
index 5a69060..2bef306 100644
--- a/src/androidTest/java/at/bitfire/ical4android/impl/TestJtxCollection.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/impl/TestJtxCollection.kt
@@ -9,7 +9,7 @@ import android.content.ContentProviderClient
import at.bitfire.ical4android.JtxCollection
import at.bitfire.ical4android.JtxCollectionFactory
import at.bitfire.ical4android.JtxICalObject
-import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues
+import at.bitfire.ical4android.util.MiscUtils.CursorHelper.toValues
import at.techbee.jtx.JtxContract
import java.util.*
diff --git a/src/androidTest/java/at/bitfire/ical4android/impl/TestJtxIcalObject.kt b/src/androidTest/java/at/bitfire/ical4android/impl/TestJtxIcalObject.kt
index 57615fb..a1397e9 100644
--- a/src/androidTest/java/at/bitfire/ical4android/impl/TestJtxIcalObject.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/impl/TestJtxIcalObject.kt
@@ -5,7 +5,9 @@
package at.bitfire.ical4android.impl
import android.content.ContentValues
-import at.bitfire.ical4android.*
+import at.bitfire.ical4android.JtxCollection
+import at.bitfire.ical4android.JtxICalObject
+import at.bitfire.ical4android.JtxICalObjectFactory
class TestJtxIcalObject(testCollection: JtxCollection<JtxICalObject>): JtxICalObject(testCollection) {
diff --git a/src/test/java/at/bitfire/ical4android/AndroidTimeUtilsTest.kt b/src/androidTest/java/at/bitfire/ical4android/util/AndroidTimeUtilsTest.kt
index 2c68b95..18a37f9 100644
--- a/src/test/java/at/bitfire/ical4android/AndroidTimeUtilsTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/util/AndroidTimeUtilsTest.kt
@@ -2,13 +2,10 @@
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
**************************************************************************************************/
-package at.bitfire.ical4android
+package at.bitfire.ical4android.util
-import at.bitfire.ical4android.util.AndroidTimeUtils
import net.fortuna.ical4j.data.CalendarBuilder
import net.fortuna.ical4j.model.*
-import net.fortuna.ical4j.model.Date
-import net.fortuna.ical4j.model.TimeZone
import net.fortuna.ical4j.model.component.VTimeZone
import net.fortuna.ical4j.model.parameter.TzId
import net.fortuna.ical4j.model.parameter.Value
@@ -19,11 +16,9 @@ import net.fortuna.ical4j.model.property.RDate
import net.fortuna.ical4j.util.TimeZones
import org.junit.Assert.*
import org.junit.Test
-import java.io.InputStreamReader
import java.io.StringReader
import java.time.Duration
import java.time.Period
-import java.util.*
class AndroidTimeUtilsTest {
diff --git a/src/test/java/at/bitfire/ical4android/DateUtilsTest.kt b/src/androidTest/java/at/bitfire/ical4android/util/DateUtilsTest.kt
index 395b8da..fe1f468 100644
--- a/src/test/java/at/bitfire/ical4android/DateUtilsTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/util/DateUtilsTest.kt
@@ -2,7 +2,7 @@
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
**************************************************************************************************/
-package at.bitfire.ical4android
+package at.bitfire.ical4android.util
import net.fortuna.ical4j.model.Date
import net.fortuna.ical4j.model.DateTime
diff --git a/src/androidTest/java/at/bitfire/ical4android/MiscUtilsAndroidTest.kt b/src/androidTest/java/at/bitfire/ical4android/util/MiscUtilsTest.kt
index d6f7e40..dee8dd9 100644
--- a/src/androidTest/java/at/bitfire/ical4android/MiscUtilsAndroidTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/util/MiscUtilsTest.kt
@@ -2,20 +2,20 @@
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
**************************************************************************************************/
-package at.bitfire.ical4android
+package at.bitfire.ical4android.util
import android.accounts.Account
import android.content.ContentValues
import android.database.MatrixCursor
import android.net.Uri
import androidx.test.filters.SmallTest
-import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues
-import at.bitfire.ical4android.MiscUtils.UriHelper.asSyncAdapter
+import at.bitfire.ical4android.util.MiscUtils.CursorHelper.toValues
+import at.bitfire.ical4android.util.MiscUtils.UriHelper.asSyncAdapter
import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Test
-class MiscUtilsAndroidTest {
+class MiscUtilsTest {
private val tzVienna = DateUtils.ical4jTimeZone("Europe/Vienna")
diff --git a/src/test/java/at/bitfire/ical4android/TimeApiExtensionsTest.kt b/src/androidTest/java/at/bitfire/ical4android/util/TimeApiExtensionsTest.kt
index 44a02a0..e5c6fc0 100644
--- a/src/test/java/at/bitfire/ical4android/TimeApiExtensionsTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/util/TimeApiExtensionsTest.kt
@@ -2,7 +2,7 @@
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
**************************************************************************************************/
-package at.bitfire.ical4android
+package at.bitfire.ical4android.util
import at.bitfire.ical4android.util.TimeApiExtensions.requireTimeZone
import at.bitfire.ical4android.util.TimeApiExtensions.toDuration
@@ -28,7 +28,18 @@ class TimeApiExtensionsTest {
@Test
- fun testDateToLocalDate() {
+ fun testTimeZone_toZoneIdCompat_NotUtc() {
+ assertEquals(ZoneId.of("Europe/Berlin"), tzBerlin.toZoneId())
+ }
+
+ @Test
+ fun testTimeZone_toZoneIdCompat_Utc() {
+ assertEquals(ZoneOffset.UTC, TimeZones.getUtcTimeZone().toZoneIdCompat())
+ }
+
+
+ @Test
+ fun testDate_toLocalDate() {
val date = Date("20200620").toLocalDate()
assertEquals(2020, date.year)
assertEquals(6, date.monthValue)
@@ -38,19 +49,19 @@ class TimeApiExtensionsTest {
@Test
- fun testDateTimeRequireTimeZone() {
+ fun testDateTime_requireTimeZone() {
val time = DateTime("2020707T010203", tzBerlin)
assertEquals(tzBerlin, time.requireTimeZone())
}
@Test
- fun testDateTimeRequireTimeZone_Floating() {
+ fun testDateTime_requireTimeZone_Floating() {
val time = DateTime("2020707T010203")
assertEquals(TimeZone.getDefault(), time.requireTimeZone())
}
@Test
- fun testDateTimeRequireTimeZone_Utc() {
+ fun testDateTime_requireTimeZone_Utc() {
val time = DateTime("2020707T010203Z").apply { isUtc = true }
assertTrue(time.isUtc)
assertEquals(TimeZones.getUtcTimeZone(), time.requireTimeZone())
@@ -58,7 +69,7 @@ class TimeApiExtensionsTest {
@Test
- fun testDateTimeToLocalDate_TimezoneBoundary() {
+ fun testDateTime_toLocalDate_TimezoneBoundary() {
val date = DateTime("20200620T000000", tzBerlin).toLocalDate()
assertEquals(2020, date.year)
assertEquals(6, date.monthValue)
@@ -67,7 +78,7 @@ class TimeApiExtensionsTest {
}
@Test
- fun testDateTimeToLocalDate_TimezoneDuringDay() {
+ fun testDateTime_toLocalDate_TimezoneDuringDay() {
val date = DateTime("20200620T123000", tzBerlin).toLocalDate()
assertEquals(2020, date.year)
assertEquals(6, date.monthValue)
@@ -76,7 +87,7 @@ class TimeApiExtensionsTest {
}
@Test
- fun testDateTimeToLocalDate_UtcDuringDay() {
+ fun testDateTime_toLocalDate_UtcDuringDay() {
val date = DateTime("20200620T123000Z").apply { isUtc = true }.toLocalDate()
assertEquals(2020, date.year)
assertEquals(6, date.monthValue)
@@ -86,23 +97,23 @@ class TimeApiExtensionsTest {
@Test
- fun testDateTimeToLocalTime() {
+ fun testDateTime_toLocalTime() {
assertEquals(LocalTime.of(12, 30), DateTime("20200620T123000", tzBerlin).toLocalTime())
}
@Test
- fun testDateTimeToLocalTime_Floating() {
+ fun testDateTime_toLocalTime_Floating() {
assertEquals(LocalTime.of(12, 30), DateTime("20200620T123000").toLocalTime())
}
@Test
- fun testDateTimeToLocalTime_Utc() {
+ fun testDateTime_toLocalTime_Utc() {
assertEquals(LocalTime.of(12, 30), DateTime("20200620T123000Z").apply { isUtc = true }.toLocalTime())
}
@Test
- fun testDateTimeToZonedDateTime() {
+ fun testDateTime_toZonedDateTime() {
assertEquals(
ZonedDateTime.of(2020, 7, 7, 10, 30, 0, 0, tzBerlin.toZoneIdCompat()),
DateTime("20200707T103000", tzBerlin).toZonedDateTime()
@@ -110,7 +121,7 @@ class TimeApiExtensionsTest {
}
@Test
- fun testDateTimeToZonedDateTime_Floating() {
+ fun testDateTime_toZonedDateTime_Floating() {
assertEquals(
ZonedDateTime.of(2020, 7, 7, 10, 30, 0, 0, ZoneId.systemDefault()),
DateTime("20200707T103000").toZonedDateTime()
@@ -118,7 +129,7 @@ class TimeApiExtensionsTest {
}
@Test
- fun testDateTimeToZonedDateTime_UTC() {
+ fun testDateTime_toZonedDateTime_UTC() {
assertEquals(
ZonedDateTime.of(2020, 7, 7, 10, 30, 0, 0, ZoneOffset.UTC),
DateTime("20200707T103000Z").apply { isUtc = true }.toZonedDateTime()
@@ -127,31 +138,31 @@ class TimeApiExtensionsTest {
@Test
- fun testLocalDateToIcal4jDate() {
+ fun testLocalDate_toIcal4jDate() {
assertEquals(Date("19000118"), LocalDate.of(1900, 1, 18).toIcal4jDate())
assertEquals(Date("20200620"), LocalDate.of(2020, 6, 20).toIcal4jDate())
}
@Test
- fun testZonedDateTimeToIcal4jDateTime() {
+ fun testZonedDateTime_toIcal4jDateTime_NotUtc() {
val tzBerlin = DateUtils.ical4jTimeZone("Europe/Berlin")
assertEquals(
- DateTime("20200705T010203", tzBerlin),
- ZonedDateTime.of(2020, 7, 5, 1, 2, 3, 0, ZoneId.of("Europe/Berlin")).toIcal4jDateTime()
+ DateTime("20200705T010203", tzBerlin),
+ ZonedDateTime.of(2020, 7, 5, 1, 2, 3, 0, ZoneId.of("Europe/Berlin")).toIcal4jDateTime()
)
}
@Test
- fun testZonedDateTimeToIcal4jDateTime_Utc() {
+ fun testZonedDateTime_toIcal4jDateTime_Utc() {
assertEquals(
- DateTime("20200705T010203Z"),
- ZonedDateTime.of(2020, 7, 5, 1, 2, 3, 0, ZoneOffset.UTC).toIcal4jDateTime()
+ DateTime("20200705T010203Z"),
+ ZonedDateTime.of(2020, 7, 5, 1, 2, 3, 0, ZoneOffset.UTC).toIcal4jDateTime()
)
}
@Test
- fun testTemporalAmountToDuration() {
+ fun testTemporalAmount_toDuration() {
assertEquals(Duration.ofHours(1), Duration.ofHours(1).toDuration(Instant.EPOCH))
assertEquals(Duration.ofDays(1), Duration.ofDays(1).toDuration(Instant.EPOCH))
assertEquals(Duration.ofDays(1), Period.ofDays(1).toDuration(Instant.EPOCH))
@@ -161,7 +172,7 @@ class TimeApiExtensionsTest {
}
@Test
- fun testTemporalAmountToRfc5545Duration_Duration() {
+ fun testTemporalAmount_toRfc5545Duration_Duration() {
assertEquals("P0S", Duration.ofDays(0).toRfc5545Duration(Instant.EPOCH))
assertEquals("P2W", Duration.ofDays(14).toRfc5545Duration(Instant.EPOCH))
assertEquals("P15D", Duration.ofDays(15).toRfc5545Duration(Instant.EPOCH))
diff --git a/src/test/java/at/bitfire/ical4android/validation/EventValidatorTest.kt b/src/androidTest/java/at/bitfire/ical4android/validation/EventValidatorTest.kt
index c64d0d7..fa3d959 100644
--- a/src/test/java/at/bitfire/ical4android/validation/EventValidatorTest.kt
+++ b/src/androidTest/java/at/bitfire/ical4android/validation/EventValidatorTest.kt
@@ -6,44 +6,42 @@ package at.bitfire.ical4android.validation
import at.bitfire.ical4android.Event
import at.bitfire.ical4android.InvalidCalendarException
-import net.fortuna.ical4j.model.Date
-import net.fortuna.ical4j.model.DateTime
-import net.fortuna.ical4j.model.Recur
-import net.fortuna.ical4j.model.TimeZoneRegistryFactory
+import net.fortuna.ical4j.model.*
import net.fortuna.ical4j.model.property.DtEnd
import net.fortuna.ical4j.model.property.DtStart
import net.fortuna.ical4j.model.property.RRule
import org.junit.Assert.*
+import org.junit.Assume
import org.junit.Test
import java.io.StringReader
-import java.util.*
class EventValidatorTest {
- val tzReg = TimeZoneRegistryFactory.getInstance().createRegistry()
+ companion object {
+ val tzReg = TimeZoneRegistryFactory.getInstance().createRegistry()
+ }
// DTSTART and DTEND
- @Test
- fun testEnsureCorrectStartAndEndTime_noDtStart() {
- assertThrows(InvalidCalendarException::class.java) {
- val event = Event().apply {
- dtEnd = DtEnd(DateTime("20000105T000000")) // DATETIME
- // no dtStart
- }
- EventValidator.correctStartAndEndTime(event)
+ @Test(expected = InvalidCalendarException::class)
+ fun testEnsureCorrectStartAndEndTime_noDtStart_DateTime() {
+ val event = Event().apply {
+ dtEnd = DtEnd(DateTime("20000105T000000")) // DATETIME
+ // no dtStart
}
+ EventValidator.correctStartAndEndTime(event)
+ }
- assertThrows(InvalidCalendarException::class.java) {
- Event.eventsFromReader(StringReader(
- "BEGIN:VCALENDAR\n" +
- "BEGIN:VEVENT\n" +
- "UID:51d8529a-5844-4609-918b-2891b855e0e8\n" +
- "DTEND;VALUE=DATE:20211116\n" + // DATE
- "END:VEVENT\n" +
- "END:VCALENDAR")).first()
- }
+ @Test(expected = InvalidCalendarException::class)
+ fun testEnsureCorrectStartAndEndTime_noDtStart_Date() {
+ Event.eventsFromReader(StringReader(
+ "BEGIN:VCALENDAR\n" +
+ "BEGIN:VEVENT\n" +
+ "UID:51d8529a-5844-4609-918b-2891b855e0e8\n" +
+ "DTEND;VALUE=DATE:20211116\n" + // DATE
+ "END:VEVENT\n" +
+ "END:VCALENDAR")).first()
}
@Test
@@ -79,10 +77,10 @@ class EventValidatorTest {
rRules.add(RRule("FREQ=MONTHLY;UNTIL=20251214T001100Z")) // DATETIME (UTC)
}
assertEquals(DateTime("20211115T001100Z"), event.dtStart!!.date)
- assertEquals(DateTime("20251214T001100Z"), event.rRules.first.recur.until)
+ assertEquals("FREQ=MONTHLY;UNTIL=20251214T001100Z", event.rRules.joinToString())
EventValidator.sameTypeForDtStartAndRruleUntil(event.dtStart!!, event.rRules)
assertEquals(DateTime("20211115T001100Z"), event.dtStart!!.date)
- assertEquals(DateTime("20251214T001100Z"), event.rRules.first.recur.until)
+ assertEquals("FREQ=MONTHLY;UNTIL=20251214T001100Z", event.rRules.joinToString())
val event1 = Event.eventsFromReader(StringReader(
"BEGIN:VCALENDAR\n" +
@@ -92,7 +90,7 @@ class EventValidatorTest {
"RRULE:FREQ=MONTHLY;UNTIL=20231214;BYMONTHDAY=15\n" + // DATE
"END:VEVENT\n" +
"END:VCALENDAR")).first()
- assertEquals(Date("20231214"), event1.rRules.first.recur.until)
+ assertEquals("FREQ=MONTHLY;UNTIL=20231214;BYMONTHDAY=15", event1.rRules.joinToString())
val event2 = Event.eventsFromReader(StringReader(
"BEGIN:VCALENDAR\n" +
@@ -102,7 +100,7 @@ class EventValidatorTest {
"RRULE:FREQ=YEARLY;UNTIL=20230216;BYMONTHDAY=15\n" + // DATE
"END:VEVENT\n" +
"END:VCALENDAR")).first()
- assertEquals(Date("20230216"), event2.rRules.first.recur.until)
+ assertEquals("FREQ=YEARLY;UNTIL=20230216;BYMONTHDAY=15", event2.rRules.joinToString())
}
@Test
@@ -119,7 +117,7 @@ class EventValidatorTest {
event.rRules.first.recur.until
)
EventValidator.sameTypeForDtStartAndRruleUntil(event.dtStart!!, event.rRules)
- assertEquals(Date("20211214"), event.rRules.first.recur.until)
+ assertEquals("FREQ=MONTHLY;UNTIL=20211214", event.rRules.joinToString())
val event1 = Event.eventsFromReader(
StringReader(
@@ -133,7 +131,7 @@ class EventValidatorTest {
)
).first()
assertEquals(1639440000000, event1.rRules.first.recur.until.time)
- assertEquals(Date("20211214"), event1.rRules.first.recur.until)
+ assertEquals("FREQ=MONTHLY;UNTIL=20211214;BYMONTHDAY=15", event1.rRules.joinToString())
}
@Test
@@ -150,22 +148,22 @@ class EventValidatorTest {
// As it does not happen often, for the sake of simplicity we just accept either
EventValidator.sameTypeForDtStartAndRruleUntil(event2.dtStart!!, event2.rRules)
assertTrue(
- Date("20230218") == event2.rRules.first.recur.until ||
- Date("20230217") == event2.rRules.first.recur.until
+ "FREQ=YEARLY;UNTIL=20230218;BYMONTHDAY=15" == event2.rRules.joinToString() ||
+ "FREQ=YEARLY;UNTIL=20230217;BYMONTHDAY=15" == event2.rRules.joinToString()
)
}
@Test
- fun testSameTypeForDtStartAndRruleUntil_DtStartIsDateTimeAndRruleUntilIsDate() {
+ fun testSameTypeForDtStartAndRruleUntil_DtStartIsDateTimeWithTzAndRruleUntilIsDate() {
// should add (possibly missing) time in UNTIL if DTSTART value is of type DATETIME (not just DATE)
val event = Event().apply {
dtStart = DtStart(DateTime("20110605T001100Z")) // DATETIME (UTC)
rRules.add(RRule("FREQ=MONTHLY;UNTIL=20211214")) // DATE
}
- assertEquals(Date("20211214"), event.rRules.first.recur.until)
+ assertEquals("FREQ=MONTHLY;UNTIL=20211214", event.rRules.joinToString())
EventValidator.sameTypeForDtStartAndRruleUntil(event.dtStart!!, event.rRules)
- assertEquals(DateTime("20211214T001100Z"), event.rRules.first.recur.until)
+ assertEquals("FREQ=MONTHLY;UNTIL=20211214T001100Z", event.rRules.joinToString())
val event1 = Event.eventsFromReader(StringReader(
"BEGIN:VCALENDAR\n" +
@@ -175,17 +173,34 @@ class EventValidatorTest {
"RRULE:FREQ=MONTHLY;UNTIL=20211214;BYMONTHDAY=15\n" + // DATE
"END:VEVENT\n" +
"END:VCALENDAR")).first()
- assertEquals(DateTime("20211214T053000", tzReg.getTimeZone("America/New_York")), event1.rRules.first.recur.until)
+ assertEquals("FREQ=MONTHLY;UNTIL=20211214T103000Z;BYMONTHDAY=15", event1.rRules.joinToString())
+ }
- val event2 = Event.eventsFromReader(StringReader(
- "BEGIN:VCALENDAR\n" +
- "BEGIN:VEVENT\n" +
- "UID:381fb26b-2da5-4dd2-94d7-2e0874128aa7\n" +
- "DTSTART;VALUE=DATETIME:20080214T001100\n" + // DATETIME (no timezone)
- "RRULE:FREQ=YEARLY;UNTIL=20110214;BYMONTHDAY=15\n" + // DATE
- "END:VEVENT\n" +
- "END:VCALENDAR")).first()
- assertEquals(DateTime("20110214T001100"), event2.rRules.first.recur.until)
+ @Test
+ fun testSameTypeForDtStartAndRruleUntil_DtStartIsDateTimeWithoutTzAndRruleUntilIsDate() {
+ // should add (possibly missing) time in UNTIL if DTSTART value is of type DATETIME (not just DATE)
+
+ val event = Event().apply {
+ dtStart = DtStart(DateTime("20110605T001100Z")) // DATETIME (UTC)
+ rRules.add(RRule("FREQ=MONTHLY;UNTIL=20211214")) // DATE
+ }
+ assertEquals("FREQ=MONTHLY;UNTIL=20211214", event.rRules.joinToString())
+ EventValidator.sameTypeForDtStartAndRruleUntil(event.dtStart!!, event.rRules)
+ assertEquals("FREQ=MONTHLY;UNTIL=20211214T001100Z", event.rRules.joinToString())
+
+ Assume.assumeTrue(TimeZone.getDefault().id == "Europe/Vienna")
+ val event2 = Event.eventsFromReader(
+ StringReader(
+ "BEGIN:VCALENDAR\n" +
+ "BEGIN:VEVENT\n" +
+ "UID:381fb26b-2da5-4dd2-94d7-2e0874128aa7\n" +
+ "DTSTART;VALUE=DATETIME:20080214T001100\n" + // DATETIME (no timezone)
+ "RRULE:FREQ=YEARLY;UNTIL=20110214;BYMONTHDAY=15\n" + // DATE
+ "END:VEVENT\n" +
+ "END:VCALENDAR"
+ )
+ ).first()
+ assertEquals("FREQ=YEARLY;UNTIL=20110213T231100Z;BYMONTHDAY=15", event2.rRules.joinToString())
}
@@ -311,4 +326,10 @@ class EventValidatorTest {
), rrules.toTypedArray())
}
+
+ // helpers
+
+ private fun Iterable<RRule>.joinToString(): String =
+ this.map { rRule -> rRule.value }.joinToString("\n")
+
} \ No newline at end of file
diff --git a/src/test/resources/events/all-day-0sec.ics b/src/androidTest/resources/events/all-day-0sec.ics
index 07679f2..07679f2 100644
--- a/src/test/resources/events/all-day-0sec.ics
+++ b/src/androidTest/resources/events/all-day-0sec.ics
diff --git a/src/test/resources/events/all-day-10days.ics b/src/androidTest/resources/events/all-day-10days.ics
index 52e6dbd..52e6dbd 100644
--- a/src/test/resources/events/all-day-10days.ics
+++ b/src/androidTest/resources/events/all-day-10days.ics
diff --git a/src/test/resources/events/all-day-1day.ics b/src/androidTest/resources/events/all-day-1day.ics
index 506d295..506d295 100644
--- a/src/test/resources/events/all-day-1day.ics
+++ b/src/androidTest/resources/events/all-day-1day.ics
diff --git a/src/test/resources/events/dst-only-vtimezone.ics b/src/androidTest/resources/events/dst-only-vtimezone.ics
index e49d74d..e49d74d 100644
--- a/src/test/resources/events/dst-only-vtimezone.ics
+++ b/src/androidTest/resources/events/dst-only-vtimezone.ics
diff --git a/src/test/resources/events/event-on-that-day.ics b/src/androidTest/resources/events/event-on-that-day.ics
index 0ccbd4f..0ccbd4f 100644
--- a/src/test/resources/events/event-on-that-day.ics
+++ b/src/androidTest/resources/events/event-on-that-day.ics
diff --git a/src/test/resources/events/latin1.ics b/src/androidTest/resources/events/latin1.ics
index 20d97d0..20d97d0 100644
--- a/src/test/resources/events/latin1.ics
+++ b/src/androidTest/resources/events/latin1.ics
diff --git a/src/test/resources/events/multiple.ics b/src/androidTest/resources/events/multiple.ics
index 6e300ab..6e300ab 100644
--- a/src/test/resources/events/multiple.ics
+++ b/src/androidTest/resources/events/multiple.ics
diff --git a/src/test/resources/events/one-event-with-exception-one-without.ics b/src/androidTest/resources/events/one-event-with-exception-one-without.ics
index 182711b..182711b 100644
--- a/src/test/resources/events/one-event-with-exception-one-without.ics
+++ b/src/androidTest/resources/events/one-event-with-exception-one-without.ics
diff --git a/src/test/resources/events/one-event-with-multiple-exceptions-one-without.ics b/src/androidTest/resources/events/one-event-with-multiple-exceptions-one-without.ics
index bff4528..bff4528 100644
--- a/src/test/resources/events/one-event-with-multiple-exceptions-one-without.ics
+++ b/src/androidTest/resources/events/one-event-with-multiple-exceptions-one-without.ics
diff --git a/src/test/resources/events/outlook1.ics b/src/androidTest/resources/events/outlook1.ics
index 000afaa..000afaa 100644
--- a/src/test/resources/events/outlook1.ics
+++ b/src/androidTest/resources/events/outlook1.ics
diff --git a/src/test/resources/events/recurring-only-exception.ics b/src/androidTest/resources/events/recurring-only-exception.ics
index 26012e1..26012e1 100644
--- a/src/test/resources/events/recurring-only-exception.ics
+++ b/src/androidTest/resources/events/recurring-only-exception.ics
diff --git a/src/test/resources/events/recurring-with-exception1.ics b/src/androidTest/resources/events/recurring-with-exception1.ics
index 0382497..0382497 100644
--- a/src/test/resources/events/recurring-with-exception1.ics
+++ b/src/androidTest/resources/events/recurring-with-exception1.ics
diff --git a/src/test/resources/events/two-events-without-exceptions.ics b/src/androidTest/resources/events/two-events-without-exceptions.ics
index be6290f..be6290f 100644
--- a/src/test/resources/events/two-events-without-exceptions.ics
+++ b/src/androidTest/resources/events/two-events-without-exceptions.ics
diff --git a/src/test/resources/events/two-line-description-without-crlf.ics b/src/androidTest/resources/events/two-line-description-without-crlf.ics
index 03c8b62..03c8b62 100644
--- a/src/test/resources/events/two-line-description-without-crlf.ics
+++ b/src/androidTest/resources/events/two-line-description-without-crlf.ics
diff --git a/src/test/resources/events/utf8.ics b/src/androidTest/resources/events/utf8.ics
index 87171a9..87171a9 100644
--- a/src/test/resources/events/utf8.ics
+++ b/src/androidTest/resources/events/utf8.ics
diff --git a/src/test/resources/events/vienna-evolution.ics b/src/androidTest/resources/events/vienna-evolution.ics
index 5f11911..5f11911 100644
--- a/src/test/resources/events/vienna-evolution.ics
+++ b/src/androidTest/resources/events/vienna-evolution.ics
diff --git a/src/test/resources/tasks/empty-priority.ics b/src/androidTest/resources/tasks/empty-priority.ics
index 69ad1ae..69ad1ae 100644
--- a/src/test/resources/tasks/empty-priority.ics
+++ b/src/androidTest/resources/tasks/empty-priority.ics
diff --git a/src/test/resources/tasks/latin1.ics b/src/androidTest/resources/tasks/latin1.ics
index b3502de..b3502de 100644
--- a/src/test/resources/tasks/latin1.ics
+++ b/src/androidTest/resources/tasks/latin1.ics
diff --git a/src/test/resources/tasks/most-fields1.ics b/src/androidTest/resources/tasks/most-fields1.ics
index ff4dbb0..ff4dbb0 100644
--- a/src/test/resources/tasks/most-fields1.ics
+++ b/src/androidTest/resources/tasks/most-fields1.ics
diff --git a/src/test/resources/tasks/most-fields2.ics b/src/androidTest/resources/tasks/most-fields2.ics
index f6c7215..f6c7215 100644
--- a/src/test/resources/tasks/most-fields2.ics
+++ b/src/androidTest/resources/tasks/most-fields2.ics
diff --git a/src/test/resources/tasks/rfc5545-sample1.ics b/src/androidTest/resources/tasks/rfc5545-sample1.ics
index 18d53df..18d53df 100644
--- a/src/test/resources/tasks/rfc5545-sample1.ics
+++ b/src/androidTest/resources/tasks/rfc5545-sample1.ics
diff --git a/src/test/resources/tasks/utf8.ics b/src/androidTest/resources/tasks/utf8.ics
index 67aaa1e..67aaa1e 100644
--- a/src/test/resources/tasks/utf8.ics
+++ b/src/androidTest/resources/tasks/utf8.ics
diff --git a/src/test/resources/tz/Karachi.ics b/src/androidTest/resources/tz/Karachi.ics
index 1515d2b..1515d2b 100644
--- a/src/test/resources/tz/Karachi.ics
+++ b/src/androidTest/resources/tz/Karachi.ics
diff --git a/src/test/resources/tz/Mogadishu.ics b/src/androidTest/resources/tz/Mogadishu.ics
index b5e4fd3..b5e4fd3 100644
--- a/src/test/resources/tz/Mogadishu.ics
+++ b/src/androidTest/resources/tz/Mogadishu.ics
diff --git a/src/test/resources/tz/Vienna.ics b/src/androidTest/resources/tz/Vienna.ics
index 5b0f65f..5b0f65f 100644
--- a/src/test/resources/tz/Vienna.ics
+++ b/src/androidTest/resources/tz/Vienna.ics
diff --git a/src/main/java/at/bitfire/ical4android/AndroidCalendar.kt b/src/main/java/at/bitfire/ical4android/AndroidCalendar.kt
index 398da20..aa92e57 100644
--- a/src/main/java/at/bitfire/ical4android/AndroidCalendar.kt
+++ b/src/main/java/at/bitfire/ical4android/AndroidCalendar.kt
@@ -10,8 +10,8 @@ import android.content.ContentUris
import android.content.ContentValues
import android.net.Uri
import android.provider.CalendarContract.*
-import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues
-import at.bitfire.ical4android.MiscUtils.UriHelper.asSyncAdapter
+import at.bitfire.ical4android.util.MiscUtils.CursorHelper.toValues
+import at.bitfire.ical4android.util.MiscUtils.UriHelper.asSyncAdapter
import java.io.FileNotFoundException
import java.util.*
import java.util.logging.Level
diff --git a/src/main/java/at/bitfire/ical4android/AndroidCompatTimeZoneRegistry.kt b/src/main/java/at/bitfire/ical4android/AndroidCompatTimeZoneRegistry.kt
new file mode 100644
index 0000000..5f7246d
--- /dev/null
+++ b/src/main/java/at/bitfire/ical4android/AndroidCompatTimeZoneRegistry.kt
@@ -0,0 +1,83 @@
+package at.bitfire.ical4android
+
+import net.fortuna.ical4j.model.*
+import net.fortuna.ical4j.model.component.VTimeZone
+import net.fortuna.ical4j.model.property.TzId
+import java.time.ZoneId
+
+/**
+ * The purpose of this class is that if a time zone has a different name in ical4j and Android,
+ * it should use the Android name.
+ *
+ * For instance, if a time zone is known as "Europe/Kyiv" (with alias "Europe/Kiev") in ical4j
+ * and only "Europe/Kiev" in Android, this registry behaves like the default [TimeZoneRegistryImpl],
+ * but the returned time zone for `getTimeZone("Europe/Kiev")` has an ID of "Europe/Kiev" and not
+ * "Europe/Kyiv".
+ */
+class AndroidCompatTimeZoneRegistry(
+ private val base: TimeZoneRegistry
+): TimeZoneRegistry by base {
+
+ /**
+ * Gets the time zone for a given ID.
+ *
+ * If a time zone with the given ID exists in Android, the icalj timezone for this ID
+ * is returned, but the TZID is set to the Android name (and not the ical4j name, which
+ * may not be known to Android).
+ *
+ * If a time zone with the given ID doesn't exist in Android, this method returns the
+ * result of its [base] method.
+ *
+ * @param id
+ * @return time zone
+ */
+ override fun getTimeZone(id: String): TimeZone? {
+ // check whether time zone is available on Android
+ val androidTzId =
+ try {
+ ZoneId.of(id).id
+ } catch (e: Exception) {
+ /* Not available in Android, should return null in a later version.
+ However, we return the ical4j timezone to keep the changes caused by AndroidCompatTimeZoneRegistry introduction
+ as small as possible. */
+ return base.getTimeZone(id)
+ }
+
+ /* Time zone known by Android. Unfortunately, we can't use the Android timezone database directly
+ to generate ical4j timezone definitions (which are based on VTIMEZONE).
+ So we have to use the timezone definition from ical4j (based on its own VTIMEZONE database),
+ but we also need to use the Android TZ name (otherwise Android may not understand it later).
+
+ Example: getTimeZone("Europe/Kiev") returns a TimeZone with TZID:Europe/Kyiv since ical4j/3.2.5,
+ but most Android devices don't now Europe/Kyiv yet.
+ */
+ val tz = base.getTimeZone(id)
+ if (tz.id != androidTzId) {
+ Ical4Android.log.warning("Using Android TZID $androidTzId instead of ical4j ${tz.id}")
+
+ // create a copy of the VTIMEZONE so that we don't modify the original registry values (which are not immutable)
+ val vTimeZone = tz.vTimeZone
+ val newVTimeZoneProperties = PropertyList(vTimeZone.properties)
+ newVTimeZoneProperties.removeAll { property ->
+ property is TzId
+ }
+ newVTimeZoneProperties += TzId(androidTzId)
+ return TimeZone(VTimeZone(
+ newVTimeZoneProperties,
+ vTimeZone.observances
+ ))
+ } else
+ return tz
+ }
+
+
+ class Factory : TimeZoneRegistryFactory() {
+
+ override fun createRegistry(): TimeZoneRegistry {
+ val ical4jRegistry = DefaultTimeZoneRegistryFactory().createRegistry()
+ return AndroidCompatTimeZoneRegistry(ical4jRegistry)
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/at/bitfire/ical4android/AndroidEvent.kt b/src/main/java/at/bitfire/ical4android/AndroidEvent.kt
index 4230ee0..7c354a6 100644
--- a/src/main/java/at/bitfire/ical4android/AndroidEvent.kt
+++ b/src/main/java/at/bitfire/ical4android/AndroidEvent.kt
@@ -14,9 +14,11 @@ import android.provider.CalendarContract.*
import android.util.Patterns
import androidx.annotation.CallSuper
import at.bitfire.ical4android.BatchOperation.CpoBuilder
-import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues
-import at.bitfire.ical4android.MiscUtils.UriHelper.asSyncAdapter
import at.bitfire.ical4android.util.AndroidTimeUtils
+import at.bitfire.ical4android.util.DateUtils
+import at.bitfire.ical4android.util.MiscUtils
+import at.bitfire.ical4android.util.MiscUtils.CursorHelper.toValues
+import at.bitfire.ical4android.util.MiscUtils.UriHelper.asSyncAdapter
import at.bitfire.ical4android.util.TimeApiExtensions
import at.bitfire.ical4android.util.TimeApiExtensions.requireZoneId
import at.bitfire.ical4android.util.TimeApiExtensions.toIcal4jDate
@@ -25,7 +27,6 @@ import at.bitfire.ical4android.util.TimeApiExtensions.toLocalDate
import at.bitfire.ical4android.util.TimeApiExtensions.toLocalTime
import at.bitfire.ical4android.util.TimeApiExtensions.toRfc5545Duration
import at.bitfire.ical4android.util.TimeApiExtensions.toZonedDateTime
-import at.bitfire.ical4android.validation.EventValidator
import net.fortuna.ical4j.model.*
import net.fortuna.ical4j.model.Date
import net.fortuna.ical4j.model.component.VAlarm
@@ -227,6 +228,7 @@ abstract class AndroidEvent(
}
}
event.dtStart = DtStart(dtStartDateTime)
+ AndroidTimeUtils.androidifyTimeZone(event.dtStart) // because it may have an ical4j timezone ID that is not available in Android
// Android events MUST have duration or dtend [https://developer.android.com/reference/android/provider/CalendarContract.Events#operations].
// Assume 1 hour if missing (should never occur, but occurs).
@@ -780,10 +782,11 @@ abstract class AndroidEvent(
builder .withValue(Events.DURATION, duration?.toRfc5545Duration(dtStart.date.toInstant()))
.withValue(Events.DTEND, null)
- // add RRULe
- if (event.rRules.isNotEmpty())
- builder.withValue(Events.RRULE, event.rRules.joinToString(AndroidTimeUtils.RECURRENCE_RULE_SEPARATOR) { it.value })
- else
+ // add RRULEs
+ if (event.rRules.isNotEmpty()) {
+ builder.withValue(Events.RRULE, event.rRules
+ .joinToString(AndroidTimeUtils.RECURRENCE_RULE_SEPARATOR) { it.value })
+ } else
builder.withValue(Events.RRULE, null)
if (event.rDates.isNotEmpty()) {
diff --git a/src/main/java/at/bitfire/ical4android/AndroidTask.kt b/src/main/java/at/bitfire/ical4android/AndroidTask.kt
index 84af609..3e56dc1 100644
--- a/src/main/java/at/bitfire/ical4android/AndroidTask.kt
+++ b/src/main/java/at/bitfire/ical4android/AndroidTask.kt
@@ -10,8 +10,10 @@ import android.net.Uri
import android.os.RemoteException
import androidx.annotation.CallSuper
import at.bitfire.ical4android.BatchOperation.CpoBuilder
-import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues
import at.bitfire.ical4android.util.AndroidTimeUtils
+import at.bitfire.ical4android.util.DateUtils
+import at.bitfire.ical4android.util.MiscUtils
+import at.bitfire.ical4android.util.MiscUtils.CursorHelper.toValues
import net.fortuna.ical4j.model.*
import net.fortuna.ical4j.model.Date
import net.fortuna.ical4j.model.TimeZone
diff --git a/src/main/java/at/bitfire/ical4android/AndroidTaskList.kt b/src/main/java/at/bitfire/ical4android/AndroidTaskList.kt
index 8439d80..01215fe 100644
--- a/src/main/java/at/bitfire/ical4android/AndroidTaskList.kt
+++ b/src/main/java/at/bitfire/ical4android/AndroidTaskList.kt
@@ -8,8 +8,8 @@ import android.accounts.Account
import android.content.ContentUris
import android.content.ContentValues
import android.net.Uri
-import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues
-import at.bitfire.ical4android.MiscUtils.UriHelper.asSyncAdapter
+import at.bitfire.ical4android.util.MiscUtils.CursorHelper.toValues
+import at.bitfire.ical4android.util.MiscUtils.UriHelper.asSyncAdapter
import org.dmfs.tasks.contract.TaskContract
import org.dmfs.tasks.contract.TaskContract.Property.Relation
import org.dmfs.tasks.contract.TaskContract.TaskLists
diff --git a/src/main/java/at/bitfire/ical4android/Event.kt b/src/main/java/at/bitfire/ical4android/Event.kt
index 3f2e567..7c6449b 100644
--- a/src/main/java/at/bitfire/ical4android/Event.kt
+++ b/src/main/java/at/bitfire/ical4android/Event.kt
@@ -4,8 +4,8 @@
package at.bitfire.ical4android
-import at.bitfire.ical4android.DateUtils.isDateTime
import at.bitfire.ical4android.ICalendar.Companion.CALENDAR_NAME
+import at.bitfire.ical4android.util.DateUtils.isDateTime
import at.bitfire.ical4android.validation.EventValidator
import net.fortuna.ical4j.data.CalendarOutputter
import net.fortuna.ical4j.data.ParserException
diff --git a/src/main/java/at/bitfire/ical4android/ICalendar.kt b/src/main/java/at/bitfire/ical4android/ICalendar.kt
index 678b195..c519278 100644
--- a/src/main/java/at/bitfire/ical4android/ICalendar.kt
+++ b/src/main/java/at/bitfire/ical4android/ICalendar.kt
@@ -4,6 +4,7 @@
package at.bitfire.ical4android
+import at.bitfire.ical4android.util.MiscUtils
import at.bitfire.ical4android.validation.ICalPreprocessor
import net.fortuna.ical4j.data.CalendarBuilder
import net.fortuna.ical4j.data.ParserException
diff --git a/src/main/java/at/bitfire/ical4android/JtxCollection.kt b/src/main/java/at/bitfire/ical4android/JtxCollection.kt
index 1527ef5..dc920be 100644
--- a/src/main/java/at/bitfire/ical4android/JtxCollection.kt
+++ b/src/main/java/at/bitfire/ical4android/JtxCollection.kt
@@ -10,7 +10,7 @@ import android.content.ContentUris
import android.content.ContentValues
import android.content.Context
import android.net.Uri
-import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues
+import at.bitfire.ical4android.util.MiscUtils.CursorHelper.toValues
import at.techbee.jtx.JtxContract
import at.techbee.jtx.JtxContract.asSyncAdapter
import net.fortuna.ical4j.model.Calendar
diff --git a/src/main/java/at/bitfire/ical4android/JtxICalObject.kt b/src/main/java/at/bitfire/ical4android/JtxICalObject.kt
index b35f733..9ffd5d3 100644
--- a/src/main/java/at/bitfire/ical4android/JtxICalObject.kt
+++ b/src/main/java/at/bitfire/ical4android/JtxICalObject.kt
@@ -10,7 +10,7 @@ import android.net.ParseException
import android.net.Uri
import android.os.ParcelFileDescriptor
import android.util.Base64
-import at.bitfire.ical4android.MiscUtils.CursorHelper.toValues
+import at.bitfire.ical4android.util.MiscUtils.CursorHelper.toValues
import at.techbee.jtx.JtxContract
import at.techbee.jtx.JtxContract.JtxICalObject.TZ_ALLDAY
import at.techbee.jtx.JtxContract.asSyncAdapter
diff --git a/src/main/java/at/bitfire/ical4android/Task.kt b/src/main/java/at/bitfire/ical4android/Task.kt
index 0e70ca0..b19e1c6 100644
--- a/src/main/java/at/bitfire/ical4android/Task.kt
+++ b/src/main/java/at/bitfire/ical4android/Task.kt
@@ -5,6 +5,7 @@
package at.bitfire.ical4android
import androidx.annotation.IntRange
+import at.bitfire.ical4android.util.DateUtils
import net.fortuna.ical4j.data.CalendarOutputter
import net.fortuna.ical4j.data.ParserException
import net.fortuna.ical4j.model.*
diff --git a/src/main/java/at/bitfire/ical4android/TaskProvider.kt b/src/main/java/at/bitfire/ical4android/TaskProvider.kt
index 6754388..a9a14e3 100644
--- a/src/main/java/at/bitfire/ical4android/TaskProvider.kt
+++ b/src/main/java/at/bitfire/ical4android/TaskProvider.kt
@@ -9,7 +9,7 @@ import android.content.ContentProviderClient
import android.content.Context
import android.content.pm.PackageManager
import androidx.core.content.pm.PackageInfoCompat
-import at.bitfire.ical4android.MiscUtils.ContentProviderClientHelper.closeCompat
+import at.bitfire.ical4android.util.MiscUtils.ContentProviderClientHelper.closeCompat
import org.dmfs.tasks.contract.TaskContract
import java.io.Closeable
import java.util.logging.Level
diff --git a/src/main/java/at/bitfire/ical4android/util/AndroidTimeUtils.kt b/src/main/java/at/bitfire/ical4android/util/AndroidTimeUtils.kt
index 9d22cd8..ec4388a 100644
--- a/src/main/java/at/bitfire/ical4android/util/AndroidTimeUtils.kt
+++ b/src/main/java/at/bitfire/ical4android/util/AndroidTimeUtils.kt
@@ -7,7 +7,6 @@
package at.bitfire.ical4android.util
import android.text.format.Time
-import at.bitfire.ical4android.DateUtils
import at.bitfire.ical4android.Ical4Android
import net.fortuna.ical4j.model.*
import net.fortuna.ical4j.model.Date
@@ -105,6 +104,8 @@ object AndroidTimeUtils {
* Returns the time-zone ID for a given date or date-time that should be used to store it
* in the Android calendar provider.
*
+ * Does not check whether Android actually knows the time zone ID – use [androidifyTimeZone] for that.
+ *
* @param date DateProperty (DATE or DATE-TIME) whose time-zone information is used
*
* @return - UTC for dates and UTC date-times
diff --git a/src/main/java/at/bitfire/ical4android/DateUtils.kt b/src/main/java/at/bitfire/ical4android/util/DateUtils.kt
index c9ceffa..6c0060e 100644
--- a/src/main/java/at/bitfire/ical4android/DateUtils.kt
+++ b/src/main/java/at/bitfire/ical4android/util/DateUtils.kt
@@ -2,8 +2,10 @@
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
**************************************************************************************************/
-package at.bitfire.ical4android
+package at.bitfire.ical4android.util
+import at.bitfire.ical4android.Ical4Android
+import at.bitfire.ical4android.UsesThreadContextClassLoader
import net.fortuna.ical4j.data.CalendarBuilder
import net.fortuna.ical4j.model.Date
import net.fortuna.ical4j.model.DateTime
diff --git a/src/main/java/at/bitfire/ical4android/MiscUtils.kt b/src/main/java/at/bitfire/ical4android/util/MiscUtils.kt
index 0c76624..76b3e02 100644
--- a/src/main/java/at/bitfire/ical4android/MiscUtils.kt
+++ b/src/main/java/at/bitfire/ical4android/util/MiscUtils.kt
@@ -2,7 +2,7 @@
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
**************************************************************************************************/
-package at.bitfire.ical4android
+package at.bitfire.ical4android.util
import android.accounts.Account
import android.content.ContentProviderClient
diff --git a/src/main/java/at/bitfire/ical4android/util/TimeApiExtensions.kt b/src/main/java/at/bitfire/ical4android/util/TimeApiExtensions.kt
index f9b1ac1..9ff5529 100644
--- a/src/main/java/at/bitfire/ical4android/util/TimeApiExtensions.kt
+++ b/src/main/java/at/bitfire/ical4android/util/TimeApiExtensions.kt
@@ -4,7 +4,6 @@
package at.bitfire.ical4android.util
-import at.bitfire.ical4android.DateUtils
import net.fortuna.ical4j.model.Date
import net.fortuna.ical4j.model.DateTime
import net.fortuna.ical4j.util.TimeZones
@@ -31,8 +30,15 @@ object TimeApiExtensions {
/**
* [TimeZone.toZoneId] can't be used with the current desugaring library yet!
+ *
+ * @return [ZoneId] of the time zone; [ZoneOffset.UTC] if the time zone equals to [TimeZones.getUtcTimeZone]
*/
- fun TimeZone.toZoneIdCompat(): ZoneId = ZoneId.of(id)
+ fun TimeZone.toZoneIdCompat(): ZoneId {
+ return if (this == TimeZones.getUtcTimeZone())
+ ZoneOffset.UTC
+ else
+ ZoneId.of(id)
+ }
/***** Dates *****/
@@ -69,6 +75,15 @@ object TimeApiExtensions {
return Date(cal)
}
+ /**
+ * Converts this zoned date-time (date/time with specific time zone) to an
+ * ical4j [DateTime] object.
+ *
+ * Sets UTC flag ([DateTime.isUtc], means `...ThhmmddZ` format) when this zone-date time object has a
+ * time zone of [ZoneOffset.UTC].
+ *
+ * @return ical4j [DateTime] of the given zoned date-time
+ */
fun ZonedDateTime.toIcal4jDateTime(): DateTime {
val date = DateTime(toEpochSecond() * MILLIS_PER_SECOND)
if (zone == ZoneOffset.UTC)
diff --git a/src/main/java/at/bitfire/ical4android/validation/EventValidator.kt b/src/main/java/at/bitfire/ical4android/validation/EventValidator.kt
index ac0e108..71e6147 100644
--- a/src/main/java/at/bitfire/ical4android/validation/EventValidator.kt
+++ b/src/main/java/at/bitfire/ical4android/validation/EventValidator.kt
@@ -4,19 +4,21 @@
package at.bitfire.ical4android.validation
-import at.bitfire.ical4android.DateUtils
import at.bitfire.ical4android.Event
import at.bitfire.ical4android.Ical4Android
import at.bitfire.ical4android.InvalidCalendarException
+import at.bitfire.ical4android.util.DateUtils
import at.bitfire.ical4android.util.TimeApiExtensions.toIcal4jDate
-import at.bitfire.ical4android.util.TimeApiExtensions.toIcal4jDateTime
import at.bitfire.ical4android.util.TimeApiExtensions.toLocalDate
import at.bitfire.ical4android.util.TimeApiExtensions.toZoneIdCompat
-import net.fortuna.ical4j.model.Date
import net.fortuna.ical4j.model.DateTime
+import net.fortuna.ical4j.model.Recur
import net.fortuna.ical4j.model.property.DtStart
import net.fortuna.ical4j.model.property.RRule
-import java.time.*
+import net.fortuna.ical4j.util.TimeZones
+import java.time.LocalTime
+import java.time.ZonedDateTime
+import java.util.*
/**
* Sometimes CalendarStorage or servers respond with invalid event definitions. Here we try to
@@ -50,34 +52,79 @@ class EventValidator(val e: Event) {
*/
internal fun sameTypeForDtStartAndRruleUntil(dtStart: DtStart, rRules: MutableList<RRule>) {
if (DateUtils.isDate(dtStart)) {
- for (rRule in rRules) {
+ // DTSTART is a DATE
+ val newRRules = mutableListOf<RRule>()
+ val rRuleIterator = rRules.iterator()
+ while (rRuleIterator.hasNext()) {
+ val rRule = rRuleIterator.next()
rRule.recur.until?.let { until ->
if (until is DateTime) {
Ical4Android.log.warning("DTSTART has DATE, but UNTIL has DATETIME; making UNTIL have DATE only")
- rRule.recur.until = until.toLocalDate().toIcal4jDate()
+
+ val newUntil = until.toLocalDate().toIcal4jDate()
+
+ // remove current RRULE and remember new one to be added
+ val newRRule = RRule(Recur.Builder(rRule.recur)
+ .until(newUntil)
+ .build())
+ Ical4Android.log.info("New $newRRule (was ${rRule.toString().trim()})")
+ newRRules += newRRule
+ rRuleIterator.remove()
}
}
}
+ // add repaired RRULEs
+ rRules += newRRules
+
} else if (DateUtils.isDateTime(dtStart)) {
- for (rRule in rRules) {
+ // DTSTART is a DATE-TIME
+ val newRRules = mutableListOf<RRule>()
+ val rRuleIterator = rRules.iterator()
+ while (rRuleIterator.hasNext()) {
+ val rRule = rRuleIterator.next()
rRule.recur.until?.let { until ->
if (until !is DateTime) {
Ical4Android.log.warning("DTSTART has DATETIME, but UNTIL has DATE; copying time from DTSTART to UNTIL")
- val timeZone = if (dtStart.timeZone != null)
- dtStart.timeZone.toZoneIdCompat()
+ val dtStartTimeZone = if (dtStart.timeZone != null)
+ dtStart.timeZone
else if (dtStart.isUtc)
- ZoneOffset.UTC
+ TimeZones.getUtcTimeZone()
else /* floating time */
- ZoneId.systemDefault()
- rRule.recur.until =
- ZonedDateTime.of(
- until.toLocalDate(), // date from until
- LocalTime.ofInstant(dtStart.date.toInstant(), timeZone), // time from dtStart
- timeZone
- ).toIcal4jDateTime()
+ TimeZone.getDefault()
+
+ val dtStartCal = Calendar.getInstance(dtStartTimeZone).apply {
+ time = dtStart.date
+ }
+ val dtStartTime = LocalTime.of(
+ dtStartCal.get(Calendar.HOUR_OF_DAY),
+ dtStartCal.get(Calendar.MINUTE),
+ dtStartCal.get(Calendar.SECOND)
+ )
+
+ val newUntil = ZonedDateTime.of(
+ until.toLocalDate(), // date from until
+ dtStartTime, // time from dtStart
+ dtStartTimeZone.toZoneIdCompat()
+ )
+
+ // Android requires UNTIL in UTC as defined in RFC 2445.
+ // https://android.googlesource.com/platform/frameworks/opt/calendar/+/refs/tags/android-12.1.0_r27/src/com/android/calendarcommon2/RecurrenceProcessor.java#93
+ val newUntilUTC = DateTime(true).apply {
+ time = newUntil.toInstant().toEpochMilli()
+ }
+
+ // remove current RRULE and remember new one to be added
+ val newRRule = RRule(Recur.Builder(rRule.recur)
+ .until(newUntilUTC)
+ .build())
+ Ical4Android.log.info("New $newRRule (was ${rRule.toString().trim()})")
+ newRRules += newRRule
+ rRuleIterator.remove()
}
}
}
+ // add repaired RRULEs
+ rRules += newRRules
} else
throw InvalidCalendarException("Event with invalid DTSTART value")
}
diff --git a/src/main/resources/ical4j.properties b/src/main/resources/ical4j.properties
index dfa5830..5a83b9f 100644
--- a/src/main/resources/ical4j.properties
+++ b/src/main/resources/ical4j.properties
@@ -1,4 +1,5 @@
net.fortuna.ical4j.timezone.cache.impl=net.fortuna.ical4j.util.MapTimeZoneCache
+net.fortuna.ical4j.timezone.registry=at.bitfire.ical4android.AndroidCompatTimeZoneRegistry$Factory
net.fortuna.ical4j.timezone.update.enabled=false
ical4j.parsing.relaxed=true
ical4j.unfolding.relaxed=true
diff --git a/src/test/README.txt b/src/test/README.txt
new file mode 100644
index 0000000..60a244a
--- /dev/null
+++ b/src/test/README.txt
@@ -0,0 +1,6 @@
+
+ATTENTION!
+
+Do not use JVM unit tests for methods the use the Time API or other APIs that behave
+differently in normal JVMs and the Android JVM. Especially do not write JVM unit tests
+that use APIs that are desugared in Android. Use Android unit tests (androidTest) instead.
diff --git a/src/test/java/at/bitfire/ical4android/MiscUtilsTest.kt b/src/test/java/at/bitfire/ical4android/MiscUtilsTest.kt
index e32ba0a..c9cf19d 100644
--- a/src/test/java/at/bitfire/ical4android/MiscUtilsTest.kt
+++ b/src/test/java/at/bitfire/ical4android/MiscUtilsTest.kt
@@ -4,6 +4,7 @@
package at.bitfire.ical4android
+import at.bitfire.ical4android.util.MiscUtils
import org.junit.Assert.assertTrue
import org.junit.Test