mirror of
https://github.com/etesync/android
synced 2024-11-22 16:08:13 +00:00
Convert RDate/ExDate properties <-> Android RDATE/EXDATE strings more precisely (+ tests)
This commit is contained in:
parent
26d9f7284a
commit
5b7947034a
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2013 – 2015 Ricki Hirner (bitfire web engineering).
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the GNU Public License v3.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.gnu.org/licenses/gpl.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
package at.bitfire.davdroid;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import net.fortuna.ical4j.model.Date;
|
||||||
|
import net.fortuna.ical4j.model.DateList;
|
||||||
|
import net.fortuna.ical4j.model.TimeZone;
|
||||||
|
import net.fortuna.ical4j.model.TimeZoneRegistry;
|
||||||
|
import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
|
||||||
|
import net.fortuna.ical4j.model.parameter.Value;
|
||||||
|
import net.fortuna.ical4j.model.property.DateListProperty;
|
||||||
|
import net.fortuna.ical4j.model.property.ExDate;
|
||||||
|
import net.fortuna.ical4j.model.property.RDate;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DateUtilsTest extends TestCase {
|
||||||
|
private static final String tzIdVienna = "Europe/Vienna";
|
||||||
|
|
||||||
|
public void testRecurrenceSetsToAndroidString() throws ParseException {
|
||||||
|
// one entry without time zone (implicitly UTC)
|
||||||
|
final List<RDate> list = new ArrayList<>(2);
|
||||||
|
list.add(new RDate(new DateList("20150101T103010Z,20150102T103020Z", Value.DATE_TIME)));
|
||||||
|
assertEquals("20150101T103010Z,20150102T103020Z", DateUtils.recurrenceSetsToAndroidString(list, false));
|
||||||
|
|
||||||
|
// two entries (previous one + this), both with time zone Vienna
|
||||||
|
list.add(new RDate(new DateList("20150103T113030,20150704T123040", Value.DATE_TIME)));
|
||||||
|
final TimeZone tz = DateUtils.tzRegistry.getTimeZone(tzIdVienna);
|
||||||
|
for (RDate rdate : list)
|
||||||
|
rdate.setTimeZone(tz);
|
||||||
|
assertEquals("20150101T103010Z,20150102T103020Z,20150103T103030Z,20150704T103040Z", DateUtils.recurrenceSetsToAndroidString(list, false));
|
||||||
|
|
||||||
|
// DATEs (without time) have to be converted to <date>T000000Z for Android
|
||||||
|
list.clear();
|
||||||
|
list.add(new RDate(new DateList("20150101,20150702", Value.DATE)));
|
||||||
|
assertEquals("20150101T000000Z,20150702T000000Z", DateUtils.recurrenceSetsToAndroidString(list, true));
|
||||||
|
|
||||||
|
// DATE-TIME (floating time or UTC) recurrences for all-day events have to converted to <date>T000000Z for Android
|
||||||
|
list.clear();
|
||||||
|
list.add(new RDate(new DateList("20150101T000000,20150702T000000Z", Value.DATE_TIME)));
|
||||||
|
assertEquals("20150101T000000Z,20150702T000000Z", DateUtils.recurrenceSetsToAndroidString(list, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAndroidStringToRecurrenceSets() throws ParseException {
|
||||||
|
// list of UTC times
|
||||||
|
ExDate exDate = (ExDate)DateUtils.androidStringToRecurrenceSet("20150101T103010Z,20150702T103020Z", ExDate.class, false);
|
||||||
|
DateList exDates = exDate.getDates();
|
||||||
|
assertEquals(Value.DATE_TIME, exDates.getType());
|
||||||
|
assertTrue(exDates.isUtc());
|
||||||
|
assertEquals(2, exDates.size());
|
||||||
|
assertEquals(1420108210000L, exDates.get(0).getTime());
|
||||||
|
assertEquals(1435833020000L, exDates.get(1).getTime());
|
||||||
|
|
||||||
|
// list of time zone times
|
||||||
|
exDate = (ExDate)DateUtils.androidStringToRecurrenceSet(tzIdVienna + ";20150101T103010,20150702T103020", ExDate.class, false);
|
||||||
|
exDates = exDate.getDates();
|
||||||
|
assertEquals(Value.DATE_TIME, exDates.getType());
|
||||||
|
assertEquals(DateUtils.tzRegistry.getTimeZone(tzIdVienna), exDates.getTimeZone());
|
||||||
|
assertEquals(2, exDates.size());
|
||||||
|
assertEquals(1420104610000L, exDates.get(0).getTime());
|
||||||
|
assertEquals(1435825820000L, exDates.get(1).getTime());
|
||||||
|
|
||||||
|
// list of dates
|
||||||
|
exDate = (ExDate)DateUtils.androidStringToRecurrenceSet("20150101T103010Z,20150702T103020Z", ExDate.class, true);
|
||||||
|
exDates = exDate.getDates();
|
||||||
|
assertEquals(Value.DATE, exDates.getType());
|
||||||
|
assertEquals(2, exDates.size());
|
||||||
|
assertEquals("20150101", exDates.get(0).toString());
|
||||||
|
assertEquals("20150702", exDates.get(1).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -30,17 +30,22 @@ import net.fortuna.ical4j.model.Dur;
|
|||||||
import net.fortuna.ical4j.model.TimeZone;
|
import net.fortuna.ical4j.model.TimeZone;
|
||||||
import net.fortuna.ical4j.model.TimeZoneRegistry;
|
import net.fortuna.ical4j.model.TimeZoneRegistry;
|
||||||
import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
|
import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
|
||||||
|
import net.fortuna.ical4j.model.ValidationException;
|
||||||
import net.fortuna.ical4j.model.component.VAlarm;
|
import net.fortuna.ical4j.model.component.VAlarm;
|
||||||
import net.fortuna.ical4j.model.parameter.Value;
|
import net.fortuna.ical4j.model.parameter.Value;
|
||||||
|
import net.fortuna.ical4j.model.property.DateListProperty;
|
||||||
import net.fortuna.ical4j.model.property.DtEnd;
|
import net.fortuna.ical4j.model.property.DtEnd;
|
||||||
import net.fortuna.ical4j.model.property.DtStart;
|
import net.fortuna.ical4j.model.property.DtStart;
|
||||||
|
import net.fortuna.ical4j.model.property.ExDate;
|
||||||
import net.fortuna.ical4j.model.property.RDate;
|
import net.fortuna.ical4j.model.property.RDate;
|
||||||
|
import net.fortuna.ical4j.util.Dates;
|
||||||
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import at.bitfire.davdroid.DateUtils;
|
||||||
import lombok.Cleanup;
|
import lombok.Cleanup;
|
||||||
|
|
||||||
public class LocalCalendarTest extends InstrumentationTestCase {
|
public class LocalCalendarTest extends InstrumentationTestCase {
|
||||||
@ -133,8 +138,7 @@ public class LocalCalendarTest extends InstrumentationTestCase {
|
|||||||
public void testBuildEntry() throws LocalStorageException, ParseException {
|
public void testBuildEntry() throws LocalStorageException, ParseException {
|
||||||
final String vcardName = "testBuildEntry";
|
final String vcardName = "testBuildEntry";
|
||||||
|
|
||||||
TimeZoneRegistry tzRegistry = TimeZoneRegistryFactory.getInstance().createRegistry();
|
final TimeZone tzVienna = DateUtils.tzRegistry.getTimeZone("Europe/Vienna");
|
||||||
TimeZone tzVienna = tzRegistry.getTimeZone("Europe/Vienna");
|
|
||||||
assertNotNull(tzVienna);
|
assertNotNull(tzVienna);
|
||||||
|
|
||||||
// build and write event to calendar provider
|
// build and write event to calendar provider
|
||||||
@ -231,23 +235,4 @@ public class LocalCalendarTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRecurrenceSetsToAndroidString() throws ParseException {
|
|
||||||
final String tzId = "Europe/Vienna";
|
|
||||||
|
|
||||||
// one entry without time zone
|
|
||||||
final List<RDate> list = new ArrayList<>(2);
|
|
||||||
list.add(new RDate(new DateList("20150101T103000,20150102T103000", Value.DATE_TIME)));
|
|
||||||
assertEquals("20150101T103000,20150102T103000", LocalCalendar.recurrenceSetsToAndroidString(list));
|
|
||||||
|
|
||||||
// two entries with time zone
|
|
||||||
list.add(new RDate(new DateList("20150103T103000,20150104T103000", Value.DATE_TIME)));
|
|
||||||
|
|
||||||
final TimeZoneRegistry tzRegistry = TimeZoneRegistryFactory.getInstance().createRegistry();
|
|
||||||
final TimeZone tz = tzRegistry.getTimeZone(tzId);
|
|
||||||
for (RDate rdate : list)
|
|
||||||
rdate.setTimeZone(tz);
|
|
||||||
|
|
||||||
assertEquals(tzId + ";20150101T103000,20150102T103000,20150103T103000,20150104T103000", LocalCalendar.recurrenceSetsToAndroidString(list));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,19 +11,43 @@ package at.bitfire.davdroid;
|
|||||||
import android.text.format.Time;
|
import android.text.format.Time;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import net.fortuna.ical4j.model.Date;
|
||||||
|
import net.fortuna.ical4j.model.DateList;
|
||||||
|
import net.fortuna.ical4j.model.DateTime;
|
||||||
import net.fortuna.ical4j.model.DefaultTimeZoneRegistryFactory;
|
import net.fortuna.ical4j.model.DefaultTimeZoneRegistryFactory;
|
||||||
import net.fortuna.ical4j.model.TimeZone;
|
import net.fortuna.ical4j.model.TimeZone;
|
||||||
import net.fortuna.ical4j.model.TimeZoneRegistry;
|
import net.fortuna.ical4j.model.TimeZoneRegistry;
|
||||||
|
import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
|
||||||
|
import net.fortuna.ical4j.model.parameter.Value;
|
||||||
|
import net.fortuna.ical4j.model.property.DateListProperty;
|
||||||
|
import net.fortuna.ical4j.model.property.ExDate;
|
||||||
|
import net.fortuna.ical4j.model.property.RDate;
|
||||||
|
import net.fortuna.ical4j.util.TimeZones;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.SimpleTimeZone;
|
import java.util.SimpleTimeZone;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
public class DateUtils {
|
public class DateUtils {
|
||||||
private final static String TAG = "davdroid.DateUtils";
|
private final static String TAG = "davdroid.DateUtils";
|
||||||
|
|
||||||
private final static TimeZoneRegistry tzRegistry = new DefaultTimeZoneRegistryFactory().createRegistry();
|
public final static TimeZoneRegistry tzRegistry = TimeZoneRegistryFactory.getInstance().createRegistry();
|
||||||
|
|
||||||
|
static {
|
||||||
|
// disable automatic time-zone updates (causes unwanted network traffic)
|
||||||
|
System.setProperty("net.fortuna.ical4j.timezone.update.enabled", "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// time zones
|
||||||
|
|
||||||
public static String findAndroidTimezoneID(String tzID) {
|
public static String findAndroidTimezoneID(String tzID) {
|
||||||
String localTZ = null;
|
String localTZ = null;
|
||||||
@ -56,8 +80,95 @@ public class DateUtils {
|
|||||||
return localTZ;
|
return localTZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TimeZone getTimeZone(String tzID) {
|
|
||||||
return tzRegistry.getTimeZone(tzID);
|
// recurrence sets
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concatenates, if necessary, multiple RDATE/EXDATE lists and converts them to
|
||||||
|
* a formatted string which Android calendar provider can process.
|
||||||
|
* Android expects this format: "[TZID;]date1,date2,date3" where date is "yyyymmddThhmmss" (when
|
||||||
|
* TZID is given) or "yyyymmddThhmmssZ". We don't use the TZID format here because then we're limited
|
||||||
|
* to one time-zone, while an iCalendar may contain multiple EXDATE/RDATE lines with different time zones.
|
||||||
|
* @param dates one more more lists of RDATE or EXDATE
|
||||||
|
* @param allDay indicates whether the event is an all-day event or not
|
||||||
|
* @return formatted string for Android calendar provider:
|
||||||
|
* - in case of all-day events, all dates/times are returned as yyyymmddT000000Z
|
||||||
|
* - in case of timed events, all dates/times are returned as UTC time: yyyymmddThhmmssZ
|
||||||
|
*/
|
||||||
|
public static String recurrenceSetsToAndroidString(List<? extends DateListProperty> dates, boolean allDay) throws ParseException {
|
||||||
|
List<String> strDates = new LinkedList<>();
|
||||||
|
|
||||||
|
/* rdate/exdate: DATE DATE_TIME
|
||||||
|
all-day store as ...T000000Z cut off time and store as ...T000000Z
|
||||||
|
event with time (ignored) store as ...ThhmmssZ
|
||||||
|
*/
|
||||||
|
final DateFormat dateFormatUtcMidnight = new SimpleDateFormat("yyyyMMdd'T'000000'Z'");
|
||||||
|
|
||||||
|
for (DateListProperty dateListProp : dates) {
|
||||||
|
final Value type = dateListProp.getDates().getType();
|
||||||
|
|
||||||
|
if (Value.DATE_TIME.equals(type)) { // DATE-TIME values will be stored in UTC format for Android
|
||||||
|
if (allDay) {
|
||||||
|
DateList dateList = dateListProp.getDates();
|
||||||
|
for (Date date : dateList)
|
||||||
|
strDates.add(dateFormatUtcMidnight.format(date));
|
||||||
|
} else {
|
||||||
|
dateListProp.setUtc(true);
|
||||||
|
strDates.add(dateListProp.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (Value.DATE.equals(type)) // DATE values have to be converted to DATE-TIME <date>T000000Z for Android
|
||||||
|
for (Date date : dateListProp.getDates())
|
||||||
|
strDates.add(dateFormatUtcMidnight.format(date));
|
||||||
|
}
|
||||||
|
return StringUtils.join(strDates, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a formatted string as provided by the Android calendar provider and returns a DateListProperty
|
||||||
|
* constructed from these values.
|
||||||
|
* @param dbStr formatted string from Android calendar provider (RDATE/EXDATE field)
|
||||||
|
* expected format: "[TZID;]date1,date2,date3" where date is "yyyymmddThhmmss[Z]"
|
||||||
|
* @param type subclass of DateListProperty, e.g. RDate or ExDate
|
||||||
|
* @param allDay true: list will contain DATE values; false: list will contain DATE_TIME values
|
||||||
|
* @return instance of "type" containing the parsed dates/times from the string
|
||||||
|
*/
|
||||||
|
public static DateListProperty androidStringToRecurrenceSet(String dbStr, Class<? extends DateListProperty> type, boolean allDay) throws ParseException {
|
||||||
|
// 1. split string into time zone and actual dates
|
||||||
|
TimeZone timeZone;
|
||||||
|
String datesStr;
|
||||||
|
final int limiter = dbStr.indexOf(';');
|
||||||
|
if (limiter != -1) { // TZID given
|
||||||
|
timeZone = DateUtils.tzRegistry.getTimeZone(dbStr.substring(0, limiter));
|
||||||
|
datesStr = dbStr.substring(limiter + 1);
|
||||||
|
} else {
|
||||||
|
timeZone = null;
|
||||||
|
datesStr = dbStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. process date string and generate list of DATEs or DATE-TIMEs
|
||||||
|
DateList dateList;
|
||||||
|
if (allDay) {
|
||||||
|
dateList = new DateList(Value.DATE);
|
||||||
|
for (String s: StringUtils.split(datesStr, ','))
|
||||||
|
dateList.add(new Date(new DateTime(s)));
|
||||||
|
} else {
|
||||||
|
dateList = new DateList(datesStr, Value.DATE_TIME, timeZone);
|
||||||
|
if (timeZone == null)
|
||||||
|
dateList.setUtc(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. generate requested DateListProperty (RDate/ExDate) from list of DATEs or DATE-TIMEs
|
||||||
|
DateListProperty list;
|
||||||
|
try {
|
||||||
|
list = (DateListProperty)type.getDeclaredConstructor(new Class[] { DateList.class } ).newInstance(dateList);
|
||||||
|
if (dateList.getTimeZone() != null)
|
||||||
|
list.setTimeZone(dateList.getTimeZone());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ParseException("Couldn't create date/time list by reflection", -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -321,7 +321,7 @@ public class Event extends iCalendar {
|
|||||||
dtStart = new DtStart(new Date(tsStart));
|
dtStart = new DtStart(new Date(tsStart));
|
||||||
} else {
|
} else {
|
||||||
DateTime start = new DateTime(tsStart);
|
DateTime start = new DateTime(tsStart);
|
||||||
start.setTimeZone(tzRegistry.getTimeZone(tzID));
|
start.setTimeZone(DateUtils.tzRegistry.getTimeZone(tzID));
|
||||||
dtStart = new DtStart(start);
|
dtStart = new DtStart(start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -340,7 +340,7 @@ public class Event extends iCalendar {
|
|||||||
dtEnd = new DtEnd(new Date(tsEnd));
|
dtEnd = new DtEnd(new Date(tsEnd));
|
||||||
} else {
|
} else {
|
||||||
DateTime end = new DateTime(tsEnd);
|
DateTime end = new DateTime(tsEnd);
|
||||||
end.setTimeZone(tzRegistry.getTimeZone(tzID));
|
end.setTimeZone(DateUtils.tzRegistry.getTimeZone(tzID));
|
||||||
dtEnd = new DtEnd(end);
|
dtEnd = new DtEnd(end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ import net.fortuna.ical4j.model.parameter.Cn;
|
|||||||
import net.fortuna.ical4j.model.parameter.CuType;
|
import net.fortuna.ical4j.model.parameter.CuType;
|
||||||
import net.fortuna.ical4j.model.parameter.PartStat;
|
import net.fortuna.ical4j.model.parameter.PartStat;
|
||||||
import net.fortuna.ical4j.model.parameter.Role;
|
import net.fortuna.ical4j.model.parameter.Role;
|
||||||
|
import net.fortuna.ical4j.model.parameter.Value;
|
||||||
import net.fortuna.ical4j.model.property.Action;
|
import net.fortuna.ical4j.model.property.Action;
|
||||||
import net.fortuna.ical4j.model.property.Attendee;
|
import net.fortuna.ical4j.model.property.Attendee;
|
||||||
import net.fortuna.ical4j.model.property.DateListProperty;
|
import net.fortuna.ical4j.model.property.DateListProperty;
|
||||||
@ -61,6 +62,7 @@ import java.net.URISyntaxException;
|
|||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import at.bitfire.davdroid.DAVUtils;
|
import at.bitfire.davdroid.DAVUtils;
|
||||||
import at.bitfire.davdroid.DateUtils;
|
import at.bitfire.davdroid.DateUtils;
|
||||||
@ -239,7 +241,7 @@ public class LocalCalendar extends LocalCollection<Event> {
|
|||||||
" AND " + Events.ORIGINAL_SYNC_ID + " NOT IN (" + StringUtils.join(sqlFileNames, ",") + ")"; // retain by remote file name
|
" AND " + Events.ORIGINAL_SYNC_ID + " NOT IN (" + StringUtils.join(sqlFileNames, ",") + ")"; // retain by remote file name
|
||||||
pendingOperations.add(ContentProviderOperation
|
pendingOperations.add(ContentProviderOperation
|
||||||
.newDelete(entriesURI())
|
.newDelete(entriesURI())
|
||||||
.withSelection(where, new String[]{ String.valueOf(id) })
|
.withSelection(where, new String[]{String.valueOf(id)})
|
||||||
.withYieldAllowed(true)
|
.withYieldAllowed(true)
|
||||||
.build()
|
.build()
|
||||||
);
|
);
|
||||||
@ -349,8 +351,7 @@ public class LocalCalendar extends LocalCollection<Event> {
|
|||||||
|
|
||||||
String strRDate = values.getAsString(Events.RDATE);
|
String strRDate = values.getAsString(Events.RDATE);
|
||||||
if (!StringUtils.isEmpty(strRDate)) {
|
if (!StringUtils.isEmpty(strRDate)) {
|
||||||
RDate rDate = new RDate();
|
RDate rDate = (RDate)DateUtils.androidStringToRecurrenceSet(strRDate, RDate.class, allDay);
|
||||||
rDate.setValue(strRDate);
|
|
||||||
e.getRdates().add(rDate);
|
e.getRdates().add(rDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,9 +364,7 @@ public class LocalCalendar extends LocalCollection<Event> {
|
|||||||
|
|
||||||
String strExDate = values.getAsString(Events.EXDATE);
|
String strExDate = values.getAsString(Events.EXDATE);
|
||||||
if (!StringUtils.isEmpty(strExDate)) {
|
if (!StringUtils.isEmpty(strExDate)) {
|
||||||
// always empty, see https://code.google.com/p/android/issues/detail?id=172411
|
ExDate exDate = (ExDate)DateUtils.androidStringToRecurrenceSet(strExDate, ExDate.class, allDay);
|
||||||
ExDate exDate = new ExDate();
|
|
||||||
exDate.setValue(strExDate);
|
|
||||||
e.getExdates().add(exDate);
|
e.getExdates().add(exDate);
|
||||||
}
|
}
|
||||||
} catch (ParseException ex) {
|
} catch (ParseException ex) {
|
||||||
@ -540,12 +539,20 @@ public class LocalCalendar extends LocalCollection<Event> {
|
|||||||
}
|
}
|
||||||
if (!event.getRdates().isEmpty()) {
|
if (!event.getRdates().isEmpty()) {
|
||||||
recurring = true;
|
recurring = true;
|
||||||
builder.withValue(Events.RDATE, recurrenceSetsToAndroidString(event.getRdates()));
|
try {
|
||||||
|
builder.withValue(Events.RDATE, DateUtils.recurrenceSetsToAndroidString(event.getRdates(), event.isAllDay()));
|
||||||
|
} catch (ParseException e) {
|
||||||
|
Log.e(TAG, "Couldn't parse RDate(s)", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (event.getExrule() != null)
|
if (event.getExrule() != null)
|
||||||
builder.withValue(Events.EXRULE, event.getExrule().getValue());
|
builder.withValue(Events.EXRULE, event.getExrule().getValue());
|
||||||
if (!event.getExdates().isEmpty())
|
if (!event.getExdates().isEmpty())
|
||||||
builder.withValue(Events.EXDATE, recurrenceSetsToAndroidString(event.getExdates()));
|
try {
|
||||||
|
builder.withValue(Events.EXDATE, DateUtils.recurrenceSetsToAndroidString(event.getExdates(), event.isAllDay()));
|
||||||
|
} catch (ParseException e) {
|
||||||
|
Log.e(TAG, "Couldn't parse ExDate(s)", e);
|
||||||
|
}
|
||||||
|
|
||||||
// set either DTEND for single-time events or DURATION for recurring events
|
// set either DTEND for single-time events or DURATION for recurring events
|
||||||
// because that's the way Android likes it (see docs)
|
// because that's the way Android likes it (see docs)
|
||||||
@ -613,10 +620,10 @@ public class LocalCalendar extends LocalCollection<Event> {
|
|||||||
.withSelection(Events.ORIGINAL_ID + "=?", new String[] { String.valueOf(event.getLocalID())}).build());
|
.withSelection(Events.ORIGINAL_ID + "=?", new String[] { String.valueOf(event.getLocalID())}).build());
|
||||||
// delete attendees
|
// delete attendees
|
||||||
pendingOperations.add(ContentProviderOperation.newDelete(syncAdapterURI(Attendees.CONTENT_URI))
|
pendingOperations.add(ContentProviderOperation.newDelete(syncAdapterURI(Attendees.CONTENT_URI))
|
||||||
.withSelection(Attendees.EVENT_ID + "=?", new String[] { String.valueOf(event.getLocalID()) }).build());
|
.withSelection(Attendees.EVENT_ID + "=?", new String[]{String.valueOf(event.getLocalID())}).build());
|
||||||
// delete reminders
|
// delete reminders
|
||||||
pendingOperations.add(ContentProviderOperation.newDelete(syncAdapterURI(Reminders.CONTENT_URI))
|
pendingOperations.add(ContentProviderOperation.newDelete(syncAdapterURI(Reminders.CONTENT_URI))
|
||||||
.withSelection(Reminders.EVENT_ID + "=?", new String[] { String.valueOf(event.getLocalID()) }).build());
|
.withSelection(Reminders.EVENT_ID + "=?", new String[]{String.valueOf(event.getLocalID())}).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -731,34 +738,4 @@ public class LocalCalendar extends LocalCollection<Event> {
|
|||||||
return calendarsURI(account);
|
return calendarsURI(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Concatenates, if necessary, multiple RDATE/EXDATE lists and prepares
|
|
||||||
* a formatted string as expected by Android calendar provider
|
|
||||||
* @param dates one more more lists of RDATE or EXDATE
|
|
||||||
* @return formatted string for Android calendar provider
|
|
||||||
*/
|
|
||||||
static String recurrenceSetsToAndroidString(List<? extends DateListProperty> dates) {
|
|
||||||
String tzID = null;
|
|
||||||
List<String> strDates = new LinkedList<>();
|
|
||||||
|
|
||||||
for (DateListProperty dateList : dates) {
|
|
||||||
if (dateList.getTimeZone() != null) {
|
|
||||||
String thisTzID = DateUtils.findAndroidTimezoneID(dateList.getTimeZone().getID());
|
|
||||||
if (tzID == null)
|
|
||||||
tzID = thisTzID;
|
|
||||||
else if (!tzID.equals(thisTzID))
|
|
||||||
Log.w(TAG, "Multiple EXDATEs/RDATEs with different time zones not supported by Android, using " + tzID + " for all dates");
|
|
||||||
}
|
|
||||||
strDates.add(dateList.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Android expects this format: "[TZID;]date1,date2,date3"
|
|
||||||
String dateStr = "";
|
|
||||||
if (tzID != null)
|
|
||||||
dateStr += tzID + ";";
|
|
||||||
dateStr += StringUtils.join(strDates, ",");
|
|
||||||
|
|
||||||
return dateStr;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -204,7 +204,7 @@ public class LocalTaskList extends LocalCollection<Task> {
|
|||||||
|
|
||||||
TimeZone tz = null;
|
TimeZone tz = null;
|
||||||
if (!cursor.isNull(8))
|
if (!cursor.isNull(8))
|
||||||
tz = DateUtils.getTimeZone(cursor.getString(8));
|
tz = DateUtils.tzRegistry.getTimeZone(cursor.getString(8));
|
||||||
|
|
||||||
if (!cursor.isNull(9) && !cursor.isNull(10)) {
|
if (!cursor.isNull(9) && !cursor.isNull(10)) {
|
||||||
long ts = cursor.getLong(9);
|
long ts = cursor.getLong(9);
|
||||||
@ -327,7 +327,7 @@ public class LocalTaskList extends LocalCollection<Task> {
|
|||||||
|
|
||||||
// TZ *must* be provided when DTSTART or DUE is set
|
// TZ *must* be provided when DTSTART or DUE is set
|
||||||
if ((task.getDtStart() != null || task.getDue() != null) && tz == null)
|
if ((task.getDtStart() != null || task.getDue() != null) && tz == null)
|
||||||
tz = DateUtils.getTimeZone(TimeZones.GMT_ID);
|
tz = DateUtils.tzRegistry.getTimeZone(TimeZones.GMT_ID);
|
||||||
if (tz != null)
|
if (tz != null)
|
||||||
builder.withValue(TaskContract.Tasks.TZ, DateUtils.findAndroidTimezoneID(tz.getID()));
|
builder.withValue(TaskContract.Tasks.TZ, DateUtils.findAndroidTimezoneID(tz.getID()));
|
||||||
|
|
||||||
|
@ -40,13 +40,8 @@ public abstract class iCalendar extends Resource {
|
|||||||
CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_UNFOLDING, true);
|
CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_UNFOLDING, true);
|
||||||
CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING, true);
|
CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING, true);
|
||||||
CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_OUTLOOK_COMPATIBILITY, true);
|
CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_OUTLOOK_COMPATIBILITY, true);
|
||||||
|
|
||||||
// disable automatic time-zone updates (causes unwanted network traffic)
|
|
||||||
System.setProperty("net.fortuna.ical4j.timezone.update.enabled", "false");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static protected final TimeZoneRegistry tzRegistry = new DefaultTimeZoneRegistryFactory().createRegistry();
|
|
||||||
|
|
||||||
|
|
||||||
public iCalendar(long localID, String name, String ETag) {
|
public iCalendar(long localID, String name, String ETag) {
|
||||||
super(localID, name, ETag);
|
super(localID, name, ETag);
|
||||||
@ -103,7 +98,7 @@ public abstract class iCalendar extends Resource {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
String localTZ = DateUtils.findAndroidTimezoneID(tzID);
|
String localTZ = DateUtils.findAndroidTimezoneID(tzID);
|
||||||
date.setTimeZone(tzRegistry.getTimeZone(localTZ));
|
date.setTimeZone(DateUtils.tzRegistry.getTimeZone(localTZ));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String TimezoneDefToTzId(String timezoneDef) throws IllegalArgumentException {
|
public static String TimezoneDefToTzId(String timezoneDef) throws IllegalArgumentException {
|
||||||
|
Loading…
Reference in New Issue
Block a user