Fix issues with incorrect email replies to event organizer
* We were previously storing the user's attendee status in the
SYNC_ADAPTER_DATA column of the Event, but it turns out that
this data isn't available in the Entity we retrieve when
uploading changes to the server
* The result is that we often thought the user's status had
changed when in fact it had not; in these cases, we sent email
to the organizer, often with the wrong information.
* As of this CL, we store the user's attendee status in an
ExtendedProperties row (these values are already exposed in event
entities)
* The logic otherwise remains the same; we now get correct data,
however
Bug: 2638762
Change-Id: Ibe8db90c16b4ca06203f77fd010aa26dde89a556
diff --git a/src/com/android/exchange/adapter/CalendarSyncAdapter.java b/src/com/android/exchange/adapter/CalendarSyncAdapter.java
index 3732423..b7c2d50 100644
--- a/src/com/android/exchange/adapter/CalendarSyncAdapter.java
+++ b/src/com/android/exchange/adapter/CalendarSyncAdapter.java
@@ -453,8 +453,17 @@
sb.append(attendeeEmail);
sb.append(ATTENDEE_TOKENIZER_DELIMITER);
if (mEmailAddress.equalsIgnoreCase(attendeeEmail)) {
- attendee.put(Attendees.ATTENDEE_STATUS,
- CalendarUtilities.attendeeStatusFromBusyStatus(busyStatus));
+ int attendeeStatus =
+ CalendarUtilities.attendeeStatusFromBusyStatus(busyStatus);
+ attendee.put(Attendees.ATTENDEE_STATUS, attendeeStatus);
+ // If we're an attendee, save away our initial attendee status in the
+ // event's ExtendedProperties (we look for differences between this and
+ // the user's current attendee status to determine whether an email needs
+ // to be sent to the organizer)
+ if (!organizerEmail.equalsIgnoreCase(attendeeEmail)) {
+ ops.newExtendedProperty("userAttendeeStatus",
+ Integer.toString(attendeeStatus));
+ }
}
if (eventId < 0) {
ops.newAttendee(attendee);
@@ -1607,6 +1616,12 @@
long exEventId = exValues.getAsLong(Events._ID);
int flag;
+ // Copy subvalues into the exception; otherwise, we won't see the
+ // attendees when preparing the message
+ for (NamedContentValues ncv: entity.getSubValues()) {
+ exEntity.addSubValue(ncv.uri, ncv.values);
+ }
+
if ((getInt(exValues, Events.DELETED) == 1) ||
(getInt(exValues, Events.STATUS) ==
Events.STATUS_CANCELED)) {
@@ -1627,11 +1642,6 @@
// the dirty/mark bits are cleared
mUploadedIdList.add(exEventId);
- // Copy subvalues into the exception; otherwise, we won't see the
- // attendees when preparing the message
- for (NamedContentValues ncv: entity.getSubValues()) {
- exEntity.addSubValue(ncv.uri, ncv.values);
- }
// Copy version so the ics attachment shows the proper sequence #
exValues.put(Events._SYNC_VERSION,
entityValues.getAsString(Events._SYNC_VERSION));
@@ -1661,6 +1671,31 @@
s.end().end(); // ApplicationData & Change
mUploadedIdList.add(eventId);
+ // Go through the extended properties of this Event and pull out our tokenized
+ // attendees list and the user attendee status; we will need them later
+ String attendeeString = null;
+ long attendeeStringId = -1;
+ String userAttendeeStatus = null;
+ long userAttendeeStatusId = -1;
+ for (NamedContentValues ncv: entity.getSubValues()) {
+ if (ncv.uri.equals(ExtendedProperties.CONTENT_URI)) {
+ ContentValues ncvValues = ncv.values;
+ String propertyName =
+ ncvValues.getAsString(ExtendedProperties.NAME);
+ if (propertyName.equals("attendees")) {
+ attendeeString =
+ ncvValues.getAsString(ExtendedProperties.VALUE);
+ attendeeStringId =
+ ncvValues.getAsLong(ExtendedProperties._ID);
+ } else if (propertyName.equals("userAttendeeStatus")) {
+ userAttendeeStatus =
+ ncvValues.getAsString(ExtendedProperties.VALUE);
+ userAttendeeStatusId =
+ ncvValues.getAsLong(ExtendedProperties._ID);
+ }
+ }
+ }
+
// Send the meeting invite if there are attendees and we're the organizer AND
// if the Event itself is dirty (we might be syncing only because an exception
// is dirty, in which case we DON'T send email about the Event)
@@ -1674,20 +1709,7 @@
userLog("Queueing invitation to ", msg.mTo);
mOutgoingMailList.add(msg);
}
- // Retrieve our tokenized string of attendee email addresses
- String attendeeString = null;
- for (NamedContentValues ncv: entity.getSubValues()) {
- if (ncv.uri.equals(ExtendedProperties.CONTENT_URI)) {
- String propertyName =
- ncv.values.getAsString(ExtendedProperties.NAME);
- if (propertyName.equals("attendees")) {
- attendeeString =
- ncv.values.getAsString(ExtendedProperties.VALUE);
- break;
- }
- }
- }
- // Make a list out of them, if we have any
+ // Make a list out of our tokenized attendees, if we have any
ArrayList<String> originalAttendeeList = new ArrayList<String>();
if (attendeeString != null) {
StringTokenizer st =
@@ -1715,24 +1737,8 @@
ContentValues cv = new ContentValues();
cv.put(ExtendedProperties.VALUE, newTokenizedAttendees.toString());
if (attendeeString != null) {
- // Now, we have to find the id of this row in ExtendedProperties
- Cursor epCursor = cr.query(ExtendedProperties.CONTENT_URI,
- new String[] {ExtendedProperties._ID},
- ExtendedProperties.EVENT_ID + "=? AND name=\"attendees\"",
- new String[] {Long.toString(eventId)}, null);
- if (epCursor != null) {
- try {
- // There's no reason this should fail (after all, we found it
- // in the subValues; if it's gone, though, that's fine
- if (epCursor.moveToFirst()) {
- cr.update(ContentUris.withAppendedId(
- ExtendedProperties.CONTENT_URI,
- epCursor.getLong(0)), cv, null, null);
- }
- } finally {
- epCursor.close();
- }
- }
+ cr.update(ContentUris.withAppendedId(ExtendedProperties.CONTENT_URI,
+ attendeeStringId), cv, null, null);
} else {
// If there wasn't an "attendees" property, insert one
cv.put(ExtendedProperties.NAME, "attendees");
@@ -1754,14 +1760,16 @@
}
} else if (!selfOrganizer) {
// If we're not the organizer, see if we've changed our attendee status
- // Note: Since we "own" our own calendar, selfAttendeeStatus will be
- // correct when we set it from the UI. We NEVER set selfAttendeeStatus
- // on downsync, however.
+ // Our last synced attendee status is in ExtendedProperties, and we've
+ // retrieved it above as userAttendeeStatus
int currentStatus = entityValues.getAsInteger(Events.SELF_ATTENDEE_STATUS);
- String adapterData = entityValues.getAsString(Events.SYNC_ADAPTER_DATA);
int syncStatus = Attendees.ATTENDEE_STATUS_NONE;
- if (adapterData != null) {
- syncStatus = Integer.parseInt(adapterData);
+ if (userAttendeeStatus != null) {
+ try {
+ syncStatus = Integer.parseInt(userAttendeeStatus);
+ } catch (NumberFormatException e) {
+ // Just in case somebody else mucked with this and it's not Integer
+ }
}
if ((currentStatus != syncStatus) &&
(currentStatus != Attendees.ATTENDEE_STATUS_NONE)) {
@@ -1779,13 +1787,13 @@
break;
}
// Make sure we have a valid status (messageFlag should never be zero)
- if (messageFlag != 0) {
+ if (messageFlag != 0 && userAttendeeStatusId >= 0) {
// Save away the new status
cidValues.clear();
- cidValues.put(Events.SYNC_ADAPTER_DATA,
+ cidValues.put(ExtendedProperties.VALUE,
Integer.toString(currentStatus));
- cr.update(ContentUris.withAppendedId(EVENTS_URI, eventId),
- cidValues, null, null);
+ cr.update(ContentUris.withAppendedId(ExtendedProperties.CONTENT_URI,
+ userAttendeeStatusId), cidValues, null, null);
// Send mail to the organizer advising of the new status
EmailContent.Message msg =
CalendarUtilities.createMessageForEventId(mContext, eventId,