diff --git a/app/src/commonTest/java/org/tasks/TestUtilities.kt b/app/src/commonTest/java/org/tasks/TestUtilities.kt index 5dc04f31f..d252d1e52 100644 --- a/app/src/commonTest/java/org/tasks/TestUtilities.kt +++ b/app/src/commonTest/java/org/tasks/TestUtilities.kt @@ -5,6 +5,8 @@ import at.bitfire.ical4android.Task.Companion.tasksFromReader import com.todoroo.astrid.data.Task import kotlinx.coroutines.runBlocking import org.tasks.caldav.iCalendar.Companion.applyRemote +import org.tasks.caldav.iCalendar.Companion.reminders +import org.tasks.data.Alarm import org.tasks.data.CaldavTask import org.tasks.preferences.Preferences import org.tasks.time.DateTime @@ -44,6 +46,9 @@ object TestUtilities { return task } + val String.alarms: List + get() = fromResource(this).reminders + fun setup(path: String): Triple { val task = Task() val vtodo = readFile(path) diff --git a/app/src/main/java/com/todoroo/astrid/service/Upgrade_12_4.kt b/app/src/main/java/com/todoroo/astrid/service/Upgrade_12_4.kt new file mode 100644 index 000000000..9312fbb66 --- /dev/null +++ b/app/src/main/java/com/todoroo/astrid/service/Upgrade_12_4.kt @@ -0,0 +1,32 @@ +@file:Suppress("ClassName") + +package com.todoroo.astrid.service + +import org.tasks.caldav.iCalendar +import org.tasks.caldav.iCalendar.Companion.reminders +import org.tasks.data.AlarmDao +import org.tasks.data.TaskDao +import org.tasks.data.UpgraderDao +import javax.inject.Inject + +class Upgrade_12_4 @Inject constructor( + private val alarmDao: AlarmDao, + private val taskDao: TaskDao, + private val upgraderDao: UpgraderDao, +) { + internal suspend fun syncExistingAlarms() { + val existingAlarms = alarmDao.getActiveAlarms().map { it.task } + upgraderDao.tasksWithVtodos().forEach { caldav -> + val remoteTask = caldav.vtodo?.let(iCalendar::fromVtodo) ?: return@forEach + remoteTask + .reminders + .onEach { alarm -> alarm.task = caldav.id } + .let { alarms -> alarmDao.insert(alarms) } + } + taskDao.touch(existingAlarms) + } + + companion object { + const val VERSION = 120400 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/todoroo/astrid/service/Upgrader.kt b/app/src/main/java/com/todoroo/astrid/service/Upgrader.kt index 933256a4c..3d4b41d25 100644 --- a/app/src/main/java/com/todoroo/astrid/service/Upgrader.kt +++ b/app/src/main/java/com/todoroo/astrid/service/Upgrader.kt @@ -59,6 +59,7 @@ class Upgrader @Inject constructor( private val upgraderDao: UpgraderDao, private val upgrade_11_3: Lazy, private val upgrade_11_12_3: Lazy, + private val upgrade_12_4: Lazy, ) { fun upgrade(from: Int, to: Int) { @@ -95,13 +96,14 @@ class Upgrader @Inject constructor( } } run(from, Upgrade_11_12_3.VERSION) { - with(upgrade_11_12_3.get()) { - migrateDefaultReminderPreference() - } + upgrade_11_12_3.get().migrateDefaultReminderPreference() } run(from, V11_13) { preferences.setString(R.string.p_completion_ringtone, "") } + run(from, Upgrade_12_4.VERSION) { + upgrade_12_4.get().syncExistingAlarms() + } preferences.setBoolean(R.string.p_just_updated, true) } preferences.setCurrentVersion(to) diff --git a/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.kt b/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.kt index b898f2df8..186d5b960 100644 --- a/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.kt +++ b/app/src/main/java/org/tasks/caldav/CaldavSynchronizer.kt @@ -12,9 +12,17 @@ import at.bitfire.dav4jvm.exception.DavException import at.bitfire.dav4jvm.exception.HttpException import at.bitfire.dav4jvm.exception.ServiceUnavailableException import at.bitfire.dav4jvm.exception.UnauthorizedException -import at.bitfire.dav4jvm.property.* +import at.bitfire.dav4jvm.property.CalendarColor +import at.bitfire.dav4jvm.property.CalendarData +import at.bitfire.dav4jvm.property.CurrentUserPrincipal +import at.bitfire.dav4jvm.property.CurrentUserPrivilegeSet +import at.bitfire.dav4jvm.property.DisplayName +import at.bitfire.dav4jvm.property.GetCTag +import at.bitfire.dav4jvm.property.GetETag import at.bitfire.dav4jvm.property.GetETag.Companion.fromResponse +import at.bitfire.dav4jvm.property.SyncToken import at.bitfire.ical4android.ICalendar.Companion.prodId +import com.todoroo.astrid.alarms.AlarmService import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.data.Task import com.todoroo.astrid.helper.UUIDHelper @@ -32,18 +40,26 @@ import org.tasks.Strings.isNullOrEmpty import org.tasks.analytics.Firebase import org.tasks.billing.Inventory import org.tasks.caldav.iCalendar.Companion.fromVtodo -import org.tasks.caldav.property.* +import org.tasks.caldav.iCalendar.Companion.reminders +import org.tasks.caldav.property.Invite +import org.tasks.caldav.property.OCAccess +import org.tasks.caldav.property.OCInvite +import org.tasks.caldav.property.OCOwnerPrincipal +import org.tasks.caldav.property.OCUser import org.tasks.caldav.property.PropertyUtils.register +import org.tasks.caldav.property.ShareAccess import org.tasks.caldav.property.ShareAccess.Companion.READ import org.tasks.caldav.property.ShareAccess.Companion.READ_WRITE import org.tasks.caldav.property.ShareAccess.Companion.SHARED_OWNER -import org.tasks.data.* +import org.tasks.caldav.property.Sharee +import org.tasks.data.CaldavAccount import org.tasks.data.CaldavAccount.Companion.ERROR_UNAUTHORIZED import org.tasks.data.CaldavAccount.Companion.SERVER_OPEN_XCHANGE import org.tasks.data.CaldavAccount.Companion.SERVER_OWNCLOUD import org.tasks.data.CaldavAccount.Companion.SERVER_SABREDAV import org.tasks.data.CaldavAccount.Companion.SERVER_TASKS import org.tasks.data.CaldavAccount.Companion.SERVER_UNKNOWN +import org.tasks.data.CaldavCalendar import org.tasks.data.CaldavCalendar.Companion.ACCESS_OWNER import org.tasks.data.CaldavCalendar.Companion.ACCESS_READ_ONLY import org.tasks.data.CaldavCalendar.Companion.ACCESS_READ_WRITE @@ -53,6 +69,10 @@ import org.tasks.data.CaldavCalendar.Companion.INVITE_DECLINED import org.tasks.data.CaldavCalendar.Companion.INVITE_INVALID import org.tasks.data.CaldavCalendar.Companion.INVITE_NO_RESPONSE import org.tasks.data.CaldavCalendar.Companion.INVITE_UNKNOWN +import org.tasks.data.CaldavDao +import org.tasks.data.CaldavTask +import org.tasks.data.PrincipalAccess +import org.tasks.data.PrincipalDao import timber.log.Timber import java.io.IOException import java.net.ConnectException @@ -60,7 +80,6 @@ import java.net.SocketTimeoutException import java.net.UnknownHostException import java.security.KeyManagementException import java.security.NoSuchAlgorithmException -import java.util.* import javax.inject.Inject import javax.net.ssl.SSLException @@ -75,6 +94,7 @@ class CaldavSynchronizer @Inject constructor( private val provider: CaldavClientProvider, private val iCal: iCalendar, private val principalDao: PrincipalDao, + private val alarmService: AlarmService, ) { suspend fun sync(account: CaldavAccount) { Thread.currentThread().contextClassLoader = context.classLoader @@ -254,8 +274,11 @@ class CaldavSynchronizer @Inject constructor( Timber.e("Invalid VCALENDAR: %s", fileName) return } - val caldavTask = caldavDao.getTask(caldavCalendar.uuid!!, fileName) + var caldavTask = caldavDao.getTask(caldavCalendar.uuid!!, fileName) iCal.fromVtodo(caldavCalendar, caldavTask, remote, vtodo, fileName, eTag) + caldavTask = caldavTask ?: caldavDao.getTask(caldavCalendar.uuid!!, fileName) + ?: continue + alarmService.synchronizeAlarms(caldavTask.task, remote.reminders.toMutableSet()) } } caldavDao diff --git a/app/src/main/java/org/tasks/caldav/iCalendar.kt b/app/src/main/java/org/tasks/caldav/iCalendar.kt index 52befc099..2b239562b 100644 --- a/app/src/main/java/org/tasks/caldav/iCalendar.kt +++ b/app/src/main/java/org/tasks/caldav/iCalendar.kt @@ -3,6 +3,7 @@ package org.tasks.caldav import at.bitfire.ical4android.DateUtils.ical4jTimeZone import at.bitfire.ical4android.Task import at.bitfire.ical4android.Task.Companion.tasksFromReader +import at.bitfire.ical4android.util.TimeApiExtensions.toDuration import com.todoroo.andlib.utility.DateUtilities import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.data.Task.Companion.HIDE_UNTIL_SPECIFIC_DAY @@ -11,17 +12,44 @@ import com.todoroo.astrid.data.Task.Companion.URGENCY_SPECIFIC_DAY import com.todoroo.astrid.data.Task.Companion.URGENCY_SPECIFIC_DAY_TIME import com.todoroo.astrid.helper.UUIDHelper import com.todoroo.astrid.service.TaskCreator -import net.fortuna.ical4j.model.Date import net.fortuna.ical4j.model.DateTime import net.fortuna.ical4j.model.Parameter +import net.fortuna.ical4j.model.ParameterList import net.fortuna.ical4j.model.Property +import net.fortuna.ical4j.model.component.VAlarm import net.fortuna.ical4j.model.parameter.RelType -import net.fortuna.ical4j.model.property.* +import net.fortuna.ical4j.model.parameter.Related +import net.fortuna.ical4j.model.parameter.Related.* +import net.fortuna.ical4j.model.property.Action +import net.fortuna.ical4j.model.property.Completed +import net.fortuna.ical4j.model.property.DateProperty +import net.fortuna.ical4j.model.property.Description +import net.fortuna.ical4j.model.property.DtStart +import net.fortuna.ical4j.model.property.Due +import net.fortuna.ical4j.model.property.Geo +import net.fortuna.ical4j.model.property.RelatedTo +import net.fortuna.ical4j.model.property.Repeat +import net.fortuna.ical4j.model.property.Status +import net.fortuna.ical4j.model.property.Trigger +import net.fortuna.ical4j.model.property.XProperty import org.tasks.Strings.isNullOrEmpty import org.tasks.caldav.GeoUtils.equalish import org.tasks.caldav.GeoUtils.toGeo import org.tasks.caldav.GeoUtils.toLikeString -import org.tasks.data.* +import org.tasks.data.Alarm +import org.tasks.data.Alarm.Companion.TYPE_DATE_TIME +import org.tasks.data.Alarm.Companion.TYPE_REL_END +import org.tasks.data.Alarm.Companion.TYPE_REL_START +import org.tasks.data.AlarmDao +import org.tasks.data.CaldavCalendar +import org.tasks.data.CaldavDao +import org.tasks.data.CaldavTask +import org.tasks.data.Geofence +import org.tasks.data.LocationDao +import org.tasks.data.Place +import org.tasks.data.TagDao +import org.tasks.data.TagData +import org.tasks.data.TagDataDao import org.tasks.date.DateTimeUtils.newDateTime import org.tasks.date.DateTimeUtils.toDateTime import org.tasks.jobs.WorkManager @@ -37,6 +65,9 @@ import timber.log.Timber import java.io.ByteArrayOutputStream import java.io.StringReader import java.text.ParseException +import java.time.Duration +import java.time.Instant +import java.time.temporal.TemporalAmount import java.util.* import javax.inject.Inject import kotlin.math.max @@ -52,7 +83,9 @@ class iCalendar @Inject constructor( private val taskCreator: TaskCreator, private val tagDao: TagDao, private val taskDao: TaskDao, - private val caldavDao: CaldavDao) { + private val caldavDao: CaldavDao, + private val alarmDao: AlarmDao, +) { suspend fun setPlace(taskId: Long, geo: Geo?) { if (geo == null) { @@ -136,6 +169,42 @@ class iCalendar @Inject constructor( if (localGeo == null || !localGeo.equalish(remoteModel.geoPosition)) { remoteModel.geoPosition = localGeo } + remoteModel.alarms.removeAll(remoteModel.alarms.filtered) + remoteModel.alarms.addAll( + alarmDao + .getAlarms(task.id) + .mapNotNull { + val trigger = when (it.type) { + TYPE_DATE_TIME -> + Trigger(getDateTime(it.time)) + TYPE_REL_START, + TYPE_REL_END -> + Trigger( + ParameterList().apply { + add(if (it.type == TYPE_REL_END) END else START) + }, + Duration.ofMillis(it.time) + ) + else -> return@mapNotNull null + } + VAlarm().apply { + with(properties) { + add(trigger) + add(Action.DISPLAY) + add(Description("Default Tasks.org description")) + if (it.repeat > 0) { + add(Repeat(it.repeat)) + add( + net.fortuna.ical4j.model.property.Duration( + Duration.ofMillis(it.interval) + ) + ) + + } + } + } + } + ) } suspend fun fromVtodo( @@ -177,6 +246,8 @@ class iCalendar @Inject constructor( private const val MOZ_SNOOZE_TIME = "X-MOZ-SNOOZE-TIME" private const val MOZ_LASTACK = "X-MOZ-LASTACK" private const val HIDE_SUBTASKS = "1" + // VALARM extensions: https://datatracker.ietf.org/doc/html/rfc9074 + private val IGNORE_ALARM = DateTime("19760401T005545Z") private val IS_PARENT = { r: RelatedTo -> r.parameters.getParameter(Parameter.RELTYPE).let { it === RelType.PARENT || it == null || it.value.isNullOrBlank() @@ -210,8 +281,7 @@ class iCalendar @Inject constructor( } } - @JvmStatic - fun getLocal(property: DateProperty): Long = + private fun getLocal(property: DateProperty): Long = org.tasks.time.DateTime.from(property.date)?.toLocal()?.millis ?: 0 fun fromVtodo(vtodo: String): Task? { @@ -389,11 +459,41 @@ class iCalendar @Inject constructor( snooze = task.reminderSnooze } + val List.filtered: List + get() = + filter { it.action == Action.DISPLAY || it.action == Action.AUDIO } + .filterNot { it.trigger.dateTime == IGNORE_ALARM } + + val Task.reminders: List + get() = alarms.filtered.mapNotNull { + val (type, time) = when { + it.trigger.date != null -> + Pair(TYPE_DATE_TIME, getLocal(it.trigger)) + it.trigger.duration != null -> + Pair( + if (it.trigger.parameters.getParameter(RELATED) == END) { + TYPE_REL_END + } else { + TYPE_REL_START + }, + it.trigger.duration.toMillis() + ) + else -> return@mapNotNull null + } + Alarm(0L, time, type, it.repeat?.count ?: 0, it.duration?.toMillis() ?: 0) + } + private fun getDateTime(timestamp: Long): DateTime { val tz = ical4jTimeZone(TimeZone.getDefault().id) val dateTime = DateTime(if (tz != null) timestamp else org.tasks.time.DateTime(timestamp).toUTC().millis) dateTime.timeZone = tz return dateTime } + + private fun net.fortuna.ical4j.model.property.Duration.toMillis() = + duration.toMillis() + + private fun TemporalAmount.toMillis(): Long = + toDuration(Instant.EPOCH).toSeconds() * 1_000 } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/data/Alarm.kt b/app/src/main/java/org/tasks/data/Alarm.kt index b64734217..56d9fa97b 100644 --- a/app/src/main/java/org/tasks/data/Alarm.kt +++ b/app/src/main/java/org/tasks/data/Alarm.kt @@ -65,7 +65,8 @@ class Alarm : Parcelable { override fun describeContents() = 0 override fun toString(): String { - return "Alarm(id=$id, task=$task, time=${printTimestamp(time)}, type=$type, repeat=$repeat, interval=$interval)" + val timestamp = if (type == TYPE_DATE_TIME) printTimestamp(time) else time + return "Alarm(id=$id, task=$task, time=$timestamp, type=$type, repeat=$repeat, interval=$interval)" } override fun equals(other: Any?): Boolean { diff --git a/app/src/test/java/org/tasks/caldav/AppleRemindersTests.kt b/app/src/test/java/org/tasks/caldav/AppleRemindersTests.kt index 4433b312f..a7642b517 100644 --- a/app/src/test/java/org/tasks/caldav/AppleRemindersTests.kt +++ b/app/src/test/java/org/tasks/caldav/AppleRemindersTests.kt @@ -3,9 +3,12 @@ package org.tasks.caldav import com.todoroo.astrid.data.Task import org.junit.After import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test +import org.tasks.TestUtilities.alarms import org.tasks.TestUtilities.vtodo +import org.tasks.data.Alarm import org.tasks.time.DateTime import java.util.* @@ -77,4 +80,17 @@ class AppleRemindersTests { fun highPriority() { assertEquals(Task.Priority.HIGH, vtodo("apple/priority_high.txt").priority) } + + @Test + fun dateTimeReminder() { + assertEquals( + listOf(Alarm(0, 1642568400000)), + "apple/date_time_reminder.txt".alarms + ) + } + + @Test + fun ignoreLocationReminders() { + assertTrue("apple/geofence_arrival.txt".alarms.isEmpty()) + } } \ No newline at end of file diff --git a/app/src/test/java/org/tasks/caldav/ThunderbirdTests.kt b/app/src/test/java/org/tasks/caldav/ThunderbirdTests.kt index 71935dbd9..8a4772af1 100644 --- a/app/src/test/java/org/tasks/caldav/ThunderbirdTests.kt +++ b/app/src/test/java/org/tasks/caldav/ThunderbirdTests.kt @@ -6,11 +6,18 @@ import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Ignore import org.junit.Test +import org.tasks.TestUtilities.alarms import org.tasks.TestUtilities.setup import org.tasks.TestUtilities.vtodo import org.tasks.caldav.iCalendar.Companion.applyLocal +import org.tasks.data.Alarm +import org.tasks.data.Alarm.Companion.TYPE_DATE_TIME +import org.tasks.data.Alarm.Companion.TYPE_REL_END +import org.tasks.data.Alarm.Companion.TYPE_REL_START import org.tasks.time.DateTime import java.util.* +import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeUnit.MINUTES class ThunderbirdTests { private val defaultTimeZone = TimeZone.getDefault() @@ -110,6 +117,62 @@ class ThunderbirdTests { vtodo("thunderbird/start_date_time.txt").hideUntil) } + @Test + fun dateTimeReminder() { + assertEquals( + listOf(Alarm(task = 0, time = 1642791600000, type = TYPE_DATE_TIME)), + "thunderbird/date_time_reminder.txt".alarms + ) + } + + @Test + fun reminderBeforeStart() { + assertEquals( + listOf(Alarm(task = 0, time = -TimeUnit.HOURS.toMillis(1), type = TYPE_REL_START)), + "thunderbird/reminder_before_start.txt".alarms + ) + } + + @Test + fun reminderAfterStart() { + assertEquals( + listOf(Alarm(task = 0, time = MINUTES.toMillis(15), type = TYPE_REL_START)), + "thunderbird/reminder_after_start.txt".alarms + ) + } + + @Test + fun reminderBeforeEnd() { + assertEquals( + listOf(Alarm(task = 0, time = -MINUTES.toMillis(15), type = TYPE_REL_END)), + "thunderbird/reminder_before_end.txt".alarms + ) + } + + @Test + fun reminderAfterEnd() { + assertEquals( + listOf(Alarm(task = 0, time = MINUTES.toMillis(15), type = TYPE_REL_END)), + "thunderbird/reminder_after_end.txt".alarms + ) + } + + @Test + fun reminderAtStart() { + assertEquals( + listOf(Alarm(task = 0, time = 0, type = TYPE_REL_START)), + "thunderbird/reminder_at_start.txt".alarms + ) + } + + @Test + fun reminder50Days() { + assertEquals( + listOf(Alarm(task = 0, time = -TimeUnit.DAYS.toMillis(50), type = TYPE_REL_START)), + "thunderbird/reminder_50_days.txt".alarms + ) + } + @Test @Ignore fun dontCrashOnMultipleTasks() { diff --git a/app/src/test/resources/apple/date_time_reminder.txt b/app/src/test/resources/apple/date_time_reminder.txt new file mode 100644 index 000000000..33bea0ac7 --- /dev/null +++ b/app/src/test/resources/apple/date_time_reminder.txt @@ -0,0 +1,39 @@ +BEGIN:VCALENDAR +CALSCALE:GREGORIAN +PRODID:-//Apple Inc.//iOS 12.1//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:America/Chicago +BEGIN:DAYLIGHT +DTSTART:20070311T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +TZNAME:CDT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +END:DAYLIGHT +BEGIN:STANDARD +DTSTART:20071104T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +TZNAME:CST +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +END:STANDARD +END:VTIMEZONE +BEGIN:VTODO +CREATED:20220119T045003Z +DTSTAMP:20220119T045013Z +DTSTART;TZID=America/Chicago:20220118T230000 +DUE;TZID=America/Chicago:20220118T230000 +LAST-MODIFIED:20220119T045013Z +STATUS:NEEDS-ACTION +SUMMARY:Test +UID:A11D558C-3BC3-45FE-B95E-8B1F0023CB17 +BEGIN:VALARM +ACTION:DISPLAY +DESCRIPTION:Reminder +TRIGGER;VALUE=DATE-TIME:20220119T050000Z +UID:2CBB42D2-039E-4F6B-BFDF-761632A15305 +X-WR-ALARMUID:2CBB42D2-039E-4F6B-BFDF-761632A15305 +END:VALARM +END:VTODO +END:VCALENDAR diff --git a/app/src/test/resources/apple/geofence_arrival.txt b/app/src/test/resources/apple/geofence_arrival.txt new file mode 100644 index 000000000..44308ce9e --- /dev/null +++ b/app/src/test/resources/apple/geofence_arrival.txt @@ -0,0 +1,25 @@ +BEGIN:VCALENDAR +CALSCALE:GREGORIAN +PRODID:-//Apple Inc.//iOS 12.1//EN +VERSION:2.0 +BEGIN:VTODO +CREATED:20220119T050820Z +DTSTAMP:20220119T050843Z +LAST-MODIFIED:20220119T050843Z +STATUS:NEEDS-ACTION +SUMMARY:Test +UID:5F1A1AD2-6412-4F61-AE56-7D0EE8CE4CE5 +BEGIN:VALARM +ACTION:DISPLAY +DESCRIPTION:Reminder +TRIGGER;VALUE=DATE-TIME:19760401T005545Z +UID:9EF67428-7209-4EA4-9029-4049F577F4FF +X-APPLE-PROXIMITY:ARRIVE +X-APPLE-STRUCTURED-LOCATION;VALUE=URI;X-ADDRESS=1600 Amphitheatre Pkwy\\ + nMountain View CA 94043\\nUnited States;X-APPLE-RADIUS=141.1748408176572 + ;X-APPLE-REFERENCEFRAME=1;X-TITLE=Google Mountain View - Headquarters:ge + o:37.422338,-122.084370 +X-WR-ALARMUID:9EF67428-7209-4EA4-9029-4049F577F4FF +END:VALARM +END:VTODO +END:VCALENDAR diff --git a/app/src/test/resources/apple/geofence_departure.txt b/app/src/test/resources/apple/geofence_departure.txt new file mode 100644 index 000000000..c8be1148e --- /dev/null +++ b/app/src/test/resources/apple/geofence_departure.txt @@ -0,0 +1,25 @@ +BEGIN:VCALENDAR +CALSCALE:GREGORIAN +PRODID:-//Apple Inc.//iOS 12.1//EN +VERSION:2.0 +BEGIN:VTODO +CREATED:20220119T050930Z +DTSTAMP:20220119T050938Z +LAST-MODIFIED:20220119T050937Z +STATUS:NEEDS-ACTION +SUMMARY:Test +UID:E41BC37A-6261-4172-96F6-1B8FF9F74380 +BEGIN:VALARM +ACTION:DISPLAY +DESCRIPTION:Reminder +TRIGGER;VALUE=DATE-TIME:19760401T005545Z +UID:9E6BDF76-8FA4-4B22-AE14-FD2A43BC4D18 +X-APPLE-PROXIMITY:DEPART +X-APPLE-STRUCTURED-LOCATION;VALUE=URI;X-ADDRESS=1600 Amphitheatre Pkwy\\ + nMountain View CA 94043\\nUnited States;X-APPLE-RADIUS=141.1748408176572 + ;X-APPLE-REFERENCEFRAME=1;X-TITLE=Google Mountain View - Headquarters:ge + o:37.422338,-122.084370 +X-WR-ALARMUID:9E6BDF76-8FA4-4B22-AE14-FD2A43BC4D18 +END:VALARM +END:VTODO +END:VCALENDAR diff --git a/app/src/test/resources/thunderbird/date_time_reminder.txt b/app/src/test/resources/thunderbird/date_time_reminder.txt new file mode 100644 index 000000000..99622c273 --- /dev/null +++ b/app/src/test/resources/thunderbird/date_time_reminder.txt @@ -0,0 +1,17 @@ +BEGIN:VCALENDAR +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VTODO +CREATED:20220121T053659Z +LAST-MODIFIED:20220121T053753Z +DTSTAMP:20220121T053753Z +UID:e7cd55dc-9519-2a45-8630-4b632a4ca297 +SUMMARY:Test +PERCENT-COMPLETE:0 +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER;VALUE=DATE-TIME:20220121T190000Z +DESCRIPTION:Default Mozilla Description +END:VALARM +END:VTODO +END:VCALENDAR diff --git a/app/src/test/resources/thunderbird/reminder_50_days.txt b/app/src/test/resources/thunderbird/reminder_50_days.txt new file mode 100644 index 000000000..280cc459a --- /dev/null +++ b/app/src/test/resources/thunderbird/reminder_50_days.txt @@ -0,0 +1,35 @@ +BEGIN:VCALENDAR +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:America/Chicago +BEGIN:DAYLIGHT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +TZNAME:CDT +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +TZNAME:CST +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11 +END:STANDARD +END:VTIMEZONE +BEGIN:VTODO +CREATED:20220206T055100Z +LAST-MODIFIED:20220206T055130Z +DTSTAMP:20220206T055130Z +UID:796d08f4-dd49-4749-94ba-6ce2ff0aae22 +SUMMARY:Test +DTSTART;TZID=America/Chicago:20230201T230000 +PERCENT-COMPLETE:0 +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER;VALUE=DURATION:-P50D +DESCRIPTION:Default Mozilla Description +END:VALARM +END:VTODO +END:VCALENDAR diff --git a/app/src/test/resources/thunderbird/reminder_after_end.txt b/app/src/test/resources/thunderbird/reminder_after_end.txt new file mode 100644 index 000000000..a6709c375 --- /dev/null +++ b/app/src/test/resources/thunderbird/reminder_after_end.txt @@ -0,0 +1,37 @@ +BEGIN:VCALENDAR +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:America/Chicago +BEGIN:DAYLIGHT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +TZNAME:CDT +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +TZNAME:CST +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11 +END:STANDARD +END:VTIMEZONE +BEGIN:VTODO +CREATED:20220121T054934Z +LAST-MODIFIED:20220121T055843Z +DTSTAMP:20220121T055843Z +UID:5b3a42d5-e99c-2f47-87d5-8ba0e2b753ab +SUMMARY:Test +DUE;TZID=America/Chicago:20220121T130000 +PERCENT-COMPLETE:0 +X-MOZ-GENERATION:3 +SEQUENCE:1 +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER;VALUE=DURATION;RELATED=END:PT15M +DESCRIPTION:Default Mozilla Description +END:VALARM +END:VTODO +END:VCALENDAR diff --git a/app/src/test/resources/thunderbird/reminder_after_start.txt b/app/src/test/resources/thunderbird/reminder_after_start.txt new file mode 100644 index 000000000..00f515e77 --- /dev/null +++ b/app/src/test/resources/thunderbird/reminder_after_start.txt @@ -0,0 +1,37 @@ +BEGIN:VCALENDAR +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:America/Chicago +BEGIN:DAYLIGHT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +TZNAME:CDT +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +TZNAME:CST +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11 +END:STANDARD +END:VTIMEZONE +BEGIN:VTODO +CREATED:20220121T054934Z +LAST-MODIFIED:20220121T055642Z +DTSTAMP:20220121T055642Z +UID:5b3a42d5-e99c-2f47-87d5-8ba0e2b753ab +SUMMARY:Test +DTSTART;TZID=America/Chicago:20220121T130000 +DUE;TZID=America/Chicago:20220121T130000 +PERCENT-COMPLETE:0 +X-MOZ-GENERATION:1 +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER;VALUE=DURATION:PT15M +DESCRIPTION:Default Mozilla Description +END:VALARM +END:VTODO +END:VCALENDAR diff --git a/app/src/test/resources/thunderbird/reminder_at_start.txt b/app/src/test/resources/thunderbird/reminder_at_start.txt new file mode 100644 index 000000000..936947f46 --- /dev/null +++ b/app/src/test/resources/thunderbird/reminder_at_start.txt @@ -0,0 +1,35 @@ +BEGIN:VCALENDAR +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:America/Chicago +BEGIN:DAYLIGHT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +TZNAME:CDT +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +TZNAME:CST +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11 +END:STANDARD +END:VTIMEZONE +BEGIN:VTODO +CREATED:20220205T213726Z +LAST-MODIFIED:20220205T213750Z +DTSTAMP:20220205T213750Z +UID:7d5eca48-9faa-294c-8155-052a4e49f342 +SUMMARY:Test +DTSTART;TZID=America/Chicago:20220205T160000 +PERCENT-COMPLETE:0 +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER;VALUE=DURATION:PT0S +DESCRIPTION:Default Mozilla Description +END:VALARM +END:VTODO +END:VCALENDAR diff --git a/app/src/test/resources/thunderbird/reminder_before_end.txt b/app/src/test/resources/thunderbird/reminder_before_end.txt new file mode 100644 index 000000000..a0875fd4b --- /dev/null +++ b/app/src/test/resources/thunderbird/reminder_before_end.txt @@ -0,0 +1,37 @@ +BEGIN:VCALENDAR +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:America/Chicago +BEGIN:DAYLIGHT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +TZNAME:CDT +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +TZNAME:CST +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11 +END:STANDARD +END:VTIMEZONE +BEGIN:VTODO +CREATED:20220121T054934Z +LAST-MODIFIED:20220121T055751Z +DTSTAMP:20220121T055751Z +UID:5b3a42d5-e99c-2f47-87d5-8ba0e2b753ab +SUMMARY:Test +DUE;TZID=America/Chicago:20220121T130000 +PERCENT-COMPLETE:0 +X-MOZ-GENERATION:2 +SEQUENCE:1 +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER;VALUE=DURATION;RELATED=END:-PT15M +DESCRIPTION:Default Mozilla Description +END:VALARM +END:VTODO +END:VCALENDAR diff --git a/app/src/test/resources/thunderbird/reminder_before_start.txt b/app/src/test/resources/thunderbird/reminder_before_start.txt new file mode 100644 index 000000000..1f4ca2a23 --- /dev/null +++ b/app/src/test/resources/thunderbird/reminder_before_start.txt @@ -0,0 +1,36 @@ +BEGIN:VCALENDAR +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:America/Chicago +BEGIN:DAYLIGHT +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +TZNAME:CDT +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +TZNAME:CST +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11 +END:STANDARD +END:VTIMEZONE +BEGIN:VTODO +CREATED:20220121T054934Z +LAST-MODIFIED:20220121T054956Z +DTSTAMP:20220121T054956Z +UID:5b3a42d5-e99c-2f47-87d5-8ba0e2b753ab +SUMMARY:Test +DTSTART;TZID=America/Chicago:20220121T130000 +DUE;TZID=America/Chicago:20220121T130000 +PERCENT-COMPLETE:0 +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER;VALUE=DURATION:-PT1H +DESCRIPTION:Default Mozilla Description +END:VALARM +END:VTODO +END:VCALENDAR