Merge "Fix bugs in SimpleIcsWriter"

This commit is contained in:
Marc Blank 2010-02-23 14:26:41 -08:00 committed by Android (Google) Code Review
commit d12bb138b0
3 changed files with 172 additions and 162 deletions

View File

@ -855,184 +855,191 @@ public class CalendarUtilities {
}
// Create our iCalendar writer and start generating tags
SimpleIcsWriter ics = new SimpleIcsWriter();
ics.writeTag("BEGIN", "VCALENDAR");
ics.writeTag("METHOD", method);
ics.writeTag("PRODID", "AndroidEmail");
ics.writeTag("VERSION", "2.0");
ics.writeTag("BEGIN", "VEVENT");
ics.writeTag("CLASS", "PUBLIC");
ics.writeTag("STATUS", "CONFIRMED");
ics.writeTag("TRANSP", "OPAQUE"); // What Exchange uses
ics.writeTag("PRIORITY", "5"); // 1 to 9, 5 = medium
ics.writeTag("SEQUENCE", "0");
if (uid == null) {
uid = entityValues.getAsString(Events._SYNC_LOCAL_ID);
}
if (uid != null) {
ics.writeTag("UID", uid);
}
if (entityValues.containsKey(Events.ALL_DAY)) {
Integer ade = entityValues.getAsInteger(Events.ALL_DAY);
ics.writeTag("X-MICROSOFT-CDO-ALLDAYEVENT", ade == 0 ? "FALSE" : "TRUE");
}
long startTime = entityValues.getAsLong(Events.DTSTART);
ics.writeTag("DTSTART", CalendarUtilities.millisToEasDateTime(startTime));
if (!entityValues.containsKey(Events.DURATION)) {
if (entityValues.containsKey(Events.DTEND)) {
ics.writeTag("DTEND", CalendarUtilities.millisToEasDateTime(
entityValues.getAsLong(Events.DTEND)));
SimpleIcsWriter ics;
try {
ics = new SimpleIcsWriter();
ics.writeTag("BEGIN", "VCALENDAR");
ics.writeTag("METHOD", method);
ics.writeTag("PRODID", "AndroidEmail");
ics.writeTag("VERSION", "2.0");
ics.writeTag("BEGIN", "VEVENT");
ics.writeTag("CLASS", "PUBLIC");
ics.writeTag("STATUS", "CONFIRMED");
ics.writeTag("TRANSP", "OPAQUE"); // What Exchange uses
ics.writeTag("PRIORITY", "5"); // 1 to 9, 5 = medium
ics.writeTag("SEQUENCE", "0");
if (uid == null) {
uid = entityValues.getAsString(Events._SYNC_LOCAL_ID);
}
} else {
// Convert this into millis and add it to DTSTART for DTEND
// We'll use 1 hour as a default
long durationMillis = HOURS;
Duration duration = new Duration();
try {
duration.parse(entityValues.getAsString(Events.DURATION));
} catch (ParseException e) {
// We'll use the default in this case
if (uid != null) {
ics.writeTag("UID", uid);
}
ics.writeTag("DTEND",
CalendarUtilities.millisToEasDateTime(startTime + durationMillis));
}
ics.writeTag("DTSTAMP", CalendarUtilities.millisToEasDateTime(System.currentTimeMillis()));
if (entityValues.containsKey(Events.EVENT_LOCATION)) {
ics.writeTag("LOCATION", entityValues.getAsString(Events.EVENT_LOCATION));
}
String title = entityValues.getAsString(Events.TITLE);
if (title != null) {
ics.writeTag("SUMMARY", title);
// TODO Add to strings.xml
msg.mSubject = "Invitation" + ": " + title;
} else {
msg.mSubject = "Invitation";
}
// TODO Handle time zone
String desc = entityValues.getAsString(Events.DESCRIPTION);
if (desc != null) {
// TODO Do we need to create something (like we'll do with the email)?
ics.writeTag("DESCRIPTION", desc);
msg.mText = "Boilerplate" + "\n\n" + desc;
} else {
msg.mText = "Boilerplate";
}
String rrule = entityValues.getAsString(Events.RRULE);
if (rrule != null) {
ics.writeTag("RRULE", rrule);
}
// Handle associated data EXCEPT for attendees, which have to be grouped
ArrayList<NamedContentValues> subValues = entity.getSubValues();
for (NamedContentValues ncv: subValues) {
Uri ncvUri = ncv.uri;
if (ncvUri.equals(Reminders.CONTENT_URI)) {
// TODO Consider sending out alarm information in the meeting request, although
// it's not obviously appropriate (i.e. telling the user what alarm to use)
// This should be for REQUEST only
// Here's what the VALARM would look like:
// BEGIN:VALARM
// ACTION:DISPLAY
// DESCRIPTION:REMINDER
// TRIGGER;RELATED=START:-PT15M
// END:VALARM
if (entityValues.containsKey(Events.ALL_DAY)) {
Integer ade = entityValues.getAsInteger(Events.ALL_DAY);
ics.writeTag("X-MICROSOFT-CDO-ALLDAYEVENT", ade == 0 ? "FALSE" : "TRUE");
}
}
// Handle attendee data here; keep track of organizer and stream it afterward
String organizerName = null;
String organizerEmail = null;
ArrayList<Address> toList = new ArrayList<Address>();
for (NamedContentValues ncv: subValues) {
Uri ncvUri = ncv.uri;
ContentValues ncvValues = ncv.values;
if (ncvUri.equals(Attendees.CONTENT_URI)) {
Integer relationship =
ncvValues.getAsInteger(Attendees.ATTENDEE_RELATIONSHIP);
// If there's no relationship, we can't create this for EAS
// Similarly, we need an attendee email for each invitee
if (relationship != null &&
ncvValues.containsKey(Attendees.ATTENDEE_EMAIL)) {
// Organizer isn't among attendees in EAS
if (relationship == Attendees.RELATIONSHIP_ORGANIZER) {
organizerName = ncvValues.getAsString(Attendees.ATTENDEE_NAME);
organizerEmail = ncvValues.getAsString(Attendees.ATTENDEE_EMAIL);
continue;
}
String attendeeEmail = ncvValues.getAsString(Attendees.ATTENDEE_EMAIL);
String attendeeName = ncvValues.getAsString(Attendees.ATTENDEE_NAME);
// This shouldn't be possible, but allow for it
if (attendeeEmail == null) continue;
long startTime = entityValues.getAsLong(Events.DTSTART);
ics.writeTag("DTSTART", CalendarUtilities.millisToEasDateTime(startTime));
if (messageFlag == Message.FLAG_OUTGOING_MEETING_INVITE) {
String icalTag = ICALENDAR_ATTENDEE_INVITE;
if (attendeeName != null) {
icalTag += ";CN=" + attendeeName;
if (!entityValues.containsKey(Events.DURATION)) {
if (entityValues.containsKey(Events.DTEND)) {
ics.writeTag("DTEND", CalendarUtilities.millisToEasDateTime(
entityValues.getAsLong(Events.DTEND)));
}
} else {
// Convert this into millis and add it to DTSTART for DTEND
// We'll use 1 hour as a default
long durationMillis = HOURS;
Duration duration = new Duration();
try {
duration.parse(entityValues.getAsString(Events.DURATION));
} catch (ParseException e) {
// We'll use the default in this case
}
ics.writeTag("DTEND",
CalendarUtilities.millisToEasDateTime(startTime + durationMillis));
}
ics.writeTag("DTSTAMP",
CalendarUtilities.millisToEasDateTime(System.currentTimeMillis()));
if (entityValues.containsKey(Events.EVENT_LOCATION)) {
ics.writeTag("LOCATION", entityValues.getAsString(Events.EVENT_LOCATION));
}
String title = entityValues.getAsString(Events.TITLE);
if (title != null) {
ics.writeTag("SUMMARY", title);
// TODO Add to strings.xml
msg.mSubject = "Invitation" + ": " + title;
} else {
msg.mSubject = "Invitation";
}
// TODO Handle time zone
String desc = entityValues.getAsString(Events.DESCRIPTION);
if (desc != null) {
// TODO Do we need to create something (like we'll do with the email)?
ics.writeTag("DESCRIPTION", desc);
msg.mText = "Boilerplate" + "\n\n" + desc;
} else {
msg.mText = "Boilerplate";
}
String rrule = entityValues.getAsString(Events.RRULE);
if (rrule != null) {
ics.writeTag("RRULE", rrule);
}
// Handle associated data EXCEPT for attendees, which have to be grouped
ArrayList<NamedContentValues> subValues = entity.getSubValues();
for (NamedContentValues ncv: subValues) {
Uri ncvUri = ncv.uri;
if (ncvUri.equals(Reminders.CONTENT_URI)) {
// TODO Consider sending out alarm information in the meeting request, although
// it's not obviously appropriate (i.e. telling the user what alarm to use)
// This should be for REQUEST only
// Here's what the VALARM would look like:
// BEGIN:VALARM
// ACTION:DISPLAY
// DESCRIPTION:REMINDER
// TRIGGER;RELATED=START:-PT15M
// END:VALARM
}
}
// Handle attendee data here; keep track of organizer and stream it afterward
String organizerName = null;
String organizerEmail = null;
ArrayList<Address> toList = new ArrayList<Address>();
for (NamedContentValues ncv: subValues) {
Uri ncvUri = ncv.uri;
ContentValues ncvValues = ncv.values;
if (ncvUri.equals(Attendees.CONTENT_URI)) {
Integer relationship =
ncvValues.getAsInteger(Attendees.ATTENDEE_RELATIONSHIP);
// If there's no relationship, we can't create this for EAS
// Similarly, we need an attendee email for each invitee
if (relationship != null &&
ncvValues.containsKey(Attendees.ATTENDEE_EMAIL)) {
// Organizer isn't among attendees in EAS
if (relationship == Attendees.RELATIONSHIP_ORGANIZER) {
organizerName = ncvValues.getAsString(Attendees.ATTENDEE_NAME);
organizerEmail = ncvValues.getAsString(Attendees.ATTENDEE_EMAIL);
continue;
}
ics.writeTag(icalTag, "MAILTO:" + attendeeEmail);
toList.add(attendeeName == null ? new Address(attendeeEmail) :
new Address(attendeeEmail, attendeeName));
} else if (attendeeEmail.equalsIgnoreCase(account.mEmailAddress)) {
String icalTag = null;
switch (messageFlag) {
case Message.FLAG_OUTGOING_MEETING_ACCEPT:
icalTag = ICALENDAR_ATTENDEE_ACCEPT;
break;
case Message.FLAG_OUTGOING_MEETING_DECLINE:
icalTag = ICALENDAR_ATTENDEE_DECLINE;
break;
case Message.FLAG_OUTGOING_MEETING_TENTATIVE:
icalTag = ICALENDAR_ATTENDEE_TENTATIVE;
break;
}
if (icalTag != null) {
String attendeeEmail = ncvValues.getAsString(Attendees.ATTENDEE_EMAIL);
String attendeeName = ncvValues.getAsString(Attendees.ATTENDEE_NAME);
// This shouldn't be possible, but allow for it
if (attendeeEmail == null) continue;
if (messageFlag == Message.FLAG_OUTGOING_MEETING_INVITE) {
String icalTag = ICALENDAR_ATTENDEE_INVITE;
if (attendeeName != null) {
icalTag += ";CN=" + attendeeName;
}
ics.writeTag(icalTag, "MAILTO:" + attendeeEmail);
toList.add(attendeeName == null ? new Address(attendeeEmail) :
new Address(attendeeEmail, attendeeName));
} else if (attendeeEmail.equalsIgnoreCase(account.mEmailAddress)) {
String icalTag = null;
switch (messageFlag) {
case Message.FLAG_OUTGOING_MEETING_ACCEPT:
icalTag = ICALENDAR_ATTENDEE_ACCEPT;
break;
case Message.FLAG_OUTGOING_MEETING_DECLINE:
icalTag = ICALENDAR_ATTENDEE_DECLINE;
break;
case Message.FLAG_OUTGOING_MEETING_TENTATIVE:
icalTag = ICALENDAR_ATTENDEE_TENTATIVE;
break;
}
if (icalTag != null) {
if (attendeeName != null) {
icalTag += ";CN=" + attendeeName;
}
ics.writeTag(icalTag, "MAILTO:" + attendeeEmail);
}
}
}
}
}
}
// Create the organizer tag for ical
if (organizerEmail != null) {
String icalTag = "ORGANIZER";
// We should be able to find this, assuming the Email is the user's email
// TODO Find this in the account
if (organizerName != null) {
icalTag += ";CN=" + organizerName;
// Create the organizer tag for ical
if (organizerEmail != null) {
String icalTag = "ORGANIZER";
// We should be able to find this, assuming the Email is the user's email
// TODO Find this in the account
if (organizerName != null) {
icalTag += ";CN=" + organizerName;
}
ics.writeTag(icalTag, "MAILTO:" + organizerEmail);
if (method.equals("REPLY")) {
toList.add(organizerName == null ? new Address(organizerEmail) :
new Address(organizerEmail, organizerName));
}
}
ics.writeTag(icalTag, "MAILTO:" + organizerEmail);
if (method.equals("REPLY")) {
toList.add(organizerName == null ? new Address(organizerEmail) :
new Address(organizerEmail, organizerName));
// If we have no "to" list, we're done
if (toList.isEmpty()) return null;
// Write out the "to" list
Address[] toArray = new Address[toList.size()];
int i = 0;
for (Address address: toList) {
toArray[i++] = address;
}
}
msg.mTo = Address.pack(toArray);
// If we have no "to" list, we're done
if (toList.isEmpty()) return null;
// Write out the "to" list
Address[] toArray = new Address[toList.size()];
int i = 0;
for (Address address: toList) {
toArray[i++] = address;
ics.writeTag("END", "VEVENT");
ics.writeTag("END", "VCALENDAR");
ics.flush();
ics.close();
} catch (IOException e) {;;
Log.w(TAG, "IOException creating message for Event");
return null;
}
msg.mTo = Address.pack(toArray);
ics.writeTag("END", "VEVENT");
ics.writeTag("END", "VCALENDAR");
ics.flush();
ics.close();
// Create the ics attachment using the "content" field
Attachment att = new Attachment();

View File

@ -16,6 +16,7 @@
package com.android.exchange.utility;
import java.io.CharArrayWriter;
import java.io.IOException;
public class SimpleIcsWriter extends CharArrayWriter {
public static final int MAX_LINE_LENGTH = 75;
@ -34,12 +35,12 @@ public class SimpleIcsWriter extends CharArrayWriter {
}
@Override
public void write(String str) {
public void write(String str) throws IOException {
int len = str.length();
// Handle the simple case here to avoid unnecessary looping
if (mLineCount + len < MAX_LINE_LENGTH) {
mLineCount += len;
write(str);
super.write(str);
return;
}
for (int i = 0; i < len; i++, mLineCount++) {
@ -53,7 +54,7 @@ public class SimpleIcsWriter extends CharArrayWriter {
}
}
public void writeTag(String name, String value) {
public void writeTag(String name, String value) throws IOException {
write(name);
write(":");
write(value);

View File

@ -15,6 +15,8 @@
package com.android.exchange.utility;
import java.io.IOException;
import junit.framework.TestCase;
/**
@ -35,7 +37,7 @@ public class SimpleIcsWriterTests extends TestCase {
private final String expectedSecondLineBreak =
string80Chars.charAt(SimpleIcsWriter.MAX_LINE_LENGTH - 1) + SimpleIcsWriter.LINE_BREAK;
public void testWriter() {
public void testWriter() throws IOException {
// Sanity test on constant strings
assertEquals(63, string63Chars.length());
assertEquals(80, string80Chars.length());