Commit bac737a8 authored by Ricki Hirner's avatar Ricki Hirner

Improve ATTENDEE/ORGANIZER handling

parent 31644f47
...@@ -10,7 +10,6 @@ package at.bitfire.davdroid.resource; ...@@ -10,7 +10,6 @@ package at.bitfire.davdroid.resource;
import android.text.format.Time; import android.text.format.Time;
import android.util.Log; import android.util.Log;
import net.fortuna.ical4j.data.CalendarBuilder;
import net.fortuna.ical4j.data.CalendarOutputter; import net.fortuna.ical4j.data.CalendarOutputter;
import net.fortuna.ical4j.data.ParserException; import net.fortuna.ical4j.data.ParserException;
import net.fortuna.ical4j.model.Component; import net.fortuna.ical4j.model.Component;
...@@ -105,12 +104,11 @@ public class Event extends iCalendar { ...@@ -105,12 +104,11 @@ public class Event extends iCalendar {
public void parseEntity(@NonNull InputStream entity, Charset charset, AssetDownloader downloader) throws IOException, InvalidResourceException { public void parseEntity(@NonNull InputStream entity, Charset charset, AssetDownloader downloader) throws IOException, InvalidResourceException {
final net.fortuna.ical4j.model.Calendar ical; final net.fortuna.ical4j.model.Calendar ical;
try { try {
CalendarBuilder builder = new CalendarBuilder();
if (charset != null) { if (charset != null) {
@Cleanup InputStreamReader reader = new InputStreamReader(entity, charset); @Cleanup InputStreamReader reader = new InputStreamReader(entity, charset);
ical = builder.build(reader); ical = calendarBuilder.build(reader);
} else } else
ical = builder.build(entity); ical = calendarBuilder.build(entity);
if (ical == null) if (ical == null)
throw new InvalidResourceException("No iCalendar found"); throw new InvalidResourceException("No iCalendar found");
...@@ -354,5 +352,4 @@ public class Event extends iCalendar { ...@@ -354,5 +352,4 @@ public class Event extends iCalendar {
} }
} }
} }
...@@ -41,6 +41,7 @@ import net.fortuna.ical4j.model.parameter.Cn; ...@@ -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.Rsvp;
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.Description; import net.fortuna.ical4j.model.property.Description;
...@@ -77,12 +78,11 @@ import lombok.Getter; ...@@ -77,12 +78,11 @@ import lombok.Getter;
public class LocalCalendar extends LocalCollection<Event> { public class LocalCalendar extends LocalCollection<Event> {
private static final String TAG = "davdroid.LocalCalendar"; private static final String TAG = "davdroid.LocalCalendar";
@Getter protected String url; @Getter private String url;
@Getter protected long id; @Getter private long id;
protected static final String COLLECTION_COLUMN_CTAG = Calendars.CAL_SYNC1; protected static final String COLLECTION_COLUMN_CTAG = Calendars.CAL_SYNC1;
/* database fields */ /* database fields */
@Override protected Uri entriesURI() { return syncAdapterURI(Events.CONTENT_URI); } @Override protected Uri entriesURI() { return syncAdapterURI(Events.CONTENT_URI); }
...@@ -425,13 +425,12 @@ public class LocalCalendar extends LocalCollection<Event> { ...@@ -425,13 +425,12 @@ public class LocalCalendar extends LocalCollection<Event> {
// availability // availability
e.setOpaque(values.getAsInteger(Events.AVAILABILITY) != Events.AVAILABILITY_FREE); e.setOpaque(values.getAsInteger(Events.AVAILABILITY) != Events.AVAILABILITY_FREE);
// set ORGANIZER only when there are attendees // set ORGANIZER
if (values.getAsInteger(Events.HAS_ATTENDEE_DATA) != 0 && values.containsKey(Events.ORGANIZER)) try {
try { e.setOrganizer(new Organizer(new URI("mailto", values.getAsString(Events.ORGANIZER), null)));
e.setOrganizer(new Organizer(new URI("mailto", values.getAsString(Events.ORGANIZER), null))); } catch (URISyntaxException ex) {
} catch (URISyntaxException ex) { Log.e(TAG, "Error when creating ORGANIZER mailto URI, ignoring", ex);
Log.e(TAG, "Error when creating ORGANIZER URI, ignoring", ex); }
}
// classification // classification
switch (values.getAsInteger(Events.ACCESS_LEVEL)) { switch (values.getAsInteger(Events.ACCESS_LEVEL)) {
...@@ -463,8 +462,20 @@ public class LocalCalendar extends LocalCollection<Event> { ...@@ -463,8 +462,20 @@ public class LocalCalendar extends LocalCollection<Event> {
void populateAttendee(Event event, ContentValues values) { void populateAttendee(Event event, ContentValues values) {
try { try {
Attendee attendee = new Attendee(new URI("mailto", values.getAsString(Attendees.ATTENDEE_EMAIL), null)); final Attendee attendee;
ParameterList params = attendee.getParameters(); final String
email = values.getAsString(Attendees.ATTENDEE_EMAIL),
idNS = values.getAsString(Attendees.ATTENDEE_ID_NAMESPACE),
id = values.getAsString(Attendees.ATTENDEE_IDENTITY);
if (idNS != null || id != null) {
// attendee identified by namespace and ID
attendee = new Attendee(new URI(idNS, id, null));
if (email != null)
attendee.getParameters().add(new iCalendar.Email(email));
} else
// attendee identified by email address
attendee = new Attendee(new URI("mailto", email, null));
final ParameterList params = attendee.getParameters();
String cn = values.getAsString(Attendees.ATTENDEE_NAME); String cn = values.getAsString(Attendees.ATTENDEE_NAME);
if (cn != null) if (cn != null)
...@@ -478,12 +489,11 @@ public class LocalCalendar extends LocalCollection<Event> { ...@@ -478,12 +489,11 @@ public class LocalCalendar extends LocalCollection<Event> {
int relationship = values.getAsInteger(Attendees.ATTENDEE_RELATIONSHIP); int relationship = values.getAsInteger(Attendees.ATTENDEE_RELATIONSHIP);
switch (relationship) { switch (relationship) {
case Attendees.RELATIONSHIP_ORGANIZER: case Attendees.RELATIONSHIP_ORGANIZER:
params.add(Role.CHAIR);
break;
case Attendees.RELATIONSHIP_ATTENDEE: case Attendees.RELATIONSHIP_ATTENDEE:
case Attendees.RELATIONSHIP_PERFORMER: case Attendees.RELATIONSHIP_PERFORMER:
case Attendees.RELATIONSHIP_SPEAKER: case Attendees.RELATIONSHIP_SPEAKER:
params.add((type == Attendees.TYPE_REQUIRED) ? Role.REQ_PARTICIPANT : Role.OPT_PARTICIPANT); params.add((type == Attendees.TYPE_REQUIRED) ? Role.REQ_PARTICIPANT : Role.OPT_PARTICIPANT);
params.add(new Rsvp(true));
break; break;
case Attendees.RELATIONSHIP_NONE: case Attendees.RELATIONSHIP_NONE:
params.add(Role.NON_PARTICIPANT); params.add(Role.NON_PARTICIPANT);
...@@ -590,11 +600,22 @@ public class LocalCalendar extends LocalCollection<Event> { ...@@ -590,11 +600,22 @@ public class LocalCalendar extends LocalCollection<Event> {
builder.withValue(Events.EVENT_LOCATION, event.getLocation()); builder.withValue(Events.EVENT_LOCATION, event.getLocation());
if (event.getDescription() != null) if (event.getDescription() != null)
builder.withValue(Events.DESCRIPTION, event.getDescription()); builder.withValue(Events.DESCRIPTION, event.getDescription());
if (event.getOrganizer() != null && event.getOrganizer().getCalAddress() != null) { Organizer organizer = event.getOrganizer();
URI organizer = event.getOrganizer().getCalAddress(); if (organizer != null) {
if (organizer.getScheme() != null && organizer.getScheme().equalsIgnoreCase("mailto")) final URI uri = organizer.getCalAddress();
builder.withValue(Events.ORGANIZER, organizer.getSchemeSpecificPart());
String email = null;
if (uri != null && "mailto".equalsIgnoreCase(uri.getScheme()))
email = uri.getSchemeSpecificPart();
else {
iCalendar.Email emailParam = (iCalendar.Email)organizer.getParameter(iCalendar.Email.PARAMETER_NAME);
if (emailParam != null)
email = emailParam.getValue();
else
Log.w(TAG, "Got ORGANIZER without email address, using given URI instead (may cause Android to behave unexpectedly)");
}
builder.withValue(Events.ORGANIZER, email != null ? email : uri.toString());
} }
Status status = event.getStatus(); Status status = event.getStatus();
...@@ -673,7 +694,18 @@ public class LocalCalendar extends LocalCollection<Event> { ...@@ -673,7 +694,18 @@ public class LocalCalendar extends LocalCollection<Event> {
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
protected Builder buildAttendee(Builder builder, Attendee attendee) { protected Builder buildAttendee(Builder builder, Attendee attendee) {
final Uri member = Uri.parse(attendee.getValue()); final Uri member = Uri.parse(attendee.getValue());
final String email = member.getSchemeSpecificPart(); if ("mailto".equalsIgnoreCase(member.getScheme()))
// attendee identified by email
builder = builder.withValue(Attendees.ATTENDEE_EMAIL, member.getSchemeSpecificPart());
else {
// attendee identified by other URI
builder = builder
.withValue(Attendees.ATTENDEE_ID_NAMESPACE, member.getScheme())
.withValue(Attendees.ATTENDEE_IDENTITY, member.getSchemeSpecificPart());
iCalendar.Email email = (iCalendar.Email)attendee.getParameter(iCalendar.Email.PARAMETER_NAME);
if (email != null)
builder = builder.withValue(Attendees.ATTENDEE_EMAIL, email.getValue());
}
final Cn cn = (Cn)attendee.getParameter(Parameter.CN); final Cn cn = (Cn)attendee.getParameter(Parameter.CN);
if (cn != null) if (cn != null)
...@@ -682,9 +714,11 @@ public class LocalCalendar extends LocalCollection<Event> { ...@@ -682,9 +714,11 @@ public class LocalCalendar extends LocalCollection<Event> {
int type = Attendees.TYPE_NONE; int type = Attendees.TYPE_NONE;
CuType cutype = (CuType)attendee.getParameter(Parameter.CUTYPE); CuType cutype = (CuType)attendee.getParameter(Parameter.CUTYPE);
if (cutype == CuType.RESOURCE) if (cutype == CuType.RESOURCE || cutype == CuType.ROOM)
// "attendee" is a (physical) resource
type = Attendees.TYPE_RESOURCE; type = Attendees.TYPE_RESOURCE;
else { else {
// attendee is not a (physical) resource
Role role = (Role)attendee.getParameter(Parameter.ROLE); Role role = (Role)attendee.getParameter(Parameter.ROLE);
int relationship; int relationship;
if (role == Role.CHAIR) if (role == Role.CHAIR)
...@@ -711,7 +745,6 @@ public class LocalCalendar extends LocalCollection<Event> { ...@@ -711,7 +745,6 @@ public class LocalCalendar extends LocalCollection<Event> {
status = Attendees.ATTENDEE_STATUS_TENTATIVE; status = Attendees.ATTENDEE_STATUS_TENTATIVE;
return builder return builder
.withValue(Attendees.ATTENDEE_EMAIL, email)
.withValue(Attendees.ATTENDEE_TYPE, type) .withValue(Attendees.ATTENDEE_TYPE, type)
.withValue(Attendees.ATTENDEE_STATUS, status); .withValue(Attendees.ATTENDEE_STATUS, status);
} }
......
...@@ -10,7 +10,6 @@ package at.bitfire.davdroid.resource; ...@@ -10,7 +10,6 @@ package at.bitfire.davdroid.resource;
import android.util.Log; import android.util.Log;
import net.fortuna.ical4j.data.CalendarBuilder;
import net.fortuna.ical4j.data.CalendarOutputter; import net.fortuna.ical4j.data.CalendarOutputter;
import net.fortuna.ical4j.data.ParserException; import net.fortuna.ical4j.data.ParserException;
import net.fortuna.ical4j.model.Component; import net.fortuna.ical4j.model.Component;
...@@ -84,12 +83,11 @@ public class Task extends iCalendar { ...@@ -84,12 +83,11 @@ public class Task extends iCalendar {
public void parseEntity(InputStream entity, Charset charset, AssetDownloader downloader) throws IOException, InvalidResourceException { public void parseEntity(InputStream entity, Charset charset, AssetDownloader downloader) throws IOException, InvalidResourceException {
final net.fortuna.ical4j.model.Calendar ical; final net.fortuna.ical4j.model.Calendar ical;
try { try {
CalendarBuilder builder = new CalendarBuilder();
if (charset != null) { if (charset != null) {
@Cleanup InputStreamReader reader = new InputStreamReader(entity, charset); @Cleanup InputStreamReader reader = new InputStreamReader(entity, charset);
ical = builder.build(reader); ical = calendarBuilder.build(reader);
} else } else
ical = builder.build(entity); ical = calendarBuilder.build(entity);
if (ical == null) if (ical == null)
throw new InvalidResourceException("No iCalendar found"); throw new InvalidResourceException("No iCalendar found");
......
...@@ -11,12 +11,19 @@ package at.bitfire.davdroid.resource; ...@@ -11,12 +11,19 @@ package at.bitfire.davdroid.resource;
import android.util.Log; import android.util.Log;
import net.fortuna.ical4j.data.CalendarBuilder; import net.fortuna.ical4j.data.CalendarBuilder;
import net.fortuna.ical4j.data.CalendarParserFactory;
import net.fortuna.ical4j.data.ParserException; import net.fortuna.ical4j.data.ParserException;
import net.fortuna.ical4j.model.DateTime; import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.Parameter;
import net.fortuna.ical4j.model.ParameterFactory;
import net.fortuna.ical4j.model.ParameterFactoryImpl;
import net.fortuna.ical4j.model.ParameterFactoryRegistry;
import net.fortuna.ical4j.model.PropertyFactoryRegistry;
import net.fortuna.ical4j.model.component.VTimeZone; import net.fortuna.ical4j.model.component.VTimeZone;
import net.fortuna.ical4j.model.property.DateProperty; import net.fortuna.ical4j.model.property.DateProperty;
import net.fortuna.ical4j.util.CompatibilityHints; import net.fortuna.ical4j.util.CompatibilityHints;
import net.fortuna.ical4j.util.SimpleHostInfo; import net.fortuna.ical4j.util.SimpleHostInfo;
import net.fortuna.ical4j.util.Strings;
import net.fortuna.ical4j.util.UidGenerator; import net.fortuna.ical4j.util.UidGenerator;
import org.apache.commons.codec.CharEncoding; import org.apache.commons.codec.CharEncoding;
...@@ -24,10 +31,12 @@ import org.apache.http.entity.ContentType; ...@@ -24,10 +31,12 @@ import org.apache.http.entity.ContentType;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.net.URISyntaxException;
import java.util.TimeZone; import java.util.TimeZone;
import at.bitfire.davdroid.DateUtils; import at.bitfire.davdroid.DateUtils;
import at.bitfire.davdroid.syncadapter.DavSyncAdapter; import at.bitfire.davdroid.syncadapter.DavSyncAdapter;
import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
public abstract class iCalendar extends Resource { public abstract class iCalendar extends Resource {
...@@ -114,4 +123,42 @@ public abstract class iCalendar extends Resource { ...@@ -114,4 +123,42 @@ public abstract class iCalendar extends Resource {
return null; return null;
} }
// ical4j helpers and extensions
private static final ParameterFactoryRegistry parameterFactoryRegistry = new ParameterFactoryRegistry();
static {
parameterFactoryRegistry.register(Email.PARAMETER_NAME, Email.FACTORY);
}
protected static final CalendarBuilder calendarBuilder = new CalendarBuilder(
CalendarParserFactory.getInstance().createParser(),
new PropertyFactoryRegistry(), parameterFactoryRegistry, DateUtils.tzRegistry);
public static class Email extends Parameter {
/* EMAIL property for ATTENDEE properties, as used by iCloud:
ATTENDEE;EMAIL=bla@domain.tld;/path/to/principal
*/
public static final ParameterFactory FACTORY = new Factory();
public static final String PARAMETER_NAME = "EMAIL";
@Getter private String value;
protected Email() {
super(PARAMETER_NAME, ParameterFactoryImpl.getInstance());
}
public Email(String aValue) {
super(PARAMETER_NAME, ParameterFactoryImpl.getInstance());
value = Strings.unquote(aValue);
}
public static class Factory implements ParameterFactory {
@Override
public Parameter createParameter(String name, String value) throws URISyntaxException {
return new Email(value);
}
}
}
} }
This source diff could not be displayed because it is too large. You can view the blob instead.
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