Commit fe0b1bf5 authored by Ricki Hirner's avatar Ricki Hirner 🐑

Better handling of events without dtend/duration

parent 6198f476
Pipeline #4888218 passed with stage
in 7 minutes and 17 seconds
......@@ -15,6 +15,7 @@ import android.Manifest;
import android.accounts.Account;
import android.content.ContentProviderClient;
import android.content.ContentUris;
import android.content.ContentValues;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.CalendarContract;
......@@ -271,4 +272,16 @@ public class AndroidEventTest extends InstrumentationTestCase {
assertTrue(event2.isAllDay());
}
public void testPopulateEventWithoutDuration() throws RemoteException, FileNotFoundException, CalendarStorageException {
ContentValues values = new ContentValues();
values.put(CalendarContract.Events.CALENDAR_ID, calendar.id);
values.put(CalendarContract.Events.DTSTART, 1381330800000L);
values.put(CalendarContract.Events.EVENT_TIMEZONE, "Europe/Vienna");
values.put(CalendarContract.Events.TITLE, "Without dtend/duration");
Uri uri = provider.insert(syncAdapterURI(CalendarContract.Events.CONTENT_URI), values);
@Cleanup("delete") TestEvent testEvent = new TestEvent(calendar, ContentUris.parseId(uri));
assertNull(testEvent.getEvent().dtEnd);
}
}
......@@ -47,6 +47,7 @@ import net.fortuna.ical4j.model.property.Action;
import net.fortuna.ical4j.model.property.Attendee;
import net.fortuna.ical4j.model.property.Description;
import net.fortuna.ical4j.model.property.DtEnd;
import net.fortuna.ical4j.model.property.DtStart;
import net.fortuna.ical4j.model.property.Duration;
import net.fortuna.ical4j.model.property.ExDate;
import net.fortuna.ical4j.model.property.ExRule;
......@@ -157,27 +158,33 @@ public abstract class AndroidEvent {
final boolean allDay = values.getAsInteger(Events.ALL_DAY) != 0;
final long tsStart = values.getAsLong(Events.DTSTART);
final Long tsEnd = values.getAsLong(Events.DTEND);
final String duration = values.getAsString(Events.DURATION);
String tzId;
Long tsEnd = values.getAsLong(Events.DTEND);
if (allDay) {
event.setDtStart(tsStart, null);
if (tsEnd == null && duration != null) {
Dur dur = new Dur(duration);
java.util.Date dEnd = dur.getTime(new java.util.Date(tsStart));
tsEnd = dEnd.getTime();
}
event.setDtEnd(tsEnd, null);
// use DATE values
event.dtStart = new DtStart(new Date(tsStart));
if (tsEnd != null)
event.dtEnd = new DtEnd(new Date(tsEnd));
else if (duration != null)
event.duration = new Duration(new Dur(duration));
} else {
// use the start time zone for the end time, too
// because apps like Samsung Planner allow the user to change "the" time zone but change the start time zone only
tzId = values.getAsString(Events.EVENT_TIMEZONE);
event.setDtStart(tsStart, tzId);
if (tsEnd != null)
event.setDtEnd(tsEnd, tzId);
else if (!StringUtils.isEmpty(duration))
// use DATE-TIME values
TimeZone tz = null;
String tzId = values.getAsString(Events.EVENT_TIMEZONE);
if (tzId != null)
tz = DateUtils.tzRegistry.getTimeZone(tzId);
DateTime start = new DateTime(tsStart);
start.setTimeZone(tz);
event.dtStart = new DtStart(start);
if (tsEnd != null) {
DateTime end = new DateTime(tsEnd);
end.setTimeZone(tz);
event.dtEnd = new DtEnd(end);
} else if (duration != null)
event.duration = new Duration(new Dur(duration));
}
......@@ -497,23 +504,25 @@ public abstract class AndroidEvent {
builder .withValue(Events.CALENDAR_ID, calendar.getId())
.withValue(Events.ALL_DAY, event.isAllDay() ? 1 : 0)
.withValue(Events.DTSTART, event.getDtStartInMillis())
.withValue(Events.DTSTART, event.dtStart.getDate().getTime())
.withValue(Events.EVENT_TIMEZONE, event.getDtStartTzID())
.withValue(Events.HAS_ATTENDEE_DATA, 1 /* we know information about all attendees and not only ourselves */);
// all-day events and "events on that day" must have a duration (set to one day if zero or missing)
if (event.isAllDay() && (event.dtEnd == null || !event.dtEnd.getDate().after(event.dtStart.getDate()))) {
// ical4j is not set to use floating times, so DATEs are UTC times internally
Constants.log.log(Level.INFO, "Changing all-day event for Android compatibility: DTEND := DTSTART + 1 day");
Constants.log.log(Level.INFO, "Changing all-day event for Android compatibility: dtend := dtstart + 1 day");
java.util.Calendar c = java.util.Calendar.getInstance(TimeZone.getTimeZone(TimeZones.UTC_ID));
c.setTime(event.dtStart.getDate());
c.add(java.util.Calendar.DATE, 1);
event.dtEnd = new DtEnd(new Date(c.getTimeInMillis()));
}
// events without dtEnd (events with zero duration)
if (event.dtEnd == null)
// fix events with zero or negative duration
else if (event.dtEnd == null || event.dtEnd.getDate().getTime() < event.dtStart.getDate().getTime()) {
Constants.log.info("Event without duration, setting dtend := dtstart");
event.dtEnd = new DtEnd(event.dtStart.getDate());
}
boolean recurring = false;
if (event.rRule != null) {
......@@ -541,10 +550,12 @@ public abstract class AndroidEvent {
// because that's the way Android likes it
if (recurring) {
// calculate DURATION from start and end date
Duration duration = new Duration(event.dtStart.getDate(), event.dtEnd.getDate());
Duration duration = (event.duration != null) ?
event.duration :
new Duration(event.dtStart.getDate(), event.dtEnd.getDate());
builder .withValue(Events.DURATION, duration.getValue());
} else
builder .withValue(Events.DTEND, event.getDtEndInMillis())
builder .withValue(Events.DTEND, event.dtEnd.getDate().getTime())
.withValue(Events.EVENT_END_TIMEZONE, event.getDtEndTzID());
if (event.summary != null)
......
......@@ -17,8 +17,6 @@ import net.fortuna.ical4j.data.ParserException;
import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.Component;
import net.fortuna.ical4j.model.ComponentList;
import net.fortuna.ical4j.model.Date;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.Property;
import net.fortuna.ical4j.model.PropertyList;
import net.fortuna.ical4j.model.TimeZone;
......@@ -387,41 +385,11 @@ public class Event extends iCalendar {
return !isDateTime(dtStart);
}
public long getDtStartInMillis() {
return dtStart.getDate().getTime();
}
public String getDtStartTzID() {
return getTzId(dtStart);
}
public void setDtStart(long tsStart, String tzID) {
if (tzID == null) { // all-day
dtStart = new DtStart(new Date(tsStart));
} else {
DateTime start = new DateTime(tsStart);
start.setTimeZone(DateUtils.tzRegistry.getTimeZone(tzID));
dtStart = new DtStart(start);
}
}
public long getDtEndInMillis() {
return dtEnd.getDate().getTime();
}
public String getDtEndTzID() {
return getTzId(dtEnd);
}
public void setDtEnd(long tsEnd, String tzID) {
if (tzID == null) { // all-day
dtEnd = new DtEnd(new Date(tsEnd));
} else {
DateTime end = new DateTime(tsEnd);
end.setTimeZone(DateUtils.tzRegistry.getTimeZone(tzID));
dtEnd = new DtEnd(end);
}
}
}
\ No newline at end of file
......@@ -88,9 +88,9 @@ public class EventTest {
public void testStartEndTimes() throws IOException, InvalidCalendarException {
// event with start+end date-time
Event eViennaEvolution = parseCalendar("vienna-evolution.ics", null)[0];
assertEquals(1381330800000L, eViennaEvolution.getDtStartInMillis());
assertEquals(1381330800000L, eViennaEvolution.dtStart.getDate().getTime());
assertEquals("Europe/Vienna", eViennaEvolution.getDtStartTzID());
assertEquals(1381334400000L, eViennaEvolution.getDtEndInMillis());
assertEquals(1381334400000L, eViennaEvolution.dtEnd.getDate().getTime());
assertEquals("Europe/Vienna", eViennaEvolution.getDtEndTzID());
}
......@@ -98,29 +98,29 @@ public class EventTest {
public void testStartEndTimesAllDay() throws IOException, InvalidCalendarException {
// event with start date only
Event eOnThatDay = parseCalendar("event-on-that-day.ics", null)[0];
assertEquals(868838400000L, eOnThatDay.getDtStartInMillis());
assertEquals(868838400000L, eOnThatDay.dtStart.getDate().getTime());
assertEquals(TimeZones.UTC_ID, eOnThatDay.getDtStartTzID());
// event with start+end date for all-day event (one day)
Event eAllDay1Day = parseCalendar("all-day-1day.ics", null)[0];
assertEquals(868838400000L, eAllDay1Day.getDtStartInMillis());
assertEquals(868838400000L, eAllDay1Day.dtStart.getDate().getTime());
assertEquals(TimeZones.UTC_ID, eAllDay1Day.getDtStartTzID());
assertEquals(868838400000L + 86400000, eAllDay1Day.getDtEndInMillis());
assertEquals(868838400000L + 86400000, eAllDay1Day.dtEnd.getDate().getTime());
assertEquals(TimeZones.UTC_ID, eAllDay1Day.getDtEndTzID());
// event with start+end date for all-day event (ten days)
Event eAllDay10Days = parseCalendar("all-day-10days.ics", null)[0];
assertEquals(868838400000L, eAllDay10Days.getDtStartInMillis());
assertEquals(868838400000L, eAllDay10Days.dtStart.getDate().getTime());
assertEquals(TimeZones.UTC_ID, eAllDay10Days.getDtStartTzID());
assertEquals(868838400000L + 10 * 86400000, eAllDay10Days.getDtEndInMillis());
assertEquals(868838400000L + 10 * 86400000, eAllDay10Days.dtEnd.getDate().getTime());
assertEquals(TimeZones.UTC_ID, eAllDay10Days.getDtEndTzID());
// event with start+end date on some day (0 sec-event)
Event eAllDay0Sec = parseCalendar("all-day-0sec.ics", null)[0];
assertEquals(868838400000L, eAllDay0Sec.getDtStartInMillis());
assertEquals(868838400000L, eAllDay0Sec.dtStart.getDate().getTime());
assertEquals(TimeZones.UTC_ID, eAllDay0Sec.getDtStartTzID());
// DTEND is same as DTSTART which is not valid for Android – but this will be handled by AndroidEvent, not Event
assertEquals(868838400000L, eAllDay0Sec.getDtEndInMillis());
assertEquals(868838400000L, eAllDay0Sec.dtEnd.getDate().getTime());
assertEquals(TimeZones.UTC_ID, eAllDay0Sec.getDtEndTzID());
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment